本文共 4768 字,大约阅读时间需要 15 分钟。
定义:为其他对象提供一种代理机制,以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。-《设计模式》
在代理模式下,有一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理对象实现同一个接口,先访问代理类再访问真正要访问的对象。
代理模式分为静态代理、动态代理
//主题接口,后续类需要实现此接口public interface Subject { public void doSomething();}
//实现上述接口的具体类,表示目标对象public class RealSubject implements Subject { public void doSomething() { System.out.println("这是真正的对象"); }}
//代理类,如上文所述,需要与目标类实现相同的接口public class ProxySubject implements Subject { //包含真正目标对象作为成员 private Subject subject; public ProxySubject(Subject subject) { this.subject = subject; } //通过访问代理类实现对目标对象的访问 public void doSomething() { System.out.println("这是一个代理对象"); this.subject.doSomething(); }}
public class ProxyTest { public static void main(String[] args){ //根据真实对象创建一个代理对象 ProxySubject proxy = new ProxySubject(new RealSubject()); //通过访问代理对象实现对真实对象的访问 proxy.doSomething(); }}
运行结果:
优点:代理类接受一个Subject接口的对象,任何实现该接口的对象,都可以通过代理类进行代理,增加了通用性。
缺点:代理对象和真实对象高度耦合,每一个代理类都必须实现一遍委托类的接口,如果接口增加方法,则代理类也必须跟着修改;其次,代理类每一个接口对象对应一个委托对象,如果委托对象非常多,则静态代理类就非常臃肿,难以胜任。
jdk动态代理
1.通过实现InvocationHandler接口来自定义自己的InvocationHandler;
2.通过Proxy.getProxyClass获得动态代理类 3.通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class) 4.通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入 5.通过代理对象调用目标方法//实现主题接口的目标类public class ActionImpl_01 implements ActionInf{ public void doSomething(String word) { System.out.println(this.getClass().getName()+"::"+"Hello "+word); }}
//实现InvocationHandler接口public class InvocationHandlerImpl implements InvocationHandler{ private ActionInf object; public InvocationHandlerImpl(ActionInf object) { this.object = object; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("======before invoke======"); System.out.println(proxy.getClass().getName()); Object res = method.invoke(this.object, args); System.out.println("======after invoke======"); return res; } public Object getProxyObject(){ return Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), this.object.getClass().getInterfaces(), this ); }}
public class DynamicProxyTest { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { ActionImpl_01 a1 = new ActionImpl_01(); //通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入 InvocationHandler handler1 = new InvocationHandlerImpl(a1); //通过Proxy.getProxyClass获得动态代理类 Class proxyClass = Proxy.getProxyClass(ActionInf.class.getClassLoader(), ActionInf.class); //通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class) Constructor constructor = proxyClass.getConstructor(InvocationHandler.class); //通过代理对象调用目标方法 ActionInf inf = (ActionInf)constructor.newInstance(handler1); inf.doSomething("world"); }}
当然,上述步骤中的2、3、4步也可通过调用Proxy.newProxyInstance()一步完成,读者可以自行查看此方法的实现过程。
上述过程中,我们并没有静态构造实现Subject主题接口的代理类,而是在运行过程中生成了一个名为com.sun.proxy.$Proxy0的代理类,此类运行时生成,结束后销毁,达到了动态的标准。
cglib动态代理
//实现cglib动态代理的关键,就是要实现MethodInterceptor(方法拦截器接口)public class CglibProxyImpl implements MethodInterceptor { private ActionInf object; public CglibProxyImpl(ActionInf object) { this.object = object; } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("======before invoke======"); ActionInf res = (ActionInf) methodProxy.invokeSuper(o, objects); System.out.println("======after invoke======"); return res; } public ActionInf getProxyObject(){ Enhancer enhancer = new Enhancer(); //设置enhancer对象的父类 enhancer.setSuperclass(this.object.getClass()); //设置enhancer的回调对象 enhancer.setCallback(this); //创建代理对象 return (ActionInf)enhancer.create(); }}
public class DynamicProxyTest { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { ActionImpl_01 a1 = new ActionImpl_01(); CglibProxyImpl cg = new CglibProxyImpl(a1); ActionInf inf = cg.getProxyObject(); inf.doSomething("World"); }}
可以看出,cglib通过继承方式基于目标类动态生成一个子类,那很明显一点,cglib不能动态代理final修饰的方法,这点与jdk动态代理有较大差别。