【Spring -- 09 AOP 实现方式】

在 Java Spring 框架中,AOP (面向切面编程) 的代理实现主要通过以下几种方式:

  1. JDK 动态代理:

    • JDK 动态代理是基于 Java 的 java.lang.reflect.Proxy 类实现的.
    • 当需要代理的目标对象实现了一个或多个接口时,Spring 会选择使用 JDK 动态代理.
    • JDK 动态代理的工作原理如下:
      1. 首先,Spring 会为目标对象的接口创建一个动态代理类.
      2. 这个动态代理类会实现目标对象的所有接口,并且包含与目标对象相同的方法签名.
      3. 在动态代理类的方法实现中,会首先执行切面逻辑,然后再调用目标对象的对应方法.
      4. 最后,Spring 会在运行时生成动态代理类的实例,并将其作为目标对象的代理使用.
    • 使用 JDK 动态代理的优点是无需依赖第三方库,可以在纯 Java 环境下运行.但缺点是只能代理接口,不能代理具体的实现类.
  2. CGLIB 代理:

    • CGLIB (Code Generation Library) 是一个第三方字节码生成库.
    • 当需要代理的目标对象没有实现任何接口时,Spring 会选择使用 CGLIB 代理.
    • CGLIB 代理的工作原理如下:
      1. CGLIB 会为目标对象生成一个子类.
      2. 在子类中,CGLIB 会重写目标对象的所有非 final 方法.
      3. 在这些重写方法的实现中,CGLIB 会先执行切面逻辑,然后再调用父类(目标对象)的对应方法.
      4. 最后,Spring 会使用 CGLIB 生成的子类作为目标对象的代理.
    • 使用 CGLIB 代理的优点是可以代理具体的实现类,而不仅限于接口.但缺点是需要依赖第三方库,并且对于 final 方法无法进行代理.
  3. AspectJ 编译时织入:

    • 除了 JDK 动态代理和 CGLIB 代理,Spring 还支持使用 AspectJ 进行编译时织入.
    • 在编译阶段,AspectJ 会将切面逻辑织入到目标类中,生成包含切面逻辑的字节码文件.
    • 这种方式的优点是执行效率高,但需要在编译时进行特殊处理.

Spring 会根据目标对象的情况自动选择合适的代理方式。如果目标对象实现了接口,则使用 JDK 动态代理;否则使用 CGLIB 代理。开发者也可以通过配置显式指定使用哪种代理方式。

在实现 AOP 功能时,Spring 会在运行时动态地为目标对象生成代理对象,并在代理对象中注入切面逻辑。这些代理对象会替代原始的目标对象,从而实现在不修改目标对象源码的情况下添加切面功能。

JDK 动态代理示例

假设有一个接口 HelloService,以及它的实现类 HelloServiceImpl:

public interface HelloService {
    void sayHello();
}

public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("Hello from HelloServiceImpl!");
    }
}

使用 JDK 动态代理来创建 HelloService 的代理对象:

public class JdkProxyExample {
    public static void main(String[] args) {
        // 创建目标对象
        HelloService helloService = new HelloServiceImpl();

        // 创建 InvocationHandler 实现类
        InvocationHandler handler = (proxy, method, args1) -> {
            System.out.println("Before method invocation");
            method.invoke(helloService, args1);
            System.out.println("After method invocation");
            return null;
        };

        // 使用 Proxy 生成动态代理对象
        HelloService proxy = (HelloService) Proxy.newProxyInstance(
                HelloService.class.getClassLoader(),
                new Class[]{HelloService.class},
                handler
        );

        // 调用代理对象的方法
        proxy.sayHello();
    }
}

在这个示例中:

  1. 创建了一个 InvocationHandler 实现类,它包含了切面逻辑(打印方法调用前后的信息)。
  2. 然后使用 Proxy.newProxyInstance() 方法创建了 HelloService 的动态代理对象。
  3. 最后,我们调用代理对象的 sayHello() 方法,它会先执行切面逻辑,然后再调用目标对象的对应方法。

CGLIB 代理示例

假设有一个 HelloServiceImpl 类,但它没有实现任何接口:

public class HelloServiceImpl {
    public void sayHello() {
        System.out.println("Hello from HelloServiceImpl!");
    }
}

使用 CGLIB 代理来创建 HelloServiceImpl 的代理对象:

public class CglibProxyExample {
    public static void main(String[] args) {
        // 创建 CGLIB 增强器
        Enhancer enhancer = new Enhancer();

        // 设置需要代理的目标类
        enhancer.setSuperclass(HelloServiceImpl.class);

        // 设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("Before method invocation");
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("After method invocation");
                return result;
            }
        });

        // 生成代理对象
        HelloServiceImpl proxy = (HelloServiceImpl) enhancer.create();

        // 调用代理对象的方法
        proxy.sayHello();
    }
}

在这个示例中:

  1. 创建了一个 CGLIB 的 Enhancer 对象,并设置需要代理的目标类 HelloServiceImpl
  2. 然后,我们设置了一个 MethodInterceptor 回调函数,它包含了切面逻辑(打印方法调用前后的信息)。
  3. 最后,我们使用 Enhancer.create() 方法生成了 HelloServiceImpl 的代理对象,并调用了它的 sayHello() 方法。

在这个示例中,CGLIB 动态生成了 HelloServiceImpl 的子类,并在子类中重写了 sayHello() 方法,在方法实现中添加了切面逻辑。

总的来说,JDK 动态代理和 CGLIB 代理都是通过动态生成代理类的方式,在不修改目标对象源码的情况下,实现了切面功能的添加。开发者可以根据实际需求选择合适的代理方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值