Spring Boot 入门
Spring Boot 自动配置
http://blog.javachen.com/2015/03/13/how-to-run-spring-boot-application.html
http://blog.javachen.com/2015/03/13/some-spring-boot-features.html
http://blog.javachen.com/2016/02/19/spring-boot-auto-configuration.html
在上篇文章如何运行 Spring Boot 应用中,已经熟悉了如何通过 maven 或者 gradle 创建一个 Spring Boot 应用,这篇文章主要学习 Spring Boot 的自动配置,包括注解的使用以及一些配置约束等等。
关于 Spring Boot 的特性介绍,可以参考Spring Boot 特性。
主应用类
在 Spring Boot 应用中,我们通常将主应用类放置于应用的根包中,例如,com.javachen.example
。主应用类有 main 方法,并且使用了@EnableAutoConfiguration
注解,并暗地里定义了一个基础的包路径,Spring Boot 会在该包路径来搜索类。例如,如果你正在编写一个 JPA 应用,被@EnableAutoConfiguration
注解的类所在包将被用来搜索带有@Entity
注解的实体类。
在主应用类上指定@ComponentScan
,同样也隐式的指定了扫描时 basePackage 的路径。
如何运行 Spring Boot 应用中 Application.java 类声明了 main 方法,还使用了@EnableAutoConfiguration
注解。
@RestController
@EnableAutoConfiguration
public class Application {
@RequestMapping("/")
String home() {
return "Hello World!";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
pasting
说明:
@RestController
和@RequestMapping
注解是 Spring MVC 注解,它们不是 Spring Boot 的特定部分,具体查看 Spring 参考文档的 MVC 章节。@EnableAutoConfiguration
这个注解告诉 Spring Boot 根据添加的 jar 依赖猜测你想如何配置 Spring。由于 spring-boot-starter-web 添加了 Tomcat 和 Spring MVC,所以 auto-configuration 将假定你正在开发一个 web 应用并相应地对 Spring 进行设置。
配置类
在该类上也可以使用@Configuration
注解,用来对 spring boot 进行配置,当然,你也可以使用一个 XML 源来调用SpringApplication.run()
进行配置。
标有@Configuration
注解的类为配置类。你不需要将所有的@Configuration
放进一个单独的类。@Import
注解可以用来导入其他配置类。另外,你也可以使用@ComponentScan
注解自动收集所有的 Spring 组件,包括@Configuration
类。
如果你需要使用基于 XML 的配置,你可以在注有@Configuration
的类上使用附加的@ImportResource
注解加载 XML 配置文件。
你可以通过将@EnableAutoConfiguration
或@SpringBootApplication
注解添加到一个@Configuration
类上来选择自动配置。自动配置的意思是 Spring Boot 尝试根据你添加的 jar 依赖自动配置你的 Spring 应用。
如果需要找出当前应用了哪些自动配置及应用的原因,你可以使用--debug
开关启动应用,这将会记录一个自动配置的报告并输出到控制台。
如果发现应用了你不想要的特定自动配置类,你可以使用@EnableAutoConfiguration
注解的排除属性来禁用它们。
import org.springframework.boot.autoconfigure.; import org.springframework.boot.autoconfigure.jdbc.; import org.springframework.context.annotation.; @Configuration @EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) public class MyConfiguration { }
总结,上面提到了几个注解,用途分别如下:
@Configuration
。标注一个类为配置类。@EnableAutoConfiguration
。开启自动配置。@SpringBootApplication
。等价于以默认属性使用@Configuration
,@EnableAutoConfiguration
和@ComponentScan
。
如果启动类在根包下面,则你可以在该类上添加@ComponentScan
注解而不需要添加任何参数,Spring Boot 会在根包下面搜索注有@Component
, @Service
,@Repository
, @Controller
注解的所有类,并将他们注册为 Spring Beans,否则,你需要在@ComponentScan
注解上定义 basePackages 或者其他属性。
这样 Application.java 可以定义为:
package com.javachen.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @Configuration @ComponentScan @EnableAutoConfiguration public class Application { @RequestMapping("/") String home() { return "Hello World!"; } public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); } }
或者:
package com.javachen.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @SpringBootApplication public class Application { @RequestMapping("/") String home() { return "Hello World!"; } public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); } }
命令行参数
启动类可以实现 CommandLineRunner 接口,通过 run 方法处理 main 方法传入的参数,并且你能够使用@Value
注解将命令行参数传入的值或者 properties 资源文件中定义的值注入到程序中。例如,创建一个 HelloWorldService 类:
package com.javachen.example.service; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class HelloWorldService { @Value("${name:World}") private String name; public String getMessage() { return "Hello" + this.name; } }
并添加资源文件 application.properties:
name: JavaChen
修改 Application 类为如下:
package com.javachen.example; import com.javachen.example.service.HelloWorldService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @SpringBootApplication public class Application implements CommandLineRunner { @Autowired private HelloWorldService helloWorldService; @RequestMapping("/") String home() { return "Hello World!"; } @Override public void run(String... args) { System.out.println(this.helloWorldService.getMessage()); if (args.length > 0 && args[0].equals("exitcode")) { throw new ExitException(); } } public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); } }
运行该类的 main 方法,则默认会输出:
Hello JavaChen
再次运行 main 方法,并传入参数--name=whatever
,则会输出:
Hello whatever
如果一些 CommandLineRunner beans 被定义必须以特定的次序调用,你可以额外实现org.springframework.core.Ordered
接口或使用@Order
注解。
利用 command-line runner 的这个特性,再配合依赖注入,可以在应用程序启动时后首先引入一些依赖 bean,例如 data source、rpc 服务或者其他模块等等,这些对象的初始化可以放在 run 方法中。不过,需要注意的是,在 run 方法中执行初始化动作的时候一旦遇到任何异常,都会使得应用程序停止运行,因此最好利用 try/catch 语句处理可能遇到的异常。
每个 SpringApplication 在退出时为了确保 ApplicationContext 被优雅的关闭,将会注册一个 JVM 的 shutdown 钩子。所有标准的 Spring 生命周期回调(比如,DisposableBean 接口或 @PreDestroy 注解)都能使用。
此外,如果 beans 想在应用结束时返回一个特定的退出码,可以实现org.springframework.boot.ExitCodeGenerator
接口,例如上面例子中的 ExitException 异常类:
package com.javachen.example; import org.springframework.boot.ExitCodeGenerator; public class ExitException extends RuntimeException implements ExitCodeGenerator { @Override public int getExitCode() { return 10; } }
自动配置
在启动类上使用@EnableAutoConfiguration
注解,就会开启自动配置,简单点说就是它会根据定义在 classpath 下的类,自动的给你生成一些 Bean,并加载到 Spring 的 Context 中。
它的神秘之处,不在于它能做什么,而在于它会生成什么样的 Bean 对于开发人员是不可预知(或者说不容易预知)。
例如,上面例子中引入了对 spring-boot-starter-web 的依赖,则会开启 Spring MVC 自动配置,观察启动日志,可以发现应用启动了 tomcat 和 spring mvc。
Spring Boot 为 Spring MVC 提供适用于多数应用的自动配置功能。在 Spring 默认基础上,自动配置添加了以下特性:
- 引入 ContentNegotiatingViewResolver 和 BeanNameViewResolver beans。
- 对静态资源的支持,包括对 WebJars 的支持。
- 自动注册 Converter,GenericConverter,Formatter beans。
- 对 HttpMessageConverters 的支持。
- 自动注册 MessageCodeResolver。
- 对静态 index.html 的支持。
- 对自定义 Favicon 的支持。
如果想全面控制 Spring MVC,你可以添加自己的 @Configuration,并使用@EnableWebMvc
对其注解。如果想保留 Spring Boot MVC 的特性,并只是添加其他的MVC 配置(拦截器,formatters,视图控制器等),你可以添加自己的 WebMvcConfigurerAdapter 类型的@Bean
(不使用@EnableWebMvc
注解)。
再举个例子:要开发一个基于 Spring JPA 的应用,会涉及到下面三个 Bean 的配置,DataSource,EntityManagerFactory,PlatformTransactionManager。
@Configuration @EnableJpaRepositories @EnableTransactionManagement public class Application { @Bean public DataSource dataSource() { ... } @Bean public EntityManagerFactory entityManagerFactory() { .. factory.setDataSource(dataSource()); return factory.getObject(); } @Bean public PlatformTransactionManager transactionManager() { JpaTransactionManager txManager = new JpaTransactionManager(); txManager.setEntityManagerFactory(entityManagerFactory()); return txManager; } }
说明:
@EnableJpaRepositories
会查找满足作为 Repository 条件(继承父类或者使用注解)的类。@EnableTransactionManagement
的作用:Enables Spring’s annotation-driven transaction management capability, similar to the support found in Spring’s <tx:> XML namespace。
但是,如果你使用了@EnableAutoConfiguration
,那么上面三个 Bean,你都不需要配置。在 classpath 下面只引入了 MySQL 的驱动和 SpringJpa。
compile 'mysql:mysql-connector-java:5.1.18' compile 'org.springframework.boot:spring-boot-starter-data-jpa'
在生产环境中,数据库连接可以使用 DataSource 池进行自动配置。下面是选取一个特定实现的算法:
- 由于 Tomcat 数据源连接池的性能和并发,在 tomcat 可用时,我们总是优先使用它。
- 如果 HikariCP 可用,我们将使用它。
- 如果 Commons DBCP 可用,我们将使用它,但在生产环境不推荐使用它。
- 最后,如果 Commons DBCP2 可用,我们将使用它。
如果你使用 spring-boot-starter-jdbc 或 spring-boot-starter-data-jpa,你将会自动获取对 tomcat-jdbc 的依赖。
DataSource 配置通过外部配置文件的spring.datasource.
属性控制。示例中,你可能会在application.properties
中声明下面的片段:
spring.datasource.url=jdbc:mysql://localhost/test spring.datasource.username=dbuser spring.datasource.password=dbpass spring.datasource.driver-class-name=com.mysql.jdbc.Driver
其他可选的配置可以查看DataSourceProperties。同时注意你可以通过spring.datasource.
配置任何 DataSource 实现相关的特定属性:具体参考你使用的连接池实现的文档。
既然 Spring Boot 能够从大多数数据库的 url 上推断出 driver-class-name,那么你就不需要再指定它了。对于一个将要创建的 DataSource 连接池,我们需要能够验证 Driver 是否可用,所以我们会在做任何事情之前检查它。比如,如果你设置
spring.datasource.driverClassName=com.mysql.jdbc.Driver
,然后这个类就会被加载。
Spring 的 JdbcTemplate 和 NamedParameterJdbcTemplate 类是被自动配置的,你可以在自己的 beans 中通过@Autowire
直接注入它们。
如果数据源是 jndi,则定义:
spring.datasource.jndi-name=java:jboss/datasources/customers
XML 配置
如果不想使用注解进行配置,则可以使用 xml 配置文件,修改 main 方法如下:
public static void main(String[] args) throws Exception { SpringApplication.run("classpath:/META-INF/application-context.xml", args); }
META-INF/application-context.xml 文件如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <context:property-placeholder/> <bean id="helloService" class="com.javachen.example.service.HelloWorldService"/> <bean id="application" class="com.javachen.example.Application"/> </beans>