springmvc工作原理(源码解析)

 spring-web工作原理

1.1 tomcat & springmvc配置

     SpringMVC在Java后端中,是一个很重要也很常用的框架,本篇来分析一下SpringMVC的源码。web.xml配置,虽然使用SpringMVC不需要我们写Servlet,但SpringMVC是封装了Servlet,提供 DispatcherServlet 来帮我们处理的。所以需要在 web.xml 配置 DispatcherServlet。可以看出 DispatcherServlet,映射的url是 /,所以所有的请求都会被它拦截,再处理给我们。我们进去看一下。

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <!--配置前端控制器-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- 加载SpringMVC配置文件 -->
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- 启动就加载这个Servlet -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    //省略其他配置...
</web-app>

线上一个tomcat和springmvc集成的架构图,如下图所示是左侧是tomcat的类结构图,右侧是springmvc的类结构图,正如我们所知道的在tomcat启动的时候,会实例化一个DispatcherServlet类放入到Tomcat容器中.请求过来的时候会从tomcat直接传递到这个类.然后又交给业务代码去处理,最后通过这个类做视图解析返回.

1.2 tomcat & springmvc架构图

  如上就是整合原理 public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware 核心就是springmvc的HttpServletBean继承tomcat的

HttpServlet接口.所以tomcat整合springmvc的核心就再spingmvc定义了HttpServlet的实现类.接收所有从tomcat过来的http请求.

1.3 3个Servlet类的作用和职责

1.3.1 HttpServletBean

主要做一些初始化工作,解析 web.xml 中配置的参数到Servlet上,比如init-param中配置的参数。提供 initServletBean() 模板方法,给子类 FrameworkServlet实现。

1.3.2 FrameworkServlet

将Servlet和SpringIoC容器关联。主要是初始化其中的 WebApplicationContext,它代表SpringMVC的上下文,它有一个父上下文,就是 web.xml 配置文件中配置的 ContextLoaderListener 监听器,监听器进行初始化容器上下文。
提供了 onRefresh() 模板方法,给子类 DispatcherServlet 实现,作为初始化入口方法。

1.3.3 DispatcherServlet

最后的子类,作为前端控制器,初始化各种组件,比如请求映射、视图解析、异常处理、请求处理等。

 初始化

我们知道,Servlet初始化时,Servlet的 init()方法会被调用。我们进入 DispatcherServlet中,发现并没有该方法,那么肯定在它集成的父类上。DispatcherServlet 继承于 FrameworkServlet,结果还是没找到,继续找它的父类 HttpServletBean。

2.1 HttpServletBean

终于找到了,HttpServletBean 继承于 HttpServlet,我们来看下这个 init() 方法。

public final void init() throws ServletException {
    //获取配置web.xml中的参数
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
        try {
            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) {
            if (logger.isErrorEnabled()) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            }
            throw ex;
        }
    }
    //重点:一个空方法,模板模式,子类FrameworkServlet,重写了它
    initServletBean();
}

2.2 FrameworkServlet

initServletBean()方法,就是一个初始化。方法内主要是调用了 initWebApplicationContext() 初始化WebApplicationContext,以及调用 initFrameworkServlet() ,这个是一个空方法,可以提供给以后的子类复写,做一些初始化的事情,暂时没有被复写。

protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
    if (logger.isInfoEnabled()) {
        logger.info("Initializing Servlet '" + getServletName() + "'");
    }
    long startTime = System.currentTimeMillis();

    try {
        //重点:初始化WebApplicationContext
        this.webApplicationContext = initWebApplicationContext();
        //一个空方法,可以提供给以后的子类复写,做一些初始化的事情,暂时没有被复写
        initFrameworkServlet();
    }
    catch (ServletException | RuntimeException ex) {
        logger.error("Context initialization failed", ex);
        throw ex;
    }
    //省略无关代码...
}

 下面分析 initWebApplicationContext()方法。spring3.0以前还有父子容器的概念所以代码保留了,现在springboot启动了就不存在父子容器的关系了.

        

    initWebApplicationContext()方法,该方法是初始化 WebApplicationContext的,而它集成于 ApplicationContext,所以它也是一个IoC容器。所以 FrameworkServlet类的职责是将 Spring 和 Servlet进行一个关联。这个方法,除了初始化WebApplicationContext外,还调用了一个 onRefresh()方法,又是模板模式,空方法,让子类复写进行逻辑处理,例如子类DispatcherServlet重写了它.

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    //有参数构造方法,传入webApplicationContext对象,就会进入该判断
    if (this.webApplicationContext != null) {
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            //还没初始化过,容器的refresh()还没有调用
            if (!cwac.isActive()) {
                //设置父容器
                if (cwac.getParent() == null) {
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
        //获取ServletContext,之前通过setAttribute设置到了ServletContext中,现在通过getAttribute获取到
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        //创建WebApplicationContext,设置环境environment、父容器,本地资源文件
        wac = createWebApplicationContext(rootContext);
    }
    if (!this.refreshEventReceived) {
        synchronized (this.onRefreshMonitor) {
            //刷新,也是模板模式,空方法,让子类重写进行逻辑处理,而子类DispatcherServlet重写了它
            onRefresh(wac);
        }
    }
    //用setAttribute(),将容器设置到ServletContext中
    if (this.publishContext) {
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }
    return wac;
}
//WebApplicationContext
public interface WebApplicationContext extends ApplicationContext {
    //...
}

接来下,我们看子类 DispatcherServlet 复写的 onRefresh()方法。

2.3 DispatcherServlet

FrameworkServlet类的职责是将 Spring 和 Servler 进行一个关联。而对于 DispatcherServlet 来说,它初始化方法是 onRefresh()。onRefresh() 方法,调用 initStrategies() 方法,进行各种组件的初始化工作。我们重点看 initHandlerMappings() 后面的流程!

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

protected void initStrategies(ApplicationContext context) {  //解析请求
    initMultipartResolver(context);  //国际化
    initLocaleResolver(context);  //主题
    initThemeResolver(context);  //处理Controller的方法和url映射关系
    initHandlerMappings(context); //初始化适配器,多样写法Controller的适配处理,实现最后返回都是ModelAndView
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context); //初始化异常处理器 
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);//初始化视图解析器,将ModelAndView保存的视图信息,转换为一个视图,输出数据
    //初始化映射处理器
    initFlashMapManager(context);
}

 组件初始化流程

下面开始 组件初始化流程 分析。

3.1 HandlerMapping 处理器映射

初始化Controller的Url映射关系。

主要是Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);,这句代码从容器中,获取所有 HandlerMapping。

private List<HandlerMapping> handlerMappings;
//一个开关,标识是否获取所有的处理器映射,如果为false,则搜寻指定名为的 handlerMapping 的 Bean实例
private boolean detectAllHandlerMappings = true;
//指定的Bean的名称
public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";

private void initHandlerMappings(ApplicationContext context) {
    //清空集合
    this.handlerMappings = null;
    
    //一个开关,默认为true,设置为false,才走else的逻辑
    if (this.detectAllHandlerMappings) {
        //重点:在容器中找到所有HandlerMapping
        Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        //找到了,进行排序,保证顺序
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    }
    else {
        //指定搜寻指定名为 handlerMapping 的 HandlerMapping 实例
        try {
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
            this.handlerMappings = Collections.singletonList(hm);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerMapping later.
        }
    }
    
    //也找不到映射关系,设置一个默认的
    if (this.handlerMappings == null) {
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }

    //配置文件名
    private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";

    //从配置文件中获取配置的组件,其他组件找不到时,也是调用这个方法进行默认配置
    protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
        //...
    }
}

如果找不到任何一个映射关系,会通过 getDefaultStrategies 方法,从配置文件中获取默认配置。其他组件找不到时,也是调用这个方法进行默认配置。配置文件名:DispatcherServlet.properties。会加入2个默认的映射关系类 BeanNameUrlHandlerMapping 、 RequestMappingHandlerMapping。

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

3.2 HandlerAdapter 处理器适配器

HandlerAdapter 的初始化逻辑和上面的 HandlerMapping 基本一样。从容器中搜寻所有 HandlerAdapter 的实例。如果找不到,则从配置文件中获取默认 的 HandlerAdapter。

