首页 > 其他分享 >SpringBoot启动流程分析之准备应用上下文refreshContext()(八)

SpringBoot启动流程分析之准备应用上下文refreshContext()(八)

时间:2024-06-06 15:33:38浏览次数:17  
标签:SpringBoot beanFactory refreshContext void refresh bean new 上下文 class

SpringBoot启动流程分析之准备应用上下文refreshContext()(八)

文章目录


org.springframework.boot.SpringApplication#run(java.lang.String…)

public ConfigurableApplicationContext run(String... args) {
            .... 
	try {
            //本篇内容从本行开始记录  
            refreshContext(context);
           //本篇内容记录到这,后续更新
            ....
        }
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}
}

流程分析

private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

最终调用父类AbstractApplicationContext的refresh()方法。

protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}
@Override
public final void refresh() throws BeansException, IllegalStateException {		
    try {
	     super.refresh();
    }
    catch (RuntimeException ex) {
	    stopAndReleaseWebServer();
	    throw ex;
    }
}

可以看到refresh中的步骤都是单个单个的方法

如果看过Spring源码的同学现在应该很熟悉了…
org.springframework.context.support.AbstractApplicationContext#refresh

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
	    // 准备刷新
	    prepareRefresh();
 
	    // 通知子类刷新内部bean工厂
	    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 
	    // 准备bean工厂以便在此上下文中使用
	    prepareBeanFactory(beanFactory);
 
	    try {
	    	// 允许上下文子类中对bean工厂进行后处理
	    	postProcessBeanFactory(beanFactory);
 
	    	// 在bean创建之前调用BeanFactoryPostProcessors后置处理方法
	    	invokeBeanFactoryPostProcessors(beanFactory);
 
	    	// 注册BeanPostProcessor
	    	registerBeanPostProcessors(beanFactory);
 
	    	// 注册DelegatingMessageSource
	    	initMessageSource();
 
	    	// 注册multicaster
	    	initApplicationEventMulticaster();
 
	    	// 创建内置的Servlet容器
	    	onRefresh();
 
	    	// 注册Listener
	    	registerListeners();
 
	    	// 完成BeanFactory初始化,初始化剩余单例bean
	    	finishBeanFactoryInitialization(beanFactory);
 
	    	// 发布对应事件
	    	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();
	    }
    }
}

开始之前先把AnnotationConfigServletWebServerApplicationContext类图放这:
在这里插入图片描述

1、准备刷新

先调用子类重写的方法,再调用父类方法。还记得前面讲过在创建AnnotationConfigServletWebServerApplicationContext的时候构造方法中实例化了一个ClassPathBeanDefinitionScanner。

@Override
protected void prepareRefresh() {
    this.scanner.clearCache();
    super.prepareRefresh();
}
1.1、子类prepareRefresh()方法

在其父类ClassPathScanningCandidateComponentProvider中有一个MetadataReaderFactory(接口)工厂对象,判断该对象是否是CachingMetadataReaderFactory这个特定类或者是它的子类的一个实例,返回Boolean值,是就是true,则清除缓存。该类中有个Map,用来缓存每个Spring资源句柄(即每个“.class”文件)的MetadataReader实例。

@Nullable
private Map<Resource, MetadataReader> metadataReaderCache;

/**
 * Clear the local MetadataReader cache, if any, removing all cached class metadata.
 */
public void clearCache() {
    if (this.metadataReaderCache instanceof LocalResourceCache) {
        synchronized (this.metadataReaderCache) {
            this.metadataReaderCache.clear();
        }
    }
    else if (this.metadataReaderCache != null) {
        // Shared resource cache -> reset to local cache.
        setCacheLimit(DEFAULT_CACHE_LIMIT);
    }
}

缓存Map如果是LocalResourceCache(可以看到该类继承了LinkedHashMap),执行的就是LinkedHashMap的clear()方法了

else如果缓存不为空,就是重新new一个LocalResourceCache

 * Specify the maximum number of entries for the MetadataReader cache.
 * <p>Default is 256 for a local cache, whereas a shared cache is
 * typically unbounded. This method enforces a local resource cache,
 * even if the {@link ResourceLoader} supports a shared resource cache.
 */
public void setCacheLimit(int cacheLimit) {
    if (cacheLimit <= 0) {
        this.metadataReaderCache = null;
    }
    else if (this.metadataReaderCache instanceof LocalResourceCache) {
        ((LocalResourceCache) this.metadataReaderCache).setCacheLimit(cacheLimit);
    }
    else {
        this.metadataReaderCache = new LocalResourceCache(cacheLimit);
    }
}
/**
 * Clear the local MetadataReader cache, if any, removing all cached class metadata.
 */
public void clearCache() {
    if (this.metadataReaderCache instanceof LocalResourceCache) {
        synchronized (this.metadataReaderCache) {
            this.metadataReaderCache.clear();
        }
    }
    else if (this.metadataReaderCache != null) {
        // Shared resource cache -> reset to local cache.
        setCacheLimit(DEFAULT_CACHE_LIMIT);
    }
}


