SpringMVC 入口及父子容器源码解析

前言

描述: springmvc相关文章如下:
【1】SpringMVC 入口及父子容器源码解析
【2】SpringMVC 请求调用流程源码解析

1 工程简介

描述: web工程
在这里插入图片描述

1.1 pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <packaging>war</packaging>
    <groupId>org.example</groupId>
    <artifactId>rosh-spring</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <spring.version>5.2.8.RELEASE</spring.version>
    </properties>

    <dependencies>
        <!--上下文-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--切面-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--事务-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--spring-mvc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
        </dependency>
        <!--servlet-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- 日志相关依赖 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.10</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.1.2</version>
        </dependency>
        <!--测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
    </dependencies>


</project>

1.2 配置文件

public class RoshWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    /**
     *  父容器配置文件
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        System.out.println("RoshWebInitializer invoke getRootConfigClasses");
        return new Class<?>[]{SpringRootConfig.class};
    }
    /**
     * 子容器配置
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        System.out.println("RoshWebInitializer invoke getServletConfigClasses");
        return new Class<?>[]{ SpringServletConfig.class};
    }
    /**
     *拦截请求(静态资源、js、css.......)
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}


/**
 * 父容器配置文件,只扫描service
 */
@Configuration
@ComponentScan(value = "com.rosh.service")
public class SpringRootConfig {

}


/**
 *  子容器配置文件,仅仅扫描@Controller、@RestController
 */
@Configuration
@ComponentScan(value="com.rosh",includeFilters={
        @ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class, RestController.class})
},useDefaultFilters=false)
@EnableWebMvc
public class SpringServletConfig {

}

1.3 HelloController

@RestController
@RequestMapping("/hello")
public class HelloController {


    @Autowired
    private HelloService helloService;


    @GetMapping("")
    public String printHello() {

        return helloService.printHello();
    }


}

1.4 HelloService

@Service
public class HelloService {


    public String printHello() {

        return "Hello World";
    }

}

1.5 启动tomcat

在这里插入图片描述

2 servlet3.0规范

在web容器启动时为提供给第三方组件机会做一些初始化的工作,servlet规范(JSR356)中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。

2.1 spring-web包

在这里插入图片描述

/**
 *
 * servlet 初始化时,会加载实现WebApplicationInitializer接口的所有类,调取onStartup方法。
 *
 */
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

	/**
	 * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
	 * implementations present on the application classpath.
	 * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
	 * Servlet 3.0+ containers will automatically scan the classpath for implementations
	 * of Spring's {@code WebApplicationInitializer} interface and provide the set of all
	 * such types to the {@code webAppInitializerClasses} parameter of this method.
	 * <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
	 * this method is effectively a no-op. An INFO-level log message will be issued notifying
	 * the user that the {@code ServletContainerInitializer} has indeed been invoked but that
	 * no {@code WebApplicationInitializer} implementations were found.
	 * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
	 * they will be instantiated (and <em>sorted</em> if the @{@link
	 * org.springframework.core.annotation.Order @Order} annotation is present or
	 * the {@link org.springframework.core.Ordered Ordered} interface has been
	 * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
	 * method will be invoked on each instance, delegating the {@code ServletContext} such
	 * that each instance may register and configure servlets such as Spring's
	 * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
	 * or any other Servlet API componentry such as filters.
	 * @param webAppInitializerClasses all implementations of
	 * {@link WebApplicationInitializer} found on the application classpath
	 * @param servletContext the servlet context to be initialized
	 * @see WebApplicationInitializer#onStartup(ServletContext)
	 * @see AnnotationAwareOrderComparator
	 */
	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<>();

		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				// Be defensive: Some servlet containers provide us with invalid classes,
				// no matter what @HandlesTypes says...
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}

		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}

}

2.2 RoshWebInitializer

描述: 查看RoshWebInitializer类图,发现该类实现了WebApplicationInitializer接口,所以当servlet容器启动时,会加载该配置文件,并且执行onStartup方法。
在这里插入图片描述

public class RoshWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    /**
     *  父容器配置文件
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        System.out.println("RoshWebInitializer invoke getRootConfigClasses");
        return new Class<?>[]{SpringRootConfig.class};
    }
    /**
     * 子容器配置
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        System.out.println("RoshWebInitializer invoke getServletConfigClasses");
        return new Class<?>[]{ SpringServletConfig.class};
    }
    /**
     *拦截请求(静态资源、js、css.......)
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

3 父容器源码解析

描述: 打断点,debug。
在这里插入图片描述

在这里插入图片描述

3.1 创建父容器

描述: 创建父容器(AnnotationConfigWebApplicationContext)、创建监听器。

	protected void registerContextLoaderListener(ServletContext servletContext) {

		/**
		 * 创建上下文
		 */
		WebApplicationContext rootAppContext = createRootApplicationContext();
		if (rootAppContext != null) {
			/**
			 *创建监听器
			 */
			ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
			listener.setContextInitializers(getRootApplicationContextInitializers());

			/**
			 * 把listener加入到上下文
			 */
			servletContext.addListener(listener);
		}
		else {
			logger.debug("No ContextLoaderListener registered, as " +
					"createRootApplicationContext() did not return an application context");
		}
	}

