Spring 源码解读:手动实现AOP的基础功能

引言

面向切面编程(Aspect-Oriented Programming, AOP)是Spring框架的核心特性之一,它允许开发者通过切面来分离横切关注点(如日志记录、事务管理、安全性等),从而实现代码的模块化。在Spring中,AOP通过动态代理和拦截器来实现。本篇文章将手动实现一个更贴近Spring的AOP框架,支持方法拦截和环绕通知,并与Spring AOP的基本实现进行对比,帮助你掌握AOP的基本概念和编程模型。

AOP的基本概念

AOP的核心思想是通过“切面”将横切关注点(Cross-Cutting Concerns)与主业务逻辑分离。切面定义了在何处、如何、以及在何种条件下横切关注点应当应用到业务方法中。

AOP的基本术语

  • 切面(Aspect):模块化的横切关注点,它通过切入点(Pointcut)和通知(Advice)来定义。
  • 通知(Advice):切面中定义的动作,通常包括“前置通知”、“后置通知”和“环绕通知”。
  • 切入点(Pointcut):定义了横切关注点应当应用的位置(通常是一个或多个方法)。
  • 目标对象(Target Object):被AOP增强的对象,即业务逻辑的载体。
  • 代理(Proxy):AOP框架为目标对象创建的代理对象,用于拦截方法调用并应用通知。

模仿Spring实现动态代理

我们将通过以下步骤来模仿Spring AOP的实现:

  1. 定义切面相关的核心接口,包括切入点(Pointcut)和通知(Advice)。
  2. 创建动态代理工厂,支持基于接口的JDK动态代理和基于类的CGLIB代理。
  3. 实现一个简单的切面类,并将其应用到目标对象上。

定义核心接口

Pointcut接口

Pointcut接口用于定义切入点,确定在哪些连接点(Join Point)上应用通知。

/**
 * 切入点接口,定义了匹配的方法
 */
public interface Pointcut {
    /**
     * 判断给定的方法和类是否符合切入点的条件
     * @param method 被检查的方法
     * @param targetClass 被检查的目标类
     * @return 如果匹配则返回true,否则返回false
     */
    boolean matches(Method method, Class<?> targetClass);
}
Advice接口

Advice接口表示一个通知,定义了在特定切入点上应用的动作。

import java.lang.reflect.Method;

/**
 * 通知接口,定义了在方法执行前后应用的动作
 */
public interface Advice {
    /**
     * 在方法执行之前调用
     * @param method 被调用的方法
     * @param args 方法的参数
     * @param target 目标对象
     * @throws Throwable 抛出可能的异常
     */
    void before(Method method, Object[] args, Object target) throws Throwable;

    /**
     * 在方法执行之后调用
     * @param method 被调用的方法
     * @param args 方法的参数
     * @param target 目标对象
     * @throws Throwable 抛出可能的异常
     */
    void after(Method method, Object[] args, Object target) throws Throwable;
}
MethodInterceptor接口

MethodInterceptor接口扩展自Advice接口,增加了环绕通知的功能。

/**
 * 方法拦截器接口,扩展了环绕通知功能
 */
public interface MethodInterceptor extends Advice {
    /**
     * 环绕通知,用于拦截方法调用
     * @param method 被调用的方法
     * @param args 方法的参数
     * @param target 目标对象
     * @return 方法的返回值
     * @throws Throwable 抛出可能的异常
     */
    Object invoke(Method method, Object[] args, Object target) throws Throwable;
}

实现动态代理工厂

AdvisedSupport类

AdvisedSupport类用于封装目标对象、切入点和通知。

/**
 * 封装代理对象的相关配置,包括目标对象、切入点和通知
 */
public class AdvisedSupport {
    private Object target; // 目标对象
    private Pointcut pointcut; // 切入点,用于确定哪些方法需要被拦截
    private MethodInterceptor methodInterceptor; // 方法拦截器,定义了环绕通知的逻辑

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public Pointcut getPointcut() {
        return pointcut;
    }

    public void setPointcut(Pointcut pointcut) {
        this.pointcut = pointcut;
    }

    public MethodInterceptor getMethodInterceptor() {
        return methodInterceptor;
    }

    public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
        this.methodInterceptor = methodInterceptor;
    }
}
JdkDynamicAopProxy类

