代理模式 :为其它对象提供代理,以控制对这个对象的访问。

代理模式的特征:代理类(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动态代理原理的更多相关文章

  1. Spring代理模式(jdk动态代理模式)

    有动态代理和静态代理: 静态代理就是普通的Java继承调用方法. Spring有俩种动态代理模式:jdk动态代理模式 和 CGLIB动态代理 jdk动态代理模式: 代码实现: 房东出租房子的方法(继承 ...

  2. 浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance

    浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口.目标接口的类加载器以及Inv ...

  3. 代理模式 & Java原生动态代理技术 & CGLib动态代理技术

    第一部分.代理模式  代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常 ...

  4. 代理模式之cglib动态代理

    上一篇博客说了实现InvocationHandler接口的jdk动态代理,还有一种实现动态代理的方式则是:通过继承的方式实现的cglib动态代理. 先在程序中导入cglib的包,cglib-nodep ...

  5. Spring AOP高级——源码实现(3)AopProxy代理对象之JDK动态代理的创建过程

    spring-aop-4.3.7.RELEASE  在<Spring AOP高级——源码实现(1)动态代理技术>中介绍了两种动态代理技术,当然在Spring AOP中代理对象的生成也是运用 ...

  6. Spring代理模式(CGLIB动态代理模式)

    jdk动态代理和CGLIB动态代理 没什么太大的区别,CGLIB动态代理不需要接口,但是需要导入jar包. 房东出租房子的方法: package com.bjsxt.proxy2; public cl ...

  7. java 代理模式二:动态代理

    java动态代理: java动态代理类位于java.lang.reflect包下,一般主要涉及两个类: 1.Interface InvocationHandler 该接口中仅定义了一个方法:Objec ...

  8. Spring的两种代理方式:JDK动态代理和CGLIB动态代理

    代理模式 代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一个对 ...

  9. 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)

    代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...

随机推荐

  1. execve(file, argv, env)参数argv获取字符串个数

    /* Copyright (C) 1999, 2000, 2002, 2003 Free Software Foundation, Inc. This file is part of the GNU ...

  2. C#学习笔记-KeyDown、KeyPress、KeyUp事件以及KeyCode、KeyData、KeyValue、KeyChar属性

    本来没打算单独写的,但是在自己弄测试小程序的时候,越写发现不清楚的东西越多,所以实践又一次证明:纸上得来终觉浅,绝知此事要躬行! 直接贴代码了: //发生顺序:KeyDown->KeyPress ...

  3. 读 [The Root of Lisp]

    首先,在对 Lisp 有一丢丢的了解下读这篇文章会大大激发你学下去的欲望.之后可以看作者的著作<ANSI Common Lisp>. 想要体会一下 Lisp 的强大,本文是不二之选. Co ...

  4. 【每日一linux命令6】命令中的命令

    许多命令在执行后,会进入该命令的操作模式,如 fdisk.pine.top 等,进入后我们必须要使用该 命令中的命令,才能正确执行:而一般要退出该命令,可以输入 exit.q.quit 或是按[Ctr ...

  5. java线程池(newSingleThreadExecutor())小应用

    创建单个线程,用来操作一个无界的队列任务,不会使用额外的线程.如果线程崩溃会重新创建一个,直到任务完成. 代码: import java.util.concurrent.ExecutorService ...

  6. .net 账号异地登录

    第一步:给用户名一个对应的字段记录登陆状态,比如 online , 登陆后就为1, 同时还应增加记录用户活动时间,比如actionTime, 在基类(比如初始化Conn的类), 加上 update [ ...

  7. Java开发环境的搭建以及使用eclipse从头一步步创建java项目

    一.java 开发环境的搭建 这里主要说的是在windows 环境下怎么配置环境. 1.首先安装JDK java的sdk简称JDK ,去其官方网站下载最近的JDK即可..http://www.orac ...

  8. 关于SQL SERVER数据库学习总结

    对于SQL SERFVER数据库也学了有一阵子了,自己也对自己所学做了一些总结. 我们首先学习数据库设计的一些知识点和用SQL语句建库. 设计数据库步骤:需求分析阶段,概要设计阶段,详细设计阶段, 建 ...

  9. JS数组去重比较

    数组去重复是一个常见的需求,我们暂时考虑同类型的数组去重复.主要是理清思路和考虑执行性能. for循环删除后面重复的 var uniqueFor = function(arr) { for (var ...

  10. Redis 学习(二)

    Redis可以存储以下5种数据类型 1. String 字符串 整数 浮点 2. List   一个链表 3. Set  无序收集器 4. Hash  无序散列表 5. Zset   有序集合