首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >手写模拟springAOP+SpringAOP源码解析

手写模拟springAOP+SpringAOP源码解析

原创
作者头像
用户12010535
发布2026-01-26 22:10:17
发布2026-01-26 22:10:17
850
举报

一、手写模拟springAOP

手写模拟SpringAOP三大过程:解析,创建代理,调用, 用通俗易懂的方式还原 Spring AOP 的核心逻辑。由浅入深解析 Spring AOP 的底层。

核心思路拆解

Spring AOP 的三大核心过程本质是:

  1. 解析:识别切面(Aspect)、切点(Pointcut)、通知(Advice),封装成可执行的切面规则;
  2. 创建代理:根据目标类是否实现接口,选择 JDK 动态代理或 CGLIB 动态代理,生成代理对象;
  3. 调用:通过代理对象执行目标方法,在执行前后织入通知逻辑。

完整代码实现

第一步:定义核心注解(模拟 Spring AOP 的注解)

代码语言:javascript
复制
// 1. 标记切面类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
}
代码语言:javascript
复制
// 2. 定义切点(简化版,匹配方法名)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PointCut {
    String value();
}
代码语言:javascript
复制
// 3. 前置通知
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Before {
}
代码语言:javascript
复制
// 4. 后置通知
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface After {
}

第二步:定义目标类(被代理的业务类)

代码语言:javascript
复制
public interface UserService {
    void addUser();
    void deleteUser();
}

public class UserServiceImpl implements UserService{
    @Override
    public void addUser() {
        System.out.println("【目标方法】执行添加用户逻辑");
    }

    @Override
    public void deleteUser() {
        System.out.println("【目标方法】执行删除用户逻辑");
    }
}

第三步:定义切面类(包含通知和切点)

代码语言:javascript
复制
// 切面类
@Aspect
public class LogAspect {
    // 切点:匹配addUser方法
    @PointCut(value = "addUser")
    @Before
    public void beforeAddUser() {
        System.out.println("【前置通知】执行添加用户前的日志记录");
    }

    // 切点:匹配deleteUser方法
    @PointCut("deleteUser")
    @After // 后置通知
    public void afterDeleteUser(){
        System.out.println("【后置通知】执行删除用户后的日志记录");
    }
}

第四步:核心实现类(解析 + 创建代理 + 调用)

代码语言:javascript
复制
public class AopProxyFactory {
    // 存储切点与通知的映射(简化版)
    private static Map<String, Method> pointcutAdviceMap = new HashMap<>();
    //第一步:解析切面类,初始化切点-通知映射
    public static void parseAspect(Class<LogAspect> aspectClass) {
        //1、判断是否是切面类
        if (!aspectClass.isAnnotationPresent(Aspect.class)) {
            return;
        }
        //2、遍历切面类的所有方法,解析切点和通知,存储到map
        Method[] methods = aspectClass.getDeclaredMethods();
        for (Method method : methods) {
            //3、获取切点注解
            if (method.isAnnotationPresent(PointCut.class)) {
                PointCut pointCut = method.getAnnotation(PointCut.class);
                String targetMethodName = pointCut.value();// 切点匹配的目标方法名
                pointcutAdviceMap.put(targetMethodName,method);// 存储切点-通知映射
            }
        }
    }
    // 第二步:创建代理对象(JDK动态代理)
    public static Object createProxy(Object target){
        // 1. JDK动态代理:目标类必须实现接口
       return Proxy.newProxyInstance(
               target.getClass().getClassLoader(),
               target.getClass().getInterfaces(),
               new InvocationHandler() {
                   @Override
                   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                       // 第三步:调用核心逻辑(织入通知+执行目标方法)
                       String methodName = method.getName();/// 当前执行的目标方法名

                       //1、执行前置通知
                       if (pointcutAdviceMap.containsKey(methodName)) {
                           Method adviceMethod = pointcutAdviceMap.get(methodName);
                           // 判断是否是前置通知
                           if (adviceMethod.isAnnotationPresent(Before.class)) {
                               adviceMethod.invoke(new LogAspect());// 执行前置通知
                           }
                       }
                       //2、执行目标方法
                       Object result = method.invoke(target, args);
                       //3、执行后置方法
                       if (pointcutAdviceMap.containsKey(methodName)) {
                           Method adviceMethod = pointcutAdviceMap.get(methodName);
                           if (adviceMethod.isAnnotationPresent(After.class)) {
                               adviceMethod.invoke(new LogAspect());
                           }
                       }

                       return result;
                   }
               }
       );
    }

}

