设计模式之 - 代理模式(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{@Overridepublic void personSay() {System.out.println("人在说话...");}@Overridepublic void personRunning() {System.out.println("人在跑步...");        }@Overridepublic 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;}@Overridepublic 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 permissionreturn 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()方法的原理已经搞清楚,到此本文完结。

转载于:https://www.cnblogs.com/qq-361807535/p/7106457.html

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

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

相关文章

JavaEE规范与系统结构

JavaEE规范&#xff1a; JavaEE规范是J2EE规范的新名称&#xff0c;早期被称为J2EE规范&#xff0c;其全称是Java 2 Platform Enterprise Edition&#xff0c;它是由SUN公司领导、各厂家共同制定并得到广泛认可的工业标准&#xff08;JCP组织成员&#xff09;。之所以改名为Jav…

HTTP协议和NDS服务器

HTTP协议&#xff1a; HTTP的全称是&#xff1a;Hyper Text Transfer Protocol&#xff0c;意为超文本传输协议。它指的是服务器和客户端之间交互必须遵循的一问一答的规则。形容这个规则&#xff1a;问答机制、握手机制。它规范了请求和响应内容的类型和格式。HTTP协议是由W3C…

Servlet方法详解

Servlet&#xff1a; Servlet是SUN公司提供的一套规范&#xff0c;名称就叫Servlet规范&#xff0c;它也是JavaEE规范之一。使用JavaEE的API。目前在Oracle官网中的最新版本是JavaEE8&#xff0c; Servlet是一个运行在web服务端的java小程序它可以用于接收和响应客户端的请求要…

继承的实现原理

一.继承的实现原理 1.继承顺序 1.1单独分叉线路&#xff1a;经典类与新式类依次从左到右&#xff0c;深度优先 1.2多条重合线路&#xff1a;经典类一路到头&#xff0c;深度优先&#xff1b;新式类&#xff0c;广度优先。<参考MRO列表&#xff0c;仅在新式类有> class A(…

Response响应方法详解

Response&#xff1a; 响应&#xff1a;服务器把请求的处理结果告知客户端。在B/S架构中&#xff0c;响应就是把结果带回浏览器。响应对象&#xff1a;在项目中用于发送响应的对象 常用状态码&#xff1a; 状态码说明200执行成功302它和307一样&#xff0c;都是用于重定向的状…

perl6 HTTP::UserAgent (2)

http://www.cnblogs.com/perl6/p/6911166.html 之前这里有个小小例子&#xff0c; 这里只要是总结一下。 HTTP::UserAgent包含了以下模块: ---------------------------------------------------------------------------------------------- Module |Path-Nam…

Java会话技术

会话技术&#xff1a; 会话指的是客户端浏览器和服务端之间的度偶次请求和响应当打开浏览器&#xff0c;访问网站地址后&#xff0c;会话开始&#xff0c;当关闭浏览器&#xff08;或者到了过期时间&#xff09;&#xff0c;会话结束。就像打电话只要不挂电话就是一次会话。 会…

【调用IP宏文件进行仿真】modelsim仿真时出现 Instantiation of 'xxx' failed. The design unit was not found....

出现错误类似&#xff1a;modelsim 仿真fifo时出现 Error: (vsim-3033) E:/Programs/ModelSim/fifo/ps2_fifo.v(75): Instantiation of scfifo failed. The design unit was not found.仿真波形不对&#xff0c;调用的ip核没有输出&#xff08;白色虚线&#xff09;等情况&…

Java Server Page

JSP JSP全称是Java Server Page&#xff0c;基于Java和Servlet一样是sun公司推出的一套开发动态web资源的技术&#xff0c;称为JSP/Servlet规范。JSP的本质其实就是一个Servlet。jsp是一种动态网页技术标准&#xff0c;jsp部署在服务器上可以处理客户端的请求&#xff0c;并根据…

HDFS概述(2)————Block块大小设置

参考&#xff1a;HDFS概述&#xff08;4&#xff09;————HDFS权限HDFS概述&#xff08;3&#xff09;————HDFS FederationHDFS概述&#xff08;2&#xff09;————Block块大小设置HDFS概述&#xff08;1&#xff09;————HDFS架构问题Q: 一个常被问到的一个问题是…

BZOJ 1083: [SCOI2005]繁忙的都市【Kruscal最小生成树裸题】

1083: [SCOI2005]繁忙的都市 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2925 Solved: 1927[Submit][Status][Discuss]Description 城市C是一个非常繁忙的大都市&#xff0c;城市中的道路十分的拥挤&#xff0c;于是市长决定对其中的道路进行改造。城市C的道 路是这样分…

Mysql介绍与安装LinuxmacOS系统

数据库&#xff1a; 用于存储和管理数据的仓库 数据库的好处&#xff1a; 可以持久化存储数据方便存储和管理数据使用了统一的方式操作数据库 – SQL 常见的数据库&#xff1a; Oracle&#xff1a;收费的大型数据库&#xff0c;Oracle公司的产品。Oracle收购SUN公司&#xff0c…

如何查看Laravel版本号的三种方法

1.PHP artisan --version 2.vim vendor/laravel/framework/src/Illuminate/Foundation/Application.php 3&#xff1a;可以写在路由里 5.4版本的路由文件夹是routes。我们可以写在routes\web.php里。 Route::get(laravel-version, function(){ $laravel app(); return…

02_反汇编_反编译

实际上安卓的应用都是zip包,只不过把zip扩展名修改了,修改成了APK.所以如果你想拿到它的图片的话,实际上特别简单&#xff0c;你就把它这个.apk换成.zip.换成.zip之后这里的图片资源就都可以拿到了. 有些公司可能美工的水平或者美工的人数比较少&#xff0c;项目还比较急&#…

SQL约束语法

约束 作用&#xff1a;对表中的数据进行限定&#xff0c;保证数据的正确性、有效性、完整性&#xff01; 约束分类&#xff1a; 约束说明PRIMARY KEY主键约束PRIMARY KEY AUTO_INCREMENT主键、自动增长UNIQUE唯一约束NOT NULL非空约束FOREIGN KEY外键约束FOREIGN KEY ON UPDAT…

修改项目名称之后,访问不到项目的问题

转载于:https://www.cnblogs.com/Joke-Jay/p/7190187.html

stanford-parser for C#

在项目里用到C#对英文句子进行词性标注。比較成熟的英文词性标注软件是stanford-parser。它个C#版本号&#xff0c;也是借助于IKVM完毕JAVA-C#的转换。详细配置过程例如以下&#xff1a; 1、下载stanford-parser的jar包 http://nlp.stanford.edu/software/lex-parser.shtml 2…

【bzoj】 1412: [ZJOI2009]狼和羊的故事

Description “狼爱上羊啊爱的疯狂&#xff0c;谁让他们真爱了一场&#xff1b;狼爱上羊啊并不荒唐&#xff0c;他们说有爱就有方向&#xff0e;&#xff0e;&#xff0e;&#xff0e;&#xff0e;&#xff0e;” Orez听到这首歌&#xff0c;心想&#xff1a;狼和羊如此和谐&am…

计算机基础--网络

互联网协议 互联网协议的功能&#xff1a;定义计算机如何接入internet&#xff0c;以及接入internet的计算机通信的标准。 互联网协议按照功能不同分为osi七层或者tcp/ip五层或tcp/ip四层 每层常见物理设备 因为学习python编程只需要了解tcp/ip五层模型&#xff0c;所以我们只需…

万字详解数据库连接池

数据库连接池的概念 数据库连接是一种关键的、有限的、昂贵的资源&#xff0c;这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性&#xff0c;影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。数据库连…