Spring詳解(七)------AOP 注解


  上一篇博客我們講解了 AspectJ 框架如何實現 AOP,然后具體的實現方式我們是通過 xml 來進行配置的。xml 方式思路清晰,便於理解,但是書寫過於麻煩。這篇博客我們將用 注解 的方式來進行 AOP 配置。

  為了便於大家理解,講解方式是這樣的,我們先給出 xml 的配置,然后介紹如何通過 注解 來進行替代。

  PS:本篇博客源碼下載鏈接:http://pan.baidu.com/s/1dFdBHZF 密碼:3v4k

 

1、xml 的方式實現 AOP 

  ①、接口 UserService

package com.ys.aop;

public interface UserService {
	//添加 user
	public void addUser();
	//刪除 user
	public void deleteUser();
}

  ②、實現類 UserServiceImpl

package com.ys.aop;

public class UserServiceImpl implements UserService{
	@Override
	public void addUser() {
		System.out.println("增加 User");
	}
	@Override
	public void deleteUser() {
		System.out.println("刪除 User");
	}
}

  ③、切面類,也就是通知類 MyAspect

package com.ys.aop;

import org.aspectj.lang.JoinPoint;


public class MyAspect {
	/**
	 * JoinPoint 能獲取目標方法的一些基本信息
	 * @param joinPoint
	 */
	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 void myAfterThrowing(JoinPoint joinPoint,Throwable e){
		System.out.println("拋出異常通知 : " + e.getMessage());
	}
	
	public void myAfter(){
		System.out.println("最終通知");
	}

}

  ④、AOP配置文件 applicationContext.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、創建目標類 -->
	<bean id="userService" class="com.ys.aop.UserServiceImpl"></bean>   
	<!--2、創建切面類(通知)  --> 
	<bean id="myAspect" class="com.ys.aop.MyAspect"></bean>
	
	<!--3、aop編程  
		3.1 導入命名空間
		3.2 使用 <aop:config>進行配置
				proxy-target-class="true" 聲明時使用cglib代理
				如果不聲明,Spring 會自動選擇cglib代理還是JDK動態代理
			<aop:pointcut> 切入點 ,從目標對象獲得具體方法
			<aop:advisor> 特殊的切面,只有一個通知 和 一個切入點
				advice-ref 通知引用
				pointcut-ref 切入點引用
		3.3 切入點表達式
			execution(* com.ys.aop.*.*(..))
			選擇方法         返回值任意   包             類名任意   方法名任意   參數任意
	
	-->
	<aop:config>
		<aop:aspect ref="myAspect">
		<!-- 切入點表達式 -->
		<aop:pointcut expression="execution(* com.ys.aop.*.*(..))" 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:after method="myAfter" pointcut-ref="myPointCut"/>	
			
		</aop:aspect>
	</aop:config>
</beans>

  ⑤、測試

@Test
public void testAop(){
	ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
	UserService useService = (UserService) context.getBean("userService");
	useService.addUser();
	useService.deleteUser();
}

  ⑥、控制台打印結果

  

 

  上面的例子很簡單,就是在 UserService 的 addUser()方法和 deleteUser()方法增加前置通知和后置通知,這在實際操作中很好理解。比如這是和數據庫打交道的話,那么我們在 addUser() 或者 deleteUser() 時,必須要在前面開始事務,操作完畢后提交事務。下面我們就用注解的方式來配置。

 

2、注解實現 AOP

  ①、導入相應的 jar 包,以及在 applicationContext.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">	
	
</beans>

  

  ②、注解配置 bean

  xml配置:

	<!--1、創建目標類 -->
	<bean id="userService" class="com.ys.aop.UserServiceImpl"></bean>   
	<!--2、創建切面類(通知)  --> 
	<bean id="myAspect" class="com.ys.aop.MyAspect"></bean>

  注解配置:

  目標類:

  

 

   切面類:

  

 

   ③、配置掃描注解識別

  這個我們在前面也講過,上面配置的注解,Spring 如何才能識別這些類上添加了注解呢?我們必須告訴他。

  在 applicationContext.xml 文件中添加如下配置:

<!-- 配置掃描注解類 
		base-package:表示含有注解類的包名。
		如果掃描多個包,則下面的代碼書寫多行,改變 base-package 里面的內容即可!
	-->
	<context:component-scan base-package="com.ys.aop"></context:component-scan>

  

  ④、注解配置 AOP

  一、我們用xml配置過如下:

  

  這是告訴 Spring 哪個是切面類。下面我們用注解配置

  我們在切面類上添加 @Aspect 注解,如下:

  

 

   二、如何讓 Spring 認識我們所配置的 AOP 注解呢?光有前面的類注解掃描是不夠的,這里我們要額外配置 AOP 注解識別。

  我們在 applicationContext.xml 文件中增加如下配置:

	<!--2、確定 aop 注解生效  -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

  

  三、注解配置前置通知

  我們先看 xml 配置前置通知如下:

<!-- 切入點表達式 -->
		<aop:pointcut expression="execution(* com.ys.aop.*.*(..))" 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"/>

  那么注解的方式如下:

  

 

  四、注解配置后置通知

  xml 配置后置通知:

<!-- 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" />

  注意看,后置通知有個 returning="ret" 配置,這是用來獲得目標方法的返回值的。

  注解配置如下:

  

  五、測試

@Test
	public void testAopAnnotation(){
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext_Annotation.xml");
		UserService useService = (UserService) context.getBean("userService");
		useService.addUser();
		useService.deleteUser();
	}

  六、控制台打印結果

  

 

3、注解改進  

   我們可以看前置通知和后置通知的注解配置:

  

  注意看紅色框住的部分,很顯然這里是重復的,而且如果我們有多個通知方法,那就得在每個方法名都寫上該注解,而且如果包名夠復雜,也很容易寫錯。那么怎么辦呢?

  解決辦法就是聲明公共切入點:

  ①、在 切面類 MyAspect.java 中新增一個切入點方法 myPointCut(),然后在這個方法上添加 @Pointcut 注解

  

 

  ②、那么前置通知和后置通知,我們可以進行如下改寫配置:

  

 

 

4、總結 

   上面我們只進行了前置通知和后置通知的講解,還有比如最終通知、環繞通知、拋出異常通知等,配置方式都差不多,這里就不進行一一講解了。然后我們看一下這些通知的注解:

  @Aspect  聲明切面,修飾切面類,從而獲得 通知。

  通知

    @Before 前置

    @AfterReturning 后置

    @Around 環繞

    @AfterThrowing 拋出異常

    @After 最終

  切入點

    @PointCut ,修飾方法 private void xxx(){}  之后通過“方法名”獲得切入點引用

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM