• SpringBoot启动分析5:刷新环境


    1.1 刷新流程概览

    跟进源码,大概看下源码对于环境刷新的整体步骤:

    refreshContext(context);
    private void refreshContext(ConfigurableApplicationContext context) {
    	refresh(context);
    	if (this.registerShutdownHook) {
    		try {
    			context.registerShutdownHook();
    		}
    		catch (AccessControlException ex) {
    			// Not allowed in some environments.
    		}
    	}
    }
    protected void refresh(ApplicationContext applicationContext) {
    	Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    	((AbstractApplicationContext) applicationContext).refresh();
    }
    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {	
    	public void refresh() throws BeansException, IllegalStateException {
    		synchronized(this.startupShutdownMonitor) {
    			this.prepareRefresh();
    			ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
    			this.prepareBeanFactory(beanFactory);
    
    			try {
    				this.postProcessBeanFactory(beanFactory);
    				this.invokeBeanFactoryPostProcessors(beanFactory);
    				this.registerBeanPostProcessors(beanFactory);
    				this.initMessageSource();
    				this.initApplicationEventMulticaster();
    				this.onRefresh();
    				this.registerListeners();
    				this.finishBeanFactoryInitialization(beanFactory);
    				this.finishRefresh();
    			} catch (BeansException var9) {
    				if (this.logger.isWarnEnabled()) {
    					this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
    				}
    				this.destroyBeans();
    				this.cancelRefresh(var9);
    				throw var9;
    			} finally {
    				this.resetCommonCaches();
    			}
    
    		}
    	}
    }	
    

    从上方可以看出最终调用了父类AbstractApplicationContext的refresh()方法。

    1.2 刷新流程分析

    1.2.1 准备刷新

    跟进源码,如下所示:

    prepareRefresh();
    public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
    
    	private final AnnotatedBeanDefinitionReader reader;
    	private final ClassPathBeanDefinitionScanner scanner;
    
    	@Override
    	protected void prepareRefresh() {
    		this.scanner.clearCache();
    		super.prepareRefresh();
    	}
    }
    
    1.2.1.1 清除缓存

    还记得scanner是当时创建上下文环境对象AnnotationConfigServletWebServerApplicationContext时所初始化的,当时对该类的初始化是这样的:

    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);
    		// 当时通过setEnvironment进行初始化,实际是调用父类ClassPathScanningCandidateComponentProvider进行初始化
    		this.scanner.setEnvironment(environment);
    	}
    }
    public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
    	// 实际初始化方法
    	public void setEnvironment(Environment environment) {
    		Assert.notNull(environment, "Environment must not be null");
    		this.environment = environment;
    		this.conditionEvaluator = null;
    	}
    }
    

    ClassPathBeanDefinitionScanner是类扫描器,也就是它的作用就是将指定包下的类通过一定规则过滤后将Class信息包装成BeanDefinition的形式注册到IOC容器中,在SpringBoot工程中它扫描的是主程序类下的包和所有子包。而ClassPathScanningCandidateComponentProvider是ClassPathBeanDefinitionScanner的基类,其本身主要作用是包扫描,ClassPathBeanDefinitionScanner在其基础上做了注册功能,所以ClassPathBeanDefinitionScanner需要传入一个BeanDefinitionRegistry对象,而ClassPathScanningCandidateComponentProvider扫描的对象是并不需要注册到BeanDefinitionRegistry中去的。
    这里的clearCache方法,主要是用于清理本地的元数据缓存,以下跟进源码:

    public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
    	public void clearCache() {
    		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
    			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    		}
    	}
    	public void clearCache() {
    		if (this.metadataReaderCache instanceof LocalResourceCache) {
    			synchronized (this.metadataReaderCache) {
    				this.metadataReaderCache.clear();
    			}
    		}
    		else if (this.metadataReaderCache != null) {
    			setCacheLimit(DEFAULT_CACHE_LIMIT);
    		}
    	}
    	public void setCacheLimit(int cacheLimit) {
    		if (cacheLimit <= 0) {
    			this.metadataReaderCache = null;
    		}
    		else if (this.metadataReaderCache instanceof LocalResourceCache) {
    			((LocalResourceCache) this.metadataReaderCache).setCacheLimit(cacheLimit);
    		}
    		else {
    			// 最终实例化LocalResourceCache并设置本地MetadataReader缓存的默认最大条目数为256
    			this.metadataReaderCache = new LocalResourceCache(cacheLimit);
    		}
    	}
    }
    
    1.2.1.2 准备刷新

    这里调用了父类AbstractApplicationContext的prepareRefresh()方法,跟进源码:

    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {	
    	protected void prepareRefresh() {
    		// 设置启动时间
    		this.startupDate = System.currentTimeMillis();
    		// 设置关闭状态为false
    		this.closed.set(false);
    		// 设置激活状态为true
    		this.active.set(true);
    		if (logger.isDebugEnabled()) {
    			if (logger.isTraceEnabled()) {
    				logger.trace("Refreshing " + this);
    			}
    			else {
    				logger.debug("Refreshing " + getDisplayName());
    			}
    		}
    		initPropertySources();
    		getEnvironment().validateRequiredProperties();
    		if (this.earlyApplicationListeners == null) {
    			this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    		}
    		else {
    			this.applicationListeners.clear();
    			this.applicationListeners.addAll(this.earlyApplicationListeners);
    		}
    		this.earlyApplicationEvents = new LinkedHashSet<>();
    	}
    }
    

    初始化属性源

    跟进initPropertySources()方法源码:

    public class GenericWebApplicationContext extends GenericApplicationContext implements ConfigurableWebApplicationContext, ThemeSource {	
    	@Override
    	protected void initPropertySources() {
    		ConfigurableEnvironment env = getEnvironment();
    		if (env instanceof ConfigurableWebEnvironment) {
    			((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);
    		}
    	}
    }
    public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
    	@Override
    	public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
    		WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
    	}
    }
    public abstract class WebApplicationContextUtils {
    	public static void initServletPropertySources(MutablePropertySources sources,@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
    		Assert.notNull(sources, "'propertySources' must not be null");
    		String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
    		if (servletContext != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
    			sources.replace(name, new ServletContextPropertySource(name, servletContext));
    		}
    		name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
    		if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
    			sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
    		}
    	}
    }
    

    这里主要是替换属性源,在应用启动之前替换一些属性占位符,但是在启动时由于servletContext、servletConfig都为空,所以这里并没有实现。

    校验必备属性

    跟进getEnvironment().validateRequiredProperties()源码:

    public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {
    
    	@Override
    	public void validateRequiredProperties() {
    		MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
    		for (String key : this.requiredProperties) {
    			if (this.getProperty(key) == null) {
    				ex.addMissingRequiredProperty(key);
    			}
    		}
    		if (!ex.getMissingRequiredProperties().isEmpty()) {
    			throw ex;
    		}
    	}
    }
    

    若存在必备属性没有配置则抛出异常,我们可以在初始化器中获取环境对象,并在环境对象中设置必备属性,改造之前的初始化器:

    @Order(1)
    public class Order1Initializer implements ApplicationContextInitializer {
    
        private Logger log = LoggerFactory.getLogger(getClass());
    
        public void initialize(ConfigurableApplicationContext applicationContext) {
            ConfigurableEnvironment environment = applicationContext.getEnvironment();
            environment.setRequiredProperties("springboot-start-properties");
        }
    }
    

    启动后会抛出如下异常:

    2020-08-16 11:02:37.146  WARN 7052 --- [           main] o.s.boot.SpringApplication               : Error handling failed (ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@223f3642, started on Sun Aug 16 11:02:37 CST 2020)
    2020-08-16 11:02:37.150 ERROR 7052 --- [           main] o.s.boot.SpringApplication               : Application run failed
    org.springframework.core.env.MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [springboot-start-properties]
    

    我们可以通过在全局配置文件中添加必备属性配置解决,例如:

    springboot-start-properties=xxxxxxxxxxxx
    

    初始化本地监听器

    只是将之前有初始化过的监听器赋值给本地监听器变量,为了后期刷新使用,同时也初始化好对应的监听事件列表:

    if (this.earlyApplicationListeners == null) {
    	this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    }else {
    	this.applicationListeners.clear();
    	this.applicationListeners.addAll(this.earlyApplicationListeners);
    }
    this.earlyApplicationEvents = new LinkedHashSet<>();
    

    1.2.2 刷新BeanFactory

    跟进源码:

    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    	refreshBeanFactory();
    	return getBeanFactory();
    }
    public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {	
    	@Override
    	protected final void refreshBeanFactory() throws IllegalStateException {
    		if (!this.refreshed.compareAndSet(false, true)) {
    			throw new IllegalStateException(
    					"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
    		}
    		this.beanFactory.setSerializationId(getId());
    	}
    }
    public void setSerializationId(@Nullable String serializationId) {
    	if (serializationId != null) {
    		serializableFactories.put(serializationId, new WeakReference<>(this));
    	}
    	else if (this.serializationId != null) {
    		serializableFactories.remove(this.serializationId);
    	}
    	this.serializationId = serializationId;
    }
    

    这里仅仅是为beanFactory设置一个id默认id值为application,并且将ID和beanFactory放入到一个map类型的工厂实例中。

    1.2.3 配置BeanFactory

    跟进源码如下:

    prepareBeanFactory(beanFactory);
    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
    
    	String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
    	String ENVIRONMENT_BEAN_NAME = "environment";
    	String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";
    
    	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    		beanFactory.setBeanClassLoader(getClassLoader());
    		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    		beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    		beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    		if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    		}
    
    		if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
    			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    		}
    		if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
    			beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    		}
    		if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
    			beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    		}
    	}
    }
    

    这里主要设置了beanFactory的一些属性、设置忽略自动装配接口、添加后置处理器以及注册一些组件。

    1.2.4 后处理BeanFactory

    跟进源码:

    postProcessBeanFactory(beanFactory);
    public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
    
    	@Override
    	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    		super.postProcessBeanFactory(beanFactory);
    		if (this.basePackages != null && this.basePackages.length > 0) {
    			this.scanner.scan(this.basePackages);
    		}
    		if (!this.annotatedClasses.isEmpty()) {
    			this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
    		}
    	}
    }
    

    待续...

  • 相关阅读:
    Thinkd Pad打开无线网络方法
    模式问题
    SQL数据库,使用事务执行增删改操作,给自己一个后悔的机会
    iOS开发-数据库FMDB队列
    iOS开发-数据库FMDB简单介绍
    iOS开发-地图定位 CoreLocation地理编码
    iOS开发-网络篇 文件的上传
    iOS开发-项目新特性
    iOS开发-Xcode插件管理工具Alcatraz的安装和使用
    iOS开发-AFNetworking 怎样上传数据? 怎样上传模拟表单
  • 原文地址:https://www.cnblogs.com/Json1208/p/13379641.html
Copyright © 2020-2023  润新知