1. 程式人生 > >SpringBoot @EnableAutoConfiguration原理

SpringBoot @EnableAutoConfiguration原理

spring Boot是一個偏執的開源框架,它可用於建立可執行的spring應用程式,採用了習慣優於配置的方法。
此框架的神奇之處在於@EnableAutoConfiguration註釋,此註釋自動載入應用程式所需的所有Bean——這依賴於Spring Boot在類路徑中的查詢。

一、@Enable*註釋

@Enable*註釋並不是新發明的註釋,早在Spring 3框架就引入了這些註釋,用這些註釋替代XML配置檔案。
很多Spring開發者都知道@EnableTransactionManagement註釋,它能夠宣告事務管理;@EnableWebMvc註釋,它能啟用Spring MVC;以及@EnableScheduling註釋,它可以初始化一個排程器。
這些註釋事實上都是簡單的配置,通過@Import註釋匯入。

**@Import類似於spring配置檔案中的include,這樣spring載入配置檔案時就不用載入多個**
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ EnableAutoConfigurationImportSelector.class,
        AutoConfigurationPackages.Registrar.class })
public @interface EnableAutoConfiguration {

    /**
     * Exclude specific auto-configuration classes such that they will never be applied.
     */
Class<?>[] exclude() default {}; }

EnableAutoConfigurationImportSelector類使用了Spring Core包的SpringFactoriesLoader類的loadFactoryNamesof()方法。
SpringFactoriesLoader會查詢META-INF/spring.factories檔案中包含的JAR檔案。
當找到spring.factories檔案後,SpringFactoriesLoader將查詢配置檔案命名的屬性。在例子中,是org.springframework.boot.autoconfigure.EnableAutoConfiguration
讓我們來看看spring-boot-autoconfigure JAR檔案,它真的包含了一個spring.factories檔案,內容如下:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
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.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
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.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.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
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.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
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.mobile.DeviceResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\
org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\
org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.JspTemplateAvailabilityProvider

在這個檔案中,可以看到一系列Spring Boot自動配置的列表。下面我們來看這些配置的細節,以MongoAutoConfiguration為例:

@Configuration
@ConditionalOnClass(Mongo.class)
@EnableConfigurationProperties(MongoProperties.class)
public class MongoAutoConfiguration {

@Autowired
private MongoProperties properties;

private Mongo mongo;

@PreDestroy
public void close() throws UnknownHostException {
    if (this.mongo != null) {
        this.mongo.close();
    }
}

@Bean
@ConditionalOnMissingBean
public Mongo mongo() throws UnknownHostException {
    this.mongo = this.properties.createMongoClient();
    return this.mongo;
}

}

如果加上
@ConditionalOnMissingBean(type = “org.springframework.data.mongodb.MongoDbFactory”)

Mongo的AutoConfiguration將會在使用者引入Mongo相關包時,並且沒有自定義MongoDbFactory時被啟用,同時配置檔案(application.properties之類的)將注入到MongoProperties中.MongoProperties類由@ConfigurationProperties

可以看到MongoClient最終由MongoAutoConfiguration呼叫MongoProperties的createMongoClient()方法建立.通過標註@Bean將MongoClient釋出到Spring容器中.

如果使用者已經用@Bean自定義了一個MongoClient,那麼Mongo AutoConfig就不會做去初始化MongoClient,配置檔案中的配置也就不生效了.

這個類進行了簡單的Spring配置,聲明瞭MongoDB所需典型Bean。
這個類跟其它很多類一樣,重度依賴於Spring Boot註釋:
1)@ConditionOnClass啟用一個配置,在類路徑中只能存在一到幾個這樣的類。
2)@EnableConfigurationProperties自動對映一個POJO到Spring Boot配置檔案(預設是application.properties檔案)的屬性集。
3)@ConditionalOnMissingBean啟用一個Bean定義,但必須是這個Bean之前未定義過才有效。
還可以使用@ AutoConfigureBefore註釋、@AutoConfigureAfter註釋來定義這些配置類的載入順序。

二、屬性對映

下面看MongoProperties類,它是一個Spring Boot屬性對映(對映到application.properties)的例子:

@ConfigurationProperties(prefix = “spring.data.mongodb”)
public class MongoProperties {

private String host;
private int port = DBPort.PORT;
private String uri = "mongodb://localhost/test";
private String database;

// ... getters/ setters omitted

}
@ConfigurationProperties註釋將POJO關聯到application.properties指定字首的每一個屬性。例如,spring.data.mongodb.port屬性將對映到這個類的埠屬性。
強烈建議Spring Boot開發者使用這種方式來刪除與配置屬性相關的瓶頸程式碼。

