AOP代理-字节码增强与类加载机制

129 阅读12分钟

本文将通过五个典型代码案例,系统解析Java生态中五种主流AOP实现方案的底层原理。涵盖从Spring AOP到字节码增强技术,从动态代理到Java Agent的全方位技术图谱。内容将深入到字节码操作、类加载机制、反射原理等底层细节。


一、Spring AOP与AspectJ的深度整合

1.1 核心机制与注解解析

Spring AOP对AspectJ的集成并非原生支持,而是通过spring-aspects模块实现的桥接方案。其核心流程可分为切面扫描→Bean注册→织入执行三个阶段,以下是关键注解的底层逻辑:

1.1.1 @EnableAspectJAutoProxy:激活AspectJ自动代理

该注解通过@Import(AspectJAutoProxyRegistrar.class)导入注册器,其核心逻辑如下:

// AspectJAutoProxyRegistrar源码片段
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    // 解析@EnableAspectJAutoProxy的属性(proxyTargetClass/exposeProxy)
    Map<String, Object> attrs = importingClassMetadata.getAnnotationAttributes(EnableAspectJAutoProxy.class.getName());
    boolean proxyTargetClass = (Boolean) attrs.get("proxyTargetClass");
    boolean exposeProxy = (Boolean) attrs.get("exposeProxy");

    // 注册AnnotationAwareAspectJAutoProxyCreator后处理器
    RootBeanDefinition creator = new RootBeanDefinition(AnnotationAwareAspectJAutoProxyCreator.class);
    creator.setSource(importingClassMetadata);
    creator.getPropertyValues().add("proxyTargetClass", proxyTargetClass);
    creator.getPropertyValues().add("exposeProxy", exposeProxy);
    registry.registerBeanDefinition(AutoProxyCreatorBeanName, creator);
}
  • 核心作用​:向Spring容器注册AnnotationAwareAspectJAutoProxyCreator后处理器,该处理器会在Bean初始化后阶段(postProcessAfterInitialization)检查是否需要生成代理。
  • proxyTargetClass​:若为true,强制使用CGLIB代理(即使目标类实现了接口);若为false(默认),则优先使用JDK动态代理(基于接口)。
  • exposeProxy​:若为true,将代理对象绑定到ThreadLocalAopContext.currentProxy()),允许在目标对象内部方法中获取当前代理。

1.1.2 @Aspect切面类:定义切点与通知

@Aspect注解标记的类会被Spring识别为切面Bean,其内部通过@Pointcut@Before等注解定义切点和通知。以下是关键处理流程:

  1. 切面扫描​:Spring在启动时会扫描所有被@Aspect标记的Bean,将其注册为AspectJExpressionPointcutAdvisor类型的后处理器。
  2. 切点解析​:对@Pointcut表达式(如execution(* com.dwl.aop.agc.AopAgcService.*()))进行语法解析,生成AspectJExpressionPointcut对象,用于匹配目标方法。
  3. 通知绑定​:将@Before@After等通知方法与对应的切点关联,生成Advice对象(如MethodBeforeAdvice)。

1.1.3 加载时织入(LTW):字节码修改的触发时机

Spring通过java.lang.instrument API实现LTW,其核心流程如下:

  1. Java Agent注册​:通过-javaagent参数指定Agent JAR包,JVM启动时会调用premain方法。
  2. Instrumentation获取​:Agent通过premain获取Instrumentation实例,用于后续的类转换操作。
  3. 类加载监听​:通过AgentBuilder监听类加载事件,当目标类(如AopAgcService)被加载时,触发字节码转换。
  4. 字节码织入​:使用Byte BuddyAspectJ Weaver将切面逻辑插入目标方法的字节码中(如在foo()方法前插入beforeFoo通知的调用)。

关键代码示例(LTW触发流程)​​:

// Spring的AspectJAutoProxyCreator后处理器核心逻辑
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != this.advisedBeansCache.get(beanName)) {
        // 检查是否需要代理(目标类是否匹配切点)
        if (this.advisorFactory.isAspectJAdvisorFactory() && isEligibleBean(beanName)) {
            // 生成代理对象(JDK或CGLIB)
            Object proxy = createProxy(bean, beanName);
            this.advisedBeansCache.put(beanName, Boolean.TRUE);
            return proxy;
        }
    }
    return bean;
}

二、Java Agent字节码增强技术实战

2.1 Java Agent核心原理

Java Agent是JVM提供的一种字节码插桩机制,允许在类加载时或运行时修改类的字节码。其核心组件包括:

  • Premain-Class​:指定Agent的入口类(包含premain方法)。
  • Instrumentation​:JVM提供的接口,用于注册类转换器(ClassFileTransformer)、获取类加载信息等。
  • Agent-Class​:可选(当使用attach方式加载Agent时指定)。

2.1.1 Agent启动流程

  1. JVM启动时加载​:通过-javaagent:path/to/agent.jar参数启动,JVM会先加载Agent JAR包,然后调用premain方法。
  2. 运行时动态加载​:通过Attach API向已运行的JVM附加Agent,调用agentmain方法。

