问题
public interface TestI extends InitializingBean {
@Override
default void afterPropertiesSet() {
TestHolder.add(name(), this);
}
String name();
void m1();
@Transactional(rollbackFor = Exception.class)
void m2();
}
@Component
public class TestA implements TestI {
private final JedisClient redisClient;
public TestA(JedisClient redisClient) {
this.redisClient = redisClient;
}
@Override
public String name() {
return "A";
}
@Override
public final void m1() {
System.out.println("ta-m1-final: " + redisClient);
}
@Override
public void m2() {
System.out.println("ta-m2-final: " + redisClient);
}
}
public class TestHolder {
private static final ConcurrentHashMap<String, TestI> MAP = new ConcurrentHashMap<>();
public static void add(String name, TestI testI) {
MAP.put(name, testI);
}
public static TestI get(String name) {
return MAP.get(name);
}
}
@Component
public class Test {
@Autowired
private TestA testA;
public static void main(String[] args) {
// 打印的结果: null
testA.m1();
// 打印的结果: redisClient 的地址
testA.m2();
// 打印的结果: redisClient 的地址
TestHolder.get("A").m1();
System.out.println(TestHolder.get("A"));
// 跟上一个数据结果一样, 因为代理对象的 toString() 是直接调用被代理对象的 toString() 实现的
System.out.println(testA);
}
}
为啥通过 @Autowired 拿到的 testA 调用 m1() 结果为空,而通过 TestHolder 结果不为空?
分析
1、因为 TetI 中加了 @Transactional,所以 TestA 会被 Spring 代理,生成一个代理类。Spring 使用的 cglib 代理,通过继承 TestA 来生成代理类。
2、由于 TestA 的 m1() 加了 final 修饰,所以代理类不会重写 m1() 方法。因此,代理类在调用 m1() 时是直接调用被代理类的 m1()。
3、而且,代理类中重写的方法会调用被代理类的方法,代理对象也不会注入被代理类依赖的属性,这就间接地导致了代理对象调用 final 修饰的方法时,拿不到由 Spring 注入的 bean,所以 redisClient 变量为 null。
4、TestHolder 中的 TestI 是被代理对象,所以打印的结果不是 null。
5、通过添加 vm options -Dcglib.debugLocation=地址 打印 cglib 生成的 TestA 的代理类。
public class TestA$$EnhancerBySpringCGLIB$$23042fe8 extends TestA implements SpringProxy, Advised, Factory {
// 省略...
final String CGLIB$name$0() {
return super.name();
}
public final String name() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$name$0$Method, CGLIB$emptyArgs, CGLIB$name$0$Proxy) : super.name();
}
// 没有重写 m1()
final void CGLIB$m2$1() {
super.m2();
}
public final void m2() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$m2$1$Method, CGLIB$emptyArgs, CGLIB$m2$1$Proxy);
} else {
super.m2();
}
}
// 省略...
final void CGLIB$afterPropertiesSet$6() {
super.afterPropertiesSet();
}
public final void afterPropertiesSet() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$afterPropertiesSet$6$Method, CGLIB$emptyArgs, CGLIB$afterPropertiesSet$6$Proxy);
} else {
super.afterPropertiesSet();
}
}
// 省略...
}
结论
1、如果被代理类的 final 方法中用到了注入的 bean,在代理对象中调用这个 final 方法,方法中的 bean 是空的。
2、Spring 的 IOC 容器中存的是代理对象。