zoukankan      html  css  js  c++  java
  • SpringBoot启动分析4:准备环境

    在前面的环境准备完毕后,后续就是创建上下文环境、准备上下文环境以及刷新上下文环境了。

    1.1 创建上下文环境

    上下文环境的创建,虽然源码是以父类来视人,实际它实例化的是AnnotationConfigServletWebServerApplicationContext对象:

    context = createApplicationContext();
    protected ConfigurableApplicationContext createApplicationContext() {
    	Class<?> contextClass = this.applicationContextClass;
    	if (contextClass == null) {
    		try {
    			switch (this.webApplicationType) {
    			case SERVLET:
    				contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
    				break;
    			case REACTIVE:
    				contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
    				break;
    			default:
    				contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
    			}
    		}
    		catch (ClassNotFoundException ex) {
    			throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
    		}
    	}
    	return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }
    

    还记得初始化SpringApplication时webApplicationType就是初始化为SERVLET,所以这里创建的是以下实例:

    public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
    

    最后通过工具类实例化该类,以下是上下文环境的类图:

    1.2 准备上下文环境

    创建完成上下文环境对象后,开始对这环境初始化,跟进源码:

    // 参数:上下文环境对象、环境对象、发布时间的监听器、命令行参数、打印对象
    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    	context.setEnvironment(environment);
    	postProcessApplicationContext(context);
    	applyInitializers(context);
    	listeners.contextPrepared(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);
    }
    

    1.2.1 设置上下文的环境信息

    context.setEnvironment(environment);
    public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
    	private final AnnotatedBeanDefinitionReader reader;
    	private final ClassPathBeanDefinitionScanner scanner;
    	
    	@Override
    	public void setEnvironment(ConfigurableEnvironment environment) {
    		super.setEnvironment(environment);
    		this.reader.setEnvironment(environment);
    		this.scanner.setEnvironment(environment);
    	}
    }
    

    这里主要对以下三个类做了做了初始化环境操作,分别如下。

    AbstractApplicationContext

    跟进源码:

    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
    
    	@Nullable
    	private ConfigurableEnvironment environment;
    	
    	@Override
    	public void setEnvironment(ConfigurableEnvironment environment) {
    		this.environment = environment;
    	}
    }
    

    这里就简单的初始化,相当于将environment对象注入到AbstractApplicationContext抽象类中。

    AnnotatedBeanDefinitionReader

    AnnotatedBeanDefinitionReader是一个Bean注解的读取器,跟进源码:

    public class AnnotatedBeanDefinitionReader {
    
    	private ConditionEvaluator conditionEvaluator;
    
    	public void setEnvironment(Environment environment) {
    		this.conditionEvaluator = new ConditionEvaluator(this.registry, environment, null);
    	}
    }
    

    可以看出AnnotatedBeanDefinitionReader也是为了初始化内部属性conditionEvaluator,conditionEvaluator是条件评估器,它的主要作用是完成条件注解的解析和判断,比如在一个Bean上添加@ConditionalOnMissingBean条件注解,后续的判断会通过conditionEvaluator的shouldSkip方法来进行过滤,跟进conditionEvaluator构造方法:

    class ConditionEvaluator {
    
    	private final ConditionContextImpl context;
    
    	public ConditionEvaluator(@Nullable BeanDefinitionRegistry registry,
    			@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
    		
    		this.context = new ConditionContextImpl(registry, environment, resourceLoader);
    	}
    	private static class ConditionContextImpl implements ConditionContext {
    		public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry,
    				@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
    			this.registry = registry;
    			this.beanFactory = deduceBeanFactory(registry);
    			this.environment = (environment != null ? environment : deduceEnvironment(registry));
    			this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry));
    			this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory);
    		}
    	}	
    }	
    

    可以看出conditionEvaluator构造方法实例了内部类ConditionContextImpl并初始化属性信息。

    ClassPathBeanDefinitionScanner

    ClassPathBeanDefinitionScanner是类路径下bean扫描器,作用就是将指定包下的类通过一定规则过滤后将Class信息包装成BeanDefinition的形式注册到IOC容器中,这里则是初始化ClassPathBeanDefinitionScanner的父类ClassPathScanningCandidateComponentProvider中的environment环境对象:

    public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
    
    	@Nullable
    	private Environment environment;
    	
    	public void setEnvironment(Environment environment) {
    		Assert.notNull(environment, "Environment must not be null");
    		this.environment = environment;
    		this.conditionEvaluator = null;
    	}
    }	
    

    1.2.2 设置转换格式器

    protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    	// beanNameGenerator尚未初始化,这里为null
    	if (this.beanNameGenerator != null) {
    		context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
    				this.beanNameGenerator);
    	}
    	// resourceLoader尚未初始化,这里为null
    	if (this.resourceLoader != null) {
    		if (context instanceof GenericApplicationContext) {
    			((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
    		}
    		if (context instanceof DefaultResourceLoader) {
    			((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
    		}
    	}
    	// addConversionService默认为true,所以将格式转换器添加到BeanFactory中
    	if (this.addConversionService) {
    		context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
    	}
    }
    

    1.2.3 初始化器初始化

    跟进源码如下:

    applyInitializers(context);
    protected void applyInitializers(ConfigurableApplicationContext context) {
    	for (ApplicationContextInitializer initializer : getInitializers()) {
    		Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
    				ApplicationContextInitializer.class);
    		Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
    		initializer.initialize(context);
    	}
    }
    

    可以看出这里遍历了初始化SpringApplication时设置的initializer,这些initializer如下:

    每个initializer会将上下文对象作为参数调用自己的initialize方法执行初始化,以下列举几个initializer的初始化过程:

    # SharedMetadataReaderFactoryContextInitializer,创建一个内部类加入到上下文的BeanFactoryPostProcessor中
    class SharedMetadataReaderFactoryContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
        public void initialize(ConfigurableApplicationContext applicationContext) {
            applicationContext.addBeanFactoryPostProcessor(new SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor());
        }
    	private static class CachingMetadataReaderFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
            private CachingMetadataReaderFactoryPostProcessor() {}
    		...
    	}	
    }
    # ContextIdApplicationContextInitializer,若环境中没有指定spring.application.name配置,则默认使用application名称实例化ContextId对象,并将ContextId注入到BeanFactory中
    public class ContextIdApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    
    	@Override
    	public void initialize(ConfigurableApplicationContext applicationContext) {
    		ContextId contextId = getContextId(applicationContext);
    		applicationContext.setId(contextId.getId());
    		applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(), contextId);
    	}
    	private ContextId getContextId(ConfigurableApplicationContext applicationContext) {
    		ApplicationContext parent = applicationContext.getParent();
    		if (parent != null && parent.containsBean(ContextId.class.getName())) {
    			return parent.getBean(ContextId.class).createChildId();
    		}
    		return new ContextId(getApplicationId(applicationContext.getEnvironment()));
    	}
    	private String getApplicationId(ConfigurableEnvironment environment) {
    		String name = environment.getProperty("spring.application.name");
    		return StringUtils.hasText(name) ? name : "application";
    	}
    }
    # ServerPortInfoApplicationContextInitializer,将该初始化器加入到上下文的监听器中
    public class ServerPortInfoApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<WebServerInitializedEvent> {
    
    	@Override
    	public void initialize(ConfigurableApplicationContext applicationContext) {
    		applicationContext.addApplicationListener(this);
    	}
    }
    

    1.2.4 监听器初始化

    跟进源码如下:

    listeners.contextPrepared(context);
    class SpringApplicationRunListeners {	
    	void contextPrepared(ConfigurableApplicationContext context) {
    		for (SpringApplicationRunListener listener : this.listeners) {
    			listener.contextPrepared(context);
    		}
    	}
    }
    public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    	@Override
    	public void contextPrepared(ConfigurableApplicationContext context) {
    		this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
    	}
    }
    

    可以看出依然是通过EventPublishingRunListener内部的方法来根据具体的事件参数(这里的事件参数为ApplicationContextInitializedEvent)获取不同的监听器,再遍历这些监听器执行各自的onApplicationEvent方法。

    1.2.5 注入Bean

    跟进源码如下:

    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
    	beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    

    将命令行参数和Beanner对象分别注入到BeanFactory中。

    1.2.6 初始化BeanDefinitionLoader

    在上下文环境对象被创建时我们可以知道创建的是AnnotationConfigServletWebApplicationContext实例,以下是类图的一部分:

    跟进源码:

    load(context, sources.toArray(new Object[0]));
    protected void load(ApplicationContext context, Object[] sources) {
    	if (logger.isDebugEnabled()) {
    		logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    	}
    	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);
    	}
    	loader.load();
    }
    

    getBeanDefinitionRegistry(context)返回的是BeanDefinitionRegistry即AnnotationConfigServletWebApplicationContext的父类,根据该父类作为参数调用createBeanDefinitionLoader方法创建一个BeanDefinitionLoader对象,创建该对象时是如何初始化的,跟进源码:

    class BeanDefinitionLoader {	
    	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 = new XmlBeanDefinitionReader(registry);
    		if (isGroovyPresent()) {
    			this.groovyReader = new GroovyBeanDefinitionReader(registry);
    		}
    		// 类路径下的扫描器
    		this.scanner = new ClassPathBeanDefinitionScanner(registry);
    		// 扫描排除当前main方法的主类,因为通过@componentScan不需要扫描启动类
    		this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
    	}
    }
    

    BeanDefinitionLoader用于从源加载Bean的定义信息,并封装成BeanDefinition对象,并注册到ApplicationContext中,加载的源可以是类注解、XML文件、package、classpath、Groovy文件等。
    以上几个扫描器或读取器用于根据SpringApplication.run(..)传入的参数不同而注册不同的Bean,根据loader.load()源码如下:

    loader.load();
    int load() {
    	int count = 0;
    	for (Object source : this.sources) {
    		count += load(source);
    	}
    	return count;
    }
    private int load(Object source) {
    	Assert.notNull(source, "Source must not be null");
    	if (source instanceof Class<?>) {
    		return load((Class<?>) source);
    	}
    	if (source instanceof Resource) {
    		return load((Resource) source);
    	}
    	if (source instanceof Package) {
    		return load((Package) source);
    	}
    	if (source instanceof CharSequence) {
    		return load((CharSequence) source);
    	}
    	throw new IllegalArgumentException("Invalid source type " + source.getClass());
    }
    

    1.参数为Java Config
    即当main入口通过Java Config即Java类作为参数传入来启动SpringBoot应用时:

    SpringApplication.run(ApplicationMain.class, args);
    

    会使用AnnotatedBeanDefinitionReader去处理参数,内部处理逻辑源码:

    class BeanDefinitionLoader {	
    	private int load(Class<?> source) {
    		if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
    			// Any GroovyLoaders added in beans{} DSL can contribute beans here
    			GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
    			load(loader);
    		}
    		if (isComponent(source)) {
    			this.annotatedReader.register(source);
    			return 1;
    		}
    		return 0;
    	}
    }
    

    2.参数为Package类型的Bean
    即当main入口通过Package类型的Bean作为参数传入来启动SpringBoot应用时:

    Package package = Package.getPackage("com.xxx.xxx");
    SpringApplication.run(package);
    

    会使用ClassPathBeanDefinitionScanner去处理参数,内部逻辑处理源码:

    class BeanDefinitionLoader {	
    	private int load(Package source) {
    		return this.scanner.scan(source.getName());
    	}
    }
    

    3.参数为Resource类型的配置
    即当main入口通过Resource类型的配置作为参数传入来启动SpringBoot应用时:

    SpringApplication.run(new ClassPathResource("applicationContext.xml"), args);
    

    会使用XmlBeanDefinitionReader去处理注册bean,内部逻辑处理源码:

    class BeanDefinitionLoader {	
    	private int load(Resource source) {
    		if (source.getFilename().endsWith(".groovy")) {
    			if (this.groovyReader == null) {
    				throw new BeanDefinitionStoreException("Cannot load Groovy beans without Groovy on classpath");
    			}
    			return this.groovyReader.loadBeanDefinitions(source);
    		}
    		return this.xmlReader.loadBeanDefinitions(source);
    	}
    }
    

    4.String类型的配置
    即当main入口通过String类型的配置作为参数传入来启动SpringBoot应用时:

    SpringApplication.run("classpath:/applicationContext.xml", args);
    

    会按照Class、Resource、Package的顺序尝试去解析String串进行加载,内部逻辑处理源码:

    class BeanDefinitionLoader {	
    	private int load(CharSequence source) {
    		String resolvedSource = this.xmlReader.getEnvironment().resolvePlaceholders(source.toString());
    		// Attempt as a Class
    		try {
    			return load(ClassUtils.forName(resolvedSource, null));
    		}
    		catch (IllegalArgumentException | ClassNotFoundException ex) {
    			// swallow exception and continue
    		}
    		// Attempt as resources
    		Resource[] resources = findResources(resolvedSource);
    		int loadCount = 0;
    		boolean atLeastOneResourceExists = false;
    		for (Resource resource : resources) {
    			if (isLoadCandidate(resource)) {
    				atLeastOneResourceExists = true;
    				loadCount += load(resource);
    			}
    		}
    		if (atLeastOneResourceExists) {
    			return loadCount;
    		}
    		// Attempt as package
    		Package packageResource = findPackage(resolvedSource);
    		if (packageResource != null) {
    			return load(packageResource);
    		}
    		throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'");
    	}
    }
    

    1.2.7 监听器初始化

    仍然是根据事件类型获取监听器执行初始化操作:

    listeners.contextLoaded(context);
    public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {	
    	@Override
    	public void contextLoaded(ConfigurableApplicationContext context) {
    		for (ApplicationListener<?> listener : this.application.getListeners()) {
    			if (listener instanceof ApplicationContextAware) {
    				((ApplicationContextAware) listener).setApplicationContext(context);
    			}
    			context.addApplicationListener(listener);
    		}
    		this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
    	}
    }
    

    这里的事件类型变成了ApplicationPreparedEvent。

  • 相关阅读:
    PHP 方法整合类 -- 1.根据概率产生随机数 --2.判断手机号归属地及运营商 --3.过滤emoji表情
    PHP 多图下载并打包压缩方法
    PHP 导出excel 精简版
    PHP获取首字母相关方法
    no input file specified 解决办法
    百度地图相关
    经纬度相关方法
    阿里云SSL证书部署至宝塔
    微信入口、生成菜单,公众号授权获取用户信息(unionid)
    超级好用超级简单的支付类库
  • 原文地址:https://www.cnblogs.com/Json1208/p/13378615.html
Copyright © 2011-2022 走看看