一.手寫Aop前基礎知識
1.aop是什麼?
面向切面程式設計(AOP):是一種程式設計正規化,提供從另一個角度來考慮程式結構從而完善物件導向程式設計(OOP)。
在進行OOP開發時,都是基於對元件(比如類)進行開發,然後對元件進行組合,OOP最大問題就是無法解耦元件進行開發,而AOP就是為了克服這個問題而出現的,它來進行這種耦合的分離。
AOP為開發者提供一種進行橫切關注點(比如日誌關注點橫切了支付關注點)分離並織入的機制,把橫切關注點分離,然後通過某種技術織入到系統中,從而無耦合的完成了我們的功能。
2.aop能幹什麼?
AOP主要用於橫切關注點和織入,因此需要理解橫切關注點和織入:
- 關注點:可以認為是所關注的任何東西,比如上邊的支付元件;
- 關注點分離:將問題細化從而單獨部分,即可以理解為不可再分割的元件,如上邊的日誌元件和支付元件;
- 橫切關注點:一個元件無法完成需要的功能,需要其他元件協作完成,如日誌元件橫切於支付元件;
- 織入:橫切關注點分離後,需要通過某種技術將橫切關注點融合到系統中從而完成需要的功能,因此需要織入,織入可能在編譯期、載入期、執行期等進行。
橫切關注點可能包含很多,比如非業務的:日誌、事務處理、快取、效能統計、許可權控制等等這些非業務的基礎功能;還可能是業務的:如某個業務元件橫切於多個模組。
面向切面方式,先將橫切關注點分離,再將橫切關注點織入到支付系統中:
3.aop的優點?
- 完善OOP;
- 降低元件和模組之間的耦合性;
- 使系統容易擴充套件;
- 而且由於關注點分離從而可以獲得元件的更好複用。
二.aop底層實現原理:代理模式
關於靜態代理模式,詳情請參閱我另一片部落格:
https://www.cnblogs.com/tc971121/p/13474638.html
三.手寫aop主要實現過程
1.定義配置標記
@Aspect:配置標記橫切物件(方法)的地址
@Order:配置標記橫切的順序
@Aspect/@Order
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
String pointcut();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
/**
* 控制類的執行順序,值越小優先順序越高
*/
int value();
}
2.定義預設切面類
package org.simplespring.aop.aspect;
import java.lang.reflect.Method;
/**
* 定義預設切面類
*/
public abstract class DefaultAspect {
/**
* 事前攔截
* @param targetClass 被代理的目標類
* @param method 被代理的目標方法
* @param args 被代理的目標方法對應的引數列表
* @throws Throwable
*/
public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable{
}
/**
* 事後攔截
* @param targetClass 被代理的目標類
* @param method 被代理的目標方法
* @param args 被代理的目標方法對應的引數列表
* @param returnValue 被代理的目標方法執行後的返回值
* @throws Throwable
*/
public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable{
return returnValue;
}
/**
*
* @param targetClass 被代理的目標類
* @param method 被代理的目標方法
* @param args 被代理的目標方法對應的引數列表
* @param e 被代理的目標方法丟擲的異常
* @throws Throwable
*/
public void afterThrowing(Class<?> targetClass, Method method, Object[] args, Throwable e) throws Throwable{
}
}
3.定義切面織入器
核心流程:
1.獲取所有的切面類
2.遍歷容器管理的類
3.篩選出匹配容器管理類的切面aspectlist
4.嘗試進行Aspect的織入 生成動態代理物件 並替換beancontainer中原先class對應所對應的例項物件
package org.simplespring.aop;
import org.simplespring.aop.annotation.Aspect;
import org.simplespring.aop.annotation.Order;
import org.simplespring.aop.aspect.AspectInfo;
import org.simplespring.aop.aspect.DefaultAspect;
import org.simplespring.core.BeanContainer;
import org.simplespring.util.ValidationUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @Classname AspectWeaver
* @Description 切面織入器
* @Date 2020/8/8 19:43
* @Created by zhangtianci
*/
public class AspectWeaver {
/**
* 擁有一個bean容器
*/
private BeanContainer beanContainer;
public AspectWeaver(){
beanContainer = BeanContainer.getInstance();
}
/**
* 1.獲取所有的切面類
* 2.遍歷容器裡的類
* 3.篩選出匹配類的切面aspect
* 4.嘗試進行Aspect的織入 生成動態代理物件
*/
public void doAop() {
//1.獲取所有的切面類
Set<Class<?>> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class);
if(ValidationUtil.isEmpty(aspectSet)){return;}
//2.拼裝AspectInfoList
List<AspectInfo> aspectInfoList = packAspectInfoList(aspectSet);
//3.遍歷容器裡的類
Set<Class<?>> classSet = beanContainer.getClasses();
for (Class<?> targetClass: classSet) {
//排除AspectClass自身
if(targetClass.isAnnotationPresent(Aspect.class)){
continue;
}
//4.粗篩符合條件的Aspect
List<AspectInfo> roughMatchedAspectList = collectRoughMatchedAspectListForSpecificClass(aspectInfoList, targetClass);
//5.嘗試進行Aspect的織入
wrapIfNecessary(roughMatchedAspectList,targetClass);
}
}
private void wrapIfNecessary(List<AspectInfo> roughMatchedAspectList, Class<?> targetClass) {
if(ValidationUtil.isEmpty(roughMatchedAspectList)){return;}
//建立動態代理物件
AspectListExecutor aspectListExecutor = new AspectListExecutor(targetClass, roughMatchedAspectList);
Object proxyBean = ProxyCreator.createProxy(targetClass, aspectListExecutor);
beanContainer.addBean(targetClass, proxyBean);
}
private List<AspectInfo> collectRoughMatchedAspectListForSpecificClass(List<AspectInfo> aspectInfoList, Class<?> targetClass) {
List<AspectInfo> roughMatchedAspectList = new ArrayList<>();
for(AspectInfo aspectInfo : aspectInfoList){
//粗篩
if(aspectInfo.getPointcutLocator().roughMatches(targetClass)){
roughMatchedAspectList.add(aspectInfo);
}
}
return roughMatchedAspectList;
}
private List<AspectInfo> packAspectInfoList(Set<Class<?>> aspectSet) {
List<AspectInfo> aspectInfoList = new ArrayList<>();
for(Class<?> aspectClass : aspectSet){
if (verifyAspect(aspectClass)){
Order orderTag = aspectClass.getAnnotation(Order.class);
Aspect aspectTag = aspectClass.getAnnotation(Aspect.class);
DefaultAspect defaultAspect = (DefaultAspect) beanContainer.getBean(aspectClass);
//初始化表示式定位器
PointcutLocator pointcutLocator = new PointcutLocator(aspectTag.pointcut());
AspectInfo aspectInfo = new AspectInfo(orderTag.value(), defaultAspect, pointcutLocator);
aspectInfoList.add(aspectInfo);
} else {
//不遵守規範則直接丟擲異常
throw new RuntimeException("@Aspect and @Order must be added to the Aspect class, and Aspect class must extend from DefaultAspect");
}
}
return aspectInfoList;
}
//框架中一定要遵守給Aspect類新增@Aspect和@Order標籤的規範,同時,必須繼承自DefaultAspect.class
//此外,@Aspect的屬性值不能是它本身
private boolean verifyAspect(Class<?> aspectClass) {
return aspectClass.isAnnotationPresent(Aspect.class) &&
aspectClass.isAnnotationPresent(Order.class) &&
DefaultAspect.class.isAssignableFrom(aspectClass);
}
}
切面資訊包裝類(增強的動作/增強物件地址/橫切順序)
package org.simplespring.aop.aspect;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.simplespring.aop.PointcutLocator;
@AllArgsConstructor
@Getter
public class AspectInfo {
private int orderIndex;
private DefaultAspect aspectObject;
private PointcutLocator pointcutLocator;
}
採用cglib動態的實現方式:
實現net.sf.cglib.proxy.MethodInterceptor介面,定義代理後方法的動作(相當於實現jdk動態代理中的InvocationHandler介面)
package org.simplespring.aop;
import lombok.Getter;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.simplespring.aop.aspect.AspectInfo;
import org.simplespring.util.ValidationUtil;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
public class AspectListExecutor implements MethodInterceptor {
//被代理的類
private Class<?> targetClass;
//排好序的Aspect列表
@Getter
private List<AspectInfo>sortedAspectInfoList;
public AspectListExecutor(Class<?> targetClass, List<AspectInfo> aspectInfoList){
this.targetClass = targetClass;
this.sortedAspectInfoList = sortAspectInfoList(aspectInfoList);
}
/**
* 按照order的值進行升序排序,確保order值小的aspect先被織入
*
* @param aspectInfoList
* @return
*/
private List<AspectInfo> sortAspectInfoList(List<AspectInfo> aspectInfoList) {
Collections.sort(aspectInfoList, new Comparator<AspectInfo>() {
@Override
public int compare(AspectInfo o1, AspectInfo o2) {
//按照值的大小進行升序排序
return o1.getOrderIndex() - o2.getOrderIndex();
}
});
return aspectInfoList;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object returnValue = null;
collectAccurateMatchedAspectList(method);
if(ValidationUtil.isEmpty(sortedAspectInfoList)){
returnValue = methodProxy.invokeSuper(proxy, args);
return returnValue;
}
//1.按照order的順序升序執行完所有Aspect的before方法
invokeBeforeAdvices(method, args);
try{
//2.執行被代理類的方法
returnValue = methodProxy.invokeSuper(proxy, args);
//3.如果被代理方法正常返回,則按照order的順序降序執行完所有Aspect的afterReturning方法
returnValue = invokeAfterReturningAdvices(method, args, returnValue);
} catch (Exception e){
//4.如果被代理方法丟擲異常,則按照order的順序降序執行完所有Aspect的afterThrowing方法
invokeAfterThrowingAdvides(method, args, e);
}
return returnValue;
}
private void collectAccurateMatchedAspectList(Method method) {
if(ValidationUtil.isEmpty(sortedAspectInfoList)){return;}
Iterator<AspectInfo> it = sortedAspectInfoList.iterator();
while (it.hasNext()){
AspectInfo aspectInfo = it.next();
if(!aspectInfo.getPointcutLocator().accurateMatches(method)){
it.remove();
}
}
}
//4.如果被代理方法丟擲異常,則按照order的順序降序執行完所有Aspect的afterThrowing方法
private void invokeAfterThrowingAdvides(Method method, Object[] args, Exception e) throws Throwable {
for (int i = sortedAspectInfoList.size() - 1; i >=0 ; i--){
sortedAspectInfoList.get(i).getAspectObject().afterThrowing(targetClass, method, args, e);
}
}
//3.如果被代理方法正常返回,則按照order的順序降序執行完所有Aspect的afterReturning方法
private Object invokeAfterReturningAdvices(Method method, Object[] args, Object returnValue) throws Throwable {
Object result = null;
for (int i = sortedAspectInfoList.size() - 1; i >=0 ; i--){
result = sortedAspectInfoList.get(i).getAspectObject().afterReturning(targetClass, method, args, returnValue);
}
return result;
}
//1.按照order的順序升序執行完所有Aspect的before方法
private void invokeBeforeAdvices(Method method, Object[] args) throws Throwable {
for(AspectInfo aspectInfo : sortedAspectInfoList){
aspectInfo.getAspectObject().before(targetClass, method, args);
}
}
}
建立代理類:
package org.simplespring.aop;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
public class ProxyCreator {
/**
* 建立動態代理物件並返回
* @param targetClass 被代理的Class物件
* @param methodInterceptor 方法攔截器
* @return
*/
public static Object createProxy(Class<?> targetClass, MethodInterceptor methodInterceptor){
return Enhancer.create(targetClass, methodInterceptor);
}
}
解析Aspect表示式並且定位被織入的目標工具類:
package org.simplespring.aop;
import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.ShadowMatch;
import java.lang.reflect.Method;
/**
* 解析Aspect表示式並且定位被織入的目標
*/
public class PointcutLocator {
/**
* Pointcut解析器,直接給它賦值上Aspectj的所有表示式,以便支援對眾多表示式的解析
*/
private PointcutParser pointcutParser= PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(
PointcutParser.getAllSupportedPointcutPrimitives()
);
/**
* 表示式解析器
*/
private PointcutExpression pointcutExpression;
public PointcutLocator(String expression){
this.pointcutExpression = pointcutParser.parsePointcutExpression(expression);
}
/**
* 判斷傳入的Class物件是否是Aspect的目標代理類,即匹配Pointcut表示式(初篩)
*
* @param targetClass 目標類
* @return 是否匹配
*/
public boolean roughMatches(Class<?> targetClass){
//couldMatchJoinPointsInType比較坑,只能校驗within
//不能校驗 (execution(精確到某個類除外), call, get, set),面對無法校驗的表示式,直接返回true
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
/**
* 判斷傳入的Method物件是否是Aspect的目標代理方法,即匹配Pointcut表示式(精篩)
* @param method
* @return
*/
public boolean accurateMatches(Method method){
ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
if(shadowMatch.alwaysMatches()){
return true;
} else{
return false;
}
}
}
總結實現aop的主要流程:
1.定義配置標記
2.定義預設切面類
3.定義切面織入器
核心流程:
1.獲取所有的切面類
2.遍歷容器管理的類
3.篩選出匹配容器管理類的切面aspectlist
4.嘗試進行Aspect的織入 生成動態代理物件 並替換beancontainer中原先class對應所對應的例項物件
4.其他類:切面資訊包裝類/InvocationHandler實現類/建立代理例項類/解析Aspect表示式工具類等。
參考部落格地址:https://www.iteye.com/blog/jinnianshilongnian-1418596