2.1.2 字节码转换流程

Agent通过Instrumentation.addTransformer注册一个ClassFileTransformer,当类被加载时,JVM会调用该转换器的transform方法对字节码进行修改:

// AgentBuilder.Default的transform逻辑简化版
public DynamicType.Builder<?> transform(TypeBuilder<?> builder, ...) {
    // 匹配需要增强的类(如包路径以com.dwl.aop.agent开头)
    if (typeDescription.matches(matcher)) {
        // 对类的方法进行转换(插入增强逻辑)
        return builder.method(methodMatcher)
                      .intercept(Advice.to(MethodLogAdvice.class));
    }
    return builder;
}

2.2 Byte Buddy实战:方法日志增强

Byte Buddy是一个简化字节码操作的库,其Advice机制可以在不手动编写字节码的情况下插入增强逻辑。以下是关键步骤:

2.2.1 Advice注解的使用

@Advice.OnMethodEnter@Advice.OnMethodExit分别标记方法进入和退出时的增强逻辑:

public class MethodLogAdvice {

    /**
     * 方法进入时的增强逻辑(@Advice.OnMethodEnter注解标记)
     * @param method 当前执行的方法对象(包含类名、方法名等信息)
     * @param args 方法入参数组(可能为null)
     * @param stopWatch 局部变量(用于计时,需通过@Advice.Local声明)
     */
    @Advice.OnMethodEnter
    static void onMethodEnter(
            @Advice.Origin Method method,
            @Advice.AllArguments Object[] args,
            @Advice.Local("stopWatch") StopWatch stopWatch) {

        // 初始化计时器并开始计时
        stopWatch = new StopWatch();
        stopWatch.start();

        // 构造方法标识(类名.方法名)
        String methodName = method.getDeclaringClass().getSimpleName() + "." + method.getName();

        // 处理入参信息(处理null情况)
        String argsInfo = (args != null && args.length > 0) ? Arrays.toString(args) : "无入参";

        System.out.printf("[方法开始] %s | 入参: %s%n", methodName, argsInfo);
    }


    /**
     * 方法退出时的增强逻辑(@Advice.OnMethodExit注解标记,捕获异常)
     * @param method 当前执行的方法对象
     * @param throwable 方法执行过程中抛出的异常(可能为null)
     * @param stopWatch 局部变量(用于获取计时结果)
     * @param result 方法返回值(动态类型,可能为null)
     */
    @Advice.OnMethodExit(onThrowable = Throwable.class)
    static void onMethodExit(
            @Advice.Origin Method method,
            @Advice.Thrown Throwable throwable,
            @Advice.Local("stopWatch") StopWatch stopWatch,
            @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result) {

        // 防御性检查(理论上不会为null,防止意外情况)
        if (stopWatch == null) {
            return;
        }

        // 停止计时并获取耗时(毫秒)
        stopWatch.stop();
        long durationMs = stopWatch.getTotalTimeMillis();

        // 构造方法标识
        String methodName = method.getDeclaringClass().getSimpleName() + "." + method.getName();

        // 处理返回值信息(区分void类型和null值)
        String resultInfo;
        if (method.getReturnType() == void.class) {
            resultInfo = "void(无返回值)";
        } else {
            resultInfo = (result != null) ? result.toString() : "null";
        }

        // 根据是否有异常输出不同级别的日志
        if (throwable != null) {
            System.err.printf("[方法异常] %s | 耗时: %dms | 异常: %s%n", methodName, durationMs, throwable.getMessage());
        } else {
            System.out.printf("[方法结束] %s | 耗时: %dms | 返回值: %s%n", methodName, durationMs, resultInfo);
        }
    }
}

2.2.2 AgentBuilder配置

通过AgentBuilder配置需要增强的类和方法:

new AgentBuilder.Default()
    .with(new CustomAgentListener())  // 自定义监听器(记录类加载日志)
    .type(ElementMatchers.nameStartsWith("com.dwl.aop.agent"))  // 匹配目标类
    .transform((builder, type, loader, module, protectionDomain) -> 
        builder.method(ElementMatchers.any()
                .and(ElementMatchers.not(ElementMatchers.isGetter()))  // 排除getter
                .and(ElementMatchers.not(ElementMatchers.isSetter())))  // 排除setter
               .intercept(Advice.to(MethodLogAdvice.class))  // 应用增强
    )
    .with(RedefinitionStrategy.RETRANSFORMATION)  // 支持热重转换
    .installOn(inst);  // 安装到Instrumentation

关键技术点​:

  • 局部变量声明​:@Advice.Local用于声明方法内的局部变量(如StopWatch),需指定类型(基本类型或引用类型)。
  • 方法匹配​:ElementMatchers提供丰富的匹配规则(如包路径、方法名、参数类型等),支持组合条件(and/or/not)。
  • 热部署支持​:RedefinitionStrategy.RETRANSFORMATION允许对已加载的类重新转换(需JVM支持-XX:+AllowEnhancedClassRedefinition)。

