SpringMVC工作原理流程(一)

一、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方法主要做了三件事:

  1. 获取Spring的根容器rootContext
  2. 设置webApplicationContext并根据情况调用onRefresh方法
  3. 将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个组件。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券