spring boot自动配置

@SpringBootApplication

     @SpringBootApplication注解是Spring Boot的核心注解,是一个组合注解,核心功能由@EnableAutoConfiguration注解提供.

@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

  自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

AutoConfigurationImportSelector:加载自动装配类

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, 
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { }
public interface DeferredImportSelector extends ImportSelector { } public interface ImportSelector { String[] selectImports(AnnotationMetadata var1); }

  AutoConfigurationImportSelector 类实现ImportSelector接口,实现接口中的 selectImports方法,方法主要用于获取所有符合条件的类的全限定类名,类需要被加载到 IoC 容器中。

private static final String[] NO_IMPORTS = new String[0];

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // <1>.判断自动装配开关是否打开
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
          //<2>.获取所有需要装配的bean
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, 
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } }

  重点关注一下getAutoConfigurationEntry()方法,主要负责加载自动配置类

 

 getAutoConfigurationEntry()的源码

private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();

AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        //<1>.
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            //<2>.
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //<3>.
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            //<4>.
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

  判断自动装配开关是否打开。默认spring.boot.enableautoconfiguration=true,可在 application.properties 或 application.yml 中设置

  第 2 步 :用于获取EnableAutoConfiguration注解中的 exclude 和 excludeName

  第 3 步:获取需要自动装配的所有配置类,读取META-INF/spring.factories

  /*
     所有的配置都存放在configurations中,而这些配置都从getCandidateConfiguration中获取,这个方法是用来获取候选的配置。
  */
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; }

  获取类路径下spring.factories下key为EnableAutoConfiguration全限定名对应值SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,this.beanClassLoader))

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryClassName = ((String) entry.getKey()).trim();
                    for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

  SpringBoot项目启动的时候,先导入AutoConfigurationImportSelector,这个类会帮选择所有候选的配置,需要导入的配置都是SpringBoot写好的一个一个的配置类,那么这些配置类的位置,存在与META-INF/spring.factories文件中,通过这个文件,Spring可以找到这些配置类的位置,于是去加载其中的配置。

  

  @ConditionalOnXXX:如果其中的条件都满足,该类才会生效。

  所以在加载自动配置类的时候,并不是将spring.factories的配置全量加载进来,而是通过这个注解的判断,如果注解中的类都存在,才会进行加载

RabbitAutoConfiguration 

   RabbitAutoConfiguration类上的注解:

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

  @ConditionalOnClass:表示存在对应的Class文件时才会去解析RabbitAutoConfiguration,否则直接跳过不解析,这也是为什么在不导入RabbitMQ依赖Jar时工程能正常启动的原因

  @EnableConfigurationProperties:表示对@ConfigurationProperties的内嵌支持,默认会将对应Class这是为bean,例如这里值为RabbitProperties.class,其定义为:

@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitProperties {
 
    /**
     * RabbitMQ host.
     */
    private String host = "localhost";
 
    /**
     * RabbitMQ port.
     */
    private int port = 5672;   .... //省略部分代码}

  RabbitProperties提供对RabbitMQ的配置信息,随后@EnableConfigurationProperties会将RabbitProperties注册为一个bean。

  @Import为导入配置,RabbitAnnotationDrivenConfiguration具体实现如下:

@Configuration
@ConditionalOnClass(EnableRabbit.class)
class RabbitAnnotationDrivenConfiguration {
 
    @Autowired(required = false)
    private PlatformTransactionManager transactionManager;
 
    @Bean
    @ConditionalOnMissingBean(name = "rabbitListenerContainerFactory")
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
            ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        if (this.transactionManager != null) {
            factory.setTransactionManager(this.transactionManager);
        }
        return factory;
    }
 
    @EnableRabbit
    @ConditionalOnMissingBean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
    protected static class EnableRabbitConfiguration {
 
    }
}

  @ConditionalOnMissingBean,其功能为如果存在指定name的bean,则该注解标注的bean不创建, 

     再回到RabbitAutoConfiguration类的具体实现  

@Configuration
    @ConditionalOnMissingBean(ConnectionFactory.class)
    protected static class RabbitConnectionFactoryCreator {
 
        @Bean
        public ConnectionFactory rabbitConnectionFactory(RabbitProperties config) {
            CachingConnectionFactory factory = new CachingConnectionFactory();
            String addresses = config.getAddresses();
            factory.setAddresses(addresses);
            if (config.getHost() != null) {
                factory.setHost(config.getHost());
                factory.setPort(config.getPort());
            }
            if (config.getUsername() != null) {
                factory.setUsername(config.getUsername());
            }
            if (config.getPassword() != null) {
                factory.setPassword(config.getPassword());
            }
            if (config.getVirtualHost() != null) {
                factory.setVirtualHost(config.getVirtualHost());
            }
            return factory;
        }
 
    }

  创建了默认的ConnectionFactory,需要注意的时,这里的ConnectionFactory无回调的设置

  @Bean
  @ConditionalOnMissingBean(RabbitTemplate.class)
  public RabbitTemplate rabbitTemplate() {
    return new RabbitTemplate(this.connectionFactory);
  }

  创建了默认的RabbitTemplate

posted on 2019-04-14 23:27  溪水静幽  阅读(259)  评论(0)    收藏  举报