AspectJ是一个基于Java语言的AOP框架,Spring2.0之后新增了对AspectJ切点表达式支持,@AspectJ 是AspectJ1.5新增功能,经过JDK5注解技术,容许直接在Bean类中定义切面,它是一种新版本Spring框架,建议使用AspectJ方式来开发AOP。主要用途:自定义开发java
为了可以灵活定义切入点位置,Spring AOP提供了多种切入点指示符。spring
语法结构: execution( 方法修饰符 方法返回值 方法所属类 匹配方法名 ( 方法中的形参表 ) 方法申明抛出的异常 )express
其中红色字体的部分时不能省略的,各部分都支持通配符 “*” 来匹配所有。编程
比较特殊的为形参表部分,其支持两种通配符app
例如:框架
()匹配一个无参方法ide
(..)匹配一个可接受任意数量参数和类型的方法字体
(*)匹配一个接受一个任意类型参数的方法this
(*,Integer)匹配一个接受两个参数的方法,第一个能够为任意类型,第二个必须为Integer。spa
下面举一些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(aop_part..*) 表示匹配包aop_part以及子包的全部方法
因为execution能够匹配包、类、方法,而within只能匹配包、类,所以execution彻底能够代替within的功能。
例如:this(aop_part.service.GodService) 表示匹配了GodService接口的代理对象的全部链接点
this经过判断代理类的类型来决定是否和切入点匹配,二者限定的对象都是指定类型的实例。
例如: target(aop_part.service.GodService) 表示匹配实现了GodService接口的目标对象的全部链接点
例如: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))
aop联盟定义通知类型,具备特性接口,必须实现,从而肯定方法名称。
aspectj 通知类型,只定义类型名称。已经方法格式。
个数:6种,知道5种,掌握1中。
before:前置通知(应用:各类校验)在方法执行前执行,若是通知抛出异常,阻止方法运行
afterReturning:后置通知(应用:常规数据处理)方法正常返回后执行,若是方法中抛出异常,通知没法执行必须在方法执行后才执行,因此能够得到方法的返回值。
around:环绕通知(应用:十分强大,能够作任何事情)方法执行先后分别执行,能够阻止方法的执行必须手动执行目标方法
afterThrowing:抛出异常通知(应用:包装异常信息)方法抛出异常后执行,若是方法没有抛出异常,没法执行
after:最终通知(应用:清理现场) 方法执行完毕后执行,不管方法中是否出现异常
Spring Aop实例:
方式一 :注解配置
UserService.java
package com.zk.b_annotation; public interface UserService { public void addUser(); public String updateUser(); public void deleteUser(); }
UserServiceImpl.java
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
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.*(..))")
@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")
@AfterReturning("myPointCut()") public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("后置通知" + joinPoint.getSignature().getName() + " , -->" + ret); } // @Around(value = "myPointCut()")
@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")
@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配置文件
<?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
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
package com.itheima.d_aspect.a_xml; public interface UserService { public void addUser(); public String updateUser(); public void deleteUser(); }
UserServiceImpl.java
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
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
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
<?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>
运行效果: