03.SpringMVC之器
整体结构介绍
在Servlet的继承结构中一共有5个类,GenericServlet和HttpServlet在java中剩下的三个类HttpServletBean、FrameworkServlet和DispatcherServlet是SpringMVC中的这三个类直接实现三个接口:EnvironmentCapable、EnvironmentAware和ApplicationContextAware。
XXXAware在spring里表示对XXX可以感知,通俗点解释就是:如果在某个类里面想要使用spring的一些东西,就可以通过实现XXXAware接口告诉spring,spring看到后就会给你送过来,而接收的方式是通过实现接口唯一的方法setXXX,比如,有一个类想要使用当前的ApplicationContext,那么我们只需要让它实现ApplicationContextAware接口,然后实现接口中唯一的方法,void setApplicationContext(ApplicationContext applicationContext(会自动传过来,直接用))就可以了,spring会自动调用这个方法将applicationContext传给我们,我们只需要接收就可以了。
EnvironmentCapable,顾名思义,当然就是具有Environment的能力,也就是可以提供Environment,所以EnvironmentCapable唯一的方法是Environment getEnvironment(),用于实现EnvironmentCapable接口的类,就是告诉spring它可以提供Environment,当spring需要Environment的时候就会调用其getEnvironment方法跟它要
HttpServletBean
在HttpServletBean的init方法中,首先将Servlet中配置的参数使用BeanWrapper设置到DispatcherServlet的相关属性,然后调用模板方法initServletBean,子类就通过这个方法初始化
public final void init() throws ServletException { // 1. 操作配置文件里的属性 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(),this.requiredProperties); if (!pvs.isEmpty()) { try { // 2.获取目标对象的beanwrapper对象 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) { throw ex; } } // 空方法 让子类实现 initServletBean(); }
BeanWrapper是Spring 提供的一个用来操作JavaBean属性的工具,使用它可以直接修改一个对象的属性
public class User{ String userName; //省略get和set方法 } public class BeanWrapperTest{ psvm{ User user = new User(); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(user); bw.setPropertyValue("userName","张三"); sout(user.getUserName()); PropertyValue value = new PropertyValue("userName","李四"); sout(user.getUserName()); } }
FrameworkServlet
FrameworkServlet继承HttpServletBean,FrameworkServlet的初始化入口方法是initServletBean
// org.springframework.web.servlet.FrameworkServlet protected final void initServletBean() throws ServletException{ //初始化WebApplicationContext this.webApplicationContext = initWebApplicationContext(); //模板方法,子类可以覆盖在里面做一些初始化的工作 initFrameworkServlet(); } protected WebApplicationContext initWebApplicationContext(){ //获取rootContext WebApplicationContext rootContext = WebApplicationContextUtils.getApplictionContext(getServletContext()); WebApplicationContext wac = null; //如果已经通过构造方法设置了webApplicationContext 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); } configureAndRefreshWebApplicationContext(cwac) } } } if(wac == null){ //当webApplicationContext已经存在ServletContext中时,通过配置在Servlet中的contextAttribute参数获取 wac = findWebApplicationContext(); } if(wac == null)[ //如果webApplicationContext还没有创建,则创建一个 wac = createWebApplicationContext(rootContext); } if(!this.refreshEventReceived){ //当ContextRefreshedEvent事件没有触发时调用此方法,模板方法,可以在子类重写 onRefresh(wac); } if(this.publicContext){ //将ApplicationContext保存到ServletContext中 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName,wac); } return wac; }
这个initWebApplicationContext方法做了三件事
1.获取spring的根容器rootContext
获取根容器的原理是,默认情况下spring会将自己的容器设置成ServletContext属性,默认根容器的key为org.springframework.web.context.WebApplicationContext.ROOT,所以获取根容器只需要调用ServletContext的getAttribute就可以了
2.设置webApplicationContext并根据情况调用onRefresh方法
3.将webApplicationContext设置到ServletContext中
这里在讲讲上面代码中的 wac == null 的几种情况:
1)、当 WebApplicationContext 已经存在 ServletContext 中时,通过配置在 servlet 中的 ContextAttribute 参数获取,调用的是 findWebApplicationContext() 方法
protected WebApplicationContext findWebApplicationContext() { String attrName = getContextAttribute(); if (attrName == null) { return null; } WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); } return wac; }
2)、如果 WebApplicationContext 还没有创建,调用的是 createWebApplicationContext 方法
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); //并设置的 contextConfigLocation 参数传给 wac,默认是 WEB-INFO/[ServletName]-Servlet.xml wac.setConfigLocation(getContextConfigLocation()); //调用的是下面的方法 configureAndRefreshWebApplicationContext(wac); return wac; } 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(); }
DispatcherServlet
OnRefresh方法是DispathcerServlet的入口方法,OnRefresh中简单地调用了initStrategis,在initStrategies中调用了9个初始化方法
//org.springframework.web.servlet.DispatcherServlet protected void onRefresh (ApplicationContext context){ initStrategies(context); } protected void initStrategies(Application context){ initMultipartResolver(context); initLocalResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }