SpringMVC
-
流程图
图片1.png
使用步骤
- SpringMVC的全局配置文件(web.xml)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- 配置:所有请求由SpringMVC管理 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 代表拦截.do结尾的请求 -->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- 代表拦截/rest/下的所有路由 -->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<!-- 配置编码过滤器 -->
<!-- 解决post请求的中文乱码问题,get的中文乱码问题该tomcat的配置,而且tomcat7及之前的才会乱码,之后的默认UTF8编码 -->
<filter>
<filter-name>EncodingFilter</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>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
- 基本配置文件 DispatcherServlet-servlet.xml(名称固定,创建在WEB-INF下)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
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-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<!-- 注解配置控制器-->
<!-- 1.配置扫描包,告知spring扫描哪些包下的控制器-->
<context:component-scan base-package="com.zengqiang.backoffice.web.controller"/>
<!-- 2.配置注解处理映射,spring会调用这个处理器,处理器会调用3适配器,然后3处理1中扫描的进行匹配-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!-- 3.配置适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<!-- 4.配置资源视图解析器,其实就是配置jsp这种视图文件的路径-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/views/"></property>
<!--后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
- 注解控制器
@Controller
// 添加根路径的注解
@RequestMapping("/user")
public class UserController{
// 该种方式只能POST方法访问,默认都可以
//@RequestMapping(value = "list",method = RequestMethod.POST)
@RequestMapping("list")
public String list(Model model){
//1.模拟数据库的数据
List<User> userList = new ArrayList<User>();
User user1 = new User("习总",60,"男",new Date());
User user2 = new User("李总",50,"男",new Date());
User user3 = new User("上官",18,"男",new Date());
user1.setId(1);
user2.setId(2);
user3.setId(3);
userList.add(user1);
userList.add(user2);
userList.add(user3);
//2.把数据存在model
model.addAttribute("userList",userList);
//这种类型的返回,其实就是WEB-INF下面的views文件夹下面的user文件夹下对应的jsp页面
//而上面的2数据封装到bean,其实就是封装之后给jsp页面使用
return "user/userlist";
}
@RequestMapping("/toRegister")
public String toRegister(){
return "user/register";
}
/**
* 第一种接收表单参数的方式:
* 默认日期格式 月/日/年 10/10/2016
* @return
*/
@RequestMapping("/register")
public String register(String username,String password,
int age,String gender,Date birthday,
String[] hobbyIds){
System.out.println(username);
System.out.println(password);
System.out.println(age);
System.out.println(gender);
System.out.println(birthday);
System.out.println(Arrays.toString(hobbyIds));
return "user/info";
}
/**
* 第二种接收表单参数的方式:
* 默认日期格式 月/日/年 10/10/2016
* @return
*/
@RequestMapping("/register2")
public String register2(User user){
System.out.println(user);
return "user/info";
}
/**
* 第三种接收表单参数的方式:
* @return
* 相当于模型里有模型
*/
@RequestMapping("/register3")
public String register3(UserExt userExt){
//BindingAwareModelMap m;
//此时如果User类产生的对象是UserExt对象的一个属性,则在jsp中使用时候,要user.username这种类似的操作
//否则无法使用
System.out.println(userExt);
return "user/info";
}
//接收集合List类型参数
@RequestMapping("/register4")
public String register4(UserExt userExt){
//BindingAwareModelMap m;
System.out.println(userExt.getUsers());
// 例如UserExt中user是List形式存在,则在jsp中要userlist[0].username形式使用
return "user/info";
}
//接收集合Map类型参数
@RequestMapping("/register5")
public String register5(UserExt userExt){
//BindingAwareModelMap m;
//例如UserExt中有:Map<String ,Object> infos=new HashMap();
//在jsp中使用infos['username']
System.out.println(userExt.getInfos());
return "user/info";
}
@RequestMapping("/edit")
public String edit(int id,Model model){
System.out.println("接收到修改的ID:" + id);
//通过id,查询数据库,返回 一个User对象,把user对象存在model
User user = new User("习总",60,"男",new Date());//假设从数据查
user.setId(1);
model.addAttribute("user",user);
return "user/useredit";
}
//@PathVariable代表路径的id对应参数的id,注意此时的{id}不是参数,是子路由
@RequestMapping("/edit1/{id}")
public String edit1(@PathVariable int id,Model model){
System.out.println("接收到修改的ID:" + id);
//通过id,查询数据库,返回 一个User对象,把user对象存在model
User user = new User("习总",60,"男",new Date());//假设从数据查
user.setId(1);
model.addAttribute("user",user);
return "user/useredit";
}
@RequestMapping("test1")
public String test1(){
//同一个控制器转发,转发到同一个控制器
return "forward:list.do";
}
@RequestMapping("/test1")
public String test1() {
//不同控制器转发
//return "forward:/user/list.do";
//不同控制器重定向
return "redirect:/user/list.do";
}
/**
此时RequestParam针对的是真的参数例如 ?30
* RequestParam参数描述
* value:参数名称
* defaultValue:默认值
* required:参数是否必须有值,如果为true,参数又为空,会报错,如有默认值则会使用默认值,不报错
*
*/
@RequestMapping("/test2")
public String test2(@RequestParam(value = "uid",required = true,defaultValue = "30") Integer uid) {
return "redirect:/user/list.do";
}
}
多试图处理
多视图是一个方法可以返回json/xml等格式的数据
- 导入xml格式支持的jar包
spring-oxm-3.2.0.RELEASE.jar
- 配置支持多视图
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<!-- 配置支持媒体类型 -->
<property name="contentNegotiationManager">
<bean
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json"></entry>
<entry key="xml" value="application/xml"></entry>
</map>
</property>
</bean>
</property>
<!-- 指定默认视图 -->
<property name="defaultViews">
<!-- 支持多个视图 -->
<list>
<!-- 对json格式视图支持 -->
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
<!-- xml格式视图支持 -->
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg>
<bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<!-- 代表哪些类可以支持转换成json或者xml -->
<value>com.zengqiang.backoffice.domain.Student</value>
</list>
</property>
</bean>
</constructor-arg>
</bean>
</list>
</property>
</bean>
- 例如2中com.zengqiang.backoffice.domain.Student是需要处理的pojo对象,务必要提供无参构造方法
而且Student如果是提供返回xml还要加上xmlrootelement注解
ResponseBody和RequestBody
- @ResponseBody把后台pojo转换json对象,返回到页面。
- @RequestBody接受前台json数据,把json数据自动封装javaBean
使用步骤
-
导入json的jar
图2.png 添加json转换器
在配置文件DispatcherServlet-servlet.xml的3适配器中
图3.png
- java代码处理
@RequestMapping("/register")
//参数中的@ResponseBody将json转换成对象
//public后面的@ResponseBody是将json对象封装成json返回
public @ResponseBody Student test2(@ResponseBody Student stu) {
return stu;
}
DispatcherServlet 组件类
Spring Web MVC 框架是围绕 DispatcherServlet 设计的,它处理所有的 HTTP 请求和响应。 Spring Web MVC DispatcherServlet 的请求处理工作流如下图所示:
以下是对应于到 DispatcherServlet 的传入 HTTP 请求的事件顺序:
- 在接收到 HTTP 请求后,DispatcherServlet 会查询 HandlerMapping 以调用相应的 Controller。
- Controller 接受请求并根据使用的 GET 或 POST 方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给 DispatcherServlet。
- DispatcherServlet 将从 ViewResolver 获取请求的定义视图。
- 当视图完成,DispatcherServlet 将模型数据传递到最终的视图,并在浏览器上呈现。
所有上述组件,即: HandlerMapping,Controller 和 ViewResolver 是 WebApplicationContext 的一部分,它是普通 ApplicationContext 的扩展,带有 Web 应用程序所需的一些额外功能。
拦截器
Spring Web MVC 的处理器拦截器,类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理
常见应用场景
日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算 PV(Page View)等
权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面
性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间
通用行为:读取 Cookie 得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取 Locale、Theme 信息等,只要是多个处理器都需要的即可使用拦截器实现
拦截器声明周期说明
Spring MVC 拦截器需要实现 HandlerInterceptor 接口,该接口定义了 3 个方法,分别为 preHandle()、postHandle() 和 afterCompletion(),咱们就是通过重写这 3 个方法来对用户的请求进行拦截处理的。
preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)
:该方法在请求处理之前进行调用。Spring MVC 中的 Interceptor 是链式调用的,在一个应用中或者说是在一个请求中可以同时存在多个 Interceptor 。每个 Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是 Interceptor 中的preHandle
方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求做一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值 Boolean 类型的,当它返回为false
时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当返回值为true
时,就会继续调用下一个 Interceptor 的preHandle
方法,如果已经是最后一个 Interceptor 的时候,就会是调用当前请求的 Controller 中的方法。postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)
:通过preHandle
方法的解释咱们知道这个方法包括后面要说到的afterCompletion
方法都只能在当前所属的 Interceptor 的preHandle
方法的返回值为true
的时候,才能被调用。postHandle
方法在当前请求进行处理之后,也就是在 Controller 中的方法调用之后执行,但是它会在DispatcherServlet
进行视图返回渲染之前被调用,所以咱们可以在这个方法中对 Controller 处理之后的ModelAndView
对象进行操作。postHandle
方法被调用的方向跟preHandle
是相反的,也就是说,先声明的 Interceptor 的postHandle
方法反而会后执行。afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)
:也是需要当前对应的 Interceptor 的preHandle
方法的返回值为true
时才会执行。因此,该方法将在整个请求结束之后,也就是在DispatcherServlet
渲染了对应的视图之后执行,这个方法的主要作用是用于进行资源清理的工作。
/**
* 登录拦截器
* <p>Title: LoginInterceptor</p>
* <p>Description: </p>
*/
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
User user = (User) httpServletRequest.getSession().getAttribute("user");
// 判断用户是否登录
if (user == null) {
// 用户未登录,重定向到登录页
httpServletResponse.sendRedirect("/login");
return false;
}
// 放行
return true;
}
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
// 如果请求来自登录页
if (modelAndView.getViewName().endsWith("login")) {
// 则直接重定向到首页不再显示登录页
httpServletResponse.sendRedirect("/main");
}
}
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
spring-mvc.xml 中配置拦截器
拦截器定义后还需要在 spring-mvc.xml 中配置拦截器,代码如下:
<!-- 拦截器配置,拦截顺序:先执行后定义的,排在第一位的最后执行。-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/static/**"/>
<mvc:exclude-mapping path="/login"/>
<bean class="com.funtl.my.shop.web.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
相关配置说明:
mvc:interceptor:定义一个拦截器
mvc:mapping:映射路径,需要拦截的请求路径
mvc:exclude-mapping:需要排除的请求路径,比如登录页本身是不需要拦截的,这里还包括了静态资源路径也是不需要拦截的
bean class:配置指定的拦截器对象