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);
}

posted on 2019-09-09 21:23  情陌人灬已不在  阅读(211)  评论(0编辑  收藏  举报

导航