springmvc深入理解

本文参考文章https://www.cnblogs.com/baiduligang/p/4247164.html。感谢作者提供的技术知识,我优化了一下。

目录 
一、前言
二、spring mvc 核心类与接口
三、spring mvc 核心流程图

四、spring mvc DispatcherServlet说明

五、spring mvc 父子上下文的说明

六、springMVC-mvc.xml 配置文件片段讲解 
七、spring mvc 如何访问到静态的文件,如jpg,js,css

八、spring mvc 请求如何映射到具体的Action中的方法

九、 spring mvc 中的拦截器:
十、 spring mvc 如何使用拦截器

十一、 spring mvc 如何实现全局的异常处理

十二、 spring mvc 如何把全局异常记录到日志中

十三、 如何给spring3 MVC中的Action做JUnit单元测试

十四、 spring mvc 转发与重定向 (带参数重定向)

十五、 spring mvc 处理ajax请求

十六、 spring mvc 关于写几个配置文件的说明 

十七、 spring mvc 如何取得Spring管理的bean

十八、 spring mvc 多视图控制器

十九、 到底做了什么工作 
二十、 本文中springMVC.xml配置文件是核心,这里给一个下载地址

很多人对于刚开始工作经验时间不长的,大家很多地方不理解。我希望可以用白话让大家更可以理解。

springmvc我们为什么要学习呢?这可能大家有很多答案

我个人认为,springmvc学习起来很简单,比Struts2学习起来更快捷,更方便,更简洁。作为程序员,我们当然希望我们把更多的经历放在业务上。让我来比一下Struts2和springmvc吧。

springmvc的优点

1.首先springmvc的入口是Servlet

2.springMVC是基于方法开发的,它默认单例模式。它本身也可以为多例模式提供了方式,只需要我们在控制类里面加@scope(value="prototype")即可。它本身默认为单例模式

3.springmvc是基于方法开发的

4.springmvc的拦截器其实就是spring的AOP的一种实现方式

5.springmvc本身可以跟spring无缝衔接。

Struts2的优点

1.首先Struts2的入口是filter

2.它本身是基本类开发的。所谓基本类指的就是当你请求时Controller,Controller里的所有方法共享参数。

3.Struts2在集成spring时配置文件繁琐复杂,不利于开发。

 

 

springmvc深入理解_第1张图片

 

以上图片引用原文作者

本人针对引用作者的原图我来补充一下

1.DispatcherServlet中会通过ServletContent(上下文对象:上下文对象的作用就是在程序初始化时,将一些信息注入到这个对象中),获取一些信息存放在ApplicationContext这个工具类中,ApplicationContext这个工具类底层采用的单例模式。所以当我们(Object)ApplicationContext.getBean("..xml")就可以得到我们注入bean的对像了。

2.DispatcherServlet-HandlerMapping(接口)-DefaultAnnotationHandlerMapping通过注解,把一个URL映射到Controller类上(order=0)或者SimpleUrlHandlerMapping (order=2147483646) 通过配置文件,把一个URL映射到Controller。如果找到控制器(Controller)-HandlerAdapter(接口适配器:就是找到你要请求那个方法的中转站)-AnnotationMethodHandlerAdapter类,通过注解或者SimpleUrlMethodHandlerAdapter(order=2147483646) 通过配置文件,把一个URL映射到Controller,把一个URL映射到Controller类的方法上,然后在查看该方法是否存在拦截器(-HandlerInterceptor (接口))-Controller-biz-dao-biz-Controller-ViewResolver接口-UrlBasedViewResolver类 通过配置文件,把一个视图名交给到一个View来处理或者InternalResourceViewResolver类,比上面的类,加入了JSTL的支持。在到View接口(-View接口)。以上是我对springmvc的原理过程的总结