private List<HandlerAdapter> handlerAdapters;
//和上面HandlerMapping一样,一个开关,是否搜寻容器中所有的HandlerAdapter,如果为false,则搜寻指定名为 handlerAdapter 的Bean
private boolean detectAllHandlerAdapters = true;
//指定的HandlerAdapter实例
public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";

private void initHandlerAdapters(ApplicationContext context) {
    //清空集合
    this.handlerAdapters = null;

    //也是一个开关,默认true,搜寻容器中所有的HandlerAdapter
    if (this.detectAllHandlerAdapters) {
        Map<String, HandlerAdapter> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
        //找到了,进行排序,保证HandlerAdapter是有序的
        if (!matchingBeans.isEmpty()) {
            this.handlerAdapters = new ArrayList<>(matchingBeans.values());
            AnnotationAwareOrderComparator.sort(this.handlerAdapters);
        }
    }
    else {
        //指定找名为 handlerAdapter 的HandlerAdapter
        try {
            HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
            this.handlerAdapters = Collections.singletonList(ha);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerAdapter later.
        }
    }

    //没有找一个HandlerAdapter,从配置文件中获取默认的HandlerAdapter
    if (this.handlerAdapters == null) {
        this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

3.3 HandlerExceptionResolver

异常处理器和上面的一样,从容器中搜寻所有的异常处理器的实例,也有一个开关去搜索指定名称的异常处理器。

private List<HandlerExceptionResolver> handlerExceptionResolvers;
//开关,是否梭巡所有的异常处理器,设置为false,就会找下面名为 handlerExceptionResolver 的Bean实例
private boolean detectAllHandlerExceptionResolvers = true;
//指定名为 handlerExceptionResolver 的实例
public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";

private void initHandlerExceptionResolvers(ApplicationContext context) {
    //清空集合
    this.handlerExceptionResolvers = null;

    //开关,默认true
    if (this.detectAllHandlerExceptionResolvers) {
        //搜寻所有的异常处理器
        Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
                .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
        //搜寻到了
        if (!matchingBeans.isEmpty()) {
            this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
            //排序
            AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
        }
    }
    else {
        try {
            HandlerExceptionResolver her =
                    context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
            this.handlerExceptionResolvers = Collections.singletonList(her);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, no HandlerExceptionResolver is fine too.
        }
    }

    //一个异常处理器都没有,从配置文件中获取默认的
    if (this.handlerExceptionResolvers == null) {
        this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

3.4 ViewResolver 视图解析器

视图解析器和上面的解析器逻辑一样,先有开关决定是搜寻容器中所有的,还是搜寻指定名称的。

private List<ViewResolver> viewResolvers;
//开关
private boolean detectAllViewResolvers = true;
//指定名称
public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";

private void initViewResolvers(ApplicationContext context) {
    //清空集合
    this.viewResolvers = null;

    if (this.detectAllViewResolvers) {
        //搜寻所有视图解析器
        Map<String, ViewResolver> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.viewResolvers = new ArrayList<>(matchingBeans.values());
            //排序
            AnnotationAwareOrderComparator.sort(this.viewResolvers);
        }
    }
    else {
        try {
            //搜寻指定名为 viewResolver 的视图解析器Bean
            ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
            this.viewResolvers = Collections.singletonList(vr);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default ViewResolver later.
        }
    }

    //没有找到任何一个视图解析器,从配置文件中读取
    if (this.viewResolvers == null) {
        this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No ViewResolvers declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

 这里主要做了什么呢看一段代码,在spring中没有配置对应的各种解析器的情况下做了什么呢

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<>(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(
                  "Unresolvable class definition for DispatcherServlet's default strategy class [" +
                  className + "] for interface [" + key + "]", err);
         }
      }
      return strategies;
   }
   else {
      return new LinkedList<>();
   }
}

    核型代码String value = defaultStrategies.getProperty(key);从配置类中加载对应的类.生成对应的集合.那么这个配置类是从哪里来的呢.我们可以看一下

private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
private static final Properties defaultStrategies;
static {
   try {
      ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
      defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
   }catch (IOException ex) {
      throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
   }
}

DispatcherServlet.properties

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.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,
\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

        如上所示我们看明白了原来HandlerMapping加载的时候会获取到两个默认的子实现类.其他的接口也是同样的道理,这下我们明白了在Servlet启动的时候,会带动DispatcherServlet进行初始化,当初始化完成以后,DispatcherServlet里面有的属性.

private LocaleResolver localeResolver;
private ThemeResolver themeResolver;
private List<HandlerMapping> handlerMappings;
private List<HandlerAdapter> handlerAdapters;
private List<HandlerExceptionResolver> handlerExceptionResolvers;
private RequestToViewNameTranslator viewNameTranslator;
private FlashMapManager flashMapManager;
private List<ViewResolver> viewResolvers;

       如上代码所示就已经被填充好了.好了当我们知道了初始化的流程以后.我们可以来看看请求是怎么进来的.

3.5 请求流程分析

 

     通过对tomcat工作原理的学习,如果不熟悉tomcat工作原理的同学请看我的另一个专栏的博客高性能服务中间件Tomcat工作原理解析(一)_worn_xiao的博客-CSDN博客;我们知道当请求进入时,我们都知道会调用Servlet的 service() 方法,通过继承层次我们可以看出,我们试着去 DispatchServlet 中搜索,发现没有。我们去到父类 FrameworkServlet 找到了。

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
    if (httpMethod != HttpMethod.PATCH && httpMethod != null) {
        super.service(request, response);
    } else {
        this.processRequest(request, response);
    }
}
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.processRequest(request, response);
}
protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.processRequest(request, response);
}
protected final void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.processRequest(request, response);
}
protected final void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.processRequest(request, response);
}

protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
        this.processRequest(request, response);
        if (response.containsHeader("Allow")) {
            return;
        }
    }
    super.doOptions(request, new HttpServletResponseWrapper(response) {
        public void setHeader(String name, String value) {
            if ("Allow".equals(name)) {
                value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name();
            }
            super.setHeader(name, value);
        }
    });
}
protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    if (this.dispatchTraceRequest) {
        this.processRequest(request, response);
        if ("message/http".equals(response.getContentType())) {
            return;
        }
    }
    super.doTrace(request, response);
}

  super.service(request, response); 核心代码如上所示,可以看出是优先调用父类的service方法的.好的那么我们看看这个service()方法是做了什么.

HttpServlet中如下所示代码

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getMethod();
    long lastModified;
    if (method.equals("GET")) {
        lastModified = this.getLastModified(req);
        if (lastModified == -1L) {
            this.doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader("If-Modified-Since");
            } catch (IllegalArgumentException var9) {
                ifModifiedSince = -1L;
            }
            if (ifModifiedSince < lastModified / 1000L * 1000L) {
                this.maybeSetLastModified(resp, lastModified);
                this.doGet(req, resp);
            } else {
                resp.setStatus(304);
            }
        }
    } else if (method.equals("HEAD")) {
        lastModified = this.getLastModified(req);
        this.maybeSetLastModified(resp, lastModified);
        this.doHead(req, resp);
    } else if (method.equals("POST")) {
        this.doPost(req, resp);
    } else if (method.equals("PUT")) {
        this.doPut(req, resp);
    } else if (method.equals("DELETE")) {
        this.doDelete(req, resp);
    } else if (method.equals("OPTIONS")) {
        this.doOptions(req, resp);
    } else if (method.equals("TRACE")) {
        this.doTrace(req, resp);
    } else {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[]{method};
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
    }
}

你会发现父类中所有的代码都是调用类doxxx()方法.然而我们子类FrameworkServlet中已经对这些个方法进行了重新编写了.好的 this.processRequest(request, response);子类中所有的doxxx()方法都是通过processRequest ()方法进行处理的,好的那么我们看看这个方法做了什么.

processRequest 处理请求

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;
    LocalContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContext localeContext = buildLocaleContext(request);
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
    initContextHolders(request, localeContext, requestAttributes);
    try {
        //重点,doService()是一个抽象方法,强制让子类进行复写
        doService(request, response);
    }
    catch (ServletException | IOException ex) {
        failureCause = ex;
        throw ex;
    }
    catch (Throwable ex) {
        failureCause = ex;
        throw new NestedServletException("Request processing failed", ex);
    }
    finally {
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }
        logResult(request, response, failureCause, asyncManager);
        publishRequestHandledEvent(request, response, startTime, failureCause);
}}

