• Spring MVC 学习 -- 创建过程


    Spring MVC 学习 -- 创建过程

     

    Spring MVC我们使用的时候会在web.xml中配置

     1   <servlet>
     2     <servlet-name>SpringMVC</servlet-name>
     3     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     4     <init-param>
     5       <param-name>contextConfigLocation</param-name>
     6       <param-value>classpath:spring/spring-web.xml</param-value>
     7     </init-param>
     8   </servlet>
     9   <servlet-mapping>
    10     <servlet-name>SpringMVC</servlet-name>
    11     <url-pattern>/</url-pattern>
    12   </servlet-mapping>

    1.核心的类结构

      继承结构主要有五个类,GenericServlet和HttpServlet 是Java的,HttpServletBean、FrameworkServlet和DispatcherServlet是Spring MVC的。

        在图中还有三个接口:EnvironmentCapable、EnvironmentAware和ApplicationContextAware~

        XXXAware 在spring里标识对XXX可以感知 -- 白话的意思就是:某个类如果想要使用spring的一些东西,就可以通过实现XXXAware接口告诉spring,spring看到后会给你送过来,而接收的方式是通过实现接口唯一的setXXX

    1 public interface ApplicationContextAware extends Aware {
    2     
    3 void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
    4 
    5 }

    源码如上,使用的话写一个类实现ApplicationContextAware,然后实现setApplicationContext()方法就行,spring为自动调用这个方法将applicationContext传给我们。 

    而EnvironmentCapable是为了实现getEnvironment去拿到ServletContext、ServletConfig、JndiProperty、系统环境变量和系统属性。

    2.HttpServletBean

     1     public final void init() throws ServletException {
     2         if (logger.isDebugEnabled()) {
     3             logger.debug("Initializing servlet '" + getServletName() + "'");
     4         }
     5 
     6         // Set bean properties from init parameters.
     7         try {
     8             PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
     9             BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
    10             ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
    11             bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
    12             initBeanWrapper(bw);
    13             bw.setPropertyValues(pvs, true);
    14         }
    15         catch (BeansException ex) {
    16             logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
    17             throw ex;
    18         }
    19 
    20         // Let subclasses do whatever initialization they like.
    21         initServletBean();
    22 
    23         if (logger.isDebugEnabled()) {
    24             logger.debug("Servlet '" + getServletName() + "' configured successfully");
    25         }
    26     }

    主要看init()方法, 使用了BeanWrapper这个类对象,BeanWrapper是操作JavaBean的工具类,主要就是对属性进行操作。

    3.FrameworkServlet

    这个类是通过HttpServletBean中的initServletBean()方法进入的。

     1     protected final void initServletBean() throws ServletException {
     2         getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
     3         if (this.logger.isInfoEnabled()) {
     4             this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
     5         }
     6         long startTime = System.currentTimeMillis();
     7 
     8         try {
     9             this.webApplicationContext = initWebApplicationContext();
    10             initFrameworkServlet();
    11         }
    12         catch (ServletException ex) {
    13             this.logger.error("Context initialization failed", ex);
    14             throw ex;
    15         }
    16         catch (RuntimeException ex) {
    17             this.logger.error("Context initialization failed", ex);
    18             throw ex;
    19         }
    20 
    21         if (this.logger.isInfoEnabled()) {
    22             long elapsedTime = System.currentTimeMillis() - startTime;
    23             this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
    24                     elapsedTime + " ms");
    25         }
    26     }

    核心代码就try里面包含的两句,很显然主要用来初始化WebApplicationContext和初始化FrameworkServlet的。

     1     protected WebApplicationContext initWebApplicationContext() {
     2         WebApplicationContext rootContext =
     3                 WebApplicationContextUtils.getWebApplicationContext(getServletContext());
     4         WebApplicationContext wac = null;
     5 
     6         if (this.webApplicationContext != null) {
     7             // A context instance was injected at construction time -> use it
     8             wac = this.webApplicationContext;
     9             if (wac instanceof ConfigurableWebApplicationContext) {
    10                 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
    11                 if (!cwac.isActive()) {
    12                     // The context has not yet been refreshed -> provide services such as
    13                     // setting the parent context, setting the application context id, etc
    14                     if (cwac.getParent() == null) {
    15                         // The context instance was injected without an explicit parent -> set
    16                         // the root application context (if any; may be null) as the parent
    17                         cwac.setParent(rootContext);
    18                     }
    19                     configureAndRefreshWebApplicationContext(cwac);
    20                 }
    21             }
    22         }
    23         if (wac == null) {
    24             // No context instance was injected at construction time -> see if one
    25             // has been registered in the servlet context. If one exists, it is assumed
    26             // that the parent context (if any) has already been set and that the
    27             // user has performed any initialization such as setting the context id
    28             wac = findWebApplicationContext();
    29         }
    30         if (wac == null) {
    31             // No context instance is defined for this servlet -> create a local one
    32             wac = createWebApplicationContext(rootContext);
    33         }
    34 
    35         if (!this.refreshEventReceived) {
    36             // Either the context is not a ConfigurableApplicationContext with refresh
    37             // support or the context injected at construction time had already been
    38             // refreshed -> trigger initial onRefresh manually here.
    39             onRefresh(wac);
    40         }
    41 
    42         if (this.publishContext) {
    43             // Publish the context as a servlet context attribute.
    44             String attrName = getServletContextAttributeName();
    45             getServletContext().setAttribute(attrName, wac);
    46             if (this.logger.isDebugEnabled()) {
    47                 this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
    48                         "' as ServletContext attribute with name [" + attrName + "]");
    49             }
    50         }
    51 
    52         return wac;
    53     }

    主要做了三件事:

    • 获取spring的根容器rootContext
    • 设置webApplicationContext并根据情况调用onRefresh方法
    • 将webApplicationContext设置到ServletContext中

    4.DispatcherServlet

    通过上面onRefresh()方法,调用initStrategies()方法,初始化策略组件。具体的内容就是用来初始化9个组件,每个组件的代码不一一贴出来了, 这几个方法都会去调用getDefaultStrategies()方法。

     1 protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
     2         String key = strategyInterface.getName();
     3         String value = defaultStrategies.getProperty(key);
     4         if (value != null) {
     5             String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
     6             List<T> strategies = new ArrayList<T>(classNames.length);
     7             for (String className : classNames) {
     8                 try {
     9                     Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
    10                     Object strategy = createDefaultStrategy(context, clazz);
    11                     strategies.add((T) strategy);
    12                 }
    13                 catch (ClassNotFoundException ex) {
    14                     throw new BeanInitializationException(
    15                             "Could not find DispatcherServlet's default strategy class [" + className +
    16                                     "] for interface [" + key + "]", ex);
    17                 }
    18                 catch (LinkageError err) {
    19                     throw new BeanInitializationException(
    20                             "Error loading DispatcherServlet's default strategy class [" + className +
    21                                     "] for interface [" + key + "]: problem with class file or dependent class", err);
    22                 }
    23             }
    24             return strategies;
    25         }
    26         else {
    27             return new LinkedList<T>();
    28         }
    29     }

    5.代码测试

    配置内容都不再贴了,启动Tomcat,然后去请求某个controller地址,进入HttpServletBean的init()方法

    然后继续,因为WebApplicationContext是null,会进入

    然后configureAndRefreshWebApplicationContext方法中会执行refresh(),这部分可以看spring bean加载过程和实例化。

  • 相关阅读:
    webpack--前端自动化工具
    Vue--入门篇
    集千篇理论,终得深拷贝与浅拷贝的初解
    事件循环--eventloop
    对象的属性(变量+对象)
    集千篇理论精华,感悟对同步和异步的理解
    vue--先决篇
    js的基本语法规范
    python 模块加载错误总结
    Python logging模块无法正常输出日志
  • 原文地址:https://www.cnblogs.com/tiecheng/p/6202058.html
Copyright © 2020-2023  润新知