一.手寫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