一、SpringMVC整体结构
下面是SpirngMVC核心Servlet的继承结构图
Servlet的继承结构一共有五个类,GenericServelt,HttpServlet,这两个类的介绍:传送门,剩下三个类HttpServletBean,FrameworkServlet,和DispatcherServlet是SpringMVC框架的类。
EnviornmentCapable介绍
这个接口顾名思义,具有Enviornment能力。当Spring需要Enviornment,实现getEnvironment方法就可以获得Enviornment。
Aware的介绍
Aware这个接口里面没有内容,XXXAware在Spring里标识对XXX可以感知。容器管理的Bean一般不需要了解容器的状态和直接使用容器,但是在某些情况下,是需要在Bean中直接对IOC容器进行操作的,这时候,就需要在Bean中设定对容器的感知。Spring IOC容器也提供了该功能,它是通过特定的Aware接口来完成的。
通俗的解释就是:如果在某个类里面想要使用Spring的内容,就可以实现XXXAware接口告诉Spring,Spring看到后就给你送过来,而接受的方式是通过实现唯一的方法setXXX。当然实现XXXAware接口的类需要交给Spring管理。
看下面代码
@Component
public class AppTest implements ApplicationContextAware, EnvironmentAware {
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//实现了这个方法Spring会把applicationContext对象传进来
System.out.println(applicationContext);
}
public void setEnvironment(Environment environment) {
//实现了这个方法Spring会把Environment对象传进来
System.out.println(environment);
}
}
environment对象封装了ServletContext、还封装了ServletConfig、JndiProperty、系统环境变量、系统属性。这些都封装到了propertySources属性里面。
二、SpringMVC实现流程的涉及的类
1、HttpServletBean类
HttpSerlvetBean继承自HttpServlet,重写了无参的init方法。
Servlet创建时候会先调用有参的init()方法,在有参的init方法内调用了无参的init方法。
public final void init() throws ServletException {
//日志省略不写
// ServletConfigPropertyValues是HttpServletBean内部静态类
//构造过程中会使用ServletConfig对象找出web.xml配置文件中的配置参数并且设置到ServletConfigPropertyValues内部
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
//使用BeanWrapper构造DispatherServlet
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
//使用属性编辑器
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
//模板方法,可以在子类调用,做一些初始化工作,bw代表DispatcherServlet,springMVC未做任何操作。
initBeanWrapper(bw);
//将配置的初始化值(如contextConfigLocation)设置到DispatherServlet
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
//日志省略不写
throw ex;
}
}
//FrameworkServelt的初始化入口
initServletBean();
//日志省略不写
}
2、FrameworkServlet类
initServletBean();
从HttpServletBean中可知,FrameworkServlet的初始化入口是initServletBean();
@Override
protected final void initServletBean() throws ServletException {
//省略无关代码及日志
//初始化WebApplicationContext属性
//WbeApplicationContext是继承自ApplicationContext接口的接口
//该属性也是Spring容器上下文,ServletFramework类的作用是让Servlet和Spring容器关联
this.webApplicationContext = initWebApplicationContext();
//该方法主要是为了让子类覆盖并做一些初始化工作
//DisPatcherServlet并未覆盖改方法
initFrameworkServlet();
//省略无关代码及日志
}
initWebApplicationContext();
protected WebApplicationContext initWebApplicationContext() {
//从servletContext中获取根上下文
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//DispatherServlet类中有一个以webApplicationContext的构造器
if (this.webApplicationContext != null) {
//如果已经通过构造方法设置了webApplicationContext,那将使当前这个。
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
//如果上下文尚未刷新,那么将设置父上下文
if (!cwac.isActive()) {
//这个webApplicationContext没有父上下文
if (cwac.getParent() == null) {
//设置父上下文,可以为空
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
//当webApplicationContext已经存在ServletContext中,这种方式创建WebApplicationContext那么会调用onRefresh方法
//可以通过在Servlet的init-param中配置
//<param-name>contextAttribute</param-name>
//<param-value>值</param-value>
//一般不会设置contextAttribute的值
//所以会返回为null
wac = findWebApplicationContext();
}
//如果webApplicationContext还没有创建,则创建一个
if (wac == null) {
//创建webApplication并设置根上下文为父上下文
//然后配置ServletConfig,serveltContext实例到这个上下文
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
onRefresh(wac);
}
if (this.publishContext) {
//将新创建的上下文保存到ServletContext中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
//省略日志
}
return wac;
}
createWebApplicationContext()
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
//这个contextClass是XmlWebApplicationContext.class
Class<?> contextClass = getContextClass();
//判断ConfigurableWebApplicationContext.class是不是contextClass的父类或者接口
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
//抛出异常
}
//实例化ConfigurableWebApplicationContext对象
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//设置Environment
wac.setEnvironment(getEnvironment());
//设置父上下文
wac.setParent(parent);
//设置SpringMVC配置文件,如果没有设置,默认传入WEB-INFO/[ServletName]-Servlet.xml
wac.setConfigLocation(getContextConfigLocation());
//配置和刷新WebApplicationContext
configureAndRefreshWebApplicationContext(wac);
return wac;
}
configureAndRefreshWebApplicationContext()
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac){
if(ObjectUtils.identityToString(wac).equals(wac.getId())){
if(this.contextId!=null){
wac.setId(this.contextId);
}
else{
//根据可用信息分配更有用的ID
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX+
ObjectUtils.getDisplayString(getServletContext().getContextPath())+'/'+getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
//添加监听ContextRefreshListener的监听器,ContextRefreshListener是FrameworkServlet的内部类
wac.addApplicationListener(new SourceFilteringListener(wac,new ContextRefreshListener()));
ConfigurableEnvironment env=wac.getEnvironment();
if(env instanceof ConfigurableWebEnvironment){
((ConfigurableWebEnvironment)env).initPropertySources(getServletContext(),getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
//初始化springMVC各种的相关配置,各种注入的Controller,配置文件等
wac.refresh();
}
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;
//调用子类的onRefresh
onRefresh(event.getApplicationContext());
}
initWebApplicationContext方法主要做了三件事:
- 获取Spring的根容器rootContext
- 设置webApplicationContext并根据情况调用onRefresh方法
- 将webApplicationContext设置到ServletContext中。
FrameworkServlet在构建的过程中主要作用就是初始化webApplicationContext属性
如果在项目中有Spring框架,也让Servlet和Spring容器做关联。
DispatcherServelt类
onRefresh方式是DispatcherServlet的入口方法。onRefresh中简单地调用了initStrategies,在initStrategies中调用了9个初始化组件方法。
@Override
protected void onRefresh(ApplicationContext context) {
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);
}
初始化分两步
private void initLocaleResolver(ApplicationContext context) {
try { //在context中获取
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
//日志
}
catch (NoSuchBeanDefinitionException ex) {
// 使用默认组件
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
//日志
}
}
1、首先通过context.getBean()在容器里面按注册时的名称或类型进行查找,只需在SpringMVC的配置文件中,配置相应类型的组件,容器就可以自动查找。
2、如果查找不到就调用getDefaultStrategy按照类型获取默认组件。
三、总结
这里主要分析了SpringMVC自身创建的过程,SpringMVC中Servlet一共有三个层次,分别是HttpServletBean,FrameworkServlet和DispatchServlet。
HttpServletBean直接继承Java的HttpServlet,作用是将Servlet中配置的参数设置到相应的属性。
FrameworkServlet作用初始化webApplicationContext。
DispatchServlet作用:初始化自身的9个组件。