一大片的处理和设置,不是我们的重点,主要是 doService() 这个方法,它是一个抽象方法,强制让子类进行复写。
所以最终子类 DispatcherServlet 肯定会复写 doService() 方法。

3.5.1 dispatcherServlet.doService()

doService() 方法中,主要的组件分发处理逻辑在 doDispatch() 方法中。所以我们可以认为在springmvc层面所有的请求处理都是doService()方法实现的.

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //打印请求
    logRequest(request);

    Map<String, Object> attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
        attributesSnapshot = new HashMap<>();
        Enumeration<?> attrNames = request.getAttributeNames();
        while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();
            if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }

    //设置组件到请求域中,给后续的其他组件可以获取到
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

    if (this.flashMapManager != null) {
        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        if (inputFlashMap != null) {
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
    }

    try {
        //重点:主要的组件分发处理逻辑在 doDispatch() 方法
        doDispatch(request, response);
    }
    finally {
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Restore the original attribute snapshot, in case of an include.
            if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
            }
        }
    }
}

     如上代码所示前面的就不看了,这里主要分析doDispatch() 分发请求给各个组件处理,该方法非常重要,分发逻辑都子在这里呈现了。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    //本次请求的处理器以及拦截器,它们组合成一个执行链
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            //检查是否是文件上传请求。是的话,做一些处理
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            //重点:找到本次请求的处理器以及拦截器
            mappedHandler = getHandler(processedRequest);
            //找不到处理器处理,响应404
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            //重点:找到本次请求中,处理器的适配器
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

            //重点:处理前,责任链模式 回调拦截器的 preHandle() 方法,如果拦截了,则不继续往下走了
            //返回true代表放心,false为拦截
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            //重点:调用适配器的处理方法,传入处理器,让适配器将处理器的结果转换成统一的ModelAndView
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            //如果找不到默认的视图,则设置默认的视图
            applyDefaultViewName(processedRequest, mv);
            //重点:处理完成,调用拦截器的 postHandle() 后置处理方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        //重点:分发结果,让视图解析器解析视图,渲染视图和数据
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                new NestedServletException("Handler processing failed", err));
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            //重点:视图渲染完成,调用拦截器的 afterConcurrentHandlingStarted() 方法
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}

如上代码所示我们梳理一下分发步骤:

  •  getHandler(),获取本次请求的处理器执行链,包括Controller和拦截器,它们组合成一个执行链 HandlerExecutionChain。
  •  getHandlerAdapter(),获取处理器的适配器,因为有很多种处理器的实现方式,例如直接是Servlet作为处理器、实现Controller接口、使用Controller注解等,每个接口方法的返回值各式各样,所以这里使用了适配器模式,让适配器对处理器的返回值统一输出为ModelAndView。
  •  mappedHandler.applyPreHandle,责任链模式调用处理器链中的拦截器的 preHandle() 方法,代表请求准备进行处理。拦截器可拦截处理。如果拦截器拦截了,则继续往下走。
  • ha.handle(),调用适配器的处理方法,传入处理器,调用处理器接口方法,并适配处理器的结果为ModelAndView。
  • mappedHandler.applyPostHandle,遍历调用处理器执行链中的拦截器的 postHandle() 后置处理方法,代表请求以被处理,但视图还未渲染
  • processDispatchResult(),处理视图和结果,调用视图处理器,将真正的视图创建,并对视图数据进行渲染。以及渲染完毕,调用拦截器的 afterCompletion()方法,代表视图渲染完毕。
  • mappedHandler.applyAfterConcurrentHandlingStarted,调用处理器执行链中的拦截器,无论当前请求处理成功,还是失败,都处理。

下面就对上面的每个步骤,进行分析。getHandler() 搜寻本次请求的处理器对象,责任链模式,遍历handlerMappings集合,找到处理器和拦截器,会调用到 AbstractHandlerMapping的 getHandler()方法。最后将处理器和拦截器都封装到 HandlerExecutionChain 这个处理器执行链对象中。

getHandlerInternal(),子类实现,主要实现有2个,第一个是AbstractUrlHandlerMapping,一般用它的子类SimpleUrlHandlerMapping,这种方式需要在xml配置文件中配置,已经很少用了。第二个是AbstractHandlerMethodMapping,就是处理我们@Controller和@RequestMapping的。

一般会使用 AbstractHandlerMethodMapping 的子类 RequestMappingHandlerMapping,其中查询注解的方法为 isHandler(),在这里分析的话,篇幅太大了,后续文章,再分析。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

//处理器映射接口,子类 AbstractHandlerMapping 实现了该方法,它是一个抽象类,所有的HandlerMapping实现类,都继承于它
//而AbstractHandlerMapping只管公共流程和处理,提取了抽象方法给子类实现,也就是模板模式
public interface HandlerMapping {
    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
        implements HandlerMapping, Ordered, BeanNameAware {
    @Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //模板方法,获取处理器,具体子类进行实现
        Object handler = getHandlerInternal(request);
        //如果没有获取到,则使用默认的处理器
        if (handler == null) {
            handler = getDefaultHandler();
        }
        //默认的也没有,那就返回null了
        if (handler == null) {
            return null;
        }
        //如果处理器是字符串类型,则在IoC容器中搜寻实例
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }

        //构成处理器执行链,主要是添加拦截器
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

        if (logger.isTraceEnabled()) {
            logger.trace("Mapped to " + handler);
        }
        else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
            logger.debug("Mapped to " + executionChain.getHandler());
        }

        if (hasCorsConfigurationSource(handler)) {
            CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            config = (config != null ? config.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
        return executionChain;
    }
    
    //组成处理器执行链,添加拦截器
    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                //和请求的url做匹配,匹配才加入
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            }
            else {
                chain.addInterceptor(interceptor);
            }
        }
        return chain;
    }
}

     接下来分析适配组件,getHandlerAdapter(),getHandlerAdapter() 获取处理器对应的适配器,责任链模式,遍历调用适配器集合,调用supports()方法,询问每个适配器,是否支持当前的处理器。如果返回true,则代表找到了,停止遍历,返回适配器。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            //责任链模式,遍历调用适配器集合,调用supports()方法,询问每个适配器,是否支持当前的处理器
            //如果返回true,则代表找到了,停止遍历,返回适配器
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }
    throw new ServletException("No adapter for handler [" + handler +
            "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

我们来看看适配器接口,以及它的子类,HttpRequestHandlerAdapter,适配 HttpRequestHandler 作为handler的适配器。SimpleServletHandlerAdapter,适配 Servlet 作为handler的适配器,SimpleControllerHandlerAdapter,适配 Controller 接口 作为handler的适配

public interface HandlerAdapter {
    //判断传入的处理器,是否支持适配
    boolean supports(Object handler);

    //上面的supports()方法返回true,才会调用该方法,进行适配,统一返回ModelAndView对象
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    //作用和Servlet的getLastModified()一样,如果适配的处理器不支持,返回-1即可
    long getLastModified(HttpServletRequest request, Object handler);
}

//适配HttpRequestHandler的适配器
public class HttpRequestHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return (handler instanceof HttpRequestHandler);
    }

    @Override
    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        ((HttpRequestHandler) handler).handleRequest(request, response);
        return null;
    }

    @Override
    public long getLastModified(HttpServletRequest request, Object handler) {
        if (handler instanceof LastModified) {
            return ((LastModified) handler).getLastModified(request);
        }
        return -1L;
    }
}

//适配Servlet的适配器
public class SimpleServletHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return (handler instanceof Servlet);
    }

    @Override
    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        ((Servlet) handler).service(request, response);
        return null;
    }

    @Override
    public long getLastModified(HttpServletRequest request, Object handler) {
        return -1;
    }
}

//适配Controller接口的适配器
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return (handler instanceof Controller);
    }

    @Override
    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        return ((Controller) handler).handleRequest(request, response);
    }

    @Override
    public long getLastModified(HttpServletRequest request, Object handler) {
        if (handler instanceof LastModified) {
            return ((LastModified) handler).getLastModified(request);
        }
        return -1L;
    }
}

根据流程,获取到对应的适配器后,就可以通知拦截器了

3.5.2拦截器前置通知

遍历拦截器链,调用它的 preHandle() 方法,通知拦截器进行请求处理前的拦截和附加处理。
如果有一个拦截器返回false,代表拦截,则处理流程被中断,就是拦截了。

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            if (!interceptor.preHandle(request, response, this.handler)) {
                triggerAfterCompletion(request, response, null);
                return false;
            }
            this.interceptorIndex = i;
        }
    }
    return true;
}

