Spring MVC源码解析

来源:互联网 发布:女流和陈一发 知乎 编辑:程序博客网 时间:2024/06/05 15:17

转载请标明出处 http://coderknock.com

DispatcherServlet

XXXAware

XXXAware在Spring中该类接口用于通过Spring自动向XXXAware实现类中实现的setXXX(XXX xxx)方法中注入XXX对象,方便在实现类中调用XXX对象。
例如:
A类需要使用当前的ApplicationContext,那么只要是A实现ApplicationContextAware接口,然后实现接口中的setApplicationContext(ApplicationContext applicationContext)方法,Spring就会自动调用setApplicationContext方法将applicationContext传给A类。

XXXCapable

实现XXXCapable接口后说明该实现类具有提供XXX的能力,当Spring需要XXX时会通过该类的getXXX方法来获取XXX对象。

Environment

Environment具体功能与Servlet中的ServletContext类似。
HttpServletBean中Environment使用的是StandardServletEnvironment。

    @Override    public ConfigurableEnvironment getEnvironment() {        if (this.environment == null) {            this.environment = this.createEnvironment();        }        return this.environment;    }    protected ConfigurableEnvironment createEnvironment() {        return new StandardServletEnvironment();    }

StandardServletEnvironment中封装了ServletContext、ServletConfig、JndiProperty、系统环境变量以及系统属性。

StandardServletEnvironment{    activeProfiles=[    ],    defaultProfiles=[        default    ],    propertySources=[        servletConfigInitParams,        servletContextInitParams,        jndiProperties,        systemProperties,        systemEnvironment    ]}

Environment
具体属性大家可以调试一下查看

转载请标明出处 http://coderknock.com

HttpServletBean类图
Servlet在创建时会直接调用无参init方法
HttpServletBean的init方法如下:

    @Override    public final void init() throws ServletException {        if (logger.isDebugEnabled()) {            logger.debug("Initializing servlet '" + getServletName() + "'");        }        try {            // 将web.xml中init-param参数封装到pvs变量中,requiredProperties是指必须的一些参数,没有的话会报错            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);            //bw是对DispatcherServlet的封装            //【BeanWrpper是Spring提供的用于操作JavaBean属性的一个工具类,通过它封装一个对象后可以直接修改对象的属性】            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));            //对DispatcherServlet做一些初始化的工作            initBeanWrapper(bw);            //将配置值设置到DispatcherServlet            bw.setPropertyValues(pvs, true);        }        catch (BeansException ex) {            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);            throw ex;        }        // 子类通过该方法初始化【该类中只是声明了该方法】        initServletBean();        if (logger.isDebugEnabled()) {            logger.debug("Servlet '" + getServletName() + "' configured successfully");        }    }    protected void initServletBean() throws ServletException {    }

转载请标明出处 http://coderknock.com

FrameworkServlet,通过之前HttpServletBean详解,我们知道,FrameworkServlet是通过initServletBean来实现自身的初始化的。我们看一下initServletBean:

    @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 {            this.webApplicationContext = initWebApplicationContext();            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");        }    }    protected void initFrameworkServlet() throws ServletException {    }

可以看到这个方法中的核心语句:

this.webApplicationContext = initWebApplicationContext();initFrameworkServlet();

其中initFrameworkServlet是供子类Override后实现自身的一些初始化工作【DispatcherServlet中并没有对initFrameworkServlet进行Override】。
可见,FrameworkServlet在构建工程中主要作用就是初始化webApplicationContext:

protected WebApplicationContext initWebApplicationContext() {//获取rootContext        WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());        WebApplicationContext wac = null;        //判断是否在构造方法中以及初始化了webApplicationContext        if (this.webApplicationContext != null) {            // 一个WebApplicationContext实例在构建实例的时候被注入            wac = this.webApplicationContext;            if (wac instanceof ConfigurableWebApplicationContext) {                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;                if (!cwac.isActive()) {                    // 上下文尚未Refresh -> 配置并Refresh                    // 设置父上下文,设置WebApplicationContext id等等                    if (cwac.getParent() == null) {                        // 如果上下文被注入时没有明确的设置Parent,则在这里设置                        // 设置rootContext为Parent【可能为null】                         cwac.setParent(rootContext);                    }                    configureAndRefreshWebApplicationContext(cwac);                }            }        }        if (wac == null) {            //如果在构建实例时没有注入WebApplicationContext            //查找WebApplicationContext是否已经存在于ServletContext中【通过配置在Servlet中的contextAttribute获取】            wac = findWebApplicationContext();        }        if (wac == null) {            // 如果还是没有WebApplicationContext,那么创建一个            wac = createWebApplicationContext(rootContext);        }        if (!this.refreshEventReceived) {            // 如果还是没有WebApplicationContext 既不是一个ConfigurableApplicationContext支持Refresh,并且WebApplicationContext注入时没有Refresh- >手动触发onRefresh【供子类Override】。            onRefresh(wac);        }        if (this.publishContext) {            // 将WebApplicationContex保存到ServletContext            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;    }    protected void onRefresh(ApplicationContext context) {        // For subclasses: do nothing by default.    }

initWebApplicationContext做了三件事:
1. 获取Spring都得rootContext
Spring默认将rootContext设置于ServletContext的属性中,属性名为ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

  1. 设置WebApplicationContext并根据情况调用onRefresh方法
    设置WebApplicationContext一共三种方法:
    一、构造方法传入
    这种方法主要用于Servlet3.0以后的环境中,在程序中使用ServletContext.addServlet方式注册Servlet,这时可以在新建FrameworkServlet和其子类时通过构造方法传递WebApplicationContext
    二、WebApplicationContext已经存在ServletContext中,这是只要将DispatcherServlet配置是将WebApplicationContext配置到contextAttribute就可以。
    三、创建一个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 + "]");        }        //检查获取到的类似是否是ConfigurableWebApplicationContext的子类        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);        //将servlet配置地址【web.xml contextConfigLocation设置】        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());        //添加ContextRefreshListener        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();    }
  1. 将WebApplicationContext设置到ServletContext中

总结一下可配置项:
1. contextAttribute:在ServletContext中,用作WebApplicationContext的属性名称
2. contextClass:创建的WebApplicationContext的类型
3. contextConfigLocation:SpringMVC配置文件的位置
4. publishContext:是否将WebApplicationContext设置到ServletContext中。

转载请标明出处 http://coderknock.com

通过之前FrameworkServlet的分析,我们知道DispatcherServlet的入口方法是onRefresh。

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

onRefresh调用了initStrategies,initStrategies中初始化了九大组件,其中的initXXXX方法基本实现都是通过WebApplicationContext.getBean(名称, class);获取注册的对应组件,如果找不到会通过getDefaultStrategy方法获取默认的组件。

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

getDefaultStrategy调用了getDefaultStrategies,而getDefaultStrategies是通过defaultStrategies来查找value值的。defaultStrategies对应的是DispatcherServlet.properties:

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolverorg.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\    org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMappingorg.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterorg.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\    org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

这里的默认设置并不是最优配置,也不是Spring推荐配置,只是为了防止没有配置。
这些默认配置是在没有配置时才会使用,使用<mvc:annotation-driven/>之后并不会全部使用默认配置。<mvc:annotation-driven/>会配置HandlerMapping、HandlerAdapter、HandlerExceptionResolver等。

0 0
原创粉丝点击