Spring 框架的核心框架之一是 AOP。允许在不修改原有代码的情况下,给程序添加一些“通用功能”,比如:
记录日志: 谁调用了哪个方法?花了多长时间?
事务管理: 开始一个数据库操作前自动开启事务,结束后自动提交或回滚。
安全检查: 执行方法前看看用户有没有权限。
性能监控: 统计方法执行时间。
Spring 实现 AOP 的主要方式是使用“代理”。
想象一下,你(业务对象)本来要亲自做一件事(比如调用 saveUser() 方法保存用户)。但 Spring 想在你做事 之前 或 之后 加点“调料”(比如开启/提交事务)。它不能直接修改你的代码,怎么办?
Spring 会找一个“替身”(代理对象)站在你前面。当别人想找你做事时,实际上先找到这个替身。这个替身会:
①先做“调料”的事情(比如开启事务)。
②然后再把你叫出来,让你真正做事(调用你真正的 saveUser() 方法)。
③等你做完事,替身可能再做点“收尾调料”(比如提交事务)。
④最后把结果告诉最初找你的人。
Spring 主要有两种造替身的方法:
①JDK 动态代理: 这是 Java 自带的本事。
怎么造? 它要求你必须有一个“行为规范”(接口)。比如你有一个 UserService 接口,里面定义了 saveUser() 方法,然后你有一个 UserServiceImpl 类实现了这个接口。
怎么工作? JDK 代理会创建一个新的对象,这个新对象也实现了 UserService 接口。当别人调用这个代理对象的 saveUser() 时,代理就能在调用真正 UserServiceImpl 的 saveUser() 前后加入“调料”了。
限制: 如果某个类没有实现任何接口,JDK 代理就没办法了!因为它造替身的前提是必须有个接口可以模仿。
②CGLib 动态代理: 这就是 Spring 所需要的方案。
怎么造? 它不需要接口!CGLib 使用了一种“黑科技”(操作字节码),它直接继承你要代理的那个类(比如 UserServiceImplWithoutInterface),然后生成这个类的一个子类作为替身。
怎么工作? 这个生成的子类(代理对象)重写了父类(原始类)的方法。当别人调用这个子类对象的 saveUser()(即使这个方法不是来自接口,而是原始类自己定义的)时,子类方法就能在调用父类真正的 saveUser() 前后加入“调料”了。
优点: 它不需要接口!能代理那些没有实现接口的普通类(POJO)。
Spring 为什么需要 CGLib?
现实世界不是只有接口: 很多遗留代码、第三方库的类或者开发者自己写的简单类,并没有实现接口。Spring 想要给这些类也加上 AOP 的魔法(事务、日志等),JDK 代理就无能为力了。
灵活性: Spring 希望尽可能广泛地支持各种类型的 Bean(Spring 管理的对象)。如果只依赖 JDK 代理,那么所有想被 AOP 增强的类都必须实现接口,这限制太大了,不符合 Spring 方便易用的哲学。
代理类本身的方法: JDK 代理只能代理接口定义的方法。如果一个类有自己的方法(不是接口方法),即使它有接口,JDK 代理也无法代理那些非接口方法。CGLib 通过继承则可以代理类中的几乎所有方法(除了 final 的)。