SpringMVC-整体架构

一、SpringMVC的整体架构

SpringMVC中核心Servlet的继承结构如下图所示:
这里写图片描述
从上图可以看出,SpringMVC与Web容器的衔接由HttpSevletBean完成。主要涉及五个核心类:GenericServlet属于java,HttpServletBean、FrameworkServlet、DispatcherServlet属于SpringMVC。

其中Environment用于存放ServletContext、ServletCofnig、JndiProperty、系统环境变量和系统属性。

二、HttpServletBean

HttpServletBean直接继承自HttpServlet,实现了与Servlet容器的衔接。同时,HttpServletBean重写了HttpServlet的init方法,对SpringMVC进行第一步的初始化工作。

/**
     * Map config parameters onto bean properties of this servlet, and
     * invoke subclass initialization.
     * @throws ServletException if bean properties are invalid (or required
     * properties are missing), or if subclass initialization fails.
     */
    @Override
    public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }

        // Set bean properties from init parameters.
        try {
        //将ServletConfig中的参数封装到PropertiValues中
            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
            //创建BeanWraper对象
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            //模板方法
            initBeanWrapper(bw);
            //将BeanWrapper设置到DispatcherServlet(木有看懂)
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            throw ex;
        }

        // Let subclasses do whatever initialization they like.
        //模板方法,链接FrameworkServlet的入口
        initServletBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }

以上就是HttpServletBean在SpringMVC在初始化的过程中所做的,首先将Servlet配置的参数设置到DispatcherServlet,然后调用模板方法initServletBean();进入下一步的初始化。

其中,BeanWrapper是Spring提供了一个用来操作JavaBean属性的工具类,用法类似于下面所示:

public class BeanWrapperTest {

    public static void main(String[] args) {
        Person person = new Person();
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(person);
        bw.setPropertyValue("name", "张三");
        System.out.println(person.getName());
        bw.setPropertyValue("name", "李四");
        System.out.println(person.getName());
    }




    private static class Person{
        private String name;
        private int age;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }

    }
}

二、FrameworkServlet

HttpServletBean在init()方法中调用了模板方法initServletBean(),FrmeworkServlet覆盖了HttpServletBean的initServletBean()的实现,从此处开始,正式进入Spring环境的初始化

/**
     * Overridden method of {@link HttpServletBean}, invoked after any bean properties
     * have been set. Creates this servlet's WebApplicationContext.
     */
    @Override
    protected final void initServletBean() throws ServletException {
        getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
        }
        long startTime = System.currentTimeMillis();

        try {
        //初始化WebApplicationContext
            this.webApplicationContext = initWebApplicationContext();
            //模板方法,衔接DispatcherServlet
            initFrameworkServlet();
        }
        catch (ServletException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }
        catch (RuntimeException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }

        if (this.logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                    elapsedTime + " ms");
        }
    }

在这个方法中的核心代码有两句:

//初始化WebApplicationContext
this.webApplicationContext = initWebApplicationContext();
//模板方法,衔接DispatcherServlet
initFrameworkServlet();
/**
     * This method will be invoked after any bean properties have been set and
     * the WebApplicationContext has been loaded. The default implementation is empty;
     * subclasses may override this method to perform any initialization they require.
     * @throws ServletException in case of an initialization exception
     */
    protected void initFrameworkServlet() throws ServletException {
    }

由于initFrameworkServlet()是模板方法,所以,FrameworkServlet的主要作用是初始化WebApplicationContext

看一下initWebApplicationContext()的具体逻辑:

/**
     * Initialize and publish the WebApplicationContext for this servlet.
     * <p>Delegates to {@link #createWebApplicationContext} for actual creation
     * of the context. Can be overridden in subclasses.
     * @return the WebApplicationContext instance
     * @see #FrameworkServlet(WebApplicationContext)
     * @see #setContextClass
     * @see #setContextConfigLocation
     */
    protected WebApplicationContext initWebApplicationContext() {
    //根据固定的key从ServletContext中获取rootContext,通常是整个应用的Spring容器
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
//如果已经通过构造方法设置了WebApplication
        if (this.webApplicationContext != null) {
            // A context instance was injected at construction time -> use it
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    // The context has not yet been refreshed -> provide services such as
                    // setting the parent context, setting the application context id, etc
                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent -> set
                        // the root application context (if any; may be null) as the parent
//将rootContext设到WebApplicationContext中                      cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        //如果没有设置WebApplicationContext
        if (wac == null) {
            // No context instance was injected at construction time -> see if one
            // has been registered in the servlet context. If one exists, it is assumed
            // that the parent context (if any) has already been set and that the
            // user has performed any initialization such as setting the context id
            //从ServletContext中查找WebApplication,查找时使用的key是通过在DispatcherServlet
            中配置contextAttribute参数指定的
            wac = findWebApplicationContext();
        }
        //如果在ServletContext中没有WebApplicationContext实例
        if (wac == null) {
            // No context instance is defined for this servlet -> create a local one
            //新创建一个WebApplicationContext实例
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            // Either the context is not a ConfigurableApplicationContext with refresh
            // support or the context injected at construction time had already been
            // refreshed -> trigger initial onRefresh manually here.
            onRefresh(wac);
        }

        if (this.publishContext) {
            // Publish the context as a servlet context attribute.
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                        "' as ServletContext attribute with name [" + attrName + "]");
            }
        }

        return wac;
    }

通常情况下,这一步都会是创建一个WebApplicationContext,创建过程主要分为两步,看一下具体的创建逻辑:

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        Class<?> contextClass = getContextClass();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet with name '" + getServletName() +
                    "' will try to create custom WebApplicationContext context of class '" +
                    contextClass.getName() + "'" + ", using parent context [" + parent + "]");
        }
        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");
        }
        //默认创建XmlWebApplicationContext
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//调用HttpServletBean的getEnvironment并设置到WebApplicationContext
        wac.setEnvironment(getEnvironment());
        //设置rootContext
        wac.setParent(parent);
//将设置的contextConfigLocation参数传入wac,默认传入WEB-INF/[Servlet-Name]-Servlet.xml       wac.setConfigLocation(getContextConfigLocation());

//进一步设置并调用refresh()     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());
            }
        }
        //设置ServletContext到WebApplicationContext
        wac.setServletContext(getServletContext());
        //将ServletConfig设置到WebApplicationContext
        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();
    }

综上所述,initWebApplicationContext主要做了三件事:
1.获取Spring跟容器rootContext
2.设置WebApplicationContext并根据情况调用onRefresh方法
3.将webApplicationContext设置到Servlet

三、DispatcherServlet

在FrameworkServlet的initWebApplicationContext()中,完成对WebApplicationContext的初始化后,会调用模板方法onRefresh(),这个方法是FrameworkServlet和DispatcherServlet进行衔接的桥梁,在DispatcherServlet中重写了onRefresh()方法,并对DispatcherServlet进行了初始化。

/**
     * This implementation calls {@link #initStrategies}.
     */
    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    /**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }

从上面的代码可知, DispatcherServlet在SpringMVC初始化过程中的主要作用是初始化八种组件。

总结:
SpringMVC中Servlet的继承结构
Servlet
|-GenericServlet
|-HttpServlet
|-HttpServletBean
|-FrameworkServlet
|-DispatcherServlet

作用:
HttpServletBean:获取DispatcherServlet配置参数
FrameworkServlet:初始化WebApplicationContext
DispatcherServlet:初始化SpringMVC九大组件

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值