Spring-Aop编程(三)-AspectJ
AspectJ
1. 介绍
AspectJ是一个基于Java语言的AOP框架,Spring2.0以后新增了对AspectJ切点表达式支持,@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面,它是一种新版本Spring框架,建议使用AspectJ方式来开发AOP。主要用途:自定义开发
2.切入点表达式
为了能够灵活定义切入点位置,Spring AOP提供了多种切入点指示符。
execution———用来匹配执行方法的连接点
语法结构: execution( 方法修饰符 方法返回值 方法所属类 匹配方法名 ( 方法中的形参表 ) 方法申明抛出的异常 )
其中红色字体的部分时不能省略的,各部分都支持通配符 “*” 来匹配全部。
比较特殊的为形参表部分,其支持两种通配符
- "*":代表一个任意类型的参数;
- “..”:代表零个或多个任意类型的参数。
例如:
()匹配一个无参方法
(..)匹配一个可接受任意数量参数和类型的方法
(*)匹配一个接受一个任意类型参数的方法
(*,Integer)匹配一个接受两个参数的方法,第一个可以为任意类型,第二个必须为Integer。
下面举一些execution的使用实例:
分类 | 示例 | 描述 |
通过方法签名定义切入点 | execution(public * * (..)) | 匹配所有目标类的public方法,第一个*为返回类型,第二个*为方法名 |
execution(* save* (..)) | 匹配所有目标类以save开头的方法,第一个*代表返回类型 | |
execution(**product(*,String)) | 匹配目标类所有以product结尾的方法,并且其方法的参数表第一个参数可为任意类型,第二个参数必须为String | |
通过类定义切入点 | execution(* aop_part.Demo1.service.*(..)) | 匹配service接口及其实现子类中的所有方法 |
通过包定义切入点 | execution(* aop_part.*(..)) | 匹配aop_part包下的所有类的所有方法,但不包括子包 |
execution(* aop_part..*(..)) | 匹配aop_part包下的所有类的所有方法,包括子包。(当".."出现再类名中时,后面必须跟“*”,表示包、子孙包下的所有类) | |
execution(* aop_part..*.*service.find*(..)) | 匹配aop_part包及其子包下的所有后缀名为service的类中,所有方法名必须以find为前缀的方法 | |
通过方法形参定义切入点 | execution(*foo(String,int)) | 匹配所有方法名为foo,且有两个参数,其中,第一个的类型为String,第二个的类型为int |
execution(* foo(String,..)) | 匹配所有方法名为foo,且至少含有一个参数,并且第一个参数为String的方法(后面可以有任意个类型不限的形参) |
within————通过类匹配模式申明切入点(只能通过类型匹配连接点)
例如:within(aop_part..*) 表示匹配包aop_part以及子包的所有方法
由于execution可以匹配包、类、方法,而within只能匹配包、类,因此execution完全可以代替within的功能。
this————限定AOP代理必须时指定类型的实例,用于匹配该对象的所有连接点
例如:this(aop_part.service.GodService) 表示匹配了GodService接口的代理对象的所有连接点
target————通过判断目标类的类型确定判断的是否匹配
this通过判断代理类的类型来决定是否和切入点匹配,两者限定的对象都是指定类型的实例。
例如: target(aop_part.service.GodService) 表示匹配实现了GodService接口的目标对象的所有连接点
args————用于对连接点的参数类型进行限制,要求参数类型时指定类型的实例
例如:args(aop_part.service) 表示匹配时,出入的参数类型时service的方法
其与execution(**(aop_part.service))的区别为,execution针对的时方法签名,而args针对的是运行时的实际参数类型。
args既匹配buyGoods(service newService),也匹配buyGoods(Buyservice newService) <Buyservice为service的子类>
execution只匹配buyGoods(service newService)
组合切入点
支持 &&、 || 、!
与其他语言所代表的意思相同
例:args(aop_part.service) &&execution(**(aop_part.service))
3.AspectJ 通知类型
aop联盟定义通知类型,具有特性接口,必须实现,从而确定方法名称。
aspectj 通知类型,只定义类型名称。已经方法格式。
个数:6种,知道5种,掌握1中。
before:前置通知(应用:各种校验)在方法执行前执行,如果通知抛出异常,阻止方法运行
afterReturning:后置通知(应用:常规数据处理)方法正常返回后执行,如果方法中抛出异常,通知无法执行必须在方法执行后才执行,所以可以获得方法的返回值。
around:环绕通知(应用:十分强大,可以做任何事情)方法执行前后分别执行,可以阻止方法的执行必须手动执行目标方法
afterThrowing:抛出异常通知(应用:包装异常信息)方法抛出异常后执行,如果方法没有抛出异常,无法执行
after:最终通知(应用:清理现场) 方法执行完毕后执行,无论方法中是否出现异常
Spring Aop实例:
方式一 :注解配置
UserService.java
1 2 3 4 5 6 7 8 9 | package com.zk.b_annotation; public interface UserService { public void addUser(); public String updateUser(); public void deleteUser(); } |
UserServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package com.zk.b_annotation; import org.springframework.stereotype.Service; @Service ( "userServiceId" ) public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println( "d_aspect.b_anno addUser" ); } @Override public String updateUser() { System.out.println( "d_aspect.b_anno updateUser" ); int i = 1 / 0 ; return "阳志就是" ; } @Override public void deleteUser() { System.out.println( "d_aspect.b_anno deleteUser" ); } } |
切面
MyAspect.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | package com.zk.b_annotation; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * 切面类,含有多个通知 */ @Component @Aspect public class MyAspect { //前置通知 // @Before("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")<br> @Before("myPointCut()") public void myBefore(JoinPoint joinPoint){ System.out.println( "前置通知" + joinPoint.getSignature().getName()); } //声明公共切入点 @Pointcut ( "execution(* com.zk.b_annotation.UserServiceImpl.*(..))" ) private void myPointCut(){ } // @AfterReturning(value="myPointCut()" ,returning="ret") <br> @AfterReturning("myPointCut()") public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println( "后置通知" + joinPoint.getSignature().getName() + " , -->" + ret); } // @Around(value = "myPointCut()")<br> @Around("myPointCut") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println( "前置通知" ); //手动执行目标方法 Object obj = joinPoint.proceed(); System.out.println( "后置通知" ); return obj; } // @AfterThrowing(value="execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))" ,throwing="e")<br> @AfterThrowing("myPointCut()") public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println( "抛出异常通知" + e.getMessage()); } @After ( "myPointCut()" ) public void myAfter(JoinPoint joinPoint){ System.out.println( "后置通知" ); } } |
beans.xml配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?xml version= "1.0" encoding= "UTF-8" ?> <beans xmlns= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xmlns:context= "http://www.springframework.org/schema/context" xmlns:aop= "http://www.springframework.org/schema/aop" xsi:schemaLocation="http: //www.springframework.org/schema/beans http: //www.springframework.org/schema/beans/spring-beans.xsd http: //www.springframework.org/schema/aop http: //www.springframework.org/schema/aop/spring-aop.xsd http: //www.springframework.org/schema/context http: //www.springframework.org/schema/context/spring-context.xsd"> <!-- 1 .扫描 注解类 --> <context:component-scan base- package = "com.zk.b_annotation" ></context:component-scan> <!-- 2 .确定 aop注解生效 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans> |
TestAspectAnno.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package com.zk.b_annotation; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAspectAnno { @Test public void demo01(){ String xmlPath = "com/zk/b_annotation/beans.xml" ; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); //获得目标bean UserService userService = (UserService) applicationContext.getBean( "userServiceId" ); userService.addUser(); userService.updateUser(); userService.deleteUser(); } } |
运行效果图:
方式二:xml配置
UserService.java
1 2 3 4 5 6 7 8 9 | package com.itheima.d_aspect.a_xml; public interface UserService { public void addUser(); public String updateUser(); public void deleteUser(); } |
UserServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.itheima.d_aspect.a_xml; public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println( "d_aspect.a_xml addUser" ); } @Override public String updateUser() { System.out.println( "d_aspect.a_xml updateUser" ); int i = 1 / 0 ; return "阳志就是屌" ; } @Override public void deleteUser() { System.out.println( "d_aspect.a_xml deleteUser" ); } } |
MyAspect.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | package com.itheima.d_aspect.a_xml; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; /** * 切面类,含有多个通知 */ public class MyAspect { public void myBefore(JoinPoint joinPoint){ System.out.println( "前置通知 : " + joinPoint.getSignature().getName()); } public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println( "后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); } public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println( "前" ); //手动执行目标方法 Object obj = joinPoint.proceed(); System.out.println( "后" ); return obj; } public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println( "抛出异常通知 : " + e.getMessage()); } public void myAfter(JoinPoint joinPoint){ System.out.println( "最终通知" ); } } |
TestAspectXml.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package com.itheima.d_aspect.a_xml; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAspectXml { @Test public void demo01(){ String xmlPath = "com/itheima/d_aspect/a_xml/beans.xml" ; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); //获得目标类 UserService userService = (UserService) applicationContext.getBean( "userServiceId" ); userService.addUser(); userService.updateUser(); userService.deleteUser(); } } |
beans.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | <?xml version= "1.0" encoding= "UTF-8" ?> <beans xmlns= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xmlns:aop= "http://www.springframework.org/schema/aop" xsi:schemaLocation="http: //www.springframework.org/schema/beans http: //www.springframework.org/schema/beans/spring-beans.xsd http: //www.springframework.org/schema/aop http: //www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 1 创建目标类 --> <bean id= "userServiceId" class = "com.itheima.d_aspect.a_xml.UserServiceImpl" ></bean> <!-- 2 创建切面类(通知) --> <bean id= "myAspectId" class = "com.itheima.d_aspect.a_xml.MyAspect" ></bean> <!-- 3 aop编程 <aop:aspect> 将切面类 声明“切面”,从而获得通知(方法) ref 切面类引用 <aop:pointcut> 声明一个切入点,所有的通知都可以使用。 expression 切入点表达式 id 名称,用于其它通知引用 --> <aop:config> <aop:aspect ref= "myAspectId" > <aop:pointcut expression= "execution(* com.itheima.d_aspect.a_xml.UserServiceImpl.*(..))" id= "myPointCut" /> <!-- 3.1 前置通知 <aop:before method= "" pointcut= "" pointcut-ref= "" /> method : 通知,及方法名 pointcut :切入点表达式,此表达式只能当前通知使用。 pointcut-ref : 切入点引用,可以与其他通知共享切入点。 通知方法格式: public void myBefore(JoinPoint joinPoint){ 参数 1 :org.aspectj.lang.JoinPoint 用于描述连接点(目标方法),获得目标方法名等 例如: --> <aop:before method= "myBefore" pointcut-ref= "myPointCut" /> <!-- 3.2 后置通知 ,目标方法后执行,获得返回值 <aop:after-returning method= "" pointcut-ref= "" returning= "" /> returning 通知方法第二个参数的名称 通知方法格式: public void myAfterReturning(JoinPoint joinPoint,Object ret){ 参数 1 :连接点描述 参数 2 :类型Object,参数名 returning= "ret" 配置的 例如: --> <aop:after-returning method= "myAfterReturning" pointcut-ref= "myPointCut" returning= "ret" /> <!-- 3.3 环绕通知 <aop:around method= "" pointcut-ref= "" /> 通知方法格式: public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ 返回值类型:Object 方法名:任意 参数:org.aspectj.lang.ProceedingJoinPoint 抛出异常 执行目标方法:Object obj = joinPoint.proceed(); 例如: --> <aop:around method= "myAround" pointcut-ref= "myPointCut" /> <!-- 3.4 抛出异常 <aop:after-throwing method= "" pointcut-ref= "" throwing= "" /> throwing :通知方法的第二个参数名称 通知方法格式: public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ 参数 1 :连接点描述对象 参数 2 :获得异常信息,类型Throwable ,参数名由throwing= "e" 配置 例如: --> <aop:after-throwing method= "myAfterThrowing" pointcut-ref= "myPointCut" throwing= "e" /> <!-- 3.5 最终通知 --> <aop:after method= "myAfter" pointcut-ref= "myPointCut" /> </aop:aspect> </aop:config> </beans> |
运行效果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)