三、动态代理技术的双雄对决

3.1 JDK动态代理:基于接口的反射调用

JDK动态代理是Java原生支持的代理机制,其核心是通过Proxy类动态生成代理类,该代理类继承自Proxy并实现目标接口。

3.1.1 核心类与接口

  • Proxy​:JDK提供的代理类生成工具,包含newProxyInstance静态方法。
  • InvocationHandler​:调用处理器,所有通过代理对象的方法调用都会被转发到此接口的invoke方法。

3.1.2 代理类生成流程

  1. 参数校验​:检查接口是否合法(必须是接口)、类加载器是否匹配。
  2. 生成字节码​:根据类加载器、接口列表和方法拦截逻辑,动态生成代理类的字节码(存储在Proxy类的proxyClassCache中)。
  3. 加载类​:使用指定的类加载器加载生成的代理类。
  4. 创建实例​:通过反射调用代理类的构造方法(参数为InvocationHandler实例),生成代理对象。

代理类字节码示例​(简化):

// 生成的代理类(继承自Proxy,实现JdkProxyInterfaceCase接口)
public final class $Proxy0 extends Proxy implements JdkProxyInterfaceCase {
    private static Method m1;  // foo()方法的Method对象

    public $Proxy0(InvocationHandler h) {
        super(h);
    }

    @Override
    public void foo() {
        try {
            // 调用InvocationHandler的invoke方法
            h.invoke(this, m1, null);
        } catch (Throwable t) {
            throw t.getCause();
        }
    }
}

3.1.3 性能瓶颈与限制

  • 反射调用​:method.invoke(target, args)通过反射调用目标方法,存在一定的性能开销(比直接调用慢约10-100倍)。
  • 接口依赖​:只能代理实现了接口的类,无法代理普通类。
  • 方法限制​:无法代理Object类的方法(如equalshashCode),因为代理类会覆盖这些方法并调用InvocationHandler

3.2 CGLIB动态代理:基于继承的字节码增强

CGLIB(Code Generation Library)通过生成目标类的子类来实现代理,其核心是Enhancer类,通过字节码操作生成子类。

3.2.1 核心类与机制

  • Enhancer​:CGLIB的核心类,用于创建代理对象。
  • MethodInterceptor​:方法拦截器,所有方法调用都会被转发到此接口的intercept方法。
  • MethodProxy​:方法代理,用于高效调用父类(目标类)的方法(避免反射)。

3.2.2 代理类生成流程

  1. 创建Enhancer实例​:设置目标类(superclass)和方法拦截器(callback)。
  2. 生成子类字节码​:通过ASM库动态生成目标类的子类(重写非final方法)。
  3. 实例化子类​:调用子类的构造方法(默认调用父类无参构造),生成代理对象。

代理类字节码示例​(简化):

// 生成的代理类(继承自CglibProxyTargetA)
public class CglibProxyTargetA$$EnhancerByCGLIB extends CglibProxyTargetA {
    private MethodInterceptor interceptor;  // 拦截器实例

    public void foo() {
        // 调用拦截器的intercept方法
        interceptor.intercept(this, CGLIB$foo$0, null, CGLIB$foo$0$MethodProxy);
    }

    // 静态方法代理(用于高效调用父类方法)
    private static final Method CGLIB$foo$0$Method = 
        Method.forName("com.dwl.aop.cglib_proxy.CglibProxyTargetA", "foo", "()V");
    private static final MethodProxy CGLIB$foo$0$MethodProxy = 
        MethodProxy.create(CGLIB$foo$0$Method, null);
}

3.2.3 关键优化与限制

  • 性能优势​:通过MethodProxy直接调用父类方法(基于字节码生成的invokespecial指令),比反射调用快得多(接近直接调用)。
  • final方法限制​:无法代理final方法(因为子类无法覆盖final方法)。
  • 构造方法调用​:代理类的构造方法会调用父类的构造方法(可通过Enhancer.setCallbackFilter过滤需要拦截的方法)。

四、五种代理模式的横向对比

4.1 对比维度详解

技术方案实现原理性能损耗(相对)接口依赖适用场景典型限制
Spring AOP动态代理+切面编织中等(JDK反射)可选业务系统通用AOP需求仅支持方法级切点
AspectJ LTWJVM字节码增强最低(无反射)复杂切面/遗留系统改造需要Agent支持,学习成本高
JDK动态代理接口反射调用较高(反射)必须接口规范明确的场景无法代理非接口类
CGLIB动态代理子类继承(字节码增强)中等(无反射)不需要非接口类增强无法代理final方法
Java AgentJVM字节码插桩最低(无反射)框架级深度定制(如日志、监控)需要JVM参数,影响全局