JdkDynamicAopProxy类实现了JDK动态代理,用于为目标对象创建代理对象。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 基于JDK动态代理的AOP代理类
 */
public class JdkDynamicAopProxy implements InvocationHandler {
    private AdvisedSupport advisedSupport; // 包含了目标对象、切入点和拦截器

    /**
     * 构造函数,接收AdvisedSupport对象
     * @param advisedSupport 包含了目标对象、切入点和拦截器的配置信息
     */
    public JdkDynamicAopProxy(AdvisedSupport advisedSupport) {
        this.advisedSupport = advisedSupport;
    }

    /**
     * 获取代理对象
     * @return 代理对象
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(
                advisedSupport.getTarget().getClass().getClassLoader(),
                advisedSupport.getTarget().getClass().getInterfaces(),
                this
        );
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 检查切入点是否匹配
        if (advisedSupport.getPointcut().matches(method, advisedSupport.getTarget().getClass())) {
            // 如果匹配,则调用拦截器的invoke方法
            return advisedSupport.getMethodInterceptor().invoke(method, args, advisedSupport.getTarget());
        } else {
            // 否则直接调用目标对象的方法
            return method.invoke(advisedSupport.getTarget(), args);
        }
    }
}

定义环绕通知实现

我们接着定义一个简单的环绕通知实现类SimpleMethodInterceptor,在方法执行前后添加自定义逻辑。

/**
 * 简单的环绕通知实现
 */
public class SimpleMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(Method method, Object[] args, Object target) throws Throwable {
        // 在方法执行之前执行前置逻辑
        System.out.println("Before method: " + method.getName());
        
        // 调用目标对象的方法
        Object result = method.invoke(target, args);
        
        // 在方法执行之后执行后置逻辑
        System.out.println("After method: " + method.getName());
        
        return result;
    }

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        // 可以在这里实现其他前置逻辑
    }

    @Override
    public void after(Method method, Object[] args, Object target) throws Throwable {
        // 可以在这里实现其他后置逻辑
    }
}

创建配置类和目标对象

接下来,我们创建一个目标对象UserService和一个配置类AppConfig,并通过ProxyFactory生成代理对象。

/**
 * 目标对象接口
 */
public interface UserService {
    void addUser();
}

/**
 * 目标对象实现类
 */
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("Adding user...");
    }
}

/**
 * 配置类,用于生成代理对象
 */
public class AppConfig {
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();
        
        // 创建方法拦截器
        MethodInterceptor interceptor = new SimpleMethodInterceptor();
        
        // 创建切入点,匹配所有的方法
        Pointcut pointcut = (method, targetClass) -> true;
        
        // 封装目标对象、切入点和拦截器
        AdvisedSupport advisedSupport = new AdvisedSupport();
        advisedSupport.setTarget(userService);
        advisedSupport.setMethodInterceptor(interceptor);
        advisedSupport.setPointcut(pointcut);
        
        // 使用JDK动态代理生成代理对象
        J

dkDynamicAopProxy jdkDynamicAopProxy = new JdkDynamicAopProxy(advisedSupport);
        UserService proxy = (UserService) jdkDynamicAopProxy.getProxy();
        
        // 调用代理对象的方法
        proxy.addUser();
    }
}

在这个示例中,UserServiceImpl是目标对象,我们通过JdkDynamicAopProxy生成它的代理对象,并在代理对象的方法执行前后添加自定义逻辑。

类图和流程图

为了更好地理解整个流程,我们提供了类图和流程图。

类图
MethodInterceptor
+Object invoke(Method method, Object[] args, Object target)
+void before(Method method, Object[] args, Object target)
+void after(Method method, Object[] args, Object target)
Pointcut
+boolean matches(Method method, Class<?> targetClass)
AdvisedSupport
-Object target
-Pointcut pointcut
-MethodInterceptor methodInterceptor
+Object getTarget()
+void setTarget(Object target)
+Pointcut getPointcut()
+void setPointcut(Pointcut pointcut)
+MethodInterceptor getMethodInterceptor()
+void setMethodInterceptor(MethodInterceptor methodInterceptor)
JdkDynamicAopProxy
-AdvisedSupport advisedSupport
+Object getProxy()
+Object invoke(Object proxy, Method method, Object[] args)
SimpleMethodInterceptor
+Object invoke(Method method, Object[] args, Object target)
+void before(Method method, Object[] args, Object target)
+void after(Method method, Object[] args, Object target)
«interface»
UserService
+void addUser()
UserServiceImpl
+void addUser()