第五步:测试入口

代码语言:javascript
复制
public static void main(String[] args) {
    //1、解析切面类
    parseAspect(LogAspect.class);
    //2、创建目标对象
    UserService target = new UserServiceImpl();
    //3、创建代理对象
    UserService proxy = (UserService) createProxy(target);
    //4、调用代理方法(触发通知+目标方法)
    proxy.addUser();
    System.out.println("------------------------");
    proxy.deleteUser();

}

代码运行结果

代码解释:

1、解析过程(parseAspect 方法)

  • 校验类是否有 @Aspect 注解,识别切面类;
  • 遍历切面方法,解析 @Pointcut 注解,将 “目标方法名 - 通知方法” 存入映射表,完成切面规则的解析。

2、创建代理过程(createProxy 方法)

  • 使用 JDK 动态代理(Proxy.newProxyInstance)生成代理对象;
  • 核心是 InvocationHandler 接口,重写 invoke 方法封装调用逻辑。

3、调用过程(invoke 方法)

  • 先根据目标方法名匹配切点,执行前置通知;
  • 调用 method.invoke(target, args) 执行目标方法;
  • 再执行后置通知,完成通知的织入。

总结

  1. 核心逻辑:解析(识别切面规则)→ 创建代理(生成代理对象)→ 调用(织入通知 + 执行目标方法);
  2. 关键技术:Java 反射 + JDK 动态代理,这是 Spring AOP 的底层核心;

二、SpringAOP源码解析

理解 Spring AOP 的源码核心逻辑,会从核心流程 + 关键类 + 源码入口三个维度,由浅入深解析 Spring AOP 的底层实现

Spring AOP 源码核心入口(从启动到代理创建)

1、 核心注解 / 配置:开启 AOP

Spring Boot 中通过 @EnableAspectJAutoProxy 开启 AOP,这是源码的入口:

代码语言:javascript
复制
// 核心注解:开启AOP自动代理
@EnableAspectJAutoProxy
@Configuration
public class AppConfig {
}

@EnableAspectJAutoProxy 会导入 AspectJAutoProxyRegistrar,注册核心 Bean:AnnotationAwareAspectJAutoProxyCreator

2、核心类:AnnotationAwareAspectJAutoProxyCreator

这是 Spring AOP 的核心处理器(继承自 BeanPostProcessor),作用是:

  • 在 Bean 初始化后,判断该 Bean 是否需要被 AOP 代理;
  • 如果需要,创建代理对象并替换原 Bean。

Spring AOP 源码核心流程解析(分 4 步)

第一步:解析切面(@Aspect 注解)

1、源码入口:AnnotationAwareAspectJAutoProxyCreator 扫描容器中所有标注 @Aspect 的 Bean;

2、解析逻辑:

  • 遍历切面类中的方法,解析 @Pointcut@Before@After 等注解;
  • 将每个通知(Advice)封装为 Advisor(切面顾问),包含「切点(Pointcut)+ 通知(Advice)」;
  • 核心方法:ReflectiveAspectJAdvisorFactory#getAdvisors()(解析切面方法为 Advisor)。

关键代码(简化)

代码语言:javascript
复制
// 解析@Aspect切面类,生成Advisor
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    // 遍历切面类的所有方法
    for (Method method : getAdvisorMethods(aspectClass)) {
        // 解析方法上的@Pointcut/@Before等注解,生成Advisor
        Advisor advisor = getAdvisor(method, aspectInstanceFactory, 0, aspectClass);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }
    return advisors;
}