三、@Conditional註釋

Spring Boot的強大之處在於使用了Spring 4框架的新特性:@Conditional註釋,此註釋使得只有在特定條件滿足時才啟用一些配置。
在Spring Boot的org.springframework.boot.autoconfigure.condition包中說明了使用@Conditional註釋能給我們帶來什麼,下面對這些註釋做一個概述:

@ConditionalOnBean
@ConditionalOnClass
@ConditionalOnExpression
@ConditionalOnMissingBean
@ConditionalOnMissingClass
@ConditionalOnNotWebApplication
@ConditionalOnResource
@ConditionalOnWebApplication
以@ConditionalOnExpression註釋為例,它允許在Spring的EL表示式中寫一個條件。

@Conditional(OnExpressionCondition.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface ConditionalOnExpression {

/**
 * The SpEL expression to evaluate. Expression should return {@code true} if the
 * condition passes or {@code false} if it fails.
 */
String value() default "true";

}
在這個類中,我們想利用@Conditional註釋,條件在OnExpressionCondition類中定義:

public class OnExpressionCondition extends SpringBootCondition {

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
    // ...
    // we first get a handle on the EL context via the ConditionContext

    boolean result = (Boolean) resolver.evaluate(expression, expressionContext);

    // ...
    // here we create a message the user will see when debugging

    return new ConditionOutcome(result, message.toString());
}

}
在最後,@Conditional通過簡單的布林表示式(即ConditionOutcome方法)來決定。

四、應用程式上下文初始化器

spring.factories還提供了第二種可能性,即定義應用程式的初始化。這使得我們可以在應用程式載入前操縱Spring的應用程式上下文ApplicationContext。
特別是,可以在上下文建立監聽器,使用ConfigurableApplicationContext類的addApplicationListener()方法。
AutoConfigurationReportLoggingInitializer監聽到系統事件時,比如上下文重新整理或應用程式啟動故障之類的事件,Spring Boot可以執行一些工作。這有助於我們以除錯模式啟動應用程式時建立自動配置的報告。
要以除錯模式啟動應用程式,可以使用-Ddebug標識,或者在application.properties檔案這新增屬性debug= true。

五、除錯Spring Boot自動配置

Positive matches:

MessageSourceAutoConfiguration
- @ConditionalOnMissingBean (types: org.springframework.context.MessageSource; SearchStrategy: all) found no beans (OnBeanCondition)

JmxAutoConfiguration
- @ConditionalOnClass classes found: org.springframework.jmx.export.MBeanExporter (OnClassCondition)
- SpEL expression on org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration: ${spring.jmx.enabled:true} (OnExpressionCondition)
- @ConditionalOnMissingBean (types: org.springframework.jmx.export.MBeanExporter; SearchStrategy: all) found no beans (OnBeanCondition)

DispatcherServletAutoConfiguration
- found web application StandardServletEnvironment (OnWebApplicationCondition)
- @ConditionalOnClass classes found: org.springframework.web.servlet.DispatcherServlet (OnClassCondition)

Negative matches:

DataSourceAutoConfiguration
- required @ConditionalOnClass classes not found: org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType (OnClassCondition)

DataSourceTransactionManagerAutoConfiguration
- required @ConditionalOnClass classes not found: org.springframework.jdbc.core.JdbcTemplate,org.springframework.transaction.PlatformTransactionManager (OnClassCondition)

MongoAutoConfiguration
- required @ConditionalOnClass classes not found: com.mongodb.Mongo (OnClassCondition)

FallbackWebSecurityAutoConfiguration
- SpEL expression on org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration: !${security.basic.enabled:true} (OnExpressionCondition)

SecurityAutoConfiguration
- required @ConditionalOnClass classes not found: org.springframework.security.authentication.AuthenticationManager (OnClassCondition)

EmbeddedServletContainerAutoConfiguration.EmbeddedJetty
- required @ConditionalOnClass classes not found: org.eclipse.jetty.server.Server,org.eclipse.jetty.util.Loader (OnClassCondition)

WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#localeResolver
- @ConditionalOnMissingBean (types: org.springframework.web.servlet.LocaleResolver; SearchStrategy: all) found no beans (OnBeanCondition)
- SpEL expression: ‘${spring.mvc.locale:}’ != ” (OnExpressionCondition)

WebSocketAutoConfiguration
- required @ConditionalOnClass classes not found: org.springframework.web.socket.WebSocketHandler,org.apache.tomcat.websocket.server.WsSci (OnClassCondition)
對於每個自動配置,可以看到它啟動或失敗的原因。

六、結論