zoukankan      html  css  js  c++  java
  • SpringMVC源代码学习(一)从HTttpServletBean到DispatcherServlet

    以下内容基于书:《看透SpringMVC-源代码分析与实践》基本照搬。。。用于自己查阅备忘。

    观察DispatcherServlet继承树 
    DispatcherServlet继承树 
    Java web的应该都知道GenericServlet到HttpServlet的关系,它们都是 javax.servlet;包下的。 
    从HttpServletBean开始就是由spring定义的了。

    我们知道HttpServlet有一个无参的init()方法交由子类实现,用于类的初始化,那我们从HTttpServletBean中的init()方法开始分析。

    @Override
    public final void init() throws ServletException {
        //log ...
        // Set bean properties from init parameters.
        try {
            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
            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 Exception ...
    
        // Let subclasses do whatever initialization they like.
        initServletBean();
    
        //log ...
    }
    

    首先将Servlet中配置的参数使用BeanWrapper设置到DispatcherServlet的相关属性,然后调用模版方法initServletBean,

    子类,也就是FrameworkServlet通过定义自己的initServletBean定义了自己的初始化内容。

    FrameworkServlet的initServletBean

    @Override
    protected final void initServletBean() throws ServletException {
        //log ...
        long startTime = System.currentTimeMillis();
    
        try {
            this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();
        }
        //catch ...
        //log ...
    }
    

    过滤掉日志与catch代码,主要代码有两句,分别说一下:

    1、FrameworkServlet的initFrameworkServlet方法。 
    类似于HTttpServletBean的initServletBean,这个方法没有内容,是供给子类调用的模版方法。

    2、initWebApplicationContext方法,详细代码如下:

    protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
    
        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
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        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
            wac = findWebApplicationContext();
        }
        if (wac == null) {
            // No context instance is defined for this servlet -> create a local one
            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;
    }
    

    其中有三步,1、第一步,判断是不是在构造方法中传递webApplicationContext参数,ServletContext.addServlet方式注册Servlet 
    2、第二步,判断是不是通过web.xml配置文件传入webApplicationContext参数 
    3、第三步,在前面两部探索后都没有找到webApplicationContext的情况下自己创建一个。 
    这个过程由createWebApplicationContext方法来做,它内部又调用了configureAndRefreshWebApplicationContext方法完成,createWebApplicationContext代码如下:

    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");
        }
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    
        wac.setEnvironment(getEnvironment());
        wac.setParent(parent);
        wac.setConfigLocation(getContextConfigLocation());
    
        configureAndRefreshWebApplicationContext(wac);
    
        return wac;
    }
    

    在其中我们看到的contextConfigLocation就是spring的配置文件地址,默认是WEB-INFO/[ServletName]-Servlet.xml。

    再看一下configureAndRefreshWebApplicationContext的代码:

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

    有一句要注意一下:

    wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
    

    ContextRefreshListener是FrameworkServlet的内部类,监听ContextRefreshedEvent事件,当接收到消息时调用FrameworkServlet的onApplicationEvent方法,在onApplicationEvent中会调用一次onRefresh方法,将refreshEventReceived标志设置为true,表示refresh过。

    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(event.getApplicationContext());
    }
    

    注意看之前的initWebApplicationContext方法,方法较后的位置会判断根据refreshEventReceived标志来判断是否要运行onRefresh, 
    第一种方法已经调用了configureAndRefreshWebApplicationContext,就是已经refresh过了,第三种方法也是,所以只有第二种需要重新运行它。 
    DispatcherServlet就是通过重写onRefresh模版方法实现初始化的。

    下面我们看DispatcherServlet中重写的onRefresh方法代码,如下:

    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }
    

    再看initStrategies

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
    

    任意取一个,如initLocaleResolver,其他8种也是类似的。

    private void initLocaleResolver(ApplicationContext context) {
        try {
            this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
            if (logger.isDebugEnabled()) {
                logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
            }
        }
        catch (NoSuchBeanDefinitionException ex) {
            // We need to use the default.
            this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
            if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
                        "': using default [" + this.localeResolver + "]");
            }
        }
    }
    

    我们可以看到它调用了getDefaultStrategy方法,getDefaultStrategy又调用了getDefaultStrategies方法,两个一起列出来

    protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
        List<T> strategies = getDefaultStrategies(context, strategyInterface);
        if (strategies.size() != 1) {
            throw new BeanInitializationException(
                    "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
        }
        return strategies.get(0);
    }
    
    protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
        String key = strategyInterface.getName();
        String value = defaultStrategies.getProperty(key);
        if (value != null) {
            String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
            List<T> strategies = new ArrayList<T>(classNames.length);
            for (String className : classNames) {
                try {
                    Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                    Object strategy = createDefaultStrategy(context, clazz);
                    strategies.add((T) strategy);
                }
                catch (ClassNotFoundException ex) {
                    throw new BeanInitializationException(
                            "Could not find DispatcherServlet's default strategy class [" + className +
                                    "] for interface [" + key + "]", ex);
                }
                catch (LinkageError err) {
                    throw new BeanInitializationException(
                            "Error loading DispatcherServlet's default strategy class [" + className +
                                    "] for interface [" + key + "]: problem with class file or dependent class", err);
                }
            }
            return strategies;
        }
        else {
            return new LinkedList<T>();
        }
    

    分析getDefaultStragedies,可以发现,数据转换的关键在于传入key,根据defaultStrategies.getProperty(key); 得到value,在ide里可以看出来defaultStrategies是一个静态变量,类型是properties,它通过一个静态块进行初始化,代码如下:

    private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
    private static final Properties defaultStrategies;
    static {
        // Load default strategy implementations from properties file.
        // This is currently strictly internal and not meant to be customized
        // by application developers.
        try {
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
        catch (IOException ex) {
            throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
        }
    }
    

    这里可以看出来这个变量通过读取DEFAULT_STRATEGIES_PATH路径下property文件来进行值得初始化,DEFAULT_STRATEGIES_PATH得代码我也顺便加在上面了,它在spring-beans-4.2.5.RELEASE-sources.jar里,位置就在org.springframework.web.servlet.DispatcherServlet.properties,内容如下:

    # Default implementation classes for DispatcherServlet's strategy interfaces.
    # Used as fallback when no matching beans are found in the DispatcherServlet context.
    # Not meant to be customized by application developers.
    
    org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
    
    org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
    
    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
        org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
    
    org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,
        org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,
        org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
    
    org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,
        org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,
        org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
    
    org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
    
    org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
    
    org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
    

    默认配置在相应类型没有配置时才会使用,如当使用< mvc:annotation-driven />后,并不会全部使用默认配置,因为它配置了HandlerMappingHandlerAdapter和Handler-ExceptionResolver。 
    那总的来说, 
    HttpServletBean直接继承自Java的HttpServlet,作用是将Servlet中配置的参数设置到相应的属性;FrameworkServlet初始化了WebApplicationContext,DispatcherServlet初始化了自身的9个组件。 
    FrameworkServlet初始化WebApplicationContext一共有三种方式,过程中使用了Servlet中配置的一些参数。

  • 相关阅读:
    HDU-1102 Constructing Roads ( 最小生成树 )
    POJ-1287 Networking ( 最小生成树 )
    HDU-1272 小希的迷宫 ( 并查集 )
    Java基本数据类型、关键字
    观察者模式
    Android系统启动过程分析
    Activity启动过程源码分析(Android 8.0)
    Okhttp解析—Okhttp概览
    Okhttp解析—Interceptor详解
    Okhttp源码分析--基本使用流程分析
  • 原文地址:https://www.cnblogs.com/fupengpeng/p/7382634.html
Copyright © 2011-2022 走看看