spring学习笔记(7)AOP前夕[2]CGLib动态代理实例解析

简介: <div class="markdown_views"><h1 id="cglib动态代理基本原理">CGLib动态代理基本原理</h1><p>CGLib——Code Generation Library,它是一个动态字节代码生成库,基于asm。使用CGLib时需要导入asm相关的jar包。而asm又是何方神圣?</p><blockquote> <p>asm是一个

CGLib动态代理基本原理

CGLib——Code Generation Library,它是一个动态字节代码生成库,基于asm。使用CGLib时需要导入asm相关的jar包。而asm又是何方神圣?

asm是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
了解asm的功能原理,有助于更好地理解我们的CGLib,但在本节不会细究asm。我们的主要关注点还是如何使用CGLib实现AOP。为我们后面分析Spring AOP铺垫。

在上一节中,我们用JDK的动态代理来模拟实现了一个性能监控的例子,用到了JDK内置的反射技术和java.lang.reflect.Proxy代理类,通过例子我们发现,它只能通过让被代理类实现代理接口的方式来生成代理,而CGLib的区别在于通过在程序运行时动态生成一个被代理类的子类的方式来完成代理。它有几个核心类:
1. Enhancer 它用于动态生成被代理的类的子类。使用此类生成子类的前奏是指定被代理类和指定CallBack接口
2. CallBack:它是一个很关键的接口,我们常常通过CallBack接口来配置我们的拦截方法,
3. MethodInterceptor:是CallBack的实现类,他会拦截我们被代理类的所有方法,来实现自己的增强细节。比如做点日志记录,方法处理等,处理完后,还能通过MethodProxy重新调用拦截掉的方法。
4. MethodProxy:主要用于重新调用MethodInterceptor拦截掉的方法,是jdk反射包中Method的代理类。
5. CallbackFilter:一个Enhancer生成类可以指定多个Callback,这样我们可以设定条件过滤,让被代理类中不同的方法被调用时使用不同的CallBack来进行处理。

实例导入,需求分析

在上篇文章的例子基础上,我们为我们“老类”的每个方法(例子中有method1、method2、method3三个方法)都实现了耗时统计,但现在,对于method3,因为它经常被用户调用,每次被调用都统计耗时会对性能造成一定影响,因此,现在需要过滤掉对method3的的耗时统计,而且我们还想对其进行日志记录,看看哪些用户什么时候调用了这个方法
现在,结合前面提到的核心类,我们通过CGLib来完成这一轮新需求

源码实例展示

1. 定义被代理对象

我们的被代理对象:OldClass当然是不(能)变的啦。

public class OldClass {
    public void method1() throws InterruptedException{
        System.out.println("正在处理业务逻辑1");
        Thread.sleep(100);//模拟处理业务逻辑1过程
        System.out.println("业务逻辑1处理完成");
    }
    public void method2() throws InterruptedException{
        System.out.println("正在处理业务逻辑2");
        Thread.sleep(200);//模拟处理业务逻辑2过程
        System.out.println("业务逻辑2处理完成");
    }
    public void method3(String userName) throws InterruptedException{
        System.out.println("正在处理业务逻辑3");
        Thread.sleep(300);//模拟处理业务逻辑3过程
        System.out.println("业务逻辑3处理完成");
    }
    //下面还有很多很多。。
}

2. 定义代理生成工厂

public class ProxyFactory {
    private Enhancer enhancer = new Enhancer();//动态的类生成器
    public Object createSubObject(Class<?> clazz){
        enhancer.setSuperclass(clazz);//设置需要创建的类,这个类的父类是clazz类
        //当通过enhancer创建的类中的方法被调用时,该方法会被CallBack指定的对象拦截。
        enhancer.setCallbacks(new Callback[]{new MyTimeInterceptor(),new MyRecordInterceptor()});
        enhancer.setCallbackFilter(new MyCallBackFilter());//设置我们自定义的过滤器
        return enhancer.create();//通过字节码技术动态创建子类实例
    }
}

3. 定义增强拦截器

下面定义我们的两个CallBack实现类,一个负责拦截需要统计耗时的方法,另一个拦截需要进行日志记录的方法

1. 耗时统计拦截器

public class MyTimeInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object target, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {//拦截所有父类方法的调用
        Long beginTime = System.currentTimeMillis();//记录开始时间
        //调用目标对象的方法,同时获取该方法的返回值,作为我们本代理方法(invoke)的返回值
        Object returnValue = proxy.invokeSuper(target, args);//target为我们方法所在的目标类,args为方法参数
        System.out.println("方法" + method.getName() + "调用结束,耗时"+ (System.currentTimeMillis() - beginTime));
        return returnValue;
    }
}

2. 日志记录拦截器

public class MyRecordInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object target, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {// 拦截所有父类方法的调用
        System.out.println(args[0] + "在"
                + new SimpleDateFormat("yyyy-MM-dd HH-mm").format(new Date())
                + "调用了方法" + method.getName());
        Object returnValue = proxy.invokeSuper(target, args);// target为我们方法所在的目标类,args为方法参数
        return returnValue;
    }

}

