文章目錄
- 一、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之前:
使用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>
- 也可以使用注解,在實作層和通知類添加注解,然後采用下面的配置代碼替代上面兩個配置
② 配置切入點
<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();
}
}
}