Spring Boot其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。
1. 創建獨立的Spring應用程序
2. 嵌入的Tomcat,無需部署WAR文件
3. 簡化Maven配置
4. 自動配置Spring
5. 提供生產就緒型功能,如指標,健康檢查和外部配置
6.開箱即用,沒有代碼生成,也無需XML配置。
l 為基于Spring的開發提供更快的入門體驗
l 開箱即用,沒有代碼生成,也無需XML配置。同時也可以修改默認值來滿足特定的需求。
l 提供了一些大型項目中常見的非功能特性,如嵌入式服務器、安全、指標,健康檢測、外部配置等。
l Spring Boot并不是對Spring功能上的增強,而是提供了一種快速使用Spring的方式。
<projectxmlns=" POM/4.0.0"xmlns:xsi=" /2001/XMLSchema-instance" xsi:schemaLocation= /POM/4.0.0 /xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.kfit</groupId> <artifactId>spring-boot-hello</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-boot-hello</name> <url> apache.org</url> <!-- spring boot 父節點依賴,引入這個之后相關的引入就不需要添加version配置,spring boot會自動選擇最合適的版本進行添加。 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.1.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- 指定一下jdk的版本 ,這里我們使用jdk 1.8 ,默認是1.6 --> <java.version>1.8</java.version> </properties> <dependencies> <!-- spring-boot-starter-web: MVC,AOP的依賴包.... --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- <version></version> 由于我們在上面指定了 parent(spring boot) --> </dependency> <dependencies> </project> |
l Codeing 步驟:
l 新建一個Controller類
package com.kfit; import java.util.Date; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 在這里我們使用RestController (等待于 @Controller 和 @RequestBody) */ @RestController public class HelloController { /** * 在這里我們使用@RequestMapping 建立請求映射: * http://127.0.0.1:8080/hello * @return */ @RequestMapping("/hello") public String hello(){ return "hello-2016-12-11.v.0"; } @RequestMapping("/hello2") public String hello2(){ return "hello2-2016"; } @RequestMapping("/hello3") public String hello3(){ return "hello3"; } /** * Spring Boot默認使用的json解析框架是jackson * @return */ @RequestMapping("/getDemo") public Demo getDemo(){ Demo demo=new Demo(); demo.setId(1); demo.setName("張三"); demo.setCreateTime(new Date()); demo.setRemarks("這是備注信息"); return demo; } } |
l 新建啟動類(App – Main方法)
package com.kfit; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.web.HttpMessageConverters; import org.springframework.context.annotation.Bean; import org.springframework.http.converter.HttpMessageConverter; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; /** * 在這里我們使用@SpringBootApplication指定這是一個 spring boot的應用程序. */ //extends WebMvcConfigurerAdapter @SpringBootApplication public class App{ /** * 這是springloader的配置方式:-javaagent:.\lib\springloaded-1.2.4.RELEASE.jar -noverify * @param args */ public static void main(String[] args) { /* * 在main方法進行啟動我們的應用程序. */ SpringApplication.run(App.class, args); } } |
l 測試代碼
l 步驟:
l 1. 編寫實體類Demo
package com.kfit; import java.util.Date; import com.alibaba.fastjson.annotation.JSONField; /** * 這是一個測試實體類. */ public class Demo { private int id; private String name; //com.alibaba.fastjson.annotation.JSONField @JSONField(format="yyyy-MM-dd HH:mm") private Date createTime;//創建時間. /* * 我們不想返回remarks? * serialize:是否需要序列化屬性. */ @JSONField(serialize=false) private String remarks;//備注信息. public String getRemarks() { return remarks; } public void setRemarks(String remarks) { this.remarks=remarks; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime=createTime; } public int getId() { return id; } public void setId(int id) { this.id=id; } public String getName() { return name; } public void setName(String name) { this.name=name; } } |
l 2. 編寫getDemo()方法
/** * Spring Boot默認使用的json解析框架是jackson * @return */ @RequestMapping("/getDemo") public Demo getDemo(){ Demo demo=new Demo(); demo.setId(1); demo.setName("張三"); demo.setCreateTime(new Date()); demo.setRemarks("這是備注信息"); returndemo; } |
l 3. 測試
l 個人使用比較習慣的json框架是fastjson,所以spring boot默認的json使用起來比較不習慣,所以很自然我就想我能不能使用fastjson進行json解析呢?
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.15</version>
</dependency>
這里要說下很重要的話,官方文檔說的1.2.10以后,會有兩個方法支持HttpMessageconvert,一個是FastJsonHttpMessageConverter,支持4.2以下的版本,一個是FastJsonHttpMessageConverter4支持4.2以上的版本,具體有什么區別暫時沒有深入研究。這里也就是說:低版本的就不支持了,所以這里最低要求就是1.2.10+。
l 第一種方法就是:
l (1)啟動類繼承extends WebMvcConfigurerAdapter
l (2)覆蓋方法configureMessageConverters
package com.kfit; import java.util.List; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; /** * 在這里我們使用@SpringBootApplication指定這是一個 spring boot的應用程序. */ //extends WebMvcConfigurerAdapter @SpringBootApplication publicclass App extends WebMvcConfigurerAdapter{ @Override publicvoid configureMessageConverters(List<HttpMessageConverter<?>>converters) { super.configureMessageConverters(converters); // 1、需要先定義一個 convert 轉換消息的對象; FastJsonHttpMessageConverter fastConverter=new FastJsonHttpMessageConverter(); //2、添加fastJson 的配置信息,比如:是否要格式化返回的json數據; FastJsonConfig fastJsonConfig=new FastJsonConfig(); fastJsonConfig.setSerializerFeatures( SerializerFeature.PrettyFormat ); //3、在convert中添加配置信息. fastConverter.setFastJsonConfig(fastJsonConfig); //4、將convert添加到converters當中. converters.add(fastConverter); } /** * 這是springloader的配置方式:-javaagent:.\lib\springloaded-1.2.4.RELEASE.jar -noverify * @param args */ publicstaticvoid main(String[] args) { /* * 在main方法進行啟動我們的應用程序. */ SpringApplication.run(App.class, args); } } |
第二種方法
l (1)在App.java啟動類中,注入Bean : HttpMessageConverters
package com.kfit; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.web.HttpMessageConverters; import org.springframework.context.annotation.Bean; import org.springframework.http.converter.HttpMessageConverter; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; /** * 在這里我們使用@SpringBootApplication指定這是一個 spring boot的應用程序. */ @SpringBootApplication public class App{ /** * 在這里我們使用 @Bean注入 fastJsonHttpMessageConvert * @return */ @Bean public HttpMessageConverters fastJsonHttpMessageConverters() { // 1、需要先定義一個 convert 轉換消息的對象; FastJsonHttpMessageConverter fastConverter=new FastJsonHttpMessageConverter(); //2、添加fastJson 的配置信息,比如:是否要格式化返回的json數據; FastJsonConfig fastJsonConfig=new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); //3、在convert中添加配置信息. fastConverter.setFastJsonConfig(fastJsonConfig); HttpMessageConverter<?> converter=fastConverter; return new HttpMessageConverters(converter); } /** * 這是springloader的配置方式:-javaagent:.\lib\springloaded-1.2.4.RELEASE.jar -noverify * @param args */ public static void main(String[] args) { /* * 在main方法進行啟動我們的應用程序. */ SpringApplication.run(App.class, args); } } |
l 問題的提出:
l 在編寫代碼的時候,你會發現我們只是簡單把打印信息改變了,就需要重新部署,如果是這樣的編碼方式,那么我們估計一天下來就真的是打幾個Hello World就下班了。那么如何解決熱部署的問題呢?那就是springloaded
l 在pom.xml文件添加依賴包:
<!-- 在這里添加springloaderplugin--> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> <version>1.2.4.RELEASE</version> </dependency> </dependencies> <executions> <execution> <goals> <goal>repackage</goal> </goals> <configuration> <classifier>exec</classifier> </configuration> </execution> </executions> </plugin> |
運行方法一:使用spring-boot:run
運行方法二:
l 如果使用的run as – java application的話,那么還需要做一些處理。
l 把spring-loader-1.2.4.RELEASE.jar下載下來,放到項目的lib目錄中,然后把IDEA的run參數里VM參數設置為:
l -javaagent:.\lib\springloaded-1.2.4.RELEASE.jar -noverify
l 然后啟動就可以了,這樣在run as的時候,也能進行熱部署
l 問題的提出:
l 通過使用springloaded進行熱部署,但是些代碼修改了,并不會進行熱部署。
l spring-boot-devtools 是一個為開發者服務的一個模塊,其中最重要的功能就是自動應用代碼更改到最新的App上面去。原理是在發現代碼有更改之后,重新啟動應用,但是速度比手動停止后再啟動還要更快,更快指的不是節省出來的手工操作的時間。
l 其深層原理是使用了兩個ClassLoader,一個Classloader加載那些不會改變的類(第三方Jar包),另一個ClassLoader加載會更改的類,稱為 restart ClassLoader
l ,這樣在有代碼更改的時候,原來的restart ClassLoader 被丟棄,重新創建一個restart ClassLoader,由于需要加載的類相比較少,所以實現了較快的重啟時間(5秒以內)。
l 添加依賴包:
<!-- spring boot devtools 依賴包. --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <scope>true</scope> </dependency> <!-- 這是spring boot devtoolplugin --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!--fork : 如果沒有該項配置,肯呢個devtools不會起作用,即應用不會restart --> <fork>true</fork> </configuration> </plugin> |
說明
l 1. devtools會監聽classpath下的文件變動,并且會立即重啟應用(發生在保存時機),注意:因為其采用的虛擬機機制,該項重啟是很快的。
l 2. devtools可以實現頁面熱部署(即頁面修改后會立即生效,這個可以直接在application.properties文件中配置spring.thymeleaf.cache=false來實現(這里注意不同的模板配置不一樣)。
l 修改類-->保存:應用會重啟
l 修改配置文件-->保存:應用會重啟
l 修改頁面-->保存:應用會重啟,頁面會刷新(原理是將spring.thymeleaf.cache設為false)
l 對應的spring-boot版本是否正確,這里使用的是1.4.1版本;
l 是否加入plugin以及屬性<fork>true</fork>
l Eclipse Project 是否開啟了Build Automatically(我自己就在這里栽了坑,不知道為什么我的工具什么時候關閉了自動編譯的功能)。
l 如果設置SpringApplication.setRegisterShutdownHook(false),則自動重啟將不起作用。
什么是JPA?
l JPA全稱Java Persistence API.JPA通過JDK 5.0注解或XML描述對象-關系表的映射關系,并將運行期的實體對象持久化到數據庫中。百度百科JPA
什么是JPA?
l 在上面只是一個JPA的定義,我們看看另外一段更能看出是什么的描述:
l JPA(Java Persistence API)是Sun官方提出的Java持久化規范。它為Java開發人員提供了一種對象/關系映射工具來管理Java應用中的關系數據。
什么是JPA?
l 持久化(Persistence),即把數據(如內存中的對象)保存到可永久保存的存儲設備中(如磁盤)。持久化的主要應用是將內存中的對象存儲在的數據庫中,或者存儲在磁盤文件中、XML數據文件中等等。
l 持久化是將程序數據在持久狀態和瞬時狀態間轉換的機制。
l JDBC就是一種持久化機制。文件IO也是一種持久化機制。
什么是JPA?
l “規范”: 所謂的規范意指明文規定或約定俗成的標準。如:道德規范、技術規范,公司管理規范。
l 那么“持久化規范”就是Sun針對持久化這一層操作指定的規范,如果沒有指定JPA規范,那么新起的框架就隨意按照自己的標準來了,那我們開發人員就沒法把我們的經歷全部集中在我們的業務層上,而是在想如何進行兼容,這種情況有點像Android開發,Android本身有官方的SDK,但是由于SDK過于開源了,結果導致很多廠商基于SDK二次開發,但是兼容性就不是很好,最好的例子就是Android的頭像上傳,就是一件很煩人的事情。好了,JPA就介紹到這里。
什么是Hibernate?
l 這里引用百度百科的話hibernate:
l Hibernate是一個開放源代碼的對象關系映射框架,它對JDBC進行了非常輕量級的對象封裝,它將POJO與數據庫表建立映射關系,是一個全自動的orm框架,hibernate可以自動生成SQL語句,自動執行,使得Java程序員可以隨心所欲的使用對象編程思維來操縱數據庫。 Hibernate可以應用在任何使用JDBC的場合,既可以在Java的客戶端程序使用,也可以在Servlet/JSP的Web應用中使用,最具革命意義的是,Hibernate可以在應用EJB的J2EE架構中取代CMP,完成數據持久化的重任。
什么是Hibernate?
l 那么是ORM呢? ORM是對象關系映射的意思,英語:Object Relational Mapping簡稱ORM,是一種程序技術,用于實現面向對象編程語言里不同系統類型的系統之間的數據轉換。好了,更多的概念需要自己去挖掘,這里只是拋裝引玉下。
什么是Spring Data?
l Spring Data是一個用于簡化數據庫訪問,并支持云服務的開源框架。其主要目標是使得數據庫的訪問變得方便快捷,并支持map-reduce框架和云計算數據服務。此外,它還支持基于關系型數據庫的數據服務,如Oracle RAC等。對于擁有海量數據的項目,可以用Spring Data來簡化項目的開發,就如Spring Framework對JDBC、ORM的支持一樣,Spring Data會讓數據的訪問變得更加方便。
什么是Spring Data JPA?
l 我們先看一個描述:
l Spring Data JPA能干什么
l 可以極大的簡化JPA的寫法,可以在幾乎不用寫實現的情況下,實現對數據的訪問和操作。除了CRUD外,還包括如分頁、排序等一些常用的功能。
l 首先我們需要清楚的是Spring Data是一個開源框架,在這個框架中Spring Data JPA只是這個框架中的一個模塊,所以名稱才叫Spring Data JPA。如果單獨使用JPA開發,你會發現這個代碼量和使用JDBC開發一樣有點煩人,所以Spring Data JPA的出現就是為了簡化JPA的寫法,讓你只需要編寫一個接口繼承一個類就能實現CRUD操作了。
JPA/Hibernate 關系?
l 我們先看下別人的描述:
l JPA是一種規范,而Hibernate是它的一種實現。除了Hibernate,還有EclipseLink(曾經的toplink),OpenJPA等可供選擇,所以使用Jpa的一個好處是,可以更換實現而不必改動太多代碼。
l
<!-- 添加MySQL數據庫驅動依賴包. --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- 添加Spring-data-jpa依賴. --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> |
######################################################## ###datasource -- \u6307\u5b9amysql\u6570\u636e\u5e93\u8fde\u63a5\u4fe1\u606f. ######################################################## spring.datasource.url=jdbc:mysql: spring.datasource.username= spring.datasource.password= spring.datasource.driverClassName=com.mysql.jdbc.Driver spring.datasource.max-active=20 spring.datasource.max-idle=8 spring.datasource.min-idle=8 spring.datasource.initial-size=10 ######################################################## ### Java Persistence Api -- Spring jpa\u7684\u914d\u7f6e\u4fe1\u606f. ######################################################## # Specify the DBMS spring.jpa.database=MYSQL # Show or not log for each sql query spring.jpa.show-sql=true # Hibernateddl auto (create, create-drop, update) spring.jpa.hibernate.ddl-auto=update # Naming strategy #[org.hibernate.cfg.ImprovedNamingStrategy #org.hibernate.cfg.DefaultNamingStrategy] spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy # stripped before adding them to the entity manager) spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect |
(1) 創建實體類Demo,如果已經存在,可以忽略。
package com.kfit.demo.bean; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; /** * 創建了一個實體類。 * 如何持久化呢? * 1、使用@Entity進行實體類的持久化操作,當JPA檢測到我們的實體類當中有 * @Entity 注解的時候,會在數據庫中生成對應的表結構信息。 * 如何指定主鍵以及主鍵的生成策略? * 2、使用@Id指定主鍵. */ @Entity public class Cat { /** * 使用@Id指定主鍵. * 使用代碼@GeneratedValue(strategy=GenerationType.AUTO) * 指定主鍵的生成策略,mysql默認的是自增長。 * */ @Id @GeneratedValue(strategy=GenerationType.AUTO) private int id;//主鍵. private String catName;//. cat_name private int catAge;// cat_age; public int getId() { return id; } public void setId(int id) { this.id=id; } public String getCatName() { return catName; } public void setCatName(String catName) { this.catName=catName; } public int getCatAge() { return catAge; } public void setCatAge(int catAge) { this.catAge=catAge; } } |
(2) 創建jpa repository類操作持久化(CrudRepository)。
package com.kfit.demo.repository; import org.springframework.data.repository.CrudRepository; import com.kfit.demo.bean.Cat; public interface CatRepository extends CrudRepository<Cat, Integer>{ } |
(3) 創建service類。
package com.kfit.demo.service; import javax.annotation.Resource; import javax.transaction.Transactional; import org.springframework.stereotype.Service; import com.kfit.demo.bean.Cat; import com.kfit.demo.repository.CatRepository; @Service public class CatService { @Resource private CatRepository catRepository; /** * save,update ,delete 方法需要綁定事務. * 使用@Transactional進行事務的綁定. * @param cat */ //保存數據. @Transactional public void save(Cat cat){ catRepository.save(cat); } //刪除數據》 @Transactional public void delete(int id){ catRepository.delete(id); } //查詢數據. public Iterable<Cat> getAll(){ return catRepository.findAll(); } } |
(4) 創建restful請求類。
package com.kfit.demo.controller; import javax.annotation.Resource; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.kfit.demo.bean.Cat; import com.kfit.demo.service.CatService; @RestController @RequestMapping("/cat") public class CatController { @Resource private CatService catService; @RequestMapping("/save") public String save(){ Cat cat=new Cat(); cat.setCatName("jack"); cat.setCatAge(3); catService.save(cat); return "save ok."; } @RequestMapping("/delete") public String delete(){ catService.delete(1); return "delete ok"; } @RequestMapping("/getAll") public Iterable<Cat> getAll(){ return catService.getAll(); } } |
(5) 測試;
Repository 接口是 Spring Data 的一個核心接口,它不提供任何方法,開發者需要在自己定義的接口中聲明需要的方法 :
public interface Repository<T, ID extends Serializable> { }
有這么幾點需要強調下:
1. Repository是一個空接口,即是一個標記接口;
2. 若我們定義的接口繼承了Repository,則該接口會被IOC容器識別為一個Repository Bean納入到IOC容器中,進而可以在該接口中定義滿足一定規范的方法。
3. 實際上也可以通過@RepositoryDefinition,注解來替代繼承Repository接口。
4. 查詢方法以find | read | get開頭;
5. 涉及查詢條件時,條件的屬性用條件關鍵字連接,要注意的是條件屬性以首字母大寫。
6.使用@Query注解可以自定義JPQL語句實現更靈活的查詢。
CrudRepository 接口提供了最基本的對實體類的添刪改查操作
--T save(T entity);//保存單個實體
--Iterable<T> save(Iterable<? extends T> entities);//保存集合
--T findOne(ID id);//根據id查找實體
--boolean exists(ID id);//根據id判斷實體是否存在
--Iterable<T> findAll();//查詢所有實體,不用或慎用!
--long count();//查詢實體數量
--void delete(ID id);//根據Id刪除實體
--void delete(T entity);//刪除一個實體
--void delete(Iterable<? extends T> entities);//刪除一個實體的集合
--void deleteAll();//刪除所有實體,不用或慎用!
該接口提供了分頁與排序功能
--Iterable<T> findAll(Sort sort); //排序
--Page<T> findAll(Pageable pageable); //分頁查詢(含排序功能)
package com.kfit.demo.repository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; import com.kfit.demo.bean.Cat; public interface Cat2Repository extends PagingAndSortingRepository<Cat, Integer>{ /** * 1/ 查詢方法 以 get | find | read 開頭. * 2/ 涉及查詢條件時,條件的屬性用條件關鍵字連接,要注意的是條件屬性以首字母大寫。 */ //根據catName進行查詢 : 根據catName進行查詢. public Cat findByCatName(String catName); /** * 如何編寫JPQL語句, * Hibernate -- HQL語句. * JPQL 語句 和HQL語句是類似的. */ @Query("from Cat where catName=:cn") public Cat findMyCatName(@Param("cn")String catName); } |
JpaRepository:查找所有實體,排序、查找所有實體,執行緩存與數據庫同步
JpaSpecificationExecutor:不屬于Repository體系,實現一組 JPA Criteria 查詢相關的方法,封裝 JPA Criteria 查詢條件。通常使用匿名內部類的方式來創建該接口的對象。
自定義 Repository:可以自己定義一個MyRepository接口。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
如果在JPA已經加入的話,則可以不用引入以上的配置。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
那么只需要在需要使用的類中加入:
@Resource
private JdbcTemplate jdbcTemplate;
聲明為:@Repository,引入JdbcTemplate
public Demo getById(long id){
String sql="select *from Demo where id=?";
RowMapper<Demo> rowMapper=new BeanPropertyRowMapper<Demo>(Demo.class);
return jdbcTemplate.queryForObject(sql, rowMapper,id);
}
package com.kfit.demo.dao; import javax.annotation.Resource; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import com.kfit.demo.bean.Cat; /** * 使用@Repository注解,標注這是一個持久化操作對象. */ @Repository publicclass CatDao { @Resource private JdbcTemplate jdbcTemplate; public Cat selectByCatName(String catName){ /** * 1、定義一個Sql語句; * 2、定義一個RowMapper. * 3、執行查詢方法. */ String sql="select * from cat where cat_name=?"; RowMapper<Cat>rowMapper=new BeanPropertyRowMapper<>(Cat.class); Cat cat=jdbcTemplate.queryForObject(sql, new Object[]{catName}, rowMapper); returncat; } } |
@Resource
private DemoDao demoDao;
public void save(Demo demo){
demoDao.save(demo);
}
package com.kfit.demo.service; import javax.annotation.Resource; import javax.transaction.Transactional; import org.springframework.stereotype.Service; import com.kfit.demo.bean.Cat; import com.kfit.demo.dao.CatDao; import com.kfit.demo.repository.Cat2Repository; import com.kfit.demo.repository.CatRepository; @Service public class CatService { @Resource private CatRepository catRepository; @Resource private Cat2Repository cat2Repository; @Resource private CatDao catDao; /** * save,update ,delete 方法需要綁定事務. * 使用@Transactional進行事務的綁定. * @param cat */ //保存數據. @Transactional public void save(Cat cat){ catRepository.save(cat); } //刪除數據》 @Transactional public void delete(int id){ catRepository.delete(id); } //查詢數據. public Iterable<Cat> getAll(){ return catRepository.findAll(); } public Cat findByCatName(String catName){ return cat2Repository.findByCatName(catName); } public Cat findByCatName2(String catName){ return cat2Repository.findMyCatName(catName); } public Cat selectByCatName(String catName){ return catDao.selectByCatName(catName); } } |
@Resource
private DemoService demoService;
@RequestMapping("/getById")
public Demo getById(long id){
return demoService.getById(id);
}
package com.kfit.demo.controller; import javax.annotation.Resource; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.kfit.demo.bean.Cat; import com.kfit.demo.service.CatService; @RestController @RequestMapping("/cat") public class CatController { @Resource private CatService catService; @RequestMapping("/save") public String save(){ Cat cat=new Cat(); cat.setCatName("jack"); cat.setCatAge(3); catService.save(cat); return "save ok."; } @RequestMapping("/delete") public String delete(){ catService.delete(1); return "delete ok"; } @RequestMapping("/getAll") public Iterable<Cat> getAll(){ return catService.getAll(); } @RequestMapping("/findByCatName") public Cat findByCatName(String catName){ return catService.findByCatName(catName); } @RequestMapping("/findByCatName2") public Cat findByCatName2(String catName){ System.out.println("CatController.findByCatName2()"); return catService.findByCatName2(catName); } //http://127.0.0.1:8080/cat/selectByCatName?catName=jack1 @RequestMapping("/selectByCatName") public Cat selectByCatName(String catName){ return catService.selectByCatName(catName); } } |
新建一個類GlobalDefaultExceptionHandler,
在class注解上@ControllerAdvice,
在方法上注解上@ExceptionHandler(value=Exception.class),具體代碼如下:
package com.kfit.config; import javax.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; /** * 1、新建一個Class,這里取名為GlobalDefaultExceptionHandler * 2、在class上添加注解,@ControllerAdvice; * 3、在class中添加一個方法 * 4、在方法上添加@ExcetionHandler攔截相應的異常信息; * 5、如果返回的是View -- 方法的返回值是ModelAndView; * 6、如果返回的是String或者是Json數據,那么需要在方法上添加@ResponseBody注解. * */ @ControllerAdvice public class GlobalDefaultExceptionHandler { @ExceptionHandler(Exception.class) @ResponseBody public String defaultExceptionHandler(HttpServletRequest req,Exception e){ //是返回的String. //ModelAndView -- 介紹 模板引擎...? // ModelAndView mv=new ModelAndView(); // mv.setViewName(viewName); return "對不起,服務器繁忙,請稍后再試!"; } } |
(1)404 -- 確定地址是否輸入正確,,此路徑非彼路徑
(2)404 -- 是否用對注解,此注解非彼注解
(3)404 -- 包路徑是否正確,此包非彼包
(4)404 -- 確認類包是否正確,此類包非彼類包
1、確認訪問地址是否正確: (1)確認端口號,默認是8080,這個可以在啟動的控制臺進行查看; (2)確認訪問的URI地址是否填寫正確,這個在啟動的控制臺查看是否被映射了。 2、確定注解是否正確,使用@RestController而不是@Controller, 另外@RestController等價于@Controller和@ResponseBody; 3、確定包的路徑是否正確 我們需要知道的是:Spring Boot默認情況下可以掃描到的是 @SpringBootApplication所在的類的同包或者子包下的類。 4、 確定類引入的包的路徑是否正確 @RestController:import org.springframework.web.bind.annotation.RestController @RequestMapping("/helo33"):import org.springframework.web.bind.annotation.RequestMapping |
Spring boot 默認端口是8080,如果想要進行更改的話,只需要修改applicatoin.properties文件,在配置文件中加入:
server.port=8081
在application.properties進行配置:
server.context-path=/spring-boot
訪問地址就是http://ip:port/spring-boot
#server.port=8080
#server.address=# bind to a specific NIC
#server.session-timeout=# session timeout in seconds
#the context path, defaults to '/'
#server.context-path=/spring-boot
#server.servlet-path=# the servlet path, defaults to '/'
#server.tomcat.access-log-pattern=# log pattern of the access log
#server.tomcat.access-log-enabled=false # is access logging enabled
#server.tomcat.protocol-header=x-forwarded-proto # ssl forward headers
#server.tomcat.remote-ip-header=x-forwarded-for
#server.tomcat.basedir=/tmp # base dir (usually not needed, defaults to tmp)
#server.tomcat.background-processor-delay=30; # in seconds
#server.tomcat.max-threads=0 # number of threads in protocol handler
#server.tomcat.uri-encoding=UTF-8 # character encoding to use for URL decoding
在pom.xml加入thymeleaf的依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
########################################################
###THYMELEAF (ThymeleafAutoConfiguration)
########################################################
#spring.thymeleaf.prefix=classpath:/templates/
#spring.thymeleaf.suffix=.html
#spring.thymeleaf.mode=HTML5
#spring.thymeleaf.encoding=UTF-8
# ;charset=<encoding> is added
#spring.thymeleaf.content-type=text/html
# set to false for hot refresh
spring.thymeleaf.cache=false
編寫模板文件src/main/resouces/templates/hello.html:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Hello World!</title>
</head>
<body>
<h1 th:inline="text">Hello.v.2</h1>
<p th:text="${hello}"></p>
</body>
</html>
<!DOCTYPEhtml> <html> <head> <metacharset="UTF-8"/> <title>Insert title here</title> </head> <body> <h1> Hello,thymeleaf <br/> This is my first thymeleaf demo. <hr/> welcome <spanth:text="${name}"></span> </h1> </body> </html> |
@Controller
public class TemplateController {
/**
* 返回html模板.
*/
@RequestMapping("/helloHtml")
public String helloHtml(Map<String,Object> map){
map.put("hello","from TemplateController.helloHtml");
return "/helloHtml";
}
}
package com.kfit; import java.util.Map; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * 注意: * 1.在Thymeleaf 模板文件中,標簽是需要閉合的,3.0之前是需要閉合的 * 2. thymeleaf 3.0+ 是可以不強制要求閉合的。 * 3. 支持同時使用多個模板引擎,比如:thymeleaf和freemarker 可以并存。 */ @Controller @RequestMapping("/templates") public class TemplatesController { /** * 映射地址是:/templates/hello * @return */ @RequestMapping("/hello") public String hello(Map<String,Object> map){ map.put("name","Andy"); return "/hello"; } @RequestMapping("/hello2") public ModelAndView hello(Map<String,Object>map){ //返回的是ModelAndView對象; ModelAndView mv=new ModelAndView("hello"); mv.addObject("name","Andy"); returnmv; } @RequestMapping("/helloFtl") public String helloFtl(Map<String,Object> map){ map.put("name","Andy"); return "helloFtl"; } } |
在pom.xml中引入freemarker
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
########################################################
###FREEMARKER (FreeMarkerAutoConfiguration)
########################################################
spring.freemarker.allow-request-override=false
spring.freemarker.cache=true
spring.freemarker.check-template-location=true
spring.freemarker.charset=UTF-8
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=false
spring.freemarker.expose-session-attributes=false
spring.freemarker.expose-spring-macro-helpers=false
#spring.freemarker.prefix=
#spring.freemarker.request-context-attribute=
#spring.freemarker.settings.*=
#spring.freemarker.suffix=.ftl
#spring.freemarker.template-loader-path=classpath:/templates/ #comma-separated list
#spring.freemarker.view-names=# whitelist of view names that can be resolved
<!DOCTYPE html>
<html xmlns=" 1999/xhtml" xmlns:th=" thymeleaf.org"
xmlns:sec=" /thymeleaf-extras-springsecurity3">
<head>
<title>Hello World!</title>
</head>
<body>
<h1>Hello.v.2</h1>
<p>${hello}</p>
</body>
</html>
@RequestMapping("/helloFtl")
public String helloFtl(Map<String,Object> map){
map.put("hello","from TemplateController.helloFtl");
return "/helloFtl";
}
使用Eclipse新建一個Maven Web Project ,項目取名為:
spring-boot-jsp
<projectxmlns=" POM/4.0.0"xmlns:xsi=" /2001/XMLSchema-instance" xsi:schemaLocation=" /POM/4.0.0 /maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.kfit</groupId> <artifactId>spring-boot-jsp</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>spring-boot-jspMavenWebapp</name> <url>http://maven.apache.org</url> <!-- spring boot parent節點,引入這個之后,在下面和spring boot相關的就不需要引入版本了; --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.1.RELEASE</version> </parent> <properties> <!-- 指定一下jdk的版本 ,這里我們使用jdk 1.8 ,默認是1.6 --> <java.version>1.8</java.version> </properties> <dependencies> <!-- web支持: 1、web mvc; 2、restful; 3、jackjson支持; 4、aop ........ --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- servlet 依賴. --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> </dependency> <!-- JSTL(JSP Standard Tag Library,JSP標準標簽庫)是一個不斷完善的開放源代碼的JSP標簽庫,是由apache的jakarta小組來維護的。 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!-- tomcat 的支持.--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>spring-boot-jsp</finalName> </build> </project> |
添加src/main/resources/application.properties:
# 頁面默認前綴目錄
spring.mvc.view.prefix=/WEB-INF/jsp/
# 響應頁面默認后綴
spring.mvc.view.suffix=.jsp
# 自定義屬性,可以在Controller中讀取
application.hello=Hello Angel From application
訪問: //helloJsp
@Controller
public class HelloController {
private String hello;
@RequestMapping("/helloJsp")
public String helloJsp(Map<String,Object> map){
System.out.println("HelloController.helloJsp().hello=hello");
map.put("hello", hello);
return "helloJsp";
}
}
在 src/main 下面創建 webapp/WEB-INF/jsp 目錄用來存放我們的jsp頁面:helloJsp.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"" /TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
helloJsp
<hr>
${hello}
</body>
</html>
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
注意
特別說明:針對el表達式,類似${hello} 這個對于servlet的版本是有限制的,2.4版本版本以下是不支持的,是無法進行識別的,請注意。
(1)新建maven project;
(2)在pom.xml文件中引入相關依賴;
(3)創建啟動類App.java
(4)在application.properties添加配置文件;
(5)編寫Demo測試類;
(6)編寫DemoMapper;
(7)編寫DemoService
(8)編寫DemoController;
(9)加入PageHelper
(10)獲取自增長ID;
新建一個maven project,取名為:spring-boot-mybatis
<projectxmlns=" /POM/4.0.0"xmlns:xsi=" /2001/XMLSchema-instance" xsi:schemaLocation=" POM/4.0.0 /xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.kfit</groupId> <artifactId>spring-boot-mybatis</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-boot-mybatis</name> <url>http://maven.apache.org</url> <!-- spring boot parent節點,引入這個之后,在下面和spring boot相關的就不需要引入版本了; --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.1.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- 指定一下jdk的版本 ,這里我們使用jdk 1.8 ,默認是1.6 --> <java.version>1.8</java.version> </properties> <dependencies> <!-- web支持: 1、web mvc; 2、restful; 3、jackjson支持; 4、aop ........ --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- mysql 數據庫驅動. --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- spring-boot mybatis依賴: 請不要使用1.0.0版本,因為還不支持攔截器插件, --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.2.0</version> </dependency> <!-- MyBatis提供了攔截器接口,我們可以實現自己的攔截器, 將其作為一個plugin裝入到SqlSessionFactory中。 Github上有位開發者寫了一個分頁插件,我覺得使用起來還可以,挺方便的。 Github項目地址: https://github.com/pagehelper/Mybatis-PageHelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>4.1.0</version> </dependency> </dependencies> </project> |
package com.kfit.spring_boot_mybatis; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * Spring Boot啟動類. */ @SpringBootApplication @MapperScan("com.kfit.*")//掃描:該包下相應的class,主要是MyBatis的持久化類. public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } } |
########################################################
###datasource
########################################################
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.max-active=20
spring.datasource.max-idle=8
spring.datasource.min-idle=8
spring.datasource.initial-size=10
package com.kfit.spring_boot_mybatis; publicclass Demo { privatelongid; private String name; publiclong getId() { returnid; } publicvoid setId(longid) { this.id=id; } public String getName() { returnname; } publicvoid setName(String name) { this.name=name; } } |
package com.kfit.spring_boot_mybatis; import java.util.List; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Select; public interface DemoMappper { //#{name}:參數占位符 @Select("select * from Demo where name=#{name}") public List<Demo> likeName(String name); @Select("select * from Demo where id=#{id}") public Demo getById(long id); @Select("select name from Demo where id=#{id}") public String getNameById(long id); /** * 保存數據. */ @Insert("insert into Demo(name) values(#{name})") @Options(useGeneratedKeys=true,keyProperty="id",keyColumn="id") public void save(Demo demo); } |
package com.kfit.spring_boot_mybatis; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class DemoService { @Autowired private DemoMappper demoMappper; public List<Demo> likeName(String name){ return demoMappper.likeName(name); } @Transactional//添加事務. public void save(Demo demo){ demoMappper.save(demo); } } |
package com.kfit.spring_boot_mybatis; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.github.pagehelper.PageHelper; @RestController public class DemoController { @Autowired private DemoService demoService; @RequestMapping("/likeName") public List<Demo> likeName(String name){ /* * 第一個參數:第幾頁 第二個參數:每頁獲取的條數. */ PageHelper.startPage(1, 2); return demoService.likeName(name); } @RequestMapping("/save") public Demo save(){ Demo demo=new Demo(); demo.setName(""); demoService.save(demo); return demo; } } |
//運行訪問: /likeName?name=張三 就可以看到返回的數據了
@Configuration
public class MyBatisConfiguration {
@Bean
public PageHelper pageHelper() {
System.out.println("MyBatisConfiguration.pageHelper()");
PageHelper pageHelper=new PageHelper();
Properties p=new Properties();
p.setProperty("offsetAsPageNum", "true");
p.setProperty("rowBoundsWithCount", "true");
p.setProperty("reasonable", "true");
pageHelper.setProperties(p);
return pageHelper;
}
}
@Insert("insert into Demo(name,password) values(#{name},#{password})")
public long save(Demo name);
@Options(useGeneratedKeys=true, keyProperty="id", keyColumn="id")
@EnableAsync//開啟異步注解功能 @EnableScheduling//開啟基于注解的定時任務 @SpringBootApplication publicclass Springboot04TaskApplication { publicstaticvoid main(String[] args) { SpringApplication.run(Springboot04TaskApplication.class, args); } } |
2)代碼
@Service publicclass ScheduledService { /** * second(秒), minute(分), hour(時), day of month(日), month(月), day of week(周幾). * 0 * * * * MON-FRI * 【0 0/5 14,18 * * ?】 每天14點整,和18點整,每隔5分鐘執行一次 * 【0 15 10 ? * 1-6】 每個月的周一至周六10:15分執行一次 * 【0 0 2 ? * 6L】每個月的最后一個周六凌晨2點執行一次 * 【0 0 2 LW * ?】每個月的最后一個工作日凌晨2點執行一次 * 【0 0 2-4 ? * 1#1】每個月的第一個周一凌晨2點到4點期間,每個整點都執行一次; */ // @Scheduled(cron="0 * * * * MON-SAT") //@Scheduled(cron="0,1,2,3,4 * * * * MON-SAT") // @Scheduled(cron="0-4 * * * * MON-SAT") @Scheduled(cron="0/4 * * * * MON-SAT") //每4秒執行一次 publicvoid hello(){ System.out.println("hello ... "); } } @Service publicclass AsyncService { //告訴Spring這是一個異步方法 @Async publicvoid hello(){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("處理數據中..."); } } |
命名為demo-spring-boot-starter
下圖為工程目錄結構
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=" /POM/4.0.0" xmlns:xsi=" /2001/XMLSchema-instance"
xsi:schemaLocation=" /POM/4.0.0 /xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<groupId>com.demo</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>0.0.1-RELEASE</version>
<name>demo-spring-boot-starter</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
@ConfigurationProperties(prefix="demo") 它可以把相同前綴的配置信息通過配置項名稱映射成實體類,比如我們這里指定 prefix="demo" 這樣,我們就能將以demo為前綴的配置項拿到了。
ps:其實這個注解很強大,它不但能映射成String或基本類型的變量。還可以映射為List,Map等數據結構。
package com.demo.starter.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 描述:配置信息 實體
*
* @Author shf
* @Date 2019/5/7 22:08
* @Version V1.0
**/
@ConfigurationProperties(prefix="demo")
public class DemoProperties {
private String sayWhat;
private String toWho;
public String getSayWhat() {
return sayWhat;
}
public void setSayWhat(String sayWhat) {
this.sayWhat=sayWhat;
}
public String getToWho() {
return toWho;
}
public void setToWho(String toWho) {
this.toWho=toWho;
}
}
package com.demo.starter.service;
/**
* 描述:隨便定義一個Service
*
* @Author shf
* @Date 2019/5/7 21:59
* @Version V1.0
**/
public class DemoService {
public String sayWhat;
public String toWho;
public DemoService(String sayWhat, String toWho){
this.sayWhat=sayWhat;
this.toWho=toWho;
}
public String say(){
return this.sayWhat + "! " + toWho;
}
}
這里,我們將DemoService類定義為一個Bean,交給Ioc容器。
▲ @Configuration 注解就不多說了。
▲ @EnableConfigurationProperties 注解。該注解是用來開啟對3步驟中 @ConfigurationProperties 注解配置Bean的支持。也就是@EnableConfigurationProperties注解告訴Spring Boot 能支持@ConfigurationProperties。
當然了,也可以在 @ConfigurationProperties 注解的類上添加 @Configuration 或者 @Component 注解
▲ @ConditionalOnProperty 注解控制 @Configuration 是否生效。簡單來說也就是我們可以通過在yml配置文件中控制 @Configuration 注解的配置類是否生效。
package com.demo.starter.config;
import com.demo.starter.properties.DemoProperties;
import com.demo.starter.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 描述:配置類
*
* @Author shf
* @Date 2019/5/7 21:50
* @Version V1.0
**/
@Configuration
@EnableConfigurationProperties(DemoProperties.class)
@ConditionalOnProperty(
prefix="demo",
name="isopen",
havingValue="true"
)
public class DemoConfig {
@Autowired
private DemoProperties demoProperties;
@Bean(name="demo")
public DemoService demoService(){
return new DemoService(demoProperties.getSayWhat(), demoProperties.getToWho());
}
}
如圖,新建META-INF文件夾,然后創建spring.factories文件,
在該文件中加入如下配置,該配置指定上步驟中定義的配置類為自動裝配的配置。(筆者努力最近把自動裝配的博客寫出來)
#-------starter自動裝配---------
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.demo.starter.config.DemoConfig
1000個線程并發還能跑,5000個線程的時候出現這種問題,查后臺debug日志,發現redis 線程池不夠。剛開始設置的是:
# redis 配置文件 #redis redis.host=127.0.0.1 redis.port=6379 redis.timeout=300 等待時間 10s改為300s redis.password=123456 redis.poolMaxTotal=1000 連接數,剛開始最大連接數 設置為100. redis.poolMaxIdle=500 最大空閑連接數 100改成500 redis.poolMaxWait=300
順便也改了一下jdbc 的連接池參數,最大空閑和最大連接數都改成1000.在測一下。可以
spring.datasource.filters=stat spring.datasource.maxActive=1000 spring.datasource.initialSize=100 spring.datasource.maxWait=60000 spring.datasource.minIdle=500 spring.datasource.timeBetweenEvictionRunsMillis=60000 spring.datasource.minEvictableIdleTimeMillis=300000 spring.datasource.validationQuery=select 'x' spring.datasource.testWhileIdle=true spring.datasource.testOnBorrow=false spring.datasource.testOnReturn=false spring.datasource.poolPreparedStatements=true spring.datasource.maxOpenPreparedStatements=20
后來看代碼發現,判斷庫存用的是if(stock==0 ) 拋出異常。應該用stock<0,因為 若此時同時2個線程進來,就永遠小于0,后面的業務邏輯都可以執行。
第一次壓力測試的時候,5000個線程,分別取不同的token(sessionId),同時訪問 秒殺這個接口,商品個數只放了20個。結果出現最后商品數量變負的問題。
接口限流防刷的時候,通過計數器限流,如果超過某個閾值,向前端返回一個codeMsg對象用于顯示的時候,顯示的是String是亂碼的問題,之前由于一直返回都是json 格式,都是封裝好在data里。
這次返回是直接通過輸出流直接寫到response直接返回字節數組的,而不是spring controller 返回數據(springboot 默認utf-8),出現亂碼問題,用utf-8編碼,解決。
壓測是利用Jmeter壓測。(Apache開發的基于java的壓測工具)。
壓測具體實現:
1.在數據庫中提前插入5000個用戶密碼(腳本 for循環 id是13000000+i),密碼統一為“123456”,隨機鹽值也是固定的,方便操作。用JDBC存入數據庫。作為5000個備用用戶。
2.然后寫了一個小腳本讓5000個用戶post請求我的登陸接口(login),生成sessionId并存入緩存,并改寫了一下login接口讓其換回sessionId。把這5000個用戶的id和對應sessionid寫到了一個TXT文件里面。
3.最后利用jmeter 創建5000個線程,賬號每個線程攜帶提前寫好的用戶token(sessionId),參數就是商品id和sessionid,商品id確定我要買的票是哪個,sessionid用來獲取用戶信息。(從緩存中拿)
壓測的瓶頸:
qps-126/s----靜態化-250/s---接口優化-860/s.
瓶頸主要是對數據庫的訪問。
1.數據庫讀取,寫入,處理請求的速度。
數據庫讀取寫入加上網絡IO速度很慢,減少對數據庫的訪問,在緩存這一端就屏蔽掉大部分訪問數據庫的請求(Redis預減庫存操作)
2.利用消息隊列,異步業務邏輯的處理速度慢,可以先返回結果,讓其輪詢。
3.利用內存map,減少對Redis服務器的訪問,flag機制。
4.其他想到的但還沒實現
服務器系統的負載均衡+集群
數據庫數據達到1000W以上就很慢,分庫分表
1.首先輸入登陸頁面的url.http://localhost:8080/login/to_login,controller根據map映射返回給html頁,到達登陸頁面
2.整個頁面是一個login表單,包含用戶名和密碼兩個輸入框部分,還有一個登陸按鈕和重置按鈕。
3.在前端,給登陸按鈕綁定一個login()方法,login()方法中會獲取表單中的用戶名和密碼,然后將密碼利用封裝好的md5()函數以及設置的固定鹽值進行拼接,鹽值設置為“1a2b3c”,然后進行MD5算法生成4個32位拼接的散列值作為輸入密碼(用于 網絡傳輸),作為參數傳給后端。(這里的目的主要是第一道加密,防止http明文傳輸,泄漏密碼)。
4.然后ajax異步訪問do_login 接口,參數為用戶名和md5之后的密碼,后端接收到前端傳輸來的參數后,會對用戶名和密碼進行參數校驗,驗證是否為空,是否有格式問題(密碼長度6位以上,用戶名格式11位等等),如果驗證不通過,返回CodeMsg(),封裝好的對應的錯誤信息給前端。
5.如果驗證成功,進入下一步,用戶的登陸,首先通過用戶名取用戶對象信息(先從緩存中取,取不到取數據庫取,取到了將用戶信息存入緩存中,下一次登錄我們可以先從緩存中取用戶,降低數據庫壓力),然后返回一個user對象,再判斷這個user對象是否為空,若是空就拋出異常,不是空的情況說明數據庫中有該用戶,然后根據傳入的密碼和數據中保存的隨機鹽值,進行md5再次拼接,獲得的值若是和數據庫中的密碼一致,那么說明登陸成功。
關鍵點: 6.登陸成功的時候,隨機生成uuid作為sessionId,將其寫入cookie中返回給客戶端,并且將模塊前綴+該用戶id作為key和sessionId 作為值,存入緩存(這里為分布式緩存提供的基礎)。這時候跳轉到 搶票列表頁面,如果密碼不匹配,拋出異常,返回。
短時間的大訪問量 網站服務器 同網站,不同項目部署,/獨立域名 避免對網站造成影響 高并發問題,不停刷新 數據庫 頁面靜態化
同網站,不同項目部署,/獨立域名 避免對網站造成影響 寬帶 同網站,不同項目部署,/獨立域名 避免對網站造成影響 不能提前下單 服務器 url動態化,+隨機數
下單之后的搶的問題 sql 樂觀鎖
大量訪問高并發的應對(主要訪問大量訪問數據庫崩潰)
1.Redis預減庫存減少數據庫訪問
2.map標記減少Redis訪問屏蔽一定的請求減輕緩存壓力
3.消息隊列異步處理
流量削峰 開始搶購的瞬間 大量并發進入,先將請求入隊,若隊列滿了,那么舍棄再入隊的請求返回一個異常
先給前端一個數據返回表示排隊中,再進行后續的業務處理,前端輪詢最后成功或者失敗在顯示業務結果
4.數據庫運行的問題,傳統的sql寫成存儲過程(直接調用),加速sql
5.數據庫里鎖及唯一索引來處理搶的問題。
頁面加載速度
頁面靜態化,緩存在客戶端
CDN服務器
在上表中列出來的解決方案中看出,利用 頁面靜態化、數據靜態化,反向代理 等方法可以避免 帶寬和sql壓力 ,但是隨之而來一個問題,頁面搶單按鈕也不會刷新了,可以把 js 文件單獨放在js服務器上,由另外一臺服務器寫 定時任務 來控制js 推送。
另外還有一個問題,js文件會被大部分瀏覽器緩存,我們可以使用xxx.js?v=隨機數 的方式來避免js被緩存
更為激進的緩存方式(之前可以用將html源碼緩存起來再讀,避免服務器渲染html過程)。
什么是瀏覽器緩存:
簡單來說,瀏覽器緩存就是把一個已經請求過的Web資源(如html頁面,圖片,js,數據等)拷貝一份副本儲存在瀏覽器中。緩存會根據進來的請求保存輸出內容的副本。當下一個請求來到的時候,如果是相同的URL,緩存會根據緩存機制決定是直接使用副本響應訪問請求,還是向源服務器再次發送請求。比較常見的就是瀏覽器會緩存訪問過網站的網頁,當再次訪問這個URL地址的時候,如果網頁沒有更新,就不會再次下載網頁,而是直接使用本地緩存的網頁。只有當網站明確標識資源已經更新,瀏覽器才會再次下載網頁。
頁面靜態化的好處:
我們知道瀏覽器會將html,圖片等靜態數據,緩存到本地,在高并發搶票場景,用戶會通過不斷的刷新頁面來進行搶票操作,這樣帶來Web帶寬的浪費以及服務器的訪問壓力。于是,我們可以通過將搶票頁面做成靜態頁面html頁,其中的票務數據通過ajax異步調用接口來獲取,僅僅交互的是部分數據,減少了帶寬,也加快用戶訪問的速度。
function getDetail() { var goodsId=g_getQueryString("goodsId"); $.ajax({ url : "/goods/to_detail/"+goodsId, type : "GET", success: function (data) { if (data.code==0) {// 訪問后端detail 接口拿到數據 render(data.data);//渲染界面的方法 }else { layer.msg(data.msg) } }, error:function () { layer.msg("客戶端請求有誤!") } }) } function render(detail) { var goodsVo=detail.goodsVo; var miaoshaStatus=detail.miaoshaStatus; var remainSeconds=detail.remainSeconds; var user=detail.user; if (user) { $("#userTip").hide();//沒有就不展示 } //用獲取的參數 放入 對應的模板中 $("#goodsName").text(goodsVo.goodsName); $("#goodsImg").attr("src", goodsVo.goodsImg); $("#startTime").text(new Date(goodsVo.startDate).format("yyyy-MM-dd hh:mm:ss")); $("#remainSeconds").val(remainSeconds); $("#goodsId").val(goodsVo.id); $("#goodsPrice").text(goodsVo.goodsPrice); $("#miaoshaPrice").text(goodsVo.miaoshaPrice); $("#stockCount").text(goodsVo.stockCount); countDown();//調用倒計時 } function countDown() { var remainSeconds=$("#remainSeconds").val(); // var remainSeconds=$("#remainSeconds").val(); var timeout;//定義一個timeout 保存Timeout 值 if (remainSeconds>0){//秒殺未開始 $("#buyButton").attr("disabled",true);/*還沒開始的時候按鈕不讓點*/ $("#miaoshaTip").html("秒殺倒計時:"+remainSeconds+"秒"); /*且做一個倒計時*/ timeout=setTimeout(function () {//setTimeout 為時間到了之后執行 該函數 $("#countDown").text(remainSeconds-1);//將顯示中的值 -1 $("#remainSeconds").val(remainSeconds-1);// remianSeconds 值減一 countDown();//在調用該方法 實現循環 },1000) }else if (remainSeconds==0){//秒殺進行中 $("#buyButton").attr("disabled",false); //當remainSeconds=0 clearTimeout(timeout);//取消timeout 代碼執行 $("#miaoshaTip").html("秒殺進行中!")//修改其中的內容 /**加入秒殺數學驗證碼 功能 * 1.一開始圖形驗證碼和輸入框都是隱藏的 * 2.當秒殺進行的時候,顯示驗證碼和輸入框 * */ $("#verifyCodeImg").attr("src", "/miaosha/verifyCode?goodsId="+$("#goodsId").val());//訪問驗證碼接口 $("#verifyCodeImg").show(); $("#verifyCode").show(); } else {//秒殺結束 $("#buyButton").attr("disabled",true); $("#miaoshaTip").html("結束!!!")//修改其中的內容 } }
做法:首先將票務詳情這個template 模板 html頁放在static 文件下,然后改掉thymeleaf 模板語言標簽讓其成為純html語言,然后將票務列表中的鏈接指向(本來是requestMapping,向后端contrller 請求這個詳情業務及數據,然后利用spring渲染模板,在返回的),現在直接指向static文件下的票務詳情頁(鏈接中帶商品id作為參數),最后在這個html頁面寫ajax異步訪問后端接口/getdetail,后端接口也改造一下返回的是這個商品的全部詳細信息,封裝在data里,以json的形式,然后寫了一個render(),把從后端傳來的數據寫進對應數據中。
/** 頁面靜態化:商品詳情頁面 * 方法:返回的是一個靜態html 頁面 + 利用ajax(通過接口)從服務端獲取對應數據 + js技術將數據放入html * */ @RequestMapping(value="/to_detail/{goodsId}") // 前端傳入的參數 goodsId @ResponseBody public Result<GoodsDetailVo> detail(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user, @PathVariable("goodsId") Long goodsId){//通過注解@PathVariable獲取路徑參數 /*先將user 傳進去 用來判斷是否登錄*/ model.addAttribute("user",user); /*根據傳入的Id 通過service 拿到對應的Good信息*/ GoodsVo goods=goodsService.getGoodsById(goodsId); model.addAttribute("goods",goods); long startTime=goods.getStartDate().getTime(); long endTime=goods.getEndDate().getTime(); long nowTime=System.currentTimeMillis();/* 拿到現在時間的毫秒值*/ /**這里要做一個秒殺時間的判斷 秒殺開始 秒殺結束 秒殺進行 * */ int miaoshaStatus=0;/*用該變量來表示 秒殺的狀態 0 表示秒殺未開始 1 開始 2 結束*/ int remainSeconds=0; /*表示剩余時間 距離秒殺開始的時間*/ if (nowTime<startTime){//秒殺未開始 miaoshaStatus=0; remainSeconds=(int)((startTime-nowTime)/1000);//注意此時是 毫秒值 要除以1000 }else if (endTime<nowTime){//秒殺結束 miaoshaStatus=2; remainSeconds=-1; }else {//秒殺進行中 miaoshaStatus=1; remainSeconds=0; } model.addAttribute("remainSeconds",remainSeconds); model.addAttribute("miaoshaStatus",miaoshaStatus); /* 將我們需要的數據 封裝到GoodsDetailVo中 */ GoodsDetailVo goodsDetailVo=new GoodsDetailVo(); goodsDetailVo.setGoodsVo(goods); goodsDetailVo.setMiaoshaStatus(miaoshaStatus); goodsDetailVo.setRemainSeconds(remainSeconds); goodsDetailVo.setUser(user); return Result.success(goodsDetailVo);
針對于上面所涉及到的知識點我總結出了有1到5年開發經驗的程序員在面試中涉及到的絕大部分架構面試題及答案做成了文檔和架構視頻資料免費分享給大家(包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并發等架構技術資料),希望能幫助到您面試前的復習且找到一個好的工作,也節省大家在網上搜索資料的時間來學習,也可以關注我一下以后會有更多干貨分享。
個 登 錄 有 必 要 做 成 那 樣 嗎 ? \textcolor{green}{一個登錄有必要做成那樣嗎?}一個登錄有必要做成那樣嗎?我 的 回 答 : 非 常 有 必 要 \textcolor{red}{我的回答:非常有必要}我的回答:非常有必要
試 想 一 下 , 如 果 一 個 網 站 可 以 隨 便 進 去 點 贊 等 操 作 , 那 還 要 登 錄 干 什 么 ? \textcolor{green}{試想一下,如果一個網站可以隨便進去點贊等操作,那還要登錄干什么?}試想一下,如果一個網站可以隨便進去點贊等操作,那還要登錄干什么?
博 主 也 在 學 習 階 段 , 如 若 發 現 問 題 , 請 告 知 , 非 常 感 謝 \textcolor{Orange}{博主也在學習階段,如若發現問題,請告知,非常感謝}博主也在學習階段,如若發現問題,請告知,非常感謝
周 榜 也 到 了 26 , 哇 塞 ( o ゜ ▽ ゜ ) o ☆ , 順 便 提 一 下 今 天 庫 里 必 登 三 分 歷 史 第 一 \textcolor{blue}{周榜也到了26,哇塞(o゜▽゜)o☆,順便提一下今天庫里必登三分歷史第一}周榜也到了26,哇塞(o゜▽゜)o☆,順便提一下今天庫里必登三分歷史第一
SpringMVC的處理器攔截器類似于Servlet開發中的過濾器Filter,用于對處理器進行預處理和后處理。
攔截器和過濾器的區別在于攔截器使AOP思想的具體應用
? ? > 新 建 一 個 M o d u l e , 添 加 w e b 支 持 \textcolor{OrangeRed}{--> 新建一個Module,添加web支持}??>新建一個Module,添加web支持
? ? > 配 置 w e b . x m l , a p p l i c a t i o n C o n t e x t . x m l , 添 加 一 個 c o n t r o l l e r 的 包 \textcolor{OrangeRed}{--> 配置web.xml,applicationContext.xml,添加一個controller的包}??>配置web.xml,applicationContext.xml,添加一個controller的包
? ? > 編 寫 測 試 \textcolor{OrangeRed}{--> 編寫測試}??>編寫測試
@RestController
public class TestController {
@GetMapping("/t1")
public String test(){
System.out.println("TestController-->test()執行了");
return "ok";
}
}
12345678
添加Artifact中的lib,以及配置Tomcat,啟動測試出現,證明Spring配置好了
? ? > 編 寫 攔 截 器 \textcolor{OrangeRed}{--> 編寫攔截器}??>編寫攔截器
package com.hxl.config;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
//在請求處理的方法之前執行
//return true;執行下一個攔截器
//如果返回false就不執行下一個攔截器
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("------------處理前------------");
return true;
}
//在請求處理方法執行之后執行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("------------處理后------------");
}
//在dispatcherServlet處理后執行,做清理工作.
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("------------清理------------");
}
}
12345678910111213141516171819202122232425
? ? > 在 a p p l i c a t i o n C o n t e x t . x m l 中 配 置 攔 截 器 \textcolor{OrangeRed}{--> 在applicationContext.xml中配置攔截器}??>在applicationContext.xml中配置攔截器
<!--關于攔截器的配置-->
<mvc:interceptors>
<mvc:interceptor>
<!--/** 包括路徑及其子路徑-->
<!--/admin/* 攔截的是/admin/add等等這種 , /admin/add/user不會被攔截-->
<!--/admin/** 攔截的是/admin/下的所有-->
<mvc:mapping path="/**"/>
<!--bean配置的就是攔截器-->
<bean class="com.hxl.config.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
1234567891011
前面的我們都不動,運行,我們可以看到效果
那么接下來就用一個實例來體驗一下攔截器(登錄)
在WEB-INF下的所有頁面或者資源,只能通過controller或者servlet進行訪問
? ? > i n d e x . j s p \textcolor{OrangeRed}{--> index.jsp}??>index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/goLogin">登錄</a>
<a href="${pageContext.request.contextPath}/goMain">首頁</a>
</body>
</html>
123456789101112
? ? > m a i n . j s p \textcolor{OrangeRed}{--> main.jsp}??>main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首頁</h1>
</body>
</html>
1234567891011
? ? > l o g i n . j s p \textcolor{OrangeRed}{--> login.jsp}??>login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登錄</title>
</head>
<body>
<h1>登錄頁面</h1>
<form action="${pageContext.request.contextPath}/login" method="post">
用戶名:<input type="text" name="username">
密碼:<input type="text" name="password">
<input type="submit" value="登錄">
</form>
</body>
</html>
12345678910111213141516
? ? > L o g i n C o n t r o l l e r \textcolor{OrangeRed}{--> LoginController}??>LoginController
package com.hxl.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
@Controller
public class LoginController {
@RequestMapping("/goMain")
public String goMain(){
return "main";
}
@RequestMapping("/goLogin")
public String goLogin(){
return "login";
}
@RequestMapping("/login")
public String login(HttpSession session,String username,String password){
// 向session記錄用戶身份信息
System.out.println("接收前端==="+username);
session.setAttribute("user", username);
return "main";
}
}
12345678910111213141516171819202122232425262728
? ? > 測 試 : \textcolor{OrangeRed}{--> 測試:}??>測試:
因為在WEB-INF下的 頁面我們不能直接訪問,所以在index中進行跳轉,然后請求到login.jsp。此時我們在這里輸入,就會在session中攜帶賬號密碼。
但此時是不符合的,因為我們還沒有登錄就可以去首頁,所以我們需要寫一個攔截器的功能
? ? > i n d e x . j s p \textcolor{OrangeRed}{--> index.jsp}??>index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/user/goLogin">登錄</a>
<a href="${pageContext.request.contextPath}/user/goMain">首頁</a>
</body>
</html>
123456789101112
? ? > l o g i n . j s p \textcolor{OrangeRed}{--> login.jsp}??>login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登錄</title>
</head>
<body>
<h1>登錄頁面</h1>
<form action="${pageContext.request.contextPath}/user/login" method="post">
用戶名:<input type="text" name="username">
密碼:<input type="text" name="password">
<input type="submit" value="登錄">
</form>
</body>
</html>
12345678910111213141516
? ? > m a i n . j s p \textcolor{OrangeRed}{--> main.jsp}??>main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首頁</h1>
<p>${username}</p>
<p>
<a href="${pageContext.request.contextPath}/user/goOut">注銷</a>
</p>
</body>
</html>
1234567891011121314
? ? > L o g i n I n t e r c e p t o r \textcolor{OrangeRed}{--> LoginInterceptor}??>LoginInterceptor
此時我們去寫一個登錄的攔截器,來判斷它到底什么時候進行攔截
package com.hxl.config;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session=request.getSession();
//放行:判斷什么情況下沒有登錄
//登錄頁面放行
if(request.getRequestURI().contains("goLogin")){
return true;
}
if(request.getRequestURI().contains("login")){
return true;
}
//用戶已登錄,第一次登錄的時候也是沒有session的。
if(session.getAttribute("user") !=null){
return true;
}
//判斷什么情況下沒有登錄
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
}
12345678910111213141516171819202122232425262728
? ? > L o g i n C o n t r o l l e r \textcolor{OrangeRed}{--> LoginController}??>LoginController
這里面我們有一個類url,下面的請求都需要加上/user,在配置攔截器的時候可以加一個,只攔截user請求下的。以及注銷功能
package com.hxl.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("/user")
public class LoginController {
@RequestMapping("/goMain")
public String goMain(){
return "main";
}
@RequestMapping("/goLogin")
public String goLogin(){
return "login";
}
@RequestMapping("/login")
public String login(HttpSession session, String username, String password, Model model){
// 向session記錄用戶身份信息
System.out.println("接收前端==="+username);
session.setAttribute("user", username);
model.addAttribute("username",username);
return "main";
}
@RequestMapping("/goOut")
public String goOut(HttpSession session){
/*//銷毀,下面的好
session.invalidate();*/
//移除
session.removeAttribute("user");
return "main";
}
}
12345678910111213141516171819202122232425262728293031323334353637383940
? ? > a p p l i c a t i o n C o n t e x t . x m l \textcolor{OrangeRed}{-->applicationContext.xml}??>applicationContext.xml
<!--關于攔截器的配置-->
<mvc:interceptors>
<mvc:interceptor>
<!--/** 包括路徑及其子路徑-->
<!--/admin/* 攔截的是/admin/add等等這種 , /admin/add/user不會被攔截-->
<!--/admin/** 攔截的是/admin/下的所有-->
<mvc:mapping path="/**"/>
<!--bean配置的就是攔截器-->
<bean class="com.hxl.config.MyInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<!--user下面的請求-->
<mvc:mapping path="/user/**"/>
<bean class="com.hxl.config.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
1234567891011121314151617
? ? > 測 試 \textcolor{OrangeRed}{-->測試}??>測試
此時我們啟動之后,如果沒有登錄,那么會重定向到goLogin頁面,然后登錄,登錄之后跳轉到main頁面,其中有注銷功能,沒有注銷之前可以去index頁面點擊首頁正常跳轉,如果注銷了,session沒有了,那么就會跳轉到登錄頁面。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。