4. 定义我们的拦截过滤器

public class MyCallBackFilter implements CallbackFilter{//需要实现特定接口

    @Override
    public int accept(Method method) {
        if("method3" .equals(method.getName())){//如果被拦截的方法名满足特定条件
            //这里的序号对应于enhancer.setCallbacks(new Callback[]{new MyTimeInterceptor(),new MyRecordInterceptor()})中的Callback数组的拦截器索引
            return 1;
        }else{
            return 0;
        }
    }
}

5. 测试方法

public static void main(String args[]) throws InterruptedException{
    ProxyFactory cgLibProxy = new ProxyFactory();//创建我们的代理类
    //通过enhancer创建我们的子类
    //因为oldClass是我们子类的父类,所以这里向上转型成功
    OldClass oldClass = (OldClass) cgLibProxy.createSubObject(OldClass.class);
    oldClass.method1();//调用方法
    oldClass.method2();//调用方法
    oldClass.method3("zenghao");//调用方法
}

6. 结果分析

运行5中的测试方法,控制台打印:

正在处理业务逻辑1
业务逻辑1处理完成
方法method1调用结束,耗时116
正在处理业务逻辑2
业务逻辑2处理完成
方法method2调用结束,耗时201
zenghao在2016-03-24 18-32调用了方法method3
正在处理业务逻辑3
业务逻辑3处理完成

在这里,我们的method1和method2还是被拦截下来统计耗时,但我们的method3就在调用前被做了日志记录了。

小结

使用CGlib来通过生成子类来完成代理,这样,我们就不用强迫我们的被代理类实现代理接口了,侵入性更低。而且,使用CGLib还能为我们的拦截方法实现智能过滤,相对于使用JDK的动态代理,还是优雅了很多。
但在实际的应用场景中,如果我们每次使用AOP,都要进行如上所示一堆配置,还是挺繁琐的,但如果我们把配置的工作,交给spring完成,那么我们只要通过简洁的配置,就能轻松实现我们的动态代理。甚至结合上spring的许多特性,我们的代理功能还会更加的灵活强大。
通过对JDK和CGLib动态代理的实例理解,我们对AOP有一个更全面而感性地认识,从下篇文章我们开始进入springAOP部分的学习分析。

源码下载

本篇博文源码可到https://github.com/jeanhao/spring的CGLibProxy文件下载。

目录
相关文章
|
10月前
|
存储 Java 文件存储
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— logback.xml 配置文件解析
本文解析了 `logback.xml` 配置文件的详细内容,包括日志输出格式、存储路径、控制台输出及日志级别等关键配置。通过定义 `LOG_PATTERN` 和 `FILE_PATH`,设置日志格式与存储路径;利用 `&lt;appender&gt;` 节点配置控制台和文件输出,支持日志滚动策略(如文件大小限制和保存时长);最后通过 `&lt;logger&gt;` 和 `&lt;root&gt;` 定义日志级别与输出方式。此配置适用于精细化管理日志输出,满足不同场景需求。
2377 1
|
5月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
11月前
|
XML 安全 Java
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
484 24
|
12月前
|
XML Java 开发者
Spring底层架构核心概念解析
理解 Spring 框架的核心概念对于开发和维护 Spring 应用程序至关重要。IOC 和 AOP 是其两个关键特性,通过依赖注入和面向切面编程实现了高效的模块化和松耦合设计。Spring 容器管理着 Beans 的生命周期和配置,而核心模块为各种应用场景提供了丰富的功能支持。通过全面掌握这些核心概念,开发者可以更加高效地利用 Spring 框架开发企业级应用。
375 18
|
11月前
|
传感器 监控 安全
智慧工地云平台的技术架构解析:微服务+Spring Cloud如何支撑海量数据?
慧工地解决方案依托AI、物联网和BIM技术,实现对施工现场的全方位、立体化管理。通过规范施工、减少安全隐患、节省人力、降低运营成本,提升工地管理的安全性、效率和精益度。该方案适用于大型建筑、基础设施、房地产开发等场景,具备微服务架构、大数据与AI分析、物联网设备联网、多端协同等创新点,推动建筑行业向数字化、智能化转型。未来将融合5G、区块链等技术,助力智慧城市建设。
557 1
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
1306 2
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
364 2
|
前端开发 Java Maven
深入解析:如何用 Spring Boot 实现分页和排序
深入解析:如何用 Spring Boot 实现分页和排序
1071 2
|
前端开发 Java 开发者
Spring MVC中的控制器:@Controller注解全解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序控制层的核心。它不仅简化了控制器的定义,还提供了灵活的请求映射和处理机制。本文将深入探讨`@Controller`注解的用法、特点以及在实际开发中的应用。
980 0

推荐镜像

更多
  • DNS