zoukankan      html  css  js  c++  java
  • Spring Boot自动装配原理源码分析

    Spring Boot自动装配原理源码分析

    1.环境准备

    使用IDEA Spring Initializr快速创建一个Spring Boot项目

    添加一个Controller类

    @RestController
    public class HelloController {
    
        @RequestMapping("hello")
        public String hello() {
            return "hello";
        }
    }
    

    主配置类如下

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

    2.注解分析

    Spring Boot规定,项目的主配置类必须放在最外层包,也就是说,所有的类都必须放在主配置类的同级包或者子包里,这么做的用意是什么?我们点开@SpringBootApplication注解慢慢分析(下面代码中省略元注解)...

    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { 
    	@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    	@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) 
    })
    public @interface SpringBootApplication {
    

    @SpringBootApplication内部标注了三个注解:

    • @SpringBootConfiguration

      进入源码中可以看见,@SpringBootConfiguration其实就是Spring中的@Configuration,用于标注配置类

      @Configuration
      public @interface SpringBootConfiguration {
      
    • @ComponentScan

      这个注解也是Spring中的,它用来将指定包需要装配的组件注册到容器中

    • @EnableAutoConfiguration

      接下来才是今天的重头戏,Spring Boot自动配置的主角!

      @AutoConfigurationPackage
      @Import(AutoConfigurationImportSelector.class)
      public @interface EnableAutoConfiguration {
      

    3.自动装配的主角

    ​ 进入@EnableAutoConfiguration源码你会发现这个注解中标注了两个注解@AutoConfigurationPackage@Import

    (1)、@AutoConfigurationPackage

    点进该注解

    @Import(AutoConfigurationPackages.Registrar.class)
    public @interface AutoConfigurationPackage {
    }
    

    在点进Register,这是一个静态内部类

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    
      @Override
      public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
          register(registry, new PackageImport(metadata).getPackageName());
       }
    
      @Override
      public Set<Object> determineImports(AnnotationMetadata metadata) {
         return Collections.singleton(new PackageImport(metadata));
      }
        
    }
    

    我们在第一个方法处打一个断点debug一下,发现new PackageImport(metadata).getPackageName()的结果其实就是一个包名,这时我们很容易的可以想到,这个包就是Spring Boot主配置类所在的包

    再看一眼metadata,果然,就是主配置类

    因此,这个注解的作用就是将主配置类所在的包作为自动配置包进行管理

    (2)、@Import(AutoConfigurationImportSelector.class)

    @Import的作用就是导入一个类到IOC容器,我们先来看一下导入的这个类:自动配置导入选择器

    源码里有一个方法selectImports,选择导入

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
       if (!isEnabled(annotationMetadata)) {
          return NO_IMPORTS;
       }
       AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
             .loadMetadata(this.beanClassLoader);
       AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
             annotationMetadata);
       return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
    

    在点进getAutoConfigurationEntry,获取自动配置类

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

    getCandidateConfigurations方法处打一个断点,发现configurations的结果是所有的xxxAtuoConfiguration类,一共124个,请记住这个数字。

    那么这些自动配置类是如何获取的呢,从哪里获取的呢?

    我们继续点进getCandidateConfigurations,获取候选的配置

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    	List<String> configurations = 			          
            SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
             getBeanClassLoader());
       Assert.notEmpty(configurations, "... ...");
       return configurations;
    }
    

    继续点,loadFactoryNames,加载工厂名,方法所在类中有一个常量FACTORIES_RESOURCE_LOCATION,看代码可以清晰的看到,这方法加载classpath下的所有jar包的META-INF/spring.factories文件,结果用一个HashMap存储

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    		String factoryTypeName = factoryType.getName();
    		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    	}
    
    	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 factoryTypeName = ((String) entry.getKey()).trim();
    					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
    						result.add(factoryTypeName, factoryImplementationName.trim());
    					}
    				}
    			}
    			cache.put(classLoader, result);
    			return result;
    		}
    		catch (IOException ex) {
    			throw new IllegalArgumentException("Unable to load factories from location [" +
    					FACTORIES_RESOURCE_LOCATION + "]", ex);
    		}
    	}
    

    打开spring-boot-autoconfigure-2.2.4.RELEASE.jar/META-INF/spring.factories,文件部分类容如下,你可以点进去看看第22~145行,确实是124个全类名

    # 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.CloudServiceConnectorsAutoConfiguration,
    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.CassandraReactiveDataAutoConfiguration,
    org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration
    ... ...
    

    4.类的加载时机

    加载了这么多类我们又不一定全都用得到,设计师肯定会想办法让类在我们需要的时候才生效,我们随便点进一个类,可以看到一片飘红,因为我们并没有引入RabbitMQ相关依赖,再看一个注解ConditionalOnClass,意思就是存在某个指定的类才生效,类似的注解还有很多,都是@ConditionaOn xxx,在一定条件下类才会生效。

    由于引入了web模块,WebMvcAutoConfiguration正常显示

  • 相关阅读:
    [转]
    Linux
    [转]
    [转]
    Linux 高级网络编程
    [转]
    [译]- 6-1 排列窗体上的控件(Laying Out Widgets on a Form)
    [转]
    [转]
    the thread has exited with code -1073741819
  • 原文地址:https://www.cnblogs.com/songjilong/p/12312164.html
Copyright © 2011-2022 走看看