likes
comments
collection
share

理解Spring AOP的基础--动态代理技术

作者站长头像
站长
· 阅读数 28

理解Spring AOP的基础--动态代理技术

Spring AOP(面向切面编程)的基础原理主要基于代理模式(Proxy Pattern)。代理模式允许通过代理对象来控制对目标对象的访问,进而在目标对象的方法调用前后添加额外的行为。Spring AOP的实现主要依赖于以下两个技术:

  1. JDK动态代理(JDK Dynamic Proxy)
  2. CGLIB代理(Code Generation Library)

1. JDK动态代理

JDK动态代理是Java标准库提供的一种代理机制,它主要用于为实现了接口的类创建代理对象。JDK动态代理基于反射机制,通过在运行时动态生成代理类来实现。

工作原理:

  • 接口要求:目标类必须实现一个或多个接口。
  • 代理类生成:使用java.lang.reflect.Proxy类动态生成代理类。
  • InvocationHandler接口:代理类会将所有方法调用委托给一个实现了InvocationHandler接口的处理器对象。

示例代码:

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

public class JDKDynamicProxyExample {
    interface Service {
        void perform();
    }

    static class ServiceImpl implements Service {
        @Override
        public void perform() {
            System.out.println("Executing service...");
        }
    }

    static class ServiceInvocationHandler implements InvocationHandler {
        private final Object target;

        public ServiceInvocationHandler(Object target) {
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Before method call");
            Object result = method.invoke(target, args);
            System.out.println("After method call");
            return result;
        }
    }

    public static void main(String[] args) {
        Service target = new ServiceImpl();
        Service proxy = (Service) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new ServiceInvocationHandler(target)
        );

        proxy.perform();
    }
}

2. CGLIB代理

CGLIB(Code Generation Library)是一个开源项目,它通过字节码操作生成目标类的子类,从而实现代理。CGLIB代理不要求目标类实现接口,因此可以代理普通类。

工作原理:

  • 字节码生成:CGLIB使用ASM库操作字节码,在运行时生成目标类的子类。
  • 方法拦截:代理类会拦截所有方法调用,并将其委托给一个实现了MethodInterceptor接口的处理器对象。

示例代码:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLIBProxyExample {
    static class Service {
        public void perform() {
            System.out.println("Executing service...");
        }
    }

    static class ServiceMethodInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("Before method call");
            Object result = proxy.invokeSuper(obj, args);
            System.out.println("After method call");
            return result;
        }
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service.class);
        enhancer.setCallback(new ServiceMethodInterceptor());

        Service proxy = (Service) enhancer.create();
        proxy.perform();
    }
}

Spring AOP的实现

Spring AOP自动选择使用JDK动态代理或CGLIB代理来创建代理对象:

  • JDK动态代理:如果目标类实现了接口,Spring AOP会优先使用JDK动态代理。
  • CGLIB代理:如果目标类没有实现接口,Spring AOP会使用CGLIB代理。

Spring AOP通过AnnotationAwareAspectJAutoProxyCreator类在Spring容器初始化bean时拦截bean的创建过程,根据切点条件为bean创建合适的代理对象。代理对象会在方法调用前后执行通知逻辑,从而实现AOP功能。

代理模式的优势

  • 解耦:将横切关注点(如日志记录、事务管理)与业务逻辑分离。
  • 可维护性:横切关注点的代码集中管理,便于维护和修改。
  • 灵活性:可以在不修改目标对象代码的情况下添加或移除横切逻辑。

通过上述原理,Spring AOP能够有效地实现面向切面编程,增强代码的可维护性和灵活性。

ByteBuddy

ByteBuddy 学习指南

ByteBuddy 是一个现代化的 Java 字节码操作库,提供了简洁的 API 来动态生成和修改类。它比 Javassist 和 ASM 更易用,且功能强大。本文将为 Java 程序员提供一份学习指南,帮助快速掌握 ByteBuddy 的核心能力。

目录

  1. 简介
  2. 安装
  3. 基本概念
  4. 创建动态类
  5. 修改现有类
  6. 方法拦截
  7. 字节码操作
  8. 实践示例
  9. 参考资料

1. 简介

ByteBuddy 是一个用于 Java 字节码操作的库。它允许在运行时生成、修改和操作类。ByteBuddy 的设计目标是提供一个高层次的 API,使得字节码操作变得简单和直观。

2. 安装

在项目中使用 ByteBuddy,首先需要在 pom.xml 文件中添加 Maven 依赖:

<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy</artifactId>
    <version>1.12.0</version>
</dependency>

3. 基本概念

  • Dynamic Type:动态生成的类。
  • Interceptor:方法拦截器,用于在方法调用前后执行额外逻辑。
  • Agent:Java 代理,用于在 JVM 启动时或运行时修改类。

4. 创建动态类

使用 ByteBuddy 可以轻松创建动态类。以下示例展示了如何创建一个简单的动态类:

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FixedValue;