4.2 典型场景选择建议

  • 接口规范明确​:优先选择JDK动态代理(如RPC框架的服务发布)。
  • 非接口类增强​:选择CGLIB(如MyBatis的Mapper代理)。
  • 复杂切面处理​:选择AspectJ LTW(如事务管理、权限控制)。
  • 框架级深度定制​:选择Java Agent(如SkyWalking的链路追踪、Arthas的在线诊断)。

五、最佳实践指南

5.1 选择建议矩阵

需求维度推荐方案原因
接口规范明确JDK动态代理原生支持,性能接近直接调用(反射优化后)
非接口类增强CGLIB支持类级代理,性能优于反射
复杂切面处理AspectJ LTW支持字段访问、构造器拦截等全类型连接点,性能无运行时开销
JVM级深度定制Java Agent可修改任意类的字节码,支持热部署

5.2 性能优化要点

  1. 缓存代理对象​:
    Spring AOP中,代理对象默认是单例的(可通过@Scope("prototype")设置为原型),避免重复创建代理实例的开销。
  2. 方法过滤​:
    使用精确的切点表达式(如execution(* com.dwl.aop.agc.AopAgcService.foo()))替代通配符(如execution(* com.dwl.aop.agc.*.*())),减少不必要的增强。
  3. 异步增强​:
    对耗时操作(如日志记录、统计)使用@Async注解或CompletableFuture异步执行,避免阻塞主线程。
  4. 编译优化​:
    使用AspectJ的ajc编译器替代javac,在编译期完成织入(避免运行时反射或字节码操作的开销)。

5.3 生产环境注意事项

  • Java Agent的内存占用​:多个Agent会增加JVM的启动时间和内存消耗,需合并功能或按需加载。
  • 切面顺序控制​:Spring AOP中,切面的执行顺序可通过@Order注解或Ordered接口控制(数值越小优先级越高)。
  • 异常处理​:在@Around通知中需显式处理目标方法的异常,避免因未捕获异常导致代理失效。

完整代码


package com.dwl.aop.agc;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * @ClassName AopAgcCase
 * @Description Spring集成AspectJ的AOP示例(基于LTW加载时织入的底层原理详解)
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
// 关键配置:启用AspectJ自动代理(Spring对AspectJ的集成入口)
@EnableAspectJAutoProxy(
        proxyTargetClass = true,  // 强制使用CGLIB代理(即使目标类实现接口)
        exposeProxy = true        // 允许通过AopContext获取当前代理对象
)
@SpringBootApplication(
        scanBasePackages = {"com.dwl.aop.agc"},
        exclude = {DataSourceAutoConfiguration.class}
)
public class AopAgcCase {
    private static final Logger log = LoggerFactory.getLogger(AopAgcCase.class);

    public static void main(String[] args) {
        log.info("[主流程] 启动Spring容器...");
        // Spring容器启动时,会加载AspectJ的切面Bean并完成织入
        ConfigurableApplicationContext context = SpringApplication.run(AopAgcCase.class, args);
        log.info("[主流程] 容器启动完成,上下文ID:{}", context.getId());

        // 从容器中获取Bean:实际是AspectJ织入后的增强类(非原始类)
        AopAgcService service = context.getBean(AopAgcService.class);
        log.info("[主流程] 获取到的Bean类型:{}", service.getClass());
        // 输出可能为原始类(若编译时织入)或CGLIB子类(若加载时织入)

        // 调用方法:触发AspectJ织入的通知逻辑
        service.foo();

        context.close();
        log.info("[主流程] 容器关闭");

        // 直接创建原始对象:无AspectJ织入,方法调用不触发通知
        log.info("[主流程] 直接创建原始对象并调用foo()");
        new AopAgcService().foo();
    }

}

/**
 * 业务服务类(被AspectJ增强的目标类)
 * 关键特性:未被final修饰(AspectJ织入需要继承或修改字节码)
 */
@Service
class AopAgcService {
    private static final Logger log = LoggerFactory.getLogger(AopAgcService.class);

    // 构造方法:AspectJ织入不影响构造方法(除非显式切点匹配构造器)
    public AopAgcService() {
        log.info("[AopAgcService] 原始类构造方法执行");
    }

    /**
     * 核心业务方法(被@Pointcut匹配的方法)
     * 关键特性:未被final修饰(AspectJ可织入方法体)
     */
    public void foo() {
        log.info("[AopAgcService] 执行核心逻辑:foo()");
    }
}

/**
 * AspectJ切面类(定义编译时/加载时织入的增强逻辑)
 * @Aspect标记此类为AspectJ切面,Spring会识别并处理其中的切点和通知
 */
@Aspect
@Component // 必须声明为Spring Bean,否则无法被Spring管理
class AopAgcAspect {

    private static final Logger log = LoggerFactory.getLogger(AopAgcAspect.class);


    // 构造方法:Spring实例化切面Bean后,AspectJ织入器会解析其中的通知
    public AopAgcAspect() {
        log.info("[AopAgcAspect] 切面Bean构造完成");
    }

