内容简介:在前面在前面的文章中实现的AOP功能时,目标类都只能被一个切面代理,如果想要生成第二个代理类,就会把之前的代理类覆盖。这篇文章就要来实现多个代理的功能,也就是实现代理链。在com.zbw.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 { /** * 目标类 */ @Getter private final Class<?> targetClass; /** * 目标实例 */ @Getter private final Object target; /** * 目标方法 */ @Getter private final Method method; /** * 目标方法参数 */ @Getter private 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; } @Override public 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 { @Override public void before(Class<?> clz, Method method, Object[] args) throws Throwable { log.info("-----------before DoodleAspect-----------"); log.info("class: {}, method: {}", clz.getName(), method.getName()); } @Override public void afterReturning(Class<?> clz, Object returnValue, Method method, Object[] args) throws Throwable { log.info("-----------after DoodleAspect-----------"); log.info("class: {}, method: {}", clz, method.getName()); } @Override public 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 { @Override public void before(Class<?> clz, Method method, Object[] args) throws Throwable { log.info("-----------before DoodleAspect2-----------"); log.info("class: {}, method: {}", clz.getName(), method.getName()); } @Override public void afterReturning(Class<?> clz, Object returnValue, Method method, Object[] args) throws Throwable { log.info("-----------after DoodleAspect2-----------"); log.info("class: {}, method: {}", clz, method.getName()); } @Override public 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 { @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(); } } 复制代码
在结果的图中可以看出 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
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Zen of CSS Design
Dave Shea、Molly E. Holzschlag / Peachpit Press / 2005-2-27 / USD 44.99
Proving once and for all that standards-compliant design does not equal dull design, this inspiring tome uses examples from the landmark CSS Zen Garden site as the foundation for discussions on how to......一起来看看 《The Zen of CSS Design》 这本书的介绍吧!