理解Spring AOP的基础--动态代理技术
理解Spring AOP的基础--动态代理技术
Spring AOP(面向切面编程)的基础原理主要基于代理模式(Proxy Pattern)。代理模式允许通过代理对象来控制对目标对象的访问,进而在目标对象的方法调用前后添加额外的行为。Spring AOP的实现主要依赖于以下两个技术:
- JDK动态代理(JDK Dynamic Proxy)
- 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. 简介
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