设计模式之 - 代理模式(Proxy Pattern)
代理模式:代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。很多可以框架中都有用到,比如: spring的AOP的实现主要就是动态代理, mybatis的Mapper代理等。
如下来看下代理模式的UML图(来自百度图片):

代理类和被代理类实现共同的接口, 其中代理类中包含一个被代理类的实例引用。代理模式可以分为静态代理和动态代理,这里主要学习下动态代理。动态代理作用可以实现业务代理和通用逻辑代码解耦,在不改变业务逻辑的同时,动态的给原逻辑代码添加一些通用功能,比如打印调用日志,权限判定,事务处理等等。
下面用代码实现动态代理:
1. 定义一个人的动作行为接口
package cn.aries.pattern.ProxyPattern;
/**
* 人的行为接口
* @author aries
*/
public interface PersonAction { /**
* 说话
*/
public void personSay();
/**
* 跑步
*/
public void personRunning();
/**
* 吃东西
*/
public void personEating(); }
2. 创建人行为的的实现类
package cn.aries.pattern.ProxyPattern;
public class PersonActionImpl implements PersonAction{
@Override
public void personSay() {
System.out.println("人在说话...");
}
@Override
public void personRunning() {
System.out.println("人在跑步...");
}
@Override
public void personEating() {
System.out.println("人在吃东西...");
}
}
3. 动态代理需要一个实现了InvoketionHandler接口的类
package cn.aries.pattern.ProxyPattern; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class ProxyPerson implements InvocationHandler{
//被代理的实例对象
PersonAction obj;
private ProxyPerson(PersonAction obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行方法之前打印动作开始。
System.out.println(method.getName() + "ation start ...");
//使用反射执行目标方法
method.invoke(obj, args);
//在方法执行结束时打印动作结束。
System.out.println(method.getName() + "ation end ...");
return null;
}
//定义一个静态方法生成代理对象
public static Object getProxyPersonAction(PersonAction obj){
PersonAction proxy = (PersonAction) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new ProxyPerson(obj));
return proxy;
}
}
4. 客户端代码
package cn.aries.pattern.ProxyPattern;
public class App {
public static void main(String[] args) throws Exception {
//设置系统参数,将生成的代理类的class文件保存到本地
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
PersonAction pa = new PersonActionImpl();
//调用生成代理类的方法
PersonAction proxyPa = (PersonAction) ProxyPerson.getProxyPersonAction(pa);
//用代理对象调用目标方法
proxyPa.personSay();
proxyPa.personRunning();
proxyPa.personEating();
//打印代理对象的父类
System.out.println(proxyPa.getClass().getSuperclass());
}
}
执行结果:
personSayation start ...
人在说话...
personSayation end ...
personRunningation start ...
人在跑步...
personRunningation end ...
personEatingation start ...
人在吃东西...
personEatingation end ...
class java.lang.reflect.Proxy
当方法在中的是分别执行我们在目标方法执行前后添加的代码。
5. 代理对象是通过Proxy.newProxyInstance(...)这个方法生成的,我们进入源代码查看下
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{
if (h == null) {
throw new NullPointerException();
}
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
这里生成代理类的字节码文件
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
//在这里获取代理类的构造函数,从前面的运行结果中可以得知,代理类是Proxy类的子类
//而constructorParams在Proxy类中是一个静态的常量: private static final Class<?>[] constructorParams = { InvocationHandler.class };
//所以这里获取的带InvocationHandler对象为入参的构造函数,也就是其父类Proxy的构造函数:protected Proxy(InvocationHandler h){...}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//这里调用newInstance()方法创建代理对象,其内部实现是:return cons.newInstance(new Object[] {h} );使用反射通过含参(hanlder)生成代理对象。
//其中h赋值给了其父类Proxy类的成员变量: protected InvocationHandler h;
//最终在这里生成代理对象并返回
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
// create proxy instance with doPrivilege as the proxy class may
// implement non-public interfaces that requires a special permission
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return newInstance(cons, ih);
}
});
} else {
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
6. 到此我了解了代理对象的生产过程,但是代理对象和handler是什么关系呢,又是如何调用其invoke(...)方法呢,这暂时是个谜团让我们来看下生成的代理类的源码,这些就都清楚了。
注:System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 这个是设置系统参数,将生产的代理类自己码文件保存在本地,然后我们通过反编译就可以获得其Java代码。
package com.sun.proxy; import cn.aries.pattern.ProxyPattern.PersonAction;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements PersonAction {
//这五个静态变量前三个m0,m1,m2分别是代理类继承的Object类的hashcode(),equals(),toString()方法
//其他从m3开始是继承的们定义的接口类的方法根据方法的多少m后面的数字递增
private static Method m1;
private static Method m3;
private static Method m5;
private static Method m0;
private static Method m4;
private static Method m2; static {
try {
//这里使用静态代码块对通过反射对代理对象中的方法进行实例化
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("cn.aries.pattern.ProxyPattern.PersonAction").getMethod("personEating", new Class[0]);
m5 = Class.forName("cn.aries.pattern.ProxyPattern.PersonAction").getMethod("personRunning", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m4 = Class.forName("cn.aries.pattern.ProxyPattern.PersonAction").getMethod("personSay", 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());
}
} public $Proxy0(InvocationHandler paramInvocationHandler)throws{
super(paramInvocationHandler);
} //这里是对我们定义的personEating方法进行实现
//根据类文件我们可以看到,代理类继承了Proxy类,所以其成员变量中包含一个Handler实例对象的引用
//在创建代理实例对象的时候,我们使用的protected Proxy(InvocationHandler h) {this.h = h;}这个构造函数
//所以下面的h就是我们传进去的handler对象
//这里使用handler对象调用自己的invoke()方法,m3就是我们要执行的方法,
//后面的方法的参数,如果有参数就传对应的参数,没有就传null
//此时我们明白了代理对象和handler的关系,以及如何调用到invoke()方法有了明确的认识了。
public final void personEating()throws{
try
{
this.h.invoke(this, m3, null);
return;
}catch (Error|RuntimeException localError){
throw localError;
}catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
//这里原理同上,为了节省空间这里就不贴出来了
public final void personSay(){...}
public final void personRunning(){...} 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);
}
} 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);
}
}
}
写完后浏览了一下,好像没有发现被代理对象的引用在代理类中出现;然后想了下,代理类继承了Proxy类,其中Proxy类中有我们写的InvoketionHandler对象的是实例,而这个handler实例中就存有我们创建的被代理对象的实例引用,在invoke方法中,传入的实例对象就是我们创建的这个被代理对象;这样就间接的持有了被代理对象的实例引用。
到此动态代理的生成过程,以及是如何调用invoke()方法的原理已经搞清楚,到此本文完结。
设计模式之 - 代理模式(Proxy Pattern)的更多相关文章
- 乐在其中设计模式(C#) - 代理模式(Proxy Pattern)
原文:乐在其中设计模式(C#) - 代理模式(Proxy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 代理模式(Proxy Pattern) 作者:webabcd 介绍 为 ...
- 二十四种设计模式:代理模式(Proxy Pattern)
代理模式(Proxy Pattern) 介绍为其他对象提供一个代理以控制对这个对象的访问. 示例有一个Message实体类,某对象对它的操作有Insert()和Get()方法,用一个代理来控制对这个对 ...
- c#设计模式之代理模式(Proxy Pattern)
引言 代理这个词语,大家在现实世界已经频繁的接触过,例如火车站代理售票点,因为这些代理售票点的存在,我们不必要去火车站的售票处就可以查询或者取到火车票.代理点本身是没有能力生产车票的,我们在代理处享受 ...
- 乐在其中设计模式(C#) - 代理模式(Proxy Pattern)【转】
介绍 为其他对象提供一个代理以控制对这个对象的访问. 示例 有一个Message实体类,某对象对它的操作有Insert()和Get()方法,用一个代理来控制对这个对象的访问. MessageModel ...
- 设计模式系列之代理模式(Proxy Pattern)——对象的间接访问
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...
- 设计模式 - 代理模式(proxy pattern) 未使用代理模式 具体解释
代理模式(proxy pattern) 未使用代理模式 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 部分代码參考: http://blog.csdn. ...
- 代理模式(Proxy pattern)
代理模式(proxy pattern):作用:为其他对象提供一种代理,以控制对这个对象的访问.代理对象在客户端对象和目标对象之间起中介的作用. 代理模式涉及到的角色: 抽象角色:声明真实对象和代理对象 ...
- 设计模式——代理模式(Proxy Pattern)
代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问. UML图: 模型设计: Subject类: package com.cnblog.clarck; /** * Subject 类 ...
- 13.代理模式(Proxy Pattern)
using System; namespace Test { //抽象角色:声明真实对象和代理对象的共同接口. //代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象, //同时代理 ...
- 大熊君说说JS与设计模式之------代理模式Proxy
一,总体概要 1,笔者浅谈 当我们浏览网页时,网页中的图片有时不会立即展示出来,这就是通过虚拟代理来替代了真实的图片,而代理存储了真实图片的路径和尺寸,这就是代理方式的一种. 代理模式是比较有用途的一 ...
随机推荐
- 拓扑排序&关键路径
拓扑排序:AOV网 概念 example:选课问题:AOV网 顶点活动(Activity On Vertex)网是指用顶点表示活动,而用边集表示活动关系的有向图. 在这个例子中,课程为结点,而有向边表 ...
- 企业实战Nginx+Tomcat动静分离架构的技术分享
Nginx动静分离简单来说就是把动态跟静态请求分开,不能理解成只是单纯的把动态页面和静态页面物理分离.严格意义上说应该是动态请求跟静态请求分开,可以理解成使用Nginx处理静态页面,Tomcat.Re ...
- MYSQL 主从复制---简单易学
本帖最后由 传说中的草包 于 2017-4-12 09:12 编辑为什么要用mysql主从复制? 这个问题不需要回答吧,,,,,想想,一个人干活快呢,还是一万个能性格功力一样的人干活快呢. 不用解释大 ...
- tp的秘密
入口文件index.php define('APP_DEBUG',True); 改为false* memory_get_usage 获取本套系统目前内存* tp框架中ThinkPHP\Library\ ...
- Shell 初步学习
Shell 概述 Shell:Linux命令解释器 脚本执行方式 chmod 755 脚本名:赋权限(调用必须显示的使用绝对路径或相对路径) bash 脚本名:通过Bash调用执行脚本 命令别名 al ...
- Koltin——最详细的可见性修饰符详解
在Kotlin中,不管是类,对象,接口,构造函数,函数,属性及其设置器都具有可见性修饰符.Kotlin中的可见性修饰符共四种.即public.protected.private.internal.在不 ...
- 反射---hasattr、getattr、setattr、delattr
class Foo: f = "类的静态变量" def __init__(self,name,age): self.name = name self.age = age def s ...
- Kotlin 一个好用的新功能:Parcelize
在开发中,如果有需要用到序列化和反序列化的操作,就会用到 Serializable 或者 Parcelable,它们各有优缺点,会适用于不同的场景. Serializable 的优点是实现简单,你只需 ...
- 高效管理http连接
1.Http连接基础 Http协议承载了互联网上的主要流量,然而说到传输,还要回归到最基本的网络分层模型TCP/IP.TCP/IP是全球计算机及网络设备都在使用的一种常用的分组交互网络分层协议集.客户 ...
- object-fit?
知道有这个属性存在,是一个很偶然的机会.有一天,设计部的一个小伙伴给了我一个网址,说很有个性,让我看一下,当发现一个很有意思的效果时,作为一个前端小兵的我当然是第一时间开始审查元素,然后看到了这个从没 ...