生活中顾客一般不会从厂家直接买东西,而是中间销售商做代理,从厂家拿到货,再买到顾客手中,而软件中也有代理模式,常见的UML示意图如下:
需要注意的是一下几点
1、用户只关心接口功能,而不在乎谁提供了功能。上图中接口是 Subject。
2、接口真正实现者是上图的 RealSubject,但是它不与用户直接接触,而是通过代理。
3、代理就是上图中的 Proxy,由于它实现了 Subject 接口,所以它能够直接与用户接触。
4、用户调用 Proxy 的时候,Proxy 内部调用了 RealSubject。所以,Proxy 是中介者,它可以增强 RealSubject 操作
下面用示例详细说明。代理可以分为静态代理和动态代理。我们先从静态代理说起
静态代理从我们平时的看电影举例。电影是电影公司委托给电影院播放的。 但是电影院播放电影时,可以买可乐爆米花,也可以再电影前后添加一些广告,用来增加收益。接下来我们用代码模拟一下
首先是创建一个接口,通用接口是代理模式实现的基础。我们创建一个命名为 Movie ,代表电影院的播放能力
package com.cc.user.myProxy;
public interface Movie {
public void play();
}
然后我们创建一个接口的实现类,调用 play() 时,表示开始播放电影了。
package com.cc.user.myProxy;
public class CcMovie implements Cinema{
@Override
public void play() {
System.out.println("Cc正在看犬夜叉");
}
}
那么代理类呢,下面我们创建一个 Cinema 代理类,让他可以播广告,买爆米花,在调用 paly() 前后我们进行了一些买东西的操作
package com.cc.user.myProxy;
public class Cinema implements Movie {
private CcMovie ccMovie;
public Cinema(CcMovie ccMovie) {
this.ccMovie = ccMovie;
}
@Override
public void play() {
this.attach("买可乐爆米花了");
ccMovie.play();
this.attach("买矿泉水啦");
}
public void attach(String msg){
System.out.println(msg);
}
}
再写一个测试类进行测试
package com.cc.user.myProxy;
public class Test {
public static void main(String[] args) {
CcMovie ccMovie = new CcMovie();
Cinema cinema = new Cinema(ccMovie);
cinema.play();
}
}
控制台输出:
可以看出代理模式在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加和增强。同时要注意,代理类和被代理类需要实现同一接口或者共同继承某类。因为类型等都是已经确定好的所以称为静态代理,下面我们进行动态代理的梳理,动态梳理分为两种,一种基于 JDK 的动态代理,另一种是基于 CGLIB 的动态代理
基于JDK的动态代理动态代理相比较静态代理,可以在程序运行中自动在内存中创建一个实现 Movie 接口的代理。不用去定义 Cimema 类,因此被称为动态代理
我们以商场柜台做示例进行代码演示。
首先我们创建一个接口,可以理解为买东西的许可
package com.cc.user.myProxy;
public interface Licence {
public void sell();
}
再创建一个类,maikele 意思就是买可乐
package com.cc.user.myProxy;
public class Maikele implements Licence {
@Override
public void sell() {
System.out.println("我卖可乐");
}
}
然后我们还需要一个柜台来卖,代码如下:
package com.cc.user.myProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class GuiTaiA implements InvocationHandler {
private Object huowu;
public GuiTaiA(Object huowu) {
this.huowu = huowu;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("销售开始,柜台:" + this.getClass().getSimpleName());
method.invoke(huowu, args);
System.out.println("卖出去啦");
return null;
}
}
然后我们就可以开始往外卖东西了
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
Licence licence = new Maikele();
InvocationHandler xiaoshou = new GuiTaiA(licence);
Licence imp = (Licence) Proxy.newProxyInstance(Maikele.class.getClassLoader(), Maikele.class.getInterfaces(), xiaoshou);
imp.sell();
}
}
控制台输出:
JDK 动态代理 如示例所示需要知道两个类 1.InvocationHandler(接口)、2.Proxy(类)
GuiTaiA 并没有像静态代理那样实现一样的接口,而是实现了 InvocationHandler 接口。
Proxy:动态代理涉及的另外一个主要的类就是 Proxy。正是通过 Proxy 的静态方法 newProxyInstance 创建的动态代理
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
参数详解:
loader : 类加载器
interfaces :代码要用来代理处理的接口
h :一个 InvocationHandler 对象
InvocationHandler :是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。 内部只是一个 invoke() 方法。
参数详解:
proxy :代理对象
method :代理对象的调用方法
args :调用方法中的参数
Proxy 动态产生的代理会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。
我们进一步演示。此时柜台A 我们不只让他卖可乐,还让他卖瓜子,啤酒,火腿肠
示例如下:
接口:
package com.cc.user.myProxy;
public interface OtherHuoWo {
public void otherSell();
}
实现类
package com.cc.user.myProxy;
public class MaiOther implements OtherHuoWo {
@Override
public void otherSell() {
System.out.println("瓜子、啤酒、火腿肠啦");
}
}
测试:
package com.cc.user.myProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
Licence licence = new Maikele();
InvocationHandler xiaoshou = new GuiTaiA(licence);
Licence imp = (Licence) Proxy.newProxyInstance(Maikele.class.getClassLoader(), Maikele.class.getInterfaces(), xiaoshou);
imp.sell();
OtherHuoWo otherHuoWo = new MaiOther();
GuiTaiA guiTaiA = new GuiTaiA(otherHuoWo);
OtherHuoWo o = (OtherHuoWo) Proxy.newProxyInstance(MaiOther.class.getClassLoader(), MaiOther.class.getInterfaces(), guiTaiA);
o.otherSell();
}
}
控制台输出:
GuiTaiA 类我们没有经过任何处理。此时我们就可以很容易看出,GuiTaiA 利用反射可以代理多种类。spring 中的 aop 就是利用动态代理来实现的。下面我们梳理另一种代理方式。
基于CGLIB的动态代理cglib:Code Generation Library,是一个强大的,高性能,高质量的Code生成类库,也是一个开源项目
首先我们引入CGLIB的jar包
cglib cglib3.3.0
我们再创建一个目标类
package com.cc.user.myCglib;
public class Maijiu {
public void sell(){
System.out.println("我是卖酒的");
}
}
实现 MethodInterceptor接口我们生成代理类
package com.cc.user.myCglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class GuiTaiB implements MethodInterceptor {
//维护目标对象
private Class object;
public GuiTaiB(Class object) {
this.object = object;
}
//给目标对象创建代理对象
public Object getProxyInstance(){
//工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(object);
//设置回调函数
enhancer.setCallback(this);
//创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("销售开始,柜台" + this.getClass().getSimpleName());
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("卖出去啦");
return o1;
}
public GuiTaiB() {
}
}
这个接口只有一个intercept()方法,这个方法有4个参数:
1)Object 表示增强的对象,即实现这个接口类的一个对象;
2)method 表示要被拦截的方法;
3)objects 表示要被拦截方法的参数;
4)methodProxy表示要触发父类的方法对象
测试类:
package com.cc.user.myCglib;
import net.sf.cglib.proxy.Enhancer;
public class Test {
public static void main(String[] args) {
GuiTaiB guiTaiB = new GuiTaiB(Maijiu.class);
Maijiu proxy = (Maijiu) guiTaiB.getProxyInstance();
proxy.sell();
}
}
控制台输出:



