1. 写在前面
做 Java 开发的朋友,十有八九都听过 Spring AOP 的大名。日志、事务、权限校验,这些“横切逻辑”靠它一揽包收。可很多人用归用,真要问一句“AOP 底层到底怎么实现的”,往往就卡壳了。
其实剥开 Spring 那层壳,核心就两样东西:反射和动态代理。今天咱们不啃源码,直接手写一个迷你版 AOP,把这两块底裤看个明白。
2. 反射:Java 的“照妖镜”
反射说白了,就是程序在运行的时候,还能“偷看”自己——拿到类的结构、方法、字段,甚至直接调用。平时写代码是编译期就定死了,反射则把这一切推迟到运行期。
看个最简单的例子:
Class<?> clazz = UserService.class; Method method = clazz.getMethod("sayHello", String.class); Object result = method.invoke(new UserServiceImpl(), "张三"); System.out.println(result);这段代码干的事,就是绕过编译期的类型检查,运行时才找到sayHello方法并调用。AOP 里要“拦截方法”,第一步就得靠反射把方法“揪”出来。
3. 动态代理:给对象套一层“马甲”
光有反射还不够。AOP 的核心思路是:不修改原类,却在方法执行前后插入额外逻辑。这就需要动态代理——运行时生成一个“马甲”对象,替原对象挡在前面。
JDK 自带的动态代理基于接口实现,写起来也不复杂:
public class LogProxy implements InvocationHandler { private Object target; public LogProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法执行前:记录日志"); Object result = method.invoke(target, args); System.out.println("方法执行后:清理资源"); return result; } public static Object wrap(Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new LogProxy(target)); } }调用的时候,把真实对象传进去,拿回来的“马甲”就能在方法前后自动加料:
UserService service = (UserService) LogProxy.wrap(new UserServiceImpl()); service.sayHello("李四");4. 手写一个迷你 AOP
有了上面两块积木,拼一个简化版 AOP 就顺理成章了。思路很简单:定义一个切面类,里面写“前置通知”和“后置通知”,再用动态代理把它们织入目标方法。
public class MiniAop { public static Object apply(Object target, Runnable before, Runnable after) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> { before.run(); Object result = method.invoke(target, args); after.run(); return result; }); } }用起来也直白:
UserService proxy = (UserService) MiniAop.apply( new UserServiceImpl(), () -> System.out.println("开启事务"), () -> System.out.println("提交事务"));跑一下,控制台会依次打印“开启事务”“业务方法执行”“提交事务”。Spring AOP 干的事,本质上就是这一套,只不过它把“通知”抽象成了注解或配置,把“织入”交给了 CGLIB 或 JDK 代理自动选择。
5. 和 Spring AOP 对上号
看到这儿,再回头看 Spring AOP 的几个概念,是不是亲切多了?
- 切面(Aspect):就是那个装了通知的类,对应我们手写的
MiniAop。 - 通知(Advice):前置、后置、环绕,对应
before和after两个回调。 - 切点(Pointcut):决定哪些方法要被拦截,对应我们手动指定的目标对象。
- 织入(Weaving):把通知塞进目标方法的过程,对应
Proxy.newProxyInstance。
Spring 比我们多做的,主要是两件事:一是用@Aspect、@Before这些注解省去手写代理的繁琐;二是目标类没实现接口时,自动改用 CGLIB 生成子类代理。原理,还是那个原理。
6. 小结
反射给了 Java 运行时“看清自己”的能力,动态代理则让“偷梁换柱”成为可能。两者一组合,AOP 的底裤就被扒干净了。下次再有人问你 Spring AOP 底层是啥,你可以淡定地回一句:反射 + 动态代理,外加一点设计巧思。
代码都贴在上面了,建议自己动手跑一遍。光看不练,等于白看。