mini-ssm Lab5 - AOP
在上一节Lab4中,我们讨论了依赖注入(DI),并解决了其中的核心问题。本节Lab5将实现AOP(面向切面编程)的基础功能,并在下一节实现AOP与Spring的集成。
实现动态代理的两种方式
- JDK代理(适用于接口)
- CGLIB代理(适用于没有接口的类)
JDK proxy
JDK代理(Java Dynamic Proxy)是Java内置的代理机制,基于Java反射API实现,主要用于接口代理。通过JDK代理,可以在运行时动态生成代理对象。
工作原理
JDK代理依赖java.lang.reflect.Proxy
类和InvocationHandler
接口。代理对象的方法调用会被invoke()
方法拦截,用于在方法调用前后增加通用功能(如日志、事务管理、权限控制等)。
主要组成
Proxy
类:用于生成代理实例,使用Proxy.newProxyInstance()
方法创建代理对象。InvocationHandler
接口:定义了invoke(Object proxy, Method method, Object[] args)
方法,用于处理方法调用。每次代理方法被调用时,invoke()
会被触发。
使用场景
JDK代理适用于以下场景:
- AOP编程:如Spring中的AOP,通过动态代理实现日志、权限验证等横切关注点。
- 事务管理:在方法调用前后自动开启和提交事务。
注意事项
- JDK代理要求代理对象必须实现接口。
- 对于未实现接口的类,需使用CGLIB等其他代理方式。
CGLIB代理
CGLIB代理(Code Generation Library)基于字节码生成技术,不依赖接口,而是通过生成子类实现代理。它常用于没有实现接口的类,广泛用于Spring AOP。
工作原理
CGLIB通过生成目标类的子类并覆盖其中的方法,来实现代理功能。方法调用会触发代理子类的同名方法,该方法会调用原方法并加入增强逻辑。
主要组成
Enhancer
类:CGLIB的核心类,用于生成代理对象,通过设置父类、回调等信息,生成子类代理对象。MethodInterceptor
接口:定义了intercept()
方法,类似JDK代理的InvocationHandler
,用于拦截目标对象的方法调用。
使用场景
CGLIB适用于以下场景:
- 没有接口的类:当类未实现接口时,CGLIB是适合的选择。
- AOP编程:为方法添加横切关注点,如日志、事务等。
注意事项
- CGLIB无法代理
final
类和final
方法。 - CGLIB代理基于字节码生成,性能通常优于JDK代理,但依赖额外库(如Spring环境下自动包含CGLIB)。
CGLIB与JDK代理对比
- 实现方式:JDK代理基于接口,CGLIB基于继承。
- 适用范围:JDK代理适用于实现接口的类;CGLIB适用于没有接口的类。
Spring AOP 核心概念
- 切面(Aspect):封装横切关注点的模块,包含一组增强逻辑。
- 连接点(Join Point):程序执行过程中的某个点(如方法调用、异常抛出)。
- 切入点(Pointcut):指定在哪些方法或连接点上应用增强。
- 通知(Advice):在连接点执行的增强逻辑,根据时机分为
@Before
、@After
、@AfterReturning
、@AfterThrowing
、@Around
等。 - 目标对象(Target Object):被代理的对象,即需要应用增强的对象。
public Object invoke(Object proxy, Method method, Object[] args) {
Object result;
try {
//@Before
result = method.invoke(target, args);
//@AfterReturning
return result;
} catch (InvocationTargetException e) {
Throwable targetException = e.getTargetException();
//@AfterThrowing
throw targetException;
} finally {
//@After
}
}
本节目标
解析Aspect
类生成Advice
,并通过JDK/CGLIB生成代理。
定义注解
Spring AOP中的切点表达式功能强大,在mini-ssm
中,我们实现常用的两种注解:
@annotation()
:匹配被指定注解修饰的方法。@within()
:匹配被指定注解修饰的类的所有方法。
以下是注解的定义:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Before {
Class<? extends Annotation>[] annotation() default {};
Class<? extends Annotation>[] within() default {};
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterThrowing {
Class<? extends Annotation>[] annotation() default {};
Class<? extends Annotation>[] within() default {};
Class<? extends Throwable>[] throwable() default Throwable.class;
}
其他注解如@Around
、@After
、@AfterReturning
的实现方式类似。
AOP实现
代理流程
- 解析
Aspect
类,根据注解类型生成不同的Advice
。 - 获取匹配的通知并构建拦截器链。
- 生成代理类。
AspectParser
解析Aspect类,并生成通知:
public abstract class AspectParser {
static final Map<Class<? extends Annotation>, List<MethodInterceptor>> withinMap = new HashMap<>();
static final Map<Class<? extends Annotation>, List<MethodInterceptor>> annotationMap = new HashMap<>();
private static final Map<Class<?>, BiFunction<Method,Object,MethodInterceptor>> supplierMap=new HashMap<>();
static {
supplierMap.put(After.class,AfterAdviceInterceptor::new);
supplierMap.put(AfterReturning.class,AfterReturningAdviceInterceptor::new);
supplierMap.put(Around.class,AroundAdviceInterceptor::new);
supplierMap.put(Before.class,BeforeAdviceInterceptor::new);
}
public static void parse(Object aspect) throws Exception {
Method[] methods = aspect.getClass().getDeclaredMethods();
for (Method m : methods) {
if(m.getAnnotations().length==0) continue;
parsing(After.class,m,aspect);
parsing(AfterReturning.class,m,aspect);
parsing(AfterThrowing.class,m,aspect);
parsing(Around.class,m,aspect);
parsing(Before.class,m,aspect);
}
}
}
AdvisedSupport
代理类需要的属性支持:
public class AdvisedSupport {
private Class<?> targetClass;
private Object target;
private final Map<Method, List<MethodInterceptor>> methodCache=new HashMap<>();
}
MethodInterceptor 实现
- BeforeAdviceInterceptor:在方法执行前调用通知。
- AroundAdviceInterceptor:将方法执行权交给通知。
- AfterThrowingAdviceInterceptor:捕获异常并执行通知方法。
JDK 动态代理实现
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
private final AdvisedSupport advised;
public JdkDynamicAopProxy(AdvisedSupport config) {
this.advised = config;
}
@Override
public Object getProxy() {
return getProxy(this.advised.getTargetClass().getClassLoader());
}
@Override
public Object getProxy(ClassLoader classLoader) {
return Proxy.newProxyInstance(classLoader, this.advised.getTargetClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取拦截器链
List<MethodInterceptor> interceptorsAndDynamicMethodMatchers =
this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, this.advised.getTargetClass());
这样,我们实现了AOP基础代理和注解支持。下一步将结合Spring AOP,将此实现与Spring框架整合。