代理的主要功能
-
拦截对所有方法的调用,并将它们根据需要传递给AOP框架。
Spring AOP 结合了动态代理技术和 AOP 切面逻辑的解析。以下是实现步骤:
1. 创建代理
2. 拦截方法调用
3. 通知链执行
通知链的执行通过
MethodInterceptor的invoke方法实现- Spring 使用
ProxyFactory创建代理对象。 - 根据目标对象是否实现接口,决定使用 JDK 动态代理还是 CGLIB 代理。
- 使用 AOP 切面配置,Spring 将切面的通知逻辑封装为
Advice对象。 - 通过
MethodInterceptor实现通知链调用。 - 代理拦截方法后,通知链按照顺序执行:
- 前置通知(@Before)。
- 调用目标方法。
- 后置通知(@After、@AfterReturning)。
- 异常通知(@AfterThrowing)。
- Spring 使用
-
通过
AopContext获取当前代理对象(代理自我公开)。ℹ️ 通常情况下,当你在目标对象的方法中调用同一个类的另一个方法时,这种内部调用是直接调用目标对象的方法,而不会触发代理(也就是不会应用通知逻辑,比如 AOP 切面)。如果需要在目标对象内部调用时也触发 AOP 逻辑,必须通过代理对象来调用方法。
举个例子:
public class MyService { public void methodA() { System.out.println("Executing methodA"); // 内部调用另一个方法 methodB(); } public void methodB() { System.out.println("Executing methodB"); } }为
MyService配置AOP切面:@Aspect @Component public class LoggingAspect { @Before("execution(* MyService.*(..))") public void logBefore(JoinPoint joinPoint) { System.out.println("Logging before: " + joinPoint.getSignature().getName()); } }当调用
methodA()时,由于methodA()在内部直接调用了methodB(),因此methodB()不会触发LoggingAspect。如果需要
methodB()同样触发LoggingAspect,则需要启用AopContext(ProxyFactory.setExposeProxy(true);),并通过AopContext来获取当前对象。public void methodA() { System.out.println("Executing methodA"); ((MyService) AopContext.currentProxy()).methodB(); // 使用代理调用方法B } -
通过
Advised接口动态修改代理的通知链。ProxyFactory factory = new ProxyFactory(new MyService()); factory.addAdvice(new LoggingAdvice()); // 获取代理对象 Object proxy = factory.getProxy(); // 代理对象实现了 Advised 接口 Advised advised = (Advised) proxy; // 动态添加新的通知 advised.addAdvice(new PerformanceMonitoringAdvice());
在代理对象创建后,你可以通过 Advised 接口获取或修改通知链(advice chain)。
支持动态增强或移除 AOP 切面逻辑,而无需重新创建代理。
JDK动态代理
JDK代理只能生成接口的代理,因此,想要代理的对象必须至少实现一个接口,并且生成的代理将是实现该接口的对象,示意图如下:

⚠️使用JDK代理时,所有方法调用都会被JVM拦截并路由到代理的invoke() 方法,也就是说决定如何处理方法调用的决策会在运行时做出
⚠️可以通过使用setInterfaces() 指定要代理的接口列表,从而让ProxyFactory 使用JDK代理
CGLIB代理
CGLIB代理生成的代理类型是目标类的子类

ℹ️ 在首次创建CGLIB代理时,会询问Spring如何处理每个方法。这意味着每次调用JDK代理上的invoke()时执行的许多决策对于CGLIB来说只会执行一次
优缺点及适用场景
| JDK 动态代理 | CGLIB 动态代理 | |
|---|---|---|
| 优点 |
1. 实现简单,原生支持,依赖较少。 2. 适合轻量级、简单场景。 | 性能高,尤其适合频繁调用。 |
| 缺点 | 性能在高频调用时明显不如 CGLIB |
1. 使用更复杂,可能引入额外依赖。 2. 需要为目标类生成子类,因此无法代理 final 类或 final 方法。 |
附 jdk1.8 环境下性能对比 (ms)
-
loop = 50000
CGLIB (STANDARD) CGLIB (FROZEN) JDK advised Method 32 6 19 unadvised Method. 11 1 11 equals() Method 2 1 16 hashcode() Method 4 4 6 getTargetClass() Method 4 3 6 -
loop = 500000
CGLIB (STANDARD) CGLIB (FROZEN) JDK advised Method 40 12 40 unadvised Method. 35 7 35 equals() Method 2 6 38 hashcode() Method 5 6 17 getTargetClass() Method 3 7 14
-
loop = 5000000
CGLIB (STANDARD) CGLIB (FROZEN) JDK advised Method 121 85 193 unadvised Method. 91 34 201 equals() Method 3 24 91 hashcode() Method 13 20 38 getTargetClass() Method 3 10 42
附 测试代码(来源 Spring5 高级编程 CH5.6)
package com.annie.study.proxy;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
/**
* @className: ProxyPerTest
* @Description: TODO
* @author: annie
* @date: 2024/11/25
*/
public class ProxyPerTest {
public static void main(String[] args) {
SimpleBean target = new DefaultSimpleBean();
Advisor advisor = new DefaultPointcutAdvisor(new TestPointcut(), new NoOpBeforeAdvise());
runCglibTests(advisor, target);
runCglibFrozenTests(advisor, target);
runjdkTests(advisor, target);
}
private static void runCglibTests(Advisor advisor, SimpleBean target){
ProxyFactory factory = new ProxyFactory();
factory.setProxyTargetClass(true);
factory.setTarget(target);
factory.addAdvisor(advisor);
SimpleBean proxy = (SimpleBean) factory.getProxy();
System.out.println("Running CGLIB (STANDARD) test");
test(proxy);
}
private static void runCglibFrozenTests(Advisor advisor, SimpleBean target){
ProxyFactory factory = new ProxyFactory();
factory.setProxyTargetClass(true);
factory.setTarget(target);
factory.addAdvisor(advisor);
factory.setFrozen(true);
SimpleBean proxy = (SimpleBean) factory.getProxy();
System.out.println("Running CGLIB (Frozen) test");
test(proxy);
}
private static void runjdkTests(Advisor advisor, SimpleBean target){
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
factory.setInterfaces(SimpleBean.class);
SimpleBean proxy = (SimpleBean) factory.getProxy();
System.out.println("Running JDK test");
test(proxy);
}
public static void test(SimpleBean bean){
int loop = 50000;
System.out.println("Loop equals " + loop);
long before = 0;
long after = 0;
System.out.println("Testing advised Method...");
before = System.currentTimeMillis();
for (int x = 0; x < loop; x ++){
bean.advised();
}
after = System.currentTimeMillis();
System.out.println("Took " + (after - before) + " ms");
System.out.println("Testing unadvised Method...");
before = System.currentTimeMillis();
for (int x = 0; x < loop; x ++){
bean.unadvised();
}
after = System.currentTimeMillis();
System.out.println("Took " + (after - before) + " ms");
System.out.println("Testing equals() Method...");
before = System.currentTimeMillis();
for (int x = 0; x < loop; x ++){
bean.equals(bean);
}
after = System.currentTimeMillis();
System.out.println("Took " + (after - before) + " ms");
System.out.println("Testing hashcode() Method...");
before = System.currentTimeMillis();
for (int x = 0; x < loop; x ++){
bean.hashCode();
}
after = System.currentTimeMillis();
System.out.println("Took " + (after - before) + " ms");
Advised advised = (Advised) bean;
System.out.println("Testing getTargetClass() Method...");
before = System.currentTimeMillis();
for (int x = 0; x < loop; x ++){
advised.getTargetClass();
}
after = System.currentTimeMillis();
System.out.println("Took " + (after - before) + " ms");
System.out.println(">>>>>>\n");
}
}
public interface SimpleBean {
void advised();
void unadvised();
}
public class DefaultSimpleBean implements SimpleBean{
private long dummy = 0;
@Override
public void advised() {
dummy = System.currentTimeMillis();
}
@Override
public void unadvised() {
dummy = System.currentTimeMillis();
}
}
public class TestPointcut extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return "advised".equals(method.getName());
}
}
public class NoOpBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
//定义一个不执行任何操作的advice
}
}

2830

被折叠的 条评论
为什么被折叠?



