第五章:AOP 基础——五种增强方式详解

第五章:AOP 基础——五种增强方式详解

本章将深入探讨 Jfire 的 AOP(面向切面编程)实现,理解五种增强方式(@Before、@After、@Around、@AfterReturning、@AfterThrowable)的使用场景和工作原理,以及 EnhanceManager 增强管理器的核心设计。

5.1 AOP 概念回顾

5.1.1 什么是 AOP

AOP(Aspect-Oriented Programming,面向切面编程) 是一种编程范式,它允许我们将横切关注点(如日志、事务、安全)从业务逻辑中分离出来。

传统编程中,这些横切关注点散布在各个业务方法中:

public class UserService {
    public void createUser(User user) {
        log.info("开始创建用户");           // 日志
        checkPermission();                  // 权限
        beginTransaction();                 // 事务开始
        try {
            userDao.save(user);             // 业务逻辑
            commitTransaction();            // 事务提交
            log.info("用户创建成功");       // 日志
        } catch (Exception e) {
            rollbackTransaction();          // 事务回滚
            log.error("用户创建失败", e);   // 日志
            throw e;
        }
    }
}

使用 AOP 后,业务代码变得简洁:

public class UserService {
    @Transactional
    public void createUser(User user) {
        userDao.save(user);  // 只关注业务逻辑
    }
}

5.1.2 AOP 核心术语

术语 英文 说明 Jfire 对应
切面 Aspect 横切关注点的模块化 @EnhanceClass 标注的类
连接点 Join Point 程序执行的某个点 ProceedPoint
切入点 Pointcut 匹配连接点的规则 @Before("save*") 中的规则
通知/增强 Advice 在切入点执行的动作 @Before、@After 等
织入 Weaving 将切面应用到目标对象 字节码生成

5.1.3 Jfire 的 AOP 特点

  1. 编译时织入:通过字节码生成技术,在容器启动时创建增强类
  2. 五种增强方式:完整支持前置、后置、环绕、返回后、异常后增强
  3. 灵活的切入点:支持方法名通配符和自定义匹配器
  4. 链式执行:多个增强器按顺序执行

5.2 EnhanceManager 增强管理器架构

EnhanceManager 是 AOP 的核心接口,定义了增强管理器的行为:

// 源码:cc/jfire/jfire/core/aop/EnhanceManager.java

public interface EnhanceManager {
    // 计数器:用于生成唯一的字段/类/方法名
    AtomicInteger FIELD_NAME_COUNTER  = new AtomicInteger(0);
    AtomicInteger CLASS_NAME_COUNTER  = new AtomicInteger(0);
    AtomicInteger METHOD_NAME_COUNTER = new AtomicInteger(0);
    AtomicInteger VAR_NAME_COUNTER    = new AtomicInteger(0);

    // 预定义的 order 值
    int DEFAULT     = 100;   // 普通 AOP
    int TRANSACTION = 10;    // 事务
    int CACHE       = 30;    // 缓存
    int VALIDATE    = 50;    // 验证

    /**
     * 扫描并标记需要增强的 Bean
     */
    default void scan(ApplicationContext context) {
        context.getAllBeanRegisterInfos().stream()
            .filter(needEnhance(context))
            .forEach(info -> info.addEnhanceManager(EnhanceManager.this));
    }

    /**
     * 判断是否需要增强的谓词
     */
    Predicate<BeanRegisterInfo> needEnhance(ApplicationContext context);

    /**
     * 执行增强操作
     * @param classModel    增强类的类模型
     * @param type          被增强的原始类
     * @param context       应用上下文
     * @param hostFieldName 原始实例的字段名
     */
    void enhance(ClassModel classModel, Class<?> type,
                ApplicationContext context, String hostFieldName);

    /**
     * 增强器的执行顺序(数值越小越先执行)
     */
    int order();
}

5.2.1 内置增强管理器

Jfire 内置了三个增强管理器:

增强管理器 order 职责
TransactionEnhanceManager 10 声明式事务管理
CacheEnhanceManager 30 声明式缓存
AopEnhanceManager 100 普通 AOP(五种增强方式)

5.2.2 执行顺序的重要性

增强器的 order() 决定了执行顺序:

请求到达
    │
    ▼
┌───────────────────────────────────────┐
│   TransactionEnhanceManager (order=10) │ ← 最先执行(事务开始)
└───────────────┬───────────────────────┘
                │
                ▼
┌───────────────────────────────────────┐
│   CacheEnhanceManager (order=30)       │ ← 缓存检查
└───────────────┬───────────────────────┘
                │
                ▼
┌───────────────────────────────────────┐
│   AopEnhanceManager (order=100)        │ ← 普通 AOP
└───────────────┬───────────────────────┘
                │
                ▼
           目标方法执行
                │
                ▼
           逆序返回

为什么事务管理器 order 最小?

事务需要包裹所有其他操作,最先开始、最后结束。

5.3 @EnhanceClass:定义切面类

@EnhanceClass 注解用于标记一个类是切面类:

// 源码:cc/jfire/jfire/core/aop/notated/EnhanceClass.java

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Resource  // 自动注册为 Bean
public @interface EnhanceClass {
    /**
     * 匹配目标类的表达式
     * 支持通配符:* 匹配任意字符
     */
    String value() default "";

    /**
     * AOP 执行的顺序。数字越大的越先执行,越先执行的越晚结束
     */
    int order() default 0;
}

5.3.1 类匹配规则

value 属性支持通配符匹配:

表达式 匹配说明 示例
com.example.service.* 匹配 service 包下的所有类 UserService、OrderService
com.example.*.impl.* 匹配任意子包下的 impl 包 dao.impl.UserDaoImpl
*Service 匹配以 Service 结尾的类 任何 XxxService
com.example.UserService 精确匹配 只匹配 UserService

5.3.2 使用示例

// 定义切面类
@EnhanceClass("com.example.service.*")  // 匹配 service 包下的所有类
public class LoggingAspect {

    @Before("save*(*)") // 匹配所有 save 开头的方法
    public void beforeSave(ProceedPoint point) {
        System.out.println("准备保存: " + Arrays.toString(point.getParams()));
    }

    @After("*(*)")  // 匹配所有方法
    public void afterAll(ProceedPoint point) {
        System.out.println("方法执行完成: " + point.getMethod().methodName());
    }
}

5.4 ProceedPoint:连接点抽象

ProceedPoint 是对方法执行点的抽象,它提供了获取方法信息和控制执行的能力:

// 源码:cc/jfire/jfire/core/aop/ProceedPoint.java

public interface ProceedPoint {
    /**
     * 调用目标方法(仅在环绕增强中有效)
     */
    void invoke();

    /**
     * 获取目标对象实例
     */
    Object getHost();

    /**
     * 获取被拦截方法的描述信息
     */
    MethodDescription getMethod();

    /**
     * 获取异常(仅在异常增强中有效)
     */
    Throwable getE();

    /**
     * 获取方法返回值(仅在返回后增强和环绕增强中有效)
     */
    Object getResult();

    /**
     * 设置返回值(仅在环绕增强中有效)
     */
    void setResult(Object result);

    /**
     * 获取方法参数数组
     */
    Object[] getParams();

    // 方法描述内部类
    class MethodDescription {
        final String methodName;
        Class<?>[] paramTypes;

        public String methodName() { return methodName; }
        public Class<?>[] getParamTypes() { return paramTypes; }
    }
}

5.4.1 ProceedPointImpl 实现

// 源码:cc/jfire/jfire/core/aop/ProceedPointImpl.java

public class ProceedPointImpl implements ProceedPoint {
    protected Object            host;              // 目标对象
    protected Object            result;            // 返回结果
    protected Throwable         e;                 // 异常
    protected Object[]          params;            // 参数数组
    protected MethodDescription methodDescription; // 方法描述

    @Override
    public void invoke() {
        result = invokeInternel();  // 调用内部方法
    }

    // 环绕增强时会被重写的方法
    public Object invokeInternel() {
        throw new RuntimeException("该方法只在环绕增强方法中可被调用");
    }

    // getter/setter 方法...
}

5.4.2 ProceedPoint 在各增强中的可用方法

方法 @Before @After @Around @AfterReturning @AfterThrowable
getHost()
getMethod()
getParams()
invoke()
getResult()
setResult()
getE()

5.5 五种增强方式详解

5.5.1 @Before:前置增强

执行时机:目标方法执行前

// 源码:cc/jfire/jfire/core/aop/notated/Before.java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Before {
    String value() default "";  // 方法匹配规则
    Class<? extends MatchTargetMethod> custom() default MatchTargetMethod.class;
}

使用示例

@EnhanceClass("com.example.service.*")
public class ValidationAspect {

    @Before("create*(*)")  // 匹配所有 create 开头且有参数的方法
    public void validateParams(ProceedPoint point) {
        Object[] params = point.getParams();
        for (Object param : params) {
            if (param == null) {
                throw new IllegalArgumentException("参数不能为空");
            }
        }
        System.out.println("参数验证通过");
    }

