Spring AOP 原理:代理模式与源码解析
Spring AOP 是基于动态代理来实现 AOP 的。
代理模式
代理模式,也叫委托模式。
定义
为其他对象提供一种代理以控制对这个对象的访问。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
使用代理前:

使用代理后:

代理模式的主要角色
- Subject: 业务接口类。可以是抽象类或者接口(不一定有)
- RealSubject: 业务实现类。具体的业务执行,也就是被代理对象。
- Proxy: 代理类。RealSubject 的代理。
UML 类图如下:

代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。 根据代理的创建时期,代理模式分为静态代理和动态代理。
• 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的.class 文件就已经存在了。 • 动态代理:在程序运行时,运用反射机制动态创建而成。
静态代理
静态代理:在程序运行前,代理类的 .class 文件就已经存在了。
(以房租租赁为例,在出租房子之前,中介已经做好了相关的工作,就等租户来租房子了)
下面我们通过代码来加深理解:
- 定义接口 (定义房东要做的事情,也是中介需要做的事情)
public interface HouseSubject {
void rentHouse();
}
- 实现接口 (房东出租房子)
public class RealHouseSubject implements HouseSubject {
@Override
public void rentHouse() {
System.out.println("我是房东,我出租房子");
}
}
- 代理 (中介,帮房东出租房子)
public class HouseProxy implements HouseSubject {
private HouseSubject target;
public HouseProxy(HouseSubject target) {
this.target = target;
}
@Override
public void rentHouse() {
//出租前
System.out.println("我是中介,开始代理");
//出租房子
target.rentHouse();
//出租后
System.out.println("我是中介,结束代理");
}
}
- 使用
public class Main {
public static void main(String[] args) {
//静态代理
HouseProxy proxy = new HouseProxy(new RealHouseSubject());
proxy.rentHouse();
}
}
运行结果:

上面这个代理实现方式就是静态代理。 从上述程序可以看出,虽然静态代理也完成了对目标对象的代理,但是由于代码都写死了,对目标对象的每个方法的增强都是手动完成的,非常不灵活。所以日常开发几乎看不到静态代理的场景。
接下来新增需求:中介又新增了其他业务:代理房屋出售,此时我们需要对上述代码进行修改:
- 接口定义修改
public interface HouseSubject {
void rentHouse();
void saleHouse();
}
- 接口实现修改
public class RealHouseSubject implements HouseSubject {
@Override
public void rentHouse() {
System.out.println("我是房东,我出租房子");
}
@Override
public void saleHouse() {
System.out.println("我是房东,我出售房子");
}
}
- 代理类修改
public class HouseProxy implements HouseSubject {
private HouseSubject target;
public HouseProxy(HouseSubject target) {
this.target = target;
}
@Override
public void rentHouse() {
//出租前
System.out.println("我是中介,开始代理");
//出租房子
target.rentHouse();
//出租后
System.out.println("我是中介,结束代理");
}
@Override
public void saleHouse() {
//出租前
System.out.println("我是中介,开始代理");
//出租房子
target.saleHouse();
//出租后
System.out.println("我是中介,结束代理");
}
}
从上述代码可以看出,我们修改接口和业务实现类时,还需要修改代理类。 同样的,如果有新增接口和业务实现类,也需要对每一个业务实现类新增代理类。 既然代理的流程是一样的,有没有一种办法,让他们通过一个代理类来实现呢? 这就需要用到动态代理技术了。
动态代理
相比于静态代理来说,动态代理更加灵活。 我们不需要针对每个目标对象都单独创建一个代理对象,而是把这个创建代理对象的工作推迟到程序运行时由 JVM 来实现。也就是说动态代理在程序运行时,根据需要动态创建生成。
Java 也对动态代理进行了实现,并给我们提供了一些 API,常见的实现方式有两种:
- JDK 动态代理
- CGLIB 动态代理
JDK 动态代理
JDK 动态代理类实现步骤:
- 定义一个接口及其实现类 (静态代理中的 HouseSubject 和 RealHouseSubject )
- 自定义 InvocationHandler 并重写 invoke 方法,在 invoke 方法中我们会调用目标方法 (被代理类的方法) 并自定义一些处理逻辑
- 通过 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 方法创建代理对象
- 定义一个接口
public interface HouseSubject {
void rentHouse();
void saleHouse();
}
- 定义一个实现类
public class RealHouseSubject implements HouseSubject {
@Override
public void rentHouse() {
System.out.println("我是房东,我出租房子");
}
@Override
public void saleHouse() {
System.out.println("我是房东,我出售房子");
}
}
- 实现 InvocationHandler 接口并重写 invoke 方法
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class JDKInvocationHandler implements InvocationHandler {
private Object target; // 目标对象
public JDKInvocationHandler(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;
}
}
- 创建一个代理对象并使用
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
//JDK 动态代理
//目标对象
RealHouseSubject target = new RealHouseSubject();
//动态生成代理对象 代理接口,运行成功
HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(
HouseSubject.class.getClassLoader(),
new Class[]{HouseSubject.class},
new JDKInvocationHandler(target));
proxy.rentHouse();
proxy.saleHouse();
}
}
运行结果:

