• 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);
    }
  • 相关阅读:
    微信小程序实战练习(仿五洲到家微信版)
    vue2.0项目 calendar.js(日历组件封装)
    基于thinkphp的后台管理系统模板快速搭建
    你不知道的javascript(上卷)读后感(二)
    你不知道的javascript(上卷)读后感(一)
    教你10分钟搭建酷炫的个人博客
    Webpack学习-工作原理(下)
    Webpack学习-工作原理(上)
    Css Secret 案例Demo全套
    基于excel导入数据到ms sql server
  • 原文地址:https://www.cnblogs.com/deityjian/p/11494345.html
Copyright © 2020-2023  润新知