代理模式

1.生活中:

代理就是一个人或者一个组织代表其它人去做一件事的现实生活中的。

在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象能够在client和目标对象之间起到中介的作用。

2.官方:

代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象。并由代理对象控制对原对象的引用

一、静态代理

类图结构例如以下



在代理模式中的角色:

  ●抽象主题角色:声明了目标对象和代理对象的共同接口,这样一来在不论什么能够使用目标对象的地方都能够使用代理对象。

  ●真实主题角色:定义了代理对象所代表的目标对象。

  ●代理主题角色:代理对象内部含有目标对象的引用,从而能够在不论什么时候操作目标对象;代理对象提供一个与目标对象同样的接口。以便能够在不论什么时候替代目标对象。代理对象通常在client调用传递给目标对象之前或之后。执行某个操作。而不是单纯地将调用传递给目标对象。

它能够添加一些真实主题里面没有的功能。

生活中的样例:过春节加班比較忙,没空去买火车票,这时能够打个电话到附近的票务中心,叫他们帮你买张回家的火车票,当然这会附加额外的劳务费。但要清楚票务中心自己并不卖票,仅仅有火车站才真正卖票,票务中心卖给你的票事实上是通过火车站实现的。这点非常重要!

上面这个样例。你就是“客户”,票务中心就是“代理角色”,火车站是“真实角色”。卖票称为“抽象角色”!

代码

抽象主题角色

//抽象角色:声明真实对象和代理对象的共同接口;
public interface TicketManager {
/**
* 售票
*/
public void soldTicket();
/**
* 改签
*/
public void changeTicket();
/**
* 退票
*/
public void returnTicket();
}

真实主题角色

public class TicketManagerImpl implements TicketManager {

    @Override
public void soldTicket() {
//checkIdentity();
System.out.println("售票");
} @Override
public void changeTicket(){
//checkIdentity();
System.out.println("改签");
} @Override
public void returnTicket() {
//checkIdentity();
System.out.println("退票");
} /**
* 身份验证
*/
public void checkIdentity(){
System.out.println("身份验证");
}
}

代理主题角色(加入了身份验证功能)

public class StaticProxyTicketManager implements TicketManager {
TicketManager ticketManager;//目标对象的引用 public StaticProxyTicketManager(TicketManager ticketManager) {
this.ticketManager = ticketManager;
} @Override
public void soldTicket() {
checkIdentity();
ticketManager.soldTicket();
} @Override
public void changeTicket() {
checkIdentity();
ticketManager.changeTicket();
} @Override
public void returnTicket() {
checkIdentity();
ticketManager.changeTicket();
}
/**
* 身份验证
*/
public void checkIdentity(){
System.out.println("身份验证--------------");
} }

第二个代理主题角色(加入了日志功能)

//代理类  实现同一个接口
public class LogProxy implements TicketManager {
TicketManager ticketManager;//目标类的引用
public LogProxy(TicketManager ticketManager){
this.ticketManager=ticketManager;
}
@Override
public void soldTicket() {
ticketManager.soldTicket();
log();//后置增强
} @Override
public void changeTicket() {
ticketManager.changeTicket();
log();
} @Override
public void returnTicket() {
ticketManager.returnTicket();
log(); }
//增强
private void log() {
System.out.println("日志..."); } }

client

public class Test {
public static void main(String[] args) { //装饰模式 new TicketManagerImpl() 真实的目标对象
//TicketManager tm=new StaticProxyTicketManager(new TicketManagerImpl());
TicketManager tm=new LogProxy(new StaticProxyTicketManager(new TicketManagerImpl())); tm.soldTicket();
tm.changeTicket();
tm.returnTicket();
}
}

结果:

身份验证————–

售票

日志…

身份验证————–

改签

日志…

身份验证————–

改签

日志…

从上面样例能够看出 client通过代理来购票 而代理实际上不能卖票给客户,他实际上是通过目标对象卖票给客户的,也就是说他是通过真实主题的目标对象实现给client卖票的功能,他仅仅是一个中介。但我们能够在它里面添加一些功能,比方身份验证或者宣传打广告等其它的功能。

静态代理类:在程序执行前。代理类的.class文件就已经存在了,已确定被代理的对象

静态代理:

长处:对真实对象进行封装,不会改动目标类的代码。

缺点:

1.多个不同类型目标对象须要代理时,我就须要建立多个代理类,造成类的膨胀

2.代码的冗余

3.编译期加入。不够灵活

二、动态代理

描写叙述(这个描写叙述从网上看到的,相对照较easy理解)

动态代理(Dynamic Proxy):相比静态代理,动态代理具有更强的灵活性。由于它不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象,我们能够把这样的指定延迟到程序执行时由JVM来实现。

所谓代理,就是须要代理类和被代理类有同样的对外接口或者说成服务。所以代理类一般都必须实现了全部被代理类已实现的接口,由于接口就是制定了一系列对外服务的标准。

1.JDK实现动态代理

正由于动态代理有这样灵活的特性,所以我们在设计动态代理类(DynamicProxy)时不用显式地让它实现与真实主题类(RealSubject)同样的接口(interface),而是把这样的实现推迟到执行时。

为了能让DynamicProxy类能够在执行时才去实现RealSubject类已实现的一系列接口并执行接口中相关的方法操作,须要让 DynamicProxy类实现JDK自带的java.lang.reflect.InvocationHandler接口,该接口中的invoke() 方法能够让DynamicProxy实例在执行时调用被代理类的“对外服务”,即调用被代理类须要对外实现的全部接口中的方法。也就是完毕对真实方法的调 用。Java帮助文档中称这些真实方法为处理程序。

依照上面所述,我们肯定必须先把被代理类RealSubject已实现的全部interface都载入到JVM中。不然JVM怎么能够找到这些方法呢?明确了这个道理,那么我们就能够创建一个被代理类的实例,获得该实例的类载入器ClassLoader。

所谓的类载入器ClassLoader,就是具有某个类的类定义,即类的内部相关结构(包含继承树、方法区等等)。

更重要的是,动态代理模式能够使得我们在不改变原来已有的代码结构的情况下,对原来的“真实方法”进行扩展、增强其功能,而且能够达到控制被代理对 象的行为的目的。

请详看以下代码中的DynamicProxy类,当中必须实现的invoke()方法在调用被代理类的真实方法的前后都可进行一定的特殊 操作。

这是动态代理最明显的长处

类图

代码

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class DynamicProxyTicketManager implements InvocationHandler {
private Object targetObject; /**
* 目标的初始化方法,依据目标生成代理类
*
* @param targetObject
* @return
*/
public Object newProxyInstance(Object targetObject) {
this.targetObject = targetObject;
// 第一个參数,目标对象 的装载器
// 第二个參数。目标接口已实现的全部接口,而这些是动态代理类要实现的接口列表
// 第三个參数, 调用实现了InvocationHandler的对象生成动态代理实例,当你一调用代理,代理就会调用InvocationHandler的invoke方法
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(),
this);
} /**
* 反射,这样你能够在不知道详细的类的情况下,依据配置的參数去调用一个类的方法。 在灵活编程的时候非常实用。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 检查
checkIdentity();
Object ret = null;
try {
// 调用目标方法
ret = method.invoke(targetObject, args);
// 执行成功,打印成功信息
log();
} catch (Exception e) {
e.printStackTrace();
// 失败时,打印失败信息
System.out.println("error-->>" + method.getName());
throw e;
}
return ret;
} /**
* 身份验证
*/
public void checkIdentity(){
System.out.println("身份验证--------------");
}
public void log(){
System.out.println("日志..." );
} }

client

public class Test {
public static void main(String[] args) {
DynamicProxyTicketManager dynamicProxyTicketManager=new DynamicProxyTicketManager();
TicketManager tm=(TicketManager) dynamicProxyTicketManager.newProxyInstance(new TicketManagerImpl()); tm.soldTicket();
tm.changeTicket();
tm.returnTicket();
}
}

结果同上

优缺点

长处:

1、一个动态代理类更加简单了。能够解决创建多个静态代理的麻烦。避免不断的反复多余的代码

2、调用目标代码时,会在方法“执行时”动态的加入,决定你是什么类型。才调谁。灵活

缺点:

1、系统灵活了,可是相比而言,效率减少了,比静态代理慢一点

2、动态代理比静态代理在代码的可读性上差了一点,不太easy理解

3、JDK动态代理仅仅能对实现了接口的类进行代理

总结

各有各的好,详细情况详细讨论

2.Cglib实现动态代理

描写叙述(网上整理)

AOP的源代码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。

两种方法同一时候存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的 ,cglib动态代理底层则是借助asm来实现的。总的来说。反射机制在生成类的过程中比較高效,而asm在生成类之后的相关执行过程中比較高效(能够通 过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。另一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。假设没有 上述前提。jdk动态代理不能应用。由此能够看出。jdk动态代理有一定的局限性。cglib这样的第三方类库实现的动态代理应用更加广泛, 且在效率上更有优势。

JDK的动态代理机制仅仅能代理实现了接口的类,否则不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖当中方法实现增强,但由于採用的是继承。所以不能对final修饰的类进行代理。

介绍:

CGLIB的核心类:

net.sf.cglib.proxy.Enhancer – 基本的增强类

net.sf.cglib.proxy.MethodInterceptor – 基本的方法拦截类。它是Callback接口的子接口。须要用户实现

net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,能够方便的实现对源对象方法的调用,如使用:

Object o = methodProxy.invokeSuper(proxy, args);//尽管第一个參数是被代理对象,也不会出现死循环的问题。

net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它常常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口仅仅定义了一个方法

public Object intercept(Object object, java.lang.reflect.Method method,

Object[] args, MethodProxy proxy) throws Throwable;

第一个參数是代理对像,第二和第三个參数各自是拦截的方法和方法的參数。原来的方法可能通过使用java.lang.reflect.Method 对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使 用。由于它更快

代码

public class CglibDynamicProxyTicketManager implements MethodInterceptor  {
private Object targetObject;//目标对象
/**
* 创建代理对象
*
* @param targetObject
* @return
*/
public Object getInstance(Object targetObject) {
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer(); // 用这个类来创建代理对象(被代理类的子类): 并设置父类;设置回调。
enhancer.setSuperclass(this.targetObject.getClass()); // 设置被代理类作为其父类的代理目标
// 回调方法
enhancer.setCallback(this); // 设置回调--当这个代理对象的方法被调用时 回调方法intercept()会被执行
// 创建代理对象
return enhancer.create();
} @Override
//回调方法
// methodProxy 代理的类的方法
/**
* methodProxy 会调用父类(目标对象)的被代理的方法,比方soldTicket方法等
*/
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
Object result = null;
checkIdentity();//前置增强
result=methodProxy.invokeSuper(obj, args); //调用新生成的cglib的代理对象 所属的父类的被代理的方法
log();//后置增强
return result;
} /**
* 身份验证
*/
public void checkIdentity(){
System.out.println("身份验证--------------");
}
public void log(){
System.out.println("日志..." );
} }

client

public class Test {
public static void main(String[] args) {
CglibDynamicProxyTicketManager cglibdynamicProxyTicketManager=new CglibDynamicProxyTicketManager();
//生成代理对象
TicketManager tm=(TicketManager) cglibdynamicProxyTicketManager.getInstance(new TicketManagerImpl()); tm.soldTicket();//当调用代理对象的被代理对象的方法时 会自己主动回调 代理类中的Intercept()方法
tm.changeTicket();
tm.returnTicket();
}
}

结果同上

