SPRINGBOOT启动原理(基于2.x版本)

Posted 盖丽男

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SPRINGBOOT启动原理(基于2.x版本)相关的知识,希望对你有一定的参考价值。

目录

版本

版本:2.5.5

注解

我们从启动类入手,springboot的启动类上要添加 @SpringBootApplication 注解,我们先看一下这个注解的作用:

@SpringBootApplication

//用于描述类、接口(包括注解类型) 或enum声明
@Target(ElementType.TYPE)
//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Retention(RetentionPolicy.RUNTIME)
//表明这个注解应该被 javadoc工具记录
@Documented
//子类将会继承该注解
@Inherited
//相当于@Configuration
@SpringBootConfiguration
//启用 SpringBoot 的自动配置机制
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = @Filter(
    type = FilterType.CUSTOM,
    classes = TypeExcludeFilter.class
), @Filter(
    type = FilterType.CUSTOM,
    classes = AutoConfigurationExcludeFilter.class
)
)

@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
//使用Import自动导入所有符合自动配置条件的Bean定义并加载到IOC容器
@Import(AutoConfigurationImportSelector.class)

这个注解里面比较引人注意的是这两个注解:

@AutoConfigurationPackage

@Import(AutoConfigurationImportSelector.class)

介绍

三种将类交给spring管理,也就是加入IOC容器的方式:

  • @Bean
  • @Componet/@Service
  • @Import

@Import一般作用于@Configuration定义的类上,它有三种用法
先假设我们有3个类

public class Apple 


public class Banana 


public class Cherry 

都通过这个类注入:

@Configuration
public class Fruits 

指定class数组方式
@Configuration
@Import(Apple.class)
public class Fruits 

ImportSelector方式(Spring Boot底层采用比较得多的方式)
public class BananaImportSelector implements ImportSelector 
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) 
        return new String[]"com.test.Banana";
    


@Configuration
@Import(Apple.class,BananaImportSelector.class)
public class Fruits 

ImportBeanDefinitionRegistrar方式
public class CherryDefinitionRegistrar implements ImportBeanDefinitionRegistrar 

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) 
        RootBeanDefinition rootBeanDefinition=new RootBeanDefinition(Cherry.class);
        //这里还可以给Cherry这个类改个名字,比如Coconut
        registry.registerBeanDefinition("Coconut",rootBeanDefinition);
    


@Configuration
@Import(Apple.class,BananaImportSelector.class,CherryDefinitionRegistrar.class)
public class Fruits 


正题

以上,也就是说,在这个地方,要先引入一些类,而且是通过第二种方式来进行的,我们看下这个类:AutoConfigurationImportSelector.class

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) 
	//判断EnableAutoConfiguration是否开启,一般默认开启的
		if (!isEnabled(annotationMetadata)) 
			return NO_IMPORTS;
		
		
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	

	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) 
	//这里又判断了一遍EnableAutoConfiguration是否开启
		if (!isEnabled(annotationMetadata)) 
			return EMPTY_ENTRY;
		
       //这里可以理解为获取到了EnableAutoConfiguration的属性和我们传入的值。
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		//这里可以理解为获取到了所有可以自动注入的类名
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		//给这些需要自动注入的类名去重,通过list-set-list的方式
		configurations = removeDuplicates(configurations);
		//把我们设置了排出的整个set返回回来
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		//验证一下要排除自动的类是否合法,不合法的话会直接抛异常
		checkExcludedClasses(configurations, exclusions);
		//这里就直接把我们想要排除的类从自动启动里都remove了
		configurations.removeAll(exclusions);
		//TODO:这块另写一篇 获取过滤器,过滤配置类
		configurations = getConfigurationClassFilter().filter(configurations);
		//TODO:这块另写一篇 获取所有的AutoConfigurationImportListener类型的监听器。然后广播AutoConfigurationImportEvent事件
		fireAutoConfigurationImportEvents(configurations, exclusions);
		//配置类名字结合和排除集合封装成AutoConfigurationEntry返回
		return new AutoConfigurationEntry(configurations, exclusions);
	
	
	protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) 
	//拿到了EnableAutoConfiguration类的名字
		String name = getAnnotationClass().getName();
		AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
		Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
				+ " annotated with " + ClassUtils.getShortName(name) + "?");
		return attributes;
	
	protected final <T> List<T> removeDuplicates(List<T> list) 
		return new ArrayList<>(new LinkedHashSet<>(list));
	


	private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) 
		List<String> invalidExcludes = new ArrayList<>(exclusions.size());
		for (String exclusion : exclusions) 
			if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) 
				invalidExcludes.add(exclusion);
			
		
		if (!invalidExcludes.isEmpty()) 
			handleInvalidExcludes(invalidExcludes);
		
	
		private ConfigurationClassFilter getConfigurationClassFilter() 
		if (this.configurationClassFilter == null) 
		//这里其实获取到了AutoConfigurationImportFilter
			List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
			for (AutoConfigurationImportFilter filter : filters) 
				invokeAwareMethods(filter);
			
			this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
		
		return this.configurationClassFilter;
	

	private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) 
		List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
		if (!listeners.isEmpty()) 
		//这块主要是获取到了AutoConfigurationImportFilter的三个实现类
			AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
			for (AutoConfigurationImportListener listener : listeners) 
				invokeAwareMethods(listener);
				listener.onAutoConfigurationImportEvent(event);
			
		
	

	protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() 
		return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
	
	
		private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) 
		List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
		if (!listeners.isEmpty()) 
			AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
			for (AutoConfigurationImportListener listener : listeners) 
				invokeAwareMethods(listener);
				listener.onAutoConfigurationImportEvent(event);
			
		
	

	protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() 
		return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
	
	

参考

@Import注解的应用和扩展

SpringBoot之@EnableAutoConfiguration注解

SpringBootApplication-@Import(AutoConfigurationImportSelector.class)

以上是关于SPRINGBOOT启动原理(基于2.x版本)的主要内容,如果未能解决你的问题,请参考以下文章

SPRINGBOOT启动原理(基于2.x版本)- SpringApplication里有啥

SpringBoot运行原理

关于SpringBoot 1.x和2.x版本差别

SpringBoot 2.x版本整合redis集群

《区块链原理设计与应用》 – 基于超级账本 Fabric 2.x(学习分享2.1-HyperLedger项目细分)

springboot启动为啥控制台要输出一个图案?