//上面的-代表下一步走到那的意思(即交给谁来处理),order分优先级(值越小,代表优先级越高)

 

  1. <web-app>  
  2.     <servlet>  
  3.         <servlet-name>exampleservlet-name>  
  4.         <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>  
  5.         <load-on-startup>1load-on-startup>  
  6.     servlet>  
  7.     <servlet-mapping>  
  8.         <servlet-name>exampleservlet-name>  
  9.         <url-pattern>*.formurl-pattern>  
  10.     servlet-mapping>  
  11. web-app>  

 

 1.1是启动顺序,让这个Servlet随Servletp容器一起启动。

    *.form 会拦截*.form结尾的请求。

2.example这个Servlet的名字是example,可以有多个DispatcherServlet,是通过名字来区分的。每一个DispatcherServlet有自己的WebApplicationContext上下文对象。同时保存的ServletContext中和Request对象中,关于key,以后说明。

3.在DispatcherServlet的初始化过程中,框架会在web应用的 WEB-INF文件夹下寻找名为[servlet-name]-servlet.xml 的配置文件,生成文件中定义的bean。

 

Xml代码 复制代码 收藏代码

  1. <servlet>  
  2.     <servlet-name>springMVCservlet-name>  
  3.     <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>  
  4.     <init-param>  
  5.         <param-name>contextConfigLocationparam-name>  
  6.         <param-value>classpath*:/springMVC.xmlparam-value>  
  7.     init-param>  
  8.     <load-on-startup>1load-on-startup>  
  9. servlet>  
  10. <servlet-mapping>  
  11.     <servlet-name>springMVCservlet-name>  
  12.     <url-pattern>/url-pattern>  
  13. servlet-mapping>  

指明了配置文件的文件名,不使用默认配置文件名,而使用springMVC.xml配置文件。

其中**.xml 这里可以使用多种写法
1、不写,使用默认值:/WEB-INF/-servlet.xml
2、/WEB-INF/classes/springMVC.xml
3、classpath*:springMVC-mvc.xml
4、多个值用逗号分隔

Servlet拦截匹配规则可以自已定义,拦截哪种URL合适?

当映射为@RequestMapping("/user/add")时,为例:

 

1、拦截*.do、*.htm, 例如:/user/add.do

这是最传统的方式,最简单也最实用。不会导致静态文件(jpg,js,css)被拦截。

 

2、拦截/,例如:/user/add

可以实现现在很流行的REST风格。很多互联网类型的应用很喜欢这种风格的URL。

弊端:会导致静态文件(jpg,js,css)被拦截后不能正常显示。想实现REST风格,事情就是麻烦一些。后面有解决办法还算简单。

3、拦截/*,这是一个错误的方式,请求可以走到Action中,但转到jsp时再次被拦截,不能访问到jsp。

如果想实现REST风格,办法我正可以先想一下,第一种是不是在我们进入spring容器之前,让Tomcat的Servlet我们先解析静态资源。第二种是让spring提供的Servlet来解析静态资源.

第一种自然可以想到在web.xml来启动Tomcat的Servlet来解析

 

  1. <servlet-mapping>  
  2.     <servlet-name>defaultservlet-name>  
  3.     <url-pattern>*.cssurl-pattern>  
  4. servlet-mapping>  
  5. <servlet-mapping>  
  6.     <servlet-name>defaultservlet-name>  
  7.     <url-pattern>*.jsurl-pattern>  
  8. servlet-mapping>  
  9. <servlet-mapping>  
  10.     <servlet-name>defaultservlet-name>  
  11.     <url-pattern>*.jpgurl-pattern>  
  12. servlet-mapping>span>  

不同的 servlet 容器/应用服务器,处理这些静态资源的 servlet 的名字不大一样:
Tomcat, Jetty, JBoss, and GlassFish:默认 Servlet 名字为 "default";
Google App Engine:默认 Servlet 名字为 "_ah_default";
Resin:默认 Servlet 名字为 "resin-file";
WebLogic:默认 Servlet 名字为 "FileServlet";
WebSphere:默认 Servlet 名字为 "SimpleFileServlet";

第二种自然启动spring提供的Servlet来解析,把自然肯定在对应的springmvc.xml(举例文件)里了 

 

  1.   
  2. <mvc:resources mapping="/css/**" location="/css/" />  
  3. <mvc:resources mapping="/image/**" location="/image/" />  
  4. <mvc:resources mapping="/js/**" location="/js/" />  

mapping:表示页面中使用到的路径

location:表示从该路径下进行查找

以上的配置:css,image,js放置在webapp目录下,页面中使用时:localhost:8080/css/aaa.css

注意:对于方法二,在tomcat下可以正常使用,但在resin下静态资源仍然找不到,最后发现,直接使用如下的配置就可以兼容tomcat和resin了

 

  1. <mvc:default-servlet-handler />  

这种配置只支持有限的一些容器,对于spring不支持的容器,需要配置defaule-servlet-name进行指定。

对于不懂得可以看一下这个地址https://blog.csdn.net/u012730299/article/details/51872704这上面有很好的讲解

以上以上是对Rest请求方式总结,注明Rest是spring3.0以后的版本才有的。

在spring请求方式之前我们请求方式get和post2种,但是在spring3.0之后新增put和delete请求方式,这是针对spring的reset请求方式支持新增的,而且也新增注解@PathVariable来配合reset参数请求

 

  1.  /** 
  2.      * PathVariable:注解方法参数,表示该参数变量值是通过URL传递的 
  3.      * URL中必须包含该参数变量名,变量名需要相同才能完成变量传递注入 
  4.      */  
  5.     @RequestMapping(value = "/deleteUser/{id}", method = RequestMethod.DELETE, produces = "application/json")  
  6.     @ResponseBody  
  7.     public String deleteUser(@PathVariable Integer id) {  
  8.         boolean flag = userService.deleteUser(id);  
  9.         if (flag) {  
  10.             message = "success";  
  11.         } else {  
  12.             message = "error";  
  13.         }  
  14.         return message;  
  15.     } 

 以上是对Rest总结,注明Rest是spring3.0以后的版本才有的。

 

  1.   
  2.     <context:component-scan base-package="com.app,com.core,JUnit4" >context:component-scan>  
  1.    
  2.     <mvc:annotation-driven />  

扫描指定的包中的类上的注解,常用的注解有:

@Controller 声明Action组件
@Service    声明Service组件    @Service("myMovieLister") 
@Repository 声明Dao组件
@Component   泛指组件, 当不好归类时. 
@RequestMapping("/menu")  请求映射
@Resource  用于注入,( j2ee提供的 ) 默认按名称装配,@Resource(name="beanName") 
@Autowired 用于注入,(srping提供的) 默认按类型装配 
@Transactional( rollbackFor={Exception.class}) 事务管理
@ResponseBody
@Scope("prototype")   设定bean的作用域

 

是一种简写形式,完全可以手动配置替代这种简写形式,简写形式可以让初学都快速应用默认配置方案。 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的。
并提供了:数据绑定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,读写XML的支持(JAXB),读写JSON的支持(Jackson)。
后面,我们处理响应ajax请求时,就使用到了对json的支持。

后面,对action写JUnit单元测试时,要从spring IOC容器中取DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,来完成测试,取的时候要知道是这一句注册的这两个bean。

 

大家思考一个问题springmvc怎么知道http方式请求时,它凭什么知道我们要去那个后台的Controller,那个方法呢?

1.很简单因为有HanflerMapper接口下有两个实现类DefaultAnnotationHandlerMapping(通过注解的方式)和SimpleUrlHandlerMapping(通过配置文件里的url)来找到相应的Controller。

2.我们该找那个具体方法了,我提到过适配器概念其实就是方法请求中转站。HandlerAdapter这个接口也有2个实现类,