解释

  • MethodInterceptor接口定义了方法拦截和环绕通知的行为。
  • Pointcut接口用于判断切入点是否匹配。
  • AdvisedSupport类封装了目标对象、切入点和拦截器。
  • JdkDynamicAopProxy负责通过JDK动态代理生成代理对象,并调用拦截器的方法。
  • SimpleMethodInterceptor是一个简单的环绕通知实现类。
  • UserServiceUserServiceImpl分别是目标对象的接口和实现类。
流程图
AdvisedSupport构造器
配置目标对象切入点和拦截器
JdkDynamicAopProxy构造器
创建代理对象
InvocationHandler拦截方法调用
检查切入点是否匹配
调用MethodInterceptor的invoke方法
执行目标方法
返回方法结果
直接调用目标对象的方法

解释

  • 流程图展示了JdkDynamicAopProxy如何通过动态代理生成代理对象,以及在方法调用时如何通过InvocationHandler将调用委托给MethodInterceptor的过程。

Spring AOP基础功能的实现

Spring AOP的基本实现

Spring AOP基于动态代理实现。对于接口类型的Bean,Spring使用JDK动态代理,而对于没有实现接口的Bean,Spring则使用CGLIB生成子类代理。

ProxyFactoryBean

ProxyFactoryBean是Spring AOP的核心类之一,它通过配置生成代理对象,并将切面应用到目标对象上。

public class ProxyFactoryBean extends ProxyCreatorSupport implements FactoryBean<Object> {

    private Object singletonInstance;

    @Override
    public Object getObject() throws Exception {
        if (this.singletonInstance == null) {
            this.singletonInstance = createAopProxy().getProxy();
        }
        return this.singletonInstance;
    }

    @Override
    public Class<?> getObjectType() {
        if (this.singletonInstance != null) {
            return this.singletonInstance.getClass();
        }
        return getProxyType();
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

详细解读

  • getObject 方法:该方法返回代理对象实例。如果代理对象还未创建,则通过createAopProxy方法生成。
  • createAopProxy 方法:这个方法负责创建AOP代理,根据目标对象的类型选择JDK动态代理或CGLIB代理。
MethodInterceptor 接口

在Spring AOP中,MethodInterceptor接口是AOP通知的核心接口之一。所有的环绕通知都必须实现这个接口。

public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation invocation) throws Throwable;
}

详细解读

  • invoke 方法:定义了方法拦截的行为,MethodInvocation对象封装了当前方法的上下文,包括目标对象、方法和参数。
  • MethodInvocation 接口:封装了方法调用的上下文,用于访问目标对象、方法参数等信息,并控制方法的执行顺序。

对比与自定义实现

  • Spring AOP的实现

    • Spring AOP基于动态代理,支持JDK代理和CGLIB代理,能够灵活地应用切面到目标对象上。
    • Spring AOP提供了丰富的通知类型,如前置通知、后置通知、环绕通知、异常通知等,功能强大且易于使用。
  • 自定义实现

    • 自定义实现的AOP框架虽然简单,但展示了AOP的基本原理和动态代理的核心机制。
    • 这种实现方式适合学习AOP的基本概念和编程模型。

总结

通过手动实现一个模仿Spring AOP的动态代理框架,并深入解读Spring AOP的基本实现,你应该对AOP的概念和编程模型有了更深入的理解。AOP作为Spring的核心特性之一,其强大的功能使得代码更加模块化和可维护,希望这些内容能帮助你更好地掌握AOP的相关知识。


互动与思考

你在实际项目中是否使用过AOP?你认为AOP在哪些场景下最为有效?欢迎在评论区分享你的看法和经验!


如果你觉得这篇文章对你有帮助,请别忘了:

  • 点赞
  • 收藏 📁
  • 关注 👀

让我们一起深入学习Spring框架,成为更优秀的开发者!


  • 23
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

捕风捉你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值