天天看點

Spring架構(六)—— Spring AOP一、Spring AOP二、AOP 的實作方式

文章目錄

  • 一、Spring AOP
    • 1、Spring AOP 概述
    • 2、AOP 核心概念
  • 二、AOP 的實作方式
    • 1、Spring 的原生AOP實作方式
    • 2、整合 Aspect 架構實作AOP(使用xml檔案)
    • 3、整合 Aspect 架構實作AOP(使用注解)

一、Spring AOP

1、Spring AOP 概述

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

(2)OOP允許開發者定義縱向的關系,但并不适合定義橫向的關系,例如日志功能。日志代碼往往橫向地散布在所有對象層次中,而與它對應的對象的核心功能毫無關系。對于其他類型的代碼,如安全性、異常處理、事務管理等等,在OOP設計中,它導緻了大量代碼的重複,而不利于各個子產品的重用。

(3)AOP技術恰恰相反,它利用一種稱為"橫切"的技術,剖解開封裝的對象内部,并将那些影響了多個類的公共行為封裝到一個可重用子產品,并将其命名為"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻為業務子產品所共同調用的邏輯或責任封裝起來,便于減少系統的重複代碼,降低子產品之間的耦合度,并有利于未來的可操作性和可維護性。

如下圖:

沒有使用AOP之前:

Spring架構(六)—— Spring AOP一、Spring AOP二、AOP 的實作方式

使用AOP之後:

Spring架構(六)—— Spring AOP一、Spring 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在

    的依賴包
    Spring架構(六)—— Spring AOP一、Spring AOP二、AOP 的實作方式

(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>
           
  • 也可以使用注解,在實作層和通知類添加注解,然後采用下面的配置代碼替代上面兩個配置

② 配置切入點

<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();
		}
	}
}