随后,就调用适配器的 handle() 方法,进行适配,返回ModelAndView。处理完后,也代表请求进过Controller处理完毕,接着进行拦截器通知。

3.5.3拦截器后置通知

     和前置通知不同,后置通知没有拦截功能,只能是增强。逻辑还是遍历拦截器链,调用拦截器的 postHandle() 方法。

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
        throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = interceptors.length - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            interceptor.postHandle(request, response, this.handler, mv);
        }
    }
}

视图、数据都获取到了,就可以进行视图生成以及数据渲染了。

3.5.结果处理

        因为 doDispatch() 的处理流程,SpringMVC都帮我们try-catch了,所以能捕获到异常,并传入该方法。接着首先判断处理过程中,是否产生了异常,有则用异常处理器处理。没有异常,则继续往下走,判断是否需要渲染,需要渲染,则进行渲染,最后回调拦截器进行通知。

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
        @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
        @Nullable Exception exception) throws Exception {
    //是否显示错误页面
    boolean errorView = false;
    //处理异常
    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            //处理异常
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }
    //判断处理器是否需要返回视图
    if (mv != null && !mv.wasCleared()) {
        //重点:渲染
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
        if (logger.isTraceEnabled()) {
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }
    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }
    //视图渲染完成,回调拦截器
    if (mappedHandler != null) {
        // Exception (if any) is already handled..
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

//拦截器回调,通知拦截器,视图已被渲染,拦截器可以再做点事情
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
        throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = this.interceptorIndex; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            try {
                interceptor.afterCompletion(request, response, this.handler, ex);
            }
            catch (Throwable ex2) {
                logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
            }
        }
    }
}

3.5.视图解析和渲染

先判断是否需要视图解析器进行视图解析,最后调用解析出来的视图的 render() 方法进行渲染操作。render() 方法进行渲染操作。

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    // Determine locale for request and apply it to the response.
    Locale locale =
            (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
    response.setLocale(locale);
    //真正的视图对象
    View view;
    String viewName = mv.getViewName();
    if (viewName != null) {
        //重点:使用视图解析器,生成正真的视图
        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
        if (view == null) {
            throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                    "' in servlet with name '" + getServletName() + "'");
        }
    }
    else {
        //不需要查找,ModelAndView中已经包含了真正的视图
        view = mv.getView();
        if (view == null) {
            throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                    "View object in servlet with name '" + getServletName() + "'");
        }
    }

    if (logger.isTraceEnabled()) {
        logger.trace("Rendering view [" + view + "] ");
    }
    try {
        if (mv.getStatus() != null) {
            response.setStatus(mv.getStatus().value());
        }
        //重点:开始渲染
        view.render(mv.getModelInternal(), request, response);
    }
    catch (Exception ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Error rendering view [" + view + "]", ex);
        }
        throw ex;
    }
}

视图解析

  • 遍历视图解析器集合,不同的视图需要不同的解析器进行处理。
  • ViewResolver解析器是一个接口,他有几个实现类,对应支持的视图技术。
  • AbstractCachingViewResolver,抽象类,支持缓存视图,所有的解析器都继承它,它内部有一个Map,缓存解析过的视图对象,解决效率问题。
  • UrlBasedViewResolver,继承于 AbstractCachingViewResolver,当我们Controller返回一个字符串,例如success,它就会从我们的xml配置文件中,找到prefix前缀和suffix后缀,和url进行拼接,输出一个完成的视图地址。还有一种就是我们返回 redirect:前缀的字符串时,会解析为重定向视图View,进行重定向操作。
  • InternalResourceViewResolver,内部资源解析器,继承于上面的 UrlBasedViewResolver,所以 UrlBasedViewResolver 有的功能呢,它都有,主要用于加载 /WEB-INF/ 目录下的资源。

还有一些其他不太常用的解析器,这里就不介绍了

<!-- 配置视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- 视图文件都从pages文件夹下找 -->
    <property name="prefix" value="/WEB-INF/pages/"/>
    <!-- 文件后缀为jsp -->
    <property name="suffix" value=".jsp"/>
</bean>
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
        Locale locale, HttpServletRequest request) throws Exception {
    if (this.viewResolvers != null) {
        //遍历视图解析器集合,不同的视图需要不同的解析器进行处理
        for (ViewResolver viewResolver : this.viewResolvers) {
            View view = viewResolver.resolveViewName(viewName, locale);
            if (view != null) {
                return view;
            }
        }
    }
    return null;
}

public interface ViewResolver {
    //尝试解析视图名称为视图对象,如果不能解析,返回null
    @Nullable
    View resolveViewName(String viewName, Locale locale) throws Exception;
}

视图渲染

  1. 视图解析完成,生成View视图对象,而View也是一个接口,它有以下实现类:
  2. AbstractView,View的抽象类,定义了渲染流程,抽象了一些抽象方法,子类做特殊处理即可,大部分的实现类都继承于它
  3. VelocityView,支持Velocity框架生成的页面。
  4. FreeMarkerView,支持FreeMarker框架生成的页面。
  5. JstlView,支持生成jstl视图。
  6. RedirectView,支持生成页面跳转视图。
  7. MappingJackson2JsonView,输出Json的视图,使用Jackson库实现Json序列
  8. 视图的本质就是通过 Response对象,进行 write() 写出到客户端
public interface View {
    String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
    String PATH_VARIABLES = View.class.getName() + ".pathVariables";
    String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";
    //视图对应的的content-type
    @Nullable
    default String getContentType() {
        return null;
    }
    //渲染数据到视图
    void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
            throws Exception;
}

最终通知 doDispatch()方法,整体try-catch后,finally 代码块,调用拦截器进行最终通知。

遍历到的拦截器必须是 AsyncHandlerInterceptor 接口的实现类才行。

void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = interceptors.length - 1; i >= 0; i--) {
            if (interceptors[i] instanceof AsyncHandlerInterceptor) {
                try {
                    AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];
                    asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
                }
                catch (Throwable ex) {
                    logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);
                }
            }
        }

 springmvc设计模式

1 责任链模式

顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

何时使用:在处理消息的时候以过滤很多道。

如何解决:拦截的类都实现统一接口。

关键代码:Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。

应用实例: 

红楼梦中的"击鼓传花"。

JS 中的事件冒泡。

JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。

优点: 

降低耦合度。它将请求的发送者和接收者解耦。

简化了对象。使得对象不需要知道链的结构。

增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。

增加新的请求处理类很方便。

缺点: 

不能保证请求一定被接收。

系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。

可能不容易观察运行时的特征,有碍于除错。

使用场景:

 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。

在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

可动态指定一组对象处理请求。

注意事项:在 JAVA WEB 中遇到很多应用。

类结构图:

     如上图所示就是责任链模式,这个跟mybatis中的插件拦截器链比较像.跟java标准的责任链模式有那么点点的区别.tomcat中管道阀的执行顺序就是标准的责任链设计模式,而在mybatis和springmvc中采用的是这种的责任链设计模式.好了接下来我们看看原代码.

HandlerExecutionChain

public class HandlerExecutionChain {
   private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
   private final Object handler;
   private HandlerInterceptor[] interceptors;
   private List<HandlerInterceptor> interceptorList;
   private int interceptorIndex = -1;
   public HandlerExecutionChain(Object handler) {
      this(handler, (HandlerInterceptor[]) null);
   }