    @Before("delete*(int)")  // 匹配 delete 开头且参数为 int 的方法
    public void checkDeletePermission(ProceedPoint point) {
        // 检查删除权限
        System.out.println("检查删除权限,ID: " + point.getParams()[0]);
    }
}

生成的代码结构

// 原始方法
public void createUser(User user) {
    // 原始逻辑
}

// 增强后的方法
public void createUser(User user) {
    // 【前置增强代码】
    ProceedPointImpl point = new ProceedPointImpl();
    point.setHost(host);
    point.setParams(new Object[]{user});
    point.setMethodDescription(new MethodDescription("createUser", ...));
    validationAspect.validateParams(point);
    // 【原始逻辑】
    // 原始代码...
}

5.5.2 @After:后置增强

执行时机:目标方法执行后(无论是否异常)

// 源码:cc/jfire/jfire/core/aop/notated/After.java

@Retention(RUNTIME)
@Target(METHOD)
public @interface After {
    String value() default "";
    Class<? extends MatchTargetMethod> custom() default MatchTargetMethod.class;
}

使用示例

@EnhanceClass("com.example.service.*")
public class ResourceCleanupAspect {

    @After("process*(*)")  // 所有 process 开头的方法
    public void cleanup(ProceedPoint point) {
        System.out.println("清理资源: " + point.getMethod().methodName());
    }
}

生成的代码结构

// 增强后的方法
public void processData(Data data) {
    try {
        // 【原始逻辑】
        // 原始代码...
    } finally {
        // 【后置增强代码】
        ProceedPointImpl point = new ProceedPointImpl();
        // 设置 point 属性...
        resourceCleanupAspect.cleanup(point);
    }
}

特点:使用 try-finally 结构,确保无论是否异常都会执行。

5.5.3 @Around:环绕增强

执行时机:完全控制目标方法的执行

// 源码:cc/jfire/jfire/core/aop/notated/Around.java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Around {
    String value() default "";
    Class<? extends MatchTargetMethod> custom() default MatchTargetMethod.class;
}

使用示例

@EnhanceClass("com.example.service.*")
public class PerformanceAspect {

    @Around("*(*)") // 所有方法
    public void measureTime(ProceedPoint point) {
        long start = System.currentTimeMillis();

        try {
            point.invoke();  // 调用目标方法
        } finally {
            long duration = System.currentTimeMillis() - start;
            System.out.println(point.getMethod().methodName()
                + " 执行耗时: " + duration + "ms");
        }
    }

    @Around("query*(*)")
    public void cacheResult(ProceedPoint point) {
        String cacheKey = generateCacheKey(point);
        Object cached = cache.get(cacheKey);

        if (cached != null) {
            point.setResult(cached);  // 直接设置结果,不调用目标方法
            return;
        }

        point.invoke();  // 调用目标方法
        cache.put(cacheKey, point.getResult());  // 缓存结果
    }
}

生成的代码结构

// 增强后的方法
public User getUser(int id) {
    final int $0 = id;  // 参数声明为 final

    ProceedPointImpl point = new ProceedPointImpl() {
        public Object invokeInternel() {
            // 【原始逻辑】
            return host.getUser($0);
        }
    };

    // 设置 point 属性...
    performanceAspect.measureTime(point);

    return (User) point.getResult();
}

关键点

  1. 原始方法体被移入匿名内部类的 invokeInternel() 方法
  2. 参数被声明为 final(匿名内部类访问需要)
  3. 通过 point.invoke() 调用原始方法
  4. 可以不调用 invoke(),直接设置结果

5.5.4 @AfterReturning:返回后增强

执行时机:目标方法正常返回后(无异常)

// 源码:cc/jfire/jfire/core/aop/notated/AfterReturning.java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AfterReturning {
    String value() default "";
    Class<? extends MatchTargetMethod> custom() default MatchTargetMethod.class;
}

使用示例

@EnhanceClass("com.example.service.*")
public class AuditAspect {

    @AfterReturning("create*(*)")
    public void auditCreation(ProceedPoint point) {
        Object result = point.getResult();
        System.out.println("创建成功,结果: " + result);
        auditLog.log("CREATE", point.getMethod().methodName(), result);
    }

    @AfterReturning("update*(*)")
    public void auditUpdate(ProceedPoint point) {
        System.out.println("更新成功: " + point.getMethod().methodName());
    }
}

生成的代码结构

