我需要了解的动态代理_动态代理的条件_有心好书的博客-程序员秘密

技术标签: java提高  

##一.说明
动态代理的思想是:代理模式+反射。
静态代理,代理者的代码是由程序员自己或者通过一些自动化工具生成,然后再进行编译。
动态代理则正好相反,通过反射机制动态的生成代理对象。

代理模式可参考下面的文章链接:
http://blog.csdn.net/a910626/article/details/50760980

反射可参考下面的文章链接:
http://blog.csdn.net/a910626/article/details/51111918

动态代理涉及的相关类:
动态代理主要的Java 类有java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler,下面是动态代理的UML类图。
在这里插入图片描述

java.lang.reflect.Proxy它提供了一组静态方法来为一组接口生成代理类和对象。

// 根据一组接口和类加载器来生成代理类的Class对象
public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)
// 用来获取该代理类对象所关联的处理器
public static InvocationHandler getInvocationHandler(Object proxy) 
// 用来判断是否是代理类
public static boolean isProxyClass(Class<?> cl)
//生成代理对象
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

##二.源码分析
Java的动态代理通过调用Proxy.newProxyInstance()方法生成代理对象。这个方法通过传入的三个参数类加载器、接口数组、调用处理器来生成动态代理对象。

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }

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

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            return newInstance(cons, h);
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

1.getProxyClass0(loader, interfaces);
这个方法有些长,抽取关键地方分析

1)先抽取被代理类实现的接口Interface,检测是否符合要求

/* collect interface names to use as key for proxy class cache */
        String[] interfaceNames = new String[interfaces.length];

        // for detecting duplicates
        Set<Class<?>> interfaceSet = new HashSet<>();

        for (int i = 0; i < interfaces.length; i++) {
            /*
             * Verify that the class loader resolves the name of this
             * interface to the same Class object.
             */
            String interfaceName = interfaces[i].getName();
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(interfaceName, false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != interfaces[i]) {
                throw new IllegalArgumentException(
                    interfaces[i] + " 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.contains(interfaceClass)) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
            interfaceSet.add(interfaceClass);

            interfaceNames[i] = interfaceName;
        }

2)查看代理对象缓存中是否有我们要创建的代理类,如果有,直接获取;没有则创建

如果有则: return proxyClassCache.get(loader, interfaces);
没有则通过ProxyClassFactory创建。
这个类比较核心的方法如下:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);

我猜大致意思就是全转为字节码,运行时(runtime)生成代理对象字节码。

2.cl.getConstructor(constructorParams);

根据上一步生成的代理类Class对象,来获取到构造器。

3.newInstance(cons, h);
创建代理实例,将
newInstance(cons, ih);//将InvocationHandler h传入代理对象中

说明:
怎么看生成的代理对象的源码是什么样的呢?
可参考链接:
查看JDK动态代理生成的类的内容
http://blog.csdn.net/zknxx/article/details/77919332

以下是Proxy0的源码

public final class $Proxy0 extends Proxy implements Manager {  
  
private static Method m1;  
private static Method m0;  
private static Method m3;  
private static Method m2;  
  
static {  
   try {  
    m1 = Class.forName("java.lang.Object").getMethod("equals",  
      new Class[] { Class.forName("java.lang.Object") });  
    m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
      new Class[0]);  
    m3 = Class.forName("com.ml.test.Manager").getMethod("modify",  
      new Class[0]);  
    m2 = Class.forName("java.lang.Object").getMethod("toString",  
      new Class[0]);  
   } catch (NoSuchMethodException nosuchmethodexception) {  
    throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
   } catch (ClassNotFoundException classnotfoundexception) {  
    throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
   }  
}  
  
public $Proxy0(InvocationHandler invocationhandler) {  
   super(invocationhandler);  
}  
  
@Override  
public final boolean equals(Object obj) {  
   try {  
    return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))  
      .booleanValue();  
   } catch (Throwable throwable) {  
    throw new UndeclaredThrowableException(throwable);  
   }  
}  
  
@Override  
public final int hashCode() {  
   try {  
    return ((Integer) super.h.invoke(this, m0, null)).intValue();  
   } catch (Throwable throwable) {  
    throw new UndeclaredThrowableException(throwable);  
   }  
}  
  
