文章目录
Spring MVC
一、Spring MVC是什么
Spring Web MVC和Struts2都是表现层的框架,是Spring框架的一部分
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts 2(一般老项目使用)等。
Spring MVC的处理流程一般是用户主机向服务器发送请求 - 用户请求传到前端控制器(DispatcherServlet)- 前端控制器将请求传递给处理器(Handler) - 处理器将处理结果传回前端控制器 - 前端控制器处理好结果转发给jsp页面 - jsp页面转化为html页面并传回给前端控制器 - 前端控制器响应用户的请求。
二、Spring MVC入门程序
(一)前端控制器配置
- 新建一个动态web工程
- 导包
- 在web.xml文件中配置前端控制器(servlet-class在spirng-webmvc-.RELEASE包下找)
url-pattern标签可以填的东西有
- /*:拦截所有的东西,与Struts2不同的是,Struts2若填写/*不拦截jsp(建议使用)
- *.action:拦截以action结尾的请求(后缀可以是其他)
- /:拦截所有的东西,不包括jsp(建议使用)
init-param配置的是配置文件的路径,如果没配置的话,默认找/WEB-INF/的[servlet的名称]-servlet.xml文件,即/WEB-INF/springmvc-servlet.xml
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
(二)编写配置文件
在src目录下创建springmvc.xml文件,文件头跟spring框架的配置文件applicationContext.xml一样
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd ">
</beans>
(三)配置处理器Handler
处理器跟Struts2中的Action相似,也就是Controller
- 新建处理器类xController,并加上注解@Controller,在处理器类中添加方法function(名称可变),方法上加上注解@RequestMapping(value=“”)(双引号内为访问的路径,即下例中访问的路径为项目名/hello.action),该方法体中有一个ModelAndView对象,ModelAndView传递了一个对象(底层其实是request.setAttribute,键值对形式)并设置了视图路径(也就是要跳转的页面),且返回值仍是这个ModelAndView对象。
@Controller
public class xController {
@RequestMapping(value = "/hello.action")
public ModelAndView function() {
ModelAndView mav = new ModelAndView();
mav.addObject("hello", "hi");
mav.setViewName("/WEB-INF/jsp/hello.jsp");
return mav;
}
}
- 在springmvc.xml中beans标签下配置扫描带有@Controller或@Service的类,base-package属性传递所想要扫描的包,如果传入的是父包,子包也会被扫描到
<context:component-scan base-package=""/>
- 在服务器上运行web程序,访问路径为项目名/hello.action,在jsp页面中取出ModelAndView传递过来的数据
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>hello world</h1>
<%=request.getAttribute("hello") %>
</body>
</html>
访问结果:
程序的运行解释:配置了前端控制器之后,拦截了所有的请求,包括此次访问的.action结尾的请求,通过springmvc.xml配置文件将请求拦截到控制层,当请求路径跟方法上的@RequestMapping注解的路径相对应时,会执行相应的方法体内的操作。
二、Spring MVC架构
下图是SpringMVC架构比较完整的执行过程图
- 用户向前端控制器发送请求,前端控制器会将请求拦截下来拿给处理器映射器,处理器映射器就会到Spring容器中寻找与具体请求url相对应的处理器(加了@Controller的类)中加了@RequestMapping的方法,当找到@RequestMapping的值与请求路径相对应的方法,再以字符串的方式返回给前端控制器(包名+类名+方法名),在返回之前若配置了拦截器,则还要经过一系列拦截器再传递给处理器Handler,如拦截用户某一些不规范的请求不让它到处理器那。
- 前端控制器收到了处理器映射器的返回值,再将请求传到处理器适配器,处理器适配器再去执行处理器(Controller,也叫后端处理器),处理器返回一个ModelAndView对象给回处理器适配器,处理器适配器再转发给前端控制器。
- 前端控制器接收到ModelAndView后向视图解析器将对象解析,将jsp页面读取到内存中,形成一个View对象并返回给前端控制器。
- 渲染视图之后形成一个html页面并响应给用户
以上大部分步骤都由springmvc帮我们完成,我们需要做的只有配置前端控制器,编写处理器(Controller类)以及视图(jsp页面)。
现在分别解释一下这几个组件。
- 前端控制器(DispatherServlet)(核心组件):mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其他组件处理用户的请求,dispatherServlet的存在降低了组件的耦合性。
- 处理器映射器( HandlerMapping):HandlerMapping负责根据用户请求url找到Handler,springmvc提供了不同的映射器实现不同的映射方式,例如配置文件方式、实现接口方式、注解方式等。
- 处理器(Handler):Handler是继DispatherServlet前端控制器的后端控制器,在DispatherServlet的控制下Handler对具体的用户请求进行处理。需要我们自己编写。
- 处理器适配器(HandlerAdapter):通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对多种类型的处理器进行执行。
- 视图解析器(ViewResolver):ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
- 视图(View):springmvc提供了很多的View视图类型的支持,如jstlView、freemakerView、pdfView,最常用的是jsp。需要我们自己编写。
三、SpringMVC默认加载的组件
在spring-webmvc包下,有一个DispatcherServlet.properties配置文件,其中包含了Springmvc会默认帮我们进行加载的组件。
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
<!-- 本地处理器,判断所在区域 -->
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
<!--校验处理器,拦截处理一些不合理的字符等 -->
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
<!-- 处理器映射器,有两个,分别是配置式(不常用)、注解式(方法体上加@RequestMapping) -->
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
<!-- 处理器适配器,有三个,前两个是用于配置式的适配器(如果要使用的话需要有实现HttpRequestHandler接口的处理器类或者实现Controller接口的处理器类),第三个是用于注解式的适配器 -->
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
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
<!-- jsp视图解析器 -->
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
上面的处理器映射器有一个DefaultAnnotationHandlerMapping,在Spring3.1版本之后已经不推荐使用了,因此我们还要在springmvc.xml中配置spring-webmvc包下的RequestMappingHandlerMapping完成注解式处理器映射。
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
处理器适配器有一个AnnotationMethodHandlerAdapter,在Spring3.2版本也不推荐使用了,因此还需要再springmvc.xml配置spring-webmvc包下的RequestMappingHandlerAdapter完成注解式处理器适配。
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" />
直接配置处理器映射器和处理器适配器比较麻烦,可以使用注解驱动来加载,使用这一行代码可以直接替代上面两行代码。不过使用注解驱动前需要导入约束mvc。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd ">
<context:component-scan base-package="com"/>
<!-- 注解驱动 -->
<mvc:annotation-driven/>
</beans>
除了配置处理器适配器和处理器映射器之外,还需要配置视图解析器,原本视图解析器springmvc会帮我们自动加载一个视图解析器,但如果我们手动配置视图解析器的话,能够向视图解析器传入所要解析的视图路径的前缀跟后缀,使编写代码更加简便。
比如通过手动配置视图解析器并传入了前缀后缀之后,我们在编写Controller类的时候,使用ModelAndView对象设置视图路径的时候就可以不用写前缀和后缀,只写逻辑视图名即中间即可。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
@RequestMapping(value = "/hello.action")
public ModelAndView function() {
ModelAndView mav = new ModelAndView();
mav.addObject("hello", "hi");
mav.setViewName("hello");
return mav;
}
四、Springmvc与Mybatis整合
整合springmvc的目的在于控制层使用springmvc,持久层使用mybatis。
- 导包
- 编写MyBatis配置文件sqlMapConfig.xml,这里只需要配置一个别名,因为数据库要交给Spring配置,且使用的是Mapper动态代理扫描开发,会自动扫描Mapper文件,不用配置相应的路径
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.pojo"/>
</typeAliases>
</configuration>
- 编写Spring配置文件applicationContext.xml,数据库配置文件jdbc.properties,配置数据库连接以及sqlSessionFactory,还有Mapper动态代理扫描开发所扫描的包。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd ">
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="maxActive" value="10"></property>
<property name="maxIdle" value="5"></property>
</bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!-- 核心配置文件的位置 -->
<property name="configLocation" value="classpath:sqlMapConfig.xml"></property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dao"></property>
</bean>
</beans>
- 配置Spring监听器,在web.xml配置监听器使得Spring随项目启动而创建
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
- 在web.xml中配置前端控制器
<!-- 前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 默认找/WEB-INF/[servlet的名称]-servlet.xml -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
- 在applicationContext.xml配置注解事务,使用spring-jdbc包下的DataSourceTransactionManager配置事务管理器
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
- 编写Controller类、Service类、在Service类中去调用Mapper接口中的方法,再通过Controller类去调用Service类。
五、参数绑定
(一)默认支持的参数
使用Springmvc 进行web编程的时候,一定会访问到Controller类中的方法,并且很多时候会涉及到数据传递,需要向request、session等域中存值,转发到另一个页面并取出来,因此Springmvc中默认支持几个参数供我们使用。包括HttpServletRequest、HttpServletResponse、HttpSession、Model等。
这里举个例子,从一个页面listUser.jsp跳转到另外一个页面edit.jsp。listUser.jsp从数据库从将用户信息查询出来并放置到页面,点击修改用户信息,可以跳转到用户信息修改页面。
listUser.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用户列表</title>
</head>
<body>
用户列表:
<table width="100%" border=1>
<tr>
<td>id</td>
<td>用户名</td>
<td>地址</td>
<td>操作</td>
</tr>
<c:forEach items="${userList }" var="user">
<tr>
<td>${user.id }</td>
<td>${user.username }</td>
<td>${user.address}</td>
<td><a href="${pageContext.request.contextPath }/edit.action?id=${user.id}">修改用户信息</a></td>
</tr>
</c:forEach>
</table>
</form>
</body>
</html>
edit.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form id="form" action="${pageContext.request.contextPath }/update.action" method="post">
<input type="text" name="id" value="${user.id }" hidden="true">
<table>
<tr>
<td>id</td>
<td >${user.id }</td>
</tr>
<tr>
<td>username</td>
<td><input type="text" name="username" value="${user.username }"/></td>
</tr>
<tr>
<td>address</td>
<td><input type="text" name="address" value="${user.address }"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交" />
</td>
</tr>
</table>
</form>
</body>
</html>
新建一个UserService接口:
public interface UserService {
public List<User> selectUsers();
public User selectUserById(Integer id);
}
新建一个UserServiceImpl并且实现UserService接口,,并在类上加上注解@Service,向其中注入UserMapper对象,实现从数据库查询一个用户或全部用户的功能:
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
public User selectUserById(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
public List<User> selectUsers() {
return userMapper.selectByExample(null);
}
}
新建一个UserController,这里给第二个方法edit传递了一个Springmvc支持使用的参数HttpServletRequest,并在方法体中使用request.getParameter从request域中取出前一个页面传递过来的id值:
@Controller
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/listUser.action")
public ModelAndView listUser() {
List<User> list = userService.selectUsers();
ModelAndView mav = new ModelAndView();
mav.addObject("userList", list);
mav.setViewName("listUser");
return mav;
}
@RequestMapping(value = "/edit.action")
public ModelAndView edit(HttpServletRequest request) {
ModelAndView mav = new ModelAndView();
String id = request.getParameter("id");
User user = userService.selectUserById(Integer.parseInt(id));
mav.addObject("user", user);
mav.setViewName("edit");
return mav;
}
}
运行结果:
点击修改用户信息后,跳转到edit.jsp(修改页面)会从数据库中查询出相关用户的数据,并显示出来:
(二)绑定简单类型
SpringMvc支持给Controller类的方法提供一些简单类型(一般推荐使用包装类,因为基本类型不可为null)作为形参。只要传递过来的请求参数名跟形参名一致且类型一致的话,就可以直接使用。
- 整型:Integer、int
- 字符串:String
- 单精度:Float、float
- 双精度:Double、double
- 布尔类型:Boolean、boolean
对上面的function2可以进行如下修改:给方法添加一个形参id,当访问这个方法的时候会自动将传递过来的数据封装到相应的形参中,但数据类型跟参数名要相对应。运行后的结果跟上面的完全一样。
@RequestMapping(value = "/edit.action")
public ModelAndView edit(Integer id ) {
ModelAndView mav = new ModelAndView();
User user = userService.selectUserById(id);
mav.addObject("user", user);
mav.setViewName("edit");
return mav;
}
Springmvc还提供了一个注解@RequestParam用于处理简单类型的绑定。当形参名跟传递过来的参数名不一样的话,可以在前面加上此注解。@RequestParam有三个参数,一个是value,一个是required,以及defaultValue。
- value:参数名字,即入参的请求参数名字。如果value=“x”表示请求的参数区中名字为x的参数的值将传入。
- required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报错,如果该参数可有可无的话,则应该手动将值指定为false。
- defaultValue:指定默认值。
@RequestMapping(value = "/edit.action")
public ModelAndView edit(@RequestParam(value="id",required=false,defaultValue = "1") Integer id ) {
ModelAndView mav = new ModelAndView();
User user = userService.selectUserById(id);
mav.addObject("user", user);
mav.setViewName("edit");
return mav;
}
这种方法比较麻烦,建议直接将形参名与请求参数名进行统一。
(三)绑定POJO
有时候从上一个页面传递过来的数据很多时,每个请求参数都作为一个形参的话比较复杂,可以将多个数据封装到一个对象作为一个形参。
接上面的例子,给UserService接口增加一个更新用户的方法并实现它:
public interface UserService {
public User selectUserById(Integer id);
public List<User> selectUsers();
public void updateUser(User user);
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
public User selectUserById(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
public List<User> selectUsers() {
return userMapper.selectByExample(null);
}
public void updateUser(User user) {
userMapper.updateByPrimaryKeySelective(user);
}
}
再给UserController增加一个方法,形参为User类:
@RequestMapping(value = "/update.action")
public ModelAndView update(User user) {
userService.updateUser(user);
ModelAndView mav = new ModelAndView();
List<User> list = userService.selectUsers();
mav.addObject("userList", list);
mav.setViewName("listUser");
return mav;
}
这样的话,当点击上面例子中的提交按钮,提交到项目名/update.action时,会自动将提交过来的数据封装成一个User对象供我们使用,不过这里有个前提,那就是提交过来的数据必须要包含主键,一般可以用input标签提交主键过来,且设置属性为hidden;并且传过来的数据名称要跟User类中的属性名相一致,也就是说,jsp页面中的input标签中的name属性要跟User类中的属性名相一致。
这里再次给出edit.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form id="form" action="${pageContext.request.contextPath }/update.action" method="post">
<input type="text" name="id" value="${user.id }" hidden="true">
<table>
<tr>
<td>id</td>
<td >${user.id }</td>
</tr>
<tr>
<td>username</td>
<td><input type="text" name="username" value="${user.username }"/></td>
</tr>
<tr>
<td>address</td>
<td><input type="text" name="address" value="${user.address }"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交" />
</td>
</tr>
</table>
</form>
</body>
</html>
通过上述操作成功修改数据库的用户信息之后,会发现修改保存到数据库的信息都存在着POST提交表单乱码问题,在这里我们还需要进行一些修改。即在web.xml中配置过滤器CharacterEncodingFilter即可,可在spring-web包下的filter包中找到。配置完之后即可拦截以.action结尾的请求并解决编码问题。
<filter>
<filter-name>encoding</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>
(四)绑定包装POJO
Controller类的方法还可以使用包装类作为形参。接上面的例子,将User类封装于包装类QueryVo中并提供getter、setter方法。并将Controller类中的update方法的形参改为QueryVo。
public class QueryVo {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
@RequestMapping(value = "/update.action")
public ModelAndView update(QueryVo vo) {
userService.updateUser(vo.getUser());
ModelAndView mav = new ModelAndView();
List<User> list = userService.selectUsers();
mav.addObject("userList", list);
mav.setViewName("listUser");
return mav;
}
因为使用了包装类,所以jsp页面也要做一些改动。将edit.jsp的name属性加上前缀(user.),即表明传递的数据要封装到QueryVo的user对象属性中的相应属性中。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form id="form" action="${pageContext.request.contextPath }/update.action" method="post">
<input type="text" name="user.id" value="${user.id }" hidden="true">
<table>
<tr>
<td>id</td>
<td >${user.id }</td>
</tr>
<tr>
<td>username</td>
<td><input type="text" name="user.username" value="${user.username }"/></td>
</tr>
<tr>
<td>address</td>
<td><input type="text" name="user.address" value="${user.address }"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交" />
</td>
</tr>
</table>
</form>
</body>
</html>
运行项目,点击修改用户信息,修改完信息后提交,跟上一个例子结果一样。
(五)自定义参数绑定
有的时候从页面传递过来的数据的格式并不满足转换成形参所需要的格式,例如日期、时间等数据,因此我们需要在Springmvc.xml中配置转换器(转换工厂)FormattingConversionServiceFactoryBean将传递过来的数据转换成我们所需要的格式,可以在spring-context包下的format目录找到。
转换器类需要我们自己编写,实现spring-core包下的Conveter接口,这个接口还需要传递泛型,一个是S,代表页面传递过来的数据类型,一个是T,代表要转换成的数据类型;并实现一个convert方法。
新建一个日期转换器,将yyyy.MM.dd格式的字符串转换为Date格式
package com.coversion;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
public class DateConverter implements Converter<String, Date>{
public Date convert(String source) {
try {
if(source != null) {
DateFormat df = new SimpleDateFormat("yyyy.MM.dd");
return df.parse(source);
}
} catch (Exception e) {
}
return null;
}
}
可以同时配置多个转换器,这里只配置一个日期转换器,将上面创建的转换器类的完整类名传,并在配置注解驱动的时候配置一个conversion-service属性,将配置的转换工厂传给它。
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
<bean id="conversionServiceFactoryBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!-- 配置多个转化器 -->
<property name="converters">
<list>
<bean class="com.coversion.DateConverter"></bean>
</list>
</property>
</bean>
接上一个例子,给edit.jsp增加了一个显示用户生日的空
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form id="form" action="${pageContext.request.contextPath }/update.action" method="post">
<input type="text" name="user.id" value="${user.id }" hidden="true">
<table>
<tr>
<td>id</td>
<td >${user.id }</td>
</tr>
<tr>
<td>username</td>
<td><input type="text" name="user.username" value="${user.username }"/></td>
</tr>
<tr>
<td>address</td>
<td><input type="text" name="user.address" value="${user.address }"/></td>
</tr>
<tr>
<td>birthday</td>
<td><input type="text" name="user.birthday" value="${user.birthday }"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交" />
</td>
</tr>
</table>
</form>
</body>
</html>
当输入指定格式的日期也就是yyyy.MM.dd就会转换成Date格式,就可以同步到数据库了。
(六)数组参数绑定
接上一个例子,给显示用户的页面listUser.jsp中为每一个用户添加一个复选框,同时选中多个用户时,点击删除按钮可以从数据库中把相应用户的数据删除。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用户列表</title>
</head>
<body>
用户列表:
<form action="${pageContext.request.contextPath }/delete.action" method="post">
<table width="100%" border=1>
<tr>
<td><input type="checkbox" name="ids" value=""/></td>
<td>id</td>
<td>用户名</td>
<td>地址</td>
<td>操作</td>
</tr>
<c:forEach items="${userList }" var="user">
<tr>
<td><input type="checkbox" name="ids" value="${user.id }"/></td>
<td>${user.id }</td>
<td>${user.username }</td>
<td>${user.address}</td>
<td><a href="${pageContext.request.contextPath }/edit.action?id=${user.id}">修改用户信息</a></td>
</tr>
</c:forEach>
</table>
<input type="submit" value="删除">
</form>
</body>
</html>
需要注意的是,每个复选框的name属性都是ids,这样的话,当点击删除按钮之后,传递到Controller类的就会是一个数组,名称为ids;然后我们就可以给Controller类中的方法设置一个数组形参来接收数据。
给UserService接口增加一个删除多个用户的功能并实现它。
public interface UserService {
public User selectUserById(Integer id);
public List<User> selectUsers();
public void updateUser(User user);
public void deleteUsersByIds(Integer[] ids);
}
public void deleteUsersByIds(Integer[] ids) {
for (Integer id : ids) {
userMapper.deleteByPrimaryKey(id);
}
}
给UserController增加一个delete方法:
@RequestMapping(value = "/delete.action")
public ModelAndView delete(Integer[] ids) {
userService.deleteUsersByIds(ids);
ModelAndView mav = new ModelAndView();
List<User> list = userService.selectUsers();
mav.addObject("userList", list);
mav.setViewName("listUser");
return mav;
}
这样的话,点击删除按钮,能够实现同时删除多个用户的功能。
总结来说,只要传递过来的数据的name属性一致的话,就可以在Controller类中用一个数组去接收这些数据并使用,也可以用一个包装类QueryVo去接收,包装类QueryVo中包含一个数组。
(七)List参数绑定
List参数绑定常放置于一个包装类QueryVo中,并提供getter、setter方法,直接将List集合作为Controller方法形参的话,会报错。
public class QueryVo {
private List<Integer> ids;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
给UserService接口提供一个deleteUsersByIds方法,接收参数为List类型,并实现它
public interface UserService {
public User selectUserById(Integer id);
public List<User> selectUsers();
public void updateUser(User user);
public void deleteUsersByIds(Integer[] ids);
public void deleteUsersByIds(List<Integer> ids);
}
package com.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dao.UserMapper;
import com.pojo.User;
import com.service.UserService;
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
public User selectUserById(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
public List<User> selectUsers() {
return userMapper.selectByExample(null);
}
public void updateUser(User user) {
userMapper.updateByPrimaryKeySelective(user);
}
public void deleteUsersByIds(Integer[] ids) {
for (Integer id : ids) {
userMapper.deleteByPrimaryKey(id);
}
}
public void deleteUsersByIds(List<Integer> ids) {
for (Integer id : ids) {
userMapper.deleteByPrimaryKey(id);
}
}
}
对Controller类中的delete方法稍作修改:
@RequestMapping(value = "/delete.action")
public ModelAndView delete(QueryVo vo) {
userService.deleteUsersByIds(vo.getIds());
ModelAndView mav = new ModelAndView();
List<User> list = userService.selectUsers();
mav.addObject("userList", list);
mav.setViewName("listUser");
return mav;
}
对listUser.jsp也稍作修改,跟数组的差别在于,需要给每一项数据的name属性指定要存到的list集合的哪一项,这里是按顺序存储下来,所以直接指定为ids[${i.index}]
:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用户列表</title>
</head>
<body>
用户列表:
<form action="${pageContext.request.contextPath }/delete.action" method="post">
<table width="100%" border=1>
<tr>
<td><input type="checkbox" name="ids" value=""/></td>
<td>id</td>
<td>用户名</td>
<td>地址</td>
<td>操作</td>
</tr>
<c:forEach items="${userList }" var="user" varStatus="i">
<tr>
<td><input type="checkbox" name="ids[${i.index}]" value="${user.id }"/></td>
<td>${user.id }</td>
<td>${user.username }</td>
<td>${user.address}</td>
<td><a href="${pageContext.request.contextPath }/edit.action?id=${user.id}">修改用户信息</a></td>
</tr>
</c:forEach>
</table>
<input type="submit" value="删除">
</form>
</body>
</html>
实现后的效果跟用数组实现的一样。
(八)图片参数绑定
这里实现文件参数绑定有一些前期工作需要做。
- 给tomcat配置一个虚拟目录来放置所需要的图片,这样的话项目与放置图片的目录就是独立开来的,每次发布项目到tomcat上的话图片目录就不会被覆盖。
在tomcat中的web module中进行设置
点击Add External Web Module,将放置图片的目录配置进去
这样的话,可以通过服务器访问到这些图片,访问路径为localhost:8080/image/某一张图片的名称。
- 给listUser.jsp跟edit.jsp增加显示图片和上传图片的区域。
listUser.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用户列表</title>
</head>
<body>
用户列表:
<form action="${pageContext.request.contextPath }/delete.action" method="post">
<table width="100%" border=1>
<tr>
<td><input type="checkbox" name="ids" value=""/></td>
<td>id</td>
<td>用户名</td>
<td>头像</td>
<td>地址</td>
<td>操作</td>
</tr>
<c:forEach items="${userList }" var="user" varStatus="i">
<tr>
<td><input type="checkbox" name="ids[${i.index}]" value="${user.id }"/></td>
<td>${user.id }</td>
<td>${user.username }</td>
<td>
<c:if test="${user.pic !=null}">
<img src="/image/${user.pic}" width=100 height=100/>
</c:if>
<c:if test="${user.pic ==null}">
无
</c:if>
</td>
<td>${user.address}</td>
<td><a href="${pageContext.request.contextPath }/edit.action?id=${user.id}">修改用户信息</a></td>
</tr>
</c:forEach>
</table>
<input type="submit" value="删除">
</form>
</body>
</html>
edit.jsp,上传图片的话需要给form表单指定属性enctype="multipart/form-data"
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form id="form" action="${pageContext.request.contextPath }/update.action" method="post" enctype="multipart/form-data">
<input type="text" name="id" value="${user.id }" hidden="true">
<table>
<tr>
<td>id</td>
<td >${user.id }</td>
</tr>
<tr>
<td>username</td>
<td><input type="text" name="username" value="${user.username }"/></td>
</tr>
<tr>
<td>pic</td>
<td>
<c:if test="${user.pic !=null}">
<img src="/image/${user.pic}" width=100 height=100/>
<br/>
</c:if>
<input type="file" name="pictureFile"/>
</td>
</tr>
<tr>
<td>address</td>
<td><input type="text" name="address" value="${user.address }"/></td>
</tr>
<tr>
<td>birthday</td>
<td><input type="text" name="birthday" value="${user.birthday }"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交" />
</td>
</tr>
</table>
</form>
</body>
</html>
- 导入文件上传所需要的包
前期工作准备完毕之后,就要来给之前的update方法绑定一个文件参数MultipartFile ,注意这个参数的名称要跟传递过来的文件名一样。由于MultipartFile是一个接口,我们还需要到springmvc.xml中给配置一个实现类,且id为固定值,这样springmvc才会将实现类注入给MultipartFile,并且该实现类还能控制上传图片的大小等。
@RequestMapping(value = "/update.action")
public ModelAndView update(User user,MultipartFile pictureFile) throws Exception {
//保存图片到D:\image
//生成随机的图片名
String picName = UUID.randomUUID().toString();
//获取后缀名
String extension = FilenameUtils.getExtension(pictureFile.getOriginalFilename());
pictureFile.transferTo(new File("D:\\image\\" + picName + "." + extension));
user.setPic(picName + "." + extension);
userService.updateUser(user);
ModelAndView mav = new ModelAndView();
List<User> list = userService.selectUsers();
mav.addObject("userList", list);
mav.setViewName("listUser");
return mav;
}eturn mav;
}
<!-- 上传图片配置实现类 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 单位为B -->
<property name="maxUploadSize" value="500000"/>
</bean>
实现结果:
选择文件点击提交之后可以更改用户头像并同步到数据库
同时图片会被保存到tomcat的虚拟目录之下,也就是说会保存到D:\image下。
六、Struts2跟Springmvc的区别
- springmvc的入口是一个servlet,而struts2的入口是一个filter过滤器。
- springmvc是基于方法开发的,一个url对应一个方法,请求参数传递到方法的形参,可以设计为单例或者多例(一般为单例),而struts2是基于类开发,传递参数是通过类的属性,所以只能设计为多例。
- struts2采用值栈存储请求和响应的数据,通过ognl存取数据,而springmvc是通过参数解析器将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过request域传输到页面。
七、@RequestMapping标签详细用法
通过@RequestMapping可以定义不同的处理器映射规则。
-
@RequestMapping用来指定映射路径,只指定一个value参数时,value可以省略,直接写@RequestMapping(“映射路径”)。value是一个数组,可以同时指定多个访问路径。
-
如果将
@RequestMapping(value=“”)
放在Conrtroller类上,可以指定访问路径的前缀。例如给Controller类加上@RequestMapping(value=“/a”)
,给方法加上@RequestMapping(value=“/b”)
,则访问的路径为项目名/a/b。 -
@RequestMapping还可以指定method参数:指定数据提交的方式为GET或者POST,如果提交数据的方式跟指定的方式不一致时会报错,method是一个数组,可以同时指定多个数据提交的方式。
@RequestMapping(value = "",method = RequestMethod.POST)
@RequestMapping(value = "{"/a.action","/b.action"}",method = {RequestMethod.POST,RequestMethod.GET})
八、Controller类方法返回值
- ModelAndView:通用的返回值,既能携带数据,又能返回视图路径。
- String:只能返回视图路径,需要携带数据的话需要给方法加上一个形参Model。推荐使用此种方式,符合解耦思想,数据与视图分离。
以上面例子Controller类的listUser方法为例,两种写法作用一样:
@RequestMapping(value = "/listUser.action")
public ModelAndView listUser() {
List<User> list = userService.selectUsers();
ModelAndView mav = new ModelAndView();
mav.addObject("userList", list);
mav.setViewName("listUser");
return mav;
}
@RequestMapping(value = "/listUser.action")
public String listUser(Model model) {
List<User> list = userService.selectUsers();
model.addAttribute("userList", list);
return "listUser";
}
- void,使用HttpServletRequest 跟HttpServletResponse来传递数据设置视图路径,采用请求转发的方式。此种方式适用于ajax请求(一种在无需重新加载整个网页的情况下,能够更新部分网页的技术),不需要跳转视图,也就不需要用到请求转发。
上面的listUser方法还可以像下面这样写:
@RequestMapping(value = "/listUser.action")
public void listUser(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
List<User> list = userService.selectUsers();
request.setAttribute("userList", list);
request.getRequestDispatcher("/WEB-INF/jsp/listUser.jsp").forward(request, response);
}
上面返回值类型为String其实还包含两种,上面所展示的那种是返回逻辑视图名,还有两种分别是:
- 重定向,Controller方法返回字符串可以重定向到一个url地址。
应用上面的例子,有一个是提交修改后的用户信息到数据库,重新定位到显示用户信息列表的页面listUser.jsp,涉及到UserController类中的update方法。此方法修改数据库中用户的信息之后是重新调用了UserService的selectUsers方法再查询一次数据库,其实UserController类中已经定义过listUser方法可以查询数据库,重新写一次比较麻烦,这时可以使用重定向来实现代码复用。
@RequestMapping(value = "/listUser.action")
public ModelAndView listUser() {
List<User> list = userService.selectUsers();
ModelAndView mav = new ModelAndView();
mav.addObject("userList", list);
mav.setViewName("listUser");
return mav;
}
@RequestMapping(value = "/update.action")
public ModelAndView update(User user) {
userService.updateUser(user);
ModelAndView mav = new ModelAndView();
List<User> list = userService.selectUsers();
mav.addObject("userList", list);
mav.setViewName("listUser");
return mav;
}
新写法,这样的话,就会重定向到listUser.action,重新访问一次listUser方法:
@RequestMapping(value = "/update.action")
public String function3(User user) {
userService.updateUser(user);
return "redirect:/listUser.action";
}
- 请求转发,Controller方法执行后会继续执行下一个方法。
@RequestMapping(value = "/update.action")
public String function3(User user) {
userService.updateUser(user);
return "forward:/listUser.action";
}
两种效果一致,但是url请求路径不会变,而重定向请求路径会发生变化。使用请求转发的话,刷新页面会重复上一次的操作,这种从逻辑上来说不太可行,所以一般都会使用重定向。
九、异常处理器
Springmvc在处理请求过程中出现的异常是交由异常处理器进行处理的。Springmvc自带的异常处理器会帮我们捕获异常,但是处理异常需要我们自己去定义。
springmvc单独设置了异常处理器,因此当客户发送请求过来时,dao层、service层或者controller层发生异常的话都不需要单独去处理异常,只要将异常一层一层往上抛出,最后有前端控制器交由异常处理器去处理。即dao-service-controller-前端控制器-异常处理器。
进行异常处理一般遵循以下步骤
- 编写异常处理器类
Springmvc提供了HandlerExceptionResolver接口供我们去编写异常处理器类。实现这个接口要实现resolveException方法。
public class ExceptionResolver implements HandlerExceptionResolver{
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object obj,
Exception e) {
ModelAndView mav = new ModelAndView();
mav.addObject("error", "页面发生错误。。");
mav.setViewName("error");
return mav;
}
}
其中obj表示发生异常的地方,是一个字符串,由包名+类名+方法名(形参)组成。可用于编写日志文件。
e表示异常的种类,通过e我们可以对不同类型的异常进行不同的处理。
- 交由Spring进行管理
在spirngmvc.xml中配置刚刚编写的异常处理器类。
<bean class="com.exception.ExceptionResolver"/>
- 当页面某处发生错误的时候,就会跳转到error页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
${error }
</body>
</html>
十、json数据交互
有的时候用来编写前端的并不是java语言,用来交互的话多数是通过json格式的字符串,为此我们需要掌握以下Controller类中方法对json字符串的接收和处理。
- 这里我们用ajax给Controller类发送一个json格式的字符串,首先要导入jquery,在WebContent下新建一个目录js,并导入jquery
- 引入jquery并用ajax给json.action发送一个json字符串
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>json</title>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>
<script type="text/javascript">
$(function(){
var params = '{"id":1,"username":"json"}';
$.ajax({
url : "${pageContext.request.contextPath }/json.action" ,
data : params,
contentType : "application/json;charset=UTF-8",
type : "post",
dataType : "json",//回调类型
success : function(data){
alert(data.username);
}
});
});
</script>
</head>
<body>
</body>
</html>
- 导入Springmvc的支持json的相关包
- 给UserController类添加一个响应发送过来的json格式字符串的方法,并使用@RequestBody,@ResponseBody标签,@RequestBody标签是用来将发送过来的json格式字符串转换成User类并注入,@ResponseBody标签是用来将返回的数据转换成json格式。
@RequestMapping(value = "/json.action")
public @ResponseBody User json(@RequestBody User user) {
return user;
}
运行结果:
十一、拦截器
springmvc提供了拦截器,让用户请求在到达Controller层之前、Contoller方法执行之后、页面渲染之后,先经过拦截器进行一系列的操作再返回给用户。
要使用拦截器的话,需要经过以下步骤:
- 手动编写拦截器类,preHandle返回true代表放行
public class Interceptor1 implements HandlerInterceptor{
//方法前
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("拦截器1-方法前");
return true;
}
//方法后
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
System.out.println("拦截器1-方法后");
}
//页面渲染后
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("拦截器1-页面渲染后");
}
}
public class Interceptor2 implements HandlerInterceptor{
//方法前
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("拦截器2-方法前");
return true;
}
//方法后
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
System.out.println("拦截器2-方法后");
}
//页面渲染后
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("拦截器2-页面渲染后");
}
}
- 在springmvc.xml中配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截规则 /**代表拦截所有-->
<mvc:mapping path="/**"/>
<!-- 自定义拦截器类 -->
<bean class="com.interceptor.Interceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.interceptor.Interceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
当两个拦截器都放行时访问页面控制台输出:
当第一个拦截器不放行时访问页面控制台输出:
当第一个拦截器放行第二个拦截器不放行访问页面控制台输出:
规律:
- preHandle方法按拦截器定义顺序调用
- postHandle方法按拦截器定义逆序调用
- afterCompletion方法按拦截器定义逆序调用
- postHandle当所有preHandle方法返回true才调用
- afterCompletion当preHandle方法返回true才调用
拦截器使用:给网页增加一个登录页面,如果没登录的话访问项目相关网页都会跳转到登录页面。
- 定义拦截器,当请求路径包含/login就放行,如果不包含/login的话从session域中取出键为USER的值,如果取出的值为空的话,重定向到登录页面。
public class Interceptor1 implements HandlerInterceptor{
//方法前
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
System.out.println("拦截器1-方法前");
//访问路径,不包括项目名
String requestURI = request.getRequestURI();
if(!requestURI.contains("/login")) {
String user = (String) request.getSession().getAttribute("USER");
String username = null,password = null;
if(user != null) {
username = user.split(",")[0];
password = user.split(",")[1];
}
if(username == null || !(username.equals("admin") && password.equals("123"))) {
response.sendRedirect(request.getContextPath() + "/login.action");
return false;
}
}
return true;
}
//方法后
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
System.out.println("拦截器1-方法后");
}
//页面渲染后
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("拦截器1-页面渲染后");
}
}
- Controller中添加两个方法,一个用于请求路径不是/login且session域中不包含键为USER的值时重定向到登录页面(GET),一个用于登录页面提交登录信息(POST)。
实现结果:访问listUser.jsp会跳转到login.jsp,当输入的用户名跟密码正确的时候,才会跳转到listUser.jsp。