SpringAOP
文章目錄
介紹
AOP:面向切面編程( Aspect Oriented Programming ),在不修改原來邏輯代碼的前提下,使用切面的方式添加功能(一般是通用功能,例如:日誌,事務,校驗等)
AOP實現原理:動態代理
好處:少寫代碼,降低通用功能與業務代碼的耦合
注:AOP在幾乎每個框架中都有,介紹的是spring的AOP
常見概念
1.通知advice
也就是你想加的功能
2.連接點 joinpoint
就是允許你加入功能的地方,一般是在一個方法的週期中,包括方法調用前,方法調用後,方法拋出異常等情況下,spring允許加功能的所有地方
3.切入點(Pointcut)
不是每個連接點都需要加強,程序員可以根據自己的情況,指定需要加強的方法以及方法運行的某個時間,這就是切入點.由程序員指定哪些方法的什麼時候被增強
4.切面(Aspect)
切面=通知+切入點。通知讓你知道需要增強的功能,切入點讓你知道切入到哪兒,一整合一張的切面就形成了。其實就是你自定義的需要增強的類.
指定哪些方法的什麼時候要被增強+增強的內容
就是invoke方法中去掉method.invoke這行代碼以外所有的叫切面
5.引介(introduction)
引介是一個過程的概念,就是把上面的切面真正的加在被代理的方法上
6.目標(target)
就是要增強的類,就是被代理類
7.代理(proxy)
動態代理(cglib,jdk)
8.織入(weaving)
就是通過Proxy.newInstance()方法生成代理類的過程,比如jdk代理就是調用類加載器生成的類過程
註解方式完成spring AOP
1.加入jar包
jar包 | 概述 |
---|---|
aspectjweaver | 是spring的切入點表達式需要用的包,指定切入點 |
aopalliance | 是AOP聯盟的API包,裏面包含了針對面向切面的接口。 通常Spring等其它具備動態織入功能的框架依賴此包 |
aspectjrt | 處理事務和AOP所需的包 |
cglib | cglib動態代理需要的包 |
spring-aop | spring對AOP支持的包 |
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>1.8</source> <!-- 源代碼使用jdk1.8支持的特性 -->
<target>1.8</target> <!-- 使用jvm1.8編譯目標代碼 -->
<compilerArgs> <!-- 傳遞參數 -->
<arg>-parameters</arg>
<arg>-Xlint:unchecked</arg>
<arg>-Xlint:deprecation </arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
2.編寫配置文件與測試類
<?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">
<!--開啓AOP註解支持
spring 的AOP默認支持JDK代理與cglib代理
當被代理類實現了接口的情況下使用JDK代理,否則使用CGLIB代理
可以在<aop:aspectj-autoproxy></aop:aspectj-autoproxy>這個配置中加入
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
告訴spring強制使用cglib代理
-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<context:component-scan base-package="cn.cdqf.aop"></context:component-scan>
</beans>
3.編寫被代理類
package cn.cdqf.aop;
import org.springframework.stereotype.Service;
@Service
public class StudentService {
public void show(){
System.out.println("show方法被調用");
}
}
4.編寫切面(增強類)
package cn.cdqf.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect //該類爲增強類:切面
@Component
public class StudentAspect {
@Pointcut("execution(* cn.cdqf.aop..*.*(..))")//切入點
public void aa(){}
//Before:定義前置通知,在方法調用前被執行
//execution表達式:
// 第一個*表示任意返回值
//第一個.. 表示當前包及其子包
//第二個*表示任意類
//第三個*表示任意方法
//第二個..表示任意參數
@Before("aa()")
public void before(){
System.out.println("前置通知執行了:在方法調用前被執行");
}
//AfterReturning:後置通知,在方法正常執行結束後執行
@AfterReturning("execution(* cn.cdqf.aop..*.*(..))")
public void afterReturning(){
System.out.println("正常返回通知執行了:在方法正常執行結束後執行");
}
@AfterThrowing("execution(* cn.cdqf.aop..*.*(..))")
public void afterThrowing(){
System.out.println("異常通知執行了:在方法拋出異常後執行");
}
@After("execution(* cn.cdqf.aop..*.*(..))")
public void after(){
System.out.println("後置通知執行了:不管拋沒拋出異常都會在方法運行後執行,相當於finally");
}
}
5.測試
package cn.cdqf.aop;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:applicationAop.xml"})
public class StudentServiceTest {
@Autowired
private StudentService studentService;
@Test
public void show() {
studentService.show();
}
}
6.環繞通知
切面代碼:invoke的所有代碼都可以在這裏寫
package cn.cdqf.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
@Aspect //該類爲增強類:切面
@Component
public class StudentAspect {
//Around:就相當於我們實現動態代理的invoke方法
//比其它通知厲害的地方:
//1.可以選擇是否執行目標方法
//2.可以改變返回值與參數等等
@Around("execution(* cn.cdqf.aop..*.*(..))")
public Object around(ProceedingJoinPoint joinPoint){
//獲得方法參數 既然能獲得就能改變
Object[] args = joinPoint.getArgs();
//獲得方法簽名
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
//獲取所有參數的名稱
String[] parameterNames = signature.getParameterNames();
//獲取方法參數類型數組,配合前面的參數 可以秀幾波
Class[] paramTypeArray = signature.getParameterTypes();
try {
//1.如果在方法前面加內容 就相當於前置通知
//調用方法
Object proceed = joinPoint.proceed(args);
//2.如果在方法後面加內容就相當於正常返回通知
//正常返回方法的返回值
return proceed;
} catch (Throwable throwable) {
//3.如果在異常出現後加內容 就相當於異常通知
throwable.printStackTrace();
}finally {
//4.如果在這裏加內容就相當於後置通知
}
return null;
}
}
配置方式實現實現AOP
<!-- 定義目標對象 -->
<bean name="productDao" class="com.zejian.spring.springAop.dao.daoimp.ProductDaoImpl" />
<!-- 定義切面 -->
<bean name="myAspectXML" class="com.zejian.spring.springAop.AspectJ.MyAspectXML" />
<!-- 配置AOP 切面 -->
<aop:config>
<!-- 定義切點函數 -->
<aop:pointcut id="pointcut" expression="execution(* com.zejian.spring.springAop.dao.ProductDao.add(..))" />
<!-- 定義其他切點函數 -->
<aop:pointcut id="delPointcut" expression="execution(* com.zejian.spring.springAop.dao.ProductDao.delete(..))" />
<!-- 定義通知 order 定義優先級,值越小優先級越大-->
<aop:aspect ref="myAspectXML" order="0">
<!-- 定義通知
method 指定通知方法名,必須與MyAspectXML中的相同
pointcut 指定切點函數
-->
<aop:before method="before" pointcut-ref="pointcut" />
<!-- 後置通知 returning="returnVal" 定義返回值 必須與類中聲明的名稱一樣-->
<aop:after-returning method="afterReturn" pointcut-ref="pointcut" returning="returnVal" />
<!-- 環繞通知 -->
<aop:around method="around" pointcut-ref="pointcut" />
<!--異常通知 throwing="throwable" 指定異常通知錯誤信息變量,必須與類中聲明的名稱一樣-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="throwable"/>
<!--
method : 通知的方法(最終通知)
pointcut-ref : 通知應用到的切點方法
-->
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
AOP總結
AOP是面向切面編程思想,強調不影響正常代碼的情況下切入通用邏輯,核心實現原理是動態代理,最大好處是減少代碼量與解耦合
可以想象AOP的功能巨大
在各大框架中也常常使用AOP,例如mybatis的插件,spring的事務控制,springmvc的攔截器等
在實際開發中常常使用到AOP