文章目錄
一、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();
}
}
}