摘要:接下來就可以把這個切點類加入到我們之前實現的功能中了。實現的切點功能首先改裝注解,把之前改成來存儲表達式。測試用例在上一篇文章從零開始實現一個簡易的框架四實現中的測試用例的基礎上修改測試用例。
前言
在上一節從零開始實現一個簡易的Java MVC框架(四)--實現AOP中我們實現了AOP的功能,已經可以生成對應的代理類了,但是對于代理對象的選擇只能通過指定的類,這樣確實不方便也不合理。這一節我們就利用aspectj來實現功能更強大的切點。
在spring初期的時候AOP功能使用起來也是很繁瑣麻煩的,到了后面整合了aspectj才有了現在這么方便的AOP功能,比如下面這樣的代碼,很簡便并且直觀的定義了切點。
@Component @Aspect public class LogAspect { @Pointcut("execution(* com.zbw.*.service..*Impl.*(..)) && @annotation(Log)") public void logPointcut() { } @Before("logPointcut()") public void before() {System.out.println("Before");} }
現在我們也來引入aspectj來實現AOP切點的功能
引入aspectj并實現aspectj的切點類首先在pom.xml中加入aspectj的依賴
... 1.8.13 ... org.aspectj aspectjweaver ${aspectj.version}
接下來就可以開始實現一個利用aspectj來判定的切點類,這個類主要是用于判斷aspectj表達式是否匹配一個指定類或者指定方法。
在zbw.aop包下創建一個類,起名叫ProxyPointcut
package com.zbw.aop; import ... /** * 代理切點類 */ public class ProxyPointcut { /** * 切點解析器 */ private PointcutParser pointcutParser; /** * (AspectJ)表達式 */ private String expression; /** * 表達式解析器 */ private PointcutExpression pointcutExpression; /** * AspectJ語法集合 */ private static final SetDEFAULT_SUPPORTED_PRIMITIVES = new HashSet<>(); static { DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET); } public ProxyPointcut() { this(DEFAULT_SUPPORTED_PRIMITIVES); } public ProxyPointcut(Set supportedPrimitives) { pointcutParser = PointcutParser .getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(supportedPrimitives); } /** * Class是否匹配切點表達式 */ public boolean matches(Class> targetClass) { checkReadyToMatch(); return pointcutExpression.couldMatchJoinPointsInType(targetClass); } /** * Method是否匹配切點表達式 */ public boolean matches(Method method) { checkReadyToMatch(); ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method); if (shadowMatch.alwaysMatches()) { return true; } else if (shadowMatch.neverMatches()) { return false; } return false; } /** * 初始化切點解析器 */ private void checkReadyToMatch() { if (null == pointcutExpression) { pointcutExpression = pointcutParser.parsePointcutExpression(expression); } } public void setExpression(String expression) { this.expression = expression; } public String getExpression() { return expression; }
這個類中有三個變量:pointcutParser,expression,pointcutExpression。
其中expression是String類型,用于存放我們要設定的aspectj表達式,比如execution(* com.zbw.*.service..*Impl.*(..))這樣的。
pointcutParser和pointcutExpression就是aspectj里面的類了,pointcutParser用于根據expression中的表達式創建pointcutExpression表達式解析器。而pointcutExpression可以用來判斷方法或者類是否匹配表達式。
這個類中最主要的兩個方法就matches(Class> targetClass)和matches(Method method),這兩個方法分別用于判定目標的類和方法是否匹配expression中的aspectj表達式。
接下來就可以把ProxyPointcut這個切點類加入到我們之前實現的AOP功能中了。
實現AOP的切點功能首先改裝Aspect注解,把之前target()改成pointcut()來存儲aspectj表達式。
package com.zbw.aop.annotation; import ...; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Aspect { /** * 切點表達式 */ String pointcut() default ""; }
然后改裝ProxyAdvisor這個類,把切點表達式匹配器放入其中,并且使用匹配器來判定目標類是否要被增強。
... public class ProxyAdvisor { ... /** * AspectJ表達式切點匹配器 */ private ProxyPointcut pointcut; /** * 執行代理方法 */ public Object doProxy(Object target, Class> targetClass, Method method, Object[] args, MethodProxy proxy) throws Throwable { if (!pointcut.matches(method)) { return proxy.invokeSuper(target, args); } ... } }
在doProxy()這個方法的最前面通過pointcut.matches()來判定目標方法是否匹配這個表達式,如果匹配的話就往下執行之前編寫的各種通知,如果不匹配那么就直接執行目標方法。通過這種方式來使aspectj表達式控制目標類的增強。
接下來改裝Aop類,由于改變了匹配目標類的規則,所以要重寫之前的doAop()方法。
... public class Aop { ... public void doAop() { beanContainer.getClassesBySuper(Advice.class) .stream() .filter(clz -> clz.isAnnotationPresent(Aspect.class)) .map(this::createProxyAdvisor) .forEach(proxyAdvisor -> beanContainer.getClasses() .stream() .filter(target -> !Advice.class.isAssignableFrom(target)) .filter(target -> !target.isAnnotationPresent(Aspect.class)) .forEach(target -> { if (proxyAdvisor.getPointcut().matches(target)) { Object proxyBean = ProxyCreator.createProxy(target, proxyAdvisor); beanContainer.addBean(target, proxyBean); } })); } /** * 通過Aspect切面類創建代理通知類 */ private ProxyAdvisor createProxyAdvisor(Class> aspectClass) { 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); } }
雖然重寫了doAop()方法,但是實現原理依舊是相同的。只不過現在把創建ProxyAdvisor的過程分離出來多帶帶寫了一個方法createProxyAdvisor(),
然后再遍歷Bean容器中的除了切面類的所有Bean,如果這個Bean匹配ProxyAdvisor中的切點表達式,那么就會生成對應的代理類。
引入aspectj實現AOP切點完成了,又到測試用例來測試功能是否成功的時候了。
測試用例在上一篇文章從零開始實現一個簡易的Java MVC框架(四)--實現AOP中的測試用例的基礎上修改測試用例。
先修改切面類DoodleAspect上的Aspect注解
package com.zbw.bean; import ... @Slf4j @Aspect(pointcut = "execution(* com.zbw.bean.DoodleController.helloForAspect(..))") public class DoodleAspect implements AroundAdvice { ... }
這個Aspect@pointcut()中的值會讓其只匹配DoodleController中的helloForAspect()方法。
接下來在DoodleController添加helloForAspect()方法
... public class DoodleController { ... public void helloForAspect() { log.info("Hello Aspectj"); } }
最后再重新編寫AopTest的測試用例。
package com.zbw.aop; import ... @Slf4j public class AopTest { @Test public 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(); controller.helloForAspect(); } }
從結果的圖中可以看到在DoodleController的hello()前后沒有打印多余的日志,而在helloForAspect()方法的前面和后面都打印了DoodleAspect中的通知方法里的內容,說明我們的AOP已經精準的匹配到了想要的目標。
從零開始實現一個簡易的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框架(五)--引入aspectj實現AOP切點
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71612.html
摘要:在前面的文章中實現的功能時,目標類都只能被一個切面代理,如果想要生成第二個代理類,就會把之前的代理類覆蓋。改裝原有功能現在要改裝原來的的實現代碼,讓的功能加入到框架中為了讓切面能夠排序,先添加一個注解,用于標記排序。 前言 在前面從零開始實現一個簡易的Java MVC框架(四)--實現AOP和從零開始實現一個簡易的Java MVC框架(五)--引入aspectj實現AOP切點這兩節文章...
摘要:不過仔細了解了一段時候發現,其實他的原理是很簡單的,所以想要自己也動手實現一個功能類似的框架。原文地址從零開始實現一個簡易的框架 前言 最近在看spring-boot框架的源碼,看了源碼之后更是讓我感受到了spring-boot功能的強大。而且使用了很多的設計模式,讓人在看的時候覺得有點難以下手。 不過仔細了解了一段時候發現,其實他的原理是很簡單的,所以想要自己也動手實現一個功能類似的...
摘要:服務器相關配置啟動類資源目錄目錄靜態文件目錄端口號目錄目錄實現內嵌服務器在上一章文章從零開始實現一個簡易的框架七實現已經在文件中引入了依賴,所以這里就不用引用了。 spring-boot的Starter 一個項目總是要有一個啟動的地方,當項目部署在tomcat中的時候,經常就會用tomcat的startup.sh(startup.bat)的啟動腳本來啟動web項目 而在spring-b...
摘要:前言在從零開始實現一個簡易的框架七實現中實現了框架的的功能,不過最后指出代碼的邏輯不是很好,在這一章節就將這一部分代碼進行優化。 前言 在從零開始實現一個簡易的Java MVC框架(七)--實現MVC中實現了doodle框架的MVC的功能,不過最后指出代碼的邏輯不是很好,在這一章節就將這一部分代碼進行優化。 優化的目標是1.去除DispatcherServlet請求分發器中的http邏...
摘要:面向切面的本章主要內容面向切面編程的基本原理通過創建切面使用注解為切面注入依賴。什么是面向切面編程切面能夠幫我們模塊化橫切關注點。在使用面向切面編程時,我們仍然在一個地方定義通知功能,而無需修改受影響的類。切面切面是通知和切點的結合。 面向切面的Spring 本章主要內容: 面向切面編程的基本原理 通過POJO創建切面 使用@Aspect注解 為AspectJ切面注入依賴。 說明 ...
閱讀 3084·2023-04-26 00:53
閱讀 3534·2021-11-19 09:58
閱讀 1697·2021-09-29 09:35
閱讀 3286·2021-09-28 09:46
閱讀 3866·2021-09-22 15:38
閱讀 2696·2019-08-30 15:55
閱讀 3015·2019-08-23 14:10
閱讀 3828·2019-08-22 18:17