ARTS001 - JDK动态代理介绍与实现

1、什么是动态代理

​ 使用jdk的反射机制,创建java对象的能力。创建的是代理类的对象。

​ 在程序执行时,调用jdk提供的方法动态的指定要代理目标类。

2、动态代理能做什么

​ 在不改变原有目标方法功能的前提下,在代理中增强自己的功能代码。

3、实现类

​ java.lang.reflect 反射包下3个类:

  • public interface InvocationHandler 接口:你的代理需要做什么)

    invoke() 方法:代理需要做的具体功能

/**
 * Object proxy: jdk创建的额代理对象
 * Method method: 目标对象的方法
 * Object[] args: 目标对象的方法的参数
 */
public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;
  • public final class Method 类:可以执行目标类中的方法;method.invoke(目标对象, 方法参数);
  • public class Proxy 类:创建的代理对象;Proxy.newProxyInstance();
/**
 * 创建动态代理对象 等用于new
 * ClassLoader loader: 目标对象的类加载器
 * Class<?>[] interfaces: 目标对象的实现的接口
 * InvocationHandler h: 代理类要做什么
 */
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

4、具体步骤

  • 创建功能的接口,定义目标类要完成的功能方法
  • 创建目标接口的实现类,实现功能方法
  • 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能 【调用目标类的功能方法 + 增强功能(补充的功能)】
  • 使用Proxy类的静态方法newProxyInstance,创建代理对象。 并把返回值转为接口类型。

5、代码

//创建功能的接口
public interface HelloService {

    //定义目标类要完成的功能方法
    String sayHello(String s);
}
//创建目标接口的实现类
public class HelloWorldImpl implements HelloService {

    //实现功能方法
    @Override
    public String sayHello(String s) {
        return s + " hello world";
    }
}
//创建InvocationHandler接口的实现类
public class HelloInvocationHandler implements InvocationHandler {

    //目标对象
    public Object target;

    //通过构造方法,将目标对象传入
    public HelloInvocationHandler(Object target) {
        this.target = target;
    }

    //在invoke方法中完成代理类的功能
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //调用目标方法
        //target:目标对象,args:方法参数
        Object result = method.invoke(target, args);

        //增强功能
        if (result != null) {
            String s = (String)result;
            result = s.concat(",hello java");
        }

        return result;
    }
}
//启动类
public class HelloMain {

    public static void main(String[] args) {

        //目标对象
        HelloService helloService = new HelloWorldImpl();

        HelloInvocationHandler handler = new HelloInvocationHandler(helloService);

        //使用Proxy类的静态方法newProxyInstance,创建代理对象。 并把返回值转为接口类型
        //创建动态代理对象 等用于new
        //参数1 类加载器,目标对象的类加载器
        //参数2 接口,目标对象的实现的接口
        //参数3 处理器,代理类要完成的功能
        HelloService proxy = (HelloService)Proxy.newProxyInstance(
                helloService.getClass().getClassLoader(),
                helloService.getClass().getInterfaces(),
                handler);

        //使用代理对象的方法
        String say = proxy.sayHello("say");
        System.out.println(say);
    }
}

6、原理

分析 Proxy.newProxyInstance 具体做了什么

java.lang.reflect.Proxy newProxyInstance() 方法

关注: Class<?> cl = getProxyClass0(loader, intfs);

//java.lang.reflect.Proxy
@CallerSensitive
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.
     * 调用getProxyClass0方法生成代理类的字节码文件 【跳转到getProxyClass0()方法】
     */
    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);
    }
}

java.lang.reflect.Proxy getProxyClass0 方法

关注: return proxyClassCache.get(loader, interfaces)

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

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    // 如果缓存中已经存在相应接口的代理类,直接返回;否则,使用ProxyClassFactory创建代理类【跳转到get()方法】
    return proxyClassCache.get(loader, interfaces);
}

java.lang.reflect.WeakCache get() 方法

//此处为缓存逻辑,可以不看,具体看使用ProxyClassFactory创建代理类【跳转ProxyClassFactory】
public V get(K key, P parameter) {
    Objects.requireNonNull(parameter);

    expungeStaleEntries();

    Object cacheKey = CacheKey.valueOf(key, refQueue);

    // lazily install the 2nd level valuesMap for the particular cacheKey
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    if (valuesMap == null) {
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }

    // create subKey and retrieve the possible Supplier<V> stored by that
    // subKey from valuesMap
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;

    while (true) {
        if (supplier != null) {
            // supplier might be a Factory or a CacheValue<V> instance
            V value = supplier.get();
            if (value != null) {
                return value;
            }
        }
        // else no supplier in cache
        // or a supplier that returned null (could be a cleared CacheValue
        // or a Factory that wasn't successful in installing the CacheValue)

        // lazily construct a Factory
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }

        if (supplier == null) {
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                // successfully installed Factory
                supplier = factory;
            }
            // else retry with winning supplier
        } else {
            if (valuesMap.replace(subKey, supplier, factory)) {
                // successfully replaced
                // cleared CacheEntry / unsuccessful Factory
                // with our Factory
                supplier = factory;
            } else {
                // retry with current supplier
                supplier = valuesMap.get(subKey);
            }
        }
    }
}

