Java动态代理学习

Java动态代理学习

代理模式

代理模式(Proxy Pattern)是设计模式中的一种,它为其他对象提供一个代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用,并且可以通过代理对象对目标对象进行额外的操作或处理,如访问控制、日志记录、事务管理等

静态代理

在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。静态代理的缺点是,如果我们的接口一旦增加新的方法,除了所有实现类需要增加此方法外,所有代理类也需要增加此方法。这样就增加了代码维护的复杂度

动态代理

在程序运行时,动态地创建代理类。动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类

JDK动态代理

JDK动态代理基于接口实现,要求目标对象要有实现的接口才能代理其方法

代码实现

目标类接口

/**
 * 目标类接口
 */
public interface Persion {
    /**
     * 被代理方法1
     */
    void talk();

    /**
     * 被代理方法2
     */
    void sing();
}

目标类

/**
 * 目标类
 */
public class Student implements Persion{

    @Override
    public void talk() {
        System.out.println("俺是学生");
    }

    @Override
    public void sing() {
        System.out.println("我唱七里香");
    }
}

代理逻辑handler

public class ProxyHandler implements InvocationHandler {

    /**
     * 目标对象
     */
    private final Object target;

    public ProxyHandler(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置操作。。");
        Object invoke = method.invoke(target, args);
        System.out.println("后置操作。。");
        return invoke;
    }
}

创建代理对象并执行代理方法

public static void main(String[] args) {
        //目标对象
        Persion  target = new Student();
        //代理对象
        ProxyHandler proxyHandler = new ProxyHandler(target);
        //创建代理类对象
        //JDK会通过传入的参数信息动态地在内存中创建和.class文件等同的字节码
        //然后会根据相应的字节码转换成对应的class
        //最后创建代理类实例
        Persion proxy = (Persion) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), proxyHandler);

        proxy.talk();
        System.out.println("------------------------");
        proxy.sing();
    }

执行结果,实现了对Student类的方法增强

image-hefw.png

源码分析

我们查看Proxy.newProxyInstance方法源码

	public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

Class<?> cl = getProxyClass0(loader, intfs);它的作用是查找或生成指定的代理类,getProxyClass0() 方法源码:

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        return proxyClassCache.get(loader, interfaces);
    }

直接从 proxyClassCache 中获取缓存的代理类副本,若没有将通过 ProxyClassFactory 创建代理类,获取到代理类后,执行 逻辑 final Constructor<?> cons = cl.getConstructor(constructorParams); 获取对应的构造方法,代理类构造函数的参数 constructorParams 是一个class数组

private static final Class<?>[] constructorParams = { InvocationHandler.class }

InvocationHandler 就是我们代理类 ProxyHandler 实现的接口

这个代理类的构造方法是怎么来的呢

回到上面从缓存 proxyClassCacheget方法逻辑,缓存没有则调用 subKeyFactory.apply(key,parameter)生成,这里的 key 与我们传入的类加载器相关,parameter 就是我们传入的接口class数 subKeyFactory.apply(key, parameter)根据规则生成一个类路径,为 com.sun.proxy.$Proxy0,后面的0是一个原子自增的数字,创建一个代理类则递增, 最终会调用一个native方法 defineClass0生成class,这就是我们动态生成的class,因此获取带有 constructorParams参数的构造方法应该是在native方法中生成的,这个在运行中生成的代理类class与我们的目标类都实现了相同的接口

通过对 newProxyInstance方法的源码两行逻辑:Class<?> cl = getProxyClass0(loader, intfs); final Constructor<?> cons = cl.getConstructor(constructorParams); 我们了解这个运行时生成的代理类的类路径为 com.sun.proxy.$Proxy0(第一个代理类为0,后面依次递增), 了解带 constructorParams参数的构造方法来源。constructorParams参数中有 InvocationHandler接口的 class,接下来就是将目标对象的方法逻辑和 ProxyHandler类invoke 一起作为代理类接口的具体实现。第三行核心逻辑 cons.newInstance(new Object[]{h}); 使用反射通过有参构造方法创建实例,这里的 h参数就是我们的 ProxyHandler实例, 与第二行核心逻辑获取的有参构造一致,它在动态生成类class时已经组装完成。通过反射创建类路径为 com.sun.proxy.$Proxy0实现了 Persion接口的 两个方法,有个成员变量为接口的数组,通过有参构造赋值, 两个方法的实现就是调用这个成员变量 InvocationHandler接口实例的 invoke方法。这种方式的动态代理是通过Java反射机制实现的,代理类必须基于统一的接口,这就是JDK动态代理。