   public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) {
      if (handler instanceof HandlerExecutionChain) {
         HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
         this.handler = originalChain.getHandler();
         this.interceptorList = new ArrayList<>();
         CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
         CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
      }
      else {
         this.handler = handler;
         this.interceptors = interceptors;
      }
   }
   public Object getHandler() {
      return this.handler;
   }
   public void addInterceptor(HandlerInterceptor interceptor) {
      initInterceptorList().add(interceptor);
   }
   public void addInterceptors(HandlerInterceptor... interceptors) {
      if (!ObjectUtils.isEmpty(interceptors)) {
         CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());
      }
   }
   private List<HandlerInterceptor> initInterceptorList() {
      if (this.interceptorList == null) {
         this.interceptorList = new ArrayList<>();
         if (this.interceptors != null) {
            CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
         }
      }
      this.interceptors = null;
      return this.interceptorList;
   }
   @Nullable
   public HandlerInterceptor[] getInterceptors() {
      if (this.interceptors == null && this.interceptorList != null) {
         this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[0]);
      }
      return this.interceptors;
   }

   boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
         for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            if (!interceptor.preHandle(request, response, this.handler)) {
               triggerAfterCompletion(request, response, null);
               return false;
            }
            this.interceptorIndex = i;
         }
      }
      return true;
   }
   void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
         throws Exception {
      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
         for (int i = interceptors.length - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            interceptor.postHandle(request, response, this.handler, mv);
         }
      }
   }
   void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
         throws Exception {
      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
         for (int i = this.interceptorIndex; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            try {
               interceptor.afterCompletion(request, response, this.handler, ex);
            }
            catch (Throwable ex2) {
               logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
            }
         }
      }
   }
   void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
         for (int i = interceptors.length - 1; i >= 0; i--) {
            if (interceptors[i] instanceof AsyncHandlerInterceptor) {
               try {
                  AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];
                  asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
               }
               catch (Throwable ex) {
                  logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);
               }
}}}
   }
}
public interface HandlerInterceptor {
   default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {
      return true;
   }
   default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
         @Nullable ModelAndView modelAndView) throws Exception {
   }
   default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
         @Nullable Exception ex) throws Exception {
   }
}

MappedInterceptor

public final class MappedInterceptor implements HandlerInterceptor {
   private final String[] includePatterns;
   private final String[] excludePatterns;
   private final HandlerInterceptor interceptor;
   private PathMatcher pathMatcher;
   public MappedInterceptor(@Nullable String[] includePatterns, HandlerInterceptor interceptor) {
      this(includePatterns, null, interceptor);
   }
   public MappedInterceptor(@Nullable String[] includePatterns, @Nullable String[] excludePatterns,
         HandlerInterceptor interceptor) {

      this.includePatterns = includePatterns;
      this.excludePatterns = excludePatterns;
      this.interceptor = interceptor;
   }
   public MappedInterceptor(@Nullable String[] includePatterns, WebRequestInterceptor interceptor) {
      this(includePatterns, null, interceptor);
   }
   public MappedInterceptor(@Nullable String[] includePatterns, @Nullable String[] excludePatterns,
         WebRequestInterceptor interceptor) {

      this(includePatterns, excludePatterns, new WebRequestHandlerInterceptorAdapter(interceptor));
   }

   public String[] getPathPatterns() {
      return this.includePatterns;
   }
   public HandlerInterceptor getInterceptor() {
      return this.interceptor;
   }
   public boolean matches(String lookupPath, PathMatcher pathMatcher) {
      PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher);
      if (!ObjectUtils.isEmpty(this.excludePatterns)) {
         for (String pattern : this.excludePatterns) {
            if (pathMatcherToUse.match(pattern, lookupPath)) {
               return false;
            }
         }
      }
      if (ObjectUtils.isEmpty(this.includePatterns)) {
         return true;
      }
      for (String pattern : this.includePatterns) {
         if (pathMatcherToUse.match(pattern, lookupPath)) {
            return true;
         }
      }
      return false;
   }

   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {

      return this.interceptor.preHandle(request, response, handler);
   }

   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
         @Nullable ModelAndView modelAndView) throws Exception {

      this.interceptor.postHandle(request, response, handler, modelAndView);
   }

   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
         @Nullable Exception ex) throws Exception {

      this.interceptor.afterCompletion(request, response, handler, ex);
   }
}

如上代码所示就是责任链代码的例子,跟mybatis插件拦截器链的实现方式是如是一处的.

2 适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

我们通过下面的实例来演示适配器模式的使用。其中,音频播放器设备只能播放 mp3 文件,通过使用一个更高级的音频播放器来播放 vlc 和 mp4 文件。

      SpringMVC中的适配器到底是解决以上哪个问题的呢?我们来一步一步看看源码,看看Spring是怎么做的,首先我们找到前端控制器DispatcherServlet可以把它理解为适配器模式中的Client,它的主要作用在于通过处理映射器(HandlerMapper)来找到相应的Handler(即Controlle(宽泛的概念Controller,以及HttpRequestHandler,Servlet,等等)r),并执行Controller中相应的方法并返回ModelAndView,mappedHandler.getHandler()其实就是通过Spring容器中获取到的(宽泛的概念Controller,以及HttpRequestHandler,Servlet,等等)Controller

DispatcherServlet中的doDispatch

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   boolean multipartRequestParsed = false;
   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
   try {
      ModelAndView mv = null;
      Exception dispatchException = null;
      try {
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
         String method = request.getMethod();
         boolean isGet = "GET".equals(method);
         if (isGet || "HEAD".equals(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }
         applyDefaultViewName(processedRequest, mv);
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }catch (Exception ex) {
         dispatchException = ex;
      }catch (Throwable err) {
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}

  在DispatchServlert中的doDispatch方法中HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());此代码通过调用DispatchServlert 中getHandlerAdapter传入Controller(宽泛的概念Controller,以及HttpRequestHandler,Servlet,等等),来获取对应的HandlerAdapter 的实现子类,从而做到使得每一种Controller有一种对应的适配器实现类.

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   if (this.handlerAdapters != null) {
      for (HandlerAdapter adapter : this.handlerAdapters) {
         if (adapter.supports(handler)) {
            return adapter;
         } }
   }}

    返回后就能通过对应的适配实现类代理Controller(宽泛的概念Controller,以及HttpRequestHandler,Servlet,等等)来执行请求的方法mv = ha.handle(processedRequest, response, mappedHandler.getHandler());下面我们对常用的处理器适配器的实现做一些讲解.

HandlerAdapter 接口

public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

supports()方法传入处理器(宽泛的概念Controller,以及HttpRequestHandler,Servlet,等等)判断是否与当前适配器支持如果支持则从DispatcherServlet中的HandlerAdapter实现类中返回支持的适配器实现类。handler方法就是代理Controller来执行请求的方法并返回结果。

HttpRequestHandlerAdapter

public class HttpRequestHandlerAdapter implements HandlerAdapter {
   public boolean supports(Object handler) {
      return (handler instanceof HttpRequestHandler);
   }
   public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {
      ((HttpRequestHandler) handler).handleRequest(request, response);
      return null;
   }
  }

SimpleServletHandlerAdapter

public class SimpleServletHandlerAdapter implements HandlerAdapter {
   public boolean supports(Object handler) {
      return (handler instanceof Servlet);
   }
   public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {
      ((Servlet) handler).service(request, response);
      return null;
   }
}

SipleControllerHandlerAdapter

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
   public boolean supports(Object handler) {
      return (handler instanceof Controller);
   }
   public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {

      return ((Controller) handler).handleRequest(request, response);
   }
}

可以看到处理器(宽泛的概念Controller,以及HttpRequestHandler,Servlet,等等)的类型不同,有多重实现方式,那么调用方式就不是确定的,如果需要直接调用Controller方法,需要调用的时候就得不断是使用if else来进行判断是哪一种子类然后执行。那么如果后面要扩展(宽泛的概念Controller,以及HttpRequestHandler,Servlet,等等)Controller,就得修改原来的代码,这样违背了开闭原则(对修改关闭,对扩展开放)。Spring创建了一个适配器接口(HandlerAdapter)使得每一种处理器(宽泛的概念Controller,以及HttpRequestHandler,Servlet,等等)有一种对应的适配器实现类,让适配器代替(宽泛的概念Controller,以及HttpRequestHandler,Servlet,等等)执行相应的方法。这样在扩展Controller 时,只需要增加一个适配器类就完成了SpringMVC的扩展.

3 策略模式

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。

何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。

如何解决:将这些算法封装成一个一个的类,任意地替换。

关键代码:实现同一个接口。

应用实例: 

1. 诸葛亮的锦囊妙计,每一个锦囊就是一个策略。

2. 旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。

3. JAVA AWT 中的 LayoutManager。

优点: 

1.算法可以自由切换。

2.避免使用多重条件判断。

3.扩展性良好。

缺点:

  1、策略类会增多。

  2、所有策略类都需要对外暴露。

使用场景:

 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。

  1. 一个系统需要动态地在几种算法中选择一种。

3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