java.lang.reflect.Proxy ProxyClassFactory 静态内部类

关注: byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);

//实现了BiFunction接口,实现了BiFunction接口中的apply方法。
//当WeakCache中没有缓存相应接口的代理类,则会调用ProxyClassFactory类的apply方法来创建代理类。
private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    // prefix for all proxy class names 
    // 所有代理类名字的前缀
    private static final String proxyClassNamePrefix = "$Proxy";

    // next number to use for generation of unique proxy class names
    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 {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            /*
             * Verify that the Class object actually represents an
             * interface.
             */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            /*
             * Verify that this interface is not a duplicate.
             */
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        String proxyPkg = null;     // package to define proxy class in
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        /*
         * Record the package of a non-public proxy interface so that the
         * proxy class will be defined in the same package.  Verify that
         * all non-public proxy interfaces are in the same package.
         */
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                accessFlags = Modifier.FINAL;
                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");
                }
            }
        }

        if (proxyPkg == null) {
            // if no non-public proxy interfaces, use com.sun.proxy package
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        /*
         * Choose a name for the proxy class to generate.
         */
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        /*
         * Generate the specified proxy class.
         * 生成代理类的字节码文件
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            //使用类加载器将代理类的字节码文件加载到JVM中
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            /*
             * A ClassFormatError here means that (barring bugs in the
             * proxy class generation code) there was some other
             * invalid aspect of the arguments supplied to the proxy
             * class creation (such as virtual machine limitations
             * exceeded).
             */
            throw new IllegalArgumentException(e.toString());
        }
    }
}

sun.misc.ProxyGenerator generateProxyClass 方法

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
    ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
    final byte[] var4 = var3.generateClassFile();
    if (saveGeneratedFiles) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                try {
                    int var1 = var0.lastIndexOf(46);
                    Path var2;
                    if (var1 > 0) {
                        Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                        Files.createDirectories(var3);
                        var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                    } else {
                        var2 = Paths.get(var0 + ".class");
                    }

                    Files.write(var2, var4, new OpenOption[0]);
                    return null;
                } catch (IOException var4x) {
                    throw new InternalError("I/O exception saving generated file: " + var4x);
                }
            }
        });
    }

从generateProxyClass() 方法可以看出,这是实际生成class字节码文件的地方,于是我们手动调用这个方法,将字节码内容写入文件中

手动触发字节码内容写入文件

public static void main(String[] args) {

    byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", HelloWorldImpl.class.getInterfaces());
    String path = "F:\\IDEAProject\\Practice\\src\\main\\java\\com.sun.proxy\\HelloWorldImpl.class";
    try (FileOutputStream fileOutputStream = new FileOutputStream(path);) {
        fileOutputStream.write(bytes);
        fileOutputStream.flush();
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("class 写入完毕");
}

字节码文件生成

在这里插入图片描述

字节码文件具体内容

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.HelloService;

public final class $Proxy0 extends Proxy implements HelloService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    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 sayHello(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, 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 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"));
            m3 = Class.forName("proxy.HelloService").getMethod("sayHello", Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            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类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
  • 提供了一个使用InvocationHandler作为参数的构造方法
    生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。
  • 重写了Object类的equals、hashCode、toString,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法
  • 代理类实现代理接口的sayHello方法中,只是简单的调用了InvocationHandler的invoke方法,我们可以在invoke方法中进行一些特殊操作,甚至不调用实现的方法,直接返回。

问题

1)为什么jdk动态代理必须要基于接口来完成?

JDK动态代理生成的对象默认是继承Proxy,Java不支持多继承

2)代理类名称是包名结构?

3)jdk动态代理与cglib动态代理 区别?

4)在目标对象的方法A内部调用目标对象的其他方法B,此时动态代理并不会对方法B进行增强。

5)@Transactional @Async 注解失效?
在Spring项目中,通过@Transactional来声明事务时,出现了事务不生效的情况,以及@Async等案例,在同一个类的两个方法中开启异步,然后在方法内部进行互相调用,最终导致异步失效的问题。

注解是通过Spring AOP 实现的,如果动态代理使用的是JDK动态代理,那么在方法的内部调用该方法中其他带有该注解的方法,由于此时调用的不是动态代理对象,所以注解失效。

6)JDK动态代理中,目标对象调用自己的另一个方法时,会经过代理对象么?
内部调用方法使用的对象是目标对象本身,被调用的方法不会经过代理对象。

7、下一篇文章

介绍 cglib 动态代理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值