Spring-AOP
记得上小学时候,上课前班长或者学习委员都会带头喊:起立,然后童鞋们起立齐喊:老师好才开始上课,下课同样起立喊老师再见,整节课才算完成。其实这是一种仪式,并不是某一位老师特别教授的技能,但又必须做,而且无论什么课程都需要举行,这种就类似于程序中的非核心业务,不能与核心业务混为一体,只是在需要出现的地方出现,为处理这种矛盾,从而就有了专门处理的方法或理念,叫面向切面编程AOP,Spring支持AOP,下面就以学生上课为例进行示范:
JavaConfig:

1 package com.spring.config; 2 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.context.annotation.EnableAspectJAutoProxy; 6 7 @Configuration 8 @ComponentScan(basePackages={"com.spring"}) 9 @EnableAspectJAutoProxy 10 public class CreatureSpingConfig { 11 }

1 package com.spring.aop; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 import org.aspectj.lang.annotation.After; 5 import org.aspectj.lang.annotation.Around; 6 import org.aspectj.lang.annotation.Aspect; 7 import org.aspectj.lang.annotation.Before; 8 import org.aspectj.lang.annotation.Pointcut; 9 import org.springframework.stereotype.Component; 10 11 @Component 12 @Aspect//需要引入aspectjrt-1.*.*.jar,此处版本需要和jdk版本匹配 13 public class Sophomore implements Student{ 14 /** 15 * 定义切点,下面的没有方法体的方法只是相当于一个别名 16 */ 17 @Pointcut("execution(* com.spring.aop.ChineseTeacher.teach(..))") 18 public void begin(){} 19 @Pointcut("execution(* com.spring.aop.ChineseTeacher.classOver(..))") 20 public void over(){} 21 22 @Before("begin()") 23 public void beforeTest(){ 24 System.out.println("班长:上课!\n童鞋们:老师好!"); 25 } 26 @Around("begin()") 27 public void aroundTest(ProceedingJoinPoint p){ 28 try { 29 System.out.println("叮铃铃~"); 30 p.proceed();//继续执行连接点的方法,如果不写表示不调用这个连接点的方法 31 } catch (Throwable e) { 32 System.out.println("自习ing");//上课出错则自习 33 } 34 } 35 @Before("over()") 36 public void ringTest(){ 37 System.out.println("叮铃铃~"); 38 } 39 @After("over()") 40 public void afterTest(){ 41 System.out.println("班长:下课!\n童鞋们:老师再见!"); 42 } 43 }

1 package com.spring.aop; 2 3 import org.springframework.stereotype.Component; 4 5 @Component 6 public class ChineseTeacher implements Teacher{ 7 @Override 8 public void teach() { 9 System.out.println("同学们好,今天我们来学习一首诗《锄禾》..."); 10 } 11 public void classOver(){ 12 System.out.println("今天先讲到这里,同学们下课!"); 13 } 14 }

1 package com.spring.test; 2 3 import org.junit.Test; 4 import org.junit.contrib.java.lang.system.StandardOutputStreamLog; 5 import org.junit.runner.RunWith; 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.test.context.ContextConfiguration; 8 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 9 10 import com.spring.aop.Teacher; 11 @RunWith(SpringJUnit4ClassRunner.class) 12 @ContextConfiguration(classes=com.spring.config.CreatureSpingConfig.class) 13 public class SpringJunit { 14 public static StandardOutputStreamLog log=new StandardOutputStreamLog(); 15 @Autowired 16 private Teacher t; 17 @Test 18 public void notNull() { 19 t.teach(); 20 System.out.println("45分钟后..."); 21 t.classOver(); 22 } 23 }
XML:

1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans 3 xmlns="http://www.springframework.org/schema/beans" 4 xmlns:c="http://www.springframework.org/schema/c" 5 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 6 xmlns:p="http://www.springframework.org/schema/p" 7 xmlns:jdbc="http://www.springframework.org/schema/jdbc" 8 xmlns:jee="http://www.springframework.org/schema/jee" 9 xmlns:aop="http://www.springframework.org/schema/aop" 10 xmlns:context="http://www.springframework.org/schema/context" 11 xmlns:conditional="http://www.springframework.org/schema/context" 12 xsi:schemaLocation="http://www.springframework.org/schema/beans 13 http://www.springframework.org/schema/beans/spring-beans.xsd 14 http://www.springframework.org/schema/context 15 http://www.springframework.org/schema/context/spring-context.xsd 16 http://www.springframework.org/schema/jdbc 17 http://www.springframework.org/schema/jdbc/spring-jdbc.xsd 18 http://www.springframework.org/schema/aop 19 http://www.springframework.org/schema/aop/spring-aop.xsd 20 http://www.springframework.org/schema/jee 21 http://www.springframework.org/schema/jee/spring-jee.xsd 22 http://www.springframework.org/schema/conditional 23 http://www.springframework.org/schema/conditional/spring-conditional.xsd"> 24 <context:annotation-config ></context:annotation-config> 25 <bean id="chineseTeacher" class="com.spring.aop.ChineseTeacher"></bean> 26 <bean id="sophomore" class="com.spring.aop.Sophomore"></bean> 27 <aop:aspectj-autoproxy proxy-target-class="true"/> 28 <aop:config> 29 <aop:aspect ref="sophomore"> 30 <aop:pointcut expression="execution(* com.spring.aop.ChineseTeacher.teach(..))" id="begin()"/> 31 <aop:pointcut expression="execution(* com.spring.aop.ChineseTeacher.classOver(..))" id="over()"/> 32 <aop:around method="aroundTest" pointcut-ref="begin()"/> 33 <aop:before method="beforeTest()" pointcut-ref="begin()"/> 34 <aop:before method="ringTest()" pointcut-ref="over()"/> 35 <aop:after method="afterTest()" pointcut-ref="over()"/> 36 </aop:aspect> 37 </aop:config> 38 </beans>
配置类:

