IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springboot启动原理自动配置原理 -> 正文阅读

[Java知识库]springboot启动原理自动配置原理

1. SpringBoot自动加载原理

@SpringBootApplication
public class Application {

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

}

首先看@SpringBootApplication注解

@Target({ElementType.TYPE}) // 标记在类上
@Retention(RetentionPolicy.RUNTIME) // 作用于运行时
@Documented // 生成javadoc文档
@Inherited // 允许被继承
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
  // ...
}

其中包含了@SpringBootConfiguration和@EnableAutoConfiguration重要注解

@SpringBootConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    // ...
}

该注解实际上起的还是@Configuration的作用,指定配置类

@EnableAutoConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

EnableAutoConfiguration中有两个注解,分别是

  • @AutoConfigurationPackage
  • @Import({AutoConfigurationImportSelector.class})

@AutoConfigurationPackage

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}

这个注解的关键是@Import({Registrar.class}),@Import的作用是让Spring加载Registrar.class作为bean,那么看看Registrar中有什么

Registrar.class
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));
		}

	}

@Import加载的类如果实现了ImportBeanDefinitionRegistrar接口,那么就会调用该接口的方法并注册成bean,再看一下该方法做了什么

register(registry, new PackageImport(metadata).getPackageName());

在这段代码上打断点然后debug项目,可以看到getPackageName()最终返回的是整个项目启动类所在的包,即该类的作用就是扫描包下面的组件注册成bean

@Import({AutoConfigurationImportSelector.class})

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered{
      // ...
    }

@Import导入的类如果实现了DeferredImportSelector或ImportSelector接口,则会执行selectImports方法,看一下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());
	}

这里重点看AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);这段代码,跟到方法里面去

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方法

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.loadFactoryNames,这个方法比较关键了,跟进去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sgPO2OOS-1646748601337)(/Users/zhangxing/Library/Application Support/typora-user-images/image-20220228230648913.png)]

可以看到自动配置实际上会加载jar包中的spring.factories文件

所以自动配置实际上分2种情况

  1. 启动类同包下以及子包下的所有组件都会进行扫描并按需加载(如@ConditionXXXX),通过@ComponentScan可以修改扫描位置
  2. 读取resource目录下的META-INF/spring.factories文件中配置的类型

2.SpringApplication.run()

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

这一句代码包含两步骤

  1. 创建SpringApplication的实例
  2. 使用实例调用run方法

创建SpringApplication实例

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  	// 推断web项目使用的mvc还是webflux,通过判断dispatcher的全限定名属于reactive包还是servlet包
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // bootstrap层的初始化,这里我们知道配置问价加载顺序是bootstrap>application,所以此处先初始化bootstrap
		this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
    // 初始化ApplicationContextInitializer,从spring.factories中加载
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 初始化ApplicationListener,从spring.factories中加载
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 通过栈 判断调用main方法的class
		this.mainApplicationClass = deduceMainApplicationClass();
	}

在这个构造方法中,由于容器还没有进行refresh方法(bean的初始化方法),所以可以通过实现ApplicationContextInitializer类来修改配置等上下文信息

