一、原因
AOP,一个神秘而又强大的功能,面试必问的知识点。
AOP的实现主要有两种方式,一种是通过回调函数,另一种是代理。回调函数指的过滤器、拦截器这种,代理指的JDK代理和CGLib代理。这篇文章只讲代理的方式,对过滤器、拦截器不太了解的可以看Filter 过滤器自定义及原理分析、拦截器HandlerInterceptor(SpringMVC)自定义及原理分析
首先说一下代理模式:
定义:为另一个对象提供一个替身或者占位符以控制对这个对象的访问。这句话引用自《Head First 设计模式 》。
MethodInterceptor是AOP项目中的拦截器,它拦截的目标是方法,即使不是Controller中的方法。
二、自定义AOP
有两种实现方式:一种是实现MethodInterceptor接口,另一种利用Aspect的注解或配置。实现MethodInterceptor接口的这种方法,还需要在配置文件中做配置。本文主要Aspect注解方式,个人觉得这种方法才比较灵活,与代码没有耦合。
添加Maven依赖
首先为SpringBoot项目pom文件中添加maven依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
DemoAspect切面类
package com.demo.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DemoAspect {
@Pointcut("execution(public * com.demo.service.DemoService.getUser(..))")
public void demoPointCut() {}
@Before("demoPointCut()")
public void beforeSay() {
System.out.println("=====beforeSay=====");
}
@After("demoPointCut()")
public void afterSay() {
System.out.println("=====afterSay=====");
}
@AfterReturning("demoPointCut()")
public void afterReturn() {
System.out.println("=====afterReturn=====");
}
@AfterThrowing("demoPointCut()")
public void afterThrow() {
System.out.println("=====afterThrow=====");
}
}
UserModel类
package com.demo.model;
import lombok.Data;
@Data
public class UserModel {
public UserModel(){}
public UserModel(String id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
private String id;
private String name;
private Integer age;
}
DemoController类
package com.demo.controller;
import com.demo.model.UserModel;
import com.demo.service.DemoService;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@Autowired
private DemoService demoService;
@RequestMapping("/user")
@GetMapping
public String list(HttpServletRequest request, String name){
String url = request.getRequestURI() + "?" + request.getQueryString();
UserModel userModel = new UserModel();
userModel.setName(name);
return demoService.getUser(name, userModel);
}
}
DemoService类
package com.demo.service;
import com.demo.model.UserModel;
import org.springframework.stereotype.Service;
@Service
public class DemoService {
public String getUser(String name, UserModel userModel) {
user = "zhangsan";
return userModel.getName();
}
}
引入maven依赖后,springboot默认开启AOP,所以不需要在启动类上添加@EnableAspectJAutoProxy注解也可以。
三、原理分析(本篇只讲AOP方法拦截器链的构造和执行过程)
AOP的实现分为两部分:
1、AOP代理对象的生成;
2、AOP方法拦截器链的构造和执行过程。
首先我们要知道的是,如果对象的方法被代理,在调用的时候使用的不再是对象本身,而是其代理对象。
在前置方法打断点,查看从Controller到调用自定义的AOP前置方法之间发生了哪些事情。
CglibAopProxy.DynamicAdvisedInterceptor#intercept()方法
在intercept()方法中我们看到了熟悉的身影,接下来让我们对拦截器链生成过程一探究竟。
拦截器链生成过程
advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)
advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice()
主要逻辑只需要关注registry.getInterceptors(advisor)这个方法,
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors();
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
for (Advisor advisor : advisors) {
......
// 主要逻辑
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
......
}
return interceptorList;
}
获取拦截器链registry.getInterceptors(advisor)
上述执行完后,回到入口处,我们可以看到拦截器链中包含了我们要增强的内容
回到CglibAopProxy.DynamicAdvisedInterceptor#intercept()方法中,继续查看拦截器链调用的核心方法proceed()。
ReflectiveMethodInvocation#proceed()
记住ReflectiveMethodInvocation这个类,这个类是拦截器链的执行体,proceed()是实现拦截器链的执行方法。拦截器链的执行逻辑和过滤器是一样的,通过++this.currentInterceptorIndex自增实现获取下一个拦截器,通过传入自己实现方法回调。
invoke()方法
至此大体就是AOP拦截器链的组装和执行过程,再详细的大家可以继续debug一探究竟。