1 package com.spring.config; 2 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.context.annotation.EnableAspectJAutoProxy; 6 7 @Configuration 8 @ComponentScan(basePackages={"com.spring"}) 9 @EnableAspectJAutoProxy 10 public class SpringConfig { 11 }
学生实现类:

1 package com.spring.aop; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 import org.aspectj.lang.annotation.After; 5 import org.aspectj.lang.annotation.Around; 6 import org.aspectj.lang.annotation.Aspect; 7 import org.aspectj.lang.annotation.Before; 8 import org.aspectj.lang.annotation.Pointcut; 9 import org.springframework.stereotype.Component; 10 public class Sophomore implements Student{ 11 public void beforeTest(){ 12 System.out.println("班长:上课!\n童鞋们:老师好!"); 13 } 14 public void aroundTest(ProceedingJoinPoint p){ 15 try { 16 System.out.println("叮铃铃~"); 17 p.proceed();//继续执行连接点的方法,如果不写表示不调用这个连接点的方法 18 } catch (Throwable e) { 19 System.out.println("自习ing");//上课出错则自习 20 } 21 } 22 public void ringTest(){ 23 System.out.println("叮铃铃~"); 24 } 25 public void afterTest(){ 26 System.out.println("班长:下课!\n童鞋们:老师再见!"); 27 } 28 }
教师实现类:

1 package com.spring.aop; 2 3 public class ChineseTeacher implements Teacher{ 4 public void teach() { 5 System.out.println("同学们好,今天我们来学习一首诗《锄禾》..."); 6 } 7 public void classOver(){ 8 System.out.println("今天先讲到这里,同学们下课!"); 9 } 10 }
测试:

1 package com.spring.aop; 2 3 import org.junit.Test; 4 import org.junit.contrib.java.lang.system.StandardOutputStreamLog; 5 import org.junit.runner.RunWith; 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.test.context.ContextConfiguration; 8 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 9 10 import com.spring.aop.Teacher; 11 @RunWith(SpringJUnit4ClassRunner.class) 12 @ContextConfiguration(locations="/spring.xml") 13 public class SpringJunit { 14 public static StandardOutputStreamLog log=new StandardOutputStreamLog(); 15 @Autowired 16 private Teacher t; 17 @Test 18 public void notNull() { 19 t.teach(); 20 System.out.println("45分钟后..."); 21 t.classOver(); 22 } 23 }
结果:
总结:
- @Aspect需要导入aspectjrt.jar;
- 在配置类或xml配置文件中应配置@EnableAspectJAutoProxy或<aop:aspectj-autoproxy/>
- 需要引入aopaliance.jar,否则会报错:java.lang.ClassNotFoundException: org.aopalliance.intercept.MethodInterceptor
- 需要引入aspectjweaver.jar,否则会报错:java.lang.ClassNotFoundException: org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException
- 过程中可能出现如下异常:java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut teach,原因是:aspectjrt.jar版本与jdk版本不一致,解决办法参考:http://www.cnblogs.com/xing901022/p/4267563.html
- jar包下载地址:http://mavenrepository.com/
- JavaConfig方式中:before方法的优先级比around低
- execution(* com.spring.aop.ChineseTeacher.teach(..))扩展
AOP支持的AspectJ切入点指示符如下:
execution:用于匹配方法执行的连接点;
within:用于匹配指定类型内的方法执行;
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
@within:用于匹配所以持有指定注解类型内的方法;
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
@annotation:用于匹配当前执行方法持有指定注解的方法;
bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;
reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。
AspectJ切入点支持的切入点指示符还有: call、get、set、preinitialization、staticinitialization、initialization、handler、adviceexecution、withincode、cflow、cflowbelow、if、@this、@withincode;但Spring AOP目前不支持这些指示符,使用这些指示符将抛出IllegalArgumentException异常。这些指示符Spring AOP可能会在以后进行扩展。
- XML方式中,执行顺序是自上而下,与JavaConfig方式不同;
- XML方式中,在<aop:around>标签中,method方法不需加(),无参方法可以,如果是around方法,必须有ProceedingJoinPoint类的参数,因此会报错:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#1d03f44': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: Unable to locate method [aroundTest()] on bean [sophomore]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:276)
... 65 more
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix