《提升能力,涨薪可待》-易理解的@SpringBootApplication注解源码解析

僤鯓⒐⒋嵵緔 提交于 2019-12-04 21:01:44

欢迎一起学习
提升能力,涨薪可待
面试知识,工作可待
实战演练,拒绝996
如果此文对你有帮助、喜欢的话,那就点个赞呗!

前言

是不是感觉在工作上难于晋升了呢?
是不是感觉找工作面试是那么难呢?
是不是感觉自己每天都在996加班呢?

在工作上必须保持学习的能力,这样才能在工作得到更好的晋升,涨薪指日可待,欢迎一起学习【提升能力,涨薪可待】系列
在找工作面试应在学习的基础进行总结面试知识点,工作也指日可待,欢迎一起学习【面试知识,工作可待】系列
最后,理论知识到准备充足,是不是该躬行起来呢?欢迎一起学习【实战演练,拒绝996】系列

springApplication

一、@SpringBootApplication 的作用是什么?

Q:springboot项目的启动类上,都会有个注解@SpringBootApplication,这个注解起到了什么作用?

@SpringBootApplication
public class MicroServiceApplication {

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

}

​ 我们进入@SpringBootApplication注解,发现它等价于三个注解@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan

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

1) @SpringBootConfiguration 就相当于 @Configuration

@Configuration
public @interface SpringBootConfiguration {

}

2) @EnabelAutoConfiguration 相当于将这两个类的实例加入到容器中AutoConfigurationImportSelector.class AutoConfigurationPackages.Registrar.class

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

}
  • AutoConfigurationImportSelector.class的作用是,注入spring.factories文件中EnableAutoConfiguration对应的类的实例,当然要经过spring.factories文件中AutoConfigurationImportFilter对应的过滤器(OnBeanConditionOnClassConditionOnWebApplicationCondition等等)的过滤。还要排除掉@EnableAutoConfiguration中的exclude和excludeName

    具体见ConfigurationClassParser的getImports方法,其中调用了AutoConfigurationImportSelector的process方法和selectImports方法。

    public Iterable<group.entry> getImports() {
    			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
    				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
    						deferredImport.getImportSelector());
    			}
    			return this.group.selectImports();
    		}
    

    AutoConfigurationImportSelector实现了DeferredImportSelector,所以它是解析@Configuration的最后一步。DeferredImportSelector可以和@Order搭配使用。AutoConfigurationImportSelector的意义在于引入其他包时,可以直接注入其他包的@Configuration,当然需要在其他包的resources文件夹下,新建META-INF目录,在META-INF目录下新建spring.factories文件,加入org.springframework.boot.autoconfigure.EnableAutoConfiguration = @Configuration标注的类的路径

  • AutoConfigurationPackages.Registrar.class的作用是,注入一个名称为AutoConfigurationPackages的BasePackages.class实例。这个实例的作用在于保存自动扫描的包路径,供以后使用(比如JPA 的entity扫描)

    public static void register(BeanDefinitionRegistry registry, String... packageNames) {
    		if (registry.containsBeanDefinition(BEAN)) {
    			BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
    			ConstructorArgumentValues constructorArguments = beanDefinition
    					.getConstructorArgumentValues();
    			constructorArguments.addIndexedArgumentValue(0,
    					addBasePackages(constructorArguments, packageNames));
    		}
    		else {
    			GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
    			beanDefinition.setBeanClass(BasePackages.class);
    			beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
    					packageNames);
    			beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    			registry.registerBeanDefinition(BEAN, beanDefinition);
    		}
    	}
    
    	private static String[] addBasePackages(
    			ConstructorArgumentValues constructorArguments, String[] packageNames) {
    		String[] existing = (String[]) constructorArguments
    				.getIndexedArgumentValue(0, String[].class).getValue();
    		Set<string> merged = new LinkedHashSet&lt;&gt;();
    		merged.addAll(Arrays.asList(existing));
    		merged.addAll(Arrays.asList(packageNames));
    		return StringUtils.toStringArray(merged);
    	}
    

3) @ComponentScan 当然是将路径下合适的类加载到容器中

Q:它为什么要用到TypeExcludeFilter.classAutoConfigurationExcludeFilter.class 这两个过滤器

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	...
}
  • 这两个过滤器在ComponentScanAnnotationParser的parse方法中,被加入

    public Set<beandefinitionholder> parse(AnnotationAttributes componentScan, final String declaringClass) {
        //此处加入了三个默认的includeFilter,一个用来筛选@Componment标记的类,一个用来筛选javax.annotation.ManagedBean标记的类,一个用来筛选JSR-330中javax.inject.Named标记的类(如果引入了JSR-330依赖注入标准的话,即引入javax.inject包)
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
    				componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    		...
    
    		for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
    			for (TypeFilter typeFilter : typeFiltersFor(filter)) {
    				scanner.addExcludeFilter(typeFilter);
    			}
    		}
    
    		...
    
    		scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
    			@Override
    			protected boolean matchClassName(String className) {
    				return declaringClass.equals(className);
    			}
    		});
    		return scanner.doScan(StringUtils.toStringArray(basePackages));
    	}
    

也是在parse这个方法里调用了 typeFiltersFor方法,对TypeFilter.class的实现类进行了实例化。(也就是说TypeExcludeFilter.class AutoConfigurationExcludeFilter.class AbstractTypeHierarchyTraversingFilter.class都在这里进行了实例化,但是没有加入spring的bean池)

