系统架构设计——设计模式之代理模式(二)CGLIB动态代理实现

本文介绍了CGLIB动态代理的基本概念、原理和应用场景,详细解析了如何使用CGLIB为BookServiceBean生成动态代理对象,实现权限管理。通过代理对象在运行时重写被代理对象的方法,进行权限验证,探讨了动态代理在AOP和远程调用中的作用,同时也讨论了动态代理的优缺点和限制条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

像上一篇所说的代理模式其实是静态代理,在实际开发中其实应用不大,因为他需要事先知道被代理对象是谁,而且被代理对象和代理对象实现了公共的接口。实际情况往往并不能满足这些条件,我们往往在写代理模式的时候并不知道到时候被代理的对象是谁。解决办法就是——动态代理。以下我们将使用CGLIB实现动态代理

一、动态代理概述

程序在运行期而不是编译器,生成被代理对象的代理对象,并且被代理对象并不需要和代理对象实现共同的接口。基于此,我们可以利用代理对象,提供一种以控制对被代理对象的访问

1.1 动态代理的原理

基于CGLIB的动态代理,其实就是利用了CGLIB,在运行时期生成被代理对象的子类,来作为代理对象。同时重写了被代理对象的所有方法。当我们用代理对象调用方法的时候,其实是调用的重写后的方法。该方法实际调用的是callback中的用户自定义的代码逻辑,例如权限验证。当验证通过了,则通过反射,反射出父类(被代理对象)的方法并调用(invoke)
那么问题来了,如何生成被代理对象的子类呢?我们事先并不知道谁是代理对象啊?而且我们在重写调用自定义逻辑呢?

1.2 CGLIB动态代理的底层实现

CGLIB是一个强大的高性能的代码生成包。
1. 它广泛的被许多AOP的框架使用,例如:Spring AOP和dynaop,为他们提供方法的interception(拦截);
2. hibernate使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的);
3. EasyMock和jMock是通过使用模仿(moke)对象来测试Java代码的包。
它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。

CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM(Java字节码操控框架),来转换字节码并生成新的类。他其实就像JVM一样,可以加载一个指定的类。这样我们就可以实现在运行时期生成我们自定义的class了。下图为cglib与一些框架和语言的关系(CGLIB Library and ASM Bytecode Framework)
CGLIB结构

二、应用场景

  1. 远程代理:也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在不同地址空间的事实。例如:WebService、RPC、RMI(Remote Method Invocation)等。
  2. 虚拟代理:是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。例如:利用虚拟代理来优化页面的打开速度。
  3. 安全代理:用来控制真实对象访问时的权限。一般用户对象应该在不同访问权限的时候。
  4. 智能指引:是指当调用真实的对象时,代理处理另外一些事情。例如:Spring AOP、Hibernate等。

三、结构图

这里写图片描述

四、实际例子

4.1 需求:为自定义的BookServiceBean动态生成代理对象,实现权限管理,只允许姓名为张三的对象可以操作。

4.2 代码示例

被代理对象

package com.xiaocao.proxy.cglib;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class BookServiceBean {

    private Logger log = LogManager.getLogger();
    public BookServiceBean() {
        log.info("this is bookservicebean 的构造方法");
    }
    public void create() {
        System.out.println("create() is running!");
    }
    public void query() {
        System.out.println("query() is running!");
    }
}

代理类

package com.xiaocao.proxy.cglib;

import java.lang.reflect.Method;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class MyCglibProxy implements MethodInterceptor{
   
   

    private Logger log = LogManager.getLogger(MyCglibProxy.class);

    private String name;

    public MyCglibProxy(String name) {
        this.name = name;
    }

    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        log.info("调用的方法是:" + method.getName());
        log.info("实际调用者是: " + object.getClass());
        for (Object obj : objects) {
            log.info("方法参数类型为:" + obj.getClass());
        }

        if (!name.equals("张三")) {
            System.out.println("权限不够");
            return null;
        }
        Object result = methodProxy.invokeSuper(object, objects);
        System.out.println("这是方法后");
        return result;
    }
}

代理对象工厂

package com.xiaocao.proxy.cglib;

import net.sf.cglib.proxy.Enhancer;

public class BookServiceFactory {
   
   

    private BookServiceFactory() {

    }
    public static BookServiceBean getProxyInstance(MyCglibProxy myProxy) {
        Enhancer enhancer = new Enhancer();
        // 将Enhancer中的superclass属性赋值成BookServiceBean
        enhancer.setSuperclass(BookServiceBean.class);
        // 将Enhancer中的callbacks属性赋值成myProxy
        enhancer.setCallback(myProxy);
        return (BookServiceBean) enhancer.create();
    }
}

测试类

测试类的主题部分很简单,只是通过工厂方法得到代理对象,然后调用,即可通过姓名判断是否有权限执行目标方法。
之后我用了许多反射机制来验证我之前想法,包括
1. 得到的代理对象其实是被代理对象的子类
2. 代理对象重写了被代理对象的所有方法,我们调用的时候其实是调用的被代理对象的重写方法

具体代码如下:

package com.xiaocao
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值