例如在DispatchServlet中的初始化组件中,用到了getDefaultStrategies方法,来决定不同组件的默认类型以实现组件的初始化操作。我们来看一下这个方法:getDefaultStrategy(context, LocaleResolver.class);

如上类图所示就是初始化的策略.可以看到组合进来的ViewResolver类视图渲染的过程其实有很渲染策略,比如xslt,比如freemarker.xml等等的渲染方式就是不同的策略.

LocaleResolver这个类有多个实现类,包括AcceptHeaderLocaleResolver、CookieLocaleResolver、FixedLocaleResolver等,对应了多种不同的处理方式,你可以决定用哪一种处理方式(绑定对应的组件就好了)。但试想一下,如果用if-else来决定用那种处理方式,光一个LocaleResolver,代码就将变得又长又臭,更何况springMVC还要初始化这么多其他组件。策略模式就用了面向对象的思想,用接口、继承、多态来代替if-else,增加了代码的可读性和可维护性。

当然因为不同的策略里面又有公共的部分所以这里还隐藏了一个模版方法的设计模式这里就不多说了.下一个设计模式我们抽一个经典的例子讲一下.这里重点看一下使用了策略模式的原代码.

DispatcherServlet初始化

protected void initStrategies(ApplicationContext context) {
   initMultipartResolver(context);
   initLocaleResolver(context);//使用策略模式
   initThemeResolver(context);//使用策略模式
   initHandlerMappings(context);//获取所有策略
   initHandlerAdapters(context);//获取所有策略
   initHandlerExceptionResolvers(context);//获取所有策略
   initRequestToViewNameTranslator(context);//使用策略模式
   initViewResolvers(context);//获取所有策略
   initFlashMapManager(context);//使用策略模式
}

加载所有策略并取其中一个策略

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<>(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(
                  "Unresolvable class definition for DispatcherServlet's default strategy class [" +
                  className + "] for interface [" + key + "]", err);
         }
      }
      return strategies;
   }
   else {
      return new LinkedList<>();
   }
}

如上代码所示,获取所有的strategies策略然后strategies.get(0)取第一个策略作为初始化的策略.以上就是springmvc中策略模式的经典引用.还有一个文件解析的地方.

4 模版方法模式

在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

主要解决:一些方法通用,却在每一个子类都重新写了这一方法。

何时使用:有一些通用的方法。

如何解决:将这些通用算法抽象出来。

关键代码:在抽象类实现,其他步骤在子类实现。

应用实例:

 1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。

  1. 西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。

3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。

优点:

 1、封装不变部分,扩展可变部分。

  1. 提取公共代码,便于维护。
  2. 行为由父类控制,子类实现。

缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

使用场景:

 1、有多个子类共有的方法,且逻辑相同。

2、重要的、复杂的方法,可以考虑作为模板方法。

注意事项:为防止恶意操作,一般模板方法都加上 final 关键词。

public interface HandlerMapping {
     HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

 getHandler()在DispatcherServlet中会被调用.

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
      implements HandlerMapping, Ordered, BeanNameAware {
 
  
   public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
      Object handler = getHandlerInternal(request);//调用子类的模版方法
      if (handler == null) {
         handler = getDefaultHandler();
      }
      if (handler == null) {
         return null;
      }
      // Bean name or resolved handler?
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = obtainApplicationContext().getBean(handlerName);
      }

      HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

      if (logger.isTraceEnabled()) {
         logger.trace("Mapped to " + handler);
      }
      else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
         logger.debug("Mapped to " + executionChain.getHandler());
      }

      if (CorsUtils.isCorsRequest(request)) {
         CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
         CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
         CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
         executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
      }
      return executionChain;
   }
   protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception; //模版方法
}
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {

   @Nullable
   private Object rootHandler;
   private boolean useTrailingSlashMatch = false;
   private boolean lazyInitHandlers = false;
   private final Map<String, Object> handlerMap = new LinkedHashMap<>();


   protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
      String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
      Object handler = lookupHandler(lookupPath, request);
      if (handler == null) {
         // We need to care for the default handler directly, since we need to
         // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
         Object rawHandler = null;
         if ("/".equals(lookupPath)) {
            rawHandler = getRootHandler();
         }
         if (rawHandler == null) {
            rawHandler = getDefaultHandler();
         }
         if (rawHandler != null) {
            // Bean name or resolved handler?
            if (rawHandler instanceof String) {
               String handlerName = (String) rawHandler;
               rawHandler = obtainApplicationContext().getBean(handlerName);
            }
            validateHandler(rawHandler, request);
            handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
         }
      }
      return handler;
   }
}
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

   @Override
   protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
      String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
      this.mappingRegistry.acquireReadLock();
      try {
         HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
         return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
      }
      finally {
         this.mappingRegistry.releaseReadLock();
      }
   }
}

如上代码所示就是运用了模版方法模式.好了关于springmvc中模版方法模式的讲解就到这里了,后面有机会再做补充.

5 组合模式

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。我们通过下面的实例来演示组合模式的用法。实例演示了一个组织中员工的层次结构。

意图:

将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

主解决:

它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

何时使用: 

  1. 您想表示对象的部分-整体层次结构(树形结构)。

2、您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。

关键代码:树枝内部组合该接口,并且含有内部属性 List,里面放 Component。

应用实例:

 1、算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作数也可以是操作数、操作符和另一个操作数。

 2、在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。

优点:

 1、高层模块调用简单。

 2、节点自由增加。

缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。

使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。

注意事项:定义时为具体类。

     如上是springmvc中应用配置的相关月函数.WebMvcConfigurer配置类其实是Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制,可以自定义一些Handler,Interceptor,ViewResolver,MessageConverter。基于java-based方式的spring mvc配置,需要创建一个配置类并实现WebMvcConfigurer 接口;

public interface WebMvcConfigurer {
   default void configurePathMatch(PathMatchConfigurer configurer) {
   }
   default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
   }
   default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
   }
   default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
   }
   default void addFormatters(FormatterRegistry registry) {
   }
   default void addInterceptors(InterceptorRegistry registry) {
   }
   default void addResourceHandlers(ResourceHandlerRegistry registry) {
   }
   default void addCorsMappings(CorsRegistry registry) {
   }
   default void addViewControllers(ViewControllerRegistry registry) {
   }
   default void configureViewResolvers(ViewResolverRegistry registry) {
   }
   default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
   }
   default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
   }
   default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
   }
   default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
   }
   default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
   }
   default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
   }
   default Validator getValidator() {
      return null;
   }
   default MessageCodesResolver getMessageCodesResolver() {
      return null;
   }
}

 树干

class WebMvcConfigurerComposite implements WebMvcConfigurer {
   private final List<WebMvcConfigurer> delegates = new ArrayList<>();
   public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
      if (!CollectionUtils.isEmpty(configurers)) {
         this.delegates.addAll(configurers);
      }
   }


   public void configurePathMatch(PathMatchConfigurer configurer) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configurePathMatch(configurer);
      }
   }
   public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configureContentNegotiation(configurer);
      }
   }
   public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configureAsyncSupport(configurer);
      }
   }
   public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configureDefaultServletHandling(configurer);
      }
   }
   public void addFormatters(FormatterRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addFormatters(registry);
      }
   }
   public void addInterceptors(InterceptorRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addInterceptors(registry);
      }
   }
   public void addResourceHandlers(ResourceHandlerRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addResourceHandlers(registry);
      }
   }
   public void addCorsMappings(CorsRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addCorsMappings(registry);
      }
   }
   public void addViewControllers(ViewControllerRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addViewControllers(registry);
      }
   }
   public void configureViewResolvers(ViewResolverRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configureViewResolvers(registry);
      }
   }

   public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addArgumentResolvers(argumentResolvers);
      }
   }
   public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addReturnValueHandlers(returnValueHandlers);
      }
   }
   public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configureMessageConverters(converters);
      }
   }
   public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.extendMessageConverters(converters);
      }
   }
   public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configureHandlerExceptionResolvers(exceptionResolvers);
      }
   }
   public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.extendHandlerExceptionResolvers(exceptionResolvers);
      }
   }
   public Validator getValidator() {
      Validator selected = null;
      for (WebMvcConfigurer configurer : this.delegates) {
         Validator validator = configurer.getValidator();
         if (validator != null) {
            if (selected != null) {
               throw new IllegalStateException("No unique Validator found: {" +
                     selected + ", " + validator + "}");
            }
            selected = validator;
         }
      }
      return selected;
   }
   public MessageCodesResolver getMessageCodesResolver() {
      MessageCodesResolver selected = null;
      for (WebMvcConfigurer configurer : this.delegates) {
         MessageCodesResolver messageCodesResolver = configurer.getMessageCodesResolver();
         if (messageCodesResolver != null) {
            if (selected != null) {
               throw new IllegalStateException("No unique MessageCodesResolver found: {" +
                     selected + ", " + messageCodesResolver + "}");
            }
            selected = messageCodesResolver;
         }
      }
      return selected;
   }
}

