Take you to understand Spring's loading mechanism from the source point of view

Preface

Spring is the only way for Java programmers, but for Spring learning, I believe that most people stay at the stage where they can use it. Then do you know what work Spring does when loading? How is the Spring context loaded? How does Spring achieve flexible expansion? Only by understanding these can we better understand Spring's implementation details and control Spring better!

Talk about containers

Many people may often talk about the concept of containers. After all, the concept of containers is very popular. So what is a container? Linux Container? Docker? A simple Map? The answer is all right. These are all containers. The cups we drink are also containers. The containers at the seaside dock are also containers. The role of containers is to "pack"-"pack" things! The concept of containers is very broad, and the focus is on this isolation container idea.

I believe that most DevOps have experienced things like this. The developed application needs to be deployed. Therefore, the operating system and various dependent environments are installed. After the deployment is completed, the company now needs to clean up the server and the application needs to be deployed. Ported to other servers. Do we need to redeploy? At this time, the smart seniors are thinking whether there is a way to make the deployed services portable to other places without installing a set of operating systems and dependent environments. This is like container transportation. The goods, a Lamborghini (like a developed application), are packaged into a container and transported from Shanghai terminal (CentOS7 environment) to other terminals (Ubuntu14 environment) by freighter, and during transportation , My Lamborghini (application) did not receive any damage (file loss), and after unloading at another terminal, it can still go fast. (Start up normally). Of course, this problem has now been solved very well! This is the application of container technology~

The core of Spring is the container. Is the Spring container a Map?

The Spring container is not just a Map, because the Spring container is not only for storing Beans, but its main function is to "manage the lifecycle and dependencies of Beans". Object creation, destruction...

Component A depends on components B and C, C depends on D, D depends on B... Now I want to get component A, I need to initialize other components layer by layer, and there may be interdependence between other components. Dependency management will be very troublesome if manual management ~ Containers will help us manage components, allowing developers to focus on program development.

Therefore, managing Beans is the core of the Spring container!

Loading of Spring ApplicationContext

To understand Spring's loading mechanism, you must first understand what Spring ApplicationContext is and how it relates to BeanFactory.

Spring ApplicationContext is designed around Spring as a whole. From the perspective of type, it is of BeanFactory type, because it is the implementation class of BeanFactory and has richer functions than BeanFactory. It can be understood that ApplicatonContext extends BeanFactory and is the core design of the Spring ApplicationContext framework.

By looking at the class diagram, we know that AbstractApplicationContext is the implementation class of ApplicationContext and the most important core class. The core method refresh is provided by the AbstractApplicationContext class.
Insert picture description here
See what the refresh() method of AbstractApplicationContext does?

@Override
public void refresh() throws BeansException, IllegalStateException {
    
    
	// 互斥锁,防止重复调用,保证上下文的对象状态
    synchronized(this.startupShutdownMonitor) {
    
    
    	// 启动前准备参数
        this.prepareRefresh();
        // 获得子类创建的 BeanFactory 实例,如已经存在,销毁重新创建
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        // 给新创建的 BeanFactory实例,准备上下文环境--类加载器,bean表达式解析器,bean后置处理器等
        this.prepareBeanFactory(beanFactory);

        try {
    
    
        	// 在标准初始化后,修改应用上下文的内部beanFactory,通过重写此方法加载所有bean,但是不实例化bean。
        	// 调用 BeanFactory 实例化后置处理器
            this.postProcessBeanFactory(beanFactory);
            // 调用 工厂处理器 注册 bean
            this.invokeBeanFactoryPostProcessors(beanFactory);
            // 在 Bean 工厂中注册 Bean 的后置处理器
            this.registerBeanPostProcessors(beanFactory);
            // 初始化消息源,并且设置父消息源来自父容器的配置
            this.initMessageSource();
            // 初始化 消息推送器,注册一个默认的单例 Bean
            this.initApplicationEventMulticaster();
            // 调用子类重写的方法初始化其他 bean,模板方法设计模式的体现!
            this.onRefresh();
            // 注册 bean 监听器
            this.registerListeners();
            // 初始化所有剩余的单例 bean(非延迟加载的)并设置冻结标志位,防止重新实例化 Bean 浪费资源
            this.finishBeanFactoryInitialization(beanFactory);
            // 注册、启动 LifecycleProcessor,并且发送启动完成事件
            this.finishRefresh();
        } catch (BeansException var9) {
    
    
            if (this.logger.isWarnEnabled()) {
    
    
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
            }

			// 销毁已经创建的单例 bean ,避免悬挂资源
            this.destroyBeans();
            // 释放标志位,标识其可以重新启动
            this.cancelRefresh(var9);
            throw var9;
        } finally {
    
    
        	// 清除与反射相关的缓存
            this.resetCommonCaches();
        }

    }
}

Reading through the above source code, we can know that the template method design pattern is used when the bean is initialized, allowing subclasses to implement their own instance loading. Let's take a look at how subclasses can flexibly extend instances through the AbstractApplicationContext base class.

XmlWebApplicationContext

Let's take a look at how the subclass XmlWebApplicationContext of AbstractApplicationContext is extended to adapt to the context instance of the Web scene through the template method of the base class.

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    
    
	// 创建一个读取XML文件的实例对象,并将读取到的 Bean 描述定义信息加载到 BeanFactory 中
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

	// 配置读取 Bean 的上下文环境
	beanDefinitionReader.setEnvironment(getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

	// 使用当前 reader 读取指定的 XML 文件中的 Bean 描述信息到工厂中
	initBeanDefinitionReader(beanDefinitionReader);
	loadBeanDefinitions(beanDefinitionReader);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
    
    
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
    
    
		// 读取 XML 配置文件
		for (String configLocation : configLocations) {
    
    
			reader.loadBeanDefinitions(configLocation);
		}
	}
}

// 子类 StaticWebApplicationContext 重写父类 AbstractApplicationContext 的方法,使用 BeanFactory 实例化后置处理器
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    
    
	// 在当前的 Bean 工厂中添加 Bean 后置处理器
	beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
	beanFactory.ignoreDependencyInterface(ServletContextAware.class);
	beanFactory.ignoreDependencyInterface(ServletConfigAware.class);

	// 注册 Web 应用的 Bean 作用范围、WebRequest、Session,详情见下方!
	WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
	// 注册 上下文环境和配置参数,并注册两个 Bean 用于存放这些参数
	WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}

The registerWebApplicationScopes method of WebApplicationContextUtils!

// 注册 web 应用的 Bean 作用域范围
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);
		// 注册 Servlet 上下文属性
		sc.setAttribute(ServletContextScope.class.getName(), appScope);
	}

	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) {
    
    
		// 避免 jsf 依赖
		FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
	}
}

Reading through the above source code, you can see how XmlWebApplicationContext uses the base class AbstractApplicationContext to flexibly expand the context instance adapted to the web scene.

Guess you like

Origin blog.csdn.net/weixin_42653522/article/details/109266142