动态代理-JDK

Sold Out

Hawk Nelson - Diamonds

 代理的应用能够使我们编程更加简洁,让编程更加的优雅!

 

何为代理?

代理模式:

       代理(Proxy)是一种设计模式,提供了间接对目标对象进行访问的方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的功能上,增加额外的功能补充,即扩展目标对象的功能.这就符合了设计模式的开闭原则,即在对既有代码不改动的情况下进行功能的扩展。

白话点

      代理类为被代理类(目标类)预处理消息、过滤消息并在此之后将消息转发给被代理类,之后还能进行消息的后置处理。代理类和被代理类通常会存在关联关系(即上面提到的持有的被代理对象的引用),代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。

 

 

 

静态代理

我们直接上代码,这样就能够更加的直观的分析,同样我会将相应的注释写在代码上,便利理解和回顾。

面向接口编程:

/** * 定义的一个接口类 */public interface StockMarkService {    /**     * 方法     */    String makeMoney();}

 

定义一个被代理类:

/** * 被代理类  中国股市市场(最近有关注股市,就地取材哈) */public class ChinaStockMarkServiceImpl                             implements StockMarkService {    @Override    public String makeMoney() {        System.out.println("中国股市从不跟风?");        return "绝对跟风";    }}

 

定义一个代理类:

/** * 代理类实现 被代理类 * 请注意这里实现的 是一个我们自己定义的接口,区别于下面的动态代理 */public class StockMarkProxy implements StockMarkService {    //被代理类和代理类的父类(接口)    private StockMarkService stockMarkService;    //实现被代理类    public StockMarkProxy(StockMarkService stockMarkService) {        this.stockMarkService = stockMarkService;    }    @Override    public void makeMoney() {        //这里只是简化流程打印出来一句话,当然在具体的业务这里应该是实现一个方法        System.out.println("静态代理前置处理方法");        //这依旧是被代理类实现具体的方法        stockMarkService.makeMoney();        //代理类的后置处理        System.out.println("静态代理后置处理方法");    }  }  

输出结果:

静态代理前置处理方法

中国股市从不跟风?

静态代理后置处理方法


静态代理总结:

使用静态代理很容易就能够完成对一个类的代理操作。但是静态代理的缺点也是显而易见的,那就是一个代理类只能实现一个被代理类,必须实现和被代理相同的接口。如果需要被代理的类非常的多,那么就需要编写大量的代理类,最起码是一个接口实现一个相应的代理类,比较繁琐。

 

 

JDK动态代理

动态代理代理类的定义:

/** * 代理类实现 被代理类 *  * 这里实现的 InvocationHandler 这个接口,和静态代理的是不一样的 */public class StockMarkProxy implements InvocationHandler {    //被代理类(目标类)对象    private Object targetObject;    //赋值 被代理类    public StockMarkProxy(Object targetObject) {        this.targetObject = targetObject;    }    //实现接口方法    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("before.....加上代理类的名称:" + proxy.getClass().getName());        //这里可以看出依旧是 被代理类 实现自己的方法,这里方法返回的结果        Object methodResult = method.invoke(targetObject, args);        System.out.println("after......加上输出的结果:" + methodResult);        return methodResult;    }}

测试类:

public class ProxyTest {    public static void main(String[] args) throws Exception {        //被代理的类        ChinaStockMarkServiceImpl chinaStockMarkService = new ChinaStockMarkServiceImpl();        //委托类(非真实的代理类)        StockMarkProxy stockMarkProxy = new StockMarkProxy(chinaStockMarkService);        //构建代理类        StockMarkService stockMarkService = (StockMarkService) Proxy.newProxyInstance(chinaStockMarkService.getClass().getClassLoader(),                chinaStockMarkService.getClass().getInterfaces(), stockMarkProxy);        //执行相应方法        String result = stockMarkService.makeMoney();        System.out.println("方法执行完的结果:{}" + result);    }}

输出结果:

before.....加上代理类的名称:com.sun.proxy.$Proxy0

中国股市从不跟风?

after......加上输出的结果:绝对跟风

方法执行完的结果: 绝对跟风


 

$Proxy0是怎么来?

到这里的时候我们就会发现我们并没有定义这样的一个类,那么这个类是从哪里来的呢?下面就让我们一起一步步的分析这个源码实现过程。

 

  • 入口Proxy.newProxyInstance(...)

//构建代理类 首先我们介绍一下这个参数:// 类加载器 , 被代理类实现的所有接口(注意:所有的) ,最后一个是 委托类(也就是实现InvacationHandler接口的类)StockMarkService stockMarkService = (StockMarkService) Proxy.newProxyInstance(chinaStockMarkService.getClass().getClassLoader(),                chinaStockMarkService.getClass().getInterfaces(), stockMarkProxy);
  • 下面的源码部分可能有点多,我们把一些不需要看的过滤掉,只关注重点

  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.         * 传入类加载器,和接口数组,          * 这里会创建出来一个 $Proxy0 的类           * ①我们会继续深入这个方法           */           Class<?> cl = getProxyClass0(loader, intfs);        /*         * Invoke its constructor with the designated invocation handler.         */        try {            if (sm != null) {                checkNewProxyPermission(Reflection.getCallerClass(), cl);            }            /**             * private static final Class<?>[] constructorParams =             *                             { InvocationHandler.class };              * 构建 构造方法 所需要的参数               * 代理类的构造参数 默认就是一个(哈哈,这里就是限制死了构造参数的意思)             */               final Constructor<?> cons = cl.getConstructor(constructorParams);            // 这里的 h就是我们实现InvacationHandler接口的类对象            final InvocationHandler ih = h;            //如果Class作用域为私有,通过 setAccessible 支持访问            if (!Modifier.isPublic(cl.getModifiers())) {                AccessController.doPrivileged(new PrivilegedAction<Void>() {                    public Void run() {                        cons.setAccessible(true);                        return null;                    }                });            }            // 这里就是通过使用构造方法形式的反射创建一个 对象            // 这里就创建出来了 &Proxy0 的对象            return cons.newInstance(new Object[]{h});        } catch (IllegalAccessException|InstantiationException e) {          //...省略了一些异常捕获        }    }

 

 
  • Class<?> cl = getProxyClass0(loader, intfs);生成字节码类,

    并且加载到JVM中。

  private static Class<?> getProxyClass0(ClassLoader loader,                                           Class<?>... interfaces) {        //类实现的接口不能超过 65535 对于我们来说基本不会超过        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来创建一个代理对象                 */        return proxyClassCache.get(loader, interfaces);    }
  • proxyClassCache.get(loader, interfaces)

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        /**        * 这次我们只关注subKeyFactory.apply(key, parameter)、        * 了解他是如何进行 字节码重组的        */        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);                }            }        }    }
  • subKeyFactory.apply(key, parameter)

     

首先看到这个BiFunction 有多个实现,只需要关注ProxyClassFactory

 

  @Override        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);            //遍历 委托类实现的接口            for (Class<?> intf : interfaces) {                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");                }                /*                 * 判断是否是 接口,毕竟要面向接口编程嘛                 */                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");                    }                }            }            //public static final String PROXY_PACKAGE = "com.sun.proxy";            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();            //private static final String proxyClassNamePrefix = "$Proxy";            // public final long getAndIncrement() {            //      return unsafe.getAndAddLong(this, valueOffset, 1L);              //  }             String proxyName = proxyPkg + proxyClassNamePrefix + num;            //这就是 &Proxy0的由来,构成 com.sun.proxy.$proxy0 的拼接方式            /*             * Generate the specified proxy class.             */             // 在这里进行字节码的重组,生成字节数组             // 代理类的名字,接口,类的作用域 都有了            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(                proxyName, interfaces, accessFlags);            try {                //这里就将 字节码类,加载到JVM中,代理类就这样生成了,                //生成Proxy的Class对象                return defineClass0(loader, proxyName,                                    proxyClassFile, 0, proxyClassFile.length);            } catch (ClassFormatError e) {                throw new IllegalArgumentException(e.toString());            }        }    }
  • ProxyGenerator.generateProxyClass(proxyName, interfaces,
     accessFlags)。进行字节码重组,生成字节码文件。
