代理模式及jdk动态代理原理
代理模式 :为其它对象提供代理,以控制对这个对象的访问。
代理模式的特征:代理类(proxyClass)与委托类(realClass)有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类(调用realClass的方法,实现代理的功能),以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
作用:主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等
动态代理结论:Proxy.newProxyInstance根据classloader和接口数组,生成一个$Proxy0代理类,并将InvocationHandler的实现类做为构造函数参数传递给$Proxy0代理类,代理类$Proxy0调用接口的方法,会在方法中调用InvocationHandler的实现类中的invoke()方法。invoke()方法中,通过反射调用委托类(realClass)的实际方法,完成了整个代理操作。
1.为了更好的理解代理模式,先理解静态代理
模拟支付宝支付,大家在淘宝买过东西都知道,买家付款后,钱放在支付宝里,不会立刻给商家,待用户确认收货后,才会支付给商家,这里支付宝就相当于代理用户付款给商家。
支付接口
package com.proxy; public interface Pay {
public boolean pay();
}
委托类实现了支付接口
package com.proxy; public class Customer implements Pay{
@Override
public boolean pay() {
System.out.println("网上购物使用支付宝结账付款");
return true;
}
}
代理类也要实现pay接口,包含委托类对象(关联关系)
package com.proxy; public class AliPay implements Pay{
private Customer customer = null;
public AliPay(Customer customer){
this.customer = customer;
} @Override
public boolean pay() {
if(customer.pay()){
System.out.println("用户收到货物,支付包支付给商家完成");
}
return customer.pay();
}
}
测试
package com.proxy; public class TestProxy {
public static void main(String[] args) {
Customer customer = new Customer();
AliPay aliPay = new AliPay(customer);
if(aliPay.pay()){
System.out.println("整个购物流程完成!");
}else{
System.out.println("付款失败!");
}
}
}
结果:
网上购物使用支付宝结账付款
用户收到货物,支付包支付给商家完成
整个购物流程完成!
使用静态代理发现,代理类只能为一个接口服务,这样会产生许多代理类。这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。
动态代理:
代理类
package com.proxy; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class ProxyPay implements InvocationHandler {
private Object target; /**
* @param proxy 代理的实例proxy instance
* @param method 代理的实例proxy instance调用接口的方法
* @param args 调用实际类方法的参数数组,没有参数为null,基本类型会转成封装类
* @return
* @date 2016-4-7
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object pay = method.invoke(target, args);
if((Boolean) pay){
System.out.println("用户收到货物,支付包支付给商家完成");
}
return pay;
} /**
* loader : 类加载器
* interfaces : 代理类实现的接口
* h :调用方法
*
* public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
*/
public Object bind(Object target) {
this.target = target;
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
} }
动态代理测试
package com.proxy; public class TestDynamicProxy {
public static void main(String[] args) {
Customer customer = new Customer();
ProxyPay proxyPay = new ProxyPay();
Pay pay = (Pay) proxyPay.bind(customer);
if(pay.pay()){
System.out.println("整个购物流程完成!");
}else{
System.out.println("付款失败!");
}
}
}
结果:
网上购物使用支付宝结账付款
用户收到货物,支付包支付给商家完成
整个购物流程完成!
虽然完成了动态代理的代码操作,但是对于整个流程还是有些疑惑,是怎么调用代理类中的invoke方法。
于是看源代码发现
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 = getProxyClass(loader, interfaces); /*
* Invoke its constructor with the designated invocation handler.
*/
try {
Constructor cons = cl.getConstructor(constructorParams);
return (Object) cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}
/*
* Returns the <code>java.lang.Class</code> object for a proxy class
* given a class loader and an array of interfaces. The proxy class
* will be defined by the specified class loader and will implement
* all of the supplied interfaces. If a proxy class for the same
* permutation of interfaces has already been defined by the class
* loader, then the existing proxy class will be returned; otherwise,
* a proxy class for those interfaces will be generated dynamically
* and defined by the class loader.
*/
getProxyClass方法就是根据给定的classload和interface生成代理类,如果存在,就返回存在的代理类。
cons.newInstance(new Object[] { h })可以看出代理类中的构造函数是传进去的InvocationHandler对象,会调用InvocationHandler实现类的invoke方法
package com.proxy; import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import sun.misc.ProxyGenerator;
public class ProxyPay implements InvocationHandler {
private Object target;
private static boolean flag = true;
/**
* @param proxy 代理的实例proxy instance
* @param method 代理的实例proxy instance调用接口的方法
* @param args 调用实际类方法的参数数组,没有参数为null,基本类型会转成封装类
* @return
* @date 2016-4-7
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
/**
* Generate a proxy class given a name and a list of proxy interfaces.
*/
String name = "$Proxy0.class";
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Pay.class});
FileOutputStream os = new FileOutputStream(new File(ProxyPay.class.getClassLoader().getResource("").getPath(),name));
os.write(bytes); System.out.println(proxy.getClass().getName());
Object pay = method.invoke(target, args);
if((Boolean) pay){
System.out.println("用户收到货物,支付包支付给商家完成");
}
if(flag){
flag = false;
System.out.println("---------------");
System.out.println(((Pay) proxy.getClass().getConstructor(InvocationHandler.class).newInstance(this)).pay());
}
return pay;
} /**
* loader : 类加载器
* interfaces : 代理类实现的接口
* h :调用方法
*
* public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
*/
public Object bind(Object target) {
this.target = target;
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
} }
结果:
$Proxy0
网上购物使用支付宝结账付款
用户收到货物,支付包支付给商家完成
---------------
$Proxy0
网上购物使用支付宝结账付款
用户收到货物,支付包支付给商家完成
true
整个购物流程完成!
通过proxy.getClass().getName()可以看到proxy代理类是$Proxy0,在内存中存储,通过反射得到此类的构造函数传入了InvocationHandler,调用接口的pay()方法,
发现会调用InvocationHandler实现类的invoke方法,这也证明了上面说的是正确的。使用ProxyGenerator.generateProxyClass(name, new Class[]{Pay.class});生成了$Proxy0的源码,
通过下面的源码pay()方法的return ((Boolean)this.h.invoke(this, m3, null)).booleanValue();可以看到会调用InvocationHandler实现类的invoke方法。
package $Proxy0; import com.proxy.Pay;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException; public final class class extends Proxy
implements Pay
{
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2; public class(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
} public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
} public final boolean pay()
throws
{
try
{
return ((Boolean)this.h.invoke(this, m3, null)).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
} public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
} public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
} static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.proxy.Pay").getMethod("pay", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
结论:Proxy.newProxyInstance根据classloader和接口数组,生成一个$Proxy0代理类,并将InvocationHandler的实现类做为构造函数参数传递给$Proxy0代理类,代理类$Proxy0调用接口的方法,会在方法中调用InvocationHandler的实现类中的invoke()方法。invoke()方法中,通过反射调用委托类(realClass)的实际方法,完成了整个代理操作。
代理模式及jdk动态代理原理的更多相关文章
- Spring代理模式(jdk动态代理模式)
有动态代理和静态代理: 静态代理就是普通的Java继承调用方法. Spring有俩种动态代理模式:jdk动态代理模式 和 CGLIB动态代理 jdk动态代理模式: 代码实现: 房东出租房子的方法(继承 ...
- 浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance
浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口.目标接口的类加载器以及Inv ...
- 代理模式 & Java原生动态代理技术 & CGLib动态代理技术
第一部分.代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常 ...
- 代理模式之cglib动态代理
上一篇博客说了实现InvocationHandler接口的jdk动态代理,还有一种实现动态代理的方式则是:通过继承的方式实现的cglib动态代理. 先在程序中导入cglib的包,cglib-nodep ...
- Spring AOP高级——源码实现(3)AopProxy代理对象之JDK动态代理的创建过程
spring-aop-4.3.7.RELEASE 在<Spring AOP高级——源码实现(1)动态代理技术>中介绍了两种动态代理技术,当然在Spring AOP中代理对象的生成也是运用 ...
- Spring代理模式(CGLIB动态代理模式)
jdk动态代理和CGLIB动态代理 没什么太大的区别,CGLIB动态代理不需要接口,但是需要导入jar包. 房东出租房子的方法: package com.bjsxt.proxy2; public cl ...
- java 代理模式二:动态代理
java动态代理: java动态代理类位于java.lang.reflect包下,一般主要涉及两个类: 1.Interface InvocationHandler 该接口中仅定义了一个方法:Objec ...
- Spring的两种代理方式:JDK动态代理和CGLIB动态代理
代理模式 代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一个对 ...
- 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)
代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...
随机推荐
- 迅为最新推出iTOP-6818开发平台无缝支持4418开发板
iTOP-6818开发板是一款四核ARM 八核开发板与iTOP-4418开发板完全兼容,CPU主频1.4GHz,内存1GB DDR3(2GB可选),存储16GB EMMC,板载千兆以太网,GPS,WI ...
- [Django]登陆界面以及用户登入登出权限
前言:简单的登陆界面展现,以及用户登陆登出,最后用户权限的问题 正文: 首先需要在settings.py设置ROOT_URLCONF,默认值为: ROOT_URLCONF = 'www.urls'# ...
- 帆软报表FineReport数据连接中游标问题解决方案汇总
1. 概念 在数据库中, 游标是一个十分重要的概念.游标是一种能从包括多条数据记录的结果集中,每次提取一条记录的机制. 用SQL语言从数据库中检索数据后,结果放在内存的一块区域中,往往是一个含有多个记 ...
- 《In Search of an Understandable Consensus Algorithm》翻译
Abstract Raft是一种用于管理replicated log的consensus algorithm.它能和Paxos产生同样的结果,有着和Paxos同样的性能,但是结构却不同于Paxos:它 ...
- 洛谷P1341 无序字母对[无向图欧拉路]
题目描述 给定n个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒).请构造一个有n+1个字母的字符串使得每个字母对都在这个字符串中出现. 输入输出格式 输入格式: 第一行输入一 ...
- spring的@Transactional
在service类前加上@Transactional,声明这个service所有方法需要事务管理.每一个业务方法开始时都会打开一个事务.Spring默认情况下会对运行期例外(RunTimeExcept ...
- Java的容器类Collection和Map
一,概念 JAVA集合只能存放引用类型的的数据,不能存放基本数据类型. java的容器类一共有两种主要类型,Colllection和Map. 两者的区别是:Collection是单个元素,而Map是存 ...
- 关于NODE NPM 输入命令后没反应的问题
输入NPM 命令 如 install config help都没有反应,光标在下面一直闪,只有 -v 有反应,查了下,是npm config set prefix 改包的路径出问题了 解决办法就是删 ...
- 遭遇input与button按钮背景图失效不显示的解决办法
笔者从事网页前端代码页面工程师已有多年,作为一个网页重构人员常常会遇到一些莫名其妙的DIV+CSS(正确的说法是XHTML+CSS)在 IE.FireFox火狐. 谷歌浏览器CHROME.苹果浏览器S ...
- HTML编写需要注意的事项
HTML在编写过程中需要注意许多关键的事项,就如最近我在学习中遇到的问题如下: 代码规范问题: 在代码视图中编写代码,一定要规范的格式,不要把代码全部都写到一块,这样不仅影响效率,更加影响视觉,当出现 ...