手写模拟SpringAOP三大过程:解析,创建代理,调用, 用通俗易懂的方式还原 Spring AOP 的核心逻辑。由浅入深解析 Spring AOP 的底层。
Spring AOP 的三大核心过程本质是:
// 1. 标记切面类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
}// 2. 定义切点(简化版,匹配方法名)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PointCut {
String value();
}// 3. 前置通知
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Before {
}// 4. 后置通知
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface After {
}public interface UserService {
void addUser();
void deleteUser();
}
public class UserServiceImpl implements UserService{
@Override
public void addUser() {
System.out.println("【目标方法】执行添加用户逻辑");
}
@Override
public void deleteUser() {
System.out.println("【目标方法】执行删除用户逻辑");
}
}// 切面类
@Aspect
public class LogAspect {
// 切点:匹配addUser方法
@PointCut(value = "addUser")
@Before
public void beforeAddUser() {
System.out.println("【前置通知】执行添加用户前的日志记录");
}
// 切点:匹配deleteUser方法
@PointCut("deleteUser")
@After // 后置通知
public void afterDeleteUser(){
System.out.println("【后置通知】执行删除用户后的日志记录");
}
}public class AopProxyFactory {
// 存储切点与通知的映射(简化版)
private static Map<String, Method> pointcutAdviceMap = new HashMap<>();
//第一步:解析切面类,初始化切点-通知映射
public static void parseAspect(Class<LogAspect> aspectClass) {
//1、判断是否是切面类
if (!aspectClass.isAnnotationPresent(Aspect.class)) {
return;
}
//2、遍历切面类的所有方法,解析切点和通知,存储到map
Method[] methods = aspectClass.getDeclaredMethods();
for (Method method : methods) {
//3、获取切点注解
if (method.isAnnotationPresent(PointCut.class)) {
PointCut pointCut = method.getAnnotation(PointCut.class);
String targetMethodName = pointCut.value();// 切点匹配的目标方法名
pointcutAdviceMap.put(targetMethodName,method);// 存储切点-通知映射
}
}
}
// 第二步:创建代理对象(JDK动态代理)
public static Object createProxy(Object target){
// 1. JDK动态代理:目标类必须实现接口
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 第三步:调用核心逻辑(织入通知+执行目标方法)
String methodName = method.getName();/// 当前执行的目标方法名
//1、执行前置通知
if (pointcutAdviceMap.containsKey(methodName)) {
Method adviceMethod = pointcutAdviceMap.get(methodName);
// 判断是否是前置通知
if (adviceMethod.isAnnotationPresent(Before.class)) {
adviceMethod.invoke(new LogAspect());// 执行前置通知
}
}
//2、执行目标方法
Object result = method.invoke(target, args);
//3、执行后置方法
if (pointcutAdviceMap.containsKey(methodName)) {
Method adviceMethod = pointcutAdviceMap.get(methodName);
if (adviceMethod.isAnnotationPresent(After.class)) {
adviceMethod.invoke(new LogAspect());
}
}
return result;
}
}
);
}
}public static void main(String[] args) {
//1、解析切面类
parseAspect(LogAspect.class);
//2、创建目标对象
UserService target = new UserServiceImpl();
//3、创建代理对象
UserService proxy = (UserService) createProxy(target);
//4、调用代理方法(触发通知+目标方法)
proxy.addUser();
System.out.println("------------------------");
proxy.deleteUser();
}
1、解析过程(parseAspect 方法):
@Aspect 注解,识别切面类;@Pointcut 注解,将 “目标方法名 - 通知方法” 存入映射表,完成切面规则的解析。2、创建代理过程(createProxy 方法):
Proxy.newProxyInstance)生成代理对象;InvocationHandler 接口,重写 invoke 方法封装调用逻辑。3、调用过程(invoke 方法):
method.invoke(target, args) 执行目标方法;理解 Spring AOP 的源码核心逻辑,会从核心流程 + 关键类 + 源码入口三个维度,由浅入深解析 Spring AOP 的底层实现
Spring Boot 中通过 @EnableAspectJAutoProxy 开启 AOP,这是源码的入口:
// 核心注解:开启AOP自动代理
@EnableAspectJAutoProxy
@Configuration
public class AppConfig {
}@EnableAspectJAutoProxy 会导入 AspectJAutoProxyRegistrar,注册核心 Bean:AnnotationAwareAspectJAutoProxyCreator。
这是 Spring AOP 的核心处理器(继承自 BeanPostProcessor),作用是:
1、源码入口:AnnotationAwareAspectJAutoProxyCreator 扫描容器中所有标注 @Aspect 的 Bean;
2、解析逻辑:
@Pointcut、@Before、@After 等注解;Advisor(切面顾问),包含「切点(Pointcut)+ 通知(Advice)」;ReflectiveAspectJAdvisorFactory#getAdvisors()(解析切面方法为 Advisor)。关键代码(简化)
// 解析@Aspect切面类,生成Advisor
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
// 遍历切面类的所有方法
for (Method method : getAdvisorMethods(aspectClass)) {
// 解析方法上的@Pointcut/@Before等注解,生成Advisor
Advisor advisor = getAdvisor(method, aspectInstanceFactory, 0, aspectClass);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}核心类:AspectJExpressionPointcut(支持切点表达式)
execution(* com..*.add*(..)))匹配目标方法;Pointcut#matches(Method method, Class<?> targetClass)(判断目标方法是否匹配切点);map 存储 “方法名 - 通知”,Spring 用 AspectJExpressionPointcut 解析表达式,更通用。核心类:DefaultAopProxyFactory、JdkDynamicAopProxy、CglibAopProxy
代理选择逻辑(源码核心)
// DefaultAopProxyFactory.createAopProxy() 核心逻辑
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 条件1:是否强制使用CGLIB | 条件2:目标类是接口 | 条件3:目标类是代理类
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
// 如果目标类是接口,还是用JDK代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 否则用CGLIB代理
return new ObjenesisCglibAopProxy(config);
} else {
// 默认:有接口用JDK代理
return new JdkDynamicAopProxy(config);
}
}核心类:JdkDynamicAopProxy(JDK 代理)、CglibAopProxy(CGLIB 代理)
JdkDynamicAopProxy.invoke())简化版
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 获取当前方法匹配的所有Advisor(切面顾问)
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 2. 如果没有切面,直接执行目标方法
if (chain.isEmpty()) {
return method.invoke(target, args);
}
// 3. 有切面:创建方法调用器,织入通知
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 4. 执行通知+目标方法(核心:责任链模式)
return invocation.proceed();
}2、核心设计:责任链模式
MethodInterceptor(方法拦截器);invocation.proceed() 会按顺序执行所有拦截器(通知),最后执行目标方法;类名 | 作用 |
|---|---|
AnnotationAwareAspectJAutoProxyCreator | 核心处理器,自动创建代理对象 |
ReflectiveAspectJAdvisorFactory | 解析 @Aspect 切面类,生成 Advisor |
AspectJExpressionPointcut | 解析切点表达式,匹配目标方法 |
DefaultAopProxyFactory | 选择 JDK/CGLIB 代理类型 |
JdkDynamicAopProxy/CglibAopProxy | 实现代理对象的创建和方法调用 |
ReflectiveMethodInvocation | 封装方法调用,执行责任链(通知 + 目标方法) |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。