摘要:接下來(lái)就可以把這個(gè)切點(diǎn)類加入到我們之前實(shí)現(xiàn)的功能中了。實(shí)現(xiàn)的切點(diǎn)功能首先改裝注解,把之前改成來(lái)存儲(chǔ)表達(dá)式。測(cè)試用例在上一篇文章從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架四實(shí)現(xiàn)中的測(cè)試用例的基礎(chǔ)上修改測(cè)試用例。
前言
在上一節(jié)從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(四)--實(shí)現(xiàn)AOP中我們實(shí)現(xiàn)了AOP的功能,已經(jīng)可以生成對(duì)應(yīng)的代理類了,但是對(duì)于代理對(duì)象的選擇只能通過(guò)指定的類,這樣確實(shí)不方便也不合理。這一節(jié)我們就利用aspectj來(lái)實(shí)現(xiàn)功能更強(qiáng)大的切點(diǎn)。
在spring初期的時(shí)候AOP功能使用起來(lái)也是很繁瑣麻煩的,到了后面整合了aspectj才有了現(xiàn)在這么方便的AOP功能,比如下面這樣的代碼,很簡(jiǎn)便并且直觀的定義了切點(diǎn)。
@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");} }
現(xiàn)在我們也來(lái)引入aspectj來(lái)實(shí)現(xiàn)AOP切點(diǎn)的功能
引入aspectj并實(shí)現(xiàn)aspectj的切點(diǎn)類首先在pom.xml中加入aspectj的依賴
... 1.8.13 ... org.aspectj aspectjweaver ${aspectj.version}
接下來(lái)就可以開(kāi)始實(shí)現(xiàn)一個(gè)利用aspectj來(lái)判定的切點(diǎn)類,這個(gè)類主要是用于判斷aspectj表達(dá)式是否匹配一個(gè)指定類或者指定方法。
在zbw.aop包下創(chuàng)建一個(gè)類,起名叫ProxyPointcut
package com.zbw.aop; import ... /** * 代理切點(diǎn)類 */ public class ProxyPointcut { /** * 切點(diǎn)解析器 */ private PointcutParser pointcutParser; /** * (AspectJ)表達(dá)式 */ private String expression; /** * 表達(dá)式解析器 */ private PointcutExpression pointcutExpression; /** * AspectJ語(yǔ)法集合 */ 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是否匹配切點(diǎn)表達(dá)式 */ public boolean matches(Class> targetClass) { checkReadyToMatch(); return pointcutExpression.couldMatchJoinPointsInType(targetClass); } /** * Method是否匹配切點(diǎn)表達(dá)式 */ public boolean matches(Method method) { checkReadyToMatch(); ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method); if (shadowMatch.alwaysMatches()) { return true; } else if (shadowMatch.neverMatches()) { return false; } return false; } /** * 初始化切點(diǎn)解析器 */ private void checkReadyToMatch() { if (null == pointcutExpression) { pointcutExpression = pointcutParser.parsePointcutExpression(expression); } } public void setExpression(String expression) { this.expression = expression; } public String getExpression() { return expression; }
這個(gè)類中有三個(gè)變量:pointcutParser,expression,pointcutExpression。
其中expression是String類型,用于存放我們要設(shè)定的aspectj表達(dá)式,比如execution(* com.zbw.*.service..*Impl.*(..))這樣的。
pointcutParser和pointcutExpression就是aspectj里面的類了,pointcutParser用于根據(jù)expression中的表達(dá)式創(chuàng)建pointcutExpression表達(dá)式解析器。而pointcutExpression可以用來(lái)判斷方法或者類是否匹配表達(dá)式。
這個(gè)類中最主要的兩個(gè)方法就matches(Class> targetClass)和matches(Method method),這兩個(gè)方法分別用于判定目標(biāo)的類和方法是否匹配expression中的aspectj表達(dá)式。
接下來(lái)就可以把ProxyPointcut這個(gè)切點(diǎn)類加入到我們之前實(shí)現(xiàn)的AOP功能中了。
實(shí)現(xiàn)AOP的切點(diǎn)功能首先改裝Aspect注解,把之前target()改成pointcut()來(lái)存儲(chǔ)aspectj表達(dá)式。
package com.zbw.aop.annotation; import ...; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Aspect { /** * 切點(diǎn)表達(dá)式 */ String pointcut() default ""; }
然后改裝ProxyAdvisor這個(gè)類,把切點(diǎn)表達(dá)式匹配器放入其中,并且使用匹配器來(lái)判定目標(biāo)類是否要被增強(qiáng)。
... public class ProxyAdvisor { ... /** * AspectJ表達(dá)式切點(diǎn)匹配器 */ private ProxyPointcut pointcut; /** * 執(zhí)行代理方法 */ 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()這個(gè)方法的最前面通過(guò)pointcut.matches()來(lái)判定目標(biāo)方法是否匹配這個(gè)表達(dá)式,如果匹配的話就往下執(zhí)行之前編寫(xiě)的各種通知,如果不匹配那么就直接執(zhí)行目標(biāo)方法。通過(guò)這種方式來(lái)使aspectj表達(dá)式控制目標(biāo)類的增強(qiáng)。
接下來(lái)改裝Aop類,由于改變了匹配目標(biāo)類的規(guī)則,所以要重寫(xiě)之前的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); } })); } /** * 通過(guò)Aspect切面類創(chuàng)建代理通知類 */ 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); } }
雖然重寫(xiě)了doAop()方法,但是實(shí)現(xiàn)原理依舊是相同的。只不過(guò)現(xiàn)在把創(chuàng)建ProxyAdvisor的過(guò)程分離出來(lái)多帶帶寫(xiě)了一個(gè)方法createProxyAdvisor(),
然后再遍歷Bean容器中的除了切面類的所有Bean,如果這個(gè)Bean匹配ProxyAdvisor中的切點(diǎn)表達(dá)式,那么就會(huì)生成對(duì)應(yīng)的代理類。
引入aspectj實(shí)現(xiàn)AOP切點(diǎn)完成了,又到測(cè)試用例來(lái)測(cè)試功能是否成功的時(shí)候了。
測(cè)試用例在上一篇文章從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(四)--實(shí)現(xiàn)AOP中的測(cè)試用例的基礎(chǔ)上修改測(cè)試用例。
先修改切面類DoodleAspect上的Aspect注解
package com.zbw.bean; import ... @Slf4j @Aspect(pointcut = "execution(* com.zbw.bean.DoodleController.helloForAspect(..))") public class DoodleAspect implements AroundAdvice { ... }
這個(gè)Aspect@pointcut()中的值會(huì)讓其只匹配DoodleController中的helloForAspect()方法。
接下來(lái)在DoodleController添加helloForAspect()方法
... public class DoodleController { ... public void helloForAspect() { log.info("Hello Aspectj"); } }
最后再重新編寫(xiě)AopTest的測(cè)試用例。
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(); } }
從結(jié)果的圖中可以看到在DoodleController的hello()前后沒(méi)有打印多余的日志,而在helloForAspect()方法的前面和后面都打印了DoodleAspect中的通知方法里的內(nèi)容,說(shuō)明我們的AOP已經(jīng)精準(zhǔn)的匹配到了想要的目標(biāo)。
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(一)--前言
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(二)--實(shí)現(xiàn)Bean容器
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(三)--實(shí)現(xiàn)IOC
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(四)--實(shí)現(xiàn)AOP
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(五)--引入aspectj實(shí)現(xiàn)AOP切點(diǎn)
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(六)--加強(qiáng)AOP功能
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(七)--實(shí)現(xiàn)MVC
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(八)--制作Starter
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(九)--優(yōu)化MVC代碼
源碼地址:doodle
原文地址:從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(五)--引入aspectj實(shí)現(xiàn)AOP切點(diǎn)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/71612.html
摘要:在前面的文章中實(shí)現(xiàn)的功能時(shí),目標(biāo)類都只能被一個(gè)切面代理,如果想要生成第二個(gè)代理類,就會(huì)把之前的代理類覆蓋。改裝原有功能現(xiàn)在要改裝原來(lái)的的實(shí)現(xiàn)代碼,讓的功能加入到框架中為了讓切面能夠排序,先添加一個(gè)注解,用于標(biāo)記排序。 前言 在前面從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(四)--實(shí)現(xiàn)AOP和從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(五)--引入aspectj實(shí)現(xiàn)AOP切點(diǎn)這兩節(jié)文章...
摘要:不過(guò)仔細(xì)了解了一段時(shí)候發(fā)現(xiàn),其實(shí)他的原理是很簡(jiǎn)單的,所以想要自己也動(dòng)手實(shí)現(xiàn)一個(gè)功能類似的框架。原文地址從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架 前言 最近在看spring-boot框架的源碼,看了源碼之后更是讓我感受到了spring-boot功能的強(qiáng)大。而且使用了很多的設(shè)計(jì)模式,讓人在看的時(shí)候覺(jué)得有點(diǎn)難以下手。 不過(guò)仔細(xì)了解了一段時(shí)候發(fā)現(xiàn),其實(shí)他的原理是很簡(jiǎn)單的,所以想要自己也動(dòng)手實(shí)現(xiàn)一個(gè)功能類似的...
摘要:服務(wù)器相關(guān)配置啟動(dòng)類資源目錄目錄靜態(tài)文件目錄端口號(hào)目錄目錄實(shí)現(xiàn)內(nèi)嵌服務(wù)器在上一章文章從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架七實(shí)現(xiàn)已經(jīng)在文件中引入了依賴,所以這里就不用引用了。 spring-boot的Starter 一個(gè)項(xiàng)目總是要有一個(gè)啟動(dòng)的地方,當(dāng)項(xiàng)目部署在tomcat中的時(shí)候,經(jīng)常就會(huì)用tomcat的startup.sh(startup.bat)的啟動(dòng)腳本來(lái)啟動(dòng)web項(xiàng)目 而在spring-b...
摘要:前言在從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架七實(shí)現(xiàn)中實(shí)現(xiàn)了框架的的功能,不過(guò)最后指出代碼的邏輯不是很好,在這一章節(jié)就將這一部分代碼進(jìn)行優(yōu)化。 前言 在從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(七)--實(shí)現(xiàn)MVC中實(shí)現(xiàn)了doodle框架的MVC的功能,不過(guò)最后指出代碼的邏輯不是很好,在這一章節(jié)就將這一部分代碼進(jìn)行優(yōu)化。 優(yōu)化的目標(biāo)是1.去除DispatcherServlet請(qǐng)求分發(fā)器中的http邏...
摘要:面向切面的本章主要內(nèi)容面向切面編程的基本原理通過(guò)創(chuàng)建切面使用注解為切面注入依賴。什么是面向切面編程切面能夠幫我們模塊化橫切關(guān)注點(diǎn)。在使用面向切面編程時(shí),我們?nèi)匀辉谝粋€(gè)地方定義通知功能,而無(wú)需修改受影響的類。切面切面是通知和切點(diǎn)的結(jié)合。 面向切面的Spring 本章主要內(nèi)容: 面向切面編程的基本原理 通過(guò)POJO創(chuàng)建切面 使用@Aspect注解 為AspectJ切面注入依賴。 說(shuō)明 ...
閱讀 3086·2023-04-26 00:53
閱讀 3536·2021-11-19 09:58
閱讀 1700·2021-09-29 09:35
閱讀 3290·2021-09-28 09:46
閱讀 3869·2021-09-22 15:38
閱讀 2697·2019-08-30 15:55
閱讀 3016·2019-08-23 14:10
閱讀 3830·2019-08-22 18:17