Spring框架(六)—— Spring AOP

一、Spring AOP

1、Spring AOP 概述

(1)AOP(Aspect Oriented Programming),即面向切面編程,AOP是對OOP(Object Oriented Programming,面向對象編程)的補充和完善。

(2)OOP允許開發者定義縱向的關係,但並不適合定義橫向的關係,例如日誌功能。日誌代碼往往橫向地散佈在所有對象層次中,而與它對應的對象的核心功能毫無關係。對於其他類型的代碼,如安全性、異常處理、事務管理等等,在OOP設計中,它導致了大量代碼的重複,而不利於各個模塊的重用。

(3)AOP技術恰恰相反,它利用一種稱爲"橫切"的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其命名爲"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模塊之間的耦合度,並有利於未來的可操作性和可維護性。
如下圖:
沒有使用AOP之前:
在這裏插入圖片描述
使用AOP之後:
在這裏插入圖片描述

2、AOP 核心概念

(1)通知 Advice:

  • 定義:通知指的就是指攔截到連接點之後要執行的代碼(工作),在 AOP 術語中,切面的工作被稱爲通知。
  • 工作內容:通知定義了切面是什麼以及何時使用。除了描述切面要完成的工作,通知還解決何時執行這個工作。
  • Spring 的 5 種通知類型:
  • 前置Before——在方法調用之前調用通知
  • 後置After-returning——在方法執行成功之後調用通知
  • 異常After-throwing——在方法拋出異常後進行通知
  • 最終After——在方法完成之後調用通知,無論方法執行成功與否
  • Around——通知包裹了被通知的方法,在被通知的方法調用之前和調用之後執行自定義的行爲

(2)連接點 JointPoint:

  • 定義:連接點是一個應用執行過程中能夠插入一個切面的點,連接點代表了時間,也就是何時去執行。
  • 程序執行過程中能夠應用通知的所有點。

(3)切點 Pointcut:

  • 定義:切點表示一組連接點 JointPoint,如果通知定義了“什麼”和“何時”。那麼切點就定義了“何處”。
  • 通常使用明確的類或者方法來指定這些切點。
  • 作用:定義通知被應用的位置(在哪些連接點)

(4)切面 Aspect:

  • 定義:切面是通知和切點的集合,通知和切點共同定義了切面的全部功能——它是什麼,在何時何處完成其功能。
    切面聲明類似於 Java 中的類聲明,在 Aspect 中會包含着一些 Pointcut 以及相應的 Advice。切面就是對何時做什麼事情的抽象。

(5)引入:

  • 引入允許我們向現有的類中添加方法或屬性

(6)織入 weave:

  • 織入是將切面應用到目標對象來創建的代理對象過程。
  • 切面在指定的連接點被織入到目標對象中,在目標對象的生命週期中有多個點可以織入
  • 編譯期——切面在目標類編譯時期被織入,這種方式需要特殊編譯器。AspectJ的織入編譯器就是以這種方式織入切面。
  • 類加載期——切面在類加載到
  • JVM ,這種方式需要特殊的類加載器,他可以在目標類被引入應用之前增強該目標類的字節碼。AspectJ5 的 LTW 就支持這種織入方式
  • 運行期——切面在應用運行期間的某個時刻被織入。一般情況下,在織入切面時候,AOP 容器會爲目標對象動態的創建代理對象。Spring AOP 就是以這種方式織入切面。

二、AOP 的實現方式

1、Spring 的原生AOP實現方式

(1)定義業務層接口和業務層實現類
① 接口層

public interface ProductsService {
	public void insert() throws Exception;
}

② 實現層

public class ProductsServiceImp implements ProductsService{
	@Override
	public void insert() throws Exception {
		System.out.println("執行新增業務操作");
	}
}

(2)定義通知類

public class TransactionAdvice implements MethodBeforeAdvice,AfterReturningAdvice{
	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
		//後置增強
		System.out.println("執行後置增強");
	}
	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
		//前置增強
		System.out.println("執行前置增強");
	}
}

