之前實現的自己手寫一個AOP動態代理框架(1)只能根據控制器,業務層,DAO層等等註解的形式來進行切面,這裏仿Spring,支持AspectJ的表達式進行定位類方法資源,然後進行代理。
集成AspectJ的語法樹,即複用AspecJ的對資源的定位功能。也就是說可以使用 execution(* com.fuyouj.service..*.*(..))
這樣的表達式。
AspectJ簡介
提供了完整的AOP實現。
定義了切面語法和切面語法的解析。
Spring支持方法級別的織入(滿足80%的需求了),AspectJ支持全方位多角度的切面織入。AspectJ學習成本高,所以Spring就沒有完整支持所有織入。
導入AspectJ的包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
AspectJ的織入時機
- 編譯時織入:利用ajc編譯器將源文件編譯成class文件,並且將切面邏輯織入到java文件裏面。
- 編譯後織入::利用ajc編譯器修改class文件。
- 類加載時織入:在類加載器加載對象的時候,將切面織入到代碼裏。JVM裏面最終只有一個類,不同於Spring的方式,JVM裏面其實時多出來了一個代理類。
從以上3點來說,理論上AspectJ的效率比Spring要高,畢竟在使用前已經準備好了。而Spring需要新生成一個代理去替換。
實際上我們的使用必須依賴於Spring的容器,容器沒有初始化成功,我們就無法使用。但是容器初始化完成,必然AOP也已經完成,我們誰又哪能感受得到這個差距呢?
AOP的實現
改造Aspect註解
//作用在類上
@Target(ElementType.TYPE)
//運行時有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
//刪除
// Class<? extends Annotation> value() default DefaultAspect.class;
//新增
String pointCut();
}
一個提供解析表示式能力的類
/**
* 解析Aspect表達式 並且定位被織入的目標
*/
public class PointCutLocator {
/**
*AspectJ的 point 解析器
*/
//支持AspectJ的所有表達式
private PointcutParser pointcutParser = PointcutParser
.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(
PointcutParser.getAllSupportedPointcutPrimitives()
);
/**
* AspectJ的表達式解析器
*/
private PointcutExpression pointcutExpression;
/**
* 初始化解析表達式
* @param expression
*/
public PointCutLocator(String expression){
pointcutExpression = pointcutParser.parsePointcutExpression(expression);
}
/**粗篩
* @param targetClass
* @return 是否匹配
*/
public boolean roughMatches(Class<?> targetClass){
//只能校驗 withIn表達式 對於不能校驗的 execution表達式,默認返回true 所以這裏是粗篩
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
/**精確篩選
*判斷傳入的 Method對象是否是Aspect的目標代理方法,即精確匹配PointCut
* @param method
* @return
*/
public boolean accurateMatches(Method method){
ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
return shadowMatch.alwaysMatches();
}
}
改寫AOP
先說說思路。
1.獲取容器裏面所有的切面類
2.拼接好切面描述類集合
3.遍歷容器的類,爲符合被代理條件的類創建初篩後的集合
4.對每個類嘗試織入,織入的時候精確篩選。
改造AspectInfo,支持複用AspectJ的解析能力。
@Data
public class AspectInfo {
private int orderIndex;
private DefaultAspect aspectObject;
//複用AspectJ
private PointCutLocator pointCutLocator;
public AspectInfo() {
}
public AspectInfo(int orderIndex, DefaultAspect aspectObject) {
this.orderIndex = orderIndex;
this.aspectObject = aspectObject;
}
public AspectInfo(int orderIndex, DefaultAspect aspectObject, PointCutLocator pointCutLocator) {
this.orderIndex = orderIndex;
this.aspectObject = aspectObject;
this.pointCutLocator = pointCutLocator;
}
}
public class AspectWeaver {
private BeanContainer beanContainer;
public AspectWeaver(){
beanContainer = BeanContainer.getInstance();
}
public void doAopByAspectJ(){
//1.獲取所有的切面類
Set<Class<?>> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class);
//非空判斷
if (ValidationUtil.isEmpty(aspectSet)){return;}
//初始化切面描述類集合
List<AspectInfo> aspectInfoList = packAspectInfoList(aspectSet);
//遍歷容器裏面所有的類
Set<Class<?>> classes = beanContainer.getClasses();
//初篩 判斷這些切面類的是否是爲這個類服務的 留下每個類粗篩後的結果
List<AspectInfo> roughMatchAspectList = collectRoughMatchedAspectListForSpecificClass(aspectInfoList,targetClass);
//對每個類粗篩後的 嘗試織入
wrapIfNecessary(roughMatchAspectList,targetClass);
然後補充上出現的方法。
/**拼接切面描述類List
* @param aspectSet
* @return
*/
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和 @Order 和繼承自DefaultAspect,且符合規範");
}
}
return aspectInfoList;
}
/**
* 初篩
* @param aspectInfoList
* @param targetClass
* @return 返回初篩後的列表
*/
private List<AspectInfo> collectRoughMatchedAspectListForSpecificClass(List<AspectInfo> aspectInfoList, Class<?> targetClass) {
List<AspectInfo> roughMatchAspectList = new ArrayList<>();
if (ValidationUtil.isEmpty(aspectInfoList)){return roughMatchAspectList;}
for (AspectInfo aspectInfo : aspectInfoList) {
//初篩
boolean matches = aspectInfo.getPointCutLocator().roughMatches(targetClass);
//如果匹配 就加入
if (matches){
roughMatchAspectList.add(aspectInfo);
}
}
return roughMatchAspectList;
}
/**
* 嘗試織入邏輯
* @param roughMatchAspectList
* @param targetClass
*/
private void wrapIfNecessary(List<AspectInfo> roughMatchAspectList, Class<?> targetClass) {
if (ValidationUtil.isEmpty(roughMatchAspectList)){
return;
}
//創建動態代理對象
AspectListExecutor executor = new AspectListExecutor(targetClass,roughMatchAspectList);
Object proxyBean = ProxyCreator.createProxy(targetClass, executor);
//替換被代理的對象
beanContainer.addBean(targetClass,proxyBean);
}
精確篩選的邏輯放在AspectListExecutor裏面
/**
* 網被代理的方法添加橫切邏輯
*/
@Data
@NoArgsConstructor
public class AspectListExecutor implements MethodInterceptor {
//被代理的class
private Class<?> targetClass;
//切面信息集合 照顧多個AOP的 情況
private List<AspectInfo> sortedAspectInfoList;
public AspectListExecutor(Class<?> targetClass,List<AspectInfo> aspectInfos){
this.targetClass = targetClass;
this.sortedAspectInfoList = sort(aspectInfos);
}
private List<AspectInfo> sort(List<AspectInfo> aspectInfos) {
//升序排列
Collections.sort(aspectInfos, new Comparator<AspectInfo>() {
@Override
public int compare(AspectInfo o1, AspectInfo o2) {
return o1.getOrderIndex() - o2.getOrderIndex();
}
});
return aspectInfos;
}
/**
*
* @param o 被增強的對象
* @param method 需要攔截的方法
* @param args 方法參數
* @param methodProxy 代理方法
* @return 返回值
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 2.0 代碼 精確篩選
collectAccurateMatchAspectList(method);
Object returnValue = null;
if (ValidationUtil.isEmpty(sortedAspectInfoList)){
//就算爲空也要執行原來的方法啊
return methodProxy.invokeSuper(o,args);
}
//1.按照order的順序執行完畢所有切面的before方法
invokeBeforeAdvices(method,args);
try {
//2。 執行被代理的方法
returnValue = methodProxy.invokeSuper(o,args);
//3. 如果被代理方法正常返回,按照order的順序 逆序執行afterReturning
returnValue = invokeAfterReturningAdvices(method,args,returnValue);
}catch (Exception e){
//執行異常時
invokeAfterThrowingAdvices(method,args,e);
}
return returnValue;
}
/**
* 精確匹配 留下符合精確匹配的結果
* @param method
*/
private void collectAccurateMatchAspectList(Method method) {
if(ValidationUtil.isEmpty(sortedAspectInfoList)){return;}
Iterator<AspectInfo> iterator = sortedAspectInfoList.iterator();
while (iterator.hasNext()){
AspectInfo aspectInfo = iterator.next();
//區別兩種方式
//如果註解不爲空 校驗註解
//精確校驗
if (aspectInfo.getPointCutLocator().accurateMatches(method) == false){
iterator.remove();
}
}
}
/**
* 發生異常的時候執行 降序
* @param method
* @param args
* @param e
*/
private void invokeAfterThrowingAdvices(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);
}
}
/**
* 如果代理方法正常返回,降序執行afterAdvice
* @param method
* @param args
* @param returnValue
* @return
*/
private Object invokeAfterReturningAdvices(Method method, Object[] args, Object returnValue) throws Throwable {
Object res = null;
for (int i = sortedAspectInfoList.size()-1; i >=0 ; i--) {
res = sortedAspectInfoList.get(i).getAspectObject().afterReturning(targetClass, method, args, returnValue);
}
return res;
}
/**
* 按照oder的 順序升序執行
* @param method
* @param args
*/
private void invokeBeforeAdvices(Method method, Object[] args) throws Throwable {
for (AspectInfo info : sortedAspectInfoList) {
info.getAspectObject().before(targetClass,method,args);
}
}
}
改寫原來的測試類
package com.fuyouj.aspect.test;
import com.framework.aop.annotation.Aspect;
import com.framework.aop.annotation.Order;
import com.framework.aop.aspect.DefaultAspect;
import com.framework.core.annotation.Controller;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
/**
* @Desc
* @Author FuYouJ
* @date 2020/6/2 0:40
*/
@Aspect(pointCut = "execution(* com.fuyouj.controller..*.*(..))")
@Order(0)
@Slf4j
public class TestAopOne extends DefaultAspect {
@Override
public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable {
log.info("我是切面邏輯before order0,我增強的目標類是{},我增強的目標方法是{},方法參數{}"
,targetClass.getSimpleName(),method.getName(),args);
}
@Override
public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable {
Integer res = (Integer) returnValue;
log.info("我是切面邏輯after order0,我增強的目標類是{},我增強的目標方法是{},方法參數{}"
,targetClass.getSimpleName(),method.getName(),args);
res++;
return res;
}
}
package com.fuyouj.aspect.test;
import com.framework.aop.annotation.Aspect;
import com.framework.aop.annotation.Order;
import com.framework.aop.aspect.DefaultAspect;
import com.framework.core.annotation.Controller;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
/**
* @Desc
* @Author FuYouJ
* @date 2020/6/2 0:40
*/
@Aspect(pointCut = "execution(* com.fuyouj.controller..*.*(..))")
@Order(1)
@Slf4j
public class TestAopTwo extends DefaultAspect {
@Override
public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable {
log.info("我是切面邏輯before order1,我增強的目標類是{},我增強的目標方法是,方法參數{}"
,targetClass.getSimpleName(),method.getName(),args);
}
@Override
public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable {
Integer res = (Integer) returnValue;
log.info("我是切面邏輯after order1,我增強的目標類是{},我增強的目標方法是,方法參數{}"
,targetClass.getSimpleName(),method.getName(),args);
res++;
return res;
}
}
測試用例
public class TestAop {
@DisplayName("測試第二個版本的AOP")
@Test
public void testAopOne(){
BeanContainer beanContainer = BeanContainer.getInstance();
//加載了所有類
beanContainer.loadBeans("com.fuyouj");
//AOP
new AspectWeaver().doAopByAspectJ();
// ioc
new DependencyInjector().doIoc();
TestAopController controller = (TestAopController) beanContainer.getBean(TestAopController.class);
int say = controller.say();
System.out.println("======================================返回結果======="+say+"======");
}
}
再改進
可以將DefaultAspect這個抽象類改造成接口,因爲java只能繼承一個類,爲了減輕實現的壓力。使用 Default關鍵字。代碼是一樣可以運行的。
package com.framework.aop.aspect;
/**
* @Desc
* @Author FuYouJ
* @date 2020/5/31 21:24
*/
import java.lang.reflect.Method;
/**
* 定義切面的通用骨架
* 通過鉤子方法實現
*/
public interface DefaultAspect {
/**
* @param targetClass 被代理的目標類型
* @param method 被代理的方法
* @param args 方法的參數
* @throws Throwable
*/
default void before(Class<?> targetClass, Method method,Object[] args) throws Throwable{
} ;
/**
*
* @param targetClass 被代理的目標類型
* @param method 被代理的方法
* @param args 方法的參數
* @param returnValue 返回值
* @return
* @throws Throwable
*/
default Object afterReturning(Class<?> targetClass,Method method
,Object[] args,Object returnValue) throws Throwable{
return returnValue;
}
/**
*
* @param targetClass 被代理的目標類型
* @param method 被代理的方法
* @param args 方法的參數
* @param e 異常
* @throws Throwable
*/
default void afterThrowing(Class<?> targetClass,
Method method,Object[] args,Throwable e) throws Throwable{
}
}