    /**
     * 前置通知:通过@Pointcut匹配AopAgcService的所有无参数方法
     * 底层通过AspectJ织入器将此逻辑插入到目标方法的字节码中
     */
    @Before("execution(* com.dwl.aop.agc.AopAgcService.*())")
    public void beforeFoo(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object target = joinPoint.getTarget(); // 原始类实例(织入后可能指向增强类)
        log.info("[AopAgcAspect] 前置通知触发,目标方法:{},原始对象类型:{}", methodName, target.getClass());
    }
}

package com.dwl.aop.agent;

import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Service;

import java.io.IOException;

/**
 * @ClassName AgentCase
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@SpringBootApplication(
        scanBasePackages = {"com.dwl.aop.agent"},
        exclude = {DataSourceAutoConfiguration.class}
)
@Slf4j
public class AgentCase {

    public static void main(String[] args) throws AgentLoadException, IOException, AttachNotSupportedException, AgentInitializationException {
        log.info("[主流程] 启动Spring容器...");
        ConfigurableApplicationContext context = SpringApplication.run(AgentCase.class, args);
        log.info("[主流程] 容器启动完成,上下文ID:{}", context.getId());

        AgentService service = context.getBean(AgentService.class);
        log.info("[主流程] 获取到的Bean类型:{}", service.getClass());

        service.foo();

//        context.close();
//        log.info("[主流程] 容器关闭");
    }

}

@Service
@Slf4j
class AgentService {

    public AgentService() {
        log.info("[AgentService] 原始类构造方法执行");
    }

    public void foo() {
        log.info("[AgentService] 执行核心逻辑:foo()");
        bar();
    }

    public void bar() {
        log.info("[AgentService] 执行核心逻辑:bar()");
    }
}

@Aspect
@Slf4j
class AgentAspect {


    public AgentAspect() {
        log.info("[AgentAspect] 切面Bean构造完成");
    }

    @Before("execution(* com.dwl.aop.agent.AgentService.*())")
    public void beforeFoo(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object target = joinPoint.getTarget(); // 原始类实例(织入后可能指向增强类)
        log.info("[AgentAspect] 前置通知触发,目标方法:{},原始对象类型:{}", methodName, target.getClass());
    }
}

package com.dwl.spring_case_agent;

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.utility.JavaModule;

/**
 * @ClassName CustomAgentListener
 * @Description Byte Buddy AgentBuilder 监听器实现类
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
public class CustomAgentListener implements AgentBuilder.Listener {
    
    /**
     * 当Agent发现一个待处理的类时触发
     *
     * @param typeName    类全限定名(如com.dwl.MyClass)
     * @param classLoader 加载该类的类加载器
     * @param module      类所属的Java模块(JPMS环境)
     * @param loaded      类是否已被JVM加载
     */
    @Override
    public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
        // 仅记录指定包下的类(过滤JMX等无关类的日志)
        if (typeName.contains("com.dwl.aop.agent")) {
            System.out.println("[Agent] 发现类: " + typeName);
        }
    }

    /**
     * 当Agent成功转换一个类的字节码时触发
     *
     * @param typeDescription 被转换类的类型描述
     * @param classLoader     加载该类的类加载器
     * @param module          类所属的Java模块
     * @param loaded          类是否已被JVM加载
     * @param dynamicType     转换后的动态类型
     */
    @Override
    public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader,
                                 JavaModule module, boolean loaded, DynamicType dynamicType) {
        String typeName = typeDescription.getName();
        if (typeName.contains("com.dwl.aop.agent")) {
            System.out.println("[Agent] 转换类: " + typeName);
        }
    }

    /**
     * 当Agent忽略一个类(不进行字节码转换)时触发
     *
     * @param typeDescription 被忽略类的类型描述
     * @param classLoader     加载该类的类加载器
     * @param module          类所属的Java模块
     * @param loaded          类是否已被JVM加载
     */
    @Override
    public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader,
                          JavaModule module, boolean loaded) {
        String typeName = typeDescription.getName();
        if (typeName.contains("com.dwl.aop.agent")) {
            System.out.println("[Agent] 忽略类: " + typeName);
        }
    }

    /**
     * 当处理类时发生错误时触发
     *
     * @param typeName    出错类全限定名
     * @param classLoader 加载该类的类加载器
     * @param module      类所属的Java模块
     * @param loaded      类是否已被JVM加载
     * @param throwable   导致错误的异常
     */
    @Override
    public void onError(String typeName, ClassLoader classLoader, JavaModule module,
                        boolean loaded, Throwable throwable) {
        System.err.println("[Agent] 处理类时出错: " + typeName);
        throwable.printStackTrace();
    }

    /**
     * 当Agent完成所有类的处理时触发(每个类单独触发)
     *
     * @param typeName    已处理类全限定名
     * @param classLoader 加载该类的类加载器
     * @param module      类所属的Java模块
     * @param loaded      类是否已被JVM加载
     */
    @Override
    public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
        if (typeName.contains("com.dwl.aop.agent")) {
            System.out.println("[Agent] 处理完成: " + typeName);
        }
    }
}


