Spring AOP介紹(一)之 Spring AOP 基礎介紹
AOP是什麼?
AOP(Aspect Oriented Programming),即面向切面編程,可以說是OOP(Object Oriented Programming,面向對象編程)的補充和完善。與OOP對比,AOP面向切面,傳統的OOP開發中,代碼邏輯是自上而下的,在這些自上而下的過程中,會產生一些橫切性的問題,這些橫切性的問題和我們的主業務邏輯關係不大,會散落在代碼的各個地方,造成難以維護,AOP的變成思想就是把業務邏輯和橫切的問題進行分離,從而達到解耦的目的,提到代碼的可重用性與開發效率。
可以看下這個圖:
AOP的應用場景有哪些?
如上圖中顯示的:
- 日誌記錄
- 權限驗證
- 事務管理
- 等等
Spring對於AOP是怎樣實現的呢?
- JDK動態代理(接口實現)
JDK原生動態代理是Java原生支持的,不需要任何外部依賴,但是它只能基於接口進行代理
- CGLIB代理(繼承)
CGLIB(Code Generation Library)是一個基於ASM的字節碼生成庫,
它允許我們在運行時對字節碼進行修改和動態生成。CGLIB通過繼承方式實現代理。
編譯時期的織入還是運行時期的織入?
兩者都是在運行時期織入(後面有源碼介紹)
初始化時期織入還是獲取對象時期織入?
通過源碼分析,可以知道是在初始化時期織入(後面有源碼介紹)
Spring AOP和AspectJ的關係是什麼?
對於Spring AOP來說,AOP是實現的目標,AspectJ是實現的手段,類似的還有IOC(Inversion of Control 控制反轉)是目標,實現的手段是DI(Dependency Injection 依賴注入)。
Spring AOP提供兩種編程風格
- @AspectJ support (利用aspectj的註解)
- Schema-based AOP support (xml aop:config 命名空間)
Spring AOP應用
讓我們首先了解一些AOP概念和術語。
概念 | 描述 |
---|---|
Join point(連接點) | 目標對象中的方法,類似數據庫表中的一條記錄 |
PointCut(切點) | 表示連接點的集合,類似數據庫中的表 |
Advice(通知) | 通知指的就是指攔截到連接點之後要執行的代碼,通知分爲前置、後置、異常、最終、環繞通知五類 |
Aspect(切面) | 切點、連接點、通知等的載體,它們加起來就是一個切面 |
Target object(目標對象) | 包含連接點的對象,也被稱作被通知或被代理對象 |
AOP proxy(AOP 代理對象) | AOP創建的對象,包含通知 |
Weaving(織入) | 把代理邏輯加入到目標對象的過程,叫做織入 |
Before advice(前置通知) | 前置通知 |
After returning advice(後置通知) | 後置通知 |
Around advice(環繞通知) | 前後都有,最常用的一種通知 |
簡單代碼演示
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo.spring</groupId>
<artifactId>spring-aop</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>spring-aop Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
com.demo.spring.dao.TestDao
package com.demo.spring.dao;
import org.springframework.stereotype.Component;
/**
* PACKAGE_NAME
*
* @author Zyy
* @date 2019/3/4 23:03
*/
@Component
public class TestDao {
public void query(){
System.out.println("dao-query ...");
}
}
com.demo.spring.config.AppConfig
package com.demo.spring.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* com.demo.spring.config
*
* @author Zyy
* @date 2019/3/4 23:05
*/
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.demo.spring")
public class AppConfig {
}
com.demo.spring.config.AspectJTest
package com.demo.spring.config;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* com.demo.spring.config
*
* @author Zyy
* @date 2019/3/4 23:36
*/
@Aspect
@Component
public class AspectJTest {
@Pointcut("execution(* com.demo.spring.dao.*.*(..))")
public void pointCut(){
System.out.println("point cut");
}
@After("com.demo.spring.config.AspectJTest.pointCut()")
public void after(){
System.out.println("after advice");
}
@Before("com.demo.spring.config.AspectJTest.pointCut()")
public void before(){
System.out.println("before advice");
}
}
com.demo.spring.test.AopTest
package com.demo.spring.test;
import com.demo.spring.config.AppConfig;
import com.demo.spring.dao.TestDao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* com.demo.spring.test
*
* @author Zyy
* @date 2019/3/4 23:07
*/
public class AopTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
TestDao dao = context.getBean(TestDao.class);
dao.query();
}
}
運行結果:
before advice
dao-query ...
after advice
歡迎留言進行討論:)