springboot启动自动配置分析

 springboot的主启动类分析(默认情况下):
  原类:
    @SpringBootApplication     //  @SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用 
    public class SuperMallApplication {
        public static void main(String[] args) {
            SpringApplication.run(SuperMallApplication.class, args);
        }
    }
  1.@SpringBootApplication  //@SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用 ,是把多个元注解的属性组合在一起形成新的注解
    源码: 
      @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Inherited             //这个注解可以被继承
      @SpringBootConfiguration       //这个注解实际上和@Configuration有相同的作用,配备了该注解的类就能够以JavaConfig的方式完成一些配置,可以不再使用XML配置。
      @EnableAutoConfiguration       //
      @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
          @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
      public @interface SpringBootApplication {
        //组合元注解EnableAutoConfiguration的exclude属性,作用:排除特定的自动配置类,使其永远不会被自动配置,通过 .class的方式指定
        @AliasFor(annotation = EnableAutoConfiguration.class)
        Class<?>[] exclude() default {};
        /组合元注解EnableAutoConfiguration的excludeName属性,作用:排除特定的自动配置类,使其永远不会被自动配置,通过类名的方式指定
        @AliasFor(annotation = EnableAutoConfiguration.class)
        String[] excludeName() default {};
        //扫描指定报下的实体并且加入到spring容器中,参数是包名的字符串数组,
        @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
        String[] scanBasePackages() default {};
        //扫描指定报下的实体并且加入到spring容器中,参数类似是Class类型数组。
        @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
        Class<?>[] scanBasePackageClasses() default {};
 
        @AliasFor(annotation = Configuration.class)
        boolean proxyBeanMethods() default true;
      }
  2.@AutoConfigurationPackage
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(AutoConfigurationPackages.Registrar.class) //使用Spring的底层注解@Import,给容器中导入AutoConfigurationPackages.Registrar.组件 
    public @interface AutoConfigurationPackage {
 
    }
    AutoConfigurationPackages.Registrar组件的功能:
      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));
        }
      }
      总结: 因此可以得知使用@AutoConfigurationPackage注解就是将主程序类所在包及所有子包下的组件到扫描到spring容器中
 3.@EnableAutoConfiguration springboot的配置注解
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited            //表明这个注解是可以被继承的
    @AutoConfigurationPackage    
    @Import(AutoConfigurationImportSelector.class) //使用Spring的底层注解@Import,给容器中导入AutoConfigurationImportSelector组件 
 
    public @interface EnableAutoConfiguration {
      String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
      Class<?>[] exclude() default {};     
      String[] excludeName() default {};     
    }
    AutoConfigurationImportSelector组件的功能:
      public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
            //......
            @Override
            public String[] selectImports(AnnotationMetadata annotationMetadata) {
                // 如果AutoConfiguration没开,返回{}
                if (!isEnabled(annotationMetadata)) {
                    return NO_IMPORTS;
                }
                // 将spring-autoconfigure-metadata.properties的键值对配置载入到PropertiesAutoConfigurationMetadata对象中并返回
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                        .loadMetadata(this.beanClassLoader);
                // 基于各种配置计算需要import的configuration和exclusion
                AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                        annotationMetadata);
                return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
            }
                     
            // 判断AudoConfiguration是否开启
            protected boolean isEnabled(AnnotationMetadata metadata) {
                if (getClass() == AutoConfigurationImportSelector.class) {
                    // 如果配置文件中有"spring.boot.enableautoconfiguration",返回该字段的值;否则返回true
                    return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
                }
                return true;
            }
                     
            protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
                if (!isEnabled(annotationMetadata)) {
                    return EMPTY_ENTRY;
                }
                // 获取注解的属性值
                AnnotationAttributes attributes = getAttributes(annotationMetadata);
                // 从META-INF/spring.factories文件中获取EnableAutoConfiguration所对应的configurations
                List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
                // 去重,List转Set再转List
                configurations = removeDuplicates(configurations);
                // 从注解的exclude/excludeName属性中获取排除项
                Set<String> exclusions = getExclusions(annotationMetadata, attributes);
                // 对于不属于AutoConfiguration的exclude报错
                checkExcludedClasses(configurations, exclusions);
                // 从configurations去除exclusions
                configurations.removeAll(exclusions);
                // 由所有AutoConfigurationImportFilter类的实例再进行一次筛选,去
                configurations = filter(configurations, autoConfigurationMetadata);
                // 把AutoConfigurationImportEvent绑定在所有AutoConfigurationImportListener子类实例上
                fireAutoConfigurationImportEvents(configurations, exclusions);
                // 返回(configurations, exclusions)组
                return new AutoConfigurationEntry(configurations, exclusions);
            }
            // ......
        }
    总结:可见selectImports()是AutoConfigurationImportSelector的核心函数,其核心功能就是获取spring.factories中EnableAutoConfiguration所对应的Configuration类列表,
    由@EnableAutoConfiguration注解中的exclude/excludeName参数筛选一遍,再由AutoConfigurationImportFilter类所有实例筛选一遍,得到最终的用于Import的configuration和exclusion。
    该函数在org.springframework.context.annotation.ConfigurationClassParser类中被processImports()调用,而processImports()函数被doProcessConfigurationClass()调用。
    详细解释这位老兄写的很到位:https://www.cnblogs.com/desertfish/p/11637933.html
    注意:
    	// AutoConfigurationImportSelector-->getAutoConfigurationEntry()-->getCandidateConfigurations()
    	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;
		}
		//SpringFactoriesLoader类下方法
		public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
			String factoryTypeName = factoryType.getName();
			return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
		}
		//SpringFactoriesLoader类下方法
		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);
			}
		}
    	在AutoConfigurationImportSelector自动配置导入选择组件中可以看到,getCandidateConfigurations方法会执行最终会调用SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
					getBeanClassLoader());
    	SpringFactoriesLoader.loadFactoryNames() 会扫描所有jar包类路径下  META‐INF/spring.factories 把扫描到的这些文件的内容包装成properties对象 
    	从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中然后用他们来做自动配置;
    	spring.factories部分内容:
    		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,\
			...
		简单分析一个:HttpEncodingAutoConfiguration
					@Configuration
					//启动指定类的@ConfigurationProperties的配置功能,将配置文件对应的值和HttpEncodingProperties的属性绑定起来
					@EnableConfigurationProperties(HttpEncodingProperties.class)
					//条件注解:条件满足的时候这个配置类才能生效
					@ConditionalOnWebApplication //这个就是当前的springboot项目是一个web项目的时候HttpEncodingAutoConfiguration配置类才能生效
					//条件注解:条件满足的时候这个配置类才能生效,条件是当前项目必须有CharacterEncodingFilter这个类
					@ConditionalOnClass(CharacterEncodingFilter.class)
					//条件注解:判断配置文件中是否存在某个属性
					@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
					public class HttpEncodingAutoConfiguration {

						private final HttpEncodingProperties properties;
						.....
					}	
		如上分析:springboot不会一下子,把spring.factories下配置指定的配置类都加载到容器中,因为配置类有@ConditionalXXX注解,配置类只有满足这些XXX条件的时候才会被加载
		ConditionalXXX注解整理:
			
        @ConditionalXXX 扩展注解 	        ||作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
			--------------------------------------------------		
			@ConditionalOnJava 				|| 系统的java版本是否符合要求
			@ConditionalOnBean 				|| 容器中存在指定Bean;
			@ConditionalOnMissingBean 		|| 容器中不存在指定Bean;
			@ConditionalOnExpression 		|| 满足SpEL表达式指定
			@ConditionalOnClass 			|| 系统中有指定的类
			@ConditionalOnMissingClass 		|| 系统中没有指定的类
			@ConditionalOnSingleCandidate 	|| 容器中只有一个指定的Bean,或者这个Bean是首选Bean
			@ConditionalOnProperty 			|| 系统中指定的属性是否有指定的值
			@ConditionalOnResource 			|| 类路径下是否存在指定资源文件
			@ConditionalOnWebApplication 	|| 当前是web环境
			@ConditionalOnNotWebApplication || 当前不是web环境
			@ConditionalOnJndi   			|| JNDI存在指定项

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值