Spring选择代理
jdk和cglib的统一
1. spring的代理选择规则
1.1 两种代理
- aspect: 多个通知和多个切面
- advisor:一个通知和一个切面
以advisor为例,一个增强包括以下几步骤:
- pointcut——定义切点
- advice——定义通知(增强)
- advisor——创建切面、创建target对象
- 创建并执行代理
1.2 切点(org.springframework.aop.Pointcut):用于筛选匹配,匹配成功后增强
- 切点实现类:org.springframework.aop.aspectj.AspectJExpressionPointcut
- 切点实现类:org.springframework.aop.support.annotation.AnnotationMatchingPointcut
1.3 通知advice(org.aopalliance.intercept.MethodInterceptor 注意,不是spring的cglib的MethodInterceptor):用于功能增强
1.4 切面advisor:org.springframework.aop.support.DefaultPointcutAdvisor
- new DefaultPointcutAdvisor(pointcut,advice);
- new Target()
1.5 创建并执行代理:
// 4. 创建代理
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target1);
proxyFactory.addAdvisors(advisor);
proxyFactory.setInterfaces(I1.class); // 设置接口类型
I1 proxy =(I1) proxyFactory.getProxy();
// 查看代理类型
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();
1.6 代理选择
- proxyTargetClass = false, 目标实现了接口,用jdk实现
- proxyTargetClass = false, 目标没有实现接口,用cglib
- proxyTargetClass = true, 总是使用cglib
2. 底层的切点实现
2.1 切点匹配
/**
* @author ssozh
* @data 2022/12/30
*/
public class A16 {
public static void main(String[] args) throws NoSuchMethodException {
/**
* case 1 根据方法名进行匹配
*/
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* bar())");
System.out.println(pointcut.matches(T1.class.getMethod("foo"), T1.class));// 切点匹配 , 要判断的方法是? 目标类是?
System.out.println(pointcut.matches(T1.class.getMethod("bar"), T1.class));// 匹配成功 返回true
/**
* case2 根据annotation进行匹配
*/
AspectJExpressionPointcut pointcut2 = new AspectJExpressionPointcut();
pointcut2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
System.out.println(pointcut2.matches(T1.class.getMethod("foo"), T1.class)); // true
System.out.println(pointcut2.matches(T1.class.getMethod("bar"), T1.class)); // false
/**
* case3
*/
StaticMethodMatcherPointcut pointcut3 = new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 先看方法上是不是有 注解
MergedAnnotations annotations = MergedAnnotations.from(method);
if (annotations.isPresent(Transactional.class)) {
return true;
}
// 第二个参数 默认使用 DIRECT 不会查看实现接口上是否包含该注解
annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
return annotations.isPresent(Transactional.class);
}
};
System.out.println(pointcut3.matches(T1.class.getMethod("foo"), T1.class)); // true
System.out.println(pointcut3.matches(T1.class.getMethod("bar"), T1.class)); // false
System.out.println(pointcut3.matches(T2.class.getMethod("bar"), T2.class)); // true
System.out.println(pointcut3.matches(T3.class.getMethod("foo"), T3.class)); // true
}
static class T1 {
@Transactional
public void foo() {
}
public void bar() {
}
}
/**
* 加在类上,这个类里面所有的方法都需要事务增强
*/
@Transactional
static class T2 {
public void foo() {
}
public void bar() {
}
}
@Transactional
interface I3 {
void foo();
}
/**
* 接口实现方法也可以实现事务加强
*/
static class T3 implements I3 {
@Override
public void foo() {
}
}
}
3. 底层的通知实现
4. 底层的切面实现
4.1 高级的@Aspect切面和低级的Advisor切面
- 给代理类添加@Aspect注解,并给通知增加相关注解(@Before、@After等)
- ApplicationContext注入BeanPostProcessor=>AnnotationAwareAspectJAutoProxyCreator
- AnnotationAwareAspectJAutoProxyCreator对context容器中的Bean对象进行增强
3.1 通过AbstractAutoProxyCreator接口实现的getAdvicesAndAdvisorsForBean方法( findEligibleAdvisors) 找到切面
3.2 通过org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary 对找到的切面创建代理
package org.springframework.aop.framework.autoproxy;
/**
* @author ssozh
* @data 2022/12/30
*/
public class A17 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean("aspect1", Aspect1.class);
context.registerBean(ConfigurationClassPostProcessor.class);
// 重点:实现了一个BeanPostProcessor接口:创建-> (创建代理) -> 依赖注入 —> 初始化 (或者创建代理)
// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors => 寻找切面
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary => 创建代理
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
// 初始化容器
context.refresh();
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
// 第一个重要方法:findEligibleAdvisors 根据目标类型 返回和该目标类型匹配的所有切面,比如
// 用于org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.getAdvicesAndAdvisorsForBean
List<Advisor> advisors = creator.findEligibleAdvisors(Target1.class, "target1");
// List<Advisor> advisors = creator.findEligibleAdvisors(Target2.class, "target2"); // 这个返回的advisors为空
for (Advisor advisor : advisors) {
System.out.println(advisor);
}
// 第二个重要方法:wrapIfNecessary 根据第一个方法返回的集合,如果不为空,则对目标类进行wrap并返回代理对象(执行这个方法 不需要先执行findEligibleAdvisors)
Object proxy1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
Object proxy2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
System.out.println(proxy2.getClass()); // 是本身:A17$Target2
System.out.println(proxy1.getClass()); // 是代理:class A17$Target1$$EnhancerBySpringCGLIB$$91cd2035
((Target1) proxy1).foo();
((Target2) proxy2).bar();
// Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
}
static class Target1 {
public void foo() {
System.out.println("Target1 exec foo");
}
}
static class Target2 {
public void bar() {
System.out.println("Target2 exec 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 before... ");
}
}
@Configuration
static class Config {
@Bean
public Advisor advisor(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, advice3);
}
@Bean
public MethodInterceptor advice3() {
return invocation -> {
System.out.println("advice3 before..");
Object result = invocation.proceed();
System.out.println("advice3 after..");
return result;
};
}
}
}
4.2 代理生成时机
代理创建时机:
a. 代理的创建时机
- 初始化之后(没有循环依赖时)
- 实例创建后,依赖注入前(有循环依赖时),并暂存于二级缓存
b. 依赖注入与初始化不应该被增强,仍赢被施加于初始对象
具体case:
-
不存在循环依赖的case:
1.1 执行Bean1的构造
1.2 执行Bean1的初始化含税
1.3 执行Bean1的增强
1.4 Bean2初始化
1.5 注入Bean1的增强后对象 -
存在循环依赖的case:
2.1 初始化Bean1
2.2 初始化Bean2
2.3 创建Bean1代理
2.4 Bean2依赖注入Bean1
2.5 初始化Bean2
2.6 Bean1依赖注入Bean2
2.6 初始化Bean1
4.3 吐槽切面的顺序控制
- 对于高级切面,可以使用@Order注解来排序,数字小的优先级高。
- 对于低级切面DefaultPointAdvisor实现了Order接口,可以setOrder
吐槽:
- @Order注解 加在@Aspect的对应的类上有效,加在@Before和@After这类的方法上是无效的
- @Order注解 加在@Bean后面也无效
4.4 高级切面转低级切面(@Before注解解析过程)
@Before
前置通知会被转化为下面元素的AspectJMethodBeforeAdvice 形式,该对象包含了如下信息:
- 通知代码从哪来
- 切点是什么?这里为啥要切点
- 通知对象如何创建,本例共用一个Aspect 对象
类似的通知还有:
- AspectJAroundAdvice (环绕通知)
- AspectJAfterAdvice()
- AspectJAfterReturningAdvice
- AspectJAfterThrowingAdvice
public class A17_2 {
static class Aspect {
@Before("execution(* foo()) ")
public void before() {
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");
}
}
static class Target{
public void foo() {
System.out.println("target.foo");
}
}
/**
* @param args
*/
public static void main(String[] args) {
List<Advisor> advisorList = new ArrayList<>();
// 创建工厂
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
// 假设知道切面类为Aspect->知道方法-
for (Method method : Aspect.class.getDeclaredMethods()) {
// 1. 检查是否标注了xx注解,然后获取切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
if (method.isAnnotationPresent(Before.class)) {
// 根据Before的值生成一个切点对象
String expr = method.getAnnotation(Before.class).value();
pointcut.setExpression(expr);
}
// 2. 通知类
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 3. 解析低级切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
// 4. 收集切面集合
advisorList.add(advisor);
}
advisorList.forEach(System.out::println);
}
}