Spring AOP
概念
- 面向切面編程,擴展功能不需修改源代碼
- AOP採用橫向抽取機制,取代了縱向抽取機制
- 在運行時,動態地將代碼切入到類的指定方法、指定位置上的編程思想
AOP和OOP的關係
- AOP面向切面編程,而OOP面向對象編程
- AOP是對OOP的一種補充,而不是對立關係
AOP的演變
從縱向抽取機制–>橫向抽取機制
AOP底層使用動態代理方式實現
第一種情況:有接口,使用JDK動態代理方式創建接口實現類的代理對象
第二種情況:沒有接口,使用cglib創建類的子類代理對象
總結
- 函數本身只需要實現業務邏輯即可
- 散落在各個功能方法上卻和業務邏輯無關的功能可以封裝起來,形成一個橫切關注點(橫切面)
AOP術語
- 連接點:類中可以被增強的方法,這些方法稱爲連接點
- 切入點:實際增強的方法稱爲切入點
- Advice(通知/增強):增強的邏輯稱爲增強,比如擴展日誌功能,日誌功能的邏輯就稱爲增強
分類 | 含義 |
---|---|
前置通知 | 在方法之前執行 |
後置通知 | 在方法之後執行 |
異常通知 | 方法出現異常 |
最終通知 | 在後置之後執行 |
環繞通知 | 在方法之前和之後執行 |
- 切面:把具體增強的邏輯應用到具體執行的方法上面的過程,稱爲切面
把增強用到切入點的過程
Spring實現AOP的操作
在Spring中進行aop操作,需要依賴aspectJ實現
基於aspectJ實現aop通過xml配置方式
一. 步驟
- 導入aop相關的jar
- 創建核心配置文件,導入AOP約束
- 準備增強類和被增強類(指定切入點和通知)
- 使用表達式配置切入點
二. 常用的表達式(使用表達式目的是配置切入點,完成方法的增強)
execution(<訪問修飾符>?<返回類型><方法名>(<參數>)<異常>), - 訪問修飾符:可以是public或者private 通常情況可以指定*,代表是任意的訪問修飾符
- 方法的全路徑:是增強方法的全路徑,表示對這個路徑下的類中的方法做增強,可以在類的後面加上*,表示對所有方法做增強
eg:execution(* com.hpe.aop.Book.* (…))
也可以匹配以XX開頭的方法做增強execution(* save* (…)):以save開頭的方法都能做增強
三. 使用xml配置切面,實現前置通知
Book.java
// 被增強類
public class Book {
// 切入點
public void add(){
System.out.println("add...");
}
// 連接點(也是切入點)
public void delete(){
System.out.println("delete...");
}
}
MyAdvice.java
// 增強類
public class MyAdvice {
// 前置通知
public void doBefore(){
System.out.println("前置增強...");
}
// 後置通知
public void doAfter(){
System.out.println("後置增強...");
}
// 環繞通知 ProceedingJoinPoint:用來調用被增強的方法
public void doAround(ProceedingJoinPoint p) throws Throwable{
// 方法之前
System.out.println("方法之前...");
// 執行被增強的方法
p.proceed();
// 方法之後
System.out.println("方法之後...");
}
}
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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1.配置對象(被增強類和增強類) -->
<bean id="book" class="com.hpe.aop.Book"></bean>
<bean id="myAdvice" class="com.hpe.aop.MyAdvice"></bean>
<!-- 2.配置aop操作 -->
<aop:config>
<!-- 2.1配置切入點(指定對哪些方法做增強) -->
<aop:pointcut expression="execution(* com.hpe.aop.Book.add(..))" id="pointcut1"/>
<aop:pointcut expression="execution(* com.hpe.aop.Book.delete(..))" id="pointcut2"/>
<!-- 2.2配置切面(把增強應用到具體的切入點) ref:增強的對象-->
<aop:aspect ref="myAdvice">
<!-- 增強的類型 method:增強使用哪個方法作爲前置通知 pointcut-ref:把增強應用到哪個切入點上面 -->
<aop:before method="doBefore" pointcut-ref="pointcut1"/>
<!-- 後置通知 -->
<aop:after-returning method="doAfter" pointcut-ref="pointcut1"/>
<!-- 環繞通知 -->
<aop:around method="doAround" pointcut-ref="pointcut2"/>
</aop:aspect>
</aop:config>
</beans>
測試類
// 使用XML配置方式實現前置、後置通知做方法增強
@Test
public void test2(){
// 1.讀取配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2.獲得對象
Book book = (Book) context.getBean("book");
// 3.訪問方法
book.add();
}
// 使用XML配置方式實現環繞通知做方法增強
@Test
public void test3(){
// 1.讀取配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2.獲得對象
Book book = (Book) context.getBean("book");
// 3.訪問方法
book.delete();
}
基於aspectJ實現aop通過註解方式
AOP相關的註解
註解 | 含義 |
---|---|
@Aspect | 註解在類上面,聲明一個切面 |
@Pointcut | 註解在方法上面,聲明一個切入點,況且要指定aspect表達式 |
@Before | 前置通知 |
@After | @AfterReturning後置通知,@AfterThrow異常通知 |
@Round | 環繞通知 |
代碼實例
被增強類Person.java
// 被增強類(被代理對象)
@Component(value="person")
public class Person {
public void eat(){
System.out.println("eat...");
}
public void drink(){
System.out.println("drink...");
}
}
增強類MyAspect.java
// 增強類
// 3.在增強類上使用註解
@Component
@Aspect
public class MyAspect {
// 在方法上面使用註解來配置通知
// Before:前置通知;value:切入點(表達式)
@Before(value="execution(* com.hpe.aop.Person.eat(..))")
public void doBefore(){
System.out.println("前置增強...");
}
@After(value="execution(* com.hpe.aop.Person.drink(..))")
public void doAfter(){
System.out.println("後置增強...");
}
}
applicationContext2.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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1.創建對象 -->
<!-- <bean id="person" class="com.hpe.aop.Person"></bean>
<bean id="myAspect" class="com.hpe.aop.MyAspect"></bean> -->
<!-- 開啓註解掃描 -->
<context:component-scan base-package="com.hpe.aop"></context:component-scan>
<!-- 2.使用註解的方式開啓AOP的操作:開啓註解掃描 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
Test.java
// 使用註解方式實現環繞通知做方法增強
@Test
public void test4(){
// 1.讀取配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
// 2.獲得對象
Person person = (Person) context.getBean("person");
// 3.訪問方法
person.eat();
person.drink();
}