run方法

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		configureHeadlessProperty();
  // 获取SpringApplicationRunListener类型的启动监听器并实例化,从spring.factories中获取
		SpringApplicationRunListeners listeners = getRunListeners(args);
  // 发送启动事件
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
      // 准备环境,包括了环境变量、java环境、spring环境、配置文件配置
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			configureIgnoreBeanInfo(environment);
      // 打印banner
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
      // 准备上下文
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      // 刷新上下文
			refreshContext(context);
      // 预留刷新上下文后的扩展,默认没有实现
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
      // 发送已启动事件
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}

		try {
      // 发送run事件
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}
  1. 获取并启动监听器

    private SpringApplicationRunListeners getRunListeners(String[] args) {
    		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    		return new SpringApplicationRunListeners(logger,
    				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
    				this.applicationStartup);
    	}
    

    这里实际上找的是SpringApplicationRunListener类型的监听器,用于监听应用的启用

    监听器实际上最终使用的是spring的事件机制实现

  2. 构造应用上下文环境
    包括系统环境变量、java环境、spring环境、spring配置文件

  3. 初始化应用上下文
    通过SpringApplication实例化的时候对webType进行的推断,当webType为MVC类型的时候,创建的上下文实际上是AnnotationConfigServletWebServerApplicationContext
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OQiSdlJh-1646748601338)(/Users/zhangxing/Library/Application Support/typora-user-images/image-20220301213421653.png)]
    AnnotationConfigServletWebServerApplicationContext实现了BeanFactory,具有IOC容器能力,其次这个类也是GenericApplicationContext的子类,GenericApplicationContext具备上下文能力,他使用一个BeanFactory字段保存了IOC容器,所以说,AnnotationConfigServletWebServerApplicationContext是上下文,并且持有了容器,对容器能力可以进行扩展

  4. 刷新应用上下文前的准备阶段

    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
    			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
    			ApplicationArguments applicationArguments, Banner printedBanner) {
      // 设置环境参数
    		context.setEnvironment(environment);
      // 上下文创建好的后置处理
    		postProcessApplicationContext(context);
      // springapplication初始化时找到的initializers做调用
    		applyInitializers(context);
    		listeners.contextPrepared(context);
    		bootstrapContext.close(context);
    		if (this.logStartupInfo) {
    			logStartupInfo(context.getParent() == null);
    			logStartupProfileInfo(context);
    		}
    		// Add boot specific singleton beans
    		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    		if (printedBanner != null) {
    			beanFactory.registerSingleton("springBootBanner", printedBanner);
    		}
    		if (beanFactory instanceof DefaultListableBeanFactory) {
    			((DefaultListableBeanFactory) beanFactory)
    					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    		}
    		if (this.lazyInitialization) {
    			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    		}
    		// Load the sources
    		Set<Object> sources = getAllSources();
    		Assert.notEmpty(sources, "Sources must not be empty");
      // 这里是重点跟进去
    		load(context, sources.toArray(new Object[0]));
    		listeners.contextLoaded(context);
    	}
    

    上下文对象创建出来之后,给上下文设置环境参数,以及调用之前找到的initializer做调用,跟入load方法

    protected void load(ApplicationContext context, Object[] sources) {
    		if (logger.isDebugEnabled()) {
    			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    		}
      // 创建bean定义的加载器,跟进去看如何创建的
    		BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    		if (this.beanNameGenerator != null) {
    			loader.setBeanNameGenerator(this.beanNameGenerator);
    		}
    		if (this.resourceLoader != null) {
    			loader.setResourceLoader(this.resourceLoader);
    		}
    		if (this.environment != null) {
    			loader.setEnvironment(this.environment);
    		}
      // bean定义加载器执行加载
    		loader.load();
    	}
    
    BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
    		Assert.notNull(registry, "Registry must not be null");
    		Assert.notEmpty(sources, "Sources must not be empty");
    		this.sources = sources;
      // 注解读取器
    		this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
      // xml读取器
    		this.xmlReader = (XML_ENABLED ? new XmlBeanDefinitionReader(registry) : null);
    		this.groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null);
      // 包扫描读取器
    		this.scanner = new ClassPathBeanDefinitionScanner(registry);
      // 排除bean过滤器
    		this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
    	}
    

    说明会通过注解方式、包扫描方式和xml方式读取哪些地方配置了需要交给spring管理的bean,最终执行.load()方法,将这三种读取方式分别执行,拿到所有的bean定义,这里只是读取到这个项目一共有哪些bean定义,然后给保存起来,并不是说这些bean定义一定都要载入到上下文中去,至于为什么在瞎main的刷新上下文中讲解

    BeanDefinition其实就是bean的基本信息,比如这个bean是什么class类型的,bean是否是懒加载,这个bean是否是primary修饰的,这个bean是单例还是property(每次都创新对象)模式等等信息…

  5. 刷新应用上下文

    public void refresh() throws BeansException, IllegalStateException {
    		synchronized (this.startupShutdownMonitor) {
    			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
    
    			// Prepare this context for refreshing.
          // 刷新前的准备,主要校验了环境参数是否完整,比如某些必填配置没有写
    			prepareRefresh();
    
    			// Tell the subclass to refresh the internal bean factory.
          // 告诉子类刷新内部的beanFactory,其实里面没做啥事,主要就是刷新BeanFactory,获取BeanFactory,返回BeanFactory
    			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
    			// 准备beanFactory,添加spel表达式的解析器、设置后置处理器等设置一堆东西
    			prepareBeanFactory(beanFactory);
    
    			try {
    				// Allows post-processing of the bean factory in context subclasses.
            // 添加bean的后置处理器,bean的后置处理器执行时机是beanDefinition创建之后,bean实例化之前,这样就拥有修改beanDefinition信息的能力
    				postProcessBeanFactory(beanFactory);
    
    				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
    				// Invoke factory processors registered as beans in the context.
            // 这里是springboot自动加载的入口,通过主类转换成的BeanDefinition,拿到basePackage,然后找到basePackage下的所有@Component,然后将beanDefinition加入到容器中
    				invokeBeanFactoryPostProcessors(beanFactory);
    
    				// Register bean processors that intercept bean creation.
    
    				registerBeanPostProcessors(beanFactory);
    				beanPostProcess.end();
    
    				// Initialize message source for this context.
    				initMessageSource();
    
    				// Initialize event multicaster for this context.
    				initApplicationEventMulticaster();
    
    				// Initialize other special beans in specific context subclasses.
    				onRefresh();
    
    				// Check for listener beans and register them.
    				registerListeners();
    
    				// Instantiate all remaining (non-lazy-init) singletons.
    				finishBeanFactoryInitialization(beanFactory);
    
    				// Last step: publish corresponding event.
    				finishRefresh();
    			}
    
    			catch (BeansException ex) {
    				if (logger.isWarnEnabled()) {
    					logger.warn("Exception encountered during context initialization - " +
    							"cancelling refresh attempt: " + ex);
    				}
    
    				// Destroy already created singletons to avoid dangling resources.
    				destroyBeans();
    
    				// Reset 'active' flag.
    				cancelRefresh(ex);
    
    				// Propagate exception to caller.
    				throw ex;
    			}
    
    			finally {
    				// Reset common introspection caches in Spring's core, since we
    				// might not ever need metadata for singleton beans anymore...
    				resetCommonCaches();
    				contextRefresh.end();
    			}
    		}
    	}
    
  6. 刷新应用上下文后的扩展接口
    方便扩展

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-10 22:17:51  更:2022-03-10 22:21:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:05:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码