sring aop的方式有兩種:(1)xml文件配置方式(2)注解的方式實現,我們可以先通過一個demo認識spring aop的實現,然后再對其進行詳細的解釋。
一、基於注解的springAop配置。
環境准備階段:
(1)pom.xml:
1 <dependencies> 2 <!-- 引入Spring-AOP等相關Jar --> 3 <dependency> 4 <groupId>org.springframework</groupId> 5 <artifactId>spring-core</artifactId> 6 <version>3.0.6.RELEASE</version> 7 </dependency> 8 <dependency> 9 <groupId>org.springframework</groupId> 10 <artifactId>spring-context</artifactId> 11 <version>3.0.6.RELEASE</version> 12 </dependency> 13 <dependency> 14 <groupId>org.springframework</groupId> 15 <artifactId>spring-aop</artifactId> 16 <version>3.0.6.RELEASE</version> 17 </dependency> 18 <dependency> 19 <groupId>org.springframework</groupId> 20 <artifactId>spring-orm</artifactId> 21 <version>3.0.6.RELEASE</version> 22 </dependency> 23 <dependency> 24 <groupId>org.aspectj</groupId> 25 <artifactId>aspectjrt</artifactId> 26 <version>1.6.1</version> 27 </dependency> 28 <dependency> 29 <groupId>aspectj</groupId> 30 <artifactId>aspectjweaver</artifactId> 31 <version>1.5.3</version> 32 </dependency> 33 <dependency> 34 <groupId>cglib</groupId> 35 <artifactId>cglib</artifactId> 36 <version>2.1_2</version> 37 </dependency> 38 39 <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 --> 40 <dependency> 41 <groupId>com.mchange</groupId> 42 <artifactId>c3p0</artifactId> 43 <version>0.9.5.2</version> 44 </dependency> 45 <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> 46 <dependency> 47 <groupId>mysql</groupId> 48 <artifactId>mysql-connector-java</artifactId> 49 <version>5.1.37</version> 50 </dependency> 51 52 <!-- https://mvnrepository.com/artifact/dom4j/dom4j --> 53 <dependency> 54 <groupId>dom4j</groupId> 55 <artifactId>dom4j</artifactId> 56 <version>1.6.1</version> 57 </dependency> 58 <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang --> 59 <dependency> 60 <groupId>commons-lang</groupId> 61 <artifactId>commons-lang</artifactId> 62 <version>2.6</version> 63 </dependency> 64 </dependencies>
(2)定義接口:
1 package cn.spring.aop.dao; 2 3 /** 4 * @author Simple 5 * @date 10:01 2019/8/20 6 * @description 7 */ 8 public interface UserService { 9 public void save(); 10 }
(3)接口實現類:
1 package cn.spring.aop.dao; 2 3 import org.springframework.stereotype.Service; 4 5 /** 6 * @author Simple 7 * @date 9:57 2019/8/20 8 * @description 9 */ 10 @Service 11 public class UserServiceImpl implements UserService { 12 13 @Override 14 public void save() { 15 System.out.println("保存成功....."); 16 } 17 }
(4)Aop類:
1 package cn.spring.aop; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 import org.aspectj.lang.annotation.*; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * @author Simple 9 * @date 10:06 2019/8/20 10 * @description 11 */ 12 @Component 13 @Aspect 14 public class AopAspect { 15 /** 16 * 前置通知 17 */ 18 @Before("execution(* cn.spring.aop.dao.UserService.save(..))") 19 public void before(){ 20 System.out.println("前置通知...."); 21 } 22 23 /** 24 * 后置通知 25 * returnVal,切點方法執行后的返回值 26 */ 27 @AfterReturning(value="execution(* cn.spring.aop.dao.UserService.save(..))",returning = "returnVal") 28 public void AfterReturning(Object returnVal){ 29 System.out.println("后置通知...."+returnVal); 30 } 31 32 33 /** 34 * 環繞通知 35 * @param joinPoint 可用於執行切點的類 36 * @return 37 * @throws Throwable 38 */ 39 @Around("execution(* cn.spring.aop.dao.UserService.save(..))") 40 public Object around(ProceedingJoinPoint joinPoint) throws Throwable { 41 System.out.println("環繞通知前...."); 42 Object obj= (Object) joinPoint.proceed(); 43 System.out.println("環繞通知后...."); 44 return obj; 45 } 46 47 /** 48 * 拋出通知 49 * @param e 50 */ 51 @AfterThrowing(value="execution(* cn.spring.aop.dao.UserService.save(..))",throwing = "e") 52 public void afterThrowable(Throwable e){ 53 System.out.println("出現異常:msg="+e.getMessage()); 54 } 55 56 /** 57 * 無論什么情況下都會執行的方法 58 */ 59 @After(value="execution(* cn.spring.aop.dao.UserService.save(..))") 60 public void after(){ 61 System.out.println("最終通知...."); 62 } 63 }
(5)spring.xml
1 <beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.springframework.org/schema/aop 7 http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 8 <!-- 開啟注解掃描 --> 9 <context:component-scan base-package="cn.spring.aop"></context:component-scan> 10 <!-- 啟動@aspectj的自動代理支持--> 11 <aop:aspectj-autoproxy /> 12 </beans>
(6)測試類:
1 package cn.spring.aop; 2 3 import cn.spring.aop.dao.UserService; 4 import cn.spring.aop.dao.UserServiceImpl; 5 import org.springframework.context.ApplicationContext; 6 import org.springframework.context.support.ClassPathXmlApplicationContext; 7 8 /** 9 * @author Simple 10 * @date 10:13 2019/8/20 11 * @description 12 */ 13 public class TestDemo { 14 public static void main(String[] args) { 15 ApplicationContext ac =new ClassPathXmlApplicationContext("spring.xml"); 16 UserService userService = (UserService) ac.getBean("userServiceImpl"); 17 userService.save(); 18 } 19 }
(7)運行結果:
二、配置詳解
(1)spring.xml中注解的作用
1.spring--<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
這個是 開啟事物注解權限,引入了三個jar包 aspectjweaver.jar aspectjrt.jar aspectj.jar aopalliance.jar。
2.Spring -- <context:component-scan>
在xml配置了這個標簽后,spring可以自動去掃描base-pack下面或者子包下面的java文件,如果掃描到有@Component @Controller@Service等這些注解的類,則把這些類注冊為bean。
(2)Aop類中的注解
在aop類中,編寫了5種注解類型的通知函數:
@Before 前置通知
@AfterReturning 后置通知
@Around 環繞通知
@AfterThrowing 異常通知
@After 最終通知
@pointcut 定義切點匹配表達式
(3)切點表達式
- execution
由於Spring切面粒度最小是達到方法級別,而execution表達式可以用於明確指定方法返回類型,類名,方法名和參數名等與方法相關的部件,並且在Spring中,大部分需要使用AOP的業務場景也只需要達到方法級別即可,因而execution表達式的使用是最為廣泛的。如下是execution表達式的語法:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
這里問號表示當前項可以有也可以沒有,其中各項的語義如下:
- modifiers-pattern方法的可見性,如public,protected;
- ret-type-pattern:方法的返回值類型,如int,void等;
- declaring-type-pattern:方法所在類的全路徑名,如com.spring.Aspect;
- name-pattern:方法名類型,如buisinessService();
- param-pattern:方法的參數類型,如java.lang.String;
- throws-pattern:方法拋出的異常類型,如java.lang.Exception;
如下是一個使用execution表達式的例子:
execution(public * com.spring.service.BusinessObject.businessService(java.lang.String,..))
上述切點表達式將會匹配使用public修飾,返回值為任意類型,並且是com.spring.BusinessObject類中名稱為businessService的方法,方法可以有多個參數,但是第一個參數必須是java.lang.String類型的方法。
通配符的類型,主要有兩種:
- *通配符,該通配符主要用於匹配單個單詞,或者是以某個詞為前綴或后綴的單詞。
如下示例表示返回值為任意類型,在com.spring.service.BusinessObject類中,並且參數個數為零的方法:execution(* com.spring.service.BusinessObject.*())
- ..通配符,該通配符表示0個或多個項,主要用於declaring-type-pattern和param-pattern中,如果用於declaring-type-pattern中,則表示匹配當前包及其子包,如果用於param-pattern中,則表示匹配0個或多個參數。
如下示例表示匹配返回值為任意類型,並且是com.spring.service包及其子包下的任意類的名稱為businessService的方法,而且該方法不能有任何參數:execution(* com.spring.service..*.businessService())
這里需要說明的是,包路徑service..*.businessService()中的..應該理解為延續前面的service路徑,表示到service路徑為止,或者繼續延續service路徑,從而包括其子包路徑;后面的*.businessService(),這里的*表示匹配一個單詞,因為是在方法名前,因而表示匹配任意的類。
如下示例是使用..表示任意個數的參數的示例,需要注意,表示參數的時候可以在括號中事先指定某些類型的參數,而其余的參數則由..進行匹配:
execution(* com.spring.service.BusinessObject.businessService(java.lang.String,..))
三、基於xml的SpringAop配置
xml配置主要是將注解轉換為xml這里我們在上述的情況下做下修改,主要修改兩個地方:1,spring.xml,2 aop類
1.springaop.xml
1 <beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.springframework.org/schema/aop 7 http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 8 <!--基於配置需要我們手動進行配置--> 9 <!-- dao 實例 --> 10 <bean id="userService" class="cn.spring.aop.dao.UserServiceImpl"></bean> 11 <!-- 切面類 --> 12 <bean id="aop" class="cn.spring.aop.AopAspect2"></bean> 13 <!-- Aop配置 --> 14 <aop:config> 15 <!-- 定義一個切入點表達式: 攔截哪些方法 --> 16 <aop:pointcut expression="execution(* cn.spring.aop.dao.UserService.*(..))" id="pt"/> 17 <!-- 切面 --> 18 <aop:aspect ref="aop"> 19 <!-- 環繞通知 --> 20 <aop:around method="around" pointcut-ref="pt"/> 21 <!-- 前置通知: 在目標方法調用前執行 --> 22 <aop:before method="before" pointcut-ref="pt"/> 23 <!-- 后置通知: --> 24 <aop:after method="after" pointcut-ref="pt"/> 25 <!-- 返回后通知 --> 26 <aop:after-returning method="afterReturning" pointcut-ref="pt" /> 27 <!-- 異常通知 --> 28 <aop:after-throwing method="after" pointcut-ref="pt"/> 29 </aop:aspect> 30 </aop:config> 31 </beans>
2.aop類 AopAspect2
1 package cn.spring.aop; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 import org.aspectj.lang.annotation.*; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * @author Simple 9 * @date 10:06 2019/8/20 10 * @description 11 */ 12 @Component 13 @Aspect 14 public class AopAspect2 { 15 /** 16 * 前置通知 17 */ 18 public void before(){ 19 System.out.println("前置通知...."); 20 } 21 22 /** 23 * 后置通知 24 * returnVal,切點方法執行后的返回值 25 */ 26 public void afterReturning(){ 27 System.out.println("后置通知...."); 28 } 29 30 31 /** 32 * 環繞通知 33 * @param joinPoint 可用於執行切點的類 34 * @return 35 * @throws Throwable 36 */ 37 public Object around(ProceedingJoinPoint joinPoint) throws Throwable { 38 System.out.println("環繞通知前...."); 39 Object obj= (Object) joinPoint.proceed(); 40 System.out.println("環繞通知后...."); 41 return obj; 42 } 43 44 /** 45 * 拋出通知 46 * @param e 47 */ 48 public void afterThrowable(Throwable e){ 49 System.out.println("出現異常:msg="+e.getMessage()); 50 } 51 52 /** 53 * 無論什么情況下都會執行的方法 54 */ 55 public void after(){ 56 System.out.println("最終通知...."); 57 } 58 }
3.測試方法
1 package cn.spring.aop; 2 3 import cn.spring.aop.dao.UserService; 4 import cn.spring.aop.dao.UserServiceImpl; 5 import org.springframework.context.ApplicationContext; 6 import org.springframework.context.support.ClassPathXmlApplicationContext; 7 8 /** 9 * @author Simple 10 * @date 10:13 2019/8/20 11 * @description 12 */ 13 public class TestDemo { 14 public static void main(String[] args) { 15 ApplicationContext ac =new ClassPathXmlApplicationContext("springaop.xml"); 16 UserService userService = (UserService) ac.getBean("userService"); 17 userService.save(); 18 } 19 }
4.運行結果
現在在開發中主要是使用注解進行開發,方便快捷,但是更多的是配置和注解一塊使用,springaop在配置方面很多都是這種注解加配置,原因主要是方便管理維護。我們這里主要講的是兩種方式的使用和切點表達式。