为了应付面试,搞了一些springmvc的面试题
1、Spring mvc工作流程
主要流程:请求发到前端控制器/核心控制器DispatcherServlet,然后转发到映射器找到对应的handler并返回,再根据对应的handler由适配器找到真正的handler并调用其处理方法后返回结果,DispatcherServlet收到结果后传给视图解析器进行解析后传给用户。
概念:
映射器:根据url找到对应的handler
适配器:调用相应的handler的方法
视图解析器:解析handler返回的结果
核心控制器:对各个处理器进行管理等
SpringMVC涉及的映射器,视图解析器的作用不难理解,映射器负责将前端请求的url映射到配置的处理器,视图解析器将最终的结果进行解析,但中间为什么要经过一层适配器呢,为什么不经映射器找到controller后直接执行返回呢?
那是因为SpringMVC为业务处理器提供了多种接口实现(例如实现了Controller接口),而适配器就是用来根据处理器实现了什么接口,最终选择与已经注册好的不同类型的Handler Adapter进行匹配,并最终执行,例如,SimpleControllerHandlerAdapter是支持实现了controller接口的控制器,如果自己写的控制器实现了controller接口,那么SimpleControllerHandlerAdapter就会去执行自己写的控制器中的具体方法来完成请求。
2、创建Controller业务逻辑类(实现Controller接口和使用Controller注解)
控制器提供访问应用程序的行为,通常通过服务接口定义或注解定义两种方法实现。 控制器解析用户的请求并将其转换为一个模型。在Spring MVC中一个控制器可以包含多个Action(动作、方法)。
第一种:实现接口Controller定义控制器
(Controller是一个接口,处在包org.springframework.web.servlet.mvc下,接口中只有一个未实现的方法,具体的接口)
public class FooController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { return new ModelAndView("/view/index", "message", "Hello,我是通过实现接口定义的一个控制器"); } }
第二种:使用注解@Controller定义控制器
@Controller public class Controller02 { @RequestMapping("/fs") public String index(Model model){ model.addAttribute("message","这是通过注解的一个控制器"); return "/view/index"; } }
3、@Controller和@RequestMapping?
1》@Controller:修饰的是控制器(即Handler)。基于注解的控制器不需要实现任何接口或父类,且基于实现Controller接口的实现类只能处理单一的请求动作(即handleRequest()方法)。
2》@RequestMapping:修饰的是处理器(映射处理器),支持的属性有
1>value属性:@RequeatMapping映射一个请求和一种方法。可以注释一个方法或类。
2>method属性:用于指定仅仅处理哪种类型请求。没有指定则,可以处理任意http请求。
3>consumes属性:指定处理请求的提交内容类型(Content-Type)。
4>produces属性:指定返回的内容类型。返回的内容类型必须是request请求头(Accept)中所包含的类型。
5>params属性:指定request中必须包含某些参数值时,才让该方法处理。如:仅处理某请求参数a,且其值为v的请求。
6>headers属性:指定request中必须包含某些指定的header值,才让此方法处理。
4、Controller的参数类型(Servlet API、Model(键值Map)、实体等)
在许多实际项目需求中,后台要从控制层直接返回前端所需的数据,这时Model大家族就派上用场了。
如果需要在请求处理方法中使用Servlet API类型、,那么可以将这些类型作为请求处理方法的参数类型,Servlet API参数类型的示例代码如下
Servlet API、org.springframework.ui.Model、输入输出流、表单实体类、注解类型、与Spring框架相关的类型等特别重要的类型是org.springframework.ui.Model类型,该类型是一个包含Map的Spring框架类型。在每次调 用请求处理方法时SpringMVC都将创建org.springframework.ui.Model对象。
请求处理方法(即Controller中的方法)可出现的参数类型:
org.springframework.ui.Model类型,不是Servlet API类型,而是一个spring mvc类型,其中包含了Map对象来存储数据。如果方法中添加了Model参数,则每次调用请求处理方法时,spring mvc都会创建Model对象,并将其作为参数传递给方法。
5、Controller的返回值类型(String、ModelAndView、Model等)
请求处理方法(即Controller中的方法)可返回的类型:
6、Model和Modelmap?
spring MVC在调用处理方法之前会创建一个隐含的模型对象,作为模型数据的存储容器。如果处理方法的参数为Model或ModelMap类型,则spring MVC会将传递给这些参数。
在处理方法内部,开发者就可以通过这个参数对象访问模型中的所有数据,也可以向模型中添加新的属性数据。Model和ModelMap可以通过如下方法对模型添加数据:
addObject(String attributeName,Object attributeValue);(---可能笔误,应该为addAttribute(...))
//使用@ModelAttribute修饰的方法,当某处理方法的参数由Model或ModelMap时, //该被修饰的方法先于处理方法执行。可以给Model或ModelMap使用如下方法增加对象 //前提被@ModelAttribute修饰的方法的参数中要有Model或ModelMap model.addAttribute(key,Object)或者 modelmap.addAttribute(key,Object)
//处理方法中,取Model或ModelMap中的参数 (某个实现类)model.asMap.get(key)或者 (某个实现类)modelMap.get(key)
7、ModelAndView理解(自己创建)
//在处理方法中获取模型数据使用如下方式 modelandview.getModel().get(key); //在处理方法中,可以通过如下方法添加模型数据 addObject(String attributeName,Object attributeValue); //可以通过如下方法设置视图 setViewName(String viewName);
8、接收参数的注解
Spring MVC中参数绑定的常用注解有:
1>处理request body部分的注解:@RequestParam、@RequestBody
2>处理request uri部分的注解:@PathVariable
3>处理request header部分的注解:@RequestHeader、@CookieValue
4>处理attribute类型的注解:@SessionAttributes、@ModelAttribute
通过@PathVariable接收URL中的请求参数
@RequestParam接收请求参数(请求参数和接收参数参数名称要一致)
通过@ModelAttribute接收请求参数
9、转发和重定向区别?
相同点
都是web开发中资源跳转的方式。不同点
转发:是服务器内部的跳转,浏览器的地址栏不会发生变化。从一个页面到另一个页面的跳转还是同一个请求,也即是只有一个请求响应。可以通过request域来传递对象。
重定向:是浏览器自动发起对跳转目标的请求,浏览器的地址栏会发生变化。从一个页面到另一个页面的跳转是不同的请求,也即是有两个或两个以上的不同的请求的响应。无法通过request域来传递对象。
————————————————在SpringMVC中实现转发和重定向
(1)在SpringMVC中仍然以传统方式进行转发和重定向
下面的代码中login.jsp就是跳转后的页面
转发:requst.getRequestDispatcher("login.jsp").forword(request,response);
重定向:response.sendRedirect("login.jsp");
(2)SpringMVC提供了便捷的转发和重定向的方式//转发
@RequestMapping("/forward")
public String forword(){
return "forward:/index.jsp";
}
//重定向
@RequestMapping("redirect")
public String redirect(){
return "redirect:/index.jsp";
}
10、RequestBody、ResponseBody
- @RequestBody 注解实现接收 http 请求的 json 数据,将 json 数据转换为 java 对象。
- @ResponseBody 注解实现将 controller 方法返回对象转化为 json 响应给客户。
11、SpringMVC初始化及请求转发实现原理?
12、为什么Spring MVC中的每个DispatcherServlet中建立起来的WebApplicationContext中要将ContextLoaderListener建立起来的WebApplicationContext设置为父容器呢?
13、Spring mvc的优点?Spring MVC的主要组件?
首先,我们得说出每个组件是做什么的,当然要每个单词读出来最好。DispatcherServlet:
spring mvc的入口,整个框架运行就是在这个servlet中完成。
HandlerMapping:处理器映射起。用于映射每个处理方法对应的请求路径。是一个map结构<url,method>。
handler:处理器。实际上就是控制器中的每个处理方法。
HandlerAdapter:处理器适配器。专门用来调用handler,因为在spring mvc中每个处理方法参数以及返回类型都不一样,因此需要用适配器来适配。
ViewResovler:视图解析器。用于指定视图技术,以及视图技术相关的配置。
View:视图。springmvc 中支持多种视图,除了jsp外 还有xml,json,pdf等。
优点:
1.清晰的角色划分:控制器(controller)、验证器(validator)、命令对象(command obect)、表单对象(form object)、模型对象(model object)、Servlet分发器(DispatcherServlet)、处理器映射(handler mapping)、试图解析器(view resoler)等等。每一个角色都可以由一个专门的对象来实现。
2.强大而直接的配置方式:将框架类和应用程序类都能作为JavaBean配置,支持跨多个context的引用,例如,在web控制器中对业务对象和验证器validator)的引用。
3.可适配、非侵入:可以根据不同的应用场景,选择何事的控制器子类(simple型、command型、from型、wizard型、multi-action型或者自定义),而不是一个单一控制器(比如Action/ActionForm)继承。
4.可重用的业务代码:可以使用现有的业务对象作为命令或表单对象,而不需要去扩展某个特定框架的基类。
5.可定制的绑定(binding)和验证(validation):比如将类型不匹配作为应用级的验证错误,这可以保证错误的值。再比如本地化的日期和数字绑定等等。在其他某些框架中,你只能使用字符串表单对象,需要手动解析它并转换到业务对象。
6.可定制的handler mapping和view resolution:Spring提供从最简单的URL映射,到复杂的、专用的定制策略。与某些web MVC框架强制开发人员使用单一特定技术相比,Spring显得更加灵活。
7.灵活的model转换:在Springweb框架中,使用基于Map的键/值对来达到轻易的与各种视图技术集成。
8.可定制的本地化和主题(theme)解析:支持在JSP中可选择地使用Spring标签库、支持JSTL、支持Velocity(不需要额外的中间层)等等。
9.简单而强大的JSP标签库(Spring Tag Library):支持包括诸如数据绑定和主题(theme)之类的许多功能。他提供在标记方面的最大灵活性。
10.JSP表单标签库:在Spring2.0中引入的表单标签库,使用在JSP编写表单更加容易。
11.Spring Bean的生命周期:可以被限制在当前的HTTp Request或者HTTp Session。准确的说,这并非Spring MVC框架本身特性,而应归属于Spring MVC使用的WebApplicationContext容器。
14、SpringMVC怎么和AJAX相互调用的?
通过Jackson框架把Java对象转换成js可以识别的json对象
步骤:
- 加入Jackson.jar
- 配置json的映射
- 在接收Ajax方法里直接返回
Object
、List
等,方法前要加@RequestBody
注解
15、SpringMVC里面拦截器、过滤器、监听器是怎么写的?
区别:
1、过滤器:
- 依赖于servlet容器;
- 在实现上基于函数回调,可以对几乎所有请求进行过滤;(范围大)
- 缺点是一个过滤器实例只能在容器初始化时调用一次;(只调用一次)
- 使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。
2、拦截器:
- 依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架;
- 在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用;
- 缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理;
- 由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。(依赖spring容器)
3、监听器
- 实现了javax.servlet.ServletContextListener 接口的服务器端程序;
- 随web应用的启动而启动;
- 只初始化一次;
- 随web应用的停止而销毁;
- 主要作用是: 做一些初始化的内容添加工作、设置一些基本的内容、比如一些参数或者是一些固定的对象等等。如SpringMVC的监听器org.springframework.web.context.ContextLoaderListener,实现了SpringMVC容器的加载、Bean对象创建、DispatchServlet初始化等。
应用场景
1.过滤器(Filter):
所谓过滤器顾名思义是用来过滤的,Java的过滤器能够为我们提供系统级别的过滤,也就是说,能过滤所有的web请求,
这一点,是拦截器无法做到的。在Java Web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或
者struts的action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者struts
的action前统一设置字符集,或者去除掉一些非法字符(聊天室经常用到的,一些骂人的话)。filter 流程是线性的,url传来之后,检查之后,
可保持原来的流程继续向下执行,被下一个filter, servlet接收。2.监听器(Listener):
Java的监听器,也是系统级别的监听。监听器随web应用的启动而启动。Java的监听器在c/s模式里面经常用到,它
会对特定的事件产生产生一个处理。监听在很多模式下用到,比如说观察者模式,就是一个使用监听器来实现的,在比如统计网站的在线人数。
又比如struts2可以用监听来启动。Servlet监听器用于监听一些重要事件的发生,监听器对象可以在事情发生前、发生后可以做一些必要的处理。3.拦截器(Interceptor):
java里的拦截器提供的是非系统级别的拦截,也就是说,就覆盖面来说,拦截器不如过滤器强大,但是更有针对性。
Java中的拦截器是基于Java反射机制实现的,更准确的划分,应该是基于JDK实现的动态代理。它依赖于具体的接口,在运行期间动态生成字节码。
拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其
执行,同时也提供了一种可以提取Action中可重用部分代码的方式。在AOP中,拦截器用于在某个方法或者字段被访问之前,进行拦截然后再之前或
者之后加入某些操作。java的拦截器主要是用在插件上,扩展件上比如 Hibernate Spring Struts2等,有点类似面向切片的技术,在用之前先要在配置文件即xml,文件里声明一段的那个东西。
过滤器和拦截器非常相似,但是它们有很大的区别
最简单明了的区别就是过滤器可以修改request,而拦截器不能
过滤器需要在servlet容器中实现,拦截器可以适用于javaEE,javaSE等各种环境
拦截器可以调用IOC容器中的各种依赖,而过滤器不能
过滤器只能在请求的前后使用,而拦截器可以详细到每个方法
区别很多,大家可以去查下总的来说
过滤器就是筛选出你要的东西,比如requeset中你要的那部分
拦截器在做安全方面用的比较多,比如终止一些流程
16、SpringMvc的控制器是不是单例模式,如果是,有什么问题,怎么解决?
默认情况下是单例模式,
在多线程进行访问的时候,有线程安全问题.
但是不建议使用同步,因为会影响性能.
解决方案,是在控制器里面不能写成员变量.
为什么设计成单例设计模式?
1.性能(不用每次请求都创建对象)
2.不需要多例(不要在控制器中定义成员变量)
17、SpringMvc中有个类把视图和数据都合并的一起的,叫什么?
ModelAndView
18、、配置过滤器,解决post的乱码问题
在web.xml中加入:
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
以上可以解决post请求乱码问题。对于get请求中文参数出现乱码解决方法有两个:
修改tomcat配置文件添加编码与工程编码一致,如下:
<ConnectorURIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
另外一种方法对参数进行重新编码:
String userName = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")
ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码
自定义过滤器解决跨域问题:
public class CrossFilter extends OncePerRequestFilter { //实现OncePerRequestFilter接口 @Override //重写doFilterInternal方法 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) { // CORS "pre-flight" request response.addHeader("Access-Control-Allow-Origin", "*"); response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); response.addHeader("Access-Control-Allow-Headers", "Content-Type"); response.addHeader("Access-Control-Max-Age", "1800");//30 min } filterChain.doFilter(request, response); }
webxml中配置:
<filter> <filter-name>cors</filter-name> <filter-class>com.flame.util.CrossFilter</filter-class> </filter> <filter-mapping> <filter-name>cors</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
19、写一个监听器
记录关于SpringMVC过程中会用到的监听器
1.Servlet监听
用来监听Servlet的生命周期。
主要类:ServletContextListener(监听网站启动过程),
HttpSessionListener(监听客户端会话过程), HttpSessionAttributeListener
实现:
创建类实现以上的接口比如:public class TestListener implements ServletContextListener, HttpSessionListener, HttpSessionAttributeListener { ..... }
web.xml配置:
<listener> <listener-class>com.test.TestListener</listener-class> </listener>
2.Bean 初始化监听
可以监听Spring创建Bean的实例的初始化前后
主要类:
BeanPostProcessor
实现:public class TestProcess implements BeanPostProcessor,Ordered { public Object postProcessBeforeInitialization(Object o, String s) throws BeansException { System.out.println("postProcessBeforeInitialization"); return o; } public Object postProcessAfterInitialization(Object o, String s) throws BeansException { System.out.println("postProcessAfterInitialization"); return o; } public int getOrder() { return 1; } }
Order主要在于多个同等类时执行的顺序,return返回的是对象,这里可以偷天换日。
SpringContext.xml:
<bean id="testpost" class="....TestProcess"></bean>
3.类的初始化和销毁的监听。
此监听旨在监听bean的自身初始化和销毁过程,初始化的执行方法在第2个监听之后
实现:
第一种1.SpringContext.xml<bean id="helloWorld" class="com.tutorialspoint.HelloWorld" init-method="init" destroy-method="destroy"> <property name="message" value="Hello World!"/> </bean>
通过init-method和destroy-method设置。
第二种2.在bean中@PostConstruct public void init(){ System.out.println("Bean is going through init."); } @PreDestroy public void destroy(){ System.out.println("Bean will destroy now."); }
4.AOP。
面向切面编程
第一种方式:
SpringContext.xml:<aop:config> <aop:aspect id="myaspect" ref="peoAsp"> <aop:pointcut id="mypointcut" expression="execution(* jis.*.*(..))"/> <aop:before method="beforeAdvice" pointcut-ref="mypointcut"/> <aop:after-throwing method="AfterThrowingAdvice" pointcut-ref="mypointcut" throwing="ex"/> <aop:after-returning method="afterReturningAdvice" pointcut-ref="mypointcut" returning="retVal"/> </aop:aspect> </aop:config>
第二种方式:
Bean中:@Aspect public class PeoAsp { public PeoAsp(){} @Pointcut("execution(* com.jis.People.aspTest(..))") public void selectAll(){}; /** * This is the method which I would like to execute * before a selected method execution. */ @Before("selectAll()") public void beforeAdvice(){ System.out.println("Going to setup student profile."); } /** * This is the method which I would like to execute * after a selected method execution. */ @After("selectAll()") public void afterAdvice(){ System.out.println("Student profile has been setup."); } /** * This is the method which I would like to execute * when any method returns. */ @AfterReturning(pointcut = "selectAll()",returning = "retVal") public void afterReturningAdvice(Object retVal){ System.out.println("Returning:" + retVal.toString() ); } /** * This is the method which I would like to execute * if there is an exception raised. */ public void AfterThrowingAdvice(Exception ex){ System.out.println("There has been an exception: " + ex.toString()); } } 并在SpringContext中配置: <aop:aspectj-autoproxy/> <bean class="com.jis.PeoAsp" id="peoAsp"></bean>
20、写一个拦截器
SpringMVC 拦截器的原理图:(拦截器是针对控制器controller起作用的)
可以简单看出,拦截器的执行顺序是:preHandle------controller------postHandle-----afterCompletion(当preHandle执行成功才执行)
springMVC拦截器的实现一般有两种方式
第一种方式是要定义的Interceptor类要实现了Spring的HandlerInterceptor 接口
第二种方式是继承实现了HandlerInterceptor接口的类,比如Spring已经提供的实现了HandlerInterceptor接口的抽象类HandlerInterceptorAdapter
HandlerInterceptor 接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的。
preHandle(): 这个方法在业务处理器处理请求之前被调用,SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
postHandle():这个方法在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行。
afterCompletion():该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
springmvc.xml:public class CommonInterceptor extends HandlerInterceptorAdapter{ private final Logger log = LoggerFactory.getLogger(CommonInterceptor.class); public static final String LAST_PAGE = "lastPage"; /** * 在业务处理器处理请求之前被调用 * 如果返回false * 从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链 * * 如果返回true * 执行下一个拦截器,直到所有的拦截器都执行完毕 * 再执行被拦截的Controller * 然后进入拦截器链, * 从最后一个拦截器往回执行所有的postHandle() * 接着再从最后一个拦截器往回执行所有的afterCompletion() */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if ("GET".equalsIgnoreCase(request.getMethod())) { RequestUtil.saveRequest(); } log.info("==============执行顺序: 1、preHandle================"); String requestUri = request.getRequestURI(); String contextPath = request.getContextPath(); String url = requestUri.substring(contextPath.length()); if ("/userController/login".equals(url)) { return true; }else { String username = (String)request.getSession().getAttribute("user"); if(username == null){ log.info("Interceptor:跳转到login页面!"); request.getRequestDispatcher("/page/index.jsp").forward(request, response); return false; }else return true; } } /** * 在业务处理器处理请求执行完成后,生成视图之前执行的动作 * 可在modelAndView中加入数据,比如当前时间 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("==============执行顺序: 2、postHandle================"); if(modelAndView != null){ //加入当前时间 modelAndView.addObject("haha", "测试postHandle"); } } /** * 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等 * 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion() */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("==============执行顺序: 3、afterCompletion================"); } }
<!-- 对静态资源文件的访问--> <!-- <mvc:resources mapping="/images/**" location="/images/"/> <mvc:resources mapping="/css/**" location="/css/" /> <mvc:resources mapping="/js/**" location="/js/" /> <mvc:resources mapping="/favicon.ico" location="favicon.ico" /> --> <!--配置拦截器, 多个拦截器,顺序执行 --> <mvc:interceptors> <mvc:interceptor> <!-- /**的意思是所有文件夹及里面的子文件夹 /*是所有文件夹,不含子文件夹 /是web项目的根目录 --> <mvc:mapping path="/**" /> <!-- 需排除拦截的地址 --> <!-- <mvc:exclude-mapping path="/userController/login"/> --> <bean id="commonInterceptor" class="org.shop.interceptor.CommonInterceptor"></bean> <!--这个类就是我们自定义的Interceptor --> </mvc:interceptor> <!-- 当设置多个拦截器时,先按顺序调用preHandle方法,然后逆序调用每个拦截器的postHandle和afterCompletion方法 --> </mvc:interceptors>