第十五讲-Spring选择代理
第十五讲-Spring选择代理
在Spring中,存在两个切面,一个是aspect
,另一个是advisor
,advisor相比于aspect是更细粒度的切面,切面类包含一个通知和切点。而一个切面是由通知和切点组成,
通知表现为一个方法,也就是增强的方法;切点决定了将来目标的哪些方法需要增强。一般为AspectJ表达式。对于一个切面,可以包含多个通知
例如下面编写的切面类:
@Aspect
class MyAspect{
@Before("execution(* foo())") // 将来任意包的任意类,只要有foo()方法调用就会前置增强
public void before(){
System.out.println("前置增强");
}
@After("execution(* foo())") // 将来任意包的任意类,只要有foo()方法调用就会后置增强
public void after(){
System.out.println("后置增强");
}
}
我们现在手动手动编写一个AOP示例:
package com.cherry.chapter1.a15;
import ch.qos.logback.classic.joran.action.LoggerAction;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
public class A15 {
private static final Logger log = LoggerFactory.getLogger(A15.class);
public static void main(String[] args) {
// 1.准备好切点对象
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
// 设置切点变道时 -- 匹配foo无法的方法
pointcut.setExpression("execution(* foo())");
// 2.准备一个通知
MethodInterceptor advise = new MethodInterceptor() {
@Override // 可以调用目标
public Object invoke(MethodInvocation invocation) throws Throwable {
log.debug("前置增强...");
Object result = invocation.proceed();// 调用目标
log.debug("后置增强...");
return result;
}
};
// 3.准备一个切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advise);
// 4. 创建代理(使用Spring提供的代理工厂创建代理对象)
ProxyFactory proxyFactory = new ProxyFactory();
// 准备一个目标对象
Target1 target1 = new Target1();
proxyFactory.setTarget(target1);
proxyFactory.addAdvisor(advisor);
// 创建代理对象
I1 proxy = (I1)proxyFactory.getProxy();
log.debug("ProxyFactory创建出来的代理类的类型为:{}",proxy.getClass());
proxy.foo();
proxy.bar();
}
}
interface I1{
void foo();
void bar();
}
class Target1 implements I1{
private static final Logger log = LoggerFactory.getLogger(Target1.class);
@Override
public void foo() {
log.debug("target1 foo()...");
}
@Override
public void bar() {
log.debug("tar1 bar()...");
}
}
运行结果如下:
18:19:55.733 [main] DEBUG com.cherry.chapter1.a15.A15 - ProxyFactory创建出来的代理类的类型为:class com.cherry.chapter1.a15.Target1$$EnhancerBySpringCGLIB$$9d838066
18:19:55.750 [main] DEBUG com.cherry.chapter1.a15.A15 - 前置增强...
18:19:55.779 [main] DEBUG com.cherry.chapter1.a15.Target1 - target1 foo()...
18:19:55.779 [main] DEBUG com.cherry.chapter1.a15.A15 - 后置增强...
18:19:55.780 [main] DEBUG com.cherry.chapter1.a15.Target1 - tar1 bar()...
我们发现,成功完成了代理,并且我们还发现到:Spring内部的代理工厂创建代理对象使用的是Cglib,那么现在有一个问题:Spring在什么情况下使用JDK的动态代理呢?什么时候使用Cglib的动态代理呢?
这里给出结论,ProxyFactory首先会读取一个属性proxyTargetClass
代理目标类
- 当
proxyTargetClass = false
, 且目标类实现了接口,就会采用JDK来生成代理对象 - 当
proxyTargetClass = false
, 但目标没有实现接口,就会采用Cglib来生成代理对象 - 当
proxyTargetClass = true
, 不管目标有没有实现接口,总是会采用Cglib来生成代理对象
这里就有人问了:为什么上面的代理工厂明明目标类实现了接口,为什么还是使用Cglib作为代理生成代理对象呢?原因是因为我们在为ProxyFactory这是了目标类和切面类,ProxFactory并不知道目标类到底有没有实现了接口,这一点ProxFactory判断不出来,因此总是用Cglib实现,因此呢,我们可以为ProxFactory手动的设置目标类实现了哪个接口,例如下面:把目标类实现了哪个接口手动的告诉代理工厂,这样代理工厂才会使用JDK来生成代理对象
proxyFactory.setInterfaces(Target1.class.getInterfaces());
18:36:03.055 [main] DEBUG com.cherry.chapter1.a15.A15 - ProxyFactory创建出来的代理类的类型为:class com.cherry.chapter1.a15.$Proxy0
18:36:03.226 [main] DEBUG com.cherry.chapter1.a15.A15 - 前置增强...
18:36:03.226 [main] DEBUG com.cherry.chapter1.a15.Target1 - target1 foo()...
18:36:03.226 [main] DEBUG com.cherry.chapter1.a15.A15 - 后置增强...
18:36:03.227 [main] DEBUG com.cherry.chapter1.a15.Target1 - tar1 bar()...
此外,如果我们手动的将proxyTargetClass
属性手动的改为true
, 这样,不管目标类有没有实现接口,其ProxyFactory在创建代理对象时都会使用Cglib作为实现:
proxyFactory.setProxyTargetClass(true);
18:38:55.487 [main] DEBUG com.cherry.chapter1.a15.A15 - ProxyFactory创建出来的代理类的类型为:class com.cherry.chapter1.a15.Target1$$EnhancerBySpringCGLIB$$5d6a49f1
18:38:55.494 [main] DEBUG com.cherry.chapter1.a15.A15 - 前置增强...
18:38:55.509 [main] DEBUG com.cherry.chapter1.a15.Target1 - target1 foo()...
18:38:55.509 [main] DEBUG com.cherry.chapter1.a15.A15 - 后置增强...
18:38:55.509 [main] DEBUG com.cherry.chapter1.a15.Target1 - tar1 bar()...
分类:
Spring 高级49讲
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构