// 原始方法被重命名
private User createUser_0(User user) {
    // 原始逻辑
    return newUser;
}

// 新生成的方法
public User createUser(User user) {
    User result = createUser_0(user);  // 调用原始方法

    ProceedPointImpl point = new ProceedPointImpl();
    // 设置 point 属性...
    point.setResult(result);  // 设置返回值
    auditAspect.auditCreation(point);

    return result;
}

与 @After 的区别

特性 @After @AfterReturning
执行时机 无论成功失败都执行 只在成功返回时执行
获取返回值 point.getResult()
异常时执行

5.5.5 @AfterThrowable:异常后增强

执行时机:目标方法抛出异常后

// 源码:cc/jfire/jfire/core/aop/notated/AfterThrowable.java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AfterThrowable {
    String value() default "";
    Class<? extends MatchTargetMethod> custom() default MatchTargetMethod.class;
}

使用示例

@EnhanceClass("com.example.service.*")
public class ExceptionHandlerAspect {

    @AfterThrowable("*(*)") // 所有方法
    public void handleException(ProceedPoint point) {
        Throwable e = point.getE();
        String method = point.getMethod().methodName();

        // 记录异常日志
        logger.error("方法 {} 执行异常", method, e);

        // 发送告警
        alertService.sendAlert("方法异常: " + method, e.getMessage());
    }
}

生成的代码结构

// 增强后的方法
public void doSomething() {
    try {
        // 【原始逻辑】
        // 原始代码...
    } catch (Throwable e) {
        ProceedPointImpl point = new ProceedPointImpl();
        // 设置 point 属性...
        point.setE(e);  // 设置异常
        exceptionHandlerAspect.handleException(point);

        ReflectUtil.throwException(e);  // 重新抛出异常
        return;  // 编译需要的占位代码
    }
}

注意:异常增强不会吞掉异常,执行完增强逻辑后会重新抛出。

5.6 方法匹配规则

5.6.1 MatchTargetMethod 接口

// 源码:cc/jfire/jfire/core/aop/notated/support/MatchTargetMethod.java

public interface MatchTargetMethod {
    boolean match(Method method);
}

5.6.2 默认实现:NameMatch

NameMatch 是默认的方法匹配器,支持方法名和参数类型的通配符匹配:

// 源码:cc/jfire/jfire/core/aop/notated/support/impl/NameMatch.java

public class NameMatch implements MatchTargetMethod {
    private final String rule;

    public NameMatch(String rule) {
        this.rule = rule;
    }

    @Override
    public boolean match(Method method) {
        // 1. 解析方法名规则
        String methodNameRule = rule.substring(0, rule.indexOf('('));
        if (!StringUtil.match(method.getName(), methodNameRule)) {
            return false;
        }

        // 2. 解析参数规则
        String paramRule = rule.substring(rule.indexOf('(') + 1, rule.length() - 1);

        // (*) 匹配任意参数
        if ("*".equals(paramRule)) {
            return true;
        }

        // () 匹配无参方法
        if ("".equals(paramRule)) {
            return method.getParameterTypes().length == 0;
        }

        // 逐个匹配参数类型
        String[] split = paramRule.split(",");
        Class<?>[] parameterTypes = method.getParameterTypes();

        if (split.length != parameterTypes.length) {
            return false;
        }

        for (int i = 0; i < split.length; i++) {
            String literals = split[i].trim();
            if ("*".equals(literals)) {
                continue;  // * 匹配任意类型
            }
            // 匹配简单类名或全限定名
            if (!literals.equals(parameterTypes[i].getSimpleName())
                && !literals.equals(parameterTypes[i].getName())) {
                return false;
            }
        }
        return true;
    }
}

5.6.3 匹配规则语法

规则 说明 匹配示例
save(*) save 方法,任意参数 save(User)、save(int, String)
save() save 方法,无参 save()
save*(*) save 开头的方法,任意参数 save、saveUser、saveAll
*User(*) 以 User 结尾的方法 getUser、createUser
get(int) get 方法,参数为 int get(int)
update(*, String) update 方法,两个参数,第二个是 String update(int, String)
find(User, *) find 方法,第一个参数是 User find(User, int)

5.6.4 自定义匹配器

// 自定义匹配器:只匹配有特定注解的方法
public class AnnotationMatch implements MatchTargetMethod {
    @Override
    public boolean match(Method method) {
        return method.isAnnotationPresent(NeedLog.class);
    }
}

// 使用自定义匹配器
@EnhanceClass("com.example.*")
public class CustomAspect {

