SpringMVC01DispatchServlet的初始化过程

编程

DispatcherServlet继承了HttpServletBean类,其中含有初始化方法init(),该方法的代码如下:

/**

* Map config parameters onto bean properties of this servlet, and

* invoke subclass initialization.

* @throws ServletException if bean properties are invalid (or required

* properties are missing), or if subclass initialization fails.

*/

@Override

public final void init() throws ServletException {

// Set bean properties from init parameters.

PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);

if (!pvs.isEmpty()) {

try {

BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);

ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());

bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));

initBeanWrapper(bw);

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();

}

该方法是获取web.xml中配置的属性参数的,并将这些属性设置到DispatcherServlet中;init()方法中还包含一个模板方法initServletBean(),该方法需要其子类去实现。

从上图中可以看到,HttpServletBean类的子类是FrameworkServlet,这里实现了模板方法initServletBean(),代码如下:

/**

* Overridden method of {@link HttpServletBean}, invoked after any bean properties

* have been set. Creates this servlet"s WebApplicationContext.

*/

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

}

// 省略其他代码

}

可以看到initServletBean()方法的核心是初始化WebApplicationContext对象,即是initWebApplicationContext()方法。

FrameworkServlet.initWebApplicationContext()代码如下:

/**

* Initialize and publish the WebApplicationContext for this servlet.

* <p>Delegates to {@link #createWebApplicationContext} for actual creation

* of the context. Can be overridden in subclasses.

* @return the WebApplicationContext instance

* @see #FrameworkServlet(WebApplicationContext)

* @see #setContextClass

* @see #setContextConfigLocation

*/

protected WebApplicationContext initWebApplicationContext() {

WebApplicationContext rootContext =

WebApplicationContextUtils.getWebApplicationContext(getServletContext());

WebApplicationContext wac = null;

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

cwac.setParent(rootContext);

}

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;

}

这个方法比较长,这里将分析最重要的createWebApplicationContext()方法。createWebApplicationContext()方法是创建WebApplicationContext对象的,进入该方法,发现其调用了FrameworkServlet的重载方法createWebApplicationContext(ApplicationContext parent),重载方法的代码如下:

/**

* Instantiate the WebApplicationContext for this servlet, either a default

* {@link org.springframework.web.context.support.XmlWebApplicationContext}

* or a {@link #setContextClass custom context class}, if set.

* <p>This implementation expects custom contexts to implement the

* {@link org.springframework.web.context.ConfigurableWebApplicationContext}

* interface. Can be overridden in subclasses.

* <p>Do not forget to register this servlet instance as application listener on the

* created context (for triggering its {@link #onRefresh callback}, and to call

* {@link org.springframework.context.ConfigurableApplicationContext#refresh()}

* before returning the context instance.

* @param parent the parent ApplicationContext to use, or {@code null} if none

* @return the WebApplicationContext for this servlet

* @see org.springframework.web.context.support.XmlWebApplicationContext

*/

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {

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");

}

ConfigurableWebApplicationContext wac =

(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

wac.setEnvironment(getEnvironment());

wac.setParent(parent);

String configLocation = getContextConfigLocation();

if (configLocation != null) {

wac.setConfigLocation(configLocation);

}

configureAndRefreshWebApplicationContext(wac);

return wac;

}

在该方法中使用BeanUtils类的instantiateClass()方法通过反射创建了web上下文对象,即ConfigurableWebApplicationContext对象,并且将web.xml中配置的contextConfigLocation元素设置到ConfigurableWebApplicationContext对象中。最后执行刷新web应用上下文的方法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);

wac.refresh();

}

该方法的最后一行ConfigurableWebApplicationContext.refresh()方法会进入到IoC容器的初始化过程,具体细节请参考IoC容器的代码解析,此处不再赘述。

 

回到FrameworkServlet的initWebApplicationContext()方法,当web应用上下文对象创建完后,将会继续执行onRefresh()方法。

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

}

}

onRefresh()方法由DispatcherServlet类实现:

/**

* This implementation calls {@link #initStrategies}.

*/

@Override

protected void onRefresh(ApplicationContext context) {

initStrategies(context);

}

onRefresh()方法中只有一行代码,调用initStrategies()方法:

/**

* Initialize the strategy objects that this servlet uses.

* <p>May be overridden in subclasses in order to initialize further strategy objects.

*/

protected void initStrategies(ApplicationContext context) {

// 初始化文件上传处理器

initMultipartResolver(context);

// 初始化本地化处理器

initLocaleResolver(context);

// 初始化主题处理器

initThemeResolver(context);

// 初始化处理器映射(用来保存Controller中配置的RequestMapping与Method映射关系)

initHandlerMappings(context);

// 初始化处理器适配器(用来动态匹配Method参数,包括类转换、动态映射)

initHandlerAdapters(context);

// 初始化异常处理器

initHandlerExceptionResolvers(context);

// 初始化请求至视图名转换

initRequestToViewNameTranslator(context);

// 初始化视图解析器

initViewResolvers(context);

// 初始化 flash 映射管理器

initFlashMapManager(context);

}

初始化策略的方法initStrategies()在不指定个性化配置文件的情况下,会使用默认的配置进行初始化,默认配置位于DispatcherServlet.properties中:

# Default implementation classes for DispatcherServlet"s strategy interfaces.

# Used as fallback when no matching beans are found in the DispatcherServlet context.

# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,

org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,

org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,

org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

从DispatcherServlet.properties默认配置策略中可以看到,其中对每种处理器配置了默认的实现,即本地化处理器LocaleResolver默认使用的是AcceptHeaderLocaleResolver。初始化各种处理器的功能已经通过注释的形式initStrategies()方法中,可进入方法内看具体实现。

以上是 SpringMVC01DispatchServlet的初始化过程 的全部内容, 来源链接: utcz.com/z/513240.html

回到顶部