今天在看《Spring5 高级编程》AOP那个章节的时候遇到一个问题
书中在实例化 ProxyFactory 时都是用的无参构造器,然后再调用父类 AdvisorSupport 中的setTarget() 方法来设置目标对象,但是 ProxyFactory 中存在一个可以直接传入目标对象的构造器:
点进去看这个构造器:
也是调用了父类 AdvisorSupport 中的 setTarget(Object target)方法,试下能不能直接用这个构造函数
省流版:不行,因为这个构造方法中还调用了 setInterfaces() 方法。指定了接口类型,会使用JDK动态代理,JDK动态代理的对象不能转换为具体的类型。
产生了如下报错:
debug看一下这两种写法有什么区别:
ProxyFactory factory = new ProxyFactory(guitarist)
使用了JDK动态代理,JDK动态代理生成的代理对象不是目标对象的类类型(即 Guitarist
类),而是一个实现了目标对象接口的代理类。
因此,改成这种写法则不会报错:
public static void main(String[] args) {
Guitarist guitarist = new Guitarist();
ProxyFactory factory = new ProxyFactory(guitarist);
Singer proxy = (Singer) factory.getProxy();
System.out.println("Proxy class: " + proxy.getClass());
System.out.println("Is CGLIB proxy: " + (proxy.getClass().getName().contains("CGLIB")));
}
如果尝试将这个代理对象强制转换为目标对象的具体类类型(Guitarist
类),会抛出 ClassCastException
ProxyFactory factory = new ProxyFactory();
factory.setTarget(guitarist);
使用这种写法时,采用了CGLIB 代理,CGLIB 代理通过创建目标类的子类实现代理,因此代理对象可以被强制转换为目标类。
** 会触发CGLIB 代理 的情况:
-
代理对象被强制生成为类代理:
- 如果你没有通过接口类型来访问代理对象,而是直接访问目标类类型(例如
Guitarist
),Spring 可能会选择使用 CGLIB,因为 JDK 动态代理不能代理具体的类。
- 如果你没有通过接口类型来访问代理对象,而是直接访问目标类类型(例如
-
未指定接口:
- 在
ProxyFactory
中,如果未显式告知代理对象应该实现哪些接口,Spring 会更倾向于使用 CGLIB,因为它可以代理具体类。 - 如果目标类实现了接口,且调用
factory.setInterfaces(Singer.class)
明确指定接口,则会使用 JDK 动态代理。
- 在