private List<typefilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
		List<typefilter> typeFilters = new ArrayList&lt;&gt;();
		FilterType filterType = filterAttributes.getEnum("type");

		for (Class<!--?--> filterClass : filterAttributes.getClassArray("classes")) {
			switch (filterType) {
				case ANNOTATION:
					Assert.isAssignable(Annotation.class, filterClass,
							"@ComponentScan ANNOTATION type filter requires an annotation type");
					@SuppressWarnings("unchecked")
					Class<annotation> annotationType = (Class<annotation>) filterClass;
					typeFilters.add(new AnnotationTypeFilter(annotationType));
					break;
				case ASSIGNABLE_TYPE:
					typeFilters.add(new AssignableTypeFilter(filterClass));
					break;
				case CUSTOM:
					Assert.isAssignable(TypeFilter.class, filterClass,
							"@ComponentScan CUSTOM type filter requires a TypeFilter implementation");
					TypeFilter filter = BeanUtils.instantiateClass(filterClass, TypeFilter.class);
					ParserStrategyUtils.invokeAwareMethods(
							filter, this.environment, this.resourceLoader, this.registry);
					typeFilters.add(filter);
					break;
				default:
					throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType);
			}
		}

		for (String expression : filterAttributes.getStringArray("pattern")) {
			switch (filterType) {
				case ASPECTJ:
					typeFilters.add(new AspectJTypeFilter(expression, this.resourceLoader.getClassLoader()));
					break;
				case REGEX:
					typeFilters.add(new RegexPatternTypeFilter(Pattern.compile(expression)));
					break;
				default:
					throw new IllegalArgumentException("Filter type not supported with String pattern: " + filterType);
			}
		}

		return typeFilters;
	}
 `ComponentScanAnnotationParser`的parse方法中,scanner最后为什么又加了一个`AbstractTypeHierarchyTraversingFilter`呢?我们看一下它的match方法,发现是**过滤掉启动类,不让它作为@configuration标记的候选类,避免再一次解析启动类上的各种注解**(因为它的两个参数considerInherited和considerInterfaces在scanner.addExcludeFilter这条语句中,设置成了false,导致下面的判断语句不生效)。
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {

		...
		ClassMetadata metadata = metadataReader.getClassMetadata();
		if (matchClassName(metadata.getClassName())) {
			return true;
		}

		if (this.considerInherited) {
			...
		}

		if (this.considerInterfaces) {
			...
		}

		return false;
	}

注:启动类本身还是会注入到spring的bean池中的,具体见SpringApplication的load方法

  • ExcludeFilter在ClassPathScanningCandidateComponentProvider的isCandidateComponent方法中被使用

    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    		for (TypeFilter tf : this.excludeFilters) {
    			if (tf.match(metadataReader, getMetadataReaderFactory())) {
    				return false;
    			}
    		}
    		for (TypeFilter tf : this.includeFilters) {
    			if (tf.match(metadataReader, getMetadataReaderFactory())) {
    				return isConditionMatch(metadataReader);
    			}
    		}
    		return false;
    	}
    
  • AutoConfigurationExcludeFilter 的作用是过滤掉会自动配置的配置类,避免重复

    @Override
    	public boolean match(MetadataReader metadataReader,
    			MetadataReaderFactory metadataReaderFactory) throws IOException {
    		//如果这个类被@Configuration标注,且属于自动加载的配置,那么过滤它,避免重复
            return isConfiguration(metadataReader) &amp;&amp; isAutoConfiguration(metadataReader);
    	}
    
    	private boolean isConfiguration(MetadataReader metadataReader) {
    		return metadataReader.getAnnotationMetadata()
    				.isAnnotated(Configuration.class.getName());
    	}
    
    	private boolean isAutoConfiguration(MetadataReader metadataReader) {
    		return getAutoConfigurations()
    				.contains(metadataReader.getClassMetadata().getClassName());
    	}
    
    	protected List<string> getAutoConfigurations() {
    		if (this.autoConfigurations == null) {
                /**
                从META-INF/spring.factories文件中,找出EnableAutoConfiguration.class
                多个jar包中,都存在spring.factories文件
                其中包含EnableAutoConfiguration.class的spring.factories文件,位于spring-boot-autoconfigure
                **/
    			this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(
    					EnableAutoConfiguration.class, this.beanClassLoader);
    		}
    		return this.autoConfigurations;
    	}
    
  • TypeExcludeFilter 的作用是加载spring bean池中所有针对TypeExcludeFilter的扩展,并循环遍历这些扩展类调用其match方法

    public boolean match(MetadataReader metadataReader,
    			MetadataReaderFactory metadataReaderFactory) throws IOException {
    		if (this.beanFactory instanceof ListableBeanFactory
    				&amp;&amp; getClass() == TypeExcludeFilter.class) {
                //加载spring bean池中所有针对TypeExcludeFilter的拓展
    			Collection<typeexcludefilter> delegates = ((ListableBeanFactory) this.beanFactory)
    					.getBeansOfType(TypeExcludeFilter.class).values();
                // 循环遍历,调用其match方法
    			for (TypeExcludeFilter delegate : delegates) {
    				if (delegate.match(metadataReader, metadataReaderFactory)) {
    					return true;
    				}
    			}
    		}
    		return false;
    	}
    

</typeexcludefilter></string></annotation></annotation></typefilter></typefilter></beandefinitionholder></string></group.entry>

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!