public class DynamicClassExample {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Class<?> dynamicType = new ByteBuddy()
            .subclass(Object.class)
            .name("com.example.DynamicType")
            .method(named("toString")).intercept(FixedValue.value("Hello ByteBuddy!"))
            .make()
            .load(DynamicClassExample.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
            .getLoaded();

        Object instance = dynamicType.newInstance();
        System.out.println(instance.toString()); // 输出: Hello ByteBuddy!
    }
}

5. 修改现有类

ByteBuddy 还可以用于修改现有类。以下示例展示了如何修改现有类的方法:

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;

public class ModifyExistingClassExample {
    public static void main(String[] args) throws Exception {
        new ByteBuddy()
            .redefine(Greeting.class)
            .method(ElementMatchers.named("sayHello"))
            .intercept(FixedValue.value("Hello ByteBuddy!"))
            .make()
            .load(Greeting.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());

        Greeting greeting = new Greeting();
        System.out.println(greeting.sayHello()); // 输出: Hello ByteBuddy!
    }
}

class Greeting {
    public String sayHello() {
        return "Hello World!";
    }
}

6. 方法拦截

ByteBuddy 提供了多种方法拦截机制。以下示例展示了如何使用 MethodDelegation 进行方法拦截:

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;

public class MethodInterceptorExample {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Class<?> dynamicType = new ByteBuddy()
            .subclass(Service.class)
            .method(ElementMatchers.named("perform"))
            .intercept(MethodDelegation.to(Interceptor.class))
            .make()
            .load(MethodInterceptorExample.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
            .getLoaded();

        Service service = (Service) dynamicType.newInstance();
        service.perform(); // 输出: Before method call, Executing service..., After method call
    }

    public static class Service {
        public void perform() {
            System.out.println("Executing service...");
        }
    }

    public static class Interceptor {
        public static void intercept() {
            System.out.println("Before method call");
            // 调用原始方法
            System.out.println("After method call");
        }
    }
}

7. 字节码操作

ByteBuddy 提供了对字节码的低级操作,使得可以实现复杂的字节码修改。以下示例展示了如何使用 Advice 注解来插入字节码:

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.matcher.ElementMatchers;

public class BytecodeManipulationExample {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Class<?> dynamicType = new ByteBuddy()
            .subclass(Service.class)
            .method(ElementMatchers.named("perform"))
            .intercept(Advice.to(LoggingAdvice.class))
            .make()
            .load(BytecodeManipulationExample.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
            .getLoaded();

        Service service = (Service) dynamicType.newInstance();
        service.perform(); // 输出: Before method call, Executing service..., After method call
    }

    public static class Service {
        public void perform() {
            System.out.println("Executing service...");
        }
    }

    public static class LoggingAdvice {
        @Advice.OnMethodEnter
        public static void onEnter() {
            System.out.println("Before method call");
        }

        @Advice.OnMethodExit
        public static void onExit() {
            System.out.println("After method call");
        }
    }
}

8. 实践示例

以下是一个综合示例,展示了如何使用 ByteBuddy 动态生成类、修改现有类以及方法拦截:

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;

public class ByteBuddyPractice {
    public static void main(String[] args) throws Exception {
        // 动态生成类
        Class<?> dynamicType = new ByteBuddy()
            .subclass(Object.class)
            .name("com.example.DynamicType")
            .method(ElementMatchers.named("toString")).intercept(FixedValue.value("Hello ByteBuddy!"))
            .make()
            .load(ByteBuddyPractice.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
            .getLoaded();

        Object instance = dynamicType.newInstance();
        System.out.println(instance.toString()); // 输出: Hello ByteBuddy!

        // 修改现有类
        new ByteBuddy()
            .redefine(Greeting.class)
            .method(ElementMatchers.named("sayHello"))
            .intercept(FixedValue.value("Hello ByteBuddy!"))
            .make()
            .load(Greeting.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());

        Greeting greeting = new Greeting();
        System.out.println(greeting.sayHello()); // 输出: Hello ByteBuddy!

        // 方法拦截
        Class<?> serviceType = new ByteBuddy()
            .subclass(Service.class)
            .method(ElementMatchers.named("perform"))
            .intercept(MethodDelegation.to(Interceptor.class))
            .make()
            .load(ByteBuddyPractice.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
            .getLoaded();

        Service service = (Service) serviceType.newInstance();
        service.perform(); // 输出: Before method call, Executing service..., After method call
    }

    public static class Greeting {
        public String sayHello() {
            return "Hello World!";
        }
    }

    public static class Service {
        public void perform() {
            System.out.println("Executing service...");
        }
    }

    public static class Interceptor {
        public static void intercept() {
            System.out.println("Before method call");
            // 调用原始方法
            System.out.println("After method call");
        }
    }
}

9. 参考资料

转载自:https://juejin.cn/post/7372084329079832586
评论
请登录