Spring AOP基础:代理模式

来源:互联网 发布:安卓手机java模拟器 编辑:程序博客网 时间:2024/06/15 01:02

代理模式

代理模式:在对已有方法进行改进时,不直接修改原有方法或代码,而是提供第三方。该第三方含有代理角色的引用,并根据需求增加相应的功能。

代理模式在程序设计中有非常重要的应用,AOP就是针对代理的一种应用。在生活中代理也无处不在,比如翻墙,比如开代理打韩服等等。
学习代理模式,先从简单的代码开始。

先定义一个简单的接口
public interface Hello{     void sayHello();}
再实现一个基本的Hello实现
public class HelloImpl implements Hello{    @override    public void sayHello{        System.out.println("Hello");    }}

如果要在println方法前分别需要一些处理逻辑,该怎么么做呢?如果是以前的我,我会直接将处理逻辑写死在在HelloImpl的sayHello方法中。但是这样的写法在大多数情况下是不妥的,就好像没学面向对象以前,将所有的方法逻辑写在一个类里面。这样的写法,会使得一个方法越来越长,处理逻辑的实现也会将类变得越来越庞大,而且这样处理逻辑可能与该类的其他方法无关。所以最好的方法是将这些处理逻辑交于代理处理。

对于我们的HelloImpl类,我们写一个HelloProxy类,让该类调用HelloImpl的sayHello方法,并在调用的前后进行相应的逻辑处理。
public class HelloProxy implements Hello{        Hello helloImpl;        HelloProxy(){            helloImpl=new HelloImpl();        }        @Override        public void sayHello(){            before();            helloImpl.sayHello();            after();        }        private void before(){            System.out.println("before");        }        public void after(){            System.out.println("after");        }}

在这段代码中,我们用HelloProxy实现Hello接口(和HelloImpl实现相同的接口),并且在构造方法中new出HelloImpl实例,则我们可以在HelloProxy的sayHello()方法中调用HelloImpl的sayHello()方法。这样的话,我们就可以在调用前后添加相应的before和after方法,并在这两个方法中去实现相应的前后处理逻辑。

我们再用main方法测试一下
    public static void main(String args[]){            HelloProxy helloProxy=new HelloProxy();            helloProxy.sayHello();    }
结果如下
beforehelloafter

这个HelloProxy就是简单的代理。这样的代理称为静态代理。所谓静态,就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
这样的静态代理几个缺点:
1. 如果要代理的方法很多,那么静态代理类的规模也势必很大。
2. 如果接口增加一个方法,那么所有代理类也必须实现此方法,如此就增加了代码维护的复杂度

为了解决静态代理的问题,我们使用JDK提供的动态代理方案DynamicProxy:

public class DynamicProxy  implements InvocationHandler{    private Object object;    public DynamicProxy(Object object) {        this.object=object;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        // TODO Auto-generated method stub        before();        Object result=method.invoke(object, args);        after();        return result;    }    private void before(){        System.out.println("before");    }    private void after(){        System.out.println("after");    }}

DynamicProxy实现了InvocationHandler接口,那么必须实现invoke方法,在invoke方法中,直接通过反射去调用被包装类的方法,在调用前后实现 分别处理before和after,最后将result返回。

在main中测试:
public static void main(String args[]){        Hello hello=new HelloImpl();        DynamicProxy dynamicProxy=new DynamicProxy(hello);        Hello helloProxy=(Hello) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), dynamicProxy);        helloProxy.sayHello();    }
结果如下:
beforehelloafter

在上述代码中,我们用这个通用的DynamicProxy类去包装HelloImpl实例,然后调用JDK给我们提供的Proxy类工厂方法newProxyInstance去动态的创建一个Hello接口的代理类,最后调用这个代理类的sayHello方法。结果和静态代理结果一样。
其实,动态代理就是动态生成XxxProxy。和静态代理相比,动态代理类的源码是在程序运行期间由JVM根据反射等机制动态生成的。
代理类和委托类的关系是在程序运行时确定的,所有如果需要在不同类的不同方法增加相同的处理逻辑,我们只需要一个动态代理类就可以实现,这就是动态代理的优点。
在main方法中我们可以看到,Proxy.newProxyInstance方法要有三个参数:
1. ClassLoader;
2. 该实现类的所有接口
3. 动态代理对象

在调用结束后还需要类型的强制转换。如果我们的动态代理需要代理多个对象,那么上述代码需要重复多次。

为了避免Proxy.newInstance方法出现多次(减少代码量和简化动态代理类的使用),我们将动态代理对象实例化部分代码封装起来(使用泛型):
    @SuppressWarnings("unchecked")    public <T> T getProxy(){                return (T)Proxy.newProxyInstance(object.getClass().getClassLoader(),                 object.getClass().getInterfaces(), this);    }
如此一来,我们的DynamicProxy的使用将更加方便,代码也看起来简洁清晰多了:
public static void main(String args[]){        DynamicProxy dynamicProxy=new DynamicProxy(new HelloImpl());        Hello helloProxy=dynamicProxy.getProxy();        helloProxy.sayHello();    }

除了减少代理类这个优点,相对于静态代理,动态代理在接口变化的时候,代理类不需要变化,而静态代理类则需要相对应的改动。
但是,我们发现,上述JDK动态代理只能代理有接口的类,如果需要代理的类没有接口,那么JDK动态代理就无用武之地。

为了解决代理没有接口的类的问题,我们使用开源的CGLIB类库。
public class CGLIBProxy implements  MethodInterceptor{    private static CGLIBProxy instance=new CGLIBProxy();    private CGLIBProxy() {    }    public static CGLIBProxy getInstance(){        return instance;    }    @SuppressWarnings("unchecked")    public <T> T getProxy(Class<T>cls){        return (T)Enhancer.create(cls, this);    }    @Override    public Object intercept(Object target, Method method, Object[] args,            MethodProxy proxy) throws Throwable {        before();        Object result=proxy.invokeSuper(target, args);        after();        return result;    }    private void before(){        System.out.println("before");    }    private void after(){        System.out.println("after");    }}

从上述代码可以看出,CGLIB类库的使用和JDK动态代理类似。但是CGLIB类库可以代理没有接口的类,且速度相较于JDK DynamicProxy更快。

测试使用及运行结果如下:
public class Greet {    public void say(String str){        System.out.println("hello"+str);    }    public static void main(String args[]){        Greet greetProxy=CGLIBProxy.getInstance().getProxy(Greet.class);        greetProxy.say("shan");    }}运行结果:beforehelloshanafter

至此,代理模式基本内容也就介绍完毕。AOP是基于JDK DynamicProxy和CGLIB代理,但是又不仅限于这些。要学好spring,AOP是重中之重。而代理又是AOP的基石,所以对代理理解并学以致用才是王道。

0 0
原创粉丝点击