    @Before(custom = AnnotationMatch.class)  // 使用自定义匹配器
    public void beforeAnnotatedMethod(ProceedPoint point) {
        // 只有标注 @NeedLog 的方法才会被增强
    }
}

5.7 AopEnhanceManager 源码解析

让我们深入分析 AopEnhanceManager 的实现:

5.7.1 needEnhance:判断是否需要增强

// 源码:AopEnhanceManager.java

@Override
public Predicate<BeanRegisterInfo> needEnhance(ApplicationContext context) {
    if (list == null) {
        list = new LinkedList<>();

        // 1. 收集所有切面类信息
        context.getAllBeanRegisterInfos().stream()
            .filter(info -> AnnotationContext.isAnnotationPresent(
                EnhanceClass.class, info.getType()))
            .map(info -> info.getType())
            .forEach(ckass -> {
                // 获取 @EnhanceClass 的 value 值列表
                List<String> enhanceClass = Stream.iterate(ckass,
                    c -> c != Object.class, c -> c.getSuperclass())
                    .filter(c -> AnnotationContext.isAnnotationPresent(
                        EnhanceClass.class, c))
                    .map(c -> AnnotationContext.getAnnotation(
                        EnhanceClass.class, c).value())
                    .toList();

                // 收集所有增强方法的匹配器
                Set<MatchTargetMethod> collect = /* ... */;

                list.add(new EnhanceClassData(enhanceClass, collect));
            });
    }

    // 2. 返回判断谓词
    return beanRegisterInfo -> list.stream()
        // 检查类名是否匹配
        .filter(v -> v.enhanceClass.stream().anyMatch(
            each -> StringUtil.match(beanRegisterInfo.getType().getName(), each)))
        // 检查是否有匹配的方法
        .flatMap(v -> v.collection().stream())
        .anyMatch(matchTargetMethod -> Arrays.stream(
            beanRegisterInfo.getType().getDeclaredMethods())
            .anyMatch(matchTargetMethod::match));
}

5.7.2 enhance:执行增强

// 源码:AopEnhanceManager.java

@Override
public void enhance(ClassModel classModel, final Class<?> originType,
                   ApplicationContext applicationContext, String hostFieldName) {

    MethodModel setEnhanceFieldsMethod = getSetEnhanceFieldsMethod(classModel);

    // 查找适用于当前类的切面
    List<BeanRegisterInfo> aspectList = findAspectClass(originType, applicationContext);

    for (BeanRegisterInfo aspect : aspectList) {
        String fieldName = "enhance_" + FIELD_NAME_COUNTER.getAndIncrement();
        int sum = 0;

        // 按顺序处理五种增强(顺序很重要!)
        sum += processBeforeAdvice(/* ... */);      // 1. 前置增强
        sum += processAroundAdvice(/* ... */);      // 2. 环绕增强
        sum += processAfterAdvice(/* ... */);       // 3. 后置增强
        sum += processAfterReturningAdvice(/* ... */); // 4. 返回后增强
        sum += processAfterThrowableAdvice(/* ... */); // 5. 异常后增强

        // 如果有增强,添加切面字段
        if (sum > 0) {
            addFiledAndBuildSet(classModel, setEnhanceFieldsMethod, aspect, fieldName);
        }
    }
}

5.7.3 处理前置增强示例

// 源码:AopEnhanceManager.java

private boolean processBeforeAdvice(ClassModel classModel, Class<?> originType,
    AnnotationContext annotationContextOnEnhanceMethod,
    String hostFieldName, String fieldName, Method enhanceMethod) {

    Before before = annotationContextOnEnhanceMethod.getAnnotation(Before.class);
    MatchTargetMethod matchTargetMethod = getMatchTargetMethod(
        enhanceMethod, before.value(), before.custom());

    return Arrays.stream(originType.getDeclaredMethods())
        .filter(method -> !method.isBridge())
        .filter(method -> !Modifier.isStatic(method.getModifiers()))
        .filter(method -> matchTargetMethod.match(method))
        .peek(method -> {
            logger.debug("前置通知规则匹配成功,方法:{},通知方法:{}",
                method.getDeclaringClass().getSimpleName() + "." + method.getName(),
                enhanceMethod.getDeclaringClass().getSimpleName() + "." + enhanceMethod.getName());

            // 获取现有方法模型
            MethodModel.MethodModelKey key = new MethodModel.MethodModelKey(method);
            MethodModel methodModel = classModel.getMethodModel(key);
            String originBody = methodModel.getBody();

            // 生成前置增强代码
            String pointName = "point_" + FIELD_NAME_COUNTER.getAndIncrement();
            StringBuilder cache = new StringBuilder();

            if (enhanceMethod.getParameterTypes().length == 0) {
                // 无参增强方法,直接调用
                cache.append(fieldName).append(".").append(enhanceMethod.getName())
                    .append("();\r\n");
            } else {
                // 有参增强方法,需要创建 ProceedPoint
                generateProceedPointImpl(classModel, hostFieldName, method,
                    pointName, cache, false);
                generateEnhanceMethodInvoke(fieldName, enhanceMethod, pointName, cache);
            }

            // 前置代码 + 原始代码
            cache.append(originBody);
            methodModel.setBody(cache.toString());
        }).count() > 0;
}

