JDK动态代理与CGLIB
代理的概念
我们执行一个方法的时候,很多情况下需要进行验证判断,比如权限验证,通过了则执行这个方法,那岂不是每个方法中都要调一次权限验证的代码,而且代码完全一样。如果是对于特定的URL进行验证处理,那我们可以尝试使用Servlet过滤器或者Spring拦截器进行请求处理判断是否执行,但如果需要拦截某些特定的方法呢?比如说com包下的所有以find开头的方法都执行验证处理,怎么做?这个时候就需要代理来完成了。我们执行一个对象的方法时,不直接去执行这个方法,而是执行这个对象的代理对象的方法,由这个代理对象的方法去执行真正执行的方法。我们可以对这个代理对象的执行进行处理,这就是代理。代理属于横切,继承属于纵切。
jdk动态代理
//JDK动态代理的实现必须有对应的接口类
public interface ExInterface {
void execute();
}
public class A implements ExInterface {
public void execute() {
System.out.println("执行A的execute方法...");
}
}
jdk动态代理处理类必须实现Invocation接口,且只能代理接口的实现,换句话说,没有接口的类代理不了。
public class JDKProxy implements InvocationHandler {
/**
* 调用被代理类(目标对象)的任意方法都会触发invoke方法
* proxy - 最终的代理对象
* method - 接口中的方法或者Object中的toString、equals、hashCode方法
* args - 调用代理方法时的实际参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//过滤不需要该业务的方法
if("execute".equals(method.getName())) {
System.out.println("增强前逻辑");
//调用目标对象的方法
Object result = method.invoke(target, args);
System.out.println("增强后逻辑");
return result;
}
//如果不需要增强直接执行原方法
return method.invoke(target,args);
}
private A target;
public JDKProxy(A target){
this.target = target;
}
public ExInterface createProxy(){
return (ExInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public static void main(String[] args){
A a=new A();
//创建JDK代理
JDKProxy jdkProxy=new JDKProxy(a);
//创建代理对象
ExInterface proxy=jdkProxy.createProxy();
ExInterface pr = jdkProxy.createProxy();
System.out.println(pr == proxy);//false
// com.sun.proxy.$Proxy0,生成的代理类在这个包下
byte[] classFile = ProxyGenerator.generateProxyClass(proxy.getClass().getName(), A.class.getInterfaces());
String path = "C:/Users/ljh/Desktop/a.class";
try(FileOutputStream fos = new FileOutputStream(path)){
fos.write(classFile);
fos.flush();
System.out.println("写入成功");
}catch (Exception e){
e.printStackTrace();
}
//执行代理对象方法
proxy.execute();
}
}
//output:
false
写入成功
增强前逻辑
执行A的execute方法...
增强后逻辑
JDK动态代理生成的class文件
package com.sun.proxy;
import com.aop.aspect_spring.jdk.ExInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements ExInterface {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void execute() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("com.aop.aspect_spring.jdk.ExInterface").getMethod("execute");
m2 = Class.forName("java.lang.Object").getMethod("toString");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
JDK动态代理的源码分析
方法中最重要的就是Proxy.newProxyInstance,现在来看看这个代理类做了什么?
/**
* loader - 定义代理类的类加载器
* interfaces - 代理类实现的接口列表
* h - 代理处理器
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
...安全检查
/*
* 检查或者生成指定的代理类,Proxy类内部维护了代理类的缓存,如果缓存里有则直接返回,如果没有,则生成
*/
Class<?> cl = getProxyClass0(loader, intfs);
...调用生成代理类中参数为InvocationHandler的构造函数创建对象
}
getProxyClass0()源码如下:
//生成一个代理对象,但是调用这个方法前一定要调用checkProxyAccess方法检查许可权
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) { //代理的接口数量不能超过65535个
throw new IllegalArgumentException("interface limit exceeded");
}
//如果代理类已经存在就从缓存取,否则它将会通过ProxyClassFactory类创建
return proxyClassCache.get(loader, interfaces);
}
既然这样我们就来看看ProxyClassFactory类到底做了什么?源码如下:
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 所有代理类名称的前缀
private static final String proxyClassNamePrefix = "$Proxy";
//生成的代理类名字用到的下一个数字,比如说$Proxy0,指的就是这个0
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
//传进来的是Class对象,然而我们知道Class对象的生成是在JVM类加载的加载阶段,还没有进入链接阶段就已经生成,它是由ClassLoader类加载器加载二进制数据到JVM由JVM生成的,说明它有属于自己的类加载器。
//我们知道一个.class文件一个类加载器对象只会加载一次,换句话说JVM不允许一个类加载器对象加载同一个class文件两次,再次加载的话应该用ClassLoader的loadClass()方法,它会从内存中读取已经加载过的Class对象。
//如果是不同的类加载器对象,那么即使加载的是同一个.class文件,最终JVM生成的Class对象也是不同的。
//详见我的另一篇博客类的加载。
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
//这个判断就是为了验证原本加载完的Class对象是否是指定的类加载器对象loader加载的
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
//判断代理的Class对象是否是接口,不是则抛异常
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
//往Map中插入数据,如果存在相同的Key,则替换这个值并返回原来的值。
//不为空的话就说明这是一个重复接口,当然这是不允许的
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
//定义生成的代理类的包名package,告诉它生成的代理类需要放到哪里
String proxyPkg = null;
//如果代理的接口不是public的,那就必须保证不是public的接口都在同一个包下,
//这也为了生成的代理类起码能够访问到这些接口,不然从何代理呢
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
//如果代理的接口是public修饰的,则在"com.sun.proxy"包下生成
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
//这是一个原子操作,目的是保证每一个num都是不同的,即使在多线程的环境中
long num = nextUniqueNumber.getAndIncrement();
//最终代理类的名字
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//生成代理类的字节数组
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
//定义生成类,就是JVM加载类的加载阶段,静态变量还没有初始化默认值
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
看看generateProxyClass(),源码如下:
public static byte[] generateProxyClass(String paramString, Class[] paramArrayOfClass)
{
ProxyGenerator localProxyGenerator = new ProxyGenerator(paramString, paramArrayOfClass);
//先加入Object的hashCode、equals、toString方法,然后是接口中的方法。
final byte[] arrayOfByte = localProxyGenerator.generateClassFile();
...
return arrayOfByte;
}
有兴趣的可以自行翻阅源码,主要篇幅有点多,这里就不介绍了。
CGLIB
JDK动态代理是通过实现接口的方式代理的,那么如果需要代理的类没有实现任何接口该怎么代理呢?Spring提供了另一种代理方式,就是通过继承目标类生成其子类的方式,既然是通过继承的方式实现的,那么这个类肯定不能是final类,且无法代理final方法。CGLIB是利用ASM开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
目标类:
public class A {
public String methodInterceptor(String name) {
System.out.println("target-methodInterceptor: " + name);
return name;
}
public void invocationHandler(int str) {
System.out.println("target-invocationHandler: " + str);
}
public int noOp(int str) {
System.out.println("target-noOp: " + str);
return str + 10;
}
public int methodFixedValue(int n) {
System.out.println("target-methodFixedValue:" + n);
return n + 10;
}
}
spring的CGLIB创建代理对象依赖于Enhancer类,具体使用如下:
public class CGLibTest {
private A target; //定义需要增强代理的对象
public CGLibTest(A target) {
this.target = target;
}
public A createProxy() {
Enhancer enhancer = new Enhancer();
//设置需要代理的类,也可以是接口,那生成的代理类就是实现这个接口
enhancer.setSuperclass(target.getClass());
enhancer.setCallbackFilter(new ClassCallbackFilter());//设置代理回调的过滤器,主要用于为多个方法设置不同的回调处理
//设置多个回调处理器,NoOp.INSTANCE表示代理类不做处理,直接执行目标类的处理;
//ClassFixedValue实现FixedValue接口,表示这个回调只执行这个回调方法,不执行目标类的方法
enhancer.setCallbacks(new Callback[]{new MethodInterceptorDemo(target), new InvocationHandlerDemo(target), NoOp.INSTANCE, new ClassFixedValue()});
A a = (A) enhancer.create();//创建代理对象
//将生成的代理类写出到指定的文件中
byte[] bs = DefaultGeneratorStrategy.INSTANCE.generate(enhancer);
String path = "C:/Users/ljh/Desktop/b.class";
FileOutputStream fos = new FileOutputStream(path);
fos.write(bs);
fos.flush();
System.out.println("写入成功");
return a;
}
public static void main(String[] args) {
CGLibTest cglib = new CGLibTest(new A());
A proxyObject = cglib.createProxy();
proxyObject.methodInterceptor("abcde");
proxyObject.invocationHandler(1);
int fixed = proxyObject.methodFixedValue(128);
System.out.println("fixedValue:" + fixed);
int c = proxyObject.noOp(4);
System.out.println("noOp:" + c);
}
}
//output:
MethodInterceptorDemo.intercept
target-methodInterceptor: abcde
target-methodInterceptor: abcde
InvocationHandlerDemo.invoke
target-invocationHandler: 1
loadObject()...
fixedValue:999
target-noOp: 4
noOp:14
回调处理器InvocationHandlerDemo实现InvocationHandler接口,注意这是Spring的接口,回调处理器都需要实现Callback接口,代码如下:
public class InvocationHandlerDemo implements InvocationHandler {
private Object target;//定义增强代理对象
public InvocationHandlerDemo(Object target) {
this.target = target;
}
/**
* o - 代理对象
* method - 目标类方法
* objects - 方法参数
*/
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("InvocationHandlerDemo.invoke");
return method.invoke(target, objects);
}
}
回调处理器MethodInterceptorDemo代码如下:
public class MethodInterceptorDemo implements MethodInterceptor {
private Object target;
public MethodInterceptorDemo(Object target){
this.target = target;
}
/**
* 回调函数
*
* @param proxy 代理对象
* @param method 目标类方法
* @param args 方法参数
* @param methodProxy 每个被代理的方法都对应一个MethodProxy对象,
* methodProxy.invokeSuper方法最终调用目标类的原始方法
* @return
* @throws Throwable
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("MethodInterceptorDemo.intercept");
Object a = method.invoke(target, args);
Object b = methodProxy.invokeSuper(proxy, args);
//两者都可以执行目标类的方法,但是注意如果实现的是接口,那么invokeSuper这个方法会因为没有父类而找不到执行的方法
return b;
}
}
回调处理器ClassFixedValue代码如下:
/**
* 表示锁定方法返回值,被代理的方法不执行
*/
public class ClassFixedValue implements FixedValue {
@Override
public Object loadObject() throws Exception {
System.out.println("loadObject()...");
Object object = 999;
return object;
}
}
好的,写了这几个回调处理器要怎样才能处理对应符合条件的方法呢?CGLIB是通过实现CallbackFilter接口的accept方法完成的。ClassCallbackFilter的代码如下:
/**
* 返回的值表示callbacks数组的下标,通知使用哪个Callback
*/
public class ClassCallbackFilter implements CallbackFilter {
@Override
public int accept(Method method) {
//判断哪个方法执行哪个回调处理器,但是要注意如果方法没有配置对应的处理器,那么会直接执行目标类的方法
switch (method.getName()) {
case "methodInterceptor":
return 0;
case "invocationHandler":
return 1;
case "noOp":
return 2;
case "methodFixedValue":
return 3;
}
return 2;
}
}
注意如果方法没有配置对应的处理器,那么会直接执行目标类的方法
CGLIB动态代理生成的class文件
package aop.proxy.cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.FixedValue;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.cglib.proxy.UndeclaredThrowableException;
public class A$$EnhancerByCGLIB$$ca0a6d8b extends A implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private InvocationHandler CGLIB$CALLBACK_1;
private NoOp CGLIB$CALLBACK_2;
private FixedValue CGLIB$CALLBACK_3;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$methodInterceptor$2$Method;
private static final MethodProxy CGLIB$methodInterceptor$2$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$invocationHandler$3;
static void CGLIB$STATICHOOK2() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("aop.proxy.cglib.A$$EnhancerByCGLIB$$ca0a6d8b");
Class var1;
CGLIB$methodInterceptor$2$Method = ReflectUtils.findMethods(new String[]{"methodInterceptor", "(Ljava/lang/String;)Ljava/lang/String;"}, (var1 = Class.forName("aop.proxy.cglib.A")).getDeclaredMethods())[0];
CGLIB$methodInterceptor$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Ljava/lang/String;", "methodInterceptor", "CGLIB$methodInterceptor$2");
CGLIB$invocationHandler$3 = Class.forName("aop.proxy.cglib.A").getDeclaredMethod("invocationHandler", Integer.TYPE);
}
final String CGLIB$methodInterceptor$2(String var1) {
return super.methodInterceptor(var1);
}
public final String methodInterceptor(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$methodInterceptor$2$Method, new Object[]{var1}, CGLIB$methodInterceptor$2$Proxy) : super.methodInterceptor(var1);
}
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case 625529861:
if (var10000.equals("methodInterceptor(Ljava/lang/String;)Ljava/lang/String;")) {
return CGLIB$methodInterceptor$2$Proxy;
}
}
return null;
}
public final void invocationHandler(int var1) {
try {
InvocationHandler var10000 = this.CGLIB$CALLBACK_1;
if (this.CGLIB$CALLBACK_1 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_1;
}
var10000.invoke(this, CGLIB$invocationHandler$3, new Object[]{new Integer(var1)});
} catch (Error | RuntimeException var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int methodFixedValue(int var1) {
FixedValue var10000 = this.CGLIB$CALLBACK_3;
if (this.CGLIB$CALLBACK_3 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_3;
}
Object var2 = var10000.loadObject();
return var2 == null ? 0 : ((Number)var2).intValue();
}
public A$$EnhancerByCGLIB$$ca0a6d8b() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
A$$EnhancerByCGLIB$$ca0a6d8b var1 = (A$$EnhancerByCGLIB$$ca0a6d8b)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (CGLIB$STATIC_CALLBACKS == null) {
return;
}
}
Callback[] var10001 = (Callback[])var10000;
var1.CGLIB$CALLBACK_3 = (FixedValue)((Callback[])var10000)[3];
var1.CGLIB$CALLBACK_2 = (NoOp)var10001[2];
var1.CGLIB$CALLBACK_1 = (InvocationHandler)var10001[1];
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)var10001[0];
}
}
public Object newInstance(Callback[] var1) {
CGLIB$SET_THREAD_CALLBACKS(var1);
A$$EnhancerByCGLIB$$ca0a6d8b var10000 = new A$$EnhancerByCGLIB$$ca0a6d8b();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Callback var1) {
throw new IllegalStateException("More than one callback object required");
}
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
A$$EnhancerByCGLIB$$ca0a6d8b var10000 = new A$$EnhancerByCGLIB$$ca0a6d8b;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
public Callback getCallback(int var1) {
CGLIB$BIND_CALLBACKS(this);
Object var10000;
switch(var1) {
case 0:
var10000 = this.CGLIB$CALLBACK_0;
break;
case 1:
var10000 = this.CGLIB$CALLBACK_1;
break;
case 2:
var10000 = this.CGLIB$CALLBACK_2;
break;
case 3:
var10000 = this.CGLIB$CALLBACK_3;
break;
default:
var10000 = null;
}
return (Callback)var10000;
}
public void setCallback(int var1, Callback var2) {
switch(var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
break;
case 1:
this.CGLIB$CALLBACK_1 = (InvocationHandler)var2;
break;
case 2:
this.CGLIB$CALLBACK_2 = (NoOp)var2;
break;
case 3:
this.CGLIB$CALLBACK_3 = (FixedValue)var2;
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[]{this.CGLIB$CALLBACK_0, this.CGLIB$CALLBACK_1, this.CGLIB$CALLBACK_2, this.CGLIB$CALLBACK_3};
}
public void setCallbacks(Callback[] var1) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
this.CGLIB$CALLBACK_1 = (InvocationHandler)var1[1];
this.CGLIB$CALLBACK_2 = (NoOp)var1[2];
this.CGLIB$CALLBACK_3 = (FixedValue)var1[3];
}
static {
CGLIB$STATICHOOK2();
}
}
Spring选择何种代理
Spring是依靠什么来判断采用哪种代理策略来生成AOP代理呢?以下代码就是Spring的判断逻辑
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
//如果目标类是一个接口或者使用JDK代理生成的类,就执行JDK动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
/**
* Determine whether the supplied {@link AdvisedSupport} has only the
* {@link org.springframework.aop.SpringProxy} interface specified
* (or no proxy interfaces specified at all).
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
}
advisedSupport.isOptimize()与advisedSupport.isProxyTargetClass()默认返回都是false,所以在默认情况下目标对象有没有实现接口决定着Spring采取的策略,当然可以设置advisedSupport.isOptimize()或者advisedSupport.isProxyTargetClass()返回为true,这样无论目标对象有没有实现接口Spring都会选择使用CGLIB代理。
下面通过一个实例讲解:
public interface IService {
void method1();
}
@Service("serviceImpl")
public class ServiceImpl implements IService {
@Transactional
public void method1() {
System.out.println("method1");
}
}
@Component
public class ProxyPattern {
@Autowired
private ServiceImpl serviceImpl;
}
public class ProxyAnnotationTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyAnnotationTest.xml");
}
}
xml配置:
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false"/>
默认是采用jdk动态代理,但是当jdk代理无法完成时,比如说没有接口,那么会自动转为CGLIB代理,尽管此时proxy-target-class属性还是false,相反,如果设置成true,则采用CGLIB代理。
目标类实现了接口,且proxy-target-class设置成false(默认),那么就采用jdk动态代理,代理的是IService接口,因此ProxyPattern中注入ServiceImpl会失败,应该注入IService接口。报错如下:
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'serviceImpl' is expected to be of type 'aop.spring.ServiceImpl' but was actually of type 'com.sun.proxy.$Proxy13'
注意:如果ServiceImpl类中没有需要代理的方法(目前是method1的事务代理),那么这个类就不会被代理。 想想也知道,无论怎么样Spring都给我们代理,那效率也太低了,启动时会卡到爆,毕竟他要生成代理类需要开销的啊。
解释@Transactional不生效的问题
我们再来讲述一个新手常犯的错误,就是数据库事务注解的不生效问题。阅读完以上的分析可以知道,JDK动态代理是通过实现接口的方式实现代理,Spring中也是优先使用JDK代理。但是当JDK代理无法完成时,就是没有接口或者Spring特定配置,导致Spring必须使用CGLIB代理,而CGLIB是通过继承实现的,再结合上一节“Spring选择何种代理”的分析,如果代理类使用的是JDK的动态代理,那么在我们写的实现类的方法内部调用另一个需要代理的方法时,发现这个事务注解不生效了。其实原因很简单,JDK代理生成的类是实现了和具体业务类相同接口,但是这是不同的两个类甚至没有继承关系,只是同时实现了相同的接口,因此我们在实现类中调用另一个方法时,那是实现类自己的方法,并没有经过代理处理,那么事务当然不生效了。
那么如何解决这种问题呢?在实现类方法中直接取到当前的这个代理对象,然后再次调用这个代理方法,那就肯定能被代理到了。具体方法:AopContext.currentProxy()
,在AOP上下文中获取当前的代理对象,但是要注意为了是方法生效还必须在配置文件中配置<aop:aspectj-autoproxy expose-proxy="true"/>
具体使用选择
1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP,在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>
3)如果目标对象没有实现接口,必须采用CGLIB,Spring会自动在JDK动态代理和CGLIB之间转换。
4)当Bean实现接口时,Spring就会用JDK动态代理。
5)当Bean没有实现接口时,Spring使用CGLIB。
6)按jdk7及以上版本来说,JDK动态代理的效率比CGLIB高。
https://blog.youkuaiyun.com/john_lw/article/details/79539070