文章目录
- 1. 前言
- 2. 谈谈代理创建器
- 3. 代理创建时机是什么时候
- 4. 浅谈@Order的失效场景
- 4. 浅谈@Order的失效场景
- 5. 高级切面如何转为低级切面
1. 前言
Spring中有两种切面,一种是@Aspect,另一种是Advisor
其中@Aspect是高级切面,Advisor是低级切面
这里的高级和低级并不代表其功能强弱,而是低级切面比较适合框架内部使用,而高级切面比较适合编码开发使用。因为低级切面的功能比较基本。
@Aspect切面里面可以包含一组或多组通知与切面。
而Advisor仅支持一组通知和切面。
@Aspect虽然是一种高级切面,但是Spring处理这种高级切面的时候,依然会把高级切面转化成低级切面。因为只有转化为低级切面才能被Spring内部所使用。
2. 谈谈代理创建器
代理创建器,指的是AnnotationAwareAspectJAutoProxyCreator类
AnnotationAwareAspectJAutoProxyCreator
是用来处理被@AspectJ
注解标注的切面类和Spring Advisors
的。这里面有两个比较重要的方法:
- findEligibleAdvisors:这个方法是用来找有资格的Advisors,这里说的有资格的Advisor一部分是低级切面,一部分是高级切面
- wrapIfNecessary:其内部调用的
findEligibleAdvisors
, 只要返回集合不空, 则表示需要创建代理
下面测试一下这两个方法。
这里先准备高级切面和低级切面
static class Target1 {
public void foo() {
System.out.println("target1 foo");
}
}
static class Target2 {
public void bar() {
System.out.println("target2 bar");
}
}
@Aspect // 高级切面类
@Order(1)
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("aspect1 before2...");
}
}
@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 invocation -> {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
};
}
}
注意:这两个方法均为protected,因此其他包并不能直接调用这两个方法,可以用反射来调用,但是这里为了简便,因此把包名设置成这个方法的类的所在包名一样
package org.springframework.aop.framework.autoproxy;
编写测试方法
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1", Aspect1.class);
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
// BeanPostProcessor
context.refresh();
/*
第一个重要方法 findEligibleAdvisors 找到有【资格】的 Advisors
*/
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> advisors = creator.findEligibleAdvisors(Target1.class, "target1");
for (Advisor advisor : advisors) {
System.out.println(advisor);
}
System.out.println("-------------------------------------------");
/*
第二个重要方法 wrapIfNecessary
*/
Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
System.out.println(o1.getClass());
Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
System.out.println(o2.getClass());
((Target1) o1).foo();
}
creator.findEligibleAdvisors(Target1.class, "target1")
findEligibleAdvisors有两个参数,第一个是目标的类型,第二个参数是这个类在容器中的名字(并不是很重要,随便写就行)
当执行这个方法的时候,就会根据目标的类型与在容器中的每一个Advisors进行对比,将可以的Advisors收集到集合中
这里输出了四个切面
org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
:这是是Spring给所有代理都要加的切面org.springframework.aop.support.DefaultPointcutAdvisor
:自己编写的低级切面InstantiationModelAwarePointcutAdvisor
:剩下两个是高级切面转化后的两个低级切面
creator.wrapIfNecessary(new Target1(), "target1", "target1")
wrapIfNecessary有三个参数
- 目标对象,因为现在是框架外部所以需要自己创建,在框架内部的话则是在容器中寻找
输出的结果可见,o1为代理对象而o2不是代理对象。
这里因为如果目标类型为Target2,那么执行了findEligibleAdvisors方法后返回的集合为空,那么调用wrapIfNecessary就不会创建代理类
3. 代理创建时机是什么时候
对于Bean来讲有三个比较重要的时机,那就是创建、依赖注入和初始化
代理创建的时机一般来说在两个位置
- 创建之后,依赖注入之前
- 初始化之后
两个位置二选一
准备一个案例
public class A17_1 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(Config.class);
context.refresh();
context.close();
}
@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 (MethodInvocation invocation) -> {
System.out.println("before...");
return invocation.proceed();
};
}
@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()");
}
//@Autowired public void setBean2(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()");
}
}
}
这个案例有个特点,依赖关系是单向的,也就是Bean2依赖于Bean1
执行测试类可以看出
首先调用了Bean1的构造方法,然后调用了Bean1的初始化方法
接下来创建Bean1的代理对象,也就是在Bean1初始化之后创建了Bean1的初始化对象
然后调用Bean2的构造方法
因为Bean2依赖于Bean,需要设置Bean1,从打印结果不难看出,这里的Bean1是一个增强后的代理方法
最后Bean2初始
如果Bean2也依赖于Bean1,那么会出现什么呢?(也就是循环依赖)
static class Bean1 {
public void foo() {
}
public Bean1() {
System.out.println("Bean1()");
}
@Autowired public void setBean2(Bean2 bean2) {
System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
}
@PostConstruct public void init() {
System.out.println("Bean1 init()");
}
}
首先调用了Bean1的构造,按理说就应该调用Bean1的setBean2方法,但是这时候Bean2还没有创建
因此调用了Bean2的构造方法。
接着调用Bean2中的setBean1方法,前面说过这里需要增强后的Bean1代理对象,因此Bean1代理对象的创建应该在调用Bean2中的setBean1方法之前
接下来初始化Bean2
Bean2初始化之后回到Bean1,给Bean1设置Bean2,接下来Bean1初始化
这就是循环依赖的情况下,代理创建的时机了
4. 浅谈@Order的失效场景
@Aspect // 高级切面类
@Order(1)
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("aspect1 before2...");
}
}
@Configuration
static class Config {
@Bean // 低级切面
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
//设置切面执行顺序
advisor.setOrder(2);
return advisor;
}
@Bean
public MethodInterceptor advice3() {
return invocation -> {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
};
}
}
这里给高级切面设置优先级为1,低级切面设置优先级为2,因此高级切面先完成,低级切面后完成
这里需要注意几个@Order注解失效的情况
-
注解加在了错误的位置
-
@Configuration static class Config { @Bean // 低级切面 @Order(2) //不可以加在这里 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 invocation -> { System.out.println("advice3 before..."); Object result = invocation.proceed(); System.out.println("advice3 after..."); return result; }; } }
-
-
不可以给高级切面里面的切面进行控制优先级
-
@Aspect // 高级切面类 @Order(1) static class Aspect1 { @Before("execution(* foo())") @Order(1) public void before1() { System.out.println("aspect1 before1..."); } @Before("execution(* foo())") @Order(2) public void before2() { System.out.println("aspect1 before2..."); } }
-
4. 浅谈@Order的失效场景
@Aspect // 高级切面类
@Order(1)
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("aspect1 before2...");
}
}
@Configuration
static class Config {
@Bean // 低级切面
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
//设置切面执行顺序
advisor.setOrder(2);
return advisor;
}
@Bean
public MethodInterceptor advice3() {
return invocation -> {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
};
}
}
这里给高级切面设置优先级为1,低级切面设置优先级为2,因此高级切面先完成,低级切面后完成
这里需要注意几个@Order注解失效的情况
-
注解加在了错误的位置
-
@Configuration static class Config { @Bean // 低级切面 @Order(2) //不可以加在这里 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 invocation -> { System.out.println("advice3 before..."); Object result = invocation.proceed(); System.out.println("advice3 after..."); return result; }; } }
-
-
不可以给高级切面里面的切面进行控制优先级
-
@Aspect // 高级切面类 @Order(1) static class Aspect1 { @Before("execution(* foo())") @Order(1) public void before1() { System.out.println("aspect1 before1..."); } @Before("execution(* foo())") @Order(2) public void before2() { System.out.println("aspect1 before2..."); } }
-
5. 高级切面如何转为低级切面
Spring底层是如何将高级切面转成低级切面的呢?请看下面案例
首先准备一个切面
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");
}
public void afterReturning() {
System.out.println("afterReturning");
}
public void afterThrowing() {
System.out.println("afterThrowing");
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("around...before");
return pjp.proceed();
} finally {
System.out.println("around...after");
}
}
}
static class Target {
public void foo() {
System.out.println("target foo");
}
}
就以@Before为例
首先遍历Aspect类中所有方法
接着判断方法上是否有@Before注解
如果有,则通过method.getAnnotation(Before.class).value()
获取@Before的值
新建一个切点AspectJExpressionPointcut
给这个切点设置表达式pointcut.setExpression(expression);
最后需要一个通知类AspectJMethodBeforeAdvice
新建这个通知类需要三个参数,第一个是方法对象,第二个是切点,第三个是切面实例工厂
切面实例工厂也就是指new SingletonAspectInstanceFactory(new Aspect())
最后new一个低级切面new DefaultPointcutAdvisor(pointcut, advice)
这样高级切面就转为低级切面了;
public static void main(String[] args) throws Throwable {
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
// 高级切面转低级切面类
List<Advisor> list = new ArrayList<>();
for (Method method : Aspect.class.getDeclaredMethods()) {
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);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}
for (Advisor advisor : list) {
System.out.println(advisor);
}
}