5.8 增强执行顺序

当多个增强作用于同一方法时,执行顺序如下:

┌─────────────────────────────────────────────────────────────┐
│                     方法调用                                 │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                   @Before (前置增强)                         │
│                   按 aspect order 顺序执行                   │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                   @Around (环绕增强开始)                     │
│                   point.invoke() 调用前的代码                │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                      目标方法执行                            │
└─────────────────────────┬───────────────────────────────────┘
                          │
                   ┌──────┴──────┐
                   │             │
              正常返回         抛出异常
                   │             │
                   ▼             ▼
    ┌─────────────────┐    ┌─────────────────┐
    │ @AfterReturning │    │ @AfterThrowable │
    │   (返回后增强)   │    │   (异常后增强)   │
    └────────┬────────┘    └────────┬────────┘
             │                      │
             └──────────┬───────────┘
                        │
                        ▼
┌─────────────────────────────────────────────────────────────┐
│                   @Around (环绕增强结束)                     │
│                   point.invoke() 调用后的代码                │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                   @After (后置增强)                          │
│                   在 finally 块中执行                        │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ▼
                       返回结果

5.9 完整示例

5.9.1 业务服务

@Resource
public class UserService {

    @Resource
    private UserDao userDao;

    public User createUser(String name, int age) {
        User user = new User(name, age);
        userDao.save(user);
        return user;
    }

    public User getUser(int id) {
        return userDao.findById(id);
    }

    public void deleteUser(int id) {
        userDao.delete(id);
    }
}

5.9.2 切面类

@EnhanceClass("com.example.service.*")
public class ComprehensiveAspect {

    // 1. 前置增强:参数验证
    @Before("create*(*, int)")
    public void validateAge(ProceedPoint point) {
        int age = (int) point.getParams()[1];
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("年龄不合法: " + age);
        }
        System.out.println("【前置】年龄验证通过: " + age);
    }

    // 2. 后置增强:资源清理
    @After("*(*)")
    public void logCompletion(ProceedPoint point) {
        System.out.println("【后置】方法执行完成: " + point.getMethod().methodName());
    }

    // 3. 环绕增强:性能监控
    @Around("get*(*)")
    public void measurePerformance(ProceedPoint point) {
        long start = System.nanoTime();
        try {
            point.invoke();
        } finally {
            long duration = System.nanoTime() - start;
            System.out.println("【环绕】" + point.getMethod().methodName()
                + " 耗时: " + duration / 1_000_000.0 + "ms");
        }
    }

    // 4. 返回后增强:审计日志
    @AfterReturning("create*(*)")
    public void auditCreation(ProceedPoint point) {
        Object result = point.getResult();
        System.out.println("【返回后】创建成功: " + result);
    }

    // 5. 异常后增强:异常处理
    @AfterThrowable("delete*(*)")
    public void handleDeleteError(ProceedPoint point) {
        Throwable e = point.getE();
        System.out.println("【异常后】删除失败: " + e.getMessage());
    }
}

5.9.3 运行结果

// 调用 createUser("张三", 25)
【前置】年龄验证通过: 25
【返回后】创建成功: User{name='张三', age=25}
【后置】方法执行完成: createUser

// 调用 getUser(1)
【环绕】getUser 耗时: 0.5ms
【后置】方法执行完成: getUser

// 调用 deleteUser(999) - 假设抛出异常
【异常后】删除失败: 用户不存在
【后置】方法执行完成: deleteUser
Exception: 用户不存在

5.10 本章小结