重要API:

  • MethodBeforeAdvice:前置增強接口,會在代理對象執行目標對象方法之前執行
  • AfterReturningAdvice:後置增強接口,會在代理對象執行目標對象方法之後執行
  • MethodInterceptor:方法攔截器,目標對象的方法被嵌入到該類的方法中,所以可以更靈活的實現各種不同的增強處理

(3)在 applicationContext.xml 中完成AOP配置

<?xml version="1.0" encoding="UTF-8"?>
<beans  
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"     xmlns:p="http://www.springframework.org/schema/p"   xmlns:util="http://www.springframework.org/schema/util"     xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-4.0.xsd">
		
		<!-- 創建要被代理的目標對象 -->
		<bean id="service" class="com.spring.demo5_aop.service.imp.ProductsServiceImp"></bean>
		
		<!-- 創建通知類對象 -->
		<bean id="transactionAdvice" class="com.spring.demo5_aop.advice.TransactionAdvice"></bean>
		
		<!-- 定義切入點 -->
		<bean id="pointCut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
		      <property name="pattern" value="com.spring.demo5_aop.service.ProductsService.insert"></property>
		</bean>
		
		<!-- 配置切面,描述何時做什麼事情 -->
		<bean id="transAspect" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		      <property name="advice" ref="transactionAdvice"></property>
		      <property name="pointcut" ref="pointCut"></property>
		</bean>
		
		<!-- 創建動態代理對象 -->
		<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		      <!-- 目標對象 -->
		      <property name="target" ref="service"></property>
		      <!-- 使用切面 -->
              <property name="interceptorNames" value="transAspect"></property>
              <!-- 代理類需要實現的接口 -->
              <property name="proxyInterfaces" value="com.spring.demo5_aop.service.ProductsService"></property>
		</bean>
</beans>
  • 解析xml文件獲取代理對象調用方法
    在上面的創建動態代理對象是手動創建,也可以使用下面的代碼替換手動創建動態代理對象的那一段代碼,從而實現在 xml 文件中使用自動動態代理構建器,Spring 會檢查切入點與 bean 的關係,在創建對象時自動生成代理對象。
<!-- 自動動態代理構建器 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>

(4)測試類

