日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

从零开始实现一个简易的Java MVC框架(六)--加强AOP功能

發布時間:2025/5/22 java 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从零开始实现一个简易的Java MVC框架(六)--加强AOP功能 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

在前面從零開始實現一個簡易的Java MVC框架(四)--實現AOP和從零開始實現一個簡易的Java MVC框架(五)--引入aspectj實現AOP切點這兩節文章中已經實現了AOP功能并且引用aspectj表達式實現切點的功能,這篇文章繼續完善doodle框架的AOP功能。

在前面的文章中實現的AOP功能時,目標類都只能被一個切面代理,如果想要生成第二個代理類,就會把之前的代理類覆蓋。這篇文章就要來實現多個代理的功能,也就是實現代理鏈。

實現代理鏈

在com.zbw.aop包下創建一個類起名為AdviceChain

package com.zbw.aop;import .../*** 通知鏈*/ public class AdviceChain {/*** 目標類*/@Getterprivate final Class<?> targetClass;/*** 目標實例*/@Getterprivate final Object target;/*** 目標方法*/@Getterprivate final Method method;/*** 目標方法參數*/@Getterprivate final Object[] args;/*** 代理方法*/private final MethodProxy methodProxy;/*** 代理通知列*/private List<ProxyAdvisor> proxyList;/*** 代理通知列index*/private int adviceIndex = 0;public AdviceChain(Class<?> targetClass, Object target, Method method, Object[] args, MethodProxy methodProxy, List<ProxyAdvisor> proxyList) {this.targetClass = targetClass;this.target = target;this.method = method;this.args = args;this.methodProxy = methodProxy;this.proxyList = proxyList;}/*** 遞歸執行 執行代理通知列*/public Object doAdviceChain() throws Throwable {...} } 復制代碼

由于要實現多個通知類鏈式執行的功能,這個類就是代替之前的ProxyAdvisor來生產代理類,并且通過doAdviceChain()方法執行具體的切面方法以及目標代理類的方法。

在最初設計這個方法的時候,我想的是直接for循環proxyList這個屬性里的ProxyAdvisor,然后一個個執行對應的Advice方法不就行了,后來發現這是不行的。因為在AOP的功能設計里,多個切面的執行順序是一種'先入后出'的順序。比如說有兩個切面Aspect1和Aspect2,那么他們的執行順序應該是Aspect1@before()->Aspect2@before()->targetClass@method()->Aspect2@after()->Aspect1@after(),先執行的Aspect1@before()方法要在最后執行Aspect1@after()。

要實現'先入后出'的功能通常有兩種實現方式,一是借助棧這個數據結構,二是用遞歸的方式,這里我們用遞歸的方式實現。

在實現doAdviceChain()的功能之前,先修改之前的ProxyAdvisor類。

