SpringBoot自動配置原理


SpringBoot作為一款全新的框架,有着諸多的優點,正受到越來越多的Java開發者的歡迎。

 

SpringBoot極大地簡化了Spring應用的初始創建和開發過程。其優點如下:

1、內嵌Tomcat,無需部署WAR文件;

2、創建獨立的Spring應用;

3、簡化Maven配置;

4、自動配置Spring;

5、提供生產就緒型功能,如指標,健康檢查和外部配置;

6、絕對沒有代碼生成並且對XML也沒有配置要求。

下面就來深入源碼分析下SpringBoot自動配置的原理(此文針對的SpringBoot版本為1.5.17.RELEASE)。

 首先從主類SpringbootApplication入手:

@SpringBootApplication
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }

}

 類上的@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 {

其中的@EnableAutoConfiguration注解是關鍵所在,其定義如下:

@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

@Import注解可以讓普通的類導入到Spring的IOC容器中,由Spring進行管理。

EnableAutoConfigurationImportSelector類定義如下:

@Deprecated
public class EnableAutoConfigurationImportSelector
        extends AutoConfigurationImportSelector {

由於該類已被標記為過時,我們來看下它的父類AutoConfigurationImportSelector:

public class AutoConfigurationImportSelector
        implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
        BeanFactoryAware, EnvironmentAware, Ordered {

AutoConfigurationImportSelector類實現了多個接口,作為一個選擇器實現了對自動配置類的選擇導入功能,其類圖關系如下:

AutoConfigurationImportSelector類中有一個重要的selectImports方法,其定義如下:

  @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        try {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                    .loadMetadata(this.beanClassLoader);//1.1
            AnnotationAttributes attributes = getAttributes(annotationMetadata);//1.2
            List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);//1.3
            configurations = removeDuplicates(configurations);//1.4
            configurations = sort(configurations, autoConfigurationMetadata);//1.5
            Set<String> exclusions = getExclusions(annotationMetadata, attributes);//1.6
            checkExcludedClasses(configurations, exclusions);//1.7
            configurations.removeAll(exclusions);//1.8
            configurations = filter(configurations, autoConfigurationMetadata);//1.9
            fireAutoConfigurationImportEvents(configurations, exclusions);//2.0
            return configurations.toArray(new String[configurations.size()]);//2.1
        }
        catch (IOException ex) {
            throw new IllegalStateException(ex);
        }
    }

1.1 加載元數據並返回AutoConfigurationMetadata對象;

1.2 獲取傳入的元數據對應的屬性值(AnnotationAttributes對象);

1.3 返回候選自動配置類名的list集合;

1.4 移除list中的重復自動配置類名;

1.5 通過讀取@AutoConfigureOrder、@link AutoConfigureBefore、@AutoConfigureAfter注解對自動配置類名進行優先級排序;

1.6 返回排除掉的自動配置類的set集合;

1.7 檢查候選配置類中是否存在排除項,然后返回給SpringFactoriesLoader加載的候選自動配置類;

1.8 移除候選配置類當中的的所有排除項(不需要自動配置的類)

1.9 利用在spring工廠注冊過的AutoConfigurationImportFilter過濾器對候選自動配置類進行過濾;

2.0 通過AutoConfigurationImportListener監聽器觸發自動配置導入事件;

2.1 返回經過上述步驟得到的自動配置類的數組。

該方法最終返回了能進行自動配置的類的數組。

我們着重看下紅色標注的getCandidateConfigurations方法:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
                getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
        Assert.notEmpty(configurations,
                "No auto configuration classes found in META-INF/spring.factories. If you "
                        + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

其中的getSpringFactoriesLoaderFactoryClass()方法正是返回EnableAutoConfiguration.class。

該方法里面調用了SpringFactoriesLoader.loadFactoryNames()方法掃描加載引入的jar包中包含META-INF/spring.factories文件中的自動配置類。

具體方法如下:

/**
 * The location to look for factories.
 * <p>Can be present in multiple JAR files.
 */
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        try {
            Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            List<String> result = new ArrayList<String>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                String factoryClassNames = properties.getProperty(factoryClassName);
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                    "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

藍色加粗的程序表示會將掃描到的文件內容包裝成properties對象。

紅色標注的文件spring.factories可以在spring-boot-autoconfigure等springboot相關的jar包中找到:

該文件里面內容都是鍵值對形式的,其中spring-boot-autoconfigure這個jar中的org.springframework.boot.autoconfigure.EnableAutoConfiguration正是我們要找的自動配置的類,包括Aop、WebMvc、Jpa、redis、jdbc等自動配置類。

# 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.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.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.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.reactor.ReactorAutoConfiguration,\
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

當然這些自動配置類並不是都能自動配置進來的,里面會根據@Conditional等注解控制bean的創建行為。

只有滿足了條件才能自動注入相關的bean。

以RabbitAutoConfiguration為例,其定義如下:

@Configuration
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {

// Only available in rabbitmq-java-client 5.4.0 +
private static final boolean CAN_ENABLE_HOSTNAME_VERIFICATION = ReflectionUtils
.findMethod(com.rabbitmq.client.ConnectionFactory.class,
"enableHostnameVerification") != null;

  @Bean
  public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties config)
   throws Exception {
   RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean();
  if (config.determineHost() != null) {
factory.setHost(config.determineHost());
    .
    .
    .
  }

@ConditionalOnClass表示有某個類的情況下才會加載bean,這里需要同時有RabbitTemplate和Channel類,當所有條件滿足的情況下,就能通過@Bean注解,讓方法產生一個bean對象,交由Spring容器來進行管理,這樣就能在SpringBoot項目啟動的時候實現自動配置。

如果上述2個類提示找不到,可以在pom.xml引入以下依賴解決:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

@EnableConfigurationProperties(RabbitProperties.class)能實現屬性類的自動配置,RabbitProperties類是配置Rabbitmq屬性用的,其定義如下:

@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitProperties {

    /**
     * RabbitMQ host.
     */
    private String host = "localhost";

    /**
     * RabbitMQ port.
     */
    private int port = 5672;

    /**
     * Login user to authenticate to the broker.
     */
    private String username;

    /**
     * Login to authenticate against the broker.
     */
    private String password;

而這些屬性值我們同樣可以配置在resources下的application.properties文件里面,而配置的這些屬性將會覆蓋RabbitProperties類里面配置的默認值。

#自定義rabbitmq屬性值
spring.rabbitmq.host=198.168.121.1
spring.rabbitmq.username=root
spring.rabbitmq.password=123456

至此,SpringBoot自動配置的源碼分析告一段落,總結如下:

(1)SpringBoot在啟動的時候會掃描所有jar包里面的META-INF/spring.properties,找出自動配置的類信息並將這些信息包裝成properties對象。到時會從properties中獲取EnableAutoConfiguration.class類名對應的值,然后使用@Bean注解將這些bean添加到Spring容器中。

(2)給Spring容器添加自動配置組件時,會從properties類中獲取屬性值,我們可以配置自定義的屬性覆蓋對應xxxProperties.class類里面的默認屬性值,實現個性化配置。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM