代理模式
一、概念
1、定义
为其他对象提供一种代理以控制对这个对象的访问,在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
2、优点
- 真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事物。
- 代理对象可以在客户端和目标对象之间起到中介作用、
- 高扩展性
二、代理模式组成
在代理模式中,通常涉及到下面4中角色:
ISubject:该接口是对被访问者或被访问资源的抽象
SubjectImpl:被访问者的具体实现类
SubjectProxy:被访问者的代理实现类,SubjectProxy持有SubjectImpl的实例
Client:代表访问者的角色,Client将会访问ISubject类型的对象或资源,Client必须通过ISubject资源的访问代理类SubjectProxy进行
三、代理模式实现方式
1、静态代理
SubjectImpl和SubjectProxy都实现了相同的接口,而SubjectProxy内部持有SubjectImpl的引用,当Client通过request()请求服务的时候,SubjectProxy将请求转发给SubjectImpl。在将请求转发给被代理对象SubjectImpl之前或者之后,都可以根据情况插入其他处理逻辑,比如在转发之前记录方法执行开始时间,在转发之后记录结束时间。示例代码如下:
/* * ISubject角色 */ public interface Aithmetic { public int add(int i,int j); public int sub(int i,int j); }
/** * ISubjectImpl角色 */ public class AithmeticImpl implements Aithmetic { public int add(int i, int j) { int result=i+j; return result; } public int sub(int i, int j) { int result=i-j; return result; } }
/* * SubjectProxy角色 */ public class AithmeticProxy implements Aithmetic { private Aithmetic aithmetic; public AithmeticProxy(Aithmetic aithmetic) { this.aithmetic=aithmetic; } public int add(int i, int j) { //加入自己的逻辑 System.out.println("自定义逻辑开始"); int sum=aithmetic.add(i, j); return sum; } public int sub(int i, int j) { System.out.println("自定义逻辑开始"); int sub=aithmetic.sub(i, j); return sub; } }
/* * Client角色 */ public class AithmeticProxyTest { @Test public void test() { Aithmetic aithmetic=new AithmeticImpl(); AithmeticProxy aithmeticProxy=new AithmeticProxy(aithmetic); int sum=aithmeticProxy.add(2, 3); System.out.println(sum); } }
静态代理的缺点:针对不一样的目标对象类型,我们要为其单独实现一个代理对象,而实际上,这些代理对象所要添加的横切逻辑是一样的。
2、动态代理
我们利用动态代理机制,可以为指定的接口在系统运行期间动态的生成代理对象。
动态代理机制的实现主要由一个类和一个接口组成,即Java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler接口,下面给出示例为两种不同的类型提供代理对象。
假设现在有两个接口,都有request方法,我们想在request方法执行之前加入自己的处理逻辑。两个接口如下:
public interface Aithmetic { public int add(int i,int j); public int sub(int i,int j); public void request(); }
public interface IRequestable { public void request(); }
public class AithmeticImpl implements Aithmetic { public int add(int i, int j) { int result=i+j; return result; } public int sub(int i, int j) { int result=i-j; return result; } public void request() { System.out.println("执行AithmeticImpl的request函数"); } }
public class IRequestableImpl implements IRequestable { public void request() { System.out.println("执行IRequestableImpl的request函数"); } }
为了达到目的,我们需要实现一个InvocationHandler:
public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this.target=target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("request")) { System.out.println("加入自己的逻辑"); } return method.invoke(target, args); } }
然后就可以使用Proxy类,根据MyInvocationHandler的逻辑,为Aithmetic和IRequestable两种类型生成相应的代理对象实例:
public class MyInvocationHandlerTest { @Test public void test() { Aithmetic aithmetic=(Aithmetic) Proxy.newProxyInstance(MyInvocationHandlerTest.class.getClassLoader(), new Class[]{Aithmetic.class}, new MyInvocationHandler(new AithmeticImpl())); aithmetic.request(); System.out.println(aithmetic.add(2,3)); IRequestable iRequestable=(IRequestable) Proxy.newProxyInstance(MyInvocationHandlerTest.class.getClassLoader(), new Class[]{IRequestable.class}, new MyInvocationHandler(new IRequestableImpl())); iRequestable.request(); } }
执行结果为:
动态代理的缺点:动态代理的只能对实现了相应的接口的类使用,如果某个类没有实现任何接口,就无法使用 动态代理机制为其生成相应的动态代理对象。
3、动态字节码生成
原理:我们可以对目标对象进行继承扩展,为其生成相应的子类,而子类可以通过覆写来扩展父类的行为,只要将横切逻辑的实现放在子类中,然后让系统使用扩展后的目标对象的子类,就可以达到与代理模式相同的效果了。我们借助于CGLIB这样的动态字节码生成库,在系统运行期间为目标对象生成相应的扩展子类。
注:需要用到以下三个包
下面是示例代码:
首先编写需要代理的目标对象:
public class TargetObject { public int add(int i, int j) { int result = i + j; return result; } }
创建cglib代理的工厂类:
public class CglibProxy implements MethodInterceptor { //设置需要创建子类的类 private Object targetObject; public Object createProxyInstance(Object target) { this.targetObject = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.targetObject.getClass()); //设置回调函数 enhancer.setCallback(this); //通过字节码技术动态创建子类实例 return enhancer.create(); } public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { /*obj:目标类实例 *method:目标类法方法的反射对象 *args:方法的动态入参 *methodProxy:代理类实例 */ System.out.println("加入自己的逻辑"); //通过代理类调用父类中的方法 Object result = methodProxy.invoke(targetObject, args); System.out.println("操作结束"); return result; } }
测试:
public class CglibProxyTest { @Test public void test() { CglibProxy cglibProxy=new CglibProxy(); TargetObject t=(TargetObject) cglibProxy.createProxyInstance(new TargetObject()); System.out.println(t.add(2, 3)); } }
执行结果:
注:CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法不要声明成final。