本文的目的是簡單介紹Spring Boot的一些入門配置的Quick Start,幫助快速上手;
不涉及Spring Boot的概念的介紹講解,如需了解請參考Spring Boot 官方文檔;
有關(guān)Spring Boot的優(yōu)缺點和適用場景的文章已經(jīng)很多,還請自行Baidu/Google;
-
開始
Spring Boot采用約定優(yōu)于配置的方式,相比原來Spring框架,配置有了大幅的簡化,不僅配置文件寫起來簡單多了,而且pom.xml的依賴配置也進行了優(yōu)化,具體請參考Spring Boot Starter
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
只需要這樣幾行簡單的配置,就能寫基于Spring MVC的Hello World了
@RestController
class HelloWorld {
@RequestMapping("/")
String home() {
return "Hello World!";
}
}
然后寫個入口
@SpringBootApplication
@EnableAutoConfiguration
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
啟動main方法,運行、調(diào)試都很簡單
-
加上日志
開始一個項目的時候,日志是比不可少的,先把日志加上,避免了System.out.println的泛濫。
Spring Boot的默認配置文件是 application.properties
, 不使用默認配置的選項全都在這里定義
Spring Boot 默認使用logback,無需任何配置就可以使用了,并提供了
logging.config= # Location of the logging configuration file. For instance
classpath:logback.xml
for Logback
logging.exception-conversion-word=%wEx # Conversion word used when logging
exceptions.logging.file= # Log file name. For instancemyapp.log
logging.level.*= # Log levels severity mapping. For instancelogging.level.org.springframework=DEBUG
logging.path= # Location of the log file. For instance/var/log
logging.pattern.console= # Appender pattern for output to the console. Only supported with the default logback setup.logging.pattern.file= # Appender pattern for output to the file. Only supported with the default logback
setup.logging.pattern.level= # Appender pattern for log level (default %5p). Only supported with the default logback
setup.logging.register-shutdown-hook=false # Register a shutdown hook for the logging system when it is initialized.
如果有更具體配置,如async appender,remote appender等還需使用原有的XML配置文件,如果使用默認配置文件名logback-spring.xml
,則可以直接生效,無需添加一行配置
如果想使用log4j2或其他的請參考howto logging
-
寫個測試
注:standalone的Spring Boot程序只允許在jar中存在一個main方法
所以原來習(xí)慣寫個main方法來測試代碼的同學(xué),是時候了解怎樣寫測試了,請移步到Junit深造
首先,添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
測試的starter已經(jīng)包含了測試工具:Junit
Hamcrest
Mockito
等,可以直接上手寫case了
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureTestDatabase
public class UserVehicleControllerApplicationTests {
@Autowired
private MockMvc mvc;
@Autowired
private ApplicationContext applicationContext;
@MockBean
private UserVehicleService userVehicleService;
@Test
public void getVehicleWhenRequestingTextShouldReturnMakeAndModel() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new
VehicleDetails("Honda", "Civic"));
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk()).andExpect(content().string("Honda Civic"));
}
@Test
public void welcomeCommandLineRunnerShouldBeAvailable() throws Exception {
assertThat(this.applicationContext.getBean(WelcomeCommandLineRunner.class).isNotNull();
}
}
測試?yán)诱埧?spring-boot-sample-test
-
數(shù)據(jù)庫連接池
數(shù)據(jù)庫連接池也是大多數(shù)項目的標(biāo)配,在Spring Boot中引入也十分簡單
以mysql為例,pom中添加依賴:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.26</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.4.7</version>
<scope>compile</scope>
</dependency>
請使用tomcat 和 C3p0 連接池的同學(xué)移駕 HikariCP 補課
application.properties
中添加:
datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8
datasource.username=user
datasource.password=password
spring.datasource.url=${datasource.url}
spring.datasource.username=${datasource.username}
spring.datasource.password=${datasource.password}
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.max-lifetime=30000
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.data-source-properties.prepStmtCacheSize=250
spring.datasource.hikari.data-source-properties.prepStmtCacheSqlLimit=2048
spring.datasource.hikari.data-source-properties.cachePrepStmts=true
spring.datasource.hikari.data-source-properties.useServerPrepStmts=true
這樣Spring Boot會自動識別這些配置并加載jdbcTemplate和dataSource的bean
-
Mybatis
Hibernate,JPA,JOOQ的同學(xué)請參考 [Working With Database](29. Working with SQL databases)
Mybatis 開發(fā)了自己的starter依賴
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
然后就可以像往常一樣編寫Dao了
@Mapper
public interface DemoDao {
@Select("select * from test")
public List<TestDomain> get();
@Insert("insert into test (name,age) values (#{name},#{age})")
void insert(@Param("name") String name, @Param("age") String age);
}
當(dāng)然如果遇到要寫一些復(fù)雜的SQL或者需要include SQL的話,使用xml文件來寫SQL會更方便,只需給定xml文件的未知即可 mybatis-mapper-locations=classpath*:**/mapper/*
詳細Mybatis的請參考 mybatis-spring-boot-autoconfigure
-
數(shù)據(jù)庫管理 Flyway
Flyway的具體使用方法請移步官網(wǎng)參考
添加pom依賴
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
配置中加入:
flyway.enabled=true
flyway.schemas=test
腳本默認位置:classpath:db\migration
搞定,程序啟動自動執(zhí)行數(shù)據(jù)庫腳本
-
Redis
廢話不說了
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
properties
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.password=
spring.redis.pool.max-active=-1
spring.redis.pool.max-idle=100
spring.redis.port=6379
spring.redis.timeout=0
code
@AutowiredRedisTemplate redisTemplate;
-
異常處理之ExceptionHandler
當(dāng)Controller拋出異常后需要進行處理,以返回友好信息給clients
@ControllerAdvice(basePackageClasses = CoreApplication.class)
public class DemoControllerAdvice {
@ExceptionHandler(DemoException.class)
@ResponseBody
ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
HttpStatus status = getStatus(request);
return new ResponseEntity<DemoError>(new DemoError(status.value(), ex.getMessage()), status);
}
@ExceptionHandler(Exception.class)
@ResponseBody
ResponseEntity<?> handleException(HttpServletRequest request, Throwable ex) {
HttpStatus status = getStatus(request);
return new ResponseEntity<DemoError>(new DemoError(status.value(), ex.getMessage()), status);
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
return HttpStatus.valueOf(statusCode);
}
}
-
異常處理之ErrorController
容器處理失敗的請求會自動轉(zhuǎn)到 /error
來處理,實現(xiàn)ErrorController接口來自定義該異常的處理機制
@RestController
public class DemoErrorController implements ErrorController {
@Autowired
ErrorAttributes errorAttributes;
@RequestMapping(value = "/error")
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = this.getStatus(request);
return new ResponseEntity(body, status);
}
protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
return false;
}
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
return this.errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace);
}
protected HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
if(statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
} else {
try {
return HttpStatus.valueOf(statusCode.intValue());
} catch (Exception var4) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}
}
@Override
public String getErrorPath() {
return "/error";
}
}
-
自定義攔截器
與Spring MVC一樣,定義一個攔截器
public class DemoInterceptor implements HandlerInterceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("pre handle");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
logger.info("post handle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception {
logger.info("after completion");
}
}
然后在WebMvcConfigurerAdapter
的子類中把定義好的攔截器添加到容其中
@Configuration
public class DemoConfiguration extends WebMvcConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**");
}
}
其他的與Spring MVC一樣
開發(fā)過程中,總會遇到本機環(huán)境、CI環(huán)境,測試環(huán)境、UAT環(huán)境、生產(chǎn)環(huán)境等等不同環(huán)境間的切換,往往因為環(huán)境的不同需要寫不同的配置文件,又要有不同的腳本去替換這些配置文件,然后又要有一套版本管理系統(tǒng)把配置文件、腳本管理起來。
在Spring Boot中,這些問題統(tǒng)統(tǒng)可以自動處理。我們可以通過定義一個啟動參數(shù)來指定當(dāng)前啟動的環(huán)境是那個,那么該環(huán)境的配置文件就會被加載執(zhí)行。
啟動參數(shù):java -jar -Dspring.profiles.active=production app.jar
Spring Boot 首先會加載默認的配置文件application.properties
,然后會根據(jù)指定的參數(shù)加載特定環(huán)境的配置文件application-*.properties
,例如:application-production.properties
而我們要做的只是將配置文件放到指定的config路徑下即可。
-
定時器
增加定時器也很容易,使用@EnableScheduling
注解
@Configuration
@EnableScheduling
public class DemoJob {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Scheduled(cron="${Demo.Cron.Expression}")
public void fire() {
logger.info("scheduler fired");
}
}
定義Cron Expression
Demo.Cron.Expression=0 0/1 * * * ?
如果需要在不同的環(huán)境有不同的執(zhí)行策略,可以通過在不同環(huán)境的配置文件中定義不同Expression來做
如果只需要在某些環(huán)境中執(zhí)行,還可以指定bean的加載環(huán)境:
@EnableScheduling
@Profile({ "UAT", "Production" })
public class DemoJob{...}
這樣,只有在指定了環(huán)境是UAT或Production時,Spring Boot才會初始化這個bean
-
加個開機提示
在配置文件中加入
# BANNER banner.charset=UTF-8 banner.location=classpath:banner.txt
Spring Boot提供了一些監(jiān)控管理應(yīng)用程序的功能,例如JVM運行時資源監(jiān)控、應(yīng)用負載等
請自行查閱文檔吧。
注:這些監(jiān)控管理功能一定要配合Security使用,要有權(quán)限控制,建議生產(chǎn)環(huán)境時將這些權(quán)限控制在本機或內(nèi)網(wǎng)。
-
最后
到這里,一個簡單的Spring Boot Web 應(yīng)用應(yīng)該可以順利跑起來了,請在使用前認真閱讀官方文檔。
-
Demo 下載
整理好會提供。