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>
運行效果: