1、拦截器
在之前学习JAVAWEB 的时候,我们学习了过滤器的知识。过滤器的作用是保护请求的服务器资源,在请求资源被执行之前,如果请求地址符合拦截范围,则会先执行过滤器。过滤器的执行时机,是在Servlet之前执行的。但是在使用了SpringMVC后,Servlet只有一个了,也就是DisptcherServlet。那么,如果我们仍然使用过滤器来完成请求的拦截,因为过滤器是在Servlet之前执行的,就会造成,过滤器会拦截DispatcherServlet所有的请求。那么,如果我们有部分请求不想被拦截,怎么办?
Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。
要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。通常拦截器类可以通过两种方式来定义。
1.通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
2.通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。
拦截器和过滤器的区别
1拦截器SpringMVC的,而过滤器是servlet的。
2拦截器不依赖与servlet容器,由spring容器初始化,过滤器依赖与servlet容器,由servlet容器初始化。
3拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
5在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
6拦截器可以获取IOC容器中的各个bean,而过滤器就不太方便,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
定义一个拦截器
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/*在请求到达我们定义的handler之前工作的*/
System.out.println("MyInterceptor preHandle");
/*返回的是true,代表放行,可以继续到达handler。handler就是自己的controller中一个请求的方法请求单元*/
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor postHandle");
/*handler 处理单元返回ModelAndView 时候进行 拦截*/
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
/*
页面渲染完毕,但是还没有给浏览器响应数据的时候
*/
System.out.println("MyInterceptor afterCompletion");
}
}
springmvc.xml中注册拦截器
<!--注册拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/login.action"/>
<bean id="myInterceptor" class="com.msb.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
拦截器内容详解
UserController
@Controller
public class UserController {
/*注入userService对象*/
@Autowired
private UserService userService;
@RequestMapping("login.action")
public ModelAndView login(String username,String password){
ModelAndView mv = new ModelAndView();
Map<String, Object> model = mv.getModel();
model.put("msg","脏话,你好");
System.out.println("login.action");
User user=userService.findUser(username,password);
if(null!=user){
mv.setViewName("/success.jsp");
}else{
mv.setViewName("/fail.jsp");
}
int i=1/0;
return mv;
}
}
1、preHandle方法
执行时机
再进入控制单元方法之前执行
如何调用
按拦截器定义顺序调用
具体作用
如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去 进行处理,则返回 true。 如果程序员决定不需要再调用其他的组件去处理请求,则返回 false。
参数详解
HttpServletRequest arg0,拦截的请求的request对象
HttpServletResponse arg1, 拦截的请求的response对象
Object arg2 封存了单元方法对象的HandleMethod对象
/**
*
* @param request 请求对象
* @param response 响应对象
* @param handler 目标要调用的Handler
* @return 返回true放行,返回false拦截
* @throws Exception 抛出异常
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/*在请求到达我们定义的handler之前工作的*/
System.out.println("MyInterceptor preHandle");
/*设置请求和响应的乱码处理*/
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
// 判断是否登录
/*User user =(User) request.getSession().getAttribute("user");
if(null == user)
response.sendRedirect("index.jsp");
return false;*/
// 用户权限控制
/*返回的是true,代表放行,可以继续到达handler。handler就是自己的controller中一个请求的方法请求单元*/
return true;
}
2、postHandle方法
执行时机
在进行数据处理和做出响应之间进行这个方法的调用
如何调用
在拦截器链内所有拦截器返成功调用
具体作用
在业务处理器处理完请求后,但是 DispatcherServlet 向客户端返回响应前被调用,
在该方法中对用户请求 request域数据进行处理。
参数详解
HttpServletRequest arg0, 拦截的请求的request对象
HttpServletResponse arg1, 拦截的请求的response对象
Object arg2, 封存了单元方法对象的HandleMethod对象
ModelAndView arg3 封存了单元方法的返回值资源路径和请求转到的Map数据
/**
*
* @param request
* @param response
* @param handler
* @param modelAndView controller响应的结果,视图和数据
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor postHandle");
/*handler 处理单元返回ModelAndView 时候进行 拦截*/
/*控制数据*/
/*Map<String, Object> map = modelAndView.getModel();
String msg = (String) map.get("msg");
String newMsg = msg.replaceAll("脏话", "***");
map.put("msg",newMsg);*/
/*控制视图*/
//modelAndView.setViewName("/testDemo1.jsp");
}
3、afterCompletion方法
执行时机
在进行页面渲染的时候执行
如何调用
按拦截器定义逆序调用
具体作用
在DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
参数详解
HttpServletRequest arg0, 拦截的请求的request对象
HttpServletResponsearg1, 拦截的请求的response对象
Object arg2, 封存了单元方法对象的HandleMethod对象
Exception arg3 存储了责任链的异常信息
/**
* 无论controller是否出现异常,都会执行的方法
* 一般来说都做一些资源释放工作
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
/*
页面渲染完毕,但是还没有给浏览器响应数据的时候
*/
System.out.println("MyInterceptor afterCompletion");
System.out.println(ex);
}
多个拦截器执行顺序
多个拦截器同时存在时,执行的顺序由配置顺序决定. 先配置谁, 谁就先执行.多个拦截器可以理解为拦截器栈, 先进后出(后进先出), 如图所示:
<!--注册拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/login.action"/>
<bean id="myInterceptor1" class="com.msb.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/login.action"/>
<bean id="myInterceptor2" class="com.msb.interceptor.MyInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
MyInterceptor preHandle
MyInterceptor2 preHandle
login.action
MyInterceptor2 postHandle
MyInterceptor postHandle
success.jsp
MyInterceptor2 afterCompletion
MyInterceptor afterCompletion
2、异常处理
SpringMVC异常简介
系统中异常包括两类:预期异常(检查型异常)和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息, 后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端控制器交由异常处理器进行异常处理,如下图
异常处理具体实现
1、使用@ExceptionHandler注解处理异常
缺点:只能处理当前Controller中的异常。
@Controller
public class ControllerDemo1 {
@RequestMapping("test1.action")
public String test1(){
int i=1/0;
return "success.jsp";
}
@RequestMapping("test2.action")
public String test2(){
String s = null;
System.out.println(s.length());
return "success.jsp";
}
@ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
public ModelAndView handeException(){
ModelAndView mv = new ModelAndView();
mv.setViewName("error1.jsp");
return mv;
}
}
2、使用:@ControllerAdvice+@ExceptionHandler
此处优先级低于局部异常处理器
@ControllerAdvice
public class GloadExceptionHandler1 {
@ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
public ModelAndView handeException(){
ModelAndView mv = new ModelAndView();
mv.setViewName("error1.jsp");
return mv;
}
}
配置包扫描
<context:component-scan base-package="com.wml.service,com.wml.exceptionhandler"/>
3、使用:SimpleMappingExceptionResolver
第一种:直接xml配置:
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArithmeticException">redirect:/error1.jsp</prop>
<prop key="java.lang.NullPointerException">redirect:/error2.jsp</prop>
</props>
</property>
</bean>
第二种:配置类配置:
/**
* 全局异常
*/
@Configuration
public class GloableException {
@Bean
public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties prop = new Properties();
prop.put("java.lang.NullPointerException", "error2.jsp");
prop.put("java.lang.ArithmeticException", "error1.jsp");
resolver.setExceptionMappings(prop);
return resolver;
}
}
xml配置:
<context:component-scan base-package="com.wml.service,com.wml.config"/>
4、自定义的HandlerExceptionResolver
@Configuration
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest Request, HttpServletResponse Response, Object o, Exception ex) {
ModelAndView mv = new ModelAndView();
if(ex instanceof NullPointerException){
mv.setViewName("error2.jsp");
}
if(ex instanceof ArithmeticException){
mv.setViewName("error1.jsp");
}
mv.addObject("msg",ex);
return mv;
}
}
xml配置:
<context:component-scan base-package="com.wml.service,com.wml.config"/>
error1.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
this si error1 page
${msg}
</body>
</html>
error2.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
this si error2 page
${msg}
</body>
</html>
3、注解
SpringMVC中注解完善
1、@PostMapping
作用:指定当前发送请求的方式只可以是post请求
属性: 和@RequestMapping中属性一致
代码实现
@RequestMapping(value = "/userControllerA",method = RequestMethod.POST);
@PostMapping("/userControllerA")
public String userControllerA(){
return "forward:/success.jsp";
}
2、@GetMapping
作用:指定当前发送请求的方式只可以是get请求
属性: 和@RequestMapping中属性一致
代码实现:
//@RequestMapping(value = "/userControllerA",method = RequestMethod.GET);
@GetMapping("/userControllerA")
public String userControllerA(){
return "forward:/success.jsp";
}
3、@RestController
作用:书写到类上,代表该类中所有控制单元方法均是ajax响应 相当于@ResponseBody+@Controller
属性: 其中的属性和@Controller中一样
代码实现:
@RestController
public class UserController {
}
4、@JsonFormat
作用: 处理响应json 数据的处理
属性:
pattern :指定响应时间日期的格式
Timezone:指定响应的时区,否则会有8个小时的时间差
代码实现:
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd" ,timezone="GMT+8")
private Date birth;
==AnnoController ==
@Controller
public class AnnoController {
@RequestMapping("demo1.action")
@ResponseBody
public Emp demo1(Emp emp){
System.out.println(emp);
return emp;
}
}
Emp
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Emp {
private String ename;
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private Date hiredate;
}
testJson.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="js/jquery.min.js"></script>
<script>
$(function (){
$.ajax({
url:"demo1.action",
data:"ename=zhangsan&hiredate=2020-2-2",
get:"get",
success:function (result){
console.log(result)
}
})
})
</script>
</head>
<body>
</body>
</html>
5、@RequestBody
作用:
用于获取请求体json格式的字符串内容。直接使用得到是 key=value&key=value…结构的数据,get 请求方式不适用。
属性:
required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。如果取值 为 false,get 请求得到是null。
实现:
==AnnoController ==
@Controller
public class AnnoController {
@RequestMapping("demo1.action")
@ResponseBody
public Emp demo1(@RequestBody(required = false) Emp emp){
System.out.println(emp);
return emp;
}
}
testJson.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="js/jquery.min.js"></script>
<script>
/*
* 1、 键值对形式 get请求发送 数据量小
* 2、JSON格式对象:要将 get:"get"改为get:"post"
* 还得告诉后台当前请求的方式: contentType:"application/json",
* data:'{ename:"zhangsan",hiredate:"2020-2-2"}'
*
* */
$(function (){
//var jsonObj={ename:"zhangsan",hiredate:"2020-2-2"}
//var jsonStr=JSON.stringify(jsonObj)
$.ajax({
url:"demo1.action",
data:'{"ename":"zhangsan","hiredate":"2020-2-2"}',
//data:jsonStr,
type:"post",
contentType:"application/json",//告诉后台当前请求的方式
success:function (result){
console.log(result)
}
})
})
</script>
</head>
<body>
</body>
</html>
6、@CrossOrigin
什么是跨域
出于浏览器的同源策略限制。同源策略(SameOriginPolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
http://127.0.0.1:8080/msb/index.jsp基础
https://127.0.0.1:8080/msb/index.jsp 协议不一样
http://192.168.24.11:8080/msb/index.jsp IP不一致
http://127.0.0.1:8888/msb/index.jsp 端口不一致
http://localhost:8080/msb/index.jsp IP不一致
作用:解决ajax请求之间的跨域问题
属性:
origins : 允许可访问的域列表IP
maxAge:准备响应前的缓存持续的最大时间(以秒为单位)。
代码实现:
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@GetMapping("/{id}")
public Account receive(@PathVariable Long id) { }
}