SpringAOP详细介绍
什么是SpringAOP?
AOP意为面向切面编程,通过预编译的方式和运行期间动态代理实现程序功能的统一维护和一种技术,AOP是OOP的的延伸,是函数式编程的一种衍生范式,降低各个业务逻辑间的耦合性,提高程序的可用性。SpringAOP其实现方式为动态代理。
什么是代理模式?
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用:想在访问一个类时做一些控制。
如何解决:增加中间层。
关键代码:实现与被代理类组合。
静态代理
在编译期间,为目标对象创建一个代理类,以访问目标对象的功能,是实现代理的一种方式
下面以房东租房为例,提供一个中介类,代理房东出租房屋
/**
* 房屋出租的接口
* @author Awei
* @date 2022/10/14 20:12
*/
public interface Rent {
public void rent();
}
/**
* 房东类
* @author Awei
* @date 2022/10/14 20:14
*/
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东在出租房屋");
}
}
/**
* 中介代理类
* @author Awei
* @date 2022/10/14 20:16
*/
public class HostProxy implements Rent {
private Host host;
public HostProxy(Host host) {
this.host = host;
}
@Override
//租房
public void rent() {
seeHouse();
host.rent();
fare();
}
//看房
public void seeHouse(){
System.out.println("带房客看房");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
/**
* 租房案例
* @author Awei
* @date 2022/10/14 20:20
*/
public class Client {
public static void main(String[] args) {
HostProxy proxy = new HostProxy(new Host());//创建代理类
proxy.rent();//租房
}
}
运行结果
带房客看房
房东在出租房屋
收中介费
Process finished with exit code 0
由此可知,静态代理即在编译阶段为目标对象创建代理实体类,为目标对象进行功能的代理访问。
动态代理
动态代理为运行期间,生成目标对象的代理类以实现代理功能的实现,其底层使用到的是java反射,为运行期间对程序进行增强的一种方式
动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
基于接口的动态代理----JDK动态代理(为目标对象创建兄弟代理类,同时实现目标对象实现的接口,进行重写接口的方法)
基于类的动态代理------cglib动态代理(继承目标对象类,重写目标对象的方法)
JDK代理
JDK代理涉及到两个关键的类,Proxy,该类的newProxyInstance方法能为目标对象生成代理类
Params:
loader – the class loader to define the proxy class 代理类的类装入器
interfaces – the list of interfaces for the proxy class to implement 代理类要实现的接口
h – the invocation handler to dispatch method invocations to 将要调用的方法分派到的调用处理程序
Returns:
a proxy instance with the specified invocation handler of a proxy class that is defined by the specified class loader and that implements the specified interfaces
一个代理实例,具有由指定的类装入器定义,并实现指定接口的代理类的指定调用处理程序
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
一个是InvocationHandler接口中的invoke方法,该方法会调用目标对象并返回结果
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
proxy代理类的实例对象,method要调用的方法,args方法的参数,返回值为调用方法返回的结果,下面案例会输出这些参数。
同样基于房屋出租的案例
package com.cn.awei.service.com.cn.awei.pojo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author Awei
* @date 2022/10/14 20:34
*/
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
/**
* @return 获取目标对象的代理类
*/
public Object getProxyIntance(Object target) {
System.out.println("目标对象"+ target.getClass());
setTarget(target);
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
/**
* 实现房屋出租的功能,底层为反射调用目标对象的方法
* @param proxy 代理类
* @param method 代理类的调用处理程序的方法对象
* @param args 接口方法的参数,如果没有则为空
* @return 调用方法返回结果
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class p = proxy.getClass();
System.out.println("代理类"+p);
System.out.println("调用"+method.getName()+"方法");
Object result = method.invoke(target,args);
return result;
}
}
/**
* @author Awei
* @date 2022/10/14 20:59
*/
public class Client1 {
public static void main(String[] args) {
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
Rent rentProxy = (Rent) proxyInvocationHandler.getProxyIntance(new Host());//获取代理类
rentProxy.rent();//代理出租房屋
}
}
运行结果
目标对象class com.cn.awei.service.com.cn.awei.pojo.Host
代理类class com.sun.proxy.$Proxy0
调用rent方法
房东在出租房屋
Process finished with exit code 0
根据控制台打印的代理class,代理类为class com.sun.proxy.$Proxy0,即运行期间动态生成代理类为目标对象进行增强。
CGLIB代理
CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类。
CGLIB是通过继承的方式实现动态代理,因此如果某个类被标记为 final,那么它是无法使用CGLIB做动态代理的。
CGLIB代理涉及两个核心类
- MethodInterceptor 接口 调用处理程序
- Enhancer 类 获取目标对象的代理实例
下面以转账案例来实现CGLIB代理
package com.cn.awei.service;
/**
* 业务类
* @author Awei
* @date 2022/10/15 21:11
*/
public class TransferService {
public void transfer(String id,String name) {
System.out.println("调用dao层,完成转账主业务");
}
}
package com.cn.awei.aspectj;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 调用处理目标对象方法的程序,可以实现程序的增强
* @author Awei
* @date 2022/10/15 21:08
*/
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
identityCheck();
System.out.println("代理对象: "+ o.getClass());
System.out.println("调用方法: "+method.getName());
System.out.println("方法参数:"+ Arrays.toString(objects));
System.out.println("方法代理:"+methodProxy.getClass());
Object o1 = methodProxy.invokeSuper(o,objects);
return o1;
}
//前置增强--身份校验
public boolean identityCheck() {
System.out.println("校验通过");
return true;
}
}
package com.cn.awei.test;
import com.cn.awei.aspectj.CglibProxy;
import com.cn.awei.service.TransferService;
import org.springframework.cglib.proxy.Enhancer;
/**
* @author Awei
* @date 2022/10/15 21:15
*/
public class Test3 {
public static void main(String[] args) {
//1.创建目标对象
TransferService transferService = new TransferService();
System.out.println("目标对象:"+transferService.getClass());
//2.生成代理对象
TransferService transferServiceProxy = (TransferService) Enhancer.create(
transferService.getClass(), new CglibProxy());
//3.调用处理程序
transferServiceProxy.transfer("4789578","张三");
}
}
运行结果
目标对象:class com.cn.awei.service.TransferService
校验通过
代理对象: class com.cn.awei.service.TransferService$$EnhancerByCGLIB$$4c2e44cc
调用方法: transfer
方法参数:[4789578, 张三]
方法代理:class org.springframework.cglib.proxy.MethodProxy
调用dao层,完成转账主业务
Process finished with exit code 0
SpringAOP
SpringAOP相关名词
横切关注点:跨越应用程序多个模块的方法或功能。即是与我们业务逻辑无关的,但是我们需要关注的部分,如日志 , 安全 , 缓存 , 事务等
切面(Aspect):横切关注点 被模块化 的特殊对象。即,它是一个类。
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
目标(Target):被通知对象。
代理(Proxy):向目标对象应用通知之后创建的对象。
切入点(PointCut):切面通知 执行的 “地点”的定义。
连接点(JointPoint):与切入点匹配的执行点。
案例中使用到的类
package com.cn.awei.service;
/**
* 业务接口
*
* @author Awei
* @date 2022/10/14 22:05
*/
public interface UserService {
public String add();
public String delete();
public String update();
public String search();
}
package com.cn.awei.service.impl;
import com.cn.awei.service.UserService;
import org.springframework.stereotype.Service;
/**
* @author Awei
* @date 2022/10/14 22:07
*/
@Service
public class UserServiceImpl implements UserService {
@Override
public String add() {
System.out.println("增加用户");
return "增加用户成功";
}
@Override
public String delete() {
System.out.println("删除用户");
return "删除用户成功";
}
@Override
public String update() {
System.out.println("更新用户");
return "更新用户成功";
}
@Override
public String search() {
System.out.println("查询用户");
return "查询用户成功";
}
}
测试类
package com.cn.awei.test;
import com.cn.awei.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Awei
* @date 2022/10/14 22:23
*/
public class Test1 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
SpringAOP实现方式1
定义一个切面类
package com.cn.awei.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 自定义一个切面类
* @author Awei
* @date 2022/10/14 22:48
*/
public class AspectService {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("---------方法环绕前---------");
Object proceed = joinPoint.proceed();
System.out.println("---------方法环绕后---------");
return proceed;
}
}
配置AOP配置文件
<!--注册bean-->
<bean id="diy" class="com.cn.awei.aspectj.AspectService"/>
<!--aop的配置-->
<aop:config>
<!--第一种方式:使用AOP的切面标签来实现-->
<aop:aspect ref="diy">
<aop:pointcut id="AspectjService" expression="execution(* com.cn.awei.service.impl.UserServiceImpl.*(..))"/>
<aop:around method="around" pointcut-ref="AspectjService"/>
<aop:after method="after" pointcut-ref="AspectjService"/>
<aop:before method="before" pointcut-ref="AspectjService"/>
</aop:aspect>
</aop:config>
运行结果
---------方法环绕前---------
---------方法执行前---------
增加用户
---------方法执行后---------
---------方法环绕后---------
Process finished with exit code 0
SpringAOP实现方式2
通过 Spring API 实现,实现AOP的增强advice接口API
定义一个切面类
package com.cn.awei.aspectj;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* 切面类
* @author Awei
* @date 2022/10/14 23:32
*/
public class AspectjService1 implements MethodBeforeAdvice,
MethodInterceptor,
AfterReturningAdvice{
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
//System.out.println( o.getClass().getName() + "的" + method.getName() + "方法执行前");
System.out.println("---------方法执行前---------");
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//System.out.println(methodInvocation.getMethod().getName()+"方法环绕前");
System.out.println("---------方法环绕前---------");
Object o = methodInvocation.proceed();
System.out.println("---------方法环绕后---------");
//System.out.println(methodInvocation.getMethod().getName()+"方法环绕后");
return o;
}
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
//System.out.println( o.getClass().getName() + "的" + method.getName() + "方法执行后");
System.out.println("---------方法执行后---------");
}
}
配置AOP配置文件
<bean id="diy1" class="com.cn.awei.aspectj.AspectjService1"></bean>
<aop:config>
<!-- 第二种:使用aop的api接口实现-->
<aop:pointcut id="pointcutexecution" expression="execution(* com.cn.awei.service.impl.UserServiceImpl.*(..))"/>
<!--切点+切面-->
<aop:advisor advice-ref="diy1" pointcut-ref="pointcutexecution"/>
</aop:config>
运行结果
---------方法环绕前---------
---------方法执行前---------
增加用户
---------方法执行后---------
---------方法环绕后---------
Process finished with exit code 0
注:MethodInterceptor接口不是aop包里的,是intercept包中的拦截器中的方法
SpringAOP实现方式3
基于注解方式实现AOP
定义一个切面类
package com.cn.awei.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* @author Awei
* @date 2022/10/14 22:17
*/
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.cn.awei.service.impl.UserServiceImpl.*(..))")//切入点
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.cn.awei.service.impl.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.cn.awei.service.impl.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("---------方法环绕前---------");
//System.out.println("签名:"+jp.getSignature());
//执行目标方法proceed
Object proceed = jp.proceed();
//System.out.println("返回结果"+proceed);
System.out.println("---------方法环绕后---------");
}
}
配置文件
<!-- 第三种方式 -->
<bean id="annotationPointcut" class="com.cn.awei.aspectj.AnnotationPointcut"/>
<!--该标签为动态代理的自动创建,使用时需要在切面类上使用@Aspect注解-->
<aop:aspectj-autoproxy/>
<aop:aspectj-autoproxy />
有一个proxy-target-class
属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class="true"/>
时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false
,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
---------方法环绕前---------
---------方法执行前---------
增加用户
---------方法执行后---------
---------方法环绕后---------
Process finished with exit code 0
SpringAOP实现方式4
基于注解开发
定义一个注解,之后基于打印日志的方法都使用该注解,注解使用在方法上,调用业务方法时生效
package com.cn.awei.aspectj;
import java.lang.annotation.*;
/**
* @author Awei
* @date 2022/10/15 0:11
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequiredLog {
}
定义一个切面类
package com.cn.awei.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author Awei
* @date 2022/10/15 0:26
*/
@Aspect
@Component
public class LogGenerate {
//与之前的切入点表达式不同,使用@annotation表达式后面加注解全限定名的方式,后续直接在方法上使用定义的注解即可达到与之前切入点的作用
@Pointcut("@annotation(com.cn.awei.aspectj.RequiredLog)")
public void doLog(){}//承载切入点的定义
/**如不使用承载的方式,可直接在通知上使用如:
@Before("@annotation(com.cn.awei.aspectj.RequiredLog)")
public void before() {} */
@Before(value = "doLog()")
public void before() {
System.out.println("---------方法执行前---------");
}
@Around(value = "doLog()")
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("---------方法环绕前---------");
Object proceed = point.proceed();
System.out.println("---------方法环绕后---------");
return proceed;
}
@After(value = "doLog()")
public void after() {
System.out.println("---------方法执行后---------");
}
}
处理请求,在GetMapping请求上使用@RequiredLog注解,每次请求发到Controller时,处理业务会生成日志或进行日志打印
package com.cn.awei.controller;
import com.cn.awei.aspectj.RequiredLog;
import com.cn.awei.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Awei
* @date 2022/10/15 0:37
*/
@RestController
public class UserController {
@Autowired
UserService userService;
@RequiredLog
@GetMapping ("/user")
public String user(){
String result = userService.add();
return result;
}
}
启动项目,发送请求
运行结果
---------方法环绕前---------
---------方法执行前---------
增加用户
---------方法执行后---------
---------方法环绕后---------
拦截器介绍
1.概念:Java 里的拦截器是动态拦截action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行,同时也提供了一种可以提取action中可重用部分的方式。在 AOP(面向切面编程)中拦截器用于在某个方法(包括构造器)或字段被访问之前进行拦截,然后在之前或之后加入某些操作。可以使用 AspectJ 与 SpringAOP 进行集成,以实现更细粒度或更多方面的拦截操作。
2.原理:拦截器Interceptor的拦截功能是基于 Java 的动态代理来实现的
3.实现方式:在 Spring 框架之中,我们要想实现拦截器的功能,主要通过两种途径,第一种是实现HandlerInterceptor接口,第二种是实现WebRequestInterceptor接口。
HandlerInterceptor接口
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}
方法:
boolean preHandle():
该方法在请求处理之前进行调用,Spring MVC 中的Interceptor是链式调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor。每个Interceptor的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor中的preHandle方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求做一个预处理,
该方法的返回值是布尔(Boolean)类型的,当它返回为false时,表示请求结束,后续的Interceptor和控制器(Controller)都不会再执行;当返回值为true时,就会继续调用下一个Interceptor的preHandle方法。
void postHandle():
进行业务逻辑处理后调用;postHandle方法在当前请求进行处理之后,也就是在控制器中的方法调用之后执行,但是它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对控制器处理之后的ModelAndView对象进行操作。
Void afterCompletion():
在整个请求处理完成后,即渲染视图之后执行,可以通过此方法进行一些资源清理,记录日志信息等工作
拦截器的工作流程:
代码实现
1.定义拦截器
package com.jt.aop;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class CustomerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("Constomer preHandle业务执行前");
return true;//放行
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Constomer postHandle业务执行后");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex)
throws Exception {
System.out.println("Constomer afterCompletion请求完成后");
}
}
2.注册拦截器
package com.jt.config;
import com.jt.aop.CustomerInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CustomerInterceptorConfig implements WebMvcConfigurer {
@Autowired
private CustomerInterceptor customerInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册自己写的拦截器,如果是多个拦截器,形成一个拦截器链
//拦截器链执行顺序:
// preHandle按拦截器定义顺序调用
//postHandler按拦截器定义逆序调用
//afterCompletion按拦截器定义逆序调用
registry.addInterceptor(customerInterceptor);
}
}
这种方式添加拦截器会对所有请求进行拦截,可加白名单进行过滤
// 白名单
List<String> patterns = new ArrayList<String>();
patterns.add("/css/**");
patterns.add("/images/**");
patterns.add("/js/**");
patterns.add("/web/login.html");
patterns.add("/web/index.html");
patterns.add("/web/product.html");
patterns.add("/users/reg");
patterns.add("/users/login");
// 通过注册工具添加拦截器
registry.addInterceptor(customerInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(patterns);
3.拦截效果显示
模拟发起一个hello请求,并输出hello!
请求处理完成后,由下图可知在打印hello前执行了preHandle方法,然后放行,依次执行拦截器之后的两个方法
WebRequestInterceptor接口
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
public interface WebRequestInterceptor {
void preHandle(WebRequest request) throws Exception;
void postHandle(WebRequest request, ModelMap model) throws Exception;
void afterCompletion(WebRequest request, Exception ex) throws Exception;
}
该接口与HandlerInterceptor接口接口类似,方法名都是相同,只有参数上的不同,参数都携带WebRequest接口对象,可以进行请求参数对象数据的传递,说明主要用于对请求进行拦截处理,接口方法参数中没有response。
方法:
void preHandle(WebRequest request):
该方法在请求处理之前进行调用,也就是说,其会在控制器中的方法调用之前被调用。这个方法跟HandlerInterceptor中的preHandle不同,主要区别在于该方法的返回值是void类型的,也就是没有返回值,因此我们主要用它来进行资源的准备工作。
postHandle(WebRequest request, ModelMap model)
该方法在请求处理之后,也就是在控制器中的方法调用之后被调用,但是会在视图返回被渲染之前被调用,所以可以在这个方法里面通过改变数据模型ModelMap来改变数据的展示。该方法有两个参数,WebRequest对象是用于传递整个请求数据的。
afterCompletion(WebRequest request, Exception ex)
该方法会在整个请求处理完成,也就是在视图返回并被渲染之后执行。因此可以在该方法中进行资源的释放操作。而WebRequest参数就可以把我们在preHandle中准备的资源传递到这里进行释放。Exception参数表示的是当前请求的异常对象,如果在控制器中抛出的异常已经被 Spring 的异常处理器给处理了的话,那么这个异常对象就是是null。
代码实现
1.定义拦截器
实现WebRequestInterceptor接口,重写接口里的3个方法
package com.jt.aop;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
@Component
public class UserWebRequestInterceptor implements WebRequestInterceptor {
@Override
public void preHandle(WebRequest webRequest) throws Exception {
System.out.println("controller方法执行前----preHandle");
}
@Override
public void postHandle(WebRequest webRequest, ModelMap modelMap) throws Exception {
System.out.println("controller方法执行后----postHandle");
}
@Override
public void afterCompletion(WebRequest webRequest, Exception e) throws Exception {
System.out.println("request整个请求处理后----preHandle");
}
}
2.注册拦截器
定义一个配置类,实现 WebMvcConfigurer接口,重写addInterceptors方法,实现拦截器的注册
package com.jt.config;
import com.jt.aop.UserWebRequestInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.*;
@Configuration
public class UserInterceptorConfig implements WebMvcConfigurer {
@Autowired
public HandlerInterceptor handlerInterceptor;
@Autowired
private UserWebRequestInterceptor userWebRequestInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册拦截器
//registry.addInterceptor(handlerInterceptor);
registry.addWebRequestInterceptor(userWebRequestInterceptor);
}
}
3.拦截效果
模拟发起一个hello请求,并输出hello!
请求处理完成后,由图可知在打印hello前执行了preHandle方法,然后放行,依次执行拦截器之后的两个方法
多个拦截器的执行顺序
开发中常用多个拦截器对请求进行处理拦截,假设有两个拦截器Interceptor1和Interceptor2,并且在配置文件种,Interceptor1的配置在前面,则执行流程的顺序如下图所示:
说明:当有多个拦截器同时工作时,它们的preHandle()方法会按照配置文件中拦截器的配置顺序执行,而它们的postHandle()方法和afterCompletion()方法则会按照配置顺序的反序执行。注:如果拦截器Interceptor1的preHandle方法返回false,则之后的所有方法将不会再执行,包括拦截器Interceptor2的方法。
修改方法中的返回值,注册拦截器1,2
多个拦截器拦截效果展示
在Interceptor1的preHandle方法返回true的情况下,拦截效果如下图所示:
在Interceptor1的preHandle方法返回false的情况下,拦截效果如下图所示:只执行了拦截器1的preHandle的方法
总结:
- preHandle按拦截器定义顺序调用
- postHandler按拦截器定义逆序调用
- afterCompletion按拦截器定义逆序调用
- postHandler在拦截器链内所有拦截器返回成功调用
- afterCompletion只有preHandle返回true才调用