詳解Spring Boot自動裝配的方法步驟
在《Spring Boot Hello World》中介紹了一個簡單的spring boot例子,體驗了spring boot中的諸多特性,其中的自動配置特性極大的簡化了程序開發(fā)中的工作(不用寫一行XML)。本文我們就來看一下spring boot是如何做到自動配置的。
首先闡明,spring boot的自動配置是基于spring framework提供的特性實現(xiàn)的,所以在本文中,我們先介紹spring framework的相關特性,在了解了這些基礎知識后,我們再來看spring boot的自動配置是如何實現(xiàn)的。
基于Java代碼對Spring進行配置
在以往使用spring framework進行程序開發(fā)時,相信大家也只是使用XML搭配注解的方式對spring容器進行配置,例如在XML文件中使用<context:component-scan base-package="**"/>指定spring需要掃描package的根路徑。
除了使用XML對spring進行配置,還可以使用Java代碼執(zhí)行完全相同的配置。下面我們詳細看一下如何使用Java代碼對spring容器進行配置,詳細內(nèi)容可參考這里。
使用Java代碼進行spring配置,有兩個核心注解@Configuration和@Bean:
@Configuration
public class AppConfig {
@Bean
public SampleService sampleService() {
return new SampleServiceImpl();
}
}
@Bean注解用于修飾方法,方法的返回值會作為一個bean裝載到spring容器中。bean的id就是方法的名字。
@Configuration注解用于修飾一個類,它表明這個類的作用是用來對spring容器進行配置的。
上面的Java代碼相當于下面的XML配置:
<beans> <bean id="sampleService" class="com.**.SampleServiceImpl"/> </beans>
使用AnnotationConfigApplicationContext類構建一個spring容器,從容器中取出對應的bean的測試代碼如下:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
SampleService myService = ctx.getBean("sampleService" ,SampleService.class);
myService.doService();
}
Java代碼配置ComponentScan
使用@ComponentScan注解指定需要掃描package的根路徑:
@Configuration
@ComponentScan(basePackages = "com.**.service.impl")
public class AppConfig {
}
上面的Java代碼相當于下面的XML配置:
<beans> <context:component-scan base-package="com.**.service.impl"/> </beans>
此外,AnnotationConfigApplicationContext類還提供了scan方法用于指定要掃描的包路徑。我們可以刪除AppConfig類上的@ComponentScan注解,在構造spring容器時使用下面代碼:
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.**.service.impl");
ctx.refresh();
SampleService myService = ctx.getBean("sampleService" ,SampleService.class);
myService.doService();
}
使用@Import組合多個配置
將所有的spring配置全部放在同一個類中肯定是不合適的,這會導致那個配置類非常復雜。通常會創(chuàng)建多個配置類,再借助@Import將多個配置類組合成一個。@Import的功能類似于XML中的<import/>。
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
上面的代碼分別創(chuàng)建了兩個配置類ConfigA和ConfigB,它們分別定義了a和b兩個Bean。在ConfigB上使用@Import注解導入ConfigA的配置,此時應用代碼如果加載ConfigB的配置,就自動也加載了ConfigA的配置。如下代碼所示:
public static void main(String[] args) {
// 只加載ConfigB一個配置類,但同時也包含了ConfigA的配置
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
System.out.println(a);
System.out.println(b);
}
@Import還可以同時導入多個配置類。當有多個配置類需要同時導入時,示意代碼如下:
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
條件注解@Conditional
@Conditional注解根據(jù)某一個條件是否成立來判斷是否構建Bean。借助Condition接口可以表示一個特定條件。例如下面代碼實現(xiàn)了一個條件,當然這個條件始終成立:
public class SampleCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 如果條件成立返回true, 反之返回false
return true;
}
}
有了表達條件的類SampleCondition,接下來我們就可以通過@Conditional注解對創(chuàng)建bean的函數(shù)進行配置:
請輸入代碼@Configuration
public class ConditionConfig {
// 只有當滿足SampleCondition指定的條件時,參會構造id時sampleBean這個bean。當然這里的條件始終成立
@Conditional(SampleCondition.class)
@Bean
public SampleBean sampleBean() {
return new SampleBean();
}
}
由于SampleCondition的matches方法返回true,表示創(chuàng)建bean的條件成立,所以sampleBean會被創(chuàng)建。如果matches返回false,sampleBean就不會被構建。
在spring boot中,根據(jù)這個原理提供了很多@ConditionOnXXX的注解,這些注解都在包org.springframework.boot.autoconfigure.condition下面。例如比較常見的@ConditionalOnClass注解,這個注解的判斷邏輯是只有指定的某個類在classpath上存在時,判斷條件才成立。@ConditionalOnClass的具體代碼如下:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}
@ConditionalOnClass具體的判斷邏輯可參看OnClassCondition類。
@SpringBootApplication注解
介紹完前面這些基礎的知識后,我們來看Spring Boot是如何實現(xiàn)自動裝配的。
《Spring Boot官方文檔第14章》推薦在程序的main class上使用注解@SpringBootApplication對Spring應用進行自動配置,我們就從分析這個注解開始。下面是@SpringBootApplication主要代碼:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// ...
@SpringBootApplication是一個組合注解,主要由@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan三個注解構成。
@SpringBootConfiguration表明被標注的類提供了Spring Boot應用的配置,其實這個注解與@Configuration注解的功能類似。
@ComponentScan指定需要掃描package的路徑,@SpringBootApplication也提供了相應屬性,指定需要掃描哪些package或不掃描哪些package?!禨pring Boot官方文檔》建議將應用的main class放置于整個工程的根路徑,并用@SpringBootApplication注解修飾main class,這樣整個項目的子package就都會被自動掃描包含。建議的工程結構如下所示,其中Application就是應用的main class。
com
+- example
+- myapplication
+- Application.java
|
+- customer
| +- Customer.java
| +- CustomerController.java
| +- CustomerService.java
| +- CustomerRepository.java
|
+- order
+- Order.java
+- OrderController.java
+- OrderService.java
+- OrderRepository.java
@EnableAutoConfiguration是這里最重要的注解,它實現(xiàn)了對Spring Boot應用自動裝配的功能。@EnableAutoConfiguration是利用SpringFactoriesLoader機制加載自動裝配配置的,它的配置數(shù)據(jù)在META-INF/spring.factories中,我們打開spring-boot-autoconfigure jar中的該文件,發(fā)現(xiàn)EnableAutoConfiguration對應著N多XXXAutoConfiguration配置類,我們截取幾個重要的配置類如下(已經(jīng)刪除了很多):
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\ org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\ org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\ org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
可以看到Spring Boot提供了N多XXXAutoConfiguration類,有Spring Framework的、Web的、redis的、JDBC的等等。
我們從其中選擇HttpEncodingAutoConfiguration這個類來看下它是如何實現(xiàn)自動配置的:
@Configuration
@EnableConfigurationProperties(HttpProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled",
matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final HttpProperties.Encoding properties;
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
上面代碼表示,只有在滿足如下條件時,才會注入characterEncodingFilter這個bean:
- 只有在WebApplication的情況
- classpath上必須存在CharacterEncodingFilter類
- 配置文件中配置了spring.http.encoding.enabled為true或者沒有配置
- Spring容器中不存在類型為CharacterEncodingFilter的bean
總結
Spring Boot自動裝配的原理并不是非常復雜,其實背后的主要原理就是條件注解。
當我們使用@EnableAutoConfiguration注解激活自動裝配時,實質(zhì)對應著很多XXXAutoConfiguration類在執(zhí)行裝配工作,這些XXXAutoConfiguration類是在spring-boot-autoconfigure jar中的META-INF/spring.factories文件中配置好的,@EnableAutoConfiguration通過SpringFactoriesLoader機制創(chuàng)建XXXAutoConfiguration這些bean。XXXAutoConfiguration的bean會依次執(zhí)行并判斷是否需要創(chuàng)建對應的bean注入到Spring容器中。
在每個XXXAutoConfiguration類中,都會利用多種類型的條件注解@ConditionOnXXX對當前的應用環(huán)境做判斷,如應用程序是否為Web應用、classpath路徑上是否包含對應的類、Spring容器中是否已經(jīng)包含了對應類型的bean。如果判斷條件都成立,XXXAutoConfiguration就會認為需要向Spring容器中注入這個bean,否則就忽略。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
為什么rest接口返回json建議采用下劃線形式,不要用駝峰
為什么rest接口返回json建議采用下劃線形式,不要用駝峰?今天小編就來為大家說明一下原因,還等什么?一起跟隨小編過來看看吧2020-09-09
SpringBoot3集成SLF4J+logback進行日志記錄的實現(xiàn)
本文主要介紹了SpringBoot3集成SLF4J+logback進行日志記錄的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01
IDEA巧用Postfix Completion讓碼速起飛(小技巧)
這篇文章主要介紹了IDEA巧用Postfix Completion讓碼速起飛,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-08-08
java實用小技巧之判斷l(xiāng)ist是否有重復項簡單例子
這篇文章主要給大家介紹了關于java實用小技巧之判斷l(xiāng)ist是否有重復項的相關資料,在開發(fā)工作中我們有時需要去判斷List集合中是否含有重復的元素,需要的朋友可以參考下2023-10-10

