spring boot自动配置原理浅析

使用@SpringBootApplication 标记主程序为spring boot程序,其中@SpringBootApplication注解是一个复合注解,包含三个重要的注解@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan,如下所示。

@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 {
//......
}

大概解析一下注解的作用,如下。
@SpringBootConfiguration
@SpringBootConfiguration继承自@Configuration,二者功能也一致,标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。这样我们@Bean定义的bean才能加入到spring的容器中。
@ComponentScan
@ComponentScan,如果不设置basePackage的话 默认会扫描包的所有类,所以最好还是写上basePackage ,减少加载时间。
@EnableAutoConfiguration
@EnableAutoConfiguration注解是spring boot自动化配置的核心,表示启用自动配置,其源码如下所示。

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

   String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
//......
}

@Import
@Import注解支持导入普通的java类,并将其声明成一个实例bean。@Import(EnableAutoConfigurationImportSelector.class)导入了类EnableAutoConfigurationImportSelector,在EnableAutoConfigurationImportSelector中,使用loadFactoryNames方法加载自动配置类路径,代码如下。

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信息,loadFactoryNames方法实现如下。

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

这个方法核心是读取 META-INF/路径下面的spring.factories文件中定义的自动配置类路径,根据EnableAutoConfiguration全限定名读取属性,属性值用‘去,’分割,转换为类路径数组。最终会根据得到的类路径加载所有的自动配置类bean。其中spring.factories文件内容截图如下。
spring.factories自动配置类相关属性
简单说下,其中加载自动配置bean的操作大部分在ConfigurationClassParser中完成。
根据获取到类路径,在ConfigurationClassParser中,转换为SourceClass,然后在ConfigurationClassParser中processImports方法根据sourceClass加载自动配置bean到容器。(这部分代码还没仔细看)。

/**
 * Factory method to obtain {@link SourceClass}s from class names.
 */
private Collection<SourceClass> asSourceClasses(String[] classNames) throws IOException {
   List<SourceClass> annotatedClasses = new ArrayList<SourceClass>(classNames.length);
   for (String className : classNames) {
      annotatedClasses.add(asSourceClass(className));
   }
   return annotatedClasses;
}

下面以其中一个自动配置作为例子,说明自动配置的过程。
以RabbitAutoConfiguration为例子。代码如下。

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

   @Configuration
   @ConditionalOnMissingBean(ConnectionFactory.class)
   protected static class RabbitConnectionFactoryCreator {

      @Bean
      public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties config)
            throws Exception {
         RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean();
         //......
         return connectionFactory;
      }

   }
//......

@ConditionalOnClass 其用途是判断当前classpath下是否存在指定类,若是则将当前的配置装载入spring容器。

@ConfigurationProperties 注解主要用来把properties配置文件转化为bean来使用的

@EnableConfigurationProperties注解的作用是@ConfigurationProperties注解生效。如果只配置@ConfigurationProperties注解,在IOC容器中是获取不到properties配置文件转化的bean的。

@ConditionalOnMissingBean(ConnectionFactory.class)表示在没有ConnectionFactory的bean的时候执行加载

@EnableConfigurationProperties(RabbitProperties.class),在RabbitProperties中,定义了大部分属性默认值,使用@EnableConfigurationProperties注解启用RabbitProperties配置的bean,方法rabbitConnectionFactory为例,方法参数使用的spring容器中定义的RabbitProperties的bean,在加载RabbitAutoConfiguration 的bean 的时候,方法rabbitConnectionFactory上有@Bean注解,在容器中创建一个CachingConnectionFactory实例,达到自动配置的效果。

猜你喜欢

转载自blog.csdn.net/qq_20597727/article/details/80740308