@SuppressWarnings("serial")
private static class LocalResourceCache extends LinkedHashMap<Resource, MetadataReader> {

    private volatile int cacheLimit;

    public LocalResourceCache(int cacheLimit) {
        super(cacheLimit, 0.75f, true);
        this.cacheLimit = cacheLimit;
    }

    public void setCacheLimit(int cacheLimit) {
        this.cacheLimit = cacheLimit;
    }

    public int getCacheLimit() {
        return this.cacheLimit;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<Resource, MetadataReader> eldest) {
        return size() > this.cacheLimit;
    }
}
1.2 父类prepareRefresh()方法
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());
		}
	}
 
	// 调用子类GenericWebApplicationContext重写后的方法替换servlet相关属性源
	initPropertySources();
 
	
	// 这里是验证由ConfigurablePropertyResolver#setRequiredProperties()方法指定的属性,解析为非空值,如果没有设置的话这个方法就不会执行什么操作。
	getEnvironment().validateRequiredProperties();
 
	// Allow for the collection of early ApplicationEvents,
	// to be published once the multicaster is available...
	this.earlyApplicationEvents = new LinkedHashSet<>();
}

在这里插入图片描述
在这里插入图片描述

看下最后这个方法,initServlet属性源,方法注释是这么说的将基于Servlet的StubPropertySource替换为使用给定servletContext和servletConfig对象填充的实例。此方法可以被调用任意次数,但将用相应的实际属性源替换为StubPropertySource一次且仅一次。

看下if判断里的条件,servletContext不为空,source中存在指定name的的属性源,且该属性源要是StubPropertySource的类型或者是其子类。也就是当第一次调用以后,该属性源就被替换成了ServletContextPropertySource和ServletConfigPropertySource,所以之后的调用最后一个判断条件就不会成立了。

public static void initServletPropertySources(MutablePropertySources sources,
			@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
 
	Assert.notNull(sources, "'propertySources' must not be null");
        //servletContextInitParams
	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));
	}
        //servletConfigInitParams
	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));
	}
}

看validateRequiredProperties()方法前,先看下Environment的类图。这里是验证由ConfigurablePropertyResolver#setRequiredProperties()方法指定的属性,解析为非空值,如果没有设置的话这个方法就不会执行什么操作,所以这里就略过了。
在这里插入图片描述

方法最后new了一个LinkedHashSet,收集早期事件,如果multicaster 有用就会广播事件。prepareRefresh()就执行完了。

2、通知子类刷新内部bean工厂

在这里插入图片描述

可以看到refreshBeanFactory()方法上的注释说的,什么都不做:我们拥有一个内部beanfactory,并依靠调用方通过我们的公共方法(或beanfactory)注册bean。

前面介绍GenericApplicationContext说了与每次刷新创建新的内部beanfactory实例的其他applicationContext实现不同,此上下文的内部beanfactory从一开始就可用,以便能够在其上注册bean定义。只能调用一次refresh()。

所以在该方法内只设置了SerializationId,该id是在准备应用上下文时调用ContextIdApplicationContextInitializer时设置的id,在setSerializationId方法中,使用id做key,new了一个弱引用对象为value,添加到了serializableFactories中,DefaultListableBeanFactory为被弱引用对象;如果需要,可以通过id得到引用对象,在通过get()方法得到DefaultListableBeanFactory对象。

@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());
}
/** Map from serialized id to factory instance. */
private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories = new ConcurrentHashMap<>(8);
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;
}
3、准备bean工厂
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	//通知内部bean工厂使用上下文的类加载器
	beanFactory.setBeanClassLoader(getClassLoader());
        //为bean定义值中的表达式指定解析策略,即解析el表达式,默认"#{"开头,"}"结尾
	beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        //在这里new了一个资源编辑注册器ResourceEditorRegistrar,该类实现了PropertyEditorRegistrar接口
        //作用是使用以下资源编辑器去填充给的的注册表:ResourceEditor、InputStreamEditor、InputSourceEditor、FileEditor、Urleditor、UriEditor、ClassEditor、ClassArrayEditor。
        //如果给的注册表是PropertyEditorRegistrySupport类型,编辑器交由该类管理
	beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
 
	// 向BeanPostProcessor的List中添加一个ApplicationContextAwareProcessor,参数是上下文
        //该类作用是将上下文传递给实现environmentaware、embeddedValueResolveraware、resourceLoaderware、applicationEventPublisheraware、messageSourceAware或applicationContextaware接口的bean。
	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 interface not registered as resolvable type in a plain factory.
	// MessageSource registered (and found for autowiring) as a bean.
        //根据第二个参数注册一个特殊的依赖类型
	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
	beanFactory.registerResolvableDependency(ResourceLoader.class, this);
	beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
	beanFactory.registerResolvableDependency(ApplicationContext.class, this);
 
	// 该BeanPostProcessor检测那些实现了ApplicationListener接口的bean,在它们创建时初始化之后,将它们添加到应用上下文的事件多播器上
        //并在这些ApplicationListener bean销毁之前,将它们从应用上下文的事件多播器上移除。
	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
 
	// Detect a LoadTimeWeaver and prepare for weaving, if found.
	if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		// Set a temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
	}
 
	// Register default environment beans.
        //注册默认environment bean,如果beanfactory不存在environment 
	if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
	}
        //systemProperties 同上
	if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
	}
        //systemEnvironment 同上
	if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
	}
}
4、允许上下文子类对bean工厂进行后置处理

