Servlet项目启动执行顺序
由前面的回顾内容可知,Servlet项目启动执行顺序如下
- ServletContainerInitializer.onStartup(Set<Class<?>> c, ServletContext ctx)
- ServletContextListener.contextInitialized(ServletContextEvent sce)
- Filter-------init(FilterConfig filterConfig)
- HttpServlet-------------init()
- Filter-------doFilter 过滤前
- HttpServlet------------doget/dopost
- Filter-------doFilter 过滤后
SpringMVC的xml配置
web.xml
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
applicationContext.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
http://www.springframework.org/schema/context/spring-context.xsd
">
<!-- 扫描基本包并开启spring注解 过滤controller层是因为Spring MVC管理Controller,Spring 管理Controller之外的Bean -->
<context:component-scan base-package="com.yoocar" >
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
</beans>
springmvc.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd"
>
<!-- 使用基于注解的控制器,spring会自动扫描base-package下的子包和类,如果扫描到会把这些类注册为bean-->
<context:component-scan base-package="com.yoocar.controller"/>
<!-- mvc 请求映射处理器与适配器 @RequestMapping -->
<mvc:annotation-driven />
<!--配置视图解析器 默认的视图解析器- -->
<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<!--<property name="contentType" value="text/html" />-->
<property name="prefix" value="/WEB-INF/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- json 支持 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
</bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</list>
</property>
</bean>
<!-- 文件上传配置 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize">
<value>104857600</value>
</property>
<property name="maxInMemorySize">
<value>4096</value>
</property>
</bean>
<!-- 拦截器配置 -->
<mvc:interceptors>
<!-- 多个拦截器,顺序执行 -->
<mvc:interceptor>
<!-- 需要拦截的url -->
<mvc:mapping path="/*/*"/>
<!-- 不拦截该请求 -->
<mvc:exclude-mapping path="/user/login*"/>
<mvc:exclude-mapping path="/video/**"/>
<!-- 拦截器实现类 -->
<bean class="com.yoocar.config.VisitInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
SpringServletContainerInitializer
SpringMVC实现了Servlet的spi机制.,如下
源码如下
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
//传入所有实现了WebApplicationInitializer的子类
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 {
//利用反射,实例化实现了WebApplicationInitializer的子类,并加入到initializers中去
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) {
//调用所有实现了WebApplicationInitializer的子类的onStartup方法
initializer.onStartup(servletContext);
}
}
}
-
调用onStartUp()前会先找到@HandlesTypes(WebApplicationInitializer.class) 所有实现了WebApplicationInitializer的类,
-
传入到OnStartup的webAppInitializerClasses参数中,并传入Servlet上下文对象。
-
找到所有WebApplicationInitializer的实现类后, 不是接口、不是抽象则通过反射进行实例化(所以,你会发现内部实现类都是抽象的,你想让其起作用我们必须添加一个自定义实现类,在下文提供我的自定义实现类)
-
调用所有上一步实例化后的对象的onStartup方法
注意,如果使用xml,且没有自定义配置,这里SpringServletContainerInitializer并没有特殊的处理,以下的知识点用于注解版补充,可以先略过
实现了了WebApplicationInitializer接口的类结构图
AbstractDispatcherServletInitializer.onStartup
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
/**
* The default servlet name. Can be customized by overriding {@link #getServletName}.
*/
public static final String DEFAULT_SERVLET_NAME = "dispatcher";
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
//注册DispatcherServlet
registerDispatcherServlet(servletContext);
}
protected void registerDispatcherServlet(ServletContext servletContext) {
//获取DispatcherServlet的名字
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
//创建WebApplicationContext对象,创建子容器
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
//这里new了一个DispatcherServlet
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
//添加dispatcherServlet到servletContext
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);
//调用抽象方法设置映射路径:getServletMappings()(此抽象方法在我们自定义的子类中实现提供我们自定义的映射路径 )
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
protected String getServletName() {
return DEFAULT_SERVLET_NAME;
}
protected abstract WebApplicationContext createServletApplicationContext();
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
return new DispatcherServlet(servletAppContext);
}
@Nullable
protected ApplicationContextInitializer<?>[] getServletApplicationContextInitializers() {
return null;
}
protected abstract String[] getServletMappings();
@Nullable
protected Filter[] getServletFilters() {
return null;
}
protected FilterRegistration.Dynamic registerServletFilter(ServletContext servletContext, Filter filter) {
String filterName = Conventions.getVariableName(filter);
Dynamic registration = servletContext.addFilter(filterName, filter);
if (registration == null) {
int counter = 0;
while (registration == null) {
if (counter == 100) {
throw new IllegalStateException("Failed to register filter with name '" + filterName + "'. " +
"Check if there is another filter registered under the same name.");
}
registration = servletContext.addFilter(filterName + "#" + counter, filter);
counter++;
}
}
registration.setAsyncSupported(isAsyncSupported());
registration.addMappingForServletNames(getDispatcherTypes(), false, getServletName());
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes() {
return (isAsyncSupported() ?
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC) :
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE));
}
protected boolean isAsyncSupported() {
return true;
}
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
}
}
createServletApplicationContext
@Override
protected WebApplicationContext createServletApplicationContext() {
//创建子容器
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
//获取子容器的配置类
Class<?>[] configClasses = getServletConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
//把配置类注册到子容器中
context.register(configClasses);
}
return context;
}
ContextLoaderListener
ContextLoaderListener通过实现ServletContextListener接口,将创建springIOC容器作为父容器融入web容器当中。
- 父容器:springIOC容器,扫描包的时候排除controller层
- 子容器:springweb,只扫描controller层
子容器可以使用父容器的bean
父容器不可以使用子容器的bean
ContextLoaderListener.contextInitialized
直接看ContextLoaderListener.contextInitialized源码
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
/**
* 初始化根容器,关键方法
* Initialize the root web application context.
*/
@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());
}
}
ContextLoader.initWebApplicationContext
//创建和初始化spring主容器对应的WebApplicationContext对象实例
//并调用refresh方法完成从contextConfigLocation指定的配置中,加载BeanDefinitions和创建bean实例
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//判断是否已经有Root WebApplicationContext,已经有则抛出异常
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 {
if (this.context == null) {
//创建上下文对象,即XmlWebApplicationContext(静态方法中从ContextLoader.properties文件中读取) 并赋值给全局变量context
this.context = createWebApplicationContext(servletContext);
}
//XmlWebApplicationContext是ConfigurableWebApplicationContext类型的
if (this.context instanceof ConfigurableWebApplicationContext) {
//这里的cwac就是XmlWebApplicationContext
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
// 设置父容器(如果有),一般来说没有,可忽略
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
//核心方法,完成配置加载,BeanDefinition定义和bean对象创建
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//ioc容器上下文设置到servlet上下文servletContext,将ROOT容器存入到Servlet域中供子容器使用
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;
}
}
ContextLoader.createWebApplicationContext
这里就是实例化了一个root 容器,即spring ioc容器,知识实例化,还没有refresh
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//核心代码
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
//利用反射实例化contextClass
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
ContextLoader.determineContextClass
获取ServletContextclass对象,如果没有指定,xml方式默认为XmlWebApplicationContext
protected Class<?> determineContextClass(ServletContext servletContext) {
//从配置文件中获取contextClass的值,一般不会手动配置
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
//如果没有指定,默认为XmlWebApplicationContext
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
defaultStrategies.getProperty
defaultStrategies的内容在static代码块中加载
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
private static final Properties defaultStrategies;
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
上面的DEFAULT_STRATEGIES_PATH如下
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
ContextLoader.properties文件位于ContextLoader类的统计目录,我们看下ContextLoader.properties文件的内容
# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
xml方式默认为XmlWebApplicationContext,类结构如下
instantiateClass
根据class对象利用反射实例化
public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
Assert.notNull(clazz, "Class must not be null");
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
//根据class对象利用反射实例化
return instantiateClass(clazz.getDeclaredConstructor());
}
catch (NoSuchMethodException ex) {
Constructor<T> ctor = findPrimaryConstructor(clazz);
if (ctor != null) {
return instantiateClass(ctor);
}
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
catch (LinkageError err) {
throw new BeanInstantiationException(clazz, "Unresolvable class definition", err);
}
}
instantiateClass
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
return KotlinDelegate.instantiateClass(ctor, args);
}
else {
Class<?>[] parameterTypes = ctor.getParameterTypes();
Assert.isTrue(args.length <= parameterTypes.length, "Can't specify more arguments than constructor parameters");
Object[] argsWithDefaultValues = new Object[args.length];
for (int i = 0 ; i < args.length; i++) {
if (args[i] == null) {
Class<?> parameterType = parameterTypes[i];
argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null);
}
else {
argsWithDefaultValues[i] = args[i];
}
}
//利用反射实例化
return ctor.newInstance(argsWithDefaultValues);
}
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
loadParentContext(servletContext);
@Nullable
protected ApplicationContext loadParentContext(ServletContext servletContext) {
return null;
}
默认为空,留着扩展
configureAndRefreshWebApplicationContext
这个方法就是根据root容器的配置文件去初始化spring ioc容器并实例化root容器的配置文件的bean
注意,这里springmvc容器的bean还没有实例化
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
//配置root容器的contextId
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
//CONFIG_LOCATION_PARAM=contextConfigLocation
//获取web.xml中的contextConfigLocation配置,即springIOC配置文件的路径
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
//获取Environment环境对象的信息,若没有,则创建Environment环境对象并返回
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
// 使用ApplicationContextInitializer对ApplicationContext进行初始化
customizeContext(sc, wac);
//spring的核心方法
wac.refresh();
}
getEnvironment
@Override
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
customizeContext(sc, wac);
这里initializerClasses默认size=0,可忽略
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(sc);
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
throw new ApplicationContextException(String.format(
"Could not apply context initializer [%s] since its generic parameter [%s] " +
"is not assignable from the type of application context used by this " +
"context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
wac.getClass().getName()));
}
this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
}
AnnotationAwareOrderComparator.sort(this.contextInitializers);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
initializer.initialize(wac);
}
}
servletContext.setAttribute
将实例化的root容器存到servletContext备用
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
currentContextPerThread.put();
private static final Map<ClassLoader, WebApplicationContext> currentContextPerThread =
new ConcurrentHashMap<>(1);
currentContextPerThread.put(ccl, this.context);
总结
ContextLoaderListener监听器的作用就是启动Web容器时,自动装配root容器的的配置信息。
如果web.xml中加入了ContextLoaderListener则初始化SpringIOC容器,即root ApplicationContex.
ContextLoaderListener监听器实现了ServletContextListener这个接口,在web.xml配置了这个监听器时,启动容器时,就会执行它实现的contextInitialized()方法
该方法初始化了WebApplicationContext实例(XmlWebApplicationContext),IOC容器的一系列流程,扫描包并实例化了除controller层之外的bean
并将该IOC容器放入到ServletContext中,以便后面使用。
DispachterServlet
springmvc最重要的类,DispachterServlet本质HttpServlet
类结构如下
web容器:web容器使用ServletContext来维护每一个web应用,ContextLoaderListener将spring容器,即WebApplicationContext,作为ServletContext的一个attribute,key为,保存在ServletContext中,从而web容器和spring项目可以通过ServletContext来交互。
从FrameworkServlet中获取WebApplicationContext,然后从WebApplicationContext中获取DispatcherServlet的相关功能子组件bean,然后在自身维护一个引用。实现doService方法并使用这些功能子组件来完成请求的处理和生成响应。
我们知道,一个HttpServlet会先调用init(),那么我们来看下DispatcherServlet的init()
DispatcherServlet本身没有实现init()方法,主要逻辑在其父类HttpServletBean中实现,因此我们先看HttpServletBean.init()方法
HttpServletBean.init()
@Override
public final void init() throws ServletException {
//读取springmvc配置文件的init-param标签,该标签配置了springmvc容器的配置文件路径
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
//将DispatcherServlet包装成BeanWrapper对象,并将上面的springmvc容器的配置文件路径读取到DispatcherServlet里去
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
//将上面的springmvc容器的配置文件路径读取到DispatcherServlet里去
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
//关键方法
// Let subclasses do whatever initialization they like.
initServletBean();
}
FrameworkServlet.initServletBean
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
//关键方法,创建子容器并返回
this.webApplicationContext = initWebApplicationContext();
//空方法,扩展用
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
FrameworkServlet.initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
//从ServletContext中拿到springIOC父容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
//这里就是方法返回值,返回的是子容器
WebApplicationContext wac = null;
//这里的webApplicationContext就是子容器,第一次为空
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
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
//设置父容器,就是IOC容器
cwac.setParent(rootContext);
}
//执行refresh方法
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
//使用父容器读取springmvc配置文件,并作为子容器返回
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) {
//这里是由DispatcherServlet实现了
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
//将子容器和org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher存到ServletContext备用
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
findWebApplicationContext
@Nullable
protected WebApplicationContext findWebApplicationContext() {
//这里attrName返回null
String attrName = getContextAttribute();
if (attrName == null) {
return null;
}
//获取springIOC容器
WebApplicationContext wac =
WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
}
return wac;
}
createWebApplicationContext
在这里实例化并初始化springmvc容器作为web容器
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
//获取父容器的Class对象
Class<?> contextClass = getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
//根据root容器的Class对象实例化web容器,这里web容器只是实例化,还没有refresh
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
//将root容器作为刚刚实例化的web容器的父容器
wac.setParent(parent);
//获取子容器的配置文件路径
String configLocation = getContextConfigLocation();
if (configLocation != null) {
//设置springmvc配置文件到父容器
wac.setConfigLocation(configLocation);
}
//关键代码,就是根据web容器的配置文件去初始化容器和bean
configureAndRefreshWebApplicationContext(wac);
return wac;
}
configureAndRefreshWebApplicationContext
和父容器的逻辑大体一致
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
//执行refresh方法
wac.refresh();
}
到这里加载springmvc的配置文件的子容器已经产生,并返回了,然后执行子容器的.refresh(),实例化了子容器的bean
进入wac.refresh();其中有个finishRefresh()方法
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event. 这里在web容器启动时会发布事件
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
而DispatcherServlet的父类FrameworkServlet里有个ContextRefreshListener,会监听此事件
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
具体实现如下
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}
而该onRefresh方法由DispatcherServlet实现,源码如下
DispatcherServlet.onRefresh
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
//初始化九大核心组件
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);//初始化我们web上下文对象的 用于文件上传下载的解析器对象
initLocaleResolver(context);//初始化我们web上下文对象用于处理国际化资源的
initThemeResolver(context);//主题解析器对象初始化
initHandlerMappings(context);//url请求映射,初始化我们的HandlerMapping
initHandlerAdapters(context);//初始化真正调用controloler方法的类,实例化我们的HandlerAdapters
initHandlerExceptionResolvers(context);//异常解析,实例化我们处理器异常解析器对象
initRequestToViewNameTranslator(context);
initViewResolvers(context);//视图解析,给DispatcherSerlvet的ViewResolvers处理器
initFlashMapManager(context);
}
至此,springmvc的root容器和web容器都已启动完成
总结
1.Tomcat在启动时首先调用 实现了ServletContextListener接口的ContextLoaderListener
- 在调用ContextLoaderListener过程中会实例化父容器,并根据配置查找root容器的配置
- 将root容器以及root配置文件的bean初始化,最后将root容器设置到ServletContext,以备后续调用
2.Tomcat在启动时调用实现了HttpServlet接口的DispatcherServlet对象的init方法
- 在init方法的执行过程中会根据root容器的class对象利用反射实例化一个新的容器作为web容器
- 将root容器设置为web容器的父容器
- 根据配置查找web容器的配置
- 将web容器以及web配置文件的bean初始化
- 在初始化的过程中发布事件,在监听事件过程中调用DispatcherServlet的onRefresh方法初始化springmvc的九大组件
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?