概述
SpringMvc最核心的类就是前端控制器DispatchServlet,作为一个Servlet,是整个SpringMvc的入口,用于调度其他的各组件工作,如Controller、HandlerMapping、ViewResolver等,控制着整个处理用户请求的流程,本篇首先来总结一下DispatchServlet的初始化过程,及进行具体处理请求前的预准备
作为一个Servlet的主要继承关系:HttpServletBean -- > FrameworkServlet -- > DispatchServlet,分别分析这三个类,各自所做的工作,就基本上总结了整个DispatchServlet的初始过程
1、HttpServletBean
重写了GenericServlet的init方法,servelt实例化时init方法会被调用:
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
//将Servlet的初始化参数(initParam)保存在PropertyValues,并检查Servlet的初始化参数包含了所必须的参数,子类可以将在this.requiredProperties指定必须的参数
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
//BeanWrapper就是对象的包裹者,主要用于对象的属性设置,这里的this,就是DispatchServlet
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
//ServletContext的资源加载器,使用本质上是使用ServletContext对象来获取与当前Servlet相关的资源
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
//自定义了属性编辑器,用于编辑被包裹对象的属性
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
//空方法留给子类自定义初始化BeanWrapper
initBeanWrapper(bw);
//将Servlet的初始化参数设置到BeanWrapper中
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
//在FrameworkServlet中实现
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
总结:主要使用ServletConfig配置信息初始化DispatchServlet的一些属性,如使用web.xml时配置的contextConfigLocation
2、FrameworkServlet
1)重写initServletBean()初始化IOC容器
调用链:HttpServletBean.init()-->FrameworkServlet.initServletBean()
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
//初始化ioc容器(设置父子容器并刷新)
this.webApplicationContext = initWebApplicationContext();
//空方法,留给子类扩展
initFrameworkServlet();
}
...
}
核心方法initWebApplicationContext():
protected WebApplicationContext initWebApplicationContext() {
//根据ServletContext.getAttribute来获取到WebApplicationContext(根容器),而获取到的根容器是之前由ContextLoaderListener(ServletContextListener)的contextInitialized方法将根容器保存在了ServletContext
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//this.webApplicationContext为子容器(保存SpringMVC组件),如果我们使用的是配置类的方式即继承AbstractAnnotationConfigDispatcherServletInitializer来指定创建父子容器,那么在Servlet容器启动的时侯webApplicationContext就被创建了
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
//将根容器设置为子容器(保存SpringMVC组件)的父容器
cwac.setParent(rootContext);
}
//配置并且刷新容器(启动初始化容器过程),之前的父子容器只是被创建没有调用refresh
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
//如果子容器还没又被创建,尝试去ServletContext中以获取
wac = findWebApplicationContext();
}
if (wac == null) {
//如果子容器还为空,就通过web.xml配置的参数contextConfigLocation指定的Xml配置文件路径来创建一个XmlWebApplicationContext类型的ioc子容器,设置父子容器关系,并刷新。
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
//在DispatchServlet中实现
onRefresh(wac);
}
//this.publishContext指定是否将web容器发布在ServletContext中,默认为ture
if (this.publishContext) {
String attrName = getServletContextAttributeName();
//将初始化好的ioc容器放入ServletContext中
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
FrameworkServlet的initServletBean方法主要就是初始化IOC容器,包括一个子容器(保存springmvc组件,如Controller、ViewResolver、HandlerMapping等等)和一个父容器(保存业务逻辑组件,如service,dao),
2)重写了HttpServelt的service方法
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
//处理PATCH请求
processRequest(request, response);
}
else {
//如果不是PATCH请求,调用HttpServelt.service()
super.service(request, response);
}
}
FrameworkServlet也重写了doGet、doPost、xxx等对应处理各类型请求的方法,最终都是调用了processRequest(request, response)来处理:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//获取之前LocaleContext(主要作用是封装请求的 Locale 信息,主要就是语言信息)默认不存在
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//创建本次请求的localeContext
LocaleContext localeContext = buildLocaleContext(request);
//获取之前RequestAttributes(封装request,response)默认不存在
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//创建本次请求的requestAttributes
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
//与异步请求相关的处理
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//将localeContext、requestAttributes分别保存在LocaleContextHolder、RequestContextHolder中,两者都是使用ThreadLocal与当前线程绑定
initContextHolders(request, localeContext, requestAttributes);
try {
//真正的处理请求过程在DispatchServlet中实现
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
//完成请求默认移除requestAttributes和localeContext
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
//无论请求是否成功都会发布请求处理完成事件(我们可以向容器中添加相应的事件监听器)
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
总结:1、在Servlet初始化阶段,初始化了IOC容器
2、在处理请求阶段,做了一些提前的准备工作
3、DispatcherServlet
1)重写onRefresh,初始化SpringMvc工作组件
HttpServletBean.init()-->FrameworkServlet.initServletBean()-->FrameworkServlet.initWebApplicationContext()-->DispatcherServlet.onRefresh(ApplicationContext context)
@Override
protected void onRefresh(ApplicationContext context) {
//context就是在FrameworkServlet.initWebApplicationContext()中完成初始化工作的IOC容器
initStrategies(context);
}
//完成各组件的初始化
protected void initStrategies(ApplicationContext context) {
//初始化文件上传的处理类
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
这里我们以处理器映射器的初始化initHandlerMappings(context)为例分析,其他组件的初始化处理也类似
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
//this.detectAllHandlerMappings默认为true
if (this.detectAllHandlerMappings) {
//在容器中找到所有的HandlerMapping
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
//将所有的HandlerMapping保存在handlerMappings中
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// 排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
//如果在IOC容器中没有找到任何的HandlerMapping,获取默认的HandlerMapping
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
getDefaultStrategies(context, HandlerMapping.class)获取默认的组件:
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
//这的key为org.springframework.web.servlet.HandlerMapping
String key = strategyInterface.getName();
//在defaultStrategies获取我们需要创建的组件的类型,多个的话,使用逗号隔开
String value = defaultStrategies.getProperty(key);
if (value != null) {
//使用逗号为分隔符,转化成数组
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<T>(classNames.length);
//遍历classNames利用反射创建对象
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
//创建并添加到IOC容器中
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Error loading DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]: problem with class file or dependent class", err);
}
}
return strategies;
}
else {
return new LinkedList<T>();
}
}
//保存了各组件接口对应的默认实现类
private static final Properties defaultStrategies;
//静态代码块,用于初始化defaultStrategies
static {
try {
//private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties",创建了一个与DispatcherServlet.class处于同一包下的DispatcherServlet.properties文件的资源对象
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
//创建Properties对象,保存了组件接口的对应的默认实现类
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
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.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,
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
各个SpringMvc工作组件的初始化过程为:首先从IOC容器中找对应接口类型的组件,如果没到,就创建一个在DispatcherServlet.properties中指定的默认组件接口实现类的实例
2)重写doService,进入核心方法doDispatch(request, response)
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
//这里与RequestDispatcher.include()相关
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
//将一些组件设置到request中
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
//最终调用了
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
最终进入doDispatch(request, response),作为DispatchServlet最核心方法,调度着各工作组件进行具体的请求处理。
转载于:https://www.cnblogs.com/qzlcl/p/11141020.html
最后
以上就是还单身雪碧为你收集整理的SpringMVC之DispatchServlet初始化过程的全部内容,希望文章能够帮你解决SpringMVC之DispatchServlet初始化过程所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复