动态代理 Proxy InvocationHandler
前奏
代理模式
- 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。 代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种。
- 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
- 动态代理:在程序运行时,运用反射机制动态创建而成。
使用Java动态代理机制的好处:
- 减少编程的工作量:假如需要实现多种代理处理逻辑,只要写多个代理处理器就可以了,无需每种方式都写一个代理类。
- 系统扩展性和维护性增强,程序修改起来也方便多了(一般只要改代理处理器类就行了)。
使用Java动态代理机制的限制:
- 目前根据GOF的代理模式,代理类和委托类需要都实现同一个接口。也就是说只有实现了某个接口的类可以使用Java动态代理机制。但是,事实上使用中并不是遇到的所有类都会给你实现一个接口。因此,对于没有实现接口的类,目前无法使用该机制。
- 有人说这不是废话吗,本来Proxy模式定义的就是委托类要实现接口的啊!但是没有实现接口的类,该如何实现动态代理呢?
- 当然不是没有办法,那就是大名鼎鼎的CGLib(Code Generation Library)...
基本过程
- 首先创建委托类对象,将其以构造函数传入代理处理器,代理处理器ProxyHandler中会以Java反射方式调用该委托类对应的方法。
- 然后使用Java反射机制中的Proxy.newProxyInstance方式创建一个代理类实例,创建该实例需要指定该实例的类加载器,需要实现的接口(即目标接口),以及处理代理实例接口调用的处理器。
- 最后,调用代理类目标接口方法时,会自动将其转发到代理处理器中的invoke方法内,invoke方法内部实现预处理,对委托类方法调用,事后处理等逻辑。
相关类介绍
在java的动态代理机制中,有两个重要的类或接口:InvocationHandler、Proxy,这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:一、InvocationHandlerInvocationHandler is the interface implemented by the invocation handler of a proxy instance.Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
- proxy:指代我们所代理的那个真实对象
- method:指代的是我们所要调用真实对象的某个方法的Method对象
- args:指代的是调用真实对象某个方法时接受的参数
二、Proxy:Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.这个方法的作用就是得到一个动态的代理对象
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
- loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
- interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
- h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
示例
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class Test {public static void main(String[] args) throws Exception {//我们要代理的真实对象Subject realSubject = new RealSubject();//我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的InvocationHandler handler = new DynamicProxy(realSubject);//通过Proxy的newProxyInstance方法来创建我们的代理对象Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),//定义了由哪个ClassLoader对象来对生成的代理对象进行加载realSubject.getClass().getInterfaces(), //表示的是我将要给我需要代理的对象提供一组什么接口,之后我这个代理对象就会实现了这组接口//因为我这个代理对象实现了这组接口,所以可以将这个代理对象强转为Subject类型handler);//表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上System.out.println("代理对象的类名:" + subject.getClass().getName());//【$Proxy0】,最后一个数字表示对象的标号subject.rent();//通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行System.out.println();subject.hello("包青天");}}//1、首先我们定义了一个Subject类型的接口interface Subject {public void rent();public void hello(String str);}//2、接着,定义了一个类来实现这个接口,这个类就是我们的真实对象class RealSubject implements Subject {@Overridepublic void rent() {System.out.println("出租房屋");}@Overridepublic void hello(String str) {System.out.println("hello:" + str);}}//3、然后,定义一个动态代理类,每一个动态代理类都必须要实现 InvocationHandler 这个接口class DynamicProxy implements InvocationHandler {private Object subject;//这个就是我们要代理的真实对象public DynamicProxy(Object subject) {this.subject = subject;}@Overridepublic Object invoke(Object object, Method method, Object[] args) throws Throwable {//在代理真实对象前我们可以添加一些自己的操作System.out.println("调用的方法: " + method);//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用method.invoke(subject, args);//在代理真实对象后我们也可以添加一些自己的操作System.out.println("调用完毕");return null;}}为什么我们可以将newProxyInstance方法返回的Object类型的对象转化为Subject类型的对象?原因就是,在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这个接口的对象,因为这里的接口是Subject类型,所以就可以将其转化为Subject类型了。同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行时动态生成的一个对象,并且命名方式都是这样的形式,以$开头,Proxy为中,最后一个数字表示对象的标号。我们来看看这两句subject.rent();subject.hello("world");这里是通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行,而我们的这个 handler 对象又接受了一个 RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用 handler 中的invoke方法去执行。
更简洁的示例
public class Test {public static void main(String[] args) {ITest iTest = (ITest) Proxy.newProxyInstance(ITest.class.getClassLoader(), new Class<?>[] { ITest.class }, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Integer a = (Integer) args[0];Integer b = (Integer) args[1];System.out.println("方法名:" + method.getName());System.out.println("参数:" + a + " , " + b);return null;}});iTest.add(3, 5);}}interface ITest {public void add(int a, int b);}
动态代理 Proxy InvocationHandler的更多相关文章
- JDK动态代理 Proxy InvocationHandler
- java 动态代理范例 InvocationHandler与Proxy
java 动态代理范例 InvocationHandler与Proxy,拦截与代理 java.lang.reflect.Proxy,Proxy 提供用于创建动态代理类和实例的静态方法.newProxy ...
- 浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance
浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口.目标接口的类加载器以及Inv ...
- Java动态代理之InvocationHandler最简单的入门教程
网上关于Java的动态代理,Proxy和InvocationHandler这些概念有讲解得非常高深的文章.其实这些概念没有那么复杂.现在咱们通过一个最简单的例子认识什么是InvocationHandl ...
- JAVA设计模式-动态代理(Proxy)示例及说明
在Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析文章的最后部分,我们提到了动态代理的概念,下面我们就简单了解一下动态代理. 一,概念 代理设计模式的目的就是在不直接操作对象的前 ...
- JAVA设计模式-动态代理(Proxy)源码分析
在文章:JAVA设计模式-动态代理(Proxy)示例及说明中,为动态代理设计模式举了一个小小的例子,那么这篇文章就来分析一下源码的实现. 一,Proxy.newProxyInstance方法 @Cal ...
- 动态代理proxy与CGLib的区别
什么是代理? 静态代理与动态代理 静态代理实例 JDK动态代理实例 CGLib 简介 CGLib 与JDK动态代理的区别 代理模式是Java中常见的一种模式,英文名字叫走Proxy或者Surrogat ...
- java动态代理Proxy
package com.gz_06; public interface StudentDao { public void login(); public void regist(); } packag ...
- JDK动态代理(Proxy)的两种实现方式
JDK自带的Proxy动态代理两种实现方式 前提条件:JDK Proxy必须实现对象接口 so,创建一个接口文件,一个实现接口对象,一个动态代理文件 接口文件:TargetInterface.java ...
随机推荐
- 解读为什么有符号的char可表示范围是-128~+127
问:为什么有符号的char可表示范围是-128~+127? 要明白这个问题,首先要明白一下几点: 对于char和int计算机中以补码形式存在. 严格来说计算机就是傻逼,它只知道某个位上是0还是1. 我 ...
- bzoj2687: 交与并
Description 对于一个区间集合{A1,A2……AK}(K>1,Ai<>Aj{i<>j}),我们定义其权值 W=|A1∪A2∪……∪A ...
- react-redux原理
react-redux原理分析 写在前面 之前写了一篇分析Redux中Store实现的文章(详见:Redux原理(一):Store实现分析),突然意识到,其实React与Redux并没有什么直接的联系 ...
- ubuntu下使用C语言开发一个cgi程序
主要步骤是: 1. 开发一个C程序(在标准输出中输出HTML字符串) 2. 复制到apache2的cgi-bin目录去 3. 在httpd.conf中开启cgi功能(我似乎没用到,也可以使用cgi) ...
- 会声会影X6-高级运动等效果的练习实践-与您分享...
视频片说明:我在学习X6的视频教程后,做了针对性练习与实 践,我所用的素材取于网络世界-百度下载,视频中的效果有,高级运动;平移缩放,分屏效果,<运用:关键帧,缩放,旋转,加相框,倒 ...
- 【Java】WSDL 简介
WSDL(网络服务描述语言,Web Services Description Language)是一门基于 XML 的语言,用于描述 Web Services 以及如何对它们进行访问. 什么是 WSD ...
- linux内核空间与用户空间信息交互方法
linux内核空间与用户空间信息交互方法 本文作者: 康华:计算机硕士,主要从事Linux操作系统内核.Linux技术标准.计算机安全.软件测试等领域的研究与开发工作,现就职于信息产业部软件与 ...
- AC+DP练习
1.HDU 2222 Keywords Search 求目标串中出现了几个模式串. #include<iostream> #include<cstdio> #include&l ...
- 图论:(Code Forces) Graph and String
Graph and String time limit per test 2 seconds memory limit per test 256 megabytes input standard in ...
- selenuim ide回放时出现的问题
[error] Unexpected Exception: fileName -> chrome://selenium-ide/content/selenium-core/scripts/htm ...