初探代理模式
实际上已经不是初探了,早在学习spring的时候就接触过代理模式,可惜当时没怎么搞明白动态代理,也没太重视这一块的知识,最近买了一本新书,里面恰好讲到了这一块的知识,于是,就认真学习了一遍,后面讲解原理的部分还没有学完,为了防止忘记,先将前面部分的偏应用的部分记录一下。
代理分为两种:静态代理和动态代理
我的理解就是所谓静态代理就是写死的代理,并不灵活,适应性比较差,但是,编写起来更为简单也更易于理解。
而动态代理从代码难易程度上来看要比静态代理复杂,但是,它的适应性也更好,更为灵活。
代理的作用是保护目标对象和增强目标对象。目标对象也就是实际被代理的对象,最终实际上还是调用了它,但不再是直接去调用它的方法,而是直接调用代理,由代理去调用目标对象的方法。
所以对目标对象起到保护作用这一点似乎并不难理解,记得我刚开始接触到代理模式的时候,总是不大能理解所谓的增强目标对象是什么意思?
经过这一次的学习,对于这个概念也有了一些体会,所谓的增强指的是在调用原本方法的前后增加其他的逻辑,使原本方法的功能更加强大。
比如这样:
public void 代理方法() { //记录日志 log(); //被代理对象的方法,查询xxx数据 xxx.select(); }
代理方法在调用原本对象的方法时,“动了些手脚”,这就是所谓的增强。
编写简单的动态代理:
首先,编写一个被代理对象的接口(这个必须有)
interface Client { public void song(); }
然后,被代理对象的实现类去实现这个接口
class ClientImpl implements Client { public void song() { System.out.println("----------"); } }
现在万事俱备,需要一个代理类来代理我们写好的被代理类
public class ProxyTest implements InvocationHandler { private Object any; public ProxyTest (Object any) { this.any = any; } private void before() { System.out.println("one day"); } private void after() { System.out.println("maybe she'll stay"); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object obj = method.invoke(this.any, args); after(); return obj; } }
步骤实际上并不复杂,首先需要实现InvocationHandler接口,然后提供一个传入被代理对象的方法,并且随你的心意去增强被代理对象的方法即可。
测试代码如下:
class Try { public static void main(String[] args) { Class<?> clazz = ClientImpl.class; Client clientPro = (Client)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new ProxyTest(new ClientImpl())); clientPro.song(); } }
想要代理哪个对象就将它的 .class 取出来,然后主方法内第二行略有些长的代码负责生成代理对象,其中传入的最后一个参数是实现了InvocationHandler接口的对象,我利用构造器将被代理对象一并传入。
然后调用代理的方法即可。
注意,此处代理对象的构造方法形参为object,所以不一定非要传入当前定义的被代理对象,也可以传入其他对象,也就是所谓的“动态”,before和after两个方法就是在原本方法的前后增加了新的逻辑,也就是所谓的“增强”。
但是,我觉得这样来实现最后还需要强转类型什么的,比较麻烦,所以,稍微简化了一下。
public class ProxyImprove<T> implements InvocationHandler { private T obj; public ProxyImprove() { } public T setObj(T obj) { this.obj = obj; Class clazz = obj.getClass(); return (T)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } private void before () { System.out.println("before"); } private void after () { System.out.println("after"); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object o = method.invoke(obj, args); after(); return o; } } class Test { public static void main(String[] args) { ProxyImprove<Client> improve = new ProxyImprove<>(); Client client = improve.setObj(new ClientImpl()); client.song(); } }
其实我觉得如果invoke中的逻辑相对固定的话,还可以这样封装一下(没测试过):
public interface MyHandler extends InvocationHandler { @Override default Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { before(); Object obj = method.invoke(get(), args); after(); return obj; } public void set(Object obj); public Object get(); public void before(); public void after(); }
由于前一段时间接触了一些安全框架,我突发奇想动态代理这东西能否用来build一个具备权限管理的项目?简单搜索了一下,貌似网上有人也用过这样的思路,等哪天有心情可能继续探索一下这部分内容。