public final void modify() {  
   try {  
    super.h.invoke(this, m3, null);  
    return;  
   } catch (Error e) {  
   } catch (Throwable throwable) {  
    throw new UndeclaredThrowableException(throwable);  
   }  
}  
  
@Override  
public final String toString() {  
   try {  
    return (String) super.h.invoke(this, m2, null);  
   } catch (Throwable throwable) {  
    throw new UndeclaredThrowableException(throwable);  
   }  
}  
}  

即生成的代理类的每个方法中都会调用InvocationHandler的invoke方法,并且会把方法名、代理对象、参数回调回去,我们可以在InvocationHandler的invoke方法中增加一些代码等效于在方法前后插入代码。

##三.一个栗子
###1.简单代理模式写法

public interface Subject {
    void request();
}

public class RealSubject implements Subject {

    @Override
    public void request() {
        System.out.println("---realsubject---");
    }
}

public class ProxySubject implements Subject {
    private RealSubject mRealSubject;

    public ProxySubject(RealSubject realSubject) {
        mRealSubject = realSubject;
    }

    @Override
    public void request() {
        mRealSubject.request();
    }
}
public class Client {
    public static void main(String[] args) {
        ProxySubject proxySubject = new ProxySubject(new RealSubject());
        proxySubject.request();
    }
}

###2.动态代理模式写法

public interface Subject {
    void request();
}
public class ProxyHandler implements InvocationHandler {
    private Subject mSubject;

    public ProxyHandler(Subject subject) {
        mSubject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.print("---before---");
        Object result = method.invoke(mSubject, args);
        System.out.print("---end---");
        return result;
    }
}
public class RealSubject implements Subject {

    @Override
    public void request() {
        System.out.println("---realsubject---");
    }
}

public class Client {
    public static void main(String[] args) {
//        ProxySubject proxySubject = new ProxySubject(new RealSubject());
//        proxySubject.request();
        RealSubject realSubject = new RealSubject();
        ProxyHandler handler = new ProxyHandler(realSubject);
        Subject proxyInstance = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(), handler);
        proxyInstance.request();
    }
}

不过动态代理可以不用具体的实现类,直接代理接口类。

public class Client {
    public static void main(String[] args) {
//        ProxySubject proxySubject = new ProxySubject(new RealSubject());
//        proxySubject.request();
//        RealSubject realSubject = new RealSubject();
//        ProxyHandler handler = new ProxyHandler(realSubject);
//        Subject proxyInstance = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(), handler);
//        proxyInstance.request();

        Subject subject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
                new Class<?>[] { Subject.class },
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("dynamic Proxy");
                        return null;
                    }
                }
        );
        subject.request();

    }
}

这个动态代理的好处是你连RealSubject都不需要。

##参考资料
Java 动态代理机制分析及扩展,第 1 部分
https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html

动态代理解析
http://blog.csdn.net/kyosky110/article/details/51911463

java 动态代理深度学习(Proxy,InvocationHandler),含$Proxy0源码
http://lijingshou.iteye.com/blog/1949134

戏说代理模式和Java动态代理
https://www.jianshu.com/p/0d919e54eef0

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/a910626/article/details/79366397

智能推荐

常见存储器ROM、RAM和FLASH介绍_a2447944219的博客-程序员秘密

常见存储器ROM、RAM和FLASH介绍最近因为在找实习工作,做了一些大公司的硬件笔试题,发现很多公司都有对存储器的考察,从来没有系统的整理过存储器的种类,是时候来一波整理了 以下主要讲了:RAM、ROM和FLASH三大类。RAM包括:SRAM、DRAM、SDRAM、DDR SDRAM、DDR2 SDRAM和DDR3 SDRAMROM包括:PROM、EPROM和EEPROM

Java 泛型 Java generic_annssdy的博客-程序员秘密

