1.自动配置的概念
    随着Ruby,Groovy等动态语言的流行,相比之下,Java的开发显得格外的笨重;配置很繁重,部署也很复杂,记得之前刚开始接触SpringMvc的时候xml文件好多好大,随着项目越来越大,xml文件越来越多,很容易出错;现在SpringBoot简化了Spring的搭建和开发过程,个人觉得SpringBoot最大的优势就是starter简化配置和Spring的自动配置;
    使用starter简化依赖的配置:核心原理是Maven和Gradle的依赖传递,当在gradle中添加某个starter时,starter的依赖也会自动的传递依赖进来;

    SpringBoot的自动配置:根据类路径中jar包,类,自动配置对应的bean,极大减少了配置的数量;简单说就是会根据gradle或者maven中的类,自动给你生成一些bean,加载到Spring的context中;自动配置充分利用了Spring 4.0的条件配置特性,能够自动配置特定的Spring Bean,启动某些特性;
    2.SpringBoot中的自动配置原理

    1>首先看SpringBoot应用程序入口
    image.png

    @SpringBootApplication是组合注解,其中包含内容如下
    image.png

    自动配置主要起作用的是@EnableAutoConfiguration;
    2>EnableAutoConfiguration
    image.png

    3>import了EnableAutoConfigurationImportSelector,代码如下
    public class AutoConfigurationImportSelector
    implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
    BeanFactoryAware, EnvironmentAware, Ordered {

    private static final String[] NOIMPORTS = {};

    private static final Log logger = LogFactory
    .getLog(AutoConfigurationImportSelector.class);

    private ConfigurableListableBeanFactory beanFactory;

    private Environment environment;

    private ClassLoader beanClassLoader;

    private ResourceLoader resourceLoader;

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
    return NO_IMPORTS;
    }
    try {
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
    .loadMetadata(this.beanClassLoader);
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List configurations = getCandidateConfigurations(annotationMetadata,
    attributes);
    configurations = removeDuplicates(configurations);
    configurations = sort(configurations, autoConfigurationMetadata);
    Set exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return configurations.toArray(new String[configurations.size()]);
    }
    catch (IOException ex) {
    throw new IllegalStateException(ex);
    }
    }
    这里重写了ImportSelector的selectImports方法,看注释这个方法的作用是返回哪些类应该被导入的
    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);

    }
    来看下这里调用的getCandidateConfigurations方法代码
    /

    Return the auto-configuration class names that should be considered. By default
    this method will load candidates using {@link SpringFactoriesLoader} with
    {@link #getSpringFactoriesLoaderFactoryClass()}.
    @param metadata the source metadata
    @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
    attributes}
    @return a list of candidate configurations
    /_
    protected List getCandidateConfigurations(AnnotationMetadata metadata,
    AnnotationAttributes attributes) {
    List 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()方法;关键的方法在这里了,我们看下loadFactoryNames方法:
    public static List loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    try {
    Enumeration urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    List result = new ArrayList();
    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);
    }
    }
    这里的FACTORIES_RESOURCE_LOCATION内容如下所示:
    public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/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 Configuration Import Listeners
    org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
    org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

    # Auto Configuration Import Filters
    org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
    org.springframework.boot.autoconfigure.condition.OnClassCondition

    # 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,\
    ………………….
    这个就是自动配置的列表,也就是SpingBoot默认会加载这些自动配置的类;下面我们以redis为例来说明,具体是怎么自动配置的;
    image.png

    根据这个,我们来对应的目录找到RedisAutoConfiguration
    image.png

    这里截取部分代码如图所示
    @Configuration
    @ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
    @EnableConfigurationProperties(RedisProperties.class)
    public class RedisAutoConfiguration {

    /**
    * Redis connection configuration.
    */
    @Configuration
    @ConditionalOnClass(GenericObjectPool.class)
    protected static class RedisConnectionConfiguration {

    private final RedisProperties properties;

    private final RedisSentinelConfiguration sentinelConfiguration;

    private final RedisClusterConfiguration clusterConfiguration;

    public RedisConnectionConfiguration(RedisProperties properties,
    ObjectProvider sentinelConfiguration,
    ObjectProvider clusterConfiguration) {
    this.properties = properties;
    this.sentinelConfiguration = sentinelConfiguration.getIfAvailable();
    this.clusterConfiguration = clusterConfiguration.getIfAvailable();
    }
    @Bean
    @ConditionalOnMissingBean(RedisConnectionFactory.class)
    public JedisConnectionFactory redisConnectionFactory()
    throws UnknownHostException {
    return applyProperties(createJedisConnectionFactory());
    }
    这个类进行了简单的Spring配置,声明了Redis所需典型Bean,和其它很多类一样,重度依赖于Spring Boot注释:
    1)@ConditionOnClass**一个配置,当类路径中存在这个类时才会配置该类
    2)@EnableConfigurationProperties自动映射一个POJO到Spring Boot配置文件(默认是application.properties文件)的属性集。

    3)@ConditionalOnMissingBean启用一个Bean定义,但必须是这个Bean之前未定义过才有效。
    4>这里了解下condition的几个注解
    1、@ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean
    2、@ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean),该注解的参数对应的类必须存在,否则不解析该注解修饰的配置类
    3、@ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean
    4、@ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean,该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean,可以给该注解传入参数例如@ConditionOnMissingBean(name = “example”),这个表示如果name为“example”的bean存在,这该注解修饰的代码块不执行
    5>Properties系列注释
    @EnableConfigurationProperties
    @ConfigurationProperties(prefix = “may”)
    在需要注入配置的类上加上这个注解,prefix的意思是,以该前缀打头的配置,以下是例子
    @ConfigurationProperties(prefix = “may”)
    public class User {
    private String name;
    private String gender;
    }
    application.yml中的配置
    may
    name: youjie

    gender: man