public class Test {
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		ProductsService service = (ProductsService) ac.getBean("proxy");	//注意這裏是"proxy"
		System.out.println(service);
		try {
			service.insert();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

2、整合 Aspect 框架實現AOP(使用xml文件)

(1)定義業務層接口和業務層實現類
① 接口層

public interface ProductsService {
	public void insert() throws Exception;
}

② 實現層

	public class ProductsServiceImp implements ProductsService{
		@Override
		public void insert() throws Exception {
			System.out.println("執行新增業務操作");
		}
	}

(2)定義通知類,該通知類不需要實現任何接口或者繼承任何類,方法名稱完全自定義

public class TransactionAdvice {
	public void start() {
		System.out.println("開啓事務");
	}
	public void commit() {
		System.out.println("提交事務");
	}
	public void rollback() {
		System.out.println("回滾事務");
	}
	public void close() {
		System.out.println("關閉事務");
	}
	//獲取代理對象調用方法
	public Object around(ProceedingJoinPoint pj) {
		Object result=null;
		try {
			start();
			result=pj.proceed(pj.getArgs());
			commit();
		} catch (Throwable e) {
			rollback();
		}finally {
			close();
		}
		return result;
	}
}
  • ProceedingJoinPoint在的依賴包
    在這裏插入圖片描述

(3)在 applicationContext.xml 文件中完成織入(通過切入點、通知、目標對象創建代理對象的過程)
① 創建通知、目標對象

<!-- 創建要被代理的目標對象 -->
<bean id="service" class="com.spring.demo5_aop.service.imp.ProductsServiceImp"></bean>
<!-- 創建通知類對象 -->
<bean id="transactionAdvice" class="com.spring.demo5_aop.advice.TransactionAdvice"></bean>
  • 也可以使用註解,在實現層和通知類添加註解,然後採用下面的配置代碼替代上面兩個配置
<context:component-scan base-package="com.spring.demo5_aop"></context:component-scan>	

② 配置切入點

<aop:config>
	//用於說明當前所織入的代理對象中通知類的對象
    <aop:aspect ref="transactionAdvice">	//ref=”引用通知類的對象”
    	  //用於描述切入點
        <aop:pointcut expression="execution(* com.spring.demo5_aop.service.imp.*.*(..))" id="point"/>
        	//執行目標方法前置增強處理
        <aop:before method="start" pointcut-ref="point"/>
        //執行目標方法後異常增強處理
        <aop:after-returning method="commit" pointcut-ref="point"/>
        //正常執行目標方法後增強處理
        <aop:after-throwing method="rollback" pointcut-ref="point"/>
        //無論正常還是異常後置增強處理
        <aop:after method="close" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>
  • 爲了完成上述功能可以也可以採用環繞式增強處理,不能再配置環繞增強的同時添加其他類型的增強方式。
<aop:config>
	<aop:aspect ref="transactionAdvice">
		<aop:pointcut expression="execution(* com.woniu.service.*.*(..))" id="point"/>
		<aop:around method="around" pointcut-ref="point"/>
	</aop:aspect>
</aop:config>

(4)測試類

public class Test {
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring5_1.xml");
		ProductsService service = (ProductsService) ac.getBean("service");
		System.out.println(service);
		try {
			service.insert();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

3、整合 Aspect 框架實現AOP(使用註解)

(1)定義業務層接口和業務層實現類
① 接口層

public interface ProductsService {
	public void insert() throws Exception;
}

② 實現層

@Service
public class ProductsServiceImp implements ProductsService{
	@Override
	public void insert() throws Exception {
		System.out.println("執行新增業務操作");
	}
}

(2)定義通知類,並在通知類上添加註解,描述那些切入點應該執行哪種增強處理

@Component
@Aspect
public class TransactionAdvice {
	@Before("execution(* com.spring.demo5_aop.service.imp.*.*(..))")
	public void start() {
		System.out.println("開啓事務");
	}
	@AfterReturning("execution(* com.spring.demo5_aop.service.imp.*.*(..))")
	public void commit() {
		System.out.println("提交事務");
	}
	@AfterThrowing("execution(* com.spring.demo5_aop.service.imp.*.*(..))")
	public void rollback() {
		System.out.println("回滾事務");
	}
	@After("execution(* com.spring.demo5_aop.service.imp.*.*(..))")
	public void close() {
		System.out.println("關閉事務");
	}
	public Object around(ProceedingJoinPoint pj) {
		Object result=null;
		try {
			start();
			result=pj.proceed(pj.getArgs());
			commit();
		} catch (Throwable e) {
			rollback();
		}finally {
			close();
		}
		return result;
	}
}

或者

@Component
@Aspect
public class TransactionAdvice {
	public void start() {
		System.out.println("開啓事務");
	}
	public void commit() {
		System.out.println("提交事務");
	}
	public void rollback() {
		System.out.println("回滾事務");
	}
	public void close() {
		System.out.println("關閉事務");
	}
	@Around("execution(* com.spring.demo5_aop.service.imp.*.*(..))")			//注意這裏
	public Object around(ProceedingJoinPoint pj) {
		Object result=null;
		try {
			start();
			result=pj.proceed(pj.getArgs());
			commit();
		} catch (Throwable e) {
			rollback();
		}finally {
			close();
		}
		return result;
	}
}

(3)在 applicationContext.xml 中開啓aop註解自動代理方式,並同時掃描包。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-4.0.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
            
       <context:component-scan base-package="com.spring.demo5_aop"></context:component-scan>
       <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

(4)測試類

public class Test {
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		ProductsService service = (ProductsService) ac.getBean("productsServiceImp");
		System.out.println(service);
		try {
			service.insert();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章