java中的代理模式

来源:互联网 发布:网络电视4k是什么意思 编辑:程序博客网 时间:2024/05/19 22:56

前言

代理模式在日常开发中可能用的不多,但是在常见的框架中却经常看到他们的身影,像spring aop就是采用了jdk动态代理和cglib动态代理组合的方式来实现的面向切面编程,有人可能会觉得日常编码中又不会自己去写动态代理,也就不需要关心他就行了。其实我觉得spring框架中运用的这个动态代理,我们就应该好好去学学动态代理,只有自己理解了底层的原理,才能运用好spring aop.当然代理模式的思想可能还在其他地方所有运用,理解好代理模式对自己的编码肯定有所帮助,所以接下来和大家一起学习总结一下三种代理模式。

静态代理

什么叫做静态代理,他和动态代理又有什么区别,其实可以和JVM的静态内存分配和动态内存分配联系在一起学习,静态代理所代理的类在程序运行之前就已经写好存在了,正是因为被代理在程序运行之后已经存在,导致了静态代理后续的缺陷这些后面会详细解释。静态代理中代理类和被代理类一般要实现一个共同的接口,而且代理类中还要有被代理类的接口引用,具体看如下代码:

代理类和被代理类都需要实现的共同接口

package test.proxy;public interface StudentInterface {    public void study();}

被代理类,实现上面的抽象接口

package test.proxy;public class StudentInterfaceImpl implements StudentInterface {    @Override    public void study() {        // TODO Auto-generated method stub        System.out.println("开始学习!!!");    }}

代理类,实现上面的接口

package test.proxy;public class ProxyClass  implements  StudentInterface {    private StudentInterface studentInterface;    public ProxyClass(StudentInterface studentInterface){        this.studentInterface=studentInterface;    }    @Override    public void study() {        // TODO Auto-generated method stub        System.out.println("开始学习之前的准备工作");        studentInterface.study();        System.out.println("学习之后也要复习才行啊");    }}

客户端调用

package test.proxy;public class Client {    public static void main(String[] args) {        StudentInterface studentInterface=new StudentInterfaceImpl();        ProxyClass ProxyClass=new ProxyClass(studentInterface);        ProxyClass.study();    }}

运行结果如下
## 静态代理总结 ##
如上所示静态代理确实能够实现为被代理类添加相关其他操作,大致功能是实现,但是有一个问题,如果我很有很多的抽象接口,那岂不是我要写很多的代理的类,这些代理类中除了调用接口中的方法不一样,其他都是一样的,这样的重复代码其实是很没有必要的。那该怎么解决呢,接下来就是动态代理登场的时刻了!

JDK动态代理

jdk动态代理是jdk自带的一种代理模式,他是利用反射在程序运行之中动态的生成.class文件,来实现动态代理的,这种代理模式就可以避免上面静态代理的不足了,具体请见代码。
抽象接口

package test.proxy.jdkproxy;public interface StudentInterface {    public void study();}

被代理类

package test.proxy.jdkproxy;public class StudentInterfaceImpl implements StudentInterface {    @Override    public void study() {        // TODO Auto-generated method stub        System.out.println("开始学习!!!");    }}

jdk动态代理类

package test.proxy.jdkproxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class JDKProxyImpl implements  InvocationHandler{    private Object target;    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        // TODO Auto-generated method stub        Object result=null;        System.out.println("学习之前需要预习");        result= method.invoke(target, args);        System.out.println("学习之后需要复习");        return result;    }    public Object getProxyClass(Object target){        this.target=target;        return Proxy.newProxyInstance(target.getClass().getClassLoader(),                target.getClass().getInterfaces(), this);        //这里的this是指代实现了InvocationHandler接口的实现类      }}

客户端调用

package test.proxy.jdkproxy;public class Client {    public static void main(String[] args) {        JDKProxyImpl jDKProxyImpl = new JDKProxyImpl();        StudentInterface  studentInterface=(StudentInterface) jDKProxyImpl.                getProxyClass(new StudentInterfaceImpl());        studentInterface.study();    }}

运行结果
## jdk动态代理总结 ##
如上面所示,jdk代理的效果也是很好的,很多人可能会想这不是很好的解决了静态代理的问题了嘛。可是前面也提到过spring的aop是整合了jdk动态代理和cglib动态代理两种方式,如果jdk动态代理真的很完美,我相信spring不会大费周折的去cglib动态代理了,在上面的代码中大家可以看到,这里的被代理类StudentInterfaceImpl 实现了抽象接口StudentInterface。还有Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this);这里也是通过获取其对应的接口来生成被代理类对象的,所以利用jdk动态代理的生成代理对象的前提是被代理对象必须实现了一个或多个接口,这样才能够运用jdk动态代理。所以对于那些没有实现接口的类,该怎么处理呢???不急,不是还有cglib动态代理嘛!

cglib动态代理

cglib动态代理是通过开源的cglib库来实现动态代理的,他是通过asm框架来生成字节码文件来实现动态代理,具体代码如下
被代理类

package test.proxy.cglibproxy;public class StudentInterfaceImpl {    public void study(){        System.out.println("开始学习代码成为大牛!");    }}

cglib代理类

package test.proxy.cglibproxy;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class CglibProxyImpl implements MethodInterceptor {    private Object target;    //注意动态代理和静态代理的区别,这里的对象是不固定的,因为他是在代码运行时动态生成的    public Object getCglibProxy(Object target){        this.target=target;        Enhancer  enhancer = new Enhancer();        enhancer.setCallback(this);        enhancer.setSuperclass(this.target.getClass());        return enhancer.create();       }    @Override    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {        // TODO Auto-generated method stub        System.out.println("学习之前要提前预习!");        arg3.invokeSuper(arg0, arg2);        System.out.println("学习之后要及时的进行复习");        return null;    }}

调用客户端

package test.proxy.cglibproxy;public class Client {    public static void main(String[] args) {        CglibProxyImpl  cglibProxyImpl =new CglibProxyImpl();        StudentInterfaceImpl studentInterfaceImpl=(StudentInterfaceImpl) cglibProxyImpl.                getCglibProxy(new StudentInterfaceImpl());        studentInterfaceImpl.study();    }}

运行结果
## cglib动态代理分析 ##
如上所示,cglib动态动力避免被代理类必须实现接口的窘境,这里采用为被代理类生成子类的方式来实现动态代理,这样就会导致另外的问题,那就是对被final修饰的类和方法是不能被动态代理的。

总结

静态代理,jdk动态代理和cglib动态代理各有优势和不足,这也就解释了spirng的aop实现为什么是采用jdk动态代理和cglib动态代理整合的方式了,只有我们理解了这些代理模式,才能更好的理解和运用框架,后续我会和大家一起来总结一下sping中的aop具体是怎么实现的。

原创粉丝点击