...public class ProxyAdvisor {.../*** 執行順序*/private int order;/*** 執行代理方法*/public Object doProxy(AdviceChain adviceChain) throws Throwable {Object result = null;Class<?> targetClass = adviceChain.getTargetClass();Method method = adviceChain.getMethod();Object[] args = adviceChain.getArgs();if (advice instanceof MethodBeforeAdvice) {((MethodBeforeAdvice) advice).before(targetClass, method, args);}try {result = adviceChain.doAdviceChain(); //執行代理鏈方法if (advice instanceof AfterReturningAdvice) {((AfterReturningAdvice) advice).afterReturning(targetClass, result, method, args);}} catch (Exception e) {if (advice instanceof ThrowsAdvice) {((ThrowsAdvice) advice).afterThrowing(targetClass, method, args, e);} else {throw new Throwable(e);}}return result;} } 復制代碼

在ProxyAdvisor類中添加一個屬性order,這是用于存儲這個切面類的執行順序的。然后再修改doProxy()方法,把傳入參數由原來的很多類相關的信息改為傳入AdviceChain,因為我們把類信息都放在了AdviceChain中了。然后把原來在doProxy()方法開頭的if (!pointcut.matches(method))這個切點判斷移除,這個判斷將會改在AdviceChain中。然后在原來要調用proxy.invokeSuper(target, args);的地方改為調用adviceChain.doAdviceChain();,這樣就能形成一個遞歸調用。

現在來具體實現AdviceChain的doAdviceChain()方法。

...public Object doAdviceChain() throws Throwable {Object result;while (adviceIndex < proxyList.size()&& !proxyList.get(adviceIndex).getPointcut().matches(method)) {//如果當前方法不匹配切點,則略過該代理通知類adviceIndex++;}if (adviceIndex < proxyList.size()) {result = proxyList.get(adviceIndex++).doProxy(this);} else {result = methodProxy.invokeSuper(target, args);}return result; } 復制代碼

在這個方法中,先是通過一個while循環判定proxyList的當前ProxyAdvisor是否匹配切點表達式,如果不匹配日則跳過這個ProxyAdvisor且adviceIndex這個計數器加一,假如匹配的話,就執行ProxyAdvisor的doProxy()方法,并且把自己當作參數傳入過去。直到adviceIndex計數器的大小大于等于proxyList的大小,則調用目標類的方法。

這樣就形成一個遞歸的形式來實現代理鏈。

改裝原有AOP功能

現在要改裝原來的AOP的實現代碼,讓AdviceChain的功能加入到框架中

為了讓切面能夠排序,先添加一個Order注解,用于標記排序。在zbw.aop包下創建Order注解類

package com.zbw.aop.annotation;import .../*** aop順序*/ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Order {/*** aop順序,值越大越先執行*/int value() default 0; } 復制代碼

然后再改裝AOP執行器,先修改createProxyAdvisor()方法,把Order注解的值存入到ProxyAdvisor中。

// Aop.java .../*** 通過Aspect切面類創建代理通知類*/ private ProxyAdvisor createProxyAdvisor(Class<?> aspectClass) {int order = 0;if (aspectClass.isAnnotationPresent(Order.class)) {order = aspectClass.getAnnotation(Order.class).value();}String expression = aspectClass.getAnnotation(Aspect.class).pointcut();ProxyPointcut proxyPointcut = new ProxyPointcut();proxyPointcut.setExpression(expression);Advice advice = (Advice) beanContainer.getBean(aspectClass);return new ProxyAdvisor(advice, proxyPointcut, order); } 復制代碼

然后再增加一個createMatchProxies()方法,由于之前生成代理類都是用一個ProxyAdvisor就可以了,而現在是一個List,所以現在要用該方法用于生成一個List,其中存放的是匹配目標類的切面集合。傳入的參數proxyList為所有的ProxyAdvisor集合,返回的參數為目標類匹配的代理通知集合,并且這個集合是根據order排序的。

// Aop.java .../*** 獲取目標類匹配的代理通知列表*/ private List<ProxyAdvisor> createMatchProxies(List<ProxyAdvisor> proxyList, Class<?> targetClass) {Object targetBean = beanContainer.getBean(targetClass);return proxyList.stream().filter(advisor -> advisor.getPointcut().matches(targetBean.getClass())).sorted(Comparator.comparingInt(ProxyAdvisor::getOrder)).collect(Collectors.toList()); } 復制代碼

最后再修改doAop()方法。

// Aop.java .../*** 執行Aop*/ public void doAop() {//創建所有的代理通知列表List<ProxyAdvisor> proxyList = beanContainer.getClassesBySuper(Advice.class).stream().filter(clz -> clz.isAnnotationPresent(Aspect.class)).map(this::createProxyAdvisor).collect(Collectors.toList());//創建代理類并注入到Bean容器中beanContainer.getClasses().stream().filter(clz -> !Advice.class.isAssignableFrom(clz)).filter(clz -> !clz.isAnnotationPresent(Aspect.class)).forEach(clz -> {List<ProxyAdvisor> matchProxies = createMatchProxies(proxyList, clz);if (matchProxies.size() > 0) {Object proxyBean = ProxyCreator.createProxy(clz, matchProxies);beanContainer.addBean(clz, proxyBean);}}); } 復制代碼

同樣的,由于代理類從ProxyAdvisor改成AdviceChain,對應的代理類創造器也要做對應的修改。

package com.zbw.aop;import .../*** 代理類創建器*/ public final class ProxyCreator {/*** 創建代理類*/public static Object createProxy(Class<?> targetClass, List<ProxyAdvisor> proxyList) {return Enhancer.create(targetClass, new AdviceMethodInterceptor(targetClass, proxyList));}/*** cglib MethodInterceptor實現類*/private static class AdviceMethodInterceptor implements MethodInterceptor {/*** 目標類*/private final Class<?> targetClass;/*** 代理通知列表*/private List<ProxyAdvisor> proxyList;public AdviceMethodInterceptor(Class<?> targetClass, List<ProxyAdvisor> proxyList) {this.targetClass = targetClass;this.proxyList = proxyList;}@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {return new AdviceChain(targetClass, target, method, args, proxy, proxyList).doAdviceChain();}} } 復制代碼

代理鏈的功能又實現了,現在可以寫測試用例了。

測試用例

先實現兩個切面DoodleAspect和DoodleAspect2:

// DoodleAspect @Slf4j @Order(1) @Aspect(pointcut = "@within(com.zbw.core.annotation.Controller)") public class DoodleAspect implements AroundAdvice {@Overridepublic void before(Class<?> clz, Method method, Object[] args) throws Throwable {log.info("-----------before DoodleAspect-----------");log.info("class: {}, method: {}", clz.getName(), method.getName());}@Overridepublic void afterReturning(Class<?> clz, Object returnValue, Method method, Object[] args) throws Throwable {log.info("-----------after DoodleAspect-----------");log.info("class: {}, method: {}", clz, method.getName());}@Overridepublic void afterThrowing(Class<?> clz, Method method, Object[] args, Throwable e) {log.error("-----------error DoodleAspect-----------");log.error("class: {}, method: {}, exception: {}", clz, method.getName(), e.getMessage());} } 復制代碼// DoodleAspect2 @Slf4j @Order(2) @Aspect(pointcut = "@within(com.zbw.core.annotation.Controller)") public class DoodleAspect2 implements AroundAdvice {@Overridepublic void before(Class<?> clz, Method method, Object[] args) throws Throwable {log.info("-----------before DoodleAspect2-----------");log.info("class: {}, method: {}", clz.getName(), method.getName());}@Overridepublic void afterReturning(Class<?> clz, Object returnValue, Method method, Object[] args) throws Throwable {log.info("-----------after DoodleAspect2-----------");log.info("class: {}, method: {}", clz, method.getName());}@Overridepublic void afterThrowing(Class<?> clz, Method method, Object[] args, Throwable e) {log.error("-----------error DoodleAspect2-----------");log.error("class: {}, method: {}, exception: {}", clz, method.getName(), e.getMessage());} } 復制代碼

然后在AopTest測試類中調用DoodleController的hello()方法。

@Slf4j public class AopTest {@Testpublic void doAop() {BeanContainer beanContainer = BeanContainer.getInstance();beanContainer.loadBeans("com.zbw");new Aop().doAop();new Ioc().doIoc();DoodleController controller = (DoodleController) beanContainer.getBean(DoodleController.class);controller.hello();} } 復制代碼

在結果的圖中可以看出DoodleAspect和DoodleAspect2兩個代理方法都執行了,并且是按照預期的執行順序執行的。


  • 從零開始實現一個簡易的Java MVC框架(一)--前言
  • 從零開始實現一個簡易的Java MVC框架(二)--實現Bean容器
  • 從零開始實現一個簡易的Java MVC框架(三)--實現IOC
  • 從零開始實現一個簡易的Java MVC框架(四)--實現AOP
  • 從零開始實現一個簡易的Java MVC框架(五)--引入aspectj實現AOP切點
  • 從零開始實現一個簡易的Java MVC框架(六)--加強AOP功能
  • 從零開始實現一個簡易的Java MVC框架(七)--實現MVC
  • 從零開始實現一個簡易的Java MVC框架(八)--制作Starter
  • 從零開始實現一個簡易的Java MVC框架(九)--優化MVC代碼

源碼地址:doodle

原文地址:從零開始實現一個簡易的Java MVC框架(六)--加強AOP功能

總結

以上是生活随笔為你收集整理的从零开始实现一个简易的Java MVC框架(六)--加强AOP功能的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。