jdk动态代理

  简单的说,代理模式是在目标对象和访问对象之间增加了一层代理对象,所有的访问对象通过代理对象来实现对目标对象的调用。

        代理对象和目标对象实现同一个接口,由代理对象来调用目标对象,对于外部来说,代理对象就可以替代目标对象被调用。通过这种方式,代理对象中可以在正常的访问中增加额外的逻辑,比如缓存、权限控制、日志记录等。

        但是这种静态代理的模式需要增加额外的代理类的实现,Java 5开始引入了动态代理机制,实现了在运行时动态地创建出代理对象,这其实是一种方法调用的拦截,AOP就是利用了这种模式。

 

动态代理的使用例子 

1
<span style="font-size:16px;">package com.dynamic.jdk;<br><br>/**<br> * 目标类的接口定义<br> * <p/><br> * Created by Vincent Tse on 12/2/15.<br> */<br>public interface MyInterface {<br>    void doSomething();<br>}<br></span>
1
<span style="font-size:16px;">package com.dynamic.jdk;<br><br>/**<br> * 目标类的具体实现<br> * <p/><br> * Created by Vincent Tse on 12/2/15.<br> */<br>public class MyInterfaceImpl implements MyInterface {<br><br>    @Override<br>    public void doSomething() {<br>        System.out.println("here is my real operation!");<br>    }<br>}<br></span>
1
<span style="font-size:16px;">package com.dynamic.jdk;<br><br>import java.lang.reflect.InvocationHandler;<br>import java.lang.reflect.Method;<br><br>/**<br> * 自定义的InvocationHandler<br> * 封装具体的调用过程<br> * <p/><br> * Created by Vincent Tse on 12/2/15.<br> */<br>public class MyInvocationHandler implements InvocationHandler {<br>    //target是真正执行方法的目标对象<br>    private Object target;<br><br>    public MyInvocationHandler(Object target) {<br>        super();<br>        this.target = target;<br>    }<br><br>    /**<br>     * 代理对象调用的方法<br>     * @param proxy<br>     * @param method<br>     * @param args<br>     * @return<br>     * @throws Throwable<br>     */<br>    @Override<br>    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {<br>        System.out.println("before target's operation!");<br>        Object result = method.invoke(target, args);<br>        System.out.println("after target's operation");<br>        return result;<br>    }<br>}<br></span>
1
<span style="font-size:16px;">package com.dynamic.jdk;<br><br>import java.lang.reflect.Proxy;<br><br>/**<br> * 测试类<br> * <p/><br> * Created by Vincent on 12/2/15.<br> */<br>public class DynamicTest {<br><br>    public static void main(String[] args) {<br><br>        //生成目标对象<br>        MyInterface myInterface = new MyInterfaceImpl();<br><br>        //实例化invocationHandler对象,传入目标对象作为target<br>        MyInvocationHandler invocationHandler = new MyInvocationHandler(myInterface);<br><br>        //调用Proxy的方法生成代理对象<br>        MyInterface proxy = (MyInterface)Proxy.newProxyInstance(myInterface.getClass().getClassLoader(),<br>                new Class[]{MyInterface.class}, invocationHandler);<br><br>        //调用代理对象的方法<br>        proxy.doSomething();<br>    }<br>}<br></span>

输出结果:

before target's operation!

here is my real operation!

after target's operation! 

 

使用起来很简单,接下来看源码分析其中的原理

从Proxy的newProxyInstance()方法开始,这个方法就是用来生成代理对象的,需要传入类加载器、实现的接口以及一个InvocationHandler对象。 

1
<span style="font-size:16px;">public static Object newProxyInstance(ClassLoader loader,<br>                                      Class<?>[] interfaces,<br>                                      InvocationHandler h)<br>    throws IllegalArgumentException<br>{<br>    Objects.requireNonNull(h);<br><br>    final Class<?>[] intfs = interfaces.clone();<br>    final SecurityManager sm = System.getSecurityManager();<br>    if (sm != null) {<br>        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);<br>    }<br><br>    //通过传入的类加载器和实现接口生成代理类<br>    Class<?> cl = getProxyClass0(loader, intfs);<br><br>    /*<br>     * Invoke its constructor with the designated invocation handler.<br>     */<br>    try {<br>        if (sm != null) {<br>            checkNewProxyPermission(Reflection.getCallerClass(), cl);<br>        }<br>                  //反射获取代理类的构造函数<br>        final Constructor<?> cons = cl.getConstructor(constructorParams);<br>        final InvocationHandler ih = h;<br>        if (!Modifier.isPublic(cl.getModifiers())) {<br>            AccessController.doPrivileged(new PrivilegedAction<Void>() {<br>                public Void run() {<br>                    cons.setAccessible(true);<br>                    return null;<br>                }<br>            });<br>        }<br>        //反射生成实例,将invocationHandler传入,之后调用invoke方法就靠它了<br>        return cons.newInstance(new Object[]{h});<br>    } catch (IllegalAccessException|InstantiationException e) {<br>        throw new InternalError(e.toString(), e);<br>    } catch (InvocationTargetException e) {<br>        Throwable t = e.getCause();<br>        if (t instanceof RuntimeException) {<br>            throw (RuntimeException) t;<br>        } else {<br>            throw new InternalError(t.toString(), t);<br>        }<br>    } catch (NoSuchMethodException e) {<br>        throw new InternalError(e.toString(), e);<br>    }<br>} <br></span>

 

 

getProxyClass0()方法就是用来生成代理类的,首先检查实现接口数量,大于65535就抛异常,记得以前java培训的时候老师讲过这块,感觉应该不会有实现那么多接口的类吧。

1
<span style="font-size:16px;">private static Class<?> getProxyClass0(ClassLoader loader,<br>                                       Class<?>... interfaces) {<br>    if (interfaces.length > 65535) {<br>        throw new IllegalArgumentException("interface limit exceeded");<br>    }<br><br>       //从一个Cache中获取代理类,如果没有就重新生成<br>    return proxyClassCache.get(loader, interfaces);<br>}<br></span>

 

proxyClassCache是一个静态的WeakCache,定义在Proxy类里。

WeakCache里有两个factory,一个是subKeyFactory, 是一个映射函数(key, parameter)->sub-key, 另一个是valueFactory,是一个映射函数(key, parameter)->value。WeakCache的get方法需要两个参数,一个是key,另一个是parameter。

具体WeakCache的用法与原理这里不再赘述,请参考源码。

1
<span style="font-size:16px;">private static final WeakCache<ClassLoader, Class<?>[], Class<?>><br>    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());<br></span>

这里传入的KeyFactory和ProxyClassFactory是Proxy类中定义的静态类,分别对应了WeakCache中的subKeyFactory和valueFactory,都实现了BiFunction接口,并实现了apply()方法。ProxyClassFactory的apply()方法里完成的就是生成代理类的逻辑,最关键的是

1
<span style="font-size:16px;">byte[] proxyClassFile = ProxyGenerator.generateProxyClass(<br>    proxyName, interfaces, accessFlags);<br></span>

上面的方法用来生成代理类的字节码,这个代理类里会有接口的实现方法,在实现的方法中会调用InvocationHandler的invoke()方法,有兴趣的可以将生成的字节码写到本地,用反编译工具打开看一下。

 

那么ProxyClassFactory的apply()方法是在什么时候被调用的呢,回到WeakCache的get()方法

1
<span style="font-size:16px;">//生成subkey来获取对应的supplier<br>Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));<br>Supplier<V> supplier = valuesMap.get(subKey);<br>Factory factory = null;<br>//一直循环,直到从supplier中获取到value为止<br>//如果没有supplier就生成factory并加入到缓存中(源码说这是一个install的过程)<br>while (true) {<br>    if (supplier != null) {<br>        V value = supplier.get();<br>        if (value != null) {<br>            return value;<br>        }<br>    }<br>    <br>    if (factory == null) {<br>        //Factory是内部类,实现了Supplier接口,实现了get()方法<br>        //实际上在get()方法中调用了valueFactory(也就是ProxyClassFactory)的apply()方法<br>        //可以debug进去看<br>        factory = new Factory(key, parameter, subKey, valuesMap);<br>    }<br><br>    if (supplier == null) {<br>        supplier = valuesMap.putIfAbsent(subKey, factory);<br>        if (supplier == null) {<br>            supplier = factory;<br>        }<br>    } else {<br>        if (valuesMap.replace(subKey, supplier, factory)) {<br>            supplier = factory;<br>        } else {<br>            supplier = valuesMap.get(subKey);<br>        }<br>    }<br>}<br></span>

好辛苦,终于生成代理类了,回到newProxyInstance方法,有了代理类的Class对象,就可以通过反射生成实例(调用的是带InvocationHandler参数的构造函数),生成实例之后就可以用代理对象了。

转载于:https://www.cnblogs.com/jinshiyill/p/5032139.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/256294.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【图像处理】——图像的灰度化处理(Python实现三种方法——最大值法、平均值法、加权均值法、gamma校正)

目录 一、什么是图像的灰度化? 二、灰度化的几种方法(最大值法、平均值法、加权均值法、gamma校正)

配置https

引子&#xff1a; 最近在一篇文章中了解到EFF(电子前哨基金会)为了推广https协议&#xff0c;成立了一个letsencrypt项目&#xff0c;可以发放免费的证书&#xff0c;此证书可以被大多数主流浏览器所信任&#xff0c;这个邪恶的念头一爆发&#xff0c;就让我走上了一条坎坷的不…

C# —— 序列化与反序列化

概念 序列化 通过使用不同的类(BinaryFormatter,SoapFormatter,XmlSerializer)将对象状态转换为可保持或传输的格式的过程,具体是将对象转变为字节流,其目的是为了保存数据的状态,方便后续还原调用。包括三种序列化形式:二进制序列化,SOAP序列化,XML序列化。于此过…

CentOS 6.5安装VNC server

1. 安装桌面&#xff0c;安装时选择了Desktop可以忽略 # yum groupinstall Desktop # yum install gnome-core xfce4 firefox 2. 安装VNC server # yum install tigervnc-server 3. 配置服务 # chkconfig vncserver on 4. 设置VNC用户密码 # vncpasswd 5. 配置文件 # vi /etc/s…

【图像处理】——图像灰度直方图的绘制(直接调用函数和自定义函数)

目录 一、灰度直方图概念 二、直接调用opencv的函数caclHist() 1、函数介绍 2、实例 <

Codeforces 722C. Destroying Array

C. Destroying Arraytime limit per test1 secondmemory limit per test256 megabytesinputstandard inputoutputstandard outputYou are given an array consisting of n non-negative integers a1, a2, ..., an. You are going to destroy integers in the array one by o…

C#中数据类型及其转换知识点汇总

概念 C#中数据类型分为两大类&#xff0c;分别是值类型和引用类型。 值类型变量是从类 System.ValueType 中派生出来的&#xff0c;当声明一个值类型变量时&#xff0c;系统分配内存来存储值。 整形 包括8种类型&#xff0c;区别在于字节数和有无符号。 浮点型 float占用…

10亿个字符串的排序问题

一、问题描述 有一个大文件&#xff0c;里面有十亿个字符串&#xff0c;乱序的&#xff0c;要求将这些字符串以字典的顺序排好序 二、解决思路 将大文件切割成小文件&#xff0c;每个小文件内归并排序&#xff1b; 对所有的小文件进行归并排序——多重归并排序 三、解决方案 3.…

MVC学习IIS的不同版本(一)

一&#xff1a;IIS5.0运行在进程InetInfo.exe中&#xff0c;该进程寄宿着一个名为World Wide Publishing Service&#xff08;W3VC&#xff09;的window服务。 W3VC的主要功能&#xff1a;包括HTTP请求的监听、工作进程和配置管理 检测到HTTP 请求时&#xff1a; 根据扩展名判断…

Halcon中visualize_object_model_3d算子详解

概念 此函数用于显示3d模型。该函数功能很多,包括设置位姿,颜色,鼠标翻转、缩放、平移,选择和取消选择目标,降低鼠标灵敏度,切换检查模式等。 参数 visualize_object_model_3d( : : WindowHandle, ObjectModel3D, CamParam, PoseIn, GenParamName, GenParamValue, Tit…

random()模块随机函数的用法总结

random()是Python中生成随机数的函数&#xff0c;是由random模块控制&#xff0c;random()函数不能直接访问&#xff0c;需要导入random 模块&#xff0c;然后再通过相应的静态对象调用该方法才能实现相应的功能 目录 1. random.random() 2. random.uniform() 3. random.ra…

ansible命令应用示例

ansible命令应用示例 ping slave组ansible slave -m ping 用bruce 用户以root 身份pingansible slave -m ping -u bruce --sudo 用bruce 用户sudo 到batman 用户pingansible slave -m ping -u bruce --sudo --sudo-user batman 给slave组安装ftpan…

史上超全halcon常见3D算子汇总(一)

读取3D模型 read_object_model_3d 此算子用于读取3D对象。 read_object_model_3d( : : FileName, Scale, GenParamName, GenParamValue : ObjectModel3D, Status) FileName:文件名,halcon支持多种3d数据格式的读取,包括 .off, .ply, .dxf, .om3, .obj, .stl等格式。 1).…

Python:常用模块简介(1)

sys模块 >>> sys.platform #返回操作系统平台名称 win32 >>> sys.stdin #输入相关 <open file <stdin>, mode r at 0x000000000337B030> >>> sys.stdout #输出相关 <open file <stdout>, mode w at 0x000000000337…

【图像处理】——Python实现图像加噪(随机噪声、椒盐噪声、高斯噪声等)

目录 1、随机噪声 2、椒盐噪声 3、高斯噪声 补充:numpy.clip函数 4、其他噪声 1、随机噪声 随机噪声就是通过随机函数在图像上随机地

100NED

将生活融入编程转载于:https://www.cnblogs.com/nedhome/p/5036680.html

Windows10 VS2019下使用CMake3.20.1打开PCL1.11.0程序

安装CMake 为什么要使用cmake cmake 是kitware 公司以及一些开源开发者在开发几个工具套件(VTK)的过程中衍生品&#xff0c;成为一个独立的开放源代码项目。 CMake是一个很强大的编译配置工具&#xff0c;支持多种平台和编译器&#xff0c;通过编写CMakeLists.txt&#xff0c…

Java 并发---ConcurrentHashMap

concurrent包下的并发容器 JDK5中添加了新的concurrent包&#xff0c;相对同步容器而言&#xff0c;并发容器通过一些机制改进了并发性能。因为同步容器将所有对容器状态的访问都串行化了&#xff0c;这样保证了线程的安全性&#xff0c;所以这种方法的代价就是严重降低了并发性…

【图像处理】——图像滤波(Python+opencv实现三种方法:均值滤波、中值滤波、高斯滤波等)

目录 一、什么是滤波以及滤波的目的? 二、均值滤波(cv2.blur()) 1、原理 2、关键代码