AnnotationMethodHandlerAdapter类(通过注解)和SimpleUrlMethodHandlerAdapter(通过配置文件里的url

是一种简写形式。通过看前面的大图,知道,我们可以配置多个HandlerMapping。会为每一个HandlerMapping,注入一个拦截器。其实我们也可以手动配置为每个HandlerMapping注入一个拦截器。

 

  1.   
  2.     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  3.         <property name="prefix" value="/WEB-INF/jsp/"/>  
  4.         <property name="suffix" value=".jsp"/>  
  5.         <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />  
  6.     bean>  

以上是我讲springmvc.xml文件分解给大家展示。目的是让大家先有编程思想,而不是我先给大家。这样方便大家自己思考学习起来也深刻。最后springmvc.xml简单的,应该配置一些东西大家有一个总体的思想。

 

现在让大家来思考一下拦截器,它的作用,可以做一些什么事情,它是怎么找到要具体拦截那个方法吧,不能全都拦截对吧?

我们在现实中是不是都碰到过看例如爱奇艺里我可以浏览,但是当我们想看会员电影时大概只能看5分钟。然后就提示请看到会员。

1.拦截器可以做权限控制,日志处理,监控系统性能等。这个就是拦截可以做一些什么事

2.springmvc的拦截器就是Aop很好的体现和应用。关于AOP在spring深入理解会细讲

它是知道要拦截那些方法对吧。那它肯定跟DispatcherServlet有关,而且怎么知道的呢?

3.在springmvc.xml里配置的

 

  
   
       
      
        
        
       
        
       
          
      
 

Spring为我们提供了:
org.springframework.web.servlet.HandlerInterceptor接口,

org.springframework.web.servlet.handler.HandlerInterceptorAdapter适配器,
实现这个接口或继承此类,可以非常方便的实现自己的拦截器。

有以下三个方法:

 Action之前执行:
 public boolean preHandle(HttpServletRequest request,
   HttpServletResponse response, Object handler);
 生成视图之前执行
 public void postHandle(HttpServletRequest request,
   HttpServletResponse response, Object handler,
   ModelAndView modelAndView);
 
最后执行,可用于释放资源
 public void afterCompletion(HttpServletRequest request,
   HttpServletResponse response, Object handler, Exception ex)
 
 
分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面) 
在preHandle中,可以进行编码、安全控制等处理; 
在postHandle中,有机会修改ModelAndView; 
在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。 
参数中的Object handler是下一个拦截器。

springmvc深入理解_第2张图片

这张图片是我粘贴别人的出处地址:https://www.cnblogs.com/Joke-Jay/p/7588580.html

如果出现错误了我应该怎么排查呢?我们应该用logger日志处理工具来处理吧。那就需要我们继承吧,我现在给大家提供一种简单方式。

1.加入日志jar包pom依赖

2.在resources目录下添加日志文件log4j.properties

3.配置web.xml日志监听

 

4:使用

 

1.

    org.slf4j
    slf4j-log4j12
    1.7.5
2.#LOG_DIR:/usr/local/.../logs
LOG_DIR = E:\\logs //这里是日志文件的路径
#Level:ERROR,WARN,INFO,DEBUG
log4j.rootLogger = DEBUG,Console,FileInfo,FileError

log4j.appender.Console = org.apache.log4j.ConsoleAppender
log4j.appender.Console.Threshold = DEBUG
log4j.appender.Console.ImmediateFlush = true
log4j.appender.Console.Target = SYSTEM_OUT
log4j.appender.Console.layout = org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

log4j.appender.FileInfo = org.apache.log4j.DailyRollingFileAppender
log4j.appender.FileInfo.Threshold = INFO
log4j.appender.FileInfo.ImmediateFlush = true
log4j.appender.FileInfo.Append = true
log4j.appender.FileInfo.DatePattern = '_'yyyy-MM-dd'.log'
log4j.appender.FileInfo.encoding=UTF-8
log4j.appender.FileInfo.File = ${LOG_DIR}/info
log4j.appender.FileInfo.layout = org.apache.log4j.PatternLayout
log4j.appender.FileInfo.layout.ConversionPattern = [%-5p][%d{ISO8601}]%m%n