package com.dwl.spring_case_agent;

import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import org.springframework.util.StopWatch;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @ClassName MethodLogAdvice
 * @Description 方法执行日志增强工具类
 * 用Byte Buddy的Advice机制,在方法执行前后插入日志记录逻辑
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
public class MethodLogAdvice {

    /**
     * 方法进入时的增强逻辑(@Advice.OnMethodEnter注解标记)
     * @param method 当前执行的方法对象(包含类名、方法名等信息)
     * @param args 方法入参数组(可能为null)
     * @param stopWatch 局部变量(用于计时,需通过@Advice.Local声明)
     */
    @Advice.OnMethodEnter
    static void onMethodEnter(
            @Advice.Origin Method method,
            @Advice.AllArguments Object[] args,
            @Advice.Local("stopWatch") StopWatch stopWatch) {

        // 初始化计时器并开始计时
        stopWatch = new StopWatch();
        stopWatch.start();

        // 构造方法标识(类名.方法名)
        String methodName = method.getDeclaringClass().getSimpleName() + "." + method.getName();

        // 处理入参信息(处理null情况)
        String argsInfo = (args != null && args.length > 0) ? Arrays.toString(args) : "无入参";

        System.out.printf("[方法开始] %s | 入参: %s%n", methodName, argsInfo);
    }


    /**
     * 方法退出时的增强逻辑(@Advice.OnMethodExit注解标记,捕获异常)
     * @param method 当前执行的方法对象
     * @param throwable 方法执行过程中抛出的异常(可能为null)
     * @param stopWatch 局部变量(用于获取计时结果)
     * @param result 方法返回值(动态类型,可能为null)
     */
    @Advice.OnMethodExit(onThrowable = Throwable.class)
    static void onMethodExit(
            @Advice.Origin Method method,
            @Advice.Thrown Throwable throwable,
            @Advice.Local("stopWatch") StopWatch stopWatch,
            @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result) {

        // 防御性检查(理论上不会为null,防止意外情况)
        if (stopWatch == null) {
            return;
        }

        // 停止计时并获取耗时(毫秒)
        stopWatch.stop();
        long durationMs = stopWatch.getTotalTimeMillis();

        // 构造方法标识
        String methodName = method.getDeclaringClass().getSimpleName() + "." + method.getName();

        // 处理返回值信息(区分void类型和null值)
        String resultInfo;
        if (method.getReturnType() == void.class) {
            resultInfo = "void(无返回值)";
        } else {
            resultInfo = (result != null) ? result.toString() : "null";
        }

        // 根据是否有异常输出不同级别的日志
        if (throwable != null) {
            System.err.printf("[方法异常] %s | 耗时: %dms | 异常: %s%n", methodName, durationMs, throwable.getMessage());
        } else {
            System.out.printf("[方法结束] %s | 耗时: %dms | 返回值: %s%n", methodName, durationMs, resultInfo);
        }
    }
}


package com.dwl.spring_case_agent;

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

/**
 * @ClassName SpringAgent
 * @Description Java Agent入口类
 * 通过premain方法初始化字节码增强代理,配置需要增强的类和方法
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
public class SpringAgent {

    /**
     * JVM启动时通过-javaagent参数调用的入口方法
     * -javaagent:"F:/idea_works_pase/workspace/TestYourself/spring_case/src/main/resources/lib/spring_case_agent-city-luoYang.jar"
     *
     * @param agentArgs 代理参数(当前未使用,可扩展)
     * @param inst      Instrumentation实例(JVM提供的字节码操作接口)
     */
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("SpringAgent启动,开始初始化字节码增强...");

        // 配置并构建AgentBuilder
        new AgentBuilder.Default()
                // 设置自定义监听器(过滤无关日志)
                .with(new CustomAgentListener())
                // 匹配需要增强的类:包路径以"com.dwl.aop.agent"开头的类
                // (可根据实际需求调整匹配规则,如更精确的类名匹配)
                .type(ElementMatchers.nameStartsWith("com.dwl.aop.agent")
                        .and(ElementMatchers.not(ElementMatchers.isInterface())) // 排除接口
                        .and(ElementMatchers.not(ElementMatchers.isAbstract())) // 排除抽象类
                        .and(ElementMatchers.not(ElementMatchers.nameContains("$$SpringCGLIB$$")))) // 排除CGLIB代理类

                // 定义类的字节码转换逻辑
                .transform(
                        (DynamicType.Builder<?> builder,
                         TypeDescription typeDescription,
                         ClassLoader classLoader,
                         JavaModule module,
                         ProtectionDomain protectionDomain) -> {

                            // 定义方法匹配规则:
                            // - 匹配所有方法(ElementMatchers.any())
                            // - 排除getter方法(isGetter())
                            // - 排除setter方法(isSetter())
                            // (避免记录简单属性访问方法的日志)
                            return builder.method(
                                            ElementMatchers.any()
                                                    .and(ElementMatchers.not(ElementMatchers.isStatic())) // 排除静态方法
                                                    .and(ElementMatchers.not(ElementMatchers.isGetter()))
                                                    .and(ElementMatchers.not(ElementMatchers.isSetter())))
                                    // 应用方法日志增强(使用MethodLogAdvice类)
                                    .intercept(Advice.to(MethodLogAdvice.class));
                        })

                // 配置重定义策略:允许对已加载的类进行重转换(支持运行时增强)
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)

                // 安装代理到Instrumentation实例(完成字节码增强)
                .installOn(inst);

        System.out.println("SpringAgent初始化完成,字节码增强生效!");
    }


}


