Aop 簡介:
Aop(Aspect Oriented Programming)面向切面編程,是OOP面向對象編程的一種補充將程序中交叉業務(事務,日誌)代碼提取出來,封裝成切面,由AOP容器在適當時機位置將封裝成的切面動態的織入到具體業務邏輯中。
AOP不是spring特有的。
應用場合:適用於具有橫切邏輯的場合,如事務管理,日誌記錄,性能檢測,異常通知,訪問控制等。
作用:不改變原有代碼的基礎動態添加新的功能。
模塊化。
術語:
**連接點(JoinPoint)**程序執行的某個特定位置,如方法調用前,方法調用後,方法拋出異常時,方法調用前後等。
**切入點(PointCut)**定位位置找到需要切入的連接點,即切點。
增強Advice也稱爲通知 在切點上執行的一段代碼用來實現某些功能。
目標對象 Target 將執行增強處理的目標類。
織入(Weaving) 將增強添加到目標類具體切入點的過程。
代理(Proxy) 一個類被織入增強後,會產生一個代理類。
切面 Aspect 切點和增強的組合。
引介 Introduction 也稱爲引入。
實現原理
代理模式:
概念:爲其他對象提供一種代理,以控制對這個對象的訪問,起到中介的作用,通過代理對象訪問目標對象,可以增強額外的操作,擴展目標對象的功能。
分類:
**靜態代理:**代理類是程序員創建或工具生成的,所謂靜態代理就是程序運行之前就已經存在代理類的字節碼文件。缺點:代理對象需要和目標對象實現相同的接口,如果接口增加方法,目標對象和代理對象都要維護。
動態代理:代理類是程序在運行期間由JVM根據反射等機制動態生成的,自動生成代理類和代理對象,所謂動態就是在程序運行之前不存在代理類的字節碼文件。
代理三要素:
一:實現目標類的接口。
二:初始化目標類的實例。
三:交叉業務,要執行的操作。
動態代理:
JDK技術:
proxy.newProxyInstance(
classloader 目標類的類加載器
interfaces 目標類得到接口列表
InvocationHandler 交叉業務邏輯
)
缺點:目標對象必須實現一個或多個接口,如果沒有實現接口,則無法使用JDK的動態代理,此時可以使用cglib技術
- cglib技術
- 添加jar包maven依賴
- <dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
</dependency>
- cglib技術,適用於無接口的使用
通過繼承實現的cglib技術要
(1):添加jar包
(2):用法:
Enhance.create(
class, 目標類的對象
InvocationHandler 交叉業務邏輯
)
Aop原理
SpringAop 原理就是動態代理
對於實現接口的目標類使用的是jdk動態代理
對於沒有實現任何接口的目標類,使用的是cglib的動態代理
代理類是程序在運行期間由JVM根據反射等機制動態生成的自動生成代理類和代理對象。
所謂動態就是指在程序運行前不存在代理類的字節碼文件。
AOP基於註解的配置介紹
一:@Component 此註解使用在class上來聲明一個Spring組件(Bean), 將其加入到應用上下文中 ,添加到IOC容器中,不區分組件類型。
二:@Aspect 此註解使用在class類上表示這是一個切面
三: @Pointcut定義切點表達式,可以使用within語法,也可以使用execution語法。
例如: @Pointcut("execution(* aop.service.impl.*ServiceImpl.*(..))")
AspectJ表達式
簡介:切點表達式,一種表達式,用來定義切點位置。
用法:
within 語法:within(包名.類名) 匹配該類中的所有方法。
execution
匹配特定包中的特定類中特定返回值類型的特定參數的特定方法。
語法:execution(表達式)
表達式:返回值類型 包名.類名.方法名(參數類型)
通配符:和…
四:@Before 此註解表示的前置通知在方法執行之前執行。
五:@AfterReturning 此註解表示的是後置通知在方法執行之後執行該方法。
六: @AfterThrowing 此註解表示的是異常通知,在方法執行的時候出現異常時執行。
七:@Around*此註解表示的是環繞通知,在方法執行前後都會執行,該通知而用於做性能的監測,比如方法執行的時間統計。
八: contxt:aspectj-autoproxy/ 表示自動創建註解並織入
使用aop註解開發的小案例進一步瞭解。
配置通知類並基於註解實現的通知(增強)此處根據自己需要實現通知
package aop.advice;
import org.aspectj.lang.JoinPoint;
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;
import sun.awt.SunHints;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* package_name:aop.advice
*
* @author:徐亞遠 Date:2020/2/21 14:09
* 項目名:springDemo01
* Description:TODO
* Version: 1.0
**/
@Component
//表示這是一個切面
@Aspect
public class AnnotationAdvice {
//定義切點表達式
@Pointcut("execution(* aop.service.impl.*ServiceImpl.*(..))")
public void pt() {
}
@Before("pt()")
public void begoreAdvice(JoinPoint joinPoint) {
//簽名
Signature signature = joinPoint.getSignature();
//方法名
String methodName = signature.getName();
//轉換爲方法簽名,本質上是方法簽名
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
//參數
Object[] args = joinPoint.getArgs();
//目標類
Object target = joinPoint.getThis();
System.out.println("前置通知: " + "methodName: " + methodName + " " + "args:" + Arrays.toString(args) + " " +
"target: " + target);
}
@AfterReturning(value = "pt()", returning = "value")
public void afterAdvice(JoinPoint joinPoint, Object value) {
//簽名
Signature signature = joinPoint.getSignature();
//方法名
String methodName = signature.getName();
//轉換爲方法簽名,本質上是方法簽名
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
//參數
Object[] args = joinPoint.getArgs();
//目標類
Object target = joinPoint.getThis();
System.out.println("後置通知: " + "methodName: " + methodName + " " + "args:" + Arrays.toString(args) + " " +
"target: " +
"" + target);
}
@AfterThrowing(value = "pt()", throwing = "e")
public void throwAdvice(JoinPoint joinPoint, Exception e) {
//簽名
Signature signature = joinPoint.getSignature();
//方法名
String methodName = signature.getName();
//轉換爲方法簽名,本質上是方法簽名
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
//參數
Object[] args = joinPoint.getArgs();
//目標類
Object target = joinPoint.getThis();
System.out.println("異常通知: " + "methodName: " + methodName + " " + "args:" + Arrays.toString(args) + " " +
"target: " +
"" + target);
}
@Around("pt()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint){
Long startTime = System.currentTimeMillis();
Object proceed = null;
try {
System.out.println("開啓事務:");
proceed = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("回滾事務:");
return "出錯";
}
Long endTime = System.currentTimeMillis();
System.out.println("方法執行時間:"+(endTime-startTime)+"ms");
return proceed;
}
}
書寫登錄的接口:
package aop.service;
/**
* package_name:com.anno.service
*
* @author:徐亞遠 Date:2020/2/20 17:19
* 項目名:springDemo01
* Description:TODO
* Version: 1.0
**/
public interface UserService {
/**
* @Author : 徐亞遠
* @Date : 2020/2/21 14:08
* @Description :
* @param password
* @param username
*/
void login(String username, String password);
}
實現登錄接口。
package aop.service.impl;
import aop.service.UserService;
import org.springframework.stereotype.Service;
/**
* package_name:com.anno.service.impl
*
* @author:徐亞遠 Date:2020/2/20 17:19
* 項目名:springDemo01
* Description:TODO
* Version: 1.0
**/
@Service()
public class UserServiceImpl implements UserService {
/**
* @param username
* @param password
* @Author : 徐亞遠
* @Date : 2020/2/21 14:08
* @Description :
*/
@Override
public void login(String username, String password) {
int i = 5/0;
System.out.println("loginUserServiceImpl登錄方法執行:" + username + " " + password);
}
}
配置配置文件(spring.xmll)
<?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:contxt="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">
<!--掃包把使用註解的實體類配置到IOC容器中,交給容器管理-->
<context:component-scan base-package="aop.service.impl"/>
<context:component-scan base-package="aop.advice"/>
<!--自動創建代理並織入切面
proxy-target-class=""默認false 表示使用jdk動態代理,true表示使用cglib動態代理 使用cglib要加入依賴-->
<contxt:aspectj-autoproxy/>
</beans>
書寫測試類:
package aop;
import aop.service.UserService;
import aop.service.impl.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* package_name:aop
*
* @author:徐亞遠 Date:2020/2/21 14:16
* 項目名:springDemo01
* Description:TODO
* Version: 1.0
**/
public class controller {
public static void main(String [] args){
ApplicationContext ac = new ClassPathXmlApplicationContext("aop/spring.xml");
UserService userService = (UserService) ac.getBean("userServiceImpl");
userService.login("12","123" );
System.out.println(userService.getClass());
}
}
測試結果如圖: