深入浅出动态代理

简介: 介绍三种代理方式,以及他们的优缺点。

生活中处处能遇到代理,代理使得复杂的事情变得简单,在我们日常开发的过程中也常常使用到代理,例如spring的AOP,hibernate的事物等等,这些优秀的代理,让我们更专注于自己的业务本身,使开发变得简单。如果想要自己实现一个代理,进行权限验证、参数检验、事务处理等,有哪些方式呢?
下面介绍三种代理,以及他们的优缺点:
静态代理:

public class Proxy implements Foo{
    public Foo foo;//需要代理的接口
    public Proxy(Foo foo){
        this.foo = foo;
    }
    @Override
    public void func(String args){//代理处理的方法
        System.out.println("before method");
        this.foo.func(args);//执行原方法调用
        System.out.println("before method");
    }
}

优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
缺点:代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
JDK原生的动态代理:
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。
在运行时生成class,提供一组interface,则动态代理类就宣称它实现了这些interface。当然,动态代理类只是充当一个代理,你不要企图它会帮你干实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

public class MyInvocationHandler implements InvocationHandler{
    private Object target;//代理对象
    MyInvocationHandler(Object target){
        this.target = target;
    }
    //代理的处理集中在这里
    @Override
    public Object invoke(Object proxy,Method method,Objects[] args)throws Throwable{
    System.out.println("before method");
    Object result = method.invoke(target,args);
    System.out.println("after method");
    return result;
    }
}

public class Test{
    public static void main(String[] args)throws Exception{
        Foo impl = new FooImpl;//需要被代理的对象
        //hander
        InvocationHander hander = new MyInvocationHandler(impl);
        //创建代理对象
        Foo f = (Foo)Proxy.newProxyInstance(FooImpl.class.getClassLoader(),FooImpl.class.getInterFaces(),hander);
        f.func("sum");
    }
}

优点:动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器方法中(InvocationHandler.invoke)。这样在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。实际中可以类似Spring AOP那样配置业务。
缺点:诚然,Proxy 已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计注定了这个遗憾。动态生成的代理类,注定有一个共同的父类。
修改编译字节码:
有很多条理由,人们可以否定对 class 代理的必要性,但是同样有一些理由,相信支持 class 动态代理会更美好。接口和类的划分,本就不是很明显,只是到了 Java 中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。

public calss Test{
    public static void main(String[] args){
        //方法拦截器
        MethodInterceptor callback = new MethodInterceptor(){
            @Override
            public Object intercept(Object target,Method method,Object[] args,MethodProxy methodProxy)throws Throwable{
                System.out.println("before method");
                Object result = methodProxy.invokeSuper(target,args);
                System.out.println("after method");
                return result;
            }
        };
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(FooImpl.class);
        enhancer.setCallback(callback);
        FooImpl foo = (FooImpl)enhancer.create();
        foo.func("sum");
    }
}

修改编译字节码方式有一些开源的项目,比如CGLib、ASM等,CGLib是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。最流行的OR Mapping工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的)。EasyMock和jMock是通过使用模仿(mock)对象来测试java代码的包。它们都通过使用CGLib来为那些没有接口的类创建模仿(mock)对象。CGLib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLib包,脚本语言例如[Groovy和BeanShell],也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
最后说明一点,CGLib所创建的动态代理对象的性能比JDK所创建的代理对象性能高不少,大概10倍,但CGLib在创建代理对象时所花费的时间却比JDK动态代理多大概8倍,所以对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建新的实例,所以比较适合CGLib动态代理技术,反之则适用于JDK动态代理技术。另外,由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final,private等方法进行处理。所以,大家需要根据实际的情况选择使用什么样的代理了。

相关文章
|
设计模式 Java 数据安全/隐私保护
设计模式之代理模式(jdk和cglib、手撕源码、自创动态代理) 1
设计模式之代理模式(jdk和cglib、手撕源码、自创动态代理)
78 0
|
4月前
|
Java 开发者 Spring
什么是静态代理和动态代理,两者的区别(笔记)
什么是静态代理和动态代理,两者的区别(笔记)
189 0
|
11月前
|
监控 Java Spring
【面试题精讲】JDK动态代理
【面试题精讲】JDK动态代理
|
设计模式 缓存 SpringCloudAlibaba
浅析动态代理原理与实现
代理是一种软件设计模式,目的地希望能做到代码重用。具体上讲,代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法。这个就好比 商户---->明星经纪人(代理)---->明星这种模式。我们可以不通过直接与明星对话的情况下,而通过明星经纪人(代理)与其产生间接对话
136 0
浅析动态代理原理与实现
|
设计模式 Java Maven
设计模式之代理模式(jdk和cglib、手撕源码、自创动态代理) 2
设计模式之代理模式(jdk和cglib、手撕源码、自创动态代理)
53 0
|
设计模式 监控 Java
|
Java Spring
动态代理学习(二)JDK动态代理源码分析
动态代理学习(二)JDK动态代理源码分析
76 0
动态代理学习(二)JDK动态代理源码分析
|
缓存 Java
源码阅读 | 动态代理
源码阅读 | 动态代理
|
设计模式 Java
一文搞懂代理模式与动态代理
代理模式与动态代理
167 0
一文搞懂代理模式与动态代理