泛型(generic)是指参数化类型的能力。可以定义带泛型类型的类或方法,随后编译器会用具体的类型来代替它。例如,可以定义一个泛型栈类,它存储的是泛型元素。可以从这个泛型类生成一个包含字符串的栈对象和一个包含数字的桟对象。这里字符串和数字都是替代泛型的具体类型。  使用泛型的主要优点是能够在编译时而不是运行时检测出错误。泛型类或方法允许用户指定可以和这些类或方法一起工作的对象类型。如果试图使用

一种好玩的自动挂载技术------Autofs_动态挂载技术是什么_Zhang To_Dream的博客-程序员秘密

1.挂载是什么,都有哪些挂载方式?挂载(mount)是指由操作系统使一个存储设备(诸如硬盘、CD-ROM或共享资源)上的计算机文件和目录可供用户通过计算机的文件系统访问的一个过程。一般来说,当计算机关机时,每个已挂载存储都将经历一次卸载,以确保所有排队的数据被写入,并保证介质上文件系统结构的完整性。挂载的方式有三种:开机挂载------通过编辑/etc/fstab配置文件实现;Moun...

UNIX基础--安装应用程序: Packages 和 Ports_unix安装包_孤逐王的博客-程序员秘密

Packages and Ports概述FreeBSD 将许多系统工具捆绑作为基本系统的一部分。另外,FreeBSD 提供了两种补充的技术来安装第三方软件:FreeBSD Ports Collection,从源代码安装; packages,从预编译的二进制版本安装。这两种方法都可以用于从本地介质, 或从网上直接安装您喜欢的应用程序的最新版本。UNIX系统典型的安装第三方软件的步骤包括:1、下载这个

设计模式之简单工厂模式_weixin_34072159的博客-程序员秘密

设计模式是C#程序员从拖控件成长为技术大牛的必经之路,经过《Head First》洗礼后,作为一个随笔留在博客园吧。OK,让我们进行第一任务:请朋友喝茶或者喝咖啡。整理下我们的思路:喝茶或者喝咖啡,都要煮开水、洗杯子、冲泡茶(咖啡)。煮开水和洗杯子代码可以复用,考虑这个原则(实现代码的复用选择抽象类,实现多态性选择接口),我们选择创建一个抽象类,用来实现煮开水和洗杯子功能。代码如下:...

Spring MVC 拦截器_a673341766的博客-程序员秘密

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:co

随便推点

基于TI-RTOS的CC2650DK开发(11)---信号量_abatei的博客-程序员秘密

第四章 同步模块4.1 信号量(Semaphores)SYS/BIOS在semaphores的基础上提供了一系列用于任务间同步和通信的函数。Semaphore通常用于协调访问一系列竞争任务间的共享资源。Semaphore模块提供的函数通过Semaphore_Handle类型句柄来操作semaphore对象的访问。参阅video introducing Semaphore

Raspberry PI swapfile_cs9dn003的博客-程序员秘密

看一下/etc/dphys-swapfile cat /etc/dphys-swapfile默认的大小是100MCONF_SWAPFILE=100修改之 CONF_SWAPSIZE=500或者更大重启Raspberry Picomefrom:https://www.phodal.com/blog/raspberry-pi-add-the-swap-file-size/...

决策树之概念及思想_abq43525的博客-程序员秘密

分类树分类决策树的核心思想就是在一个数据集中找到一个最优特征,然后从这个特征的选值中找一个最优候选值(这段话稍后解释),根据这个最优候选值将数据集分为两个子数据集,然后递归上述操作,直到满足指定条件为止。1.最优特征怎么找?这个问题其实就是决策树的一个核心问题了。我们常用的方法是更具信息增益或者信息增益率来寻找最优特征,信息增益这东西怎么理解呢!搞清这个概念我们首先需要明...

Java基础方法12(枚举)_页川叶川的博客-程序员秘密

##17.1.枚举类型interface Constants{ //使用枚举类型设置常亮 public static final int Constants_A=1; public static final int Constants_B=12;}public class ConstantsTest{ enum Constants2{...

[分享]iOS开发-AutoLayout控件等比间距的百分比布局_ios 调整百分比控件_斗笔程序猿的博客-程序员秘密

参考链接:https://segmentfault.com/a/1190000005272005 把这些按钮都设置为与SuperView居中对齐,然后调整约束Multiplier的值。这个值是一个百分比,最小0,最大2???