待完善:cglib 代理类是什么样子,JDK 动态代理是什么样子
事务类型
通常我们只会用到 @Transactional(propagation = Propagation.REQUIRED)
。
在特殊需求的时候需要在一个方法内部提前提交一部分事务或者是让内部的一段代码处于单独的一个事务管理的时候需要用到 REQUIRES_NEW
。
基于继承的代理
cglib 实现动态代理就是基于继承实现的。
public class ProxyReferenceTest {
@Test
public void testReferenceRelation() {
EatProxyClass eatProxyClass = new EatProxyClass();
eatProxyClass.eat();
}
static class EatTargetClass {
public void eat(){
System.out.println("EatTargetClass 中 this -->" + this);
}
}
static class EatProxyClass extends EatTargetClass {
public void eat(){
System.out.println("EatProxyClass 中 this -->" + this);
super.eat();
}
}
}
测试方法运行结果如下
通过上述测试我们可以得出结论:基于继承的代理是目标对象和代理对象的引用是一样。
基于接口的代理
jdk 实现动态代理就是基于接口实现的。
public class ProxyReferenceTest {
@Test
public void testReferenceRelation() {
EatTargetClass eatTargetClass = new EatTargetClass();
EatProxyClass eatProxyClass = new EatProxyClass(eatTargetClass);
eatProxyClass.eat();
}
static interface EatService {
void eat();
}
static class EatTargetClass implements EatService{
@Override
public void eat(){
System.out.println("TargetClass 中 this-->" + this);
}
}
static class EatProxyClass implements EatService{
EatTargetClass eatTargetClass;
public EatProxyClass(EatTargetClass eatTargetClass){
this.eatTargetClass = eatTargetClass;
}
@Override
public void eat() {
System.out.println("EatProxyClass 中 this -->" + this);
eatTargetClass.eat();
}
}
}
测试方法运行结果如下
通过上述测试我们可以得出结论:基于接口的代理是目标对象和代理对象的引用是不一样。
spring 源码中动态代理的思考
我们都知道 spring 的 AOP 是通过动态代理实现的,动态代理的实现技术又分为:
- cglib 动态代理
- JDK 动态代理
spring 中的声明式事务也是用 AOP 实现的,通过生成代理对象来达到一个事务的效果。我们经常遇到一个事务方法(这里为了方便后续描述,假定这个方法为 saveOrder)中调度另一个事务方法(假定这个方法为 updateStock)时,updateStock 这个方法的事务不起作用。产生这个问题的原因在于我们在 saveOrder 方法中的 this 对象引用是目标对象的引用,不是事务代理对象的引用,自然也就不会被事务代理对象进行逻辑的增强。
而如果使用 cglib 这种动态代理的技术去生成事务代理对象,saveOrder 方法中的 this 对象引用就是事务代理对象的引用,从而调度另一个事务方法 updateStock 时,updateStock 是可以起到事务特性作用的。
为了大家更好理解我上诉说的意思,我再拿 spring 中 @Configuration 这个例子来讲述。
加了 @Configuration 注解的类是会被 ConfigurationClassPostProcessor 这个后置处理器处理的。
@Configuration
public class AppConfig {
@Bean
public Student student(){
return new Student();
}
@Bean
public Teacher teacher(){
return new Teacher( student() );
}
}
teacher 这个 bean 方法内部调用了另外一个 bean 方法 student,这里调用 student 这个方法返回的对象是和直接调用 student 方法返回的对象是一致的。
这个是怎么实现呢?
其实就是通过生成代理对象实现的。ConfigurationClassPostProcessor 这个后置处理器会在 spring context 初始化(refresh)时,给加了 @Configuration 注解的类生成 cglib 代理对象。这个代理对象对于执行加了 @Bean 注解的方法执行前做一个拦截,从而保证 @Bean 方法多次调用生成的是同一个对象。
那问题来了,一个 bean 方法内部调用另外一个 bean 方法,为什么能执行到切面呢 ?
其实答案就是在于我们这个代理对象是基于继承生成的,再回顾一下我上面说的那句话:基于继承的代理是目标对象和代理对象的引用是一样。
参考
https://www.jianshu.com/p/3e9267b025b2