在这里插入图片描述

描述: 创建父容器,直接new AnnotationConfigWebApplicationContext,加载父容器配置文件,然后返回。

在这里插入图片描述

@Override
	@Nullable
	protected WebApplicationContext createRootApplicationContext() {
		
		/**
		 * 获取父容器配置文件
		 */   
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			
			/**
			 * 创建父容器设置父容器配置文件
			 */   
			AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
			context.register(configClasses);
			return context;
		}
		else {
			return null;
		}
	}

3.2 父容器初始化

3.2.1 监听器

描述: ContextLoaderListener 监听器,实现ServletContextListener接口。ServletContextListener接口包含两个方法,一个是contextInitialized()方法,用来监听ServletContext的启动和初始化;一个是contextDestroyed()方法,用来监听ServletContext的销毁。
在这里插入图片描述

3.2 ContextLoaderListener

描述: 在servletContext容器初始化时,调用方法contextInitialized
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

描述: ioc容器初始化,可以看前面几篇文章。
在这里插入图片描述

/**
	 * 该方法是Spring容器初始化核心方法,采用模板设计模式。根据不同的上下文对象会调用不同对象子类方法。
	 *
	 * 核心上下文子类:
	 * 		AbstractXmlApplicationContext(XML上下文)
	 * 		AnnotationConfigApplicationContext(注解上下文)
	 *
	 *
	 */
	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {

			//容器初始化
			prepareRefresh();

			/**
			 * AbstractXmlApplicationContext:
			 *
			 * (1) 创建BeanFacotry
			 * (2) 解析Xml
			 */
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 给beanFactory设置一些值
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				//完成对BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor接口的调用
				invokeBeanFactoryPostProcessors(beanFactory);

				//实现了BeanPostProcessor接口的实例化,并且加入到BeanFactory中
				registerBeanPostProcessors(beanFactory);

				// 国际化
				initMessageSource();

				// Initialize event multicaster for this context.
				//初始化事件管理器
				initApplicationEventMulticaster();

				// 钩子方法
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				/**
				 *
				 * bean的实例化、IOC
				 *
				 */
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				/**
				 * publicsh event 事件
				 */
				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();
			}
		}
	}

3.3 子容器初始化

在这里插入图片描述

在这里插入图片描述

protected void registerDispatcherServlet(ServletContext servletContext) {
		String servletName = getServletName();
		Assert.hasLength(servletName, "getServletName() must not return null or empty");

		/**
		 * 【1】 创建mvc上下文
		 */
		WebApplicationContext servletAppContext = createServletApplicationContext();
		Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");

		/**
		 * 【2】 创建DispatcherServlet对象,把springmvc上下文设置到dispatcherServlet中
		 */
		FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
		Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
		dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

		/**
		 * 【3】 把DispatcherServlet加入到servlet上下文中
		 */
		ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
		if (registration == null) {
			throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
					"Check if there is another servlet registered under the same name.");
		}

		registration.setLoadOnStartup(1);
		//拦截器
		registration.addMapping(getServletMappings());
		registration.setAsyncSupported(isAsyncSupported());

		//过滤器
		Filter[] filters = getServletFilters();
		if (!ObjectUtils.isEmpty(filters)) {
			for (Filter filter : filters) {
				registerServletFilter(servletContext, filter);
			}
		}

		customizeRegistration(registration);
	}

3.3.1 创建子容器

描述: 和父容器一样,获取子容器配置文件,创建AnnotationConfigWebApplicationContext,设置配置文件,返回。
在这里插入图片描述
在这里插入图片描述

3.3.2 子容器初始化

结论: DispatcherServlet继承了HttpServlet,子容器的初始化在servlet init方法中
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3.3 设置父子容器

在这里插入图片描述

protected WebApplicationContext initWebApplicationContext() {

		/**
		 * 【1】 从servletContext中获取父容器
		 */
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it

			/**
			 * 【2】 获取springMvc 容器,子容器
			 */
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						/**
						 *【3】 设置父子关系
						 */
						cwac.setParent(rootContext);
					}
					/**
					 * 【4】 启动容器
					 */
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			synchronized (this.onRefreshMonitor) {
				onRefresh(wac);
			}
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}

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

3.3.4 初始化自容器其它属性

描述: 当子容器完成bean的创建后,会触发publish事件初始化spring mvc 相关属性。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

响彻天堂丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值