手动实现一个迷你版的AOP(实战增强版)(上)

简介: 手动实现一个迷你版的AOP(实战增强版)(上)

在正式进行aop模块的介绍之前,我们需要先弄懂一些基本的术语概念。


在软件业,AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。


利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。


AOP可以用于解决什么问题?


代码复用,公共函数抽取,简化开发,业务之间的解耦;最典型的例子就是日志功能,因为日志功能往往横跨系统中的每个业务模块,使用 AOP 可以很好的将日志功能抽离出来。


目前已有的几款AOP框架技术:


  • AspectJ:基于源代码和字节码解包的一个编织器,用户需要使用的时候用到一门新的语言,因此Spring的Aop对AspectJ进行了一次封装。
  • AspectWerkz:AOP 框架,使用字节码动态编织器和 XML 配置
  • JBoss-AOP:基于拦截器和元数据的 AOP 框架,运行在 JBoss 应用服务器上,以及 AOP 中用到的一些相关技术实现
  • Javassist:Java 字节码操作类库,JBoss 的一个子项目


AOP基本概念


在介绍AOP技术之前,我们先来理清几个基本概念点:


Aspect(切面)


可以理解为是将业务代码抽出来的一个类,例如:


@Aspect
public class LogAspect {
  /** ... 这里面是相关方法的部分 省略大部分内容 ... **/
}


JoinPoint(连接点)


拦截点其实可以理解为下边这个参数:


image.png


Spring框架目前只支持方法级别的拦截,其实aop的连接点还可以有多种方式,例如说参数,构造函数等等。


PointCut(切入点)


可以理解为对各个连接点进行拦截对一个定义。


image.png


Advice(通知)


指拦截到连接点之后需要执行的代码。


分为了前置,后置,异常,环绕,最终


具体表现如下:


image.png


目标对象


代理的目标对象


织入(weave)


将切面应用到目标对象,并且导致代理对象创建的过程。weave是一个操作过程。


引入(introduction)


在不修改代码的前提下,引入可以在运行的时候动态天津一些方法或者字段。


Cglib如何实现接口调用的代理


首先我们定义一个基本的业务代码对象:


package org.idea.spring.aop.cglib;
/**
 * @Author linhao
 * @Date created in 3:56 下午 2021/5/6
 */
public class MyBis {
    void doBus(){
        System.out.println("this is do bis");
    }
}


接着是目标对象的拦截:


package org.idea.spring.aop.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
 * 整体的一个调用流程其实就是:
 * @Author linhao
 * @Date created in 3:57 下午 2021/5/6
 */
public class TargetInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("==== intercept before ====");
        //从代理实例的方法调用返回的值。
        Object result = methodProxy.invokeSuper(o,objects);
        System.out.println("==== intercept after ====");
        return result;
    }
}


最后是测试代码的执行。


package org.idea.spring.aop.cglib;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
/**
 * @Author linhao
 * @Date created in 3:59 下午 2021/5/6
 */
public class TestCglib {
    public static void main(String[] args) throws InterruptedException {
        //将cglib生成的字节码文件存放到这个目录下边,查看下会有什么东西
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"/Users/idea/IdeaProjects/framework-project/spring-framework/spring-core/spring-aop/cglib-class");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MyBis.class);
        enhancer.setCallback(new TargetInterceptor());
        MyBis myBis = (MyBis) enhancer.create();
        myBis.doBus();
    }
}


执行结果:


image.png


上边代码中的TargetInterceptor中的intercept方法会在目标函数被调用之后自动进行回调操作,从而实现代理调用的效果。


cglib和jdk代理


cglib的代理模式和jdk实现的代理技术本质还是会有较大差异性,jdk要求被代理对象需要实现jdk内部的InvocationHandler接口才能进行接口回调操作,但是cglib对是否实现接口方面没有强制要求,而且其性能也比JDK自带的代理要高效许多。


cglib代理的原理


关于cglib的原理我只能简单地介绍一下,仔细看了下里面的内容点实在是太多了,如果一下子深入挖掘容易掉进坑,所以这里打算用些大白话的方式来介绍好了。


cglib实现代理的基本思路


1.对被调用对象进行一层包装,并且对方法建立索引。


2.当调用目标方法的时候,通过索引值去寻找并调用函数。


这里面详细细节点可以参考这篇博客:


https://www.cnblogs.com/cruze/p/3865180.html


根据这篇博客介绍的思路,我自己也简单实现了一个cglib类似的代理工具。代码地址见文末


难点:


如何给方法建立索引?如何根据索引调用函数?


这里贴出我自己的一些思考和输出。


对调用对方法名称取出hashcode,然后通过switch关键字判断需要调用对函数名称:


image.png


使用起来差不多,不过很多细节方面没有做得特别完善:


image.png


使用cglib实现代理功能,主要目的就是希望在执行某些函数之前去调用某些方法。为了实现这种方式,其实借助反射也是可以达成目的的。但是反射在多次调用的时候性能开销比较大。cglib在这块所做的优化主要是对调用方法做了一次索引的包装,生产新的字节码,实现性能上的提升。


相关实现对代码仓库地址可以见文末。

相关文章
|
4月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
1月前
|
安全 Java 开发者
AOP中的JDK动态代理与CGLIB动态代理:深度解析与实战模拟
【11月更文挑战第21天】面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(cross-cutting concerns)与业务逻辑分离,以提高代码的可维护性和可重用性。在Java开发中,AOP的实现离不开动态代理技术,其中JDK动态代理和CGLIB动态代理是两种常用的方式。本文将从背景、历史、功能点、业务场景、底层逻辑等多个维度,深度解析这两种代理方式的区别,并通过Java示例进行模拟和比较。
71 4
|
4月前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
|
4月前
|
XML Java 数据库
Spring5入门到实战------10、操作术语解释--Aspectj注解开发实例。AOP切面编程的实际应用
这篇文章是Spring5框架的实战教程,详细解释了AOP的关键术语,包括连接点、切入点、通知、切面,并展示了如何使用AspectJ注解来开发AOP实例,包括切入点表达式的编写、增强方法的配置、代理对象的创建和优先级设置,以及如何通过注解方式实现完全的AOP配置。
|
7月前
|
存储 消息中间件 Java
Java多线程实战-异步操作日志记录解决方案(AOP+注解+多线程)
Java多线程实战-异步操作日志记录解决方案(AOP+注解+多线程)
|
7月前
|
安全 前端开发 Java
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)3
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)
129 0
|
7月前
|
Java 数据库连接 API
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)2
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)
99 0
|
7月前
|
存储 Java 数据库连接
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)1
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)
94 0
|
7月前
|
前端开发 Java 数据库
【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换&读写分离】—— 案例实战(下)
【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换&读写分离】—— 案例实战(下)
122 0
|
7月前
|
Java 数据库连接 数据库
【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换&读写分离】—— 案例实战(中)
【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换&读写分离】—— 案例实战(中)
367 0