Spring AOP
函数增强,当前有二种处理方式,
a. 基于jdk的接口动态代理增强
b. 基于 cglib 的父类动态代理增强
在Spring框架中,在动态代理增加时,依据是否有接口来自动识别并实现增强
如果有接口,将使用基于 jdk的接口动态代理,否则使用基于cglib的父类动态代理增强;
01. 基于 jdk 动态代理增强
>> 要求: 必须有接口;否则无法实现动态代理;
>> 代码实现
1 //接口 2 public interface TargetInterface { 3 public void save(); 4 }
1 //接口实现 2 public class Target implements TargetInterface { 3 @Override 4 public void save() { 5 System.out.println("目标方法执行 ... "); 6 } 7 }
1 //增强对象 2 public class Advice { 3 4 public void preAdvice(){ 5 System.out.println("前置增强..."); 6 } 7 8 public void sufAdvice(){ 9 System.out.println("后置增强..."); 10 } 11 }
1 //动态代理增强 2 public static void main(String[] args) { 3 //目标对象 4 Target target = new Target(); 5 //增强对象 6 Advice advice = new Advice(); 7 //jdk的接口代理增强 8 TargetInterface proxyInstance = (TargetInterface) Proxy.newProxyInstance( 9 target.getClass().getClassLoader(), 10 target.getClass().getInterfaces(), 11 new InvocationHandler() { 12 @Override 13 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 14 advice.preAdvice(); 15 Object invoke = method.invoke(target, args); 16 advice.sufAdvice(); 17 return invoke; 18 } 19 } 20 ); 21 proxyInstance.save(); 22 }
02.基于cglib的动态代理增强
>> 无接口要求
>> 代码实现 (增强对象与上面一样)
1 //目标对象 2 public class Target { 3 public void save() { 4 System.out.println("目标方法执行 ... "); 5 } 6 }
1 //动态代理增强 2 public static void main(String[] args) { 3 //目标对象 4 Target target = new Target(); 5 //增强对象 6 Advice advice = new Advice(); 7 //cglib增强对象 8 Enhancer enhancer = new Enhancer(); 9 //设置父类 10 enhancer.setSuperclass(target.getClass()); 11 //设置回调 12 enhancer.setCallback(new MethodInterceptor() { 13 @Override 14 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 15 advice.preAdvice(); 16 Object invoke = method.invoke(target, args); 17 advice.sufAdvice(); 18 return invoke; 19 } 20 }); 21 //生成代理对象 22 Target o = (Target) enhancer.create(); 23 o.save(); 24 25 }
03. 基于Spring 配置实现动态代理增强 (目标对象带接口)
1 // applicationContext.xml 配置 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xsi:schemaLocation=" 6 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd 8 "> 9 10 <!--目标对象(带接口)--> 11 <bean id="target" class="club.wuyu.proxy.AOP.Target"/> 12 <!--目标对象(无接口)--> 13 <bean id="targetcglib" class="club.wuyu.proxy.AOP.TargetCglib"/> 14 <!--切面对象--> 15 <bean id="advice" class="club.wuyu.proxy.AOP.Advice"/> 16 17 <!--织入--> 18 <aop:config> 19 <aop:aspect ref="advice"> 20 <aop:before method="preAdvice" pointcut="execution(public void club.wuyu.proxy.AOP.Target.save())"/> 21 <aop:after method="sufAdvice" pointcut="execution(public void club.wuyu.proxy.AOP.Target.save())"/> 22 23 <aop:before method="preAdvice" pointcut="execution(public void club.wuyu.proxy.AOP.TargetCglib.save())"/> 24 <aop:after method="sufAdvice" pointcut="execution(public void club.wuyu.proxy.AOP.TargetCglib.save())"/> 25 </aop:aspect> 26 </aop:config> 27 </beans>
//测试 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class AOPTest { @Autowired private TargetInterface targetInterface; @Autowired private TargetCglib targetCglib; @Test public void test02(){ targetCglib.save(); } @Test public void test01(){ targetInterface.save(); } }
04. AOP配置说明
>> 环绕方法的配置
<aop:around method="around" pointcut="execution(* com.itheima.aop.*.*(..))"/>
>> 切点表达式的抽取, 便于多个不同的切点类型引用切点表达式;
切点表达式的引用:pointcut-ref
05. 关于注解方式动态代理的实现
>> 创建目标接口及目标类 (内部有目标方法);
>> 创建切面类 (内部有增强方法)
>> 将目标类和切面类做注解申明,将对象的创建权交由Spring; @Componet 注解;
>> 在切面类中使用注解,配置织入关系
1 @Component("advice") //切面类的创建权,交由Spring 2 @Aspect //标识当前类为一个切面类 3 public class Advice { 4 5 @Before("execution(* club.wuyu.proxy.anno.*.*(..))") //织入 6 public void preAdvice(){ 7 System.out.println("前置增强..."); 8 } 9 @After("execution(* club.wuyu.proxy.anno.*.*(..))") //织入 10 public void sufAdvice(){ 11 System.out.println("后置增强..."); 12 } 13 }
>> 配置文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd 8 http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> 9 10 <context:component-scan base-package="club.wuyu.proxy.anno"/> 11 12 <aop:aspectj-autoproxy proxy-target-class="true"/> 13 <!--<aop:aspectj-autoproxy/>--> 14 15 </beans>
<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspect 切面的bean创建代理,织入切面。
<aop:aspectj-autoproxy />的proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强。
当配为<aop:aspectj-autoproxy poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。
如果proxy-target-class设置为false,但是目标类没有声明接口,则spring将自动使用CGLib动态代理。
>> 关于 but was actually of type 'com.sun.proxy.$Proxy20' 错误的原因及处理
>> 在 java 中,默认使用的动态代理是JDK基于接口的代理,如果出现上述错误,极有可能是在自动注入的时候,注入到了目标对象的实现类上,而不是目标对象的接口上;
即:配置 <aop:aspectj-autoproxy/>
或:配置 <aop:aspectj-autoproxy proxy-target-class="false"/> 时
自动注入配置到了实现类上
1 @RunWith(SpringJUnit4ClassRunner.class) 2 @ContextConfiguration("classpath:applicationContext-anno.xml") 3 public class AnnoTest { 4 5 @Autowired 6 private Target target; 7 8 @Test 9 public void Test01(){ 10 target.save(); 11 } 12 }
正确的注入,应该是接口
1 @Autowired 2 private TargetInterface target;
或者,直接配置为 CGLib 动态代理,
即:<aop:aspectj-autoproxy proxy-target-class="true"/>
>> 注解方式下,切点表达式的抽取
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix