1.什麼是AOP:
簡而言之, AOP是 我們把重複性的代碼提取出來, 在需要的執行的時候, 使用動態代理的技術, 在不修改源碼的情況下,進行功能增強
2.AOP術語
Joinpoint( 連接點):
所謂連接點是指那些被攔截到的點。在 spring 中,這些點指的是方法,因爲 spring 只支持方法類型的連接點。
Pointcut( 切入點):
所謂切入點是指我們要對哪些 Joinpoint 進行攔截的定義。
Advice( 通知/ 增強):
所謂通知是指攔截到 Joinpoint 之後所要做的事情就是通知。
通知的類型:前置通知,後置通知,異常通知,最終通知,環繞通知。
Introduction( 引介):
引介是一種特殊的通知在不修改類代碼的前提下, Introduction 可以在運行期爲類動態地添加一些方法或 Field。
Target( 目標對象):
代理的目標對象。
Weaving( 織入):
是指把增強應用到目標對象來創建新的代理對象的過程。
spring 採用動態代理織入,而 AspectJ 採用編譯期織入和類裝載期織入。
Proxy (代理):
一個類被 AOP 織入增強後,就產生一個結果代理類。
Aspect( 切面):
是切入點和通知(引介)的結合。
3.jar包說明
4.案例
Maven座標
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<!-- 這個用於解析切入點方法表達式 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
實體類接口-AccountService.java:
public interface AccountService
{
/*
* @Author chensy
* @Description //TODO 模擬保存賬戶
* @Date 13:53 2019/4/22
* @Param []
* @return
**/
void saveAccount();
/*
* @Author chensy
* @Description //TODO 模擬更新賬戶
* @Date 13:54 2019/4/22
* @Param [i]
* @return void
**/
void updateAccount(int i);
/*
* @Author chensy
* @Description //TODO 模擬刪除賬戶
* @Date 13:54 2019/4/22
* @Param []
* @return void
**/
int deleteAccount();
}
實體類實現類--AccountSeriveImp.java
import com.chensy.service.AccountService;
public class AccountSeriveImp implements AccountService
{
public void saveAccount() {
System.out.println("執行了保存");
}
public void updateAccount(int i) {
System.out.println("執行了更新" + i);
}
public int deleteAccount() {
System.out.println("執行了刪除");
return 0;
}
}
通知類-- Logger:
public class Logger
{
/*
* @Author chensy
* @Description //TODO 打印日誌, 讓其在切入點方法執行之前執行
* @Date 13:57 2019/4/22
* @Param []
* @return void
**/
public void printLog(){
System.out.println("Logger[printLog] is stating.....");
}
}
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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置spring的ioc , 把service對象配置進來-->
<bean id="accountService" class="com.chensy.service.Imp.AccountSeriveImp"></bean>
<!-- spring中的基於xml的AOP 配置步驟
1.把通知Bean也交給spring來管理
2.使用aop:config標籤表明開始AOP的配置
3.使用aop:aspect標籤表明配置切面
id屬性: 給切面提供一個唯一標識
ref屬性: 指定通知類bean的id
4.在aop:aspect標籤內部使用相應的標籤來配置通知的類型
aop:before 前置通知
method: 指定Logger類中的哪個方法是前置通知
pointcuit: 用於指定切入點表達式, 該表達式指的是哪些方法需要增強
切入點表達式的寫法:
execution
表達式:
訪問符 返回值 包名.包名.......類名.方法名(參數列表)
如:
execution(public void com.chensy.service.Imp.AccountSeriveImp.saveAccount())
全通配寫法:
* *..*.*(..)
一般寫法:
* com.chensy.service.Imp.*.*(..)
注: 1.訪問修飾符可以省略
2.返回值可以用通配符表示
3.包路徑用..來代替, 它表示當前包下所有的子包
4.類名和方法名均可以用通配符通配
5.方法參數列表可以:
基本類型名稱: int
引用類型 寫包名.類名 java.lang.String
可以使用通配符*, 表示所有有參數的方法
可以使用.. 表示各種有無參數的方法
-->
<!-- 配置 Logger類 -->
<bean id="logger" class="com.chensy.Utils.Logger"></bean>
<!-- 配置AOP -->
<aop:config>
<aop:aspect id="logAdvice" ref="logger">
<aop:before method="printLog" pointcut="execution(public void com.chensy.service.Imp.AccountSeriveImp.saveAccount())"></aop:before>
</aop:aspect>
</aop:config>
</beans>
運行結果:
5.補充
<aop:before> 前置通知
<aop:after-returning>後置通知
<aop:after-throwing>異常通知
<aop:after> 最終通知
<aop:pointcut> 切入點表達式
<aop:round method="" pointcut-ref=""> 環繞通知
環繞通知實際上是Spring框架爲我們提供的一種可以在代碼裏手動控制增強方法何時執行的方式
用法:
public Object aroundPringLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
Object[] args = pjp.getArgs();//得到方法執行所需的參數
System.out.println("Logger類中的aroundPringLog方法開始記錄日誌了。。。前置");
rtValue = pjp.proceed(args);//明確調用業務層方法(切入點方法)
System.out.println("Logger類中的aroundPringLog方法開始記錄日誌了。。。後置");
return rtValue;
}catch (Throwable t){
System.out.println("Logger類中的aroundPringLog方法開始記錄日誌了。。。異常");
throw new RuntimeException(t);
}finally {
System.out.println("Logger類中的aroundPringLog方法開始記錄日誌了。。。最終");
}
}