第二步:匹配切点(Pointcut)

核心类AspectJExpressionPointcut(支持切点表达式)

  1. 核心逻辑:Spring 不只是匹配方法名,而是通过切点表达式(如 execution(* com..*.add*(..)))匹配目标方法;
  2. 关键方法:Pointcut#matches(Method method, Class<?> targetClass)(判断目标方法是否匹配切点);
  3. 对比你手写的版本:你用 map 存储 “方法名 - 通知”,Spring 用 AspectJExpressionPointcut 解析表达式,更通用。

第三步:创建代理对象(JDK/CGLIB)

核心类DefaultAopProxyFactoryJdkDynamicAopProxyCglibAopProxy

代理选择逻辑(源码核心)

代码语言:javascript
复制
// DefaultAopProxyFactory.createAopProxy() 核心逻辑
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // 条件1:是否强制使用CGLIB | 条件2:目标类是接口 | 条件3:目标类是代理类
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        // 如果目标类是接口,还是用JDK代理
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        // 否则用CGLIB代理
        return new ObjenesisCglibAopProxy(config);
    } else {
        // 默认:有接口用JDK代理
        return new JdkDynamicAopProxy(config);
    }
}

第四步:方法调用(织入通知)

核心类JdkDynamicAopProxy(JDK 代理)、CglibAopProxy(CGLIB 代理)

  1. JDK 代理核心逻辑(JdkDynamicAopProxy.invoke())简化版
代码语言:javascript
复制

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 1. 获取当前方法匹配的所有Advisor(切面顾问)
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    // 2. 如果没有切面,直接执行目标方法
    if (chain.isEmpty()) {
        return method.invoke(target, args);
    }
    // 3. 有切面:创建方法调用器,织入通知
    MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    // 4. 执行通知+目标方法(核心:责任链模式)
    return invocation.proceed();
}

2、核心设计:责任链模式

  • 所有通知(Before/After/Around)被封装为 MethodInterceptor(方法拦截器);
  • invocation.proceed() 会按顺序执行所有拦截器(通知),最后执行目标方法;
  • 这是 Spring AOP 最核心的设计,比你手写的 “硬编码前置 / 后置” 更灵活。

Spring AOP 源码核心类总结

类名

作用

AnnotationAwareAspectJAutoProxyCreator

核心处理器,自动创建代理对象

ReflectiveAspectJAdvisorFactory

解析 @Aspect 切面类,生成 Advisor

AspectJExpressionPointcut

解析切点表达式,匹配目标方法

DefaultAopProxyFactory

选择 JDK/CGLIB 代理类型

JdkDynamicAopProxy/CglibAopProxy

实现代理对象的创建和方法调用

ReflectiveMethodInvocation

封装方法调用,执行责任链(通知 + 目标方法)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、手写模拟springAOP
    • 核心思路拆解
    • 完整代码实现
      • 第一步:定义核心注解(模拟 Spring AOP 的注解)
      • 第二步:定义目标类(被代理的业务类)
      • 第三步:定义切面类(包含通知和切点)
      • 第四步:核心实现类(解析 + 创建代理 + 调用)
      • 第五步:测试入口
      • 代码运行结果
      • 代码解释:
      • 总结
  • 二、SpringAOP源码解析
    • Spring AOP 源码核心入口(从启动到代理创建)
      • 1、 核心注解 / 配置:开启 AOP
      • 2、核心类:AnnotationAwareAspectJAutoProxyCreator
    • Spring AOP 源码核心流程解析(分 4 步)
      • 第一步:解析切面(@Aspect 注解)
      • 第二步:匹配切点(Pointcut)
      • 第三步:创建代理对象(JDK/CGLIB)
      • 第四步:方法调用(织入通知)
    • Spring AOP 源码核心类总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档