字节码文件

在main方法开头添加 System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 方法修改系统变量,可以保存生成的字节码文件

public final class $Proxy0 extends Proxy implements Persion {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m0;

    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 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);
        }
    }

    public final void talk() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void sing() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    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);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("tao.Persion").getMethod("talk");
            m4 = Class.forName("tao.Persion").getMethod("sing");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

简单说明关键内容:

  • 代理类继承Proxy类,实现目标类接口(Persion接口),重写equals、hashCode、toString的方法
  • 类和方法都被public final 修饰,所以类和方法只能使用,不能继承和重写
  • 方法的实现最终会调用到代理调用处理器对象(ProxyHandler类实例)的invoke方法

CGLIB动态代理

代码实现

引入Maven依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

创建一个User类,不实现任何接口

public class User {

    public void study(){
        System.out.println("执行 study 方法 。。");
    }

    public void work(){
        System.out.println("执行 work 方法 。。");
    }
}

创建一个 UserInterceptor类 ,实现MethodInterceptor接口,用于方法的拦截回调

public class UserInterceptor implements MethodInterceptor {
    /**
     * @param o 表示要进行增强的对象
     * @param method 表示拦截的方法
     * @param objects 数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double
     * @param methodProxy 表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用
     * @return 执行结果
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("前置通知");
        // 注意这里是调用 invokeSuper 而不是 invoke,否则死循环,methodProxy.invokesuper执行的是原始类的方法,method.invoke执行的是子类的方法
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("后置通知");
        return result;
    }
}

创建代理类,执行测试

public class ProxyDemo {
    public static void main(String[] args) {

        Enhancer enhancer = new Enhancer();
        //设置超类,cglib是通过继承来实现的
        enhancer.setSuperclass(User.class);
        enhancer.setCallback(new UserInterceptor());
        //创建代理类
        User user = (User) enhancer.create();
        //执行测试
        user.study();
        System.out.println("-------------------");
        user.work();
    }
}

打印结果

image-fqee.png

源码分析

查看 Enhancer 类,这里只保留重点学习的部分

public class Enhancer extends AbstractClassGenerator {
    private static final CallbackFilter ALL_ZERO = new CallbackFilter() {
        public int accept(Method method) {
            return 0;
        }
    };
    private static final Source SOURCE;

    private Class[] interfaces;
    private Callback[] callbacks;
    private Class superclass;
  
    public Enhancer() {
        super(SOURCE);
    }
}

Enhancer 继承了类 AbstractClassGenerator,创建 Enhancer 实例,会调用无参构造方法,无参构造方法中使用 super()调用父类的构造方法 。 SOURCE 是一个静态成员变量,数据结构如下

    protected static class Source {
        String name;
        Map cache = new WeakHashMap();

        public Source(String name) {
            this.name = name;
        }
    }

看一下 AbstractClassGenerator 类的构造方法

    protected AbstractClassGenerator(AbstractClassGenerator.Source source) {
        this.strategy = DefaultGeneratorStrategy.INSTANCE;
        this.namingPolicy = DefaultNamingPolicy.INSTANCE;
        this.useCache = true;
        this.source = source;
    }

设置了四个属性,sourceEnhancer 子类的成员变量;useCache 设置为true表示使用缓存;namingPolicy 设置为 DefaultNamingPolicy.INSTANCE, 设置类的命名策略,查看一下 DefaultNamingPolicy,详细逻辑这里跳过

public class DefaultNamingPolicy implements NamingPolicy {
    public static final DefaultNamingPolicy INSTANCE = new DefaultNamingPolicy();

    public DefaultNamingPolicy() {
    }

    public String getClassName(String prefix, String source, Object key, Predicate names) {
        if (prefix == null) {
            prefix = "net.sf.cglib.empty.Object";
        } else if (prefix.startsWith("java")) {
            prefix = "$" + prefix;
        }

        String base = prefix + "$$" + source.substring(source.lastIndexOf(46) + 1) + this.getTag() + "$$" + Integer.toHexString(key.hashCode());
        String attempt = base;

        for(int var7 = 2; names.evaluate(attempt); attempt = base + "_" + var7++) {
        }

        return attempt;
    }
}

了解了 Enhancer 类实例化的过程,再看一下 enhancer.setSuperclass(User.class); 方法实现

    public void setSuperclass(Class superclass) {
        if (superclass != null && superclass.isInterface()) {
            this.setInterfaces(new Class[]{superclass});
        } else if (superclass != null && superclass.equals(class$java$lang$Object == null ? (class$java$lang$Object = class$("java.lang.Object")) : class$java$lang$Object)) {
            this.superclass = null;
        } else {
            this.superclass = superclass;
        }

    }

判断传入的class不为空且为接口的条件下,设置 enhancer.interfaces 的值,如果不是接口的则设置 enhancer.superclass 的值为传入的class,也就是 User.class

继续查看 enhancer.setCallback(new UserInterceptor()); 方法的实现

    public void setCallback(Callback callback) {
        this.setCallbacks(new Callback[]{callback});
    }

    public void setCallbacks(Callback[] callbacks) {
        if (callbacks != null && callbacks.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty");
        } else {
            this.callbacks = callbacks;
        }
    }

就是将入参包装成数组并赋值给成员变量,接下来看核心的 enhancer.create()

    public Object create() {
        this.classOnly = false;
        this.argumentTypes = null;
        return this.createHelper();
    }

设置 classOnly = false ,继续看 this.createHelper();

    private Object createHelper() {
        this.validate();
        if (this.superclass != null) {
            this.setNamePrefix(this.superclass.getName());
        } else if (this.interfaces != null) {
            this.setNamePrefix(this.interfaces[ReflectUtils.findPackageProtected(this.interfaces)].getName());
        }

        return super.create(KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter, this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID));
    }

this.validate(); 校验并赋值,这里不过多说明;接着if判断区分了类和接口,为两种情况设置不同的 enhancer.namePrefix,我们测试代码中是类,此时设置的 值为 com.tao.proxy.User;最后调用父类的 create方法,KEY_FACTORY.newInstance方法生成了一个 Object类型的key作为 create方法的入参,create方法与JDK动态代理从缓存中获取代理类相似。 具体逻辑也是从缓存中获取,如果没有,回去加载前面生成的类路径,这里会抛出异常 ClassNotFoundException,最终调用反射中的native方法 newInstance动态生成代理类

字节码文件

在 main 方法开头加上 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".\\");,第二个参数为路径,表示与src同级目录

public class User$$EnhancerByCGLIB$$e488937e extends User implements Factory {

	//省略属性字段....

    final void CGLIB$study$0() {
        super.study();
    }

    public final void study() {
        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$study$0$Method, CGLIB$emptyArgs, CGLIB$study$0$Proxy);
        } else {
            super.study();
        }
    }

    final void CGLIB$work$1() {
        super.work();
    }

    public final void work() {
        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$work$1$Method, CGLIB$emptyArgs, CGLIB$work$1$Proxy);
        } else {
            super.work();
        }
    }

    //其余部分省略...
}

上述代码只保留了我们定义的 studywork 方法,这个新生成的类继承了我们的 User类,简单分析一下 study 方法(work 方法原理一样)

  • 首先给 var10000赋值,它是一个 MethodInterceptor类型, 也就是前面的 UserInterceptor
  • 然后 var10000不为null则调用 var10000.intercept()
  • 如果 var10000为null则调用 super.study();我们最初目标类的方法

总结

学习了两种动态代理的使用方式,通过源码的阅读简单了解了两种方式的实现原理和区别