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);
}
}
說明:
-
@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>