Java设计模式(八)----代理模式的更多相关文章

  1. Java设计模式之代理模式(静态代理和JDK、CGLib动态代理)以及应用场景

    我做了个例子 ,需要可以下载源码:代理模式 1.前言: Spring 的AOP 面向切面编程,是通过动态代理实现的, 由两部分组成:(a) 如果有接口的话 通过 JDK 接口级别的代理 (b) 如果没 ...

  2. java设计模式6——代理模式

    java设计模式6--代理模式 1.代理模式介绍: 1.1.为什么要学习代理模式?因为这就是Spring Aop的底层!(SpringAop 和 SpringMvc) 1.2.代理模式的分类: 静态代 ...

  3. 夜话JAVA设计模式之代理模式(Proxy)

    代理模式定义:为另一个对象提供一个替身或者占位符以控制对这个对象的访问.---<Head First 设计模式> 代理模式换句话说就是给某一个对象创建一个代理对象,由这个代理对象控制对原对 ...

  4. Java设计模式:代理模式(转)

    代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.这里使用到编程中的一 ...

  5. Java 设计模式_代理模式(2016-08-19)

    概念: 代理模式是对象的结构模式.代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. 就是一个人或者机构代表另一个人或者机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...

  6. JAVA设计模式:代理模式&& 装饰模式区别

    在前面学习了代理模式和装饰模式后,发现对两者之间有时候会混淆,因此对两者进行了区别和理解: 装饰模式你可以这样理解,就像糖一样,卖的时候商家大多要在外面包一层糖纸,其实原本还是糖. public in ...

  7. Java设计模式:代理模式(二)

    承接上文 三.计数代理 计数代理的应用场景是:当客户程序需要在调用服务提供者对象的方法之前或之后执行日志或者计数等额外功能时,就可以用到技术代理模式.计数代理模式并不是把额外操作的代码直接添加到原服务 ...

  8. java设计模式之代理模式 ,以及和java 回调机制的区别

    java 代理模式就是: 将自己要做的事交给别人去做(这个别人就是代理者,自己就是被代理者),为什么自己能做的要交给别人去做了?假如一个小学生小明,现在要写作业,但是又想玩游戏,他更想玩游戏,并且不想 ...

  9. Java设计模式之代理模式(Proxy)

    前言: 最近在研究Retrofit开源框架的时候,其主要核心代码是通过注解标示参数,动态代理模式实现具体接口,反射机制进行参数解析,最终实现发送请求.其实之前在学习Xutils源码的时候,Xutils ...

  10. Java设计模式 之 代理模式

    所谓的代理模式就是为其它类或对象提供一个代理以控制对这个对象的访问.那么常见的代理有远程代理,虚拟代理,保护代理,智能代理. 1. 远程代理:为一个不同地址空间的对象提供一个本地代理对象. 2. 虚拟 ...

随机推荐

  1. Perl、PHP、Python、Java 和 Ruby 比较【转载+整理】

    从本文的内容上,写的时间比较早,而且有些术语我认为也不太准,有点口语化,但是意思到了. 问题: Perl.Python.Ruby 和 PHP 各自有何特点? 为什么动态语言多作为轻量级的解决方案? L ...

  2. centos7.2安装社区版docker-ce-17.06.1

    先yum install安装如下包: container-selinux-2.21-1.el7.noarch libcgroup-0.41-13.el7.x86_64 libtool-ltdl-2.4 ...

  3. Selenium2(WebDriver)总结(三)---元素定位方法

    元素定位的重要性不言而喻,如果定位不到元素谈何操作元素呢,webdrvier提供了很多种元素定位方法,如ID,Name,xpath,css,tagname等. 例如需要定位如下元素: <inpu ...

  4. PetaPoco使用

    <?xml version="1.0" encoding="utf-8" ?> <configuration> <connecti ...

  5. 什么是Session分布式共享

    在了解session分布式共享之前先来了解Session.Redis和Nginx的相关知识. 一.Session相关知识 1.Session 介绍 Session在网络应用中,称为“会话控制”. 每个 ...

  6. Android View的事件分发机制

    准备了一阵子,一直想写一篇事件分发的文章总结一下.这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等.这些子View的外层还有ViewGroup ...

  7. sell 项目 订单详情表 设计 及 创建

    1.数据库设计 2.订单详情表 创建 /** * 订单详情表 */ create table `order_detail` ( `detail_id` varchar(32) not null, `o ...

  8. 在vim中安装及配置NERDTree插件

    使用Vundle插件安装,在.vimrc中加入以下代码: Plugin 'scrooloose/nerdtree' 打开vim,输入命令如下: :BundleInstall 等待安装完毕 配置NERD ...

  9. cocos2d-js 写日志log 查看日志log Android调试查看log

    1 输出日志的方式,当然是cc.log了 2 如何查看日志?        a)如果小程序可以先在浏览器上跑,例如用chrome,在控制台就可以看到输出的log:        b)如果在真机上调试, ...

  10. java AES加密、解密(兼容windows和linux)

      java AES加密.解密 CreationTime--2018年7月14日10点06分 Author:Marydon 1.准备工作 updateTime--2018年8月10日15点28分 up ...