设计模式之动态代理

在人生的道路上,不管是潇洒走一回,或者是千山独行,皆须是自己想走的路,虽然,有的人并不是很快就能找到自己的方向和道路,不过,只要坚持到底,我相信,就一定可以找到自己的路,只要找到路,就不必怕路途遥远了。

导读:本篇文章讲解 设计模式之动态代理,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

9.动态代理

9.1 介绍
  • JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。动态代理是动态地在内存中构建代理对象,从而实现对目标对象的代理功能,接口增加方法时代理对象不受影响,但性能会降一个数量级
  • 框架核心底层反射机制
  • 动态代理和静态代理角色一样
  • 动态代理相当于无侵入式拓展了功能
9.2 分类
  • 动态代理分为2类:基于接口的动态代理和基于类的动态代理

基于接口:jdk的动态代理。

JDK的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口

基于类:cglib的动态代理

cglib是一个强大的高性能的代码生成包、类库,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP,为他们提供方法的interception(拦截),Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成

CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。

关于java字节码的处理,有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。

基于字节码:javassist

开源分析,编辑,创建java字节码的类库为JBoss实现动态AOP框架

不需要跟虚拟机指令打交道直接使用java编码形式动态改变类结构和生成类

9.3 JDK的动态代理实现
  • Proxy : 提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

  • InvocationHandler:由代理实例的调用处理程序实现的接口 。 每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

  • 在动态代理技术里,由于不管用户调用代理对象的什么方法,都是调用开发人员编写的处理器的invoke方法(这相当于invoke方法拦截到了代理对象的方法调用)

9.4 举例理解(JDK的动态代理)
  • 中介抽象类
//中介抽象类
public interface Rent {
    public void rentFunction();
}
  • 真实对象
//代理对象
//要代理的真实对象,车主去实现租借接口
public class Owner implements Rent {
    @Override
    public void rentFunction() {
        System.out.println("车主出租车");
    }
}
  • 调用处理程序-动态代理工具类模板。
  • **执行机制:**跟静态代理一样,目标对象和代理类都要实现目标类的抽象借口,注意ObjInvocationHandler是调用处理程序,由它去创建代理类,通过Proxy.newProxyInstance方法创建一个实现了 target.getClass().getInterfaces()目标对象接口的代理类
/**
 * 代理实例关联的“调用处理程序”,实现的InvocationHandler接口,重写 invoke方法
 * 调用处理程序生成代理
 * 本质使用反射机制,返回代理类和代理方法
 */
public class ObjInvocationHandler implements InvocationHandler {

    /**
    * 被代理的对象类或接口
    */
    private Object target;

    public ObjInvocationHandler(Object target) {
        this.target = target;
    }