接口介绍
InvocationHandler
InvocationHandler 接口是 Java 动态代理的关键接口之一,它定义了一个单一方法 invoke() ,用于处理被代理对象的方法调用。
public interface InvocationHandler {
/**
* 参数说明
* proxy:代理对象
* method:代理对象需要实现的方法,即其中需要重写的方法
* args:method 所对应方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
通过实现 InvocationHandler 接口,可以对被代理对象的方法进行功能增强。
Proxy
Proxy 类中使用频率最高的方法是:newProxyInstance() ,这个方法主要用来生成一个代理对象。

这个方法一共有 3 个参数:
Loader: 类加载器,用于加载代理对象。 interfaces : 被代理类实现的一些接口 (这个参数的定义,也决定了 JDK 动态代理只能代理实现了一些接口的类) h : 实现了 InvocationHandler 接口的对象
如果让 JDK 代理类的话
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
//JDK 动态代理
//目标对象
RealHouseSubject target = new RealHouseSubject();
//JDK 代理类
RealHouseSubject proxy = (RealHouseSubject) Proxy.newProxyInstance(
RealHouseSubject.class.getClassLoader(),
new Class[]{RealHouseSubject.class},
new JDKInvocationHandler(target));
proxy.rentHouse();
proxy.saleHouse();
}
}
运行结果:

CGLIB 动态代理
JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。 有些场景下,我们的业务代码是直接实现的,并没有接口定义。为了解决这个问题,我们可以用 CGLIB 动态代理机制来解决。
CGLIB(Code Generation Library) 是一个基于 ASM 的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理,很多知名的开源框架都使用到了 CGLIB。例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。
CGLIB 动态代理类实现步骤
- 定义一个类 (被代理类)
- 自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于增强目标方法,和 JDK 动态代理中的 invoke 方法类似
- 通过 Enhancer 类的 create() 创建代理类
代码示例
HouseSubject
public interface HouseSubject {
void rentHouse();
void saleHouse();
}
RealHouseSubject
public class RealHouseSubject implements HouseSubject {
@Override
public void rentHouse() {
System.out.println("我是房东,我出租房子");
}
@Override
public void saleHouse() {
System.out.println("我是房东,我出售房子");
}
}
CGLibMethodInterceptor
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLibMethodInterceptor implements MethodInterceptor {
private Object target;
public CGLibMethodInterceptor(Object target) {
this.target = target;
}
/**
* 调用代理对象的方法
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("我是中介,开始代理");
Object result = method.invoke(target, args);
System.out.println("我是中介,结束代理");
return result;
}
}
Main
import org.springframework.cglib.proxy.Enhancer;
public class Main {
public static void main(String[] args) {
// 使用 CGLib 完成代理
//目标对象
HouseSubject target = new RealHouseSubject();
//代理接口
HouseSubject houseSubject = (HouseSubject) Enhancer.create(target.getClass(), new CGLibMethodInterceptor(target));
houseSubject.rentHouse();
houseSubject.saleHouse();
}
}
运行结果:

Main
import org.springframework.cglib.proxy.Enhancer;
public class Main {
public static void main(String[] args) {
// 使用 CGLib 完成代理
//目标对象
HouseSubject target = new RealHouseSubject();
//代理类
RealHouseSubject houseSubject = (RealHouseSubject) Enhancer.create(target.getClass(), new CGLibMethodInterceptor(target));
houseSubject.rentHouse();
houseSubject.saleHouse();
}
}
运行结果:

接口介绍
MethodInterceptor

参数说明:
o: 被代理的对象 method: 目标方法 (被拦截的方法,也就是需要增强的方法) objects: 方法入参 methodProxy: 用于调用原始方法
Enhancer.create()
Enhancer.create() 用来生成一个代理对象。

参数说明:
argumentTypes:被代理类的类型(类或接口) arguments:自定义方法拦截器 MethodInterceptor
Spring AOP 源码解析
Spring AOP 主要基于两种方式实现的:JDK 及 CGLIB 的方式。
Spring 对于 AOP 的实现,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成。 生成代理对象的逻辑在父类 AbstractAutoProxyCreator 中。

在上面的代码中有一个重要的属性:proxyTargetClass,默认值为 false。也可以通过程序设置。
| proxyTargetClass | 目标对象 | 代理方式 |
|---|---|---|
| false | 实现了接口 | JDK 代理 |
| false | 未实现接口(只有实现类) | CGLIB 代理 |
| true | 实现了接口 | CGLIB 代理 |
| true | 未实现接口(只有实现类) | CGLIB 代理 |
注意
Spring Boot 2.X 开始,默认使用 CGLIB 代理 可以通过配置项 spring.aop.proxy-target-class=false 来进行修改,设置默认为 jdk 代理, 原来可以使用 @EnableAspectJAutoProxy(proxyTargetClass = true) 来设置,但是 SpringBoot 设置 @EnableAspectJAutoProxy 无效,因为 Spring Boot 默认使用 AopAutoConfiguration 进行装配
验证
没实现接口
TestController
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequestMapping("/test")
@RestController
public class TestController {
@MyAspect
@RequestMapping("/t1")
public String t1() {
log.info("执行 t1 方法...");
return "t1";
}
}
SpringAopApplication
import com.wmh.springaop.controller.TestController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class SpringAopApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringAopApplication.class, args);
TestController bean = context.getBean(TestController.class);
System.out.println(bean);
}
}
设置配置项 spring.aop.proxy-target-class=false 时 Debug 模式下查看到的:

设置配置项 spring.aop.proxy-target-class=true 时 Debug 模式下查看到的:

实现了接口
Iface
public interface Iface {
void test();
}
MyAspect
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect { }
TestController2
import com.wmh.springaop.config.MyAspect;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController2 implements Iface {
@MyAspect
@RequestMapping("/test1")
public void test() {
System.out.println("测试");
}
}
SpringAopApplication
import com.wmh.springaop.controller.Iface;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class SpringAopApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringAopApplication.class, args);
Iface iface = (Iface) context.getBean("testController2");
System.out.println(iface);
}
}
设置配置项 spring.aop.proxy-target-class=false 时 Debug 模式下查看到的:

设置配置项 spring.aop.proxy-target-class=true 时 Debug 模式下查看到的:

小结
Spring 默认 proxyTargetClass 为 false,实现了接口,使用 JDK 代理,未实现接口,使用 CGLIB 代理 SpringBoot 默认 proxyTargetClass 为 true,默认使用 CGLIB 代理 但是可以通过设置配置项 spring.aop.proxy-target-class 指定 proxyTargetClass。


