SpringBoot詳解系列文章: SpringBoot詳解(一)-快速入門 SpringBoot詳解(二)-Spring Boot的核心 SpringBoot詳解(三)-Spring Boot的web開發 SpringBoot詳解(四)-優雅地處理日誌
一、web基礎配置
1、訪問靜態資源
1)進入規則為 / 時
如果進入SpringMVC的規則為/時,Spring Boot的預設靜態資源的路徑為:
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
複製程式碼
也就是說,在預設的Spring MVC進入規則下,classpath下的META-INF/resources目錄、resources目錄、static目錄和public目錄中的靜態資料是可以直接通過 "http: // xxx.com/靜態資源" 的方式訪問到的。如:
2)進入規則為*.xxx 或者 不指定靜態檔案路徑時
如果進入SpringMVC的規則為*.xxx時(如:*.html),則上述目錄下的靜態資源將無法直接訪問,需要將靜態資源放置到webapp下的static目錄中即可通過 "http://xxx.com/static/靜態資源" 訪問。此外,預設不配置SpringMVC的規則下也可以如此訪問,也就是說這種訪問靜態資源的方式是通用的。如圖所示:
2、自定義攔截器
增加一個攔截器,需要通過繼承WebMvcConfigurerAdapter然後重寫父類中的方法進行擴充套件。
@Configuration
public class MySpringMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
HandlerInterceptor handlerInterceptor = new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("自定義攔截器。。。");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
};
// 新增攔截器並設定攔截規則
registry.addInterceptor(handlerInterceptor).addPathPatterns("/**");
}
}
複製程式碼
3、自定義訊息轉化器
自定義訊息轉化器有兩種實現方式,一種是@Bean方式,另一種是自定義攔截器。
1)@Bean方式
只需要在@Configuration的類中新增訊息轉化器的@bean加入到Spring容器,就會被Spring Boot自動加入到容器中。
// spring boot預設就有訊息轉化器,其編碼格式為utf-8
@Bean
public StringHttpMessageConverter stringHttpMessageConverter() {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
return stringHttpMessageConverter;
}
複製程式碼
2)自定義攔截器方式
WebMvcConfigurerAdapter的功能很強大,除了可以配置攔截器外,還可以配置訊息轉換器。
@Configuration
public class MySpringMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
converters.add(stringHttpMessageConverter);
}
}
複製程式碼
4、讀取外部的配置檔案
@Configuration
@PropertySource(value = { "classpath:jdbc.properties", "classpath:base.properties" }, ignoreResourceNotFound = true)
public class 任意類 {
}
複製程式碼
5、Druid DataSource的配置
Druid提供了一個高效、功能強大、可擴充套件性好的資料庫連線池,常用於替換DBCP和C3P0。但在Spring Boot上配置比較“噁心”,這裡就簡單的貼出個人參照網上的配置程式碼,不必深究。
1)引入依賴
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.11</version>
</dependency>
複製程式碼
2)jdbc.properties
專案中一般會建立一個jdbc.properties檔案來記錄資料庫的連線資訊。
#MySQL
jdbc.url=jdbc:mysql://127.0.0.1:3306/dbxxx?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456
#Druid
jdbc.initialSize=0
jdbc.minIdle=0
jdbc.maxActive=150
複製程式碼
3)配置Druid資料來源
建議將配置Druid資料來源的操作放在@SpringBootApplication註解的類中。
@SpringBootApplication
@Configuration
@PropertySource(value = {"classpath:jdbc.properties"})
public class MyWebApplication{
@Value("${jdbc.url}")
public String jdbcUrl;
@Value("${jdbc.username}")
public String jdbcUsername;
@Value("${jdbc.password}")
public String jdbcPassword;
@Value("${jdbc.initialSize}")
public int jdbcInitialSize;
@Value("${jdbc.minIdle}")
public int jdbcMinIdle;
@Value("${jdbc.maxActive}")
public int jdbcMaxActive;
@Bean
public ServletRegistrationBean druidServletRegistrationBean() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
servletRegistrationBean.setServlet(new StatViewServlet());
servletRegistrationBean.addUrlMappings("/druid/*");
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean duridFilterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
HashMap<String, String> initParams = new HashMap<>();
// 設定忽略請求
initParams.put("exclusions", "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*");
filterRegistrationBean.setInitParameters(initParams);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
@Bean(initMethod = "init", destroyMethod = "close")
public DruidDataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(jdbcUrl);
druidDataSource.setUsername(jdbcUsername);
druidDataSource.setPassword(jdbcPassword);
druidDataSource.setInitialSize(jdbcInitialSize);
druidDataSource.setMinIdle(jdbcMinIdle);
druidDataSource.setMaxActive(jdbcMaxActive);
return druidDataSource;
}
}
複製程式碼
之後就可以通過@AutoWried註解得到資料來源(druidDataSource)了。
6、資料庫框架整合
1)jpa整合
Jpa在使用上跟Hibernate很像,因為它們之前的關係非同一般,有興趣可以看看《Hibernate與Jpa的關係,終於弄懂》這篇文章。Spring Boot對jpa的支援也是很好的,配置起來非常簡單。
在pom.xml中引用jpa及資料庫驅動(如:mysql)依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
複製程式碼
在application.yml檔案中配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1/dbgirl
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update #第一次載入hibernate時根據model類會自動建立起表的結構(前提是先建立好資料庫),以後載入hibernate時根據 model類自動更新表結構,即使表結構改變了但表中的行仍然存在不會刪除以前的行。要注意的是當部署到伺服器後,表結構是不會被馬上建立起來的,是要等 應用第一次執行起來後才會。
show-sql: true
複製程式碼
2)MyBatis整合
Mybatis和Spring Boot的整合有兩種方式:
第一種:使用mybatis官方提供的Spring Boot整合包實現,地址:spring-boot-starter
第二種:使用mybatis-spring整合的方式,也就是我們傳統的方式
這裡推薦並使用第二種方式,因為可以很方便的控制Mybatis的各種配置。這裡假設你已經配置過資料來源了(資料來源可以是druid、dbcp、c3p0...)。
首先,需要在pom.xml檔案中引用mybatis依賴
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
複製程式碼
然後,建立一個Mybatis的配置類:
@Configuration
public class MybatisConfig {
@Autowired
DruidDataSource druidDataSource;
@Bean
@ConditionalOnMissingBean// 當Spring容器中沒有SqlSessionFactoryBean時才建立
public SqlSessionFactoryBean sqlSessionFactoryBean() {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
// 設定資料來源
sqlSessionFactory.setDataSource(druidDataSource);
// 設定別名掃描包
sqlSessionFactory.setTypeAliasesPackage("com.lqr.demo3.bean");
// 設定Mybatis的配置檔案位置
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource mybatisConfigXml = resolver.getResource("classpath:mybatis-config.xml");
sqlSessionFactory.setConfigLocation(mybatisConfigXml);
return sqlSessionFactory;
}
}
複製程式碼
最後,建立Mapper介面的掃描類MapperScannerConfig:
@Configuration
@AutoConfigureAfter(MybatisConfig.class)// Mybatis的掃描配置必須在SqlSessionFactory被建立之後
public class MapperScanConfig {
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.lqr.demo3.mapper");
return mapperScannerConfigurer;
}
}
複製程式碼
7、設定事務管理
Spring Boot中推薦使用@Transactional註解來申明事務。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
複製程式碼
當引入jdbc依賴之後,Spring Boot會自動預設分別注入DataSourceTransactionManager或JpaTransactionManager,所以我們不需要任何額外配置就可以用@Transactional註解進行事務的使用。
@Service
@Transactional
public class GirlService {
@Transactional
public void insertGirl() {
Girl girlA = new Girl();
girlA.setCupSize("A");
girlA.setAge(18);
girlRespository.save(girlA);
}
}
複製程式碼
@Transactional不僅可以註解在方法,也可以註解在類上。當註解在類上時,意味著此類所有public方法都會開啟事務。如果類級別和方法級別同時使用了@Transactional註解,則使用在類級別的註解會過載方法級別的註解。
8、開啟jsp支援
Spring boot預設內嵌的tomcat是不支援jsp頁面的,如果專案中使用到了jsp,需要匯入如下依賴才能正常訪問。
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
複製程式碼
二、Web編碼進階
這部分可能跟Spring Boot的關係並不是很大(或者說,並非Spring Boot特有),但很值得我們在編碼方面作為參考。
1、Spring MVC中新的註解
1)@RestController
現在的Web專案已經越來越趨向於將後端與前端分別開發了,如果貴公司的專案就是使用json來進行前後端互動,且使用Spring MVC來開發的話,就一定會用到下面這2個註解:
@Controller
@ResponseBody
public class GirlController {
...
}
複製程式碼
而在Spring MVC4之後,我們可以使用@RestController 註解來開發基於Spring MVC4的REST風格的JSON服務。
通俗的說就是@RestController = @Controller + @ResponseBody。
@Controller和@RestController的區別:
如果只是使用@RestController註解Controller,則Controller中的方法無法返回jsp頁面,配置的檢視解析器InternalResourceViewResolver不起作用,返回的內容就是Return 裡的內容。 例如:本來應該到success.jsp頁面的,則其顯示success.
2)http組合註解
Spring4.3中引進了{@GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping},來幫助簡化常用的HTTP方法的對映,並更好地表達被註解方法的語義。
@GetMapping =>
@RequestMapping(value = "/xxx",method = RequestMethod.GET)
@PostMapping =>
@RequestMapping(value = "/xxx",method = RequestMethod.POST)
@PutMapping =>
@RequestMapping(value = "/xxx",method = RequestMethod.PUT)
@DeleteMapping =>
@RequestMapping(value = "/xxx",method = RequestMethod.DELETE)
...
複製程式碼
2、資料校驗
Web開發中,對從前端傳過來的資料做資料校驗已經是家常便飯的事了,但如果校驗的資料很多,那麼,一方面在開發上就需要做很多if判斷,另一方面則是程式碼上顯得不再簡潔。其實,使用@Valid + BindingResult就可以優雅地解決這樣的問題。使用示例如下:
1)@Valid + BindingResult
首先,使用一個Java Bean來接收前端提交的資料。在這個Java Bean之前加上@Valid,在這個Java Bean之後加上BindingResult(BindingResult引數必須緊跟在@Valid引數之後。)
/**
* 新增一個女生
*/
@PostMapping(value = "/girls")
public Result<Girl> girlAdd(@Valid Girl girl, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return ResultUtils.error(-1, bindingResult.getFieldError().getDefaultMessage());
}
return ResultUtils.success(girlRespository.save(girl));
}
複製程式碼
2)設定校驗規則
然後,需要在@Valid註解的Java Bean中,使用各種"規則註解"對其中的屬性進行校驗規則的設定。示例如下:
public class Girl {
private Integer id;
@NotBlank(message = "這個欄位必傳")
private String cupSize;
@Min(value = 18, message = "未成年少女禁止入內")
private Integer age;
@NotNull(message = "金額必傳")
private Double money;
...
}
複製程式碼
示例中,"規則註解"的message值可以在Controller中通過bindingResult.getFieldError().getDefaultMessage()獲取。
這類用於資料校驗的註解還有很多,有興趣的可以好好研究一下:
註解 | 型別 | 說明 |
---|---|---|
@AssertFalse | Boolean,boolean | 驗證註解的元素值是false |
@AssertTrue | Boolean,boolean | 驗證註解的元素值是true |
@NotNull | 任意型別 | 驗證註解的元素值不是null |
@Null | 任意型別 | 驗證註解的元素值是null |
@Min(value=值) | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(儲存的是數字)子型別 | 驗證註解的元素值大於等於@Min指定的value值 |
@Max(value=值) | 和@Min要求一樣 | 驗證註解的元素值小於等於@Max指定的value值 |
@DecimalMin(value=值) | 和@Min要求一樣 | 驗證註解的元素值大於等於@ DecimalMin指定的value值 |
@DecimalMax(value=值) | 和@Min要求一樣 | 驗證註解的元素值小於等於@ DecimalMax指定的value值 |
@Digits(integer=整數位數, fraction=小數位數) | 和@Min要求一樣 | 驗證註解的元素值的整數位數和小數位數上限 |
@Size(min=下限, max=上限) | 字串、Collection、Map、陣列等 | 驗證註解的元素值的在min和max(包含)指定區間之內,如字元長度、集合大小 |
@Past | java.util.Date,java.util.Calendar;Joda Time類庫的日期型別 | 驗證註解的元素值(日期型別)比當前時間早 |
@Future | 與@Past要求一樣 | 驗證註解的元素值(日期型別)比當前時間晚 |
@NotBlank | CharSequence子型別 | 驗證註解的元素值不為空(不為null、去除首位空格後長度為0),不同於@NotEmpty,@NotBlank只應用於字串且在比較時會去除字串的首位空格 |
@Length(min=下限, max=上限) | CharSequence子型別 | 驗證註解的元素值長度在min和max區間內 |
@NotEmpty | CharSequence子型別、Collection、Map、陣列 | 驗證註解的元素值不為null且不為空(字串長度不為0、集合大小不為0) |
@Range(min=最小值, max=最大值) | BigDecimal,BigInteger,CharSequence, byte, short, int, long等原子型別和包裝型別 | 驗證註解的元素值在最小值和最大值之間 |
@Email(regexp=正規表示式,flag=標誌的模式) | CharSequence子型別(如String) | 驗證註解的元素值是Email,也可以通過regexp和flag指定自定義的email格式 |
@Pattern(regexp=正規表示式,flag=標誌的模式) | String,任何CharSequence的子型別 | 驗證註解的元素值與指定的正規表示式匹配 |
@Valid | 任何非原子型別 | 指定遞迴驗證關聯的物件;如使用者物件中有個地址物件屬性,如果想在驗證使用者物件時一起驗證地址物件的話,在地址物件上加@Valid註解即可級聯驗證 |
3、面積切面程式設計(AOP配置)
AOP是Spring中一大核心,若在SpringBoot中要使用AOP只需兩步:
1)引入AOP的starter依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
複製程式碼
2)編寫切面類
@Aspect
@Component
public class HttpAspect {
private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);
@Pointcut("execution(public * com.lqr.controller.GirlController.*(..))")
public void log() {
}
@Before("log()")
public void deBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// url
logger.info("url={}", request.getRequestURL());
// method
logger.info("method={}", request.getMethod());
// ip
logger.info("ip={}", request.getRemoteAddr());
// 類方法
logger.info("class_method={}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
// 引數
logger.info("args={}", joinPoint.getArgs());
}
@After("log()")
public void doAfter() {
logger.info("doAfter...");
}
@AfterReturning(returning = "object", pointcut = "log()")
public void doAfterReturning(Object object) {
if (object != null)
logger.info("response={}", object.toString());
}
}
複製程式碼
4、統一異常處理(配置通知Advice)
1)自定義異常
這裡之所以繼承RuntimeException,是為了方便事務回滾。而自定義異常的好處在於:一方面可以使程式碼語義化,另一方面使得我們編碼更加方便。
public class GirlException extends RuntimeException {
private Integer code;
public GirlException(ResultEnum resultEnum) {
super(resultEnum.getMsg());
this.code = resultEnum.getCode();
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
複製程式碼
2)配置全域性異常捕獲器
使用全域性異常捕獲器,一方面可以捕獲到整個專案中的Exception及其子類(包含RuntimeException等),另一方面可以對異常進行統一處理並返回統一的資料格式,為前端提供友好的資料互動。
@ControllerAdvice
public class ExceptionHandle {
private final Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result handle(Exception e) {
if (e instanceof GirlException) {
GirlException girlException = (GirlException) e;
return ResultUtils.error(girlException.getCode(), girlException.getMessage());
} else {
logger.error("【系統異常】{}", e);
return ResultUtils.error(-1, "未知錯誤");
}
}
}
複製程式碼
三、開發與生產
1、熱部署
Web開發中,沒有熱部署怎麼能行呢?所以,下面就介紹下Spring Boot的熱部署配置。
1)在pom.xml中配置熱部署需要的依賴與外掛
<dependencies>
...
<!--spring boot 熱部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<!--spring boot 熱部署-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
...
</build>
複製程式碼
2)開啟自動編譯
開啟Idea的Settings介面,找到"Build,Execution,Depolyment",在Compiler中將"Build project automatically"打鉤。
3)修改pom.xml的維護(Maintenance)登記項
使用快捷鍵 "ctrl+shift+alt+/" 開啟pom.xml的維護(Maintenance)選單,找到登記(registry)項,單擊開啟。
4)允許應用程式執行時自動編譯
在登記(registry)中找到"compiler.automake.allow.when.app.running"這一項打鉤,關閉。最後重啟專案即可!!!
2、釋出到獨立的tomcat中執行
儘管Spring Boot專案會內建一個tomcat,僅只需通過一個簡單的指令便可啟動專案,但在生產環境下,我們還是習慣將專案釋出到第三外的servlet容器中,下面將介紹如果將一個Spring Boot專案團部署到第三方tomcat中執行。
1)修改工程的打包方式為war
2)將spring-boot-starter-tomcat的範圍設定為provided
spring-boot-starter-tomcat是Spring Boot預設就會配置的,即上面說到的內嵌tomcat,將其設定為provided是在打包時會將該包(依賴)排除,因為要放到獨立的tomcat中執行,Spring Boot內嵌的Tomcat是不需要用到的。
<!--spring boot tomcat(預設可以不用配置,但當需要把當前web應用佈置到外部servlet容器時就需要配置,並將scope配置為provided)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
複製程式碼
3)修改程式碼,設定啟動配置
需要繼承SpringBootServletInitializer,並重寫configure()方法,將Spring Boot的入口類設定進去。
// 若要部署到外部servlet容器,需要繼承SpringBootServletInitializer並重寫configure()
@SpringBootApplication
@Configuration
public class MyWebApplication extends SpringBootServletInitializer
{
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
// 設定啟動類,用於獨立tomcat執行的入口
return builder.sources(MyWebApplication.class);
}
}
複製程式碼