本章我们深入学习了 Jfire 的 AOP 实现:

  1. EnhanceManager 架构

    • 定义了增强管理器的核心接口
    • 通过 order() 控制执行顺序
    • 内置三个增强管理器:事务、缓存、AOP
  2. 五种增强方式

    • @Before:前置增强,方法执行前
    • @After:后置增强,方法执行后(finally)
    • @Around:环绕增强,完全控制执行
    • @AfterReturning:返回后增强,正常返回时
    • @AfterThrowable:异常后增强,抛出异常时
  3. ProceedPoint 连接点

    • 提供方法信息、参数、返回值、异常的访问
    • 环绕增强中可调用 invoke() 执行原方法
  4. 方法匹配规则

    • 支持方法名通配符:save**User
    • 支持参数匹配:(*), (), (int, String)
    • 支持自定义匹配器
  5. 代码生成

    • 前置:增强代码 + 原始代码
    • 后置:try { 原始 } finally { 增强 }
    • 环绕:匿名内部类包装原始方法
    • 返回后:调用原始 → 设置 result → 调用增强
    • 异常后:try { 原始 } catch { 增强; rethrow }

下一章预告:我们将深入 AOP 的字节码生成实现,了解 Jfire 如何使用 SMC 库动态创建增强类,以及增强类的完整结构。

思考题

  1. 为什么 @Around 增强需要将参数声明为 final

  2. 如果同一个方法被多个切面的同类型增强(如多个 @Before)匹配,执行顺序是怎样的?

  3. @After@AfterReturning 能否同时作用于一个方法?它们的执行顺序是什么?

  4. 如何实现一个自定义的 EnhanceManager,比如参数验证增强器?

核心源码清单

文件 路径 核心内容
EnhanceManager.java core/aop/ 增强管理器接口
AopEnhanceManager.java core/aop/impl/ AOP 增强管理器实现
ProceedPoint.java core/aop/ 连接点接口
ProceedPointImpl.java core/aop/ 连接点实现
EnhanceClass.java core/aop/notated/ 切面类注解
Before.java core/aop/notated/ 前置增强注解
After.java core/aop/notated/ 后置增强注解
Around.java core/aop/notated/ 环绕增强注解
AfterReturning.java core/aop/notated/ 返回后增强注解
AfterThrowable.java core/aop/notated/ 异常后增强注解
MatchTargetMethod.java core/aop/notated/support/ 方法匹配器接口
NameMatch.java core/aop/notated/support/impl/ 默认方法匹配器
Java 进阶提升,手写系列 文章被收录于专栏

专栏以轻量级 Java 框架 Jfire 为蓝本,带你从零手写一个完整的 IOC 容器。专栏共 10 章,涵盖 IOC 容器核心原理:Bean 定义与生命周期、Bean 工厂设计、五种依赖注入策略、循环依赖解决方案;深入 AOP 实现:五种增强方式、字节码动态生成技术;以及企业级特性:声明式事务管理(四种传播级别)、声明式缓存框架、条件注解与自动配置机制。

全部评论

相关推荐