package com.dwl.aop.jdk_proxy;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @ClassName JdkProxyCase
 * @Description JDK动态代理示例类
 * JDK动态代理核心特点:
 * 1. 只能基于接口生成代理(运行时动态实现接口)
 * 2. 代理类由JDK动态生成(继承Proxy类并实现目标接口)
 * 3. 方法调用通过InvocationHandler转发
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Slf4j
public class JdkProxyCase {

    /**
     * 目标接口:定义需要代理的业务方法
     */
    interface JdkProxyInterfaceCase {
        void foo(); // 业务方法
    }

    /**
     * 目标对象实现类:真正执行业务逻辑的对象
     */
    static class JdkProxyTarget implements JdkProxyInterfaceCase {
        /**
         * 业务方法实现
         */
        @Override
        public void foo() {
            log.info("Target#foo() 执行具体业务逻辑"); // 实际业务输出
        }
    }

    /**
     * 主方法:演示JDK动态代理完整流程
     */
    public static void main(String[] args) {
        /* -------------------- 步骤1:创建目标对象 ------------------- */
        JdkProxyTarget target = new JdkProxyTarget();
        log.info("目标对象创建完成:{}", target); // 输出目标对象内存地址

        /* -------------------- 步骤2:生成动态代理对象 ------------------- */
        /*
         * Proxy.newProxyInstance 核心作用:
         * 1. 根据类加载器、接口列表动态生成代理类字节码
         * 2. 加载字节码生成Class对象
         * 3. 通过反射创建代理类实例(实现了目标接口)
         * 4. 将InvocationHandler绑定到代理实例
         *
         * 参数说明:
         * loader:类加载器(用于加载代理类),通常使用目标接口的类加载器
         * interfaces:代理需要实现的接口数组(必须全部是接口类型)
         * h:调用处理器(实际处理方法调用的逻辑)
         */
        ClassLoader loader = JdkProxyCase.class.getClassLoader(); // 获取当前类的类加载器
        JdkProxyInterfaceCase proxyInstance = (JdkProxyInterfaceCase) Proxy.newProxyInstance(
                loader,
                new Class<?>[]{JdkProxyInterfaceCase.class}, // 指定要代理的接口
                new InvocationHandler() { // 自定义调用处理器(此处用Lambda简化)
                    /**
                     * 所有通过代理对象调用的方法都会被转发到这里
                     * @param proxy 代理对象本身(注意:不要在invoke中调用proxy的方法,可能导致循环调用)
                     * @param method 被调用的方法(Method对象)
                     * @param args 方法参数数组
                     * @return 方法返回值
                     * @throws Throwable 方法调用异常
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        log.info("进入InvocationHandler.invoke(),准备处理方法调用");
                        log.info("被调用的方法:{},参数:{}", method.getName(), args);

                        /* -------------------- 步骤3:前置增强 ------------------- */
                        log.info("【前置通知】开始执行业务方法前的操作");

                        /* -------------------- 步骤4:调用目标对象方法 ------------------- */
                        /*
                         * method.invoke(target, args) 核心作用:
                         * 通过反射调用目标对象的指定方法
                         * 注意:如果目标方法是private/protected,需要设置setAccessible(true)
                         */
                        Object result = method.invoke(target, args); // 调用目标方法

                        /* -------------------- 步骤5:后置增强 ------------------- */
                        log.info("【后置通知】业务方法执行完成,处理后续操作");

                        return result; // 返回目标方法的执行结果
                    }
                }
        );

        log.info("动态代理对象创建完成:{}", proxyInstance); // 输出代理对象内存地址

        /* -------------------- 步骤6:通过代理对象调用业务方法 ------------------- */
        log.info("开始通过代理对象调用foo()方法");
        proxyInstance.foo(); // 触发代理逻辑
        log.info("代理对象foo()方法调用完成");
    }
}


package com.dwl.aop.cglib_proxy;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;