log4j.appender.FileError = org.apache.log4j.DailyRollingFileAppender
log4j.appender.FileError.Threshold = ERROR
log4j.appender.FileError.ImmediateFlush = true
log4j.appender.FileError.Append = true
log4j.appender.FileError.DatePattern = '_'yyyy-MM-dd'.log'
log4j.appender.FileError.encoding=UTF-8
log4j.appender.FileError.File = ${LOG_DIR}/error
log4j.appender.FileError.layout = org.apache.log4j.PatternLayout
log4j.appender.FileError.layout.ConversionPattern = [%-5p][%d{ISO8601}]%m%n

log4j.appender.cn.yivi.service.pay = info,pay
log4j.additivity.cn.yivi.service.pay = false
log4j.appender.pay = org.apache.log4j.DailyRollingFileAppender
log4j.appender.pay.Threshold = INFO
log4j.appender.pay.ImmediateFlush = true
log4j.appender.pay.Append = true
log4j.appender.pay.DatePattern = '_'yyyy-MM-dd'.log'
log4j.appender.pay.encoding=UTF-8
log4j.appender.pay.File = ${LOG_DIR}/pay
log4j.appender.pay.layout = org.apache.log4j.PatternLayout
log4j.appender.pay.layout.ConversionPattern = [%-5p][%d{ISO8601}]%m%n
3.
  log4jConfigLocation
  classpath:log4j.properties



  org.springframework.web.util.Log4jConfigListener

4.final Logger logger = LoggerFactory.getLogger(UserInfoService.class);

 

以上是我对日志的配置过程的总结,方式很多。我找了一个最方便,最常用的方式

 

大家是不是我们错误的日志是不可能给用户看呢对吧,一旦出现了错误。我们只能给用户看统一的错误页面,不是一个错误对应一个错误页面。我们也做不到。所有做一个统一的错误页面,一旦发生异常我们统一给用户看的,在springmvc中我该怎么做呢?

这里提供给大家2种方式

1.web.xml增加标签项即可。注:其实只是在原有的springmvc配置的基础上增加标签项即可。

  springmvc深入理解_第3张图片

 

web.xml  

复制代码




  Archetype Created Web Application

  
    dispatcher
    org.springframework.web.servlet.DispatcherServlet
    1
  
  
  
    dispatcher
    /
  

  
  
  encodingFilter
  org.springframework.web.filter.CharacterEncodingFilter
  
    encoding
    UTF-8
  
  
  forceEncoding
    true
  
  
  
    encodingFilter
    /*
  
  
  
  
    java.lang.Throwable
    /WEB-INF/views/error/404.jsp
  
  
    404
    /WEB-INF/views/error/404.jsp
  
  
    500
    /WEB-INF/views/error/500.jsp
  

复制代码




    
    
    
    
    
    

    
    
    
    

    
    
    
        
        
        
    

    
    
        
            
            
        
    

TestController.java

复制代码

package com.controller;

import com.log.Log;
import com.remote.RemoteDemo;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Created by sunjf on 2016/6/26.
 */
@Controller
public class TestController {

    @Log(desc = "log测试")
    @RequestMapping(value = "test1")
    public String test1(){
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", "sun");
        jsonObject.put("password", "123");
       // String result = RemoteDemo.remoteJsonRequest("http://localhost:8080/test2?username=sun&password=123", 5000, jsonObject);
        //System.out.println(result);
        return "error/success1";
    }

}

注:该方法跳转页面为success1.jsp,从项目结构中可以看出,我们的项目中是没有这个页面的

 

 

 

index.jsp 

复制代码



Hello World!

404.jsp

复制代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    404


    

404

未能找到您访问的资源。

运行结果

  访问链接

  

  访问结果

  springmvc深入理解_第4张图片

2.自定义的方式:

2.1.我们在WEB-INF/views/commons/error(目录自己定)新建我们自定义的错误页面,404.html, 500.html等等

 