昨天 10:50
门头沟学院 Java
点赞 评论 收藏
分享
从年头开始,就一直在忙忙碌碌,背八股,投简历,找实习。我人生的第一场面试是腾讯的实习岗面试,大概是三月份吧,那时候我lc几乎就没怎么刷,八股也是被的稀稀拉拉。一上来面试官还没让做自我介绍就甩了五道编程题给我做,但我一道都不会做,他没有开摄像头,我看着空白的函数段和在屏幕中那个窘迫的我,手心一直在冒汗,过了大概25分钟,他问我遇到了什么问题,我说“不好意思面试官,这里面的题,我哪一道都没有思路”就这样,我说完本次面试唯一的一句话后,结束了这场面试,关闭会议后我坐在电脑屏幕前发呆了很久,其实也没有感到难过,只是大脑一片空白后面也陆陆续续收到其他的一些面试,然后我不知道我哪来的自信,我就偏偏要去大厂,投递的全是叫得上名儿的大厂,结果不是笔试没过就是面试中答不上来,但是后面所有的面试官都很好,会耐心的指出我的不足或者可以改进的地方,让我每次面试后都有所进步。到了四月末,为了不拖我秋招的进度,我在boss上也开始投递一些中厂,然后顺利地拿到了offer,但我清楚的知道,我不属于那里,我不是没有资格去更高的平台,只是我还没有准备好而已。五月份我一个人从佛山出发,坐动车到了深圳这座城市,租到了满意的房子,买了一口小电锅,开始我的实习生活。深圳真的很奇幻,一边是密密麻麻的城中村,回家走在路上感觉能被电鸡创死,一边是高楼耸立的CBD,全是英文牌坊的商场,感觉每一次出地铁口都是在开盲盒。我开始逐渐适应一个人的生活,一个人自己给自己做饭,一个人吃烤肉,无聊的时候一个人去唱k,去逛展,甚至用dy开游戏直播,但有时候也会半夜emo躺床上在某个瞬间觉得自己咋这么惨入职第一天,看到自己工位上挂着“前端开发实习生-XXX”的时候,有一种不真实的感觉,我咋就开始上班了,像小孩儿装大人一样,第一个月收到工资还觉得哇撒好多,后面就在想快快给我涨工资。整个实习大概持续了三个月半吧,这里非常感谢我的mt,他不仅制定了简而有序的培养方案,还在工作与生活上给予了我非常多的支持,最开始的一个月,那个规划表里面真的是按天为单位去罗列我要干什么的,我完成一项就在上面打一个勾然后mt帮我去检验成果,后面他就慢慢放手,提醒我要开始进行思维的转换,要开始学着主动,自己去沟通,有风险只要及时暴露给他,他帮我兜底。这段时间我真的成长非常快,不仅仅是敲代码,像业务流程,技术选型,排查问题,遇到需要跨团队解决问题的能力都有所提升,到后面我好像就是一个“正式员工”了,干的还算不错,+2说如果转正要给我涨工资。mt经常与我one&nbsp;one一些我在工作上暴露的一些问题和他看到我进步的地方,真的是亦师亦友,从入职欢迎的那一顿饭到最后我离职欢送的那一段饭,前前后后我的mt请我吃了得五六顿饭了,平日也是带着我和前端同事一起吃饭,希望我能够在大家面前多刷刷脸,告诉我同事之间的关系也很重要,他忙的时候会给我发消息给我推荐哪里好吃。mt最后一次跟我one&nbsp;one就是我提离职准备秋招了,他希望我能够留下来,问我的意愿是怎么样的。“我还是说想去到一个更好的平台发展”他笑着点点头,和我们最后一顿饭碰杯一样说道“预祝你顺利拿到大厂offer,前程似锦”在公司的最后一天晚上,mt帮我记录了我在公司的最后几个瞬间,拍了好多照片,我带着这段回忆和经历重返了校园不知道大家有没有这种感觉&nbsp;,实习或者工作后回到学校会觉得不真实,好像自己已经离开了很久了不再属于这里了,外面被拷打了一番感觉岁月静好哈哈,回来去测mbti,从i/enfp变成了esfp?所以本就持怀疑态度的我再也信这玩意儿了。短暂歇了一段时间就开始准备秋招了,诚实讲相比于准备实习,秋招的整个阶段都比较浮躁,不知道是八股滚了一遍又一遍的原因还是时间线确实太长了,面试前其实不想做太多的复习。但是心态上就比实习那时候更加的平和,能过就过,不过拉倒,基本上有面试的中大厂一面都可以过,就是在二面上栽跟头。说实话秋招除了能力真的挺看运气的,看合不合面试官眼缘,问的问题是不是刚刚好都会答,但是越努力越幸运嘛,还是得在每次面试后总结面经,然后相应得拓宽自己的知识范围。期间加了几个秋招群,发现应聘大厂的真的是人才扎堆,硕士满天,92横飞,很担心最后横向自己的履历比不过别人。面试期间比较抓马的一幕是,我携程(上海)已经hr面后泡了1个月了,然后我在面哈啰的时候一个021(上海)开头的电话打过来,我以为是oc电话,但是又出于对面试官的尊重没有马上接听,面试结束后赶紧打了过去,结果是之前在boss上面投递的另外一家公司的笔试提醒......好在几天后携程真的打过来了,真是命运捉弄秋招人,没想到是秋招的第一家面的公司最后给到了offer,这时候已经接近12月了,终于是松了口气结束了秋招。最后总结一下吧,感觉写到这里回头看写的就像流水账,其实从选方向到实习到秋招,我非常感谢我坚持了我自己认为对的事情,身边没有一个同学选择前端,但是我觉得用户能直接与我做的界面流畅交互让我觉得有成就感,我也喜欢好看的东西,我乐意与UI打交道,我选择了。对于我拒绝了实习转正,别人也不明白明明待遇还不错为什么还要离开,我选择了。在我实习和秋招在大厂面试吃瘪的时候,有时会收到一些质疑,我选择继续,实习不行秋招,秋招不行春招。希望在未来,我也能坚持自己的想法,大步向前。
牛客80849854...:看完了,真不容易夸夸自己吧太棒了您
2025年终总结
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务