spring mvc启动配置和九大组件使用介绍
一,九大组件
1.handlerMapping
用来查找handler的,也就是处理器,对应的就是标签@RequestMapping。也就是说handler,可以是类,也可以是方法.
2.handlerAdapter
我们最原始的servlet处理方式可以知道,当一个请求到达的时候,是封装成request发送到servlet的doService(HttpServletRequest,HttpServletResponse)形式的,所以,要从传统的servlet模式转到spring mvc,这里就需要一个适配器,将对应的url,映射到对应的handler上面
3.handlerExceptionResolver
从名字上看,这个是处理handler异常的组件,此组件的作用是根据异常设置ModelAndView,之后交给渲染方法进行渲染,渲染方法将ModelAndView渲染成页面,返回给客户端。
4.ViewResolver
视图解析器,功能是将string类型的视图名解析为View类型的视图,只有一个resolveViewName()方法。View用来渲染页面,也就是将程序返回的参数和数据填入模板中。
5.RequestToViewNameTranslator
作用是从请求中获取ViewName,因为ViewResolver根据ViewName查找View,如果Handler处理完之后,没有设置View,也没有ViewName,这个时候就需要这个组件中查找ViewName。
6.LocaleResolver
ViewResolver组件的resolveViewName()方法需要两个参数,一个是ViewName,一个就是Locale,Locale的获得就是该组件需要提供的,它从请求中解析出Locale,表示一个区域。中国locale就是zh-CN。
7.ThemeResolver
解析主题,主题就是样式,图片以及他们所形成的显示效果的集合,spring mvc主题对应一个properties文件,里面存放着当前主题相关的所有资源。
8.MultipartResolver
用于处理上传请求,通过将普通的请求包装成MultipartHttpServletRequest来实现,它可以通过getFIle()直接获取文件。简单来说就是封装请求,使其有文件上传的功能
9.FlashMapManager
从名字可以知道,FlashMap的管理者,FlashMap用于重定向时的参数传递,
二,初始化阶段spring
在ssm配置的启动web.xml中可以看到如下信息:
springmvc在web.xml中的配置:
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
spring在web.xml中的启动配置:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/spring-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
所以,我们可以知道,这里的启动流程中,其实是有两个线路的,一个是IOC容器的建立,另外一个是web容器的建立,首先我们分析IOC容器是如何初始化的:
ContextLoaderListener类,内容简单,两个构造方法,一个上下文初始化,一个上下文摧毁方法:
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
//Initialize the root web application context.
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
// Close the root web application context.
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
它的继承关系比较简单,实现了ServletContextListener接口,继承了ContextLoader类,由于它实现了该接口,所以当容器启动的时候,会调用ContextLoaderListener类的contextInitialized()方法,进行一些初始化动作。该方法的调用者是StandardContext,它是tomcat体系中很重要的一个类,可以研究一下tomcat。
ContextLoaderListener主要代码功能在父类ContextLoader中,如下面的initWebApplicationContext()方法:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
try {
if (this.context == null) {
//创建根上下文 【入】
this.context = createWebApplicationContext(servletContext);
}
//如果web上下文 为可配置的web上下文
if (this.context instanceof ConfigurableWebApplicationContext) {
//转型为需要产生的IOC容器
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
//载入 根上下文的双亲上下文
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//设置标志位,把spring的web上下文,放到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);
}
return this.context;
}
}
创建容器,保存到当前类的字段:private WebApplicationContext context; ,重点是如何创建的该容器,跟踪createWebApplicationContext()方法,依然位于ContextLoader类中:
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//判断使用什么样的类作为web容器中的ioc容器,也就是查找webApplicationContext的实现类
Class<?> contextClass = determineContextClass(sc);
//根据报错信息,这个类,一定是实现了ConfigurableWeb...接口的!
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {}
//通过BeanUtils工具类进行实例化,和getBean()底层实例化手法相同
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
方法中重点内容是第一个方法determineContextClass()方法:
protected Class<?> determineContextClass(ServletContext servletContext) {
//从servletContext中(web.xml)获取 信息 :contextClass
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) { //如果web.xml中配置了信息,则直接使用,反射创建
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
else { //为空,使用默认的实现类:XmlWebApplicationContext
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
}
我们的web.xml中没有定义,所以走默认配置,默认配置是从本类的一个字段中获取,该字段存放的信息,是通过该类的static{}今天代码块初始化的:
static {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
该文件位于spring-web模块中的web.context文件夹下面:ContextLoader.properties文件中,存放的唯一一条信息:
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
所以默认实现的容器是XmlWebApplicationContext,看一下继承图:
拿到类的全路径,通过反射,实例化了本对象,回到initWebApplicationContext()方法中,获得实例化对象后,保存到了ContextLoader的context字段中,由于该对象是才初始化的,所以active=false,parent=null,所以会进入loadParentContext()方法,但是现在在spring 5.0中这个方法直接返回null.我们就跳过,
来到最后一个方法configureAndRefreshWebApplicationContext()方法:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
....
//把sc 设置到spring的web上下文中
wac.setServletContext(sc);
//获取web.xml中配置的 spring.web容器的配置文件路径
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
//调用上下文的刷新方法,开始bean的加载自动注入,初始化
wac.refresh();
}
整体流程总结:
参考链接:https://blog.csdn.net/zknxx/article/details/73060866(很详细)
https://www.imooc.com/article/19606(慕课)
二,mvc初始化
分析springmvc的启动,与类DispatcherServlet有关,看它的继承图:
httpServlet是jdk自带的,,第一层父类HttpServletBean类重写了init()方法,它既然是一个servlet,那么就要遵守servlet的生命周期,会有实例化,初始化,接收请求,处理请求,销毁等流程,这里从init()方法开始:
public final void init() throws ServletException {
// Set bean properties from init parameters. 该类在读取web.xml中配置的信息,封装到了pvs中,和依赖注入手法相同
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
//封装自己称为一个Bean,进行依赖注入,将web.xml中配置的字段信息,注入到该类中
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
// 简单的 上下文封装,该类结构简单
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw); //【空方法】
//为DispatcherServlet中的属性 赋值 比如web.xml中的配置属性,在这里赋值
bw.setPropertyValues(pvs, true); //依赖注入,一样的手法
}
}
initServletBean(); //【入】
}
第一点是pvs,属性值的获取,通过内部类的构造方法,进行读取,(配置在web.xml中的属性值)
public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties) {
Enumeration<String> paramNames = config.getInitParameterNames();
while (paramNames.hasMoreElements()) {
String property = paramNames.nextElement();
Object value = config.getInitParameter(property);
addPropertyValue(new PropertyValue(property, value)); //【添加】
}
}
准备好属性值之后,将DispatcherServlet封装成一个bean,然后使用spring DI的代码,进行依赖注入,将属性值注入到该bean中。
之后重点是iniServletBean()方法,该类是委派模式,在第二层FrameworkServlet类中:
protected final void initServletBean() throws ServletException {
this.webApplicationContext = initWebApplicationContext(); //初始化Ioc容器,最终调用refresh()方法
initFrameworkServlet(); //空方法
}
初始化web容器:
protected WebApplicationContext initWebApplicationContext() {
//通过工具类,获取ContextLoaderListener中创建的webApplicationContext容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//如果容器 已经存在,则直接使用已经存在的
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) { //激活
if (cwac.getParent() == null) {
cwac.setParent(rootContext); //将listener中创建的容器,设置为父容器
}
configureAndRefreshWebApplicationContext(cwac); //内部就是调用refresh()方法
}
}
}
if (wac == null) {
wac = findWebApplicationContext(); //则从servletContext中查找配置的webApplicationContext
}
if (wac == null) {
wac = createWebApplicationContext(rootContext); //没有就直接创建 默认的同样是xmlWeb..
}
//======= 以上都是在创建 wac ===================
if (!this.refreshEventReceived) { //如果还没有调用refresh()方法,则调用onRefresh()方法
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
这里的容器创建,和listener的模式一样,同样是先去查找有没有指定的实现类,如果没有,就直接create一个默认的容器实现类:
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
Class<?> contextClass = getContextClass(); //默认的XmlWebApplicationContext 类
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//属性设置
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
//设置,然后调用refresh()方法
configureAndRefreshWebApplicationContext(wac);
return wac;
}
最后调用onRefresh()方法,在子类DispatcherServlet类中,开启了初始化
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
//1.多文件上传的组件
initMultipartResolver(context);
//2.初始化本地语言环境
initLocaleResolver(context);
//3.初始化 模板处理器
initThemeResolver(context);
//4.初始化handlerMapping url和controller的关系建立
initHandlerMappings(context);
//5.初始化 参数适配器
initHandlerAdapters(context);
//6.初始化 异常拦截器
initHandlerExceptionResolvers(context);
//7.初始化 视图预处理器
initRequestToViewNameTranslator(context);
//8.初始化 视图转换器
initViewResolvers(context);
//9.初始化 flashMap管理器
initFlashMapManager(context);
}
参考链接:https://blog.csdn.net/zknxx/article/details/73073468
本文地址:https://blog.csdn.net/W1427259949/article/details/107891865