springboot注解之:@EnableAutoConfiguration

Posted Small leaf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot注解之:@EnableAutoConfiguration相关的知识,希望对你有一定的参考价值。

spring注解:@ComponentScan,@Bean,@Import,@Component,这篇文章当中讲到ConfigurationClassPostProcessor是我们实现注解的核心类,改类在spring启动的时候,会去加载该类到spring容器当中,因为该类是BeanDefinitionRegistryPostProcessor的子类,在spring生命周期当中它会去执行其相应的方法。

大致处理流程就是:
1、先去扫描已经被@Component所注释的类,当然会先判断有没有@Condition相关的注解。
2、然后递归的取扫描该类中的@ImportResource,@PropertySource,@ComponentScan,@Bean,@Import。一直处理完。

上篇提到
@Import:实例化一个指定的类,当这个类没有实现ImportSelector或者ImportBeanDefinitionRegistrar,会把它当中一个普通的@Configuration来处理。

现在我们就来讲解下ImportSelector或者ImportBeanDefinitionRegistrar到底是用来干嘛,我们可以如何自己来实现。

题外话:上篇博客是2017-12-14今天是2018-2月-8日。好像是12月底,我成为一位股民,从买股票必须100股才能算一手不知道,到两周不到盈利居然快到50%,一两天就能获得接近一个月的工资,瞬间觉得撸代码没啥意思,工作也没啥意思,这样赚钱是不是早日可以实现财富自由,到后来各种追涨杀跌,股灾来临,竟然浮亏了一点。幸好比较理智,及时止损,持平状态下出局,回想起这两个月,内心发生了极大的变化。虽然浮亏了,但是纠正了自己的错误的投资观念。内心也获得了极大的成长,从胜利到失败,这个过程体会了很多。

所以写篇博客,来冷静下:

先来看看ImportSelector

package org.springframework.context.annotation;

import org.springframework.core.type.AnnotationMetadata;

/**
 * Interface to be implemented by types that determine which @@link Configuration
 * class(es) should be imported based on a given selection criteria, usually one or more
 * annotation attributes.
 *
 * <p>An @link ImportSelector may implement any of the following
 * @link org.springframework.beans.factory.Aware Aware interfaces, and their respective
 * methods will be called prior to @link #selectImports:
 * <ul>
 * <li>@link org.springframework.context.EnvironmentAware EnvironmentAware</li>
 * <li>@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware</li>
 * <li>@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware</li>
 * <li>@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware</li>
 * </ul>
 *
 * <p>ImportSelectors are usually processed in the same way as regular @code @Import
 * annotations, however, it is also possible to defer selection of imports until all
 * @code @Configuration classes have been processed (see @link DeferredImportSelector
 * for details).
 *
 * @author Chris Beams
 * @since 3.1
 * @see DeferredImportSelector
 * @see Import
 * @see ImportBeanDefinitionRegistrar
 * @see Configuration
 */
public interface ImportSelector 

    /**
     * Select and return the names of which class(es) should be imported based on
     * the @link AnnotationMetadata of the importing @@link Configuration class.
     */
    String[] selectImports(AnnotationMetadata importingClassMetadata);

意思大概是:实现这个接口可以根据自己的筛选原则,来决定哪个@Configuration能够被导入。这个接口也可以实现Aware的接口,selectImports方法在Aware对应的方法后面执行。
selectImports:就是返回哪些类能够被导入,根据筛选条件。

我们来看看:DeferredImportSelector

package org.springframework.context.annotation;

/**
 * A variation of @link ImportSelector that runs after all @code @Configuration beans
 * have been processed. This type of selector can be particularly useful when the selected
 * imports are @code @Conditional.
 *
 * <p>Implementations can also extend the @link org.springframework.core.Ordered
 * interface or use the @link org.springframework.core.annotation.Order annotation to
 * indicate a precedence against other @link DeferredImportSelectors.
 *
 * @author Phillip Webb
 * @since 4.0
 */
public interface DeferredImportSelector extends ImportSelector 

意思是:DeferredImportSelector这个实现的类会执行当所有的@Configuration注解的bean被处理后执行。这个类主要是用来处理@Conditional的。我们也可以用 @Ordered 决定多个 DeferredImportSelector 的执行顺序。

来看看:ImportBeanDefinitionRegistrar

package org.springframework.context.annotation;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.core.type.AnnotationMetadata;

/**
 * Interface to be implemented by types that register additional bean definitions when
 * processing @@link Configuration classes. Useful when operating at the bean definition
 * level (as opposed to @code @Bean method/instance level) is desired or necessary.
 *
 * <p>Along with @code @Configuration and @link ImportSelector, classes of this type
 * may be provided to the @@link Import annotation (or may also be returned from an
 * @code ImportSelector).
 *
 * <p>An @link ImportBeanDefinitionRegistrar may implement any of the following
 * @link org.springframework.beans.factory.Aware Aware interfaces, and their respective
 * methods will be called prior to @link #registerBeanDefinitions:
 * <ul>
 * <li>@link org.springframework.context.EnvironmentAware EnvironmentAware</li>
 * <li>@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware
 * <li>@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware
 * <li>@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware
 * </ul>
 *
 * <p>See implementations and associated unit tests for usage examples.
 *
 * @author Chris Beams
 * @since 3.1
 * @see Import
 * @see ImportSelector
 * @see Configuration
 */
