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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值