// 传入类名,实现的所有接口,类的作用域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);                    }                }            });        }        return var4;    }
  • var3.generateClassFile();

 private byte[] generateClassFile() {        //每个类(都是Object的子类)中都会有 hashcode,equals,toString 方法        this.addProxyMethod(hashCodeMethod, Object.class);        this.addProxyMethod(equalsMethod, Object.class);        this.addProxyMethod(toStringMethod, Object.class);        Class[] var1 = this.interfaces;        //所有接口的长度        int var2 = var1.length;        int var3;        Class var4;        //遍历接口,将方法加载到代理类中&Proxy0中        for(var3 = 0; var3 < var2; ++var3) {            var4 = var1[var3];            //每个接口的所有方法            Method[] var5 = var4.getMethods();            int var6 = var5.length;            //遍历每个接口中的所有方法            for(int var7 = 0; var7 < var6; ++var7) {                Method var8 = var5[var7];                this.addProxyMethod(var8, var4);            }        }        Iterator var11 = this.proxyMethods.values().iterator();        List var12;        while(var11.hasNext()) {            var12 = (List)var11.next();            checkReturnTypes(var12);        }        Iterator var15;        try {            // 生成代理类的构造方法            this.methods.add(this.generateConstructor());            var11 = this.proxyMethods.values().iterator();            while(var11.hasNext()) {                var12 = (List)var11.next();                var15 = var12.iterator();                while(var15.hasNext()) {                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));                    this.methods.add(var16.generateMethod());                }            }            this.methods.add(this.generateStaticInitializer());        } catch (IOException var10) {            throw new InternalError("unexpected I/O Exception", var10);        }        //下面的一些就是一些 IO流的操作了。        if (this.methods.size() > 65535) {            throw new IllegalArgumentException("method limit exceeded");        } else if (this.fields.size() > 65535) {            throw new IllegalArgumentException("field limit exceeded");        } else {            this.cp.getClass(dotToSlash(this.className));            this.cp.getClass("java/lang/reflect/Proxy");            var1 = this.interfaces;            var2 = var1.length;            for(var3 = 0; var3 < var2; ++var3) {                var4 = var1[var3];                this.cp.getClass(dotToSlash(var4.getName()));            }            this.cp.setReadOnly();            //字节流            ByteArrayOutputStream var13 = new ByteArrayOutputStream();            //数据流            DataOutputStream var14 = new DataOutputStream(var13);            try {                var14.writeInt(-889275714);                var14.writeShort(0);                var14.writeShort(49);                this.cp.write(var14);                var14.writeShort(this.accessFlags);                var14.writeShort(this.cp.getClass(dotToSlash(this.className)));                var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));                var14.writeShort(this.interfaces.length);                Class[] var17 = this.interfaces;                int var18 = var17.length;                for(int var19 = 0; var19 < var18; ++var19) {                    Class var22 = var17[var19];                    var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));                }                var14.writeShort(this.fields.size());                var15 = this.fields.iterator();                while(var15.hasNext()) {                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();                    var20.write(var14);                }                var14.writeShort(this.methods.size());                var15 = this.methods.iterator();                while(var15.hasNext()) {                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();                    var21.write(var14);                }                var14.writeShort(0);                return var13.toByteArray();            } catch (IOException var9) {                throw new InternalError("unexpected I/O Exception", var9);            }        }    }

 

啊哈,到现在为止,代理对象的生成过程就已经结束,是否已经了解了这个整个流

程,或许有点抽象,那我们使用代码,生成一个代理类,看看它构建的字节码文

件到底什么样子。


 

 

生成字节码文件

 

那我们应该怎样生成字节码文件呢?其实,如果我们仔细的查看源码,我们就能够从中获取到如何获取字节码,哈哈~废话不多说,我们来直接上代码吧!

    public static void main(String[] args) throws Exception {        //被代理的类        ChinaStockMarkServiceImpl chinaStockMarkService = new ChinaStockMarkServiceImpl();        //看到这个是不是有点熟悉呢?哈哈,我截个图大家就明白了        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", chinaStockMarkService.getClass().getInterfaces());        FileOutputStream fos =new FileOutputStream(new File("/home/Project/$Proxy0.class"));        fos.write(bytes);        fos.flush();    }
 
这个方法是不是感觉很熟悉呢?嘻嘻
public static byte[] generateProxyClass(String var0,
                             Class<?>[] var1) {
    //这个方法就是默认调用了我们之前分析整个流程中的生成字节码的方法
    return generateProxyClass(var0, var1, 49);
}
//生成字节码文件的方法
public static byte[] generateProxyClass(final String var0,
                     Class<?>[] var1, int var2) {
    ProxyGenerator var3 = new ProxyGenerator(var0, var1, 
                                                var2);
    final byte[] var4 = var3.generateClassFile();

 

好了,通过这个方法生成的是字节码文件,也就是 &Proxy0.class文件,那我们如何反编译一下呢?这个时候我们就可以用到我们的Idea编译器了,它真的很强大哈,把生成的字节码文件拖到Idea中就可以看到了。

 

 

反编译字节码文件,并分析

将字节码文件&Proxy0.class文件拖到idea中代码如图所示:

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//import com.proxy.demo.proxy.StockMarkService;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 StockMarkService {    private static Method m1;    private static Method m2;    private static Method m0;    private static Method m3;    //构造方法,参数就只有一个InvocationHandler对象    public $Proxy0(InvocationHandler var1) throws  {        super(var1);    }    //加入到字节码文件中的Object中的方法    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 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 String makeMoney() throws  {        try {             //这里的 h是那个对象呢?其实就是我们写的 代理类(假的)            // StockMarkProxy            return (String)super.h.invoke(this, m3, (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");            m0 = Class.forName("java.lang.Object").getMethod("hashCode");            //获取代理类的方法            m3 = Class.forName("com.proxy.demo.proxy.StockMarkService").getMethod("makeMoney");        } catch (NoSuchMethodException var2) {            throw new NoSuchMethodError(var2.getMessage());        } catch (ClassNotFoundException var3) {            throw new NoClassDefFoundError(var3.getMessage());        }    }}

到这里就差不多是整个Jdk动态代理的整个流程了,最后我们在总结一下invoke方法是如何自动执行的吧。

 

 

今天的动态代理就到这里了。自己动手点一下源码实现过程,能够更好的理解整个过程。

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值