3、spring5.2.x源码解读之容器启动IOC实现流程

1、什么是IOC

IOC是Inversion of Control的缩写,控制反转,将创建对象的实现交给spring,从而降低项目的复杂度和系统之间的耦合度。

2、web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
	<!-- 指定spring加载容器的类,没指定默认去加载spring-web下的ContextLoader.properties文件,使用org.springframework.web.context.support.XmlWebApplicationContex -->
	<context-param>
	    <param-name>contextClass</param-name>
	    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
	</context-param>
    <!--  日志配置文件 -->
    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>/WEB-INF/classes/log4j.properties</param-value>
    </context-param>
    <!-- Spring相关配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/classes/application*.xml</param-value>
    </context-param>
	<!-- 配置过滤器-->
    <filter>
        <filter-name>CharacterFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <init-param>
            <param-name>force</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
	<!-- 为过滤器配置映射路径-->
    <filter-mapping>
        <filter-name>CharacterFilter</filter-name>
        <url-pattern>/</url-pattern>
    </filter-mapping>

      <!--  Spring 容器启动监听器 -->
  <listener>             
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
  
  <!--  Spring MVCServlet,它将加载WEB-INF/testserver-servlet.xml 的
      配置文件,以启动Spring MVC模块-->
    <servlet>
        <servlet-name>testserver</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
   <!-- 配置controller请求后缀-->
    <servlet-mapping>
        <servlet-name>testserver</servlet-name>
        <url-pattern>*.do</url-pattern>
  </servlet-mapping>
  
    <session-config>
        <!-- Default to 5 minute session timeouts -->
        <session-timeout>10</session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>/welcome.jsp</welcome-file>
    </welcome-file-list>
</web-app>

application.xml的配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
	<bean id="messageService" class="org.springframework.test.web.test.service.impl.MessageServiceImpl"/>
	<!-- 配置包扫描,改包下的及子包下的,有@Service注解,@Component注解的,会将对应的对象做初始化bean处理-->
	<context:component-scan base-package="org.springframework.test.web.test" />
</beans>

3、IOC实现流程

在这里插入图片描述
容器加载监听器,容器启动后会去执行,spring初始化入口
org.springframework.web.context.ContextLoaderListener

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
	public ContextLoaderListener() {
	}
	
	public ContextLoaderListener(WebApplicationContext context) {
		super(context);
	}
	
	/**
	 * 初始化web容器的入口
	 */
	@Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}


	/**
	 * Close the root web application context.
	 */
	@Override
	public void contextDestroyed(ServletContextEvent event) {
		closeWebApplicationContext(event.getServletContext());
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}
}

初始化web应用容器
org.springframework.web.context.ContextLoader#initWebApplicationContext

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}

		servletContext.log("Initializing Spring root WebApplicationContext");
		Log logger = LogFactory.getLog(ContextLoader.class);
		if (logger.isInfoEnabled()) {
			logger.info("Root WebApplicationContext: initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			//创建web应用容器,如果没有配置contextClass,就读取默认的配置文件ContextLoader.properties,创建默认的容器XmlWebApplicationContext
			if (this.context == null) {
				this.context = createWebApplicationContext(servletContext);
			}
			//有实现ConfigurableWebApplicationContext接口,就会走这里的逻辑
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					if (cwac.getParent() == null) {
						//加载父级容器
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					//加载web.xml配置的相关spring配置和初始化bean入口,具体加载怎么加载beanDefine和bean请查看:https://blog.csdn.net/qq_38019655/article/details/140385244
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			if (logger.isInfoEnabled()) {
				long elapsedTime = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
			}

			return this.context;
		}
		catch (RuntimeException | Error ex) {
			logger.error("Context initialization failed", ex);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
			throw ex;
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值