树叶

@EnableWebMvc
static abstract class AbstractWebConfig implements WebMvcConfigurer {
   @Bean
   public SampleController sampleController() {
      return new SampleController();
   }
}
@Configuration
@EnableWebMvc
static class RequestPartTestConfig implements WebMvcConfigurer {
   @Bean
   public RequestPartTestController controller() {
      return new RequestPartTestController();
   }
}

弃用了struts,用spring mvc框架做了几个项目,感觉都不错,而且使用了注解方式,可以省掉一大堆配置文件。本文主要介绍使用注解方式配置的spring mvc,之前写的spring3.0 mvc和rest小例子没有介绍到数据层的内容,现在这一篇补上。下面开始贴代码。 文中用的框架版本:spring 3,hibernate 3,没有的,自己上网下。 先说web.xml配置: [java] view plaincopy 01.<?xml version="1.0" encoding="UTF-8"?> 02.<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> 03. <display-name>s3h3</display-name> 04. <context-param> 05. <param-name>contextConfigLocation</param-name> 06. <param-value>classpath:applicationContext*.xml</param-value> 07. </context-param> 08. <listener> 09. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 10. </listener> 11. 12. <servlet> 13. <servlet-name>spring</servlet-name> 14. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 15. <load-on-startup>1</load-on-startup> 16. </servlet> 17. <servlet-mapping> 18. <servlet-name>spring</servlet-name> <!-- 这里在配成spring,下边也要写一个名为spring-servlet.xml的文件,主要用来配置它的controller --> 19. <url-pattern>*.do</url-pattern> 20. </servlet-mapping> 21. <welcome-file-list> 22. <welcome-file>index.jsp</welcome-file> 23. </welcome-file-list> 24.</web-app> spring-servlet,主要配置controller的信息 [java] view plaincopy 01.<?xml version="1.0" encoding="UTF-8"?> 02. <beans 03. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 04. xmlns:context="http://www.springframework.org/schema/context" 05. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 06. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 07. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 08. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> 09. 10. <context:annotation-config /> 11. <!-- 把标记了@Controller注解的类转换为bean --> 12. <context:component-scan base-package="com.mvc.controller" /> 13. <!-- 启动Spring MVC的注解功能,完成请求和注解POJO的映射 --> 14. <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" /> 15. 16. <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 --> 17. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 18. p:prefix="/WEB-INF/view/" p:suffix=".jsp" /> 19. 20. <bean id="multipartResolver" 21. class="org.springframework.web.multipart.commons.CommonsMultipartResolver" 22. p:defaultEncoding="utf-8" /> 23. </beans> applicationContext.xml代码 [java] view plaincopy 01.<?xml version="1.0" encoding="UTF-8"?> 02.<beans 03. xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 04. xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" 05. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 06. xsi:schemaLocation=" 07. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 08. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 09. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 10. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> 11. 12. <context:annotation-config /> 13. <context:component-scan base-package="com.mvc" /> <!-- 自动扫描所有注解该路径 --> 14. 15. <context:property-placeholder location="classpath:/hibernate.properties" /> 16. 17. <bean id="sessionFactory" 18. class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> 19. <property name="dataSource" ref="dataSource" /> 20. <property name="hibernateProperties"> 21. <props> 22. <prop key="hibernate.dialect">${dataSource.dialect}</prop> 23. <prop key="hibernate.hbm2ddl.auto">${dataSource.hbm2ddl.auto}</prop> 24. <prop key="hibernate.hbm2ddl.auto">update</prop> 25. </props> 26. </property> 27. <property name="packagesToScan"> 28. <list> 29. <value>com.mvc.entity</value><!-- 扫描实体类,也就是平时所说的model --> 30. </list> 31. </property> 32. </bean> 33. 34. <bean id="transactionManager" 35. class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 36. <property name="sessionFactory" ref="sessionFactory" /> 37. <property name="dataSource" ref="dataSource" /> 38. </bean> 39. 40. <bean id="dataSource" 41. class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 42. <property name="driverClassName" value="${dataSource.driverClassName}" /> 43. <property name="url" value="${dataSource.url}" /> 44. <property name="username" value="${dataSource.username}" /> 45. <property name="password" value="${dataSource.password}" /> 46. </bean> 47. <!-- Dao的实现 --> 48. <bean id="entityDao" class="com.mvc.dao.EntityDaoImpl"> 49. <property name="sessionFactory" ref="sessionFactory" /> 50. </bean> 51. <tx:annotation-driven transaction-manager="transactionManager" /> 52. <tx:annotation-driven mode="aspectj"/> 53. 54. <aop:aspectj-autoproxy/> 55.</beans> hibernate.properties数据库连接配置 [java] view plaincopy 01.dataSource.password=123 02.dataSource.username=root 03.dataSource.databaseName=test 04.dataSource.driverClassName=com.mysql.jdbc.Driver 05.dataSource.dialect=org.hibernate.dialect.MySQL5Dialect 06.dataSource.serverName=localhost:3306 07.dataSource.url=jdbc:mysql://localhost:3306/test 08.dataSource.properties=user=${dataSource.username};databaseName=${dataSource.databaseName};serverName=${dataSource.serverName};password=${dataSource.password} 09.dataSource.hbm2ddl.auto=update 配置已经完成,下面开始例子 先在数据库建表,例子用的是mysql数据库 [java] view plaincopy 01.CREATE TABLE `test`.`student` ( 02. `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 03. `name` varchar(45) NOT NULL, 04. `psw` varchar(45) NOT NULL, 05. PRIMARY KEY (`id`) 06.) 建好表后,生成实体类 [java] view plaincopy 01.package com.mvc.entity; 02. 03.import java.io.Serializable; 04. 05.import javax.persistence.Basic; 06.import javax.persistence.Column; 07.import javax.persistence.Entity; 08.import javax.persistence.GeneratedValue; 09.import javax.persistence.GenerationType; 10.import javax.persistence.Id; 11.import javax.persistence.Table; 12. 13.@Entity 14.@Table(name = "student") 15.public class Student implements Serializable { 16. private static final long serialVersionUID = 1L; 17. @Id 18. @Basic(optional = false) 19. @GeneratedValue(strategy = GenerationType.IDENTITY) 20. @Column(name = "id", nullable = false) 21. private Integer id; 22. @Column(name = "name") 23. private String user; 24. @Column(name = "psw") 25. private String psw; 26. public Integer getId() { 27. return id; 28. } 29. public void setId(Integer id) { 30. this.id = id; 31. } 32. 33. public String getUser() { 34. return user; 35. } 36. public void setUser(String user) { 37. this.user = user; 38. } 39. public String getPsw() { 40. return psw; 41. } 42. public void setPsw(String psw) { 43. this.psw = psw; 44. } 45.} Dao层实现 [java] view plaincopy 01.package com.mvc.dao; 02. 03.import java.util.List; 04. 05.public interface EntityDao { 06. public List<Object> createQuery(final String queryString); 07. public Object save(final Object model); 08. public void update(final Object model); 09. public void delete(final Object model); 10.} [java] view plaincopy 01.package com.mvc.dao; 02. 03.import java.util.List; 04. 05.import org.hibernate.Query; 06.import org.springframework.orm.hibernate3.HibernateCallback; 07.import org.springframework.orm.hibernate3.support.HibernateDaoSupport; 08. 09.public class EntityDaoImpl extends HibernateDaoSupport implements EntityDao{ 10. public List<Object> createQuery(final String queryString) { 11. return (List<Object>) getHibernateTemplate().execute( 12. new HibernateCallback<Object>() { 13. public Object doInHibernate(org.hibernate.Session session) 14. throws org.hibernate.HibernateException { 15. Query query = session.createQuery(queryString); 16. List<Object> rows = query.list(); 17. return rows; 18. } 19. }); 20. } 21. public Object save(final Object model) { 22. return getHibernateTemplate().execute( 23. new HibernateCallback<Object>() { 24. public Object doInHibernate(org.hibernate.Session session) 25. throws org.hibernate.HibernateException { 26. session.save(model); 27. return null; 28. } 29. }); 30. } 31. public void update(final Object model) { 32. getHibernateTemplate().execute(new HibernateCallback<Object>() { 33. public Object doInHibernate(org.hibernate.Session session) 34. throws org.hibernate.HibernateException { 35. session.update(model); 36. return null; 37. } 38. }); 39. } 40. public void delete(final Object model) { 41. getHibernateTemplate().execute(new HibernateCallback<Object>() { 42. public Object doInHibernate(org.hibernate.Session session) 43. throws org.hibernate.HibernateException { 44. session.delete(model); 45. return null; 46. } 47. }); 48. } 49.} Dao在applicationContext.xml注入 <bean id="entityDao" class="com.mvc.dao.EntityDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> Dao只有一个类的实现,直接供其它service层调用,如果你想更换为其它的Dao实现,也只需修改这里的配置就行了。 开始写view页面,WEB-INF/view下新建页面student.jsp,WEB-INF/view这路径是在spring-servlet.xml文件配置的,你可以配置成其它,也可以多个路径。student.jsp代码 [xhtml] view plaincopy 01.<%@ page language="java" contentType="text/html; charset=UTF-8" 02. pageEncoding="UTF-8"%> 03.<%@ include file="/include/head.jsp"%> 04.<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 05.<html> 06.<head> 07.<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 08.<title>添加</title> 09.<mce:script language="javascript" src="<%=request.getContextPath()%><!-- 10./script/jquery.min.js"> 11.// --></mce:script> 12.<mce:style><!-- 13.table{ border-collapse:collapse; } 14.td{ border:1px solid #f00; } 15.--></mce:style><style mce_bogus="1">table{ border-collapse:collapse; } 16.td{ border:1px solid #f00; }</style> 17.<mce:script type="text/javascript"><!-- 18.function add(){ 19. [removed].href="<%=request.getContextPath() %>/student.do?method=add"; 20.} 21. 22.function del(id){ 23.$.ajax( { 24. type : "POST", 25. url : "<%=request.getContextPath()%>/student.do?method=del&id;=" + id, 26. dataType: "json", 27. success : function(data) { 28. if(data.del == "true"){ 29. alert("删除成功!"); 30. $("#" + id).remove(); 31. } 32. else{ 33. alert("删除失败!"); 34. } 35. }, 36. error :function(){ 37. alert("网络连接出错!"); 38. } 39.}); 40.} 41.// --></mce:script> 42.</head> 43.<body> 44. 45.<input id="add" type="button" value="添加"/> 46.<table > 47. <tr> 48. <td>序号</td> 49. <td>姓名</td> 50. <td>密码</td> 51. <td>操作</td> 52. </tr> 53. <c:forEach items="${list}" var="student"> 54. <tr id="<c:out value="${student.id}"/>"> 55. <td><c:out value="${student.id}"/></td> 56. <td><c:out value="${student.user}"/></td> 57. <td><c:out value="${student.psw}"/></td> 58. <td> 59. <input type="button" value="编辑"/> 60. <input type="button" value="${student.id}"/>')" value="删除"/> 61. </td> 62. </tr> 63. </c:forEach> 64. 65.</table> 66.</body> 67.</html> student_add.jsp [xhtml] view plaincopy 01.<%@ page language="java" contentType="text/html; charset=UTF-8" 02. pageEncoding="UTF-8"%> 03.<%@ include file="/include/head.jsp"%> 04.<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 05.<html> 06.<head> 07.<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 08.<title>学生添加</title> 09.<mce:script type="text/javascript"><!-- 10.function turnback(){ 11. [removed].href="<%=request.getContextPath() %>/student.do"; 12.} 13.// --></mce:script> 14.</head> 15.<body> 16.<form method="post" action="<%=request.getContextPath() %>/student.do?method=save"> 17.<div><c:out value="${addstate}"></c:out></div> 18.<table> 19. <tr><td>姓名</td><td><input id="user" name="user" type="text" /></td></tr> 20. <tr><td>密码</td><td><input id="psw" name="psw" type="text" /></td></tr> 21. <tr><td colSpan="2" align="center"><input type="submit" value="提交"/><input type="button" value="返回" /> </td></tr> 22.</table> 23. 24.</form> 25.</body> 26.</html> controller类实现,只需把注解写上,spring就会自动帮你找到相应的bean,相应的注解标记意义,不明白的,可以自己查下@Service,@Controller,@Entity等等的内容。 [java] view plaincopy 01.package com.mvc.controller; 02. 03.import java.util.List; 04. 05.import javax.servlet.http.HttpServletRequest; 06.import javax.servlet.http.HttpServletResponse; 07. 08.import org.apache.commons.logging.Log; 09.import org.apache.commons.logging.LogFactory; 10.import org.springframework.beans.factory.annotation.Autowired; 11.import org.springframework.stereotype.Controller; 12.import org.springframework.ui.ModelMap; 13.import org.springframework.web.bind.annotation.RequestMapping; 14.import org.springframework.web.bind.annotation.RequestMethod; 15.import org.springframework.web.bind.annotation.RequestParam; 16.import org.springframework.web.servlet.ModelAndView; 17. 18.import com.mvc.entity.Student; 19.import com.mvc.service.StudentService; 20. 21.@Controller 22.@RequestMapping("/student.do") 23.public class StudentController { 24. protected final transient Log log = LogFactory 25. .getLog(StudentController.class); 26. @Autowired 27. private StudentService studentService; 28. public StudentController(){ 29. 30. } 31. 32. @RequestMapping 33. public String load(ModelMap modelMap){ 34. List<Object> list = studentService.getStudentList(); 35. modelMap.put("list", list); 36. return "student"; 37. } 38. 39. @RequestMapping(params = "method=add") 40. public String add(HttpServletRequest request, ModelMap modelMap) throws Exception{ 41. return "student_add"; 42. } 43. 44. @RequestMapping(params = "method=save") 45. public String save(HttpServletRequest request, ModelMap modelMap){ 46. String user = request.getParameter("user"); 47. String psw = request.getParameter("psw"); 48. Student st = new Student(); 49. st.setUser(user); 50. st.setPsw(psw); 51. try{ 52. studentService.save(st); 53. modelMap.put("addstate", "添加成功"); 54. } 55. catch(Exception e){ 56. log.error(e.getMessage()); 57. modelMap.put("addstate", "添加失败"); 58. } 59. 60. return "student_add"; 61. } 62. 63. @RequestMapping(params = "method=del") 64. public void del(@RequestParam("id") String id, HttpServletResponse response){ 65. try{ 66. Student st = new Student(); 67. st.setId(Integer.valueOf(id)); 68. studentService.delete(st); 69. response.getWriter().print("{/"del/":/"true/"}"); 70. } 71. catch(Exception e){ 72. log.error(e.getMessage()); 73. e.printStackTrace(); 74. } 75. } 76.} service类实现 [java] view plaincopy 01.package com.mvc.service; 02. 03.import java.util.List; 04. 05.import org.springframework.beans.factory.annotation.Autowired; 06.import org.springframework.stereotype.Service; 07.import org.springframework.transaction.annotation.Transactional; 08. 09.import com.mvc.dao.EntityDao; 10.import com.mvc.entity.Student; 11. 12.@Service 13.public class StudentService { 14. @Autowired 15. private EntityDao entityDao; 16. 17. @Transactional 18. public List<Object> getStudentList(){ 19. StringBuffer sff = new StringBuffer(); 20. sff.append("select a from ").append(Student.class.getSimpleName()).append(" a "); 21. List<Object> list = entityDao.createQuery(sff.toString()); 22. return list; 23. } 24. 25. public void save(Student st){ 26. entityDao.save(st); 27. } 28. public void delete(Object obj){ 29. entityDao.delete(obj); 30. } 31.} OK,例子写完。有其它业务内容,只需直接新建view,并实现相应comtroller和service就行了,配置和dao层的内容基本不变,也就是每次只需写jsp(view),controller和service调用dao就行了。 怎样,看了这个,spring mvc是不是比ssh实现更方便灵活。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值