2.2.SimpleMappingExceptionResolver只实现映射关系,我们还需要通过配置web.xml来实现。

 


    404
    /error/404.html



    500
    /error/500.html

2.3.在spring-mvc配置文件中将404.html、500.html等设置为资源文件,避免被springmvc再次拦截。  

<mvc:resources mapping="/error/**" location="/WEB-INF/views/commons/error/" />

 

2.4.配置SimpleMappingExceptionResolver。

class="org.springframework.web.servlet.handler. SimpleMappingExceptionResolver">
    name="exceptionMappings">
        
            key="ResourceNotFoundException" value="common/error/resourceNotFoundError" />
            key=".DataAccessException" value="common/error/dataAccessError" />
        
    
    name="statusCodes">
        
            key="common/error/resourceNotFoundError" value="404" />
            key="common/error/dataAccessError" value="500" />
        
    
 

感谢作者提供的:具体详细请参考https://www.cnblogs.com/baiduligang/p/4247164.html和https://www.cnblogs.com/sunjf/p/springmvc_error_view.html

springmvc中有转发和重定向这一概念,即forward(转发)和redirect(重定向),虽然我们实际中很少用,但是重定向可以解决跨域问题,还可以防止from重复

 

 

 

Xml代码 复制代码 收藏代码

  1. xml version="1.0" encoding="UTF-8"?>  
  2. <beans  
  3.     xmlns="http://www.springframework.org/schema/beans"  
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5.     xmlns:tx="http://www.springframework.org/schema/tx"  
  6.     xmlns:context="http://www.springframework.org/schema/context"     
  7.     xmlns:mvc="http://www.springframework.org/schema/mvc"     
  8.     xsi:schemaLocation="http://www.springframework.org/schema/beans    
  9.     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
  10.     http://www.springframework.org/schema/tx    
  11.     http://www.springframework.org/schema/tx/spring-tx-3.0.xsd   
  12.     http://www.springframework.org/schema/context   
  13.     http://www.springframework.org/schema/context/spring-context-3.0.xsd   
  14.     http://www.springframework.org/schema/mvc   
  15.     http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">  
  16.   
  17.   
  18.       
  19.     <context:component-scan base-package="com.app,com.core,JUnit4" >context:component-scan>  
  20.        
  21.       
  22.     <mvc:annotation-driven />  
  23.        
  24.       
  25.     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  26.         <property name="prefix" value="/WEB-INF/jsp/"/>  
  27.         <property name="suffix" value=".jsp"/>  
  28.         <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />  
  29.     bean>  
  30.        
  31.       
  32.     <mvc:interceptors>  
  33.         <bean class="com.core.mvc.MyInteceptor" />  
  34.     mvc:interceptors>        
  35.        
  36.       
  37.     <mvc:default-servlet-handler/>  
  38.        
  39.       
  40.     <mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/>  
  41.     <mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/>  
  42.     <mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"/>  
  43.   
  44. beans>   

在web.xml中:

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  
      version="3.0">
   
   
   
        default
        *.css
   

   
        default
        *.js
   

   
        default
        *.html
   

   
        default
        *.jpg
   

     
        spring-mvc
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocation  
            classpath:spring-mvc-servlet.xml
        

   

   
        spring-mvc
        /
   

   
   
        log4jConfigLocation
        classpath:log4j.properties
   

   
        org.springframework.web.util.Log4jConfigListener
   

   
   
        encodingFilter
        org.springframework.web.filter.CharacterEncodingFilter
        
            encoding
            UTF-8
        

        
            forceEncoding
            true
        

   

   
        encodingFilter
        /
   

   
   
        404
        /WEB-INF/view/error404.jsp
   

   
        java.lang.Throwable
        /WEB-INF/view/error500.jsp
   

   
        500
        /WEB-INF/view/error500.jsp
   

   
        Login.jsp
   
 


   
        60
   

 

 

 

你可能感兴趣的:(springmvc深入理解)