basePackages和annotatedClasses都为空。跳过
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext#postProcessBeanFactory

@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));
    }
}

注册了一个WebApplicationContextServletContextAwareProcessor。并将ServletContextAware添加到忽略的集合中。再注册web相关作用域bean。
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#postProcessBeanFactory

@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    beanFactory.addBeanPostProcessor(
            new WebApplicationContextServletContextAwareProcessor(this));
    beanFactory.ignoreDependencyInterface(ServletContextAware.class);
    registerWebApplicationScopes();
}
private void registerWebApplicationScopes() {
    ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
            getBeanFactory());
    WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());
    existingScopes.restore();
}   
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,
        @Nullable ServletContext sc) {

    beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
    beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());
    if (sc != null) {
        ServletContextScope appScope = new ServletContextScope(sc);
        beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
        // Register as ServletContext attribute, for ContextCleanupListener to detect it.
        sc.setAttribute(ServletContextScope.class.getName(), appScope);
    }
    // 注册ServletRequest 的工厂bean
    beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
    beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
    beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
    beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
    if (jsfPresent) {
        FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
    }
}

标签:SpringBoot,beanFactory,refreshContext,void,refresh,bean,new,上下文,class
From: https://blog.csdn.net/lvyuanj/article/details/139466731

相关文章

  • SpringBoot+Vue房屋租赁网站(前后端分离)
    技术栈JavaSpringBootMavenMySQLVueElement-UIShiroMybatis-Plus系统角色用户管理员房东系统功能截图......
  • 【第7章】SpringBoot实战篇之用户详细信息
    文章目录前言一、获取用户详细信息1.ThreadLocalUtil2.LoginInceptor3.UserController14.测试二、更新用户基本信息1.ValidatedGroups2.User3.UserController14.service5.测试1.参数校验2.更新测试三、更新用户头像1.UserController12.测试四、更新用户密......
  • 【第8章】SpringBoot实战篇之文章分类(上)
    文章目录前言一、后端代码1.CategoryController2.service3.CategoryMapper4.Category二、测试1.失败(校验)2.正常总结前言从这开始进入文章相关的接口开发,本章主要介绍定义文章分类接口和新增文章分类建表语句和测试用例,在SpringBoot专栏首页,此处只涉及后......
  • 【第5章】SpringBoot实战篇之登录模式切换
    文章目录前言一、接口扩展1.LoginStorage2.LocalLoginStorage3.RedisLoginStorage4.参数配置二、登录相关接口改动1.登录接口2.登录拦截器总结前言前面分别介绍了本地Map和redis存储用户登录信息,但是第二天我登录就出现问题了,因为我Redis部署在虚拟机里面,不......
  • 【毕业设计】基于SpringBoot星之语明星周边产品销售网站的设计与实现.zip
    1.项目概述随着信息互联网信息的飞速发展,无纸化作业变成了一种趋势,针对这个问题开发一个专门适应洗衣店业务新的交流形式的网站。本文介绍了星之语明星周边产品销售网站的开发全过程。通过分析企业对于星之语明星周边产品销售网站的需求,创建了一个计算机管理星之语明星周边产......
  • JAVA计算机毕业设计基于的儿童疫苗预约系统(附源码+springboot+开题+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着科技的进步和人们健康意识的增强,儿童疫苗接种已成为保障儿童健康成长的重要措施。然而,传统的疫苗预约方式往往存在诸多不便,如预约流程繁琐、信息......
  • JAVA计算机毕业设计基于的高校党务管理系统(附源码+springboot+开题+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着高校党建工作的不断深入和发展,党务管理面临着越来越多的挑战。传统的党务管理方式往往依赖于纸质记录和人工操作,效率低下且容易出错。为了提高党......
  • SpringBoot 请求响应
    SpringBoot请求响应来源于黑马程序员JavaWeb课程,总结笔记1.ApiFoxApifox快速入门教程2.基本参数简单参数:在向服务器发起请求时,向服务器传递的是一些普通的请求数据。//RequestController.javaimportjakarta.servlet.http.HttpServletRequest;importorg.springf......
  • JAVA计算机毕业设计基于的畅游旅游网(附源码+springboot+开题+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在信息化和全球化的大背景下,旅游业作为现代服务业的重要组成部分,正在经历着前所未有的变革。畅游旅游网作为一个集旅游信息、服务、交易于一体的综合......
  • JAVA计算机毕业设计基于的仓库管理系统(附源码+springboot+开题+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的迅猛发展和企业规模的不断扩大,仓库管理作为企业运营中不可或缺的一环,面临着日益复杂和多样化的挑战。传统的仓库管理模式依赖于人工操......