一、Advice、Advisor、Advised接口
Advice: org.aopalliance.aop.Advice
“通知”,实际增强的逻辑部分。
Pointcut: org.springframework.aop.Pointcut
“切点”,一些特使的连接点,是具体附加通知的地方。例如坐地铁的时候,具体在某个站下车,那这个站就是切入点。
- 连接点:在应用执行过程中能够插入切面(Aspect)的一个点。例如坐地铁的时候,每一个站都可以下车,那么这每一个站都是一个接入点。假如一个对象中有多个方法,那么这个每一个方法就是一个连接点。
Advisor: org.springframework.aop.Advisor
“通知者”,它持有 Advice,是 Spring AOP 的一个基础接口。
Advised: org.springframework.aop.framework.Advised
AOP 代理工厂配置类接口。提供了操作和管理 Advice 和 Advisor 的能力。
1、Advice、Advisor、Advised 类图
1. Advisor 可以获取到 Advice。
2. PointcutAdvisor 可以获取到 Pointcut 和 Advice。
Pointcut 可以匹配连接点,Advice 是具体的通知,所以,PointcutAdvisor 是一个功能完善接口。
3. Advised 是 AOP 代理工厂配置类接口,它可以操作和管理 Advice 和 Advisor,它的实现类有 ProxyFactory、AspectJProxyFactory,用于生成AOP 代理类。
2、Advice
Advice 大体上分为了三类:BeforeAdvice、MethodInterceptor、AfterAdvice
可以看出,MethodInterceptor 是功能最强大的,它能够处理 BeforeAdvice、AroundAdvice、AfterAdvice、ThrowsAdvice、@Valid方法参数校验、@Async异步等
3、Advisor
Advisor 大体分为了三类:PointcutAdvisor、IntroductionAdvisor、PrototypePlaceholderAdvisor
其中,用到的最多的就是 PointcutAdvisor
,它涵盖了绝大部分的 Advisor。
PointcutAdvisor
PointcutAdvisor 是一个功能完善接口,也是 Spring AOP 中使用最多的,它涵盖了绝大部分的 Advisor。
通过 PointcutAdvisor 可以获取到 Pointcut 和 Advice。Pointcut 可以完成连接点的匹配,而 Advice 就是在连接点上具体要执行的"通知"。
4、Advised
Advised 是 AOP 代理工厂配置类接口。
它的实现类有:ProxyFactory、AspectJProxyFactory、ProxyFactoryBean。
Advised 提供了操作和管理 Advice 和 Advisor 的能力,所以,ProxyFactory 实现 Advised 之后,就可以方便的获取和操作 Advice、Advisor,从而创建 AOP 代理类了。
Advised、ProxyConfig、AdvisedSupport 都是跟 Spring AOP 代理配置相关的接口和类,它们可以统一 Spring AOP 的代理配置。
参考文章:【Spring源码三千问】Advice、Advisor、Advised都是什么接口?
二、Spring的代理选择规则
代理相关类图
-
AopProxyFactory 根据 proxyTargetClass 等设置选择 AopProxy 实现
-
AopProxy 通过 getProxy 创建代理对象
-
图中 Proxy 都实现了 Advised 接口,能够获得关联的切面集合与目标(其实是从 ProxyFactory 取得)
-
调用代理方法时,会借助 ProxyFactory 将通知统一转为环绕通知:MethodInterceptor
先说结论:
- proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
- proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
- proxyTargetClass = true, 总是使用 cglib 实现
public class A15 {
public static void main(String[] args) {
// 1. 备好切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
// 2. 备好通知 ,注意同名MethodInterceptor
MethodInterceptor advice = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
//调用目标
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
};
// 3. 备好切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
//4. 创建代理
Target1 target1 = new Target1();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target1);
factory.addAdvisor(advisor);
I1 proxy = (I1) factory.getProxy();
//5. 调用增强后的方法
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();
}
interface I1 {
void foo();
void bar();
}
static class Target1 implements I1 {
public void foo() {
System.out.println("target1 foo");
}
public void bar() {
System.out.println("target1 bar");
}
}
static class Target2 {
public void foo() {
System.out.println("target2 foo");
}
public void bar() {
System.out.println("target2 bar");
}
}
}
结果:使用 CGLIB 实现,不应该是 JDK 吗?
class com.itheima.a15.A15$Target1$$EnhancerBySpringCGLIB$$88a7fb77
before...
target1 foo
after...
target1 bar
解释:因为生成代理类时并不知道目标类是否实现了接口,需要告诉工厂目标类实现的接口
factory.setInterfaces(target1.getClass().getInterfaces());
设置接口后再进行测试,使用JDK实现
class com.itheima.a15.$Proxy2
设置 ProxyTargetClass 为 true 再进行测试,使用CGLIB实现
factory.setProxyTargetClass(true);
class com.itheima.a15.A15$Target1$$EnhancerBySpringCGLIB$$abb40a5f
总结
ProxyFactory 是用来创建代理的核心实现,用 AopProxyFactory 选择具体代理实现
- JdkDynamicAopProxy
- ObjenesisCglibAopProxy
注意:要区分上面提到的 MethodInterceptor,它与之前 CGLIB 中用的的 MethodInterceptor 是不同的接口
三、切点匹配
1、根据指定的方法进行匹配
public class A16 {
public static void main(String[] args) throws NoSuchMethodException {
AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();
//根据指定的方法进行匹配
pt1.setExpression("execution(* bar())");
//判断是否匹配
System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class));
}
static class T1 {
public void foo() {
}
public void bar() {
}
}
}
matches方法的第二个参数可以随意指定,对于结果没有影响,后面在自己实现切点匹配时会解释。
结果:
false
true
2、根据方法上的注解进行匹配
public class A16 {
public static void main(String[] args) throws NoSuchMethodException {
AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();
//根据方法上的注解进行匹配
pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));
}
static class T1 {
@Transactional
public void foo() {
}
public void bar() {
}
}
}
结果:
true
false
3、自己实现切点匹配
MergedAnnotations是一个注解的工具类
public class A16 {
public static void main(String[] args) throws NoSuchMethodException {
StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
/* 检查方法上是否加了 Transactional 注解 */
//获取方法上标注的注解
MergedAnnotations annotations = MergedAnnotations.from(method);
//是否有@Transactional注解
if (annotations.isPresent(Transactional.class)) {
return true;
}
/* 查看类上是否加了 Transactional 注解 */
//获取类上标注的注解
annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
//是否有@Transactional注解
if (annotations.isPresent(Transactional.class)) {
return true;
}
return false;
}
};
System.out.println(pt3.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class));
System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));
System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class));
}
static class T1 {
@Transactional
public void foo() {
}
public void bar() {
}
}
@Transactional
static class T2 {
public void foo() {
}
}
@Transactional
interface I3 {
void foo();
}
static class T3 implements I3 {
public void foo() {
}
}
}
说明:from方法默认的搜索策略是在本类,那么如果在接口或者父类上标注 @Transactional 注解则查找不到。
static MergedAnnotations from(AnnotatedElement element) {
return from(element, MergedAnnotations.SearchStrategy.DIRECT);
}
所以更换了策略
MergedAnnotations.SearchStrategy.TYPE_HIERARCHY:从继承树查找
默认策略的结果:
true
false
true
false
指定为 TYPE_HIERARCHY 策略的结果:
true
false
true
true
总结
- 底层切点实现是如何匹配的:调用了 aspectj 的匹配方法,比较关键的是它实现了 MethodMatcher 接口,用来执行方法的匹配。
- aspectj 切点的局限性:只能匹配方法的信息
四、从 @Aspect 到 Advisor
两个切面概念
aspect =
- 通知1(advice) + 切点1(pointcut)
- 通知2(advice) + 切点2(pointcut)
- 通知3(advice) + 切点3(pointcut)
- ...
advisor =
- 更细粒度的切面,包含一个通知和切点
aspect:高级切面,advisor:低级切面
1、代理创建器
AnnotationAwareAspectJAutoProxyCreator其实是个Bean后处理器,具体可以看它的继承关系。
AnnotationAwareAspectJAutoProxyCreator的作用:
-
将高级 @Aspect 切面统一为低级 Advisor 切面
-
在合适的时机创建代理
public class A17 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1", Aspect1.class);
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
//解析@Aspect,产生代理
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
context.refresh();
/*
第一个重要方法 findEligibleAdvisors:找到有资格的Advisors
a. 有资格的 Advisor 一部分是低级的,可以由自己编写,如下例中的 advisor3
b. 有资格的 Advisor 一部分是高级的,由解析 @Aspect 后获得
*/
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
Method findEligibleAdvisors = creator.getClass().getSuperclass().getSuperclass().
getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
findEligibleAdvisors.setAccessible(true);
//找到和 Target1 类匹配的所有切面
// 参数一:目标类型,参数二:bean名字,由于没交给spring管理,随便写一个
List<Advisor> advisors = (List<Advisor>) findEligibleAdvisors.invoke(creator, Target1.class, "target1");
for (Advisor advisor : advisors) {
System.out.println(advisor);
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
/*
第二个重要方法 wrapIfNecessary
a. 它内部调用 findEligibleAdvisors,只要返回集合不为空,则表示需要创建代理
*/
//参数一:目标对象 参数二:bean名字,由于没交给spring管理,随便写一个 参数三:对于这个操作没用,随便写一个
Method wrapIfNecessary = creator.getClass().getSuperclass().getSuperclass().getSuperclass().
getDeclaredMethod("wrapIfNecessary", Object.class, String.class, Object.class);
wrapIfNecessary.setAccessible(true);
Object o1 = wrapIfNecessary.invoke(creator, new Target1(), "target1", "target1");
//代理对象
System.out.println(o1.getClass());
Object o2 = wrapIfNecessary.invoke(creator, new Target2(), "target2", "target2");
//对象本身
System.out.println(o2.getClass());
((Target1) o1).foo();
}
static class Target1 {
public void foo(){
System.out.println("target1 foo");
}
}
static class Target2 {
public void bar(){
System.out.println("target2 bar");
}
}
@Aspect //高级切面类
static class Aspect1 {
@Before("execution(* foo())")
public void before(){
System.out.println("aspect1 before...");
}
@After("execution(* foo())")
public void after(){
System.out.println("aspect1 after...");
}
}
@Configuration
static class Config {
@Bean//低级切面
public Advisor advisor3(MethodInterceptor advice3){
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
return advisor;
}
@Bean
public MethodInterceptor advice3() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
}
};
}
}
}
结果:
//spring加的,暴露调用器的拦截器,作用后面会讲
org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
//低级切面
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [com.itheima.a17.A17$Config$1@209da20d]
//高级切面转换后的低级切面
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void com.itheima.a17.A17$Aspect1.before()]; perClauseKind=SINGLETON
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void com.itheima.a17.A17$Aspect1.after()]; perClauseKind=SINGLETON
>>>>>>>>>>>>>>>>>>>>>>>>>>
[TRACE] 16:10:29.788 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'target1' with 0 common interceptors and 4 specific interceptors
class com.itheima.a17.A17$Target1$$EnhancerBySpringCGLIB$$a2f64ee5
class com.itheima.a17.A17$Target2
advice3 before...
aspect1 before...
target1 foo
aspect1 after...
advice3 after...
总结
-
AnnotationAwareAspectJAutoProxyCreator 的作用
-
将高级 @Aspect 切面统一为低级 Advisor 切面
-
在合适的时机创建代理
-
-
findEligibleAdvisors 找到有【资格】的 Advisors
-
有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如本例 A17 中的 advisor3
-
有【资格】的 Advisor 另一部分是高级的, 由解析 @Aspect 后获得
-
-
wrapIfNecessary
-
它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理
-
它的调用时机通常在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行
-
2、代理创建时机
1)代理的创建时机:
-
初始化之后 (无循环依赖时)
-
实例创建后,依赖注入前 (有循环依赖时),并暂存于二级缓存
注意:不会重复创建代理,两个位置二选一
无循环依赖
public class A17_1 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
}
@Configuration
static class Config {
@Bean //解析 @Aspect,产生代理
public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}
@Bean //解析 @Autowired
public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
return new AutowiredAnnotationBeanPostProcessor();
}
@Bean //解析 @PostConstruct
public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
return new CommonAnnotationBeanPostProcessor();
}
@Bean
public Advisor advisor(MethodInterceptor advice) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, advice);
}
@Bean
public MethodInterceptor advice(){
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
return result;
}
};
}
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean1 {
public void foo(){
}
public Bean1(){
System.out.println("Bean1()");
}
@PostConstruct
public void init() {
System.out.println("Bean1 init()");
}
}
static class Bean2 {
public Bean2(){
System.out.println("Bean2()");
}
@Autowired
public void setBean1(Bean1 bean1) {
System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
}
@PostConstruct
public void init() {
System.out.println("Bean2 init()");
}
}
}
结果:
Bean1()
Bean1 init()
[TRACE] 16:21:47.952 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors
Bean2()
Bean2 setBean1(bean1) class is: class com.itheima.a17.A17_1$Bean1$$EnhancerBySpringCGLIB$$b0011e5d
Bean2 init()
有循环依赖时
static class Bean1 {
public void foo(){
}
public Bean1(){
System.out.println("Bean1()");
}
@Autowired
public void setBean1(Bean2 bean2) {
System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
}
@PostConstruct
public void init() {
System.out.println("Bean1 init()");
}
}
static class Bean2 {
public Bean2(){
System.out.println("Bean2()");
}
@Autowired
public void setBean1(Bean1 bean1) {
System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
}
@PostConstruct
public void init() {
System.out.println("Bean2 init()");
}
}
结果:
Bean1()
Bean2()
[TRACE] 16:23:35.276 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors
Bean2 setBean1(bean1) class is: class com.itheima.a17.A17_1$Bean1$$EnhancerBySpringCGLIB$$2d7cace9
Bean2 init()
Bean1 setBean2(bean2) class is: class com.itheima.a17.A17_1$Bean2
Bean1 init()
3、@Before 对应的低级通知
@Before 前置通知会被转换为原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息
-
通知代码从哪儿来
-
切点是什么(这里为啥要切点, 后面解释)
-
Aspect 对象,将来反射调用切面类的方法需要切面类对象
类似的还有:
-
AspectJAroundAdvice (环绕通知)
-
AspectJAfterReturningAdvice
-
AspectJAfterThrowingAdvice (环绕通知)
-
AspectJAfterAdvice (环绕通知)
环绕通知:实现了 MethodInterceptor 接口的类
public class A17_2 {
static class Aspect {
@Before("execution(* foo())")
public void before1() {
System.out.println("before1");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("before2");
}
public void after() {
System.out.println("after");
}
}
static class Target {
public void foo() {
System.out.println("target foo");
}
}
public static void main(String[] args) {
//切面对象工厂,将来由工厂拿到切面对象
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
List<Advisor> list = new ArrayList<>();
//高级切面转低级切面类
for (Method method : Aspect.class.getDeclaredMethods()) {
//方法上是否标注 @Before 注解
if (method.isAnnotationPresent(Before.class)) {
//解析切点
String expression = method.getAnnotation(Before.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
//通知类
//参数三:将来反射调用切面类的方法需要切面类对象,所以需要传入切面类对象,具体从工厂获取
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
//切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}
for (Advisor advisor : list) {
System.out.println(advisor);
}
}
}
结果:
org.springframework.aop.support.DefaultPointcutAdvisor:
pointcut [AspectJExpressionPointcut: () execution(* foo())];
advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void com.itheima.a17.A17_2$Aspect.before1()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor:
pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice:
advice method [public void com.itheima.a17.A17_2$Aspect.before2()]; aspect name '']
五、静态通知调用
步骤:
- 高级切面转低级切面类
- 通知统一转换为环绕通知 MethodInterceptor
- 创建并执行调用链 (所有的环绕通知 + 目标)
1、通知为什么需要统一转换为环绕通知?
其实无论proxyFactory基于哪种方式创建,最后干活(调用 advice)的是一个 MethodInvocation 对象
1)因为 advisor 有多个,且一个套一个调用,因此需要一个调用链对象,即 MethodInvocation
2)MethodInvocation 要知道 advice 有哪些,还要知道目标,调用次序
3)从上图可以看出,环绕通知才适合作为 advice,因此其他 before,afterReturning 都会被转换为环绕通知
如何统一转换为 MethodInterceptor 环绕通知?
统一转换为环绕通知,体现的是设计模式的适配器模式
- 对外是为了方便使用要区分 before, afterReturning, afterThrowing
- 对内统一都是环绕通知,统一用 MethodInterceptor 表示
适配如下
- MethodBeforeAdviceAdapter 将 AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
- AfterReturningAdviceAdapter 将 AspectJAfterReturningAdvice 适配为 MethodAfterReturningAdviceInterceptor
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
MethodBeforeAdviceAdapter() {
}
//支持哪种通知的适配
public boolean supportsAdvice(Advice advice) {
return advice instanceof MethodBeforeAdvice;
}
//转换成 MethodInterceptor 接口
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
2、模拟静态通知调用
public class A18 {
static class Aspect {
@Before("execution(* foo())")
public void before1() {
System.out.println("before1");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("before2");
}
public void after() {
System.out.println("after");
}
@AfterReturning("execution(* foo())")
public void afterReturning() {
System.out.println("afterReturning");
}
@AfterThrowing("execution(* foo())")
public void afterThrowing(Exception e) {
System.out.println("afterThrowing" + e.getMessage());
}
@Around("execution(* foo())")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around before...");
Object result = pjp.proceed();
System.out.println("around after...");
return result;
}
}
static class Target {
public void foo() {
System.out.println("target foo");
}
}
public static void main(String[] args) throws Throwable {
/* 1. 高级切面转低级切面类 */
//切面对象工厂,由工厂拿到切面对象
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
List<Advisor> list = new ArrayList<>();
for (Method method : Aspect.class.getDeclaredMethods()) {
//方法上是否标注 @Before 注解
if (method.isAnnotationPresent(Before.class)) {
//解析切点
String expression = method.getAnnotation(Before.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
//通知类
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
//切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
} else if (method.isAnnotationPresent(AfterReturning.class)) {
//方法上是否标注 @AfterReturning 注解
if (method.isAnnotationPresent(AfterReturning.class)) {
//解析切点
String expression = method.getAnnotation(AfterReturning.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
//通知类
AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pointcut, factory);
//切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
} else if (method.isAnnotationPresent(Around.class)) {
//方法上是否标注 @Around 注解
if (method.isAnnotationPresent(Around.class)) {
//解析切点
String expression = method.getAnnotation(Around.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
//通知类
AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, factory);
//切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}
}
for (Advisor advisor : list) {
System.out.println(advisor);
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
/* 省略了 advisor 排序步骤 */
/* 2. 通知统一转换为环绕通知 MethodInterceptor */
ProxyFactory proxyFactory = new ProxyFactory();
Target target = new Target();
proxyFactory.setTarget(target);
//准备把 MethodInvocation 放入当前通知
proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE);
proxyFactory.addAdvisors(list);
//把除了环绕通知以外的其他类型通知统一转换为环绕通知
List<Object> methodInterceptorList =
proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
for (Object o : methodInterceptorList) {
System.out.println(o);
}
/* 3. 创建并执行调用链 (所有的环绕通知 + 目标) */
Constructor<ReflectiveMethodInvocation> constructor = ReflectiveMethodInvocation.class.getDeclaredConstructor(
Object.class, Object.class, Method.class, Object[].class, Class.class, List.class);
constructor.setAccessible(true);
ReflectiveMethodInvocation methodInvocation = constructor.newInstance(
null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList);
methodInvocation.proceed();
/*
此步模拟调用链过程,是一个简单的递归调用
1. proceed() 方法调用链中下一个环绕通知
2. 每个环绕通知内部继续调用 proceed()
3. 调用到没有更多通知了,就调用目标方法
*/
}
}
结果:
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object com.itheima.a18.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAfterReturningAdvice: advice method [public void com.itheima.a18.A18$Aspect.afterReturning()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void com.itheima.a18.A18$Aspect.before1()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void com.itheima.a18.A18$Aspect.before2()]; aspect name '']
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
org.springframework.aop.interceptor.ExposeInvocationInterceptor@8e24743
org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object com.itheima.a18.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name ''
org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor@74a10858
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@23fe1d71
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@28ac3dc3
around before...
before1
before2
target foo
afterReturning
around after...
ExposeInvocationInterceptor的作用
某些通知会用到 MethodInvocation,在执行的时候,需要放在公共的位置,让所有的通知都能找到,如果某个通知想取的时候没取到,就会报如下错误:
如何放置?
ExposeInvocationInterceptor的作用就是将 MethodInvocation 放到当前线程,这样所有通知就都能获取到了,它同时也是个环绕通知,那把它放到最外层的环绕通知就可以了。
总结
代理方法执行时会做如下工作
1. 通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知。这体现的是适配器设计模式
2. 所谓静态通知,体现在上面方法的 Interceptors 部分,这些通知调用时无需再次检查切点,直接调用即可
3. 结合目标与环绕通知链,创建 MethodInvocation 对象,通过它完成整个调用
3、模拟 MethodInvocation
用到的是职责链模式
public class A18_1 {
static class Target {
public void foo() {
System.out.println("Target.foo()");
}
}
static class Advice1 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Advice1.before()");
Object result = invocation.proceed(); //调用下一个通知目标
System.out.println("Advice1.after()");
return result;
}
}
static class Advice2 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Advice2.before()");
Object result = invocation.proceed(); //调用下一个通知目标
System.out.println("Advice2.after()");
return result;
}
}
static class MyInvocation implements MethodInvocation {
private Object targrt;
private Method method;
private Object[] args;
private List<MethodInterceptor> methodInterceptorList;
private int count = 1; //调用次数
public MyInvocation(Object targrt, Method method, Object[] args, List<MethodInterceptor> methodInterceptorList) {
this.targrt = targrt;
this.method = method;
this.args = args;
this.methodInterceptorList = methodInterceptorList;
}
@Override
public Method getMethod() {
return method;
}
@Override
public Object[] getArguments() {
return args;
}
@Override
public Object proceed() throws Throwable { //调用每一个环绕通知,调用目标
if (count > methodInterceptorList.size()) {
//调用目标,返回并结束通知
return method.invoke(targrt, args);
}
//逐一调用通知
MethodInterceptor methodInterceptor = methodInterceptorList.get(count++ - 1);
return methodInterceptor.invoke(this);
}
@Override
public Object getThis() {
return targrt;
}
@Override
public AccessibleObject getStaticPart() {
return method;
}
}
public static void main(String[] args) throws Throwable {
Target target = new Target();
ArrayList<MethodInterceptor> list = new ArrayList<>();
list.add(new Advice1());
list.add(new Advice2());
MyInvocation myInvocation = new MyInvocation(target, Target.class.getMethod("foo"), new Object[0], list);
myInvocation.proceed();
}
}
结果:
Advice1.before()
Advice2.before()
Target.foo()
Advice2.after()
Advice1.after()
具体过程:
-
proceed() 方法调用链中下一个环绕通知
-
每个环绕通知内部继续调用 proceed()
-
调用到没有更多通知了, 就调用目标方法
MethodInvocation 的编程技巧在实现拦截器、过滤器时能用上
六、动态通知调用
带参数绑定的通知方法调用
public class A19 {
@Aspect
static class MyAspect {
@Before("execution(* foo(..))") //静态通知调用,不需要参数绑定,执行时不需要切点
public void before1(){
System.out.println("before1");
}
@Before("execution(* foo(..)) && args(x)") //动态通知调用,需要参数绑定,执行时需要切点对象
public void before2(int x){
System.out.printf("before2(%d)%n", x);
}
}
static class Target {
public void foo(int x) {
System.out.printf("target foo(%d)%n", x);
}
}
@Configuration
static class MyConfig {
@Bean
AnnotationAwareAspectJAutoProxyCreator proxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}
@Bean
public MyAspect myAspect() {
return new MyAspect();
}
}
public static void main(String[] args) throws Throwable {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(MyConfig.class);
context.refresh();
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
Method findEligibleAdvisors = creator.getClass().getSuperclass().getSuperclass().
getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
findEligibleAdvisors.setAccessible(true);
//找到和Target匹配的切面
List<Advisor> list = (List<Advisor>) findEligibleAdvisors.invoke(creator, Target.class, "target");
Target target = new Target();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisors(list);
//获取代理
Object proxy = factory.getProxy();
//把除了环绕通知以外的其他类型通知统一转换为环绕通知
List<Object> interceptorList =
factory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo", int.class), Target.class);
for (Object o : interceptorList) {
showDetail(o);
}
//创建并执行调用链
Constructor<ReflectiveMethodInvocation> constructor = ReflectiveMethodInvocation.class.getDeclaredConstructor(
Object.class, Object.class, Method.class, Object[].class, Class.class, List.class);
constructor.setAccessible(true);
ReflectiveMethodInvocation methodInvocation = constructor.newInstance(
proxy, target, Target.class.getMethod("foo", int.class), new Object[]{100}, Target.class, interceptorList);
methodInvocation.proceed();
}
public static void showDetail(Object o) {
try {
Class<?> clazz = Class.forName("org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher");
if (clazz.isInstance(o)) {
Field methodMatcher = clazz.getDeclaredField("methodMatcher");
methodMatcher.setAccessible(true);
Field interceptor = clazz.getDeclaredField("interceptor");
interceptor.setAccessible(true);
System.out.println("环绕通知和切点:" + o);
System.out.println("\t切点为:" + methodMatcher.get(o));
System.out.println("\t通知为:" + interceptor.get(o));
}else {
System.out.println("普通环绕通知:" + o);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
普通环绕通知:org.springframework.aop.interceptor.ExposeInvocationInterceptor@82de64a
普通环绕通知:org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@659499f1
环绕通知和切点:org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher@47e2e487
切点为:AspectJExpressionPointcut: (int x) execution(* foo(..)) && args(x)
通知为:org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@201a4587
before1
before2(100)
target foo(100)
InterceptorAndDynamicMethodMatcher:拦截器和动态的方法匹配器,内部包含了真正的环绕通知和切点,它并不是一个环绕通知,查看它的类就能知道。
class InterceptorAndDynamicMethodMatcher {
// 环绕通知
final MethodInterceptor interceptor;
// 切点
final MethodMatcher methodMatcher;
public InterceptorAndDynamicMethodMatcher(MethodInterceptor interceptor, MethodMatcher methodMatcher) {
this.interceptor = interceptor;
this.methodMatcher = methodMatcher;
}
}
总结:
-
通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知
-
所谓动态通知,体现在上面方法的 DynamicInterceptionAdvice 部分,这些通知调用时因为要为通知方法绑定参数,还需再次利用切点表达式
-
动态通知调用复杂程度高,性能较低