    //获得代理类
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }
    /**
     * 处理代理实例上的方法调用并返回结果。 
     * 当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法调用前!");
        beforeFunction(method.getName());
        //反射对象的方法是privite,则需要invoke来激活
        //操作反射对象的方法是privite属性,需要关闭程序的安全检查,属性或方法的setAccessible(true)
        //利用反射机制,实现目标对象的方法调用
        Object result = method.invoke(target, args);
        System.out.println("方法调用后!");
        afterFunction();
        return result;
    }

    public static void beforeFunction(String msg) {

        System.out.println("租借前行为,执行了"+msg+"方法");
    }
    public static void afterFunction() {

        System.out.println("租借后行为");
    }
}
  • 测试
@Test
public void testProxy01(){
    //真实角色
    Rent target = new Owner();
    //代理角色
    //方式1:动态生成代理实例
    Rent proxyInstance = (Rent)Proxy.newProxyInstance(target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new ObjInvocationHandler(target));
    //执行方法
    proxyInstance.rentFunction();
    System.out.println("方式1=======================方式2");
    ObjInvocationHandler objInvocationHandler = new ObjInvocationHandler(target);
    //方式2:动态生成代理实例(将产生代理实例封装为一个方法)
    Rent proxyInstance1 = (Rent)objInvocationHandler.getProxyInstance();
    proxyInstance1.rentFunction();
}
  • 结果
方法调用前!
租借前行为,执行了rentFunction方法
车主出租车
方法调用后!
租借后行为
方式1=======================方式2
方法调用前!
租借前行为,执行了rentFunction方法
车主出租车
方法调用后!
租借后行为

Process finished with exit code 0
9.5 举例理解(cglib的动态代理)
  • JDK动态代理还是需要被代理对象实现特定的接口,为了解决这一缺陷,可使用cglib动态代理的方式解决,Cglib是继承了被代理对象。
  • 倒入依赖cglib、asm
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm</artifactId>
    <version>9.2</version>
</dependency>
  • 目标对象
//目标对象
//要代理的真实对象,车主去实现租借接口
public class Owner {
    public void rentFunction() {
        System.out.println("车主出租车");
    }
}
  • Cglib动态代理工具类
public class ObjInterceptor implements MethodInterceptor {

    /**获取代理对象,参数为目标对象的Class类对象*/
    public Object getProxyInstance(Class target){
        //实现方式1:
        /* Object result = Enhancer.create(target, this);
        return  result;*/
        //实现方式2
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    /**参数分别为代理对象,目标对象方法,方法参数,代理对象方法*/
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        beforeFunction(methodProxy.getSuperName());
        //利用反射机制,使用代理对象方法,传入的是代理对象和方法参数
        Object invoke = methodProxy.invokeSuper(o, objects);
        return invoke;
    }

    public static void beforeFunction(String msg) {

        System.out.println("租借前行为,执行了"+msg+"方法");
    }
    public static void afterFunction() {

        System.out.println("租借后行为");
    }
}
  • 测试
    @Test
    public void testProxyCglib(){
        Owner owner = new Owner();
        //调用方式1:
        System.out.println("方式1");
        Owner proxyInstance1 = (Owner)Enhancer.create(Owner.class, new ObjInterceptor());
        proxyInstance1.rentFunction();

        //调用方式2
        System.out.println("方式2");
        ObjInterceptor objInterceptor = new ObjInterceptor();
        Owner proxyInstance2 = (Owner)objInterceptor.getProxyInstance(owner.getClass());
        proxyInstance2.rentFunction();
    }
}


  • 结果
方式1
租借前行为,执行了CGLIB$rentFunction$0方法
车主出租车
方式2
租借前行为,执行了CGLIB$rentFunction$0方法
车主出租车
  • 注意:上面工具类invokeSuper()是使用代理对象方法,传入的是代理对象和方法参数 。若想利用反射机制,实现目标对象的方法调用invoke()方法需要变动一下代理工具类和调用方式
public class ObjInterceptor2 implements MethodInterceptor {
    /**
     * 被代理的对象类或接口
     */
    private Object target;

    public ObjInterceptor2(Object target) {
        this.target = target;
    }

    /**获取代理对象,参数为目标对象的Class类对象*/
    public Object getProxyInstance(){
        //实现方式1:
        /* Object result = Enhancer.create(target, this);
        return  result;*/
        //实现方式2
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
    /**参数分别为代理对象,目标对象方法,方法参数,代理对象方法*/
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        beforeFunction(methodProxy.getSuperName());
        //方式3.利用反射机制,实现目标对象的方法调用
        //实现和JDK形式一样
        Object result = method.invoke(target, objects);
        return result;
    }

    public static void beforeFunction(String msg) {

        System.out.println("租借前行为,执行了"+msg+"方法");
    }
    public static void afterFunction() {

        System.out.println("租借后行为");
    }
}
  • 调用方式变化
@Test
public void testProxyCglib(){
	Owner owner = new Owner();
    //实现目标对象的方法调用
    System.out.println("实现方式3");
    ObjInterceptor2 objInterceptor2 = new ObjInterceptor2(owner);
    Owner proxyInstance3 = (Owner)objInterceptor2.getProxyInstance();
    proxyInstance3.rentFunction();
}
  • 结果
实现方式3
租借前行为,执行了CGLIB$rentFunction$0方法
车主出租车
9.6 JDK和Cglib动态代理区别

JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。

JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。

JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。

9.7 优缺点
  • 优点

可以使真实角色的操作更加纯粹,不去关注一些公共的业务

公共的业务交给代理角色,实现了业务分工,降低耦合性

代理角色扩展的公共业务,更容易集中管理

一个动态代理类代理的是一个接口,一般是对应的一类业务

一个动态代理类可以代理多个类,只要是实现了同一个接口即可

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/123926.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!