public interface ImportBeanDefinitionRegistrar 

    /**
     * Register bean definitions as necessary based on the given annotation metadata of
     * the importing @code @Configuration class.
     * <p>Note that @link BeanDefinitionRegistryPostProcessor types may <em>not</em> be
     * registered here, due to lifecycle constraints related to @code @Configuration
     * class processing.
     * @param importingClassMetadata annotation metadata of the importing class
     * @param registry current bean definition registry
     */
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

这个类就是用来注入一些我们想要的类。也就是我们实现了该接口就可以自己个性化在spring容器当中注入自己想要的类了。

接下来就来了解@EnableAutoConfiguration:

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

从上篇中我们知道了,ConfigurationClassPostProcessor在扫描@Component时候会去判断该类是否有@Import并且判断是不是实现了ImportSelector或者ImportBeanDefinitionRegistrar不是的话就按照普通的@Configuration放在spring容器当中。

EnableAutoConfigurationImportSelector类:

public class EnableAutoConfigurationImportSelector
        extends AutoConfigurationImportSelector 


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

EnableAutoConfigurationImportSelector实现了DeferredImportSelector,我们来看看实现类

@Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) 
        if (!isEnabled(annotationMetadata)) 
            return NO_IMPORTS;
        
        try 
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                    .loadMetadata(this.beanClassLoader);
            //获得注解下的一些属性信息,也就是EnableAutoConfiguration类下的,exclude和excludeName
            AnnotationAttributes attributes = getAttributes(annotationMetadata);

            List<String> configurations = getCandidateConfigurations(annotationMetadata,
                    attributes);
            //去除重复的配置
            configurations = removeDuplicates(configurations);
            configurations = sort(configurations, autoConfigurationMetadata);
            Set<String> exclusions = getExclusions(annotationMetadata, attributes);
            checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            //根据注解上attributes来过滤相关配置处理类
            configurations = filter(configurations, autoConfigurationMetadata);
            fireAutoConfigurationImportEvents(configurations, exclusions);
            return configurations.toArray(new String[configurations.size()]);
        
        catch (IOException ex) 
            throw new IllegalStateException(ex);
        
    

重点是上面
List < String > configurations = getCandidateConfigurations(annotationMetadata,
attributes);
这里获取了springboot自动配置相关类

    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;
    
protected Class<?> getSpringFactoriesLoaderFactoryClass() 
        return EnableAutoConfiguration.class;
    


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);
        
    

String factoryClassName = factoryClass.getName();
这行代码我们这里取出的是:
org.springframework.boot.autoconfigure.EnableAutoConfiguration

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

从META-INF/spring.factories取出factoryClassName对应的url。
在很多包中我们都会有这个文件,我们既然是springboot,那么我们查找下对应的jar下的文件。
我们来看看META-INF/spring.factories是什么


# 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

后面的流程就是,将这里类读取出来,通过反射注册到spring容器当中,因为我们实现自动配置需要用到这些配置类。

例如:

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

RabbitAutoConfiguration也是一个配置使用了@Configuration,@ConditionalOnClass就是在存在这个指定类的时候才会将这个类注册到spring容器当中。

@EnableAutoConfiguration使用了@Import,然后通过selectImports从META-INF/spring.factories中取出想要的配置,将这些类注册到spring容器当中。

到了这里,如果让你写一个新的jar,要求使用到了spring自动配置如何做?
1、我们只需要在自己的jar下面的配置文件INF/spring.factories写入:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=这里是我们希望spring容器启动时希望自己加载配置的地方。

2、使用@EnableAutoConfiguration注解即可。

也许你会问,我为什么要这样做,我项目里面直接用@Import不行吗?当然可以,既然是自动配置,那当然是希望项目启动就加载。

例如springcloud当中很多类似的,我一定要导入这个配置,为什么还要手动写一次,万一忘记启动就报错,这样自动配置就好多了。

ImportBeanDefinitionRegistrar类就相对简单多了,写个例子:

public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar 

    private static final String BEAN="stu";
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) 
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(Student.class);

        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        registry.registerBeanDefinition(BEAN, beanDefinition);
    

简单理解,就是这个提供了BeanDefinitionRegistry,我们继承这个类,然后在这里可以做自己想要的注册工作。方法提供给你了,有没有需求就看你了。

下一篇就讲解下,@Conditional,其实很好理解的,根据这命名来就知道什么意思,下篇从源码的角度分析,是如何实现的,重点也是ConfigurationClassPostProcessor。

写完这篇文章,内心平静了很多,当初入股市,只是单纯的想去玩玩,没想到入市初胜,让我迷失了方向,一度不想学习了,每天只想盯着K线,只想发财。很庆幸的是,我还很年轻,亏的起,虽然不是大彻大悟,至少醒悟了,明白了自己已经迷失了,而且这个过程确实学到了很多,关于投资的观念,也明白了为什么会有那么多人葬身股市。人最大的敌人就是自己,控制自己的欲望,贪婪,惰性等等

退市是不可能的,入市估计很难。

瞎想啥呢?还不滚去工作…


菜鸟不易,望有问题指出,共同进步

以上是关于springboot注解之:@EnableAutoConfiguration的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot之常用注解

SpringBoot之常用注解

SpringBoot之@SpringBootApplication注解详解

SpringBoot系列之@ConfigurationProperties VS @Value注解

SpringBoot自动配置的原理及实现/SpringBoot之@Import注解正确使用方式

springboot注解之:@EnableAutoConfiguration