/**
 * @ClassName CglibProxyCase
 * @Description CGLIB动态代理示例类
 * CGLIB(Code Generation Library)核心特点:
 * 1. 基于继承实现代理(无需目标类实现接口)
 * 2. 通过字节码增强技术生成目标类的子类作为代理
 * 3. 代理类会覆盖目标类的非final方法(final方法无法被覆盖,因此无法代理)
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Slf4j
public class CglibProxyCase {

    /**
     * 主方法:演示CGLIB动态代理完整流程(包含多种目标类场景验证)
     */
    public static void main(String[] args) {
        /* -------------------- 场景1:普通目标类(非final方法) ------------------- */
        CglibProxyTargetA targetA = new CglibProxyTargetA();
        // 通过Enhancer创建代理对象(继承自CglibProxyTargetA)
        CglibProxyTargetA proxyTargetA = (CglibProxyTargetA) Enhancer.create(
                CglibProxyTargetA.class,  // 目标类(代理将继承此类)
                createMethodInterceptor(targetA)  // 方法拦截器(核心逻辑)
        );
        log.info("【场景1】调用普通目标类的代理方法");
        proxyTargetA.foo();  // 触发代理逻辑


        /* -------------------- 场景2:另一个普通目标类(验证多实例代理) ------------------- */
        try {
            CglibProxyTargetB targetB = new CglibProxyTargetB();
            CglibProxyTargetB proxyTargetB = (CglibProxyTargetB) Enhancer.create(
                    CglibProxyTargetB.class,
                    createMethodInterceptor(targetB)
            );
            log.info("\n【场景2】调用另一个普通目标类的代理方法");
            proxyTargetB.foo();
        } catch (Exception e) {
            log.error("场景2执行异常:{}", e.getMessage());
        }


        /* -------------------- 场景3:包含final方法的目标类(验证CGLIB限制) ------------------- */
        CglibProxyTargetC targetC = new CglibProxyTargetC();
        CglibProxyTargetC proxyTargetC = (CglibProxyTargetC) Enhancer.create(
                CglibProxyTargetC.class,
                createMethodInterceptor(targetC)
        );
        log.info("\n【场景3】调用包含final方法的目标类的代理方法");
        proxyTargetC.foo();  // final方法不会被代理,直接调用目标类方法


        /* -------------------- 场景4:使用proxy.invoke()(CGLIB推荐方式) ------------------- */
        CglibProxyTargetD targetD = new CglibProxyTargetD();
        CglibProxyTargetD proxyTargetD = (CglibProxyTargetD) Enhancer.create(
                CglibProxyTargetD.class,
                (MethodInterceptor) (obj, method, args1, proxy) -> {
                    log.info("【场景4】前置通知");
                    // CGLIB推荐方式:通过MethodProxy调用目标方法(性能优于反射)
                    Object ob = proxy.invoke(targetD, args1);  // 关键差异点
                    log.info("【场景4】后置通知");
                    return ob;
                }
        );
        log.info("\n【场景4】调用使用proxy.invoke()的代理方法");
        proxyTargetD.foo();


        /* -------------------- 场景5:使用proxy.invokeSuper()(代理自身方法) ------------------- */
        CglibProxyTargetE proxyTargetE = (CglibProxyTargetE) Enhancer.create(
                CglibProxyTargetE.class,
                (MethodInterceptor) (obj, method, args1, proxy) -> {
                    log.info("【场景5】前置通知");
                    // 特殊场景:调用代理类自身的方法(而非目标类)
                    Object ob = proxy.invokeSuper(obj, args1);  // 关键差异点
                    log.info("【场景5】后置通知");
                    return ob;
                }
        );
        log.info("\n【场景5】调用使用proxy.invokeSuper()的代理方法");
        proxyTargetE.foo();
    }

    /**
     * 工具方法:创建通用MethodInterceptor(简化代码重复)
     */
    private static MethodInterceptor createMethodInterceptor(Object target) {
        return (obj, method, args1, proxy) -> {
            log.info("[通用拦截器] 前置通知:准备执行方法 {}", method.getName());
            Object result = method.invoke(target, args1);  // 反射调用目标方法
            log.info("[通用拦截器] 后置通知:方法 {} 执行完成", method.getName());
            return result;
        };
    }

    /**
     * 目标类A(普通类,非final方法)
     */
    static class CglibProxyTargetA {
        public void foo() {
            log.info("CglibProxyTargetA#foo() 执行具体业务逻辑");
        }
    }

    /**
     * 目标类B(普通类,验证多实例代理)
     */
    static class CglibProxyTargetB {
        public void foo() {
            log.info("CglibProxyTargetB#foo() 执行具体业务逻辑");
        }
    }

    /**
     * 目标类C(包含final方法,验证CGLIB限制)
     */
    static class CglibProxyTargetC {
        public final void foo() {  // final方法无法被CGLIB覆盖
            log.info("CglibProxyTargetC#foo() 执行具体业务逻辑(final方法)");
        }
    }

    /**
     * 目标类D(普通类,验证proxy.invoke())
     */
    static class CglibProxyTargetD {
        public void foo() {
            log.info("CglibProxyTargetD#foo() 执行具体业务逻辑");
        }
    }

    /**
     * 目标类E(普通类,验证proxy.invokeSuper())
     */
    static class CglibProxyTargetE {
        public void foo() {
            log.info("CglibProxyTargetE#foo() 执行具体业务逻辑");
        }
    }
}