山有木兮木有枝,心悦君兮君不知。
到目前位置,关于 aop
的部分,我们已经完成了以下功能
Bean
的名称和方法名,获取 Method
对象。 MethodLocatingFactory
Pointcut
的表达式。 AspectJExpressionPointcut
AspectJBeforeAdvice
, AspectJAfterReturningAdvice
, AspectJAfterThrowingAdvice
Advice
按次续依次执行。 ReflectiveMethodInvocation
AopConif
,使用 Cglib
生成一个对象的代理。 接下来只剩下最后一个工作,读取 bean-v5.xml
创建 BeanDefinition
。
在创建 BeanDefinition
之前我们先看一下 aop:config
的定义,之前 Bean
的定义。
<bean id = "nioCoder" class = "com.niocoder.service.v1.NioCoderService"> </bean>
现在 Bean
的定义
<!-- aop 配置--> <aop:config> <!-- aop 核心配置 依赖tx--> <aop:aspect ref="tx"> <!-- 切入点配置 包下面的placeOrder 方法--> <aop:pointcut id="placeOrder" expression="execution(* com.niocoder.service.v5.*.placeOrder(..))"/> <!-- 通知配置,--> <aop:before pointcut-ref="placeOrder" method="start"/> <aop:after-returning pointcut-ref="placeOrder" method="commit"/> <aop:after-throwing pointcut-ref="placeOrder" method="rollback"/> </aop:aspect> </aop:config>
之前如果 Bean
有其它额外的属性,我们都是通过 PropertyValue
或 ConstructorArgument
来表示。但 aop:config
的标签该如何表达呢?
spring
还是通过 GenericBeanDefinition
来表示 aop:config
标签,关于标签属性的映射,参考下图
<aop:pointcut id="placeOrder" expression="execution(* com.niocoder.service.v5.*.placeOrder(..))"/>
<aop:before pointcut-ref="placeOrder" method="start"/>
通过上图的构造器类型,我们发现这与我们之前创建 AspectJBeforeAdvice
的构造器参数不匹配。
public AspectJBeforeAdvice(Method adviceMethod, AspectJExpressionPointcut pointcut, Object adviceObject) { super(adviceMethod, pointcut, adviceObject); }
因上图的第一个构造器参数中含有 methodName
,所以我们只需将修改 adviceObject
修改为 AspectInstanceFactory
。关于 AspectInstanceFactory
的类图设计如下:
工厂bean,
/** * 工厂bean * @author zhenglongfei */ public interface FactoryBean<T> { T getObject() throws Exception; Class<?> getObjectType(); }
有些实现类需要用到 BeanFactory
,实现该接口即可。
public interface BeanFactoryAware { /** * set beanFactory * * @param beanFactory * @throws BeansException */ void setBeanFactory(BeanFactory beanFactory) throws BeansException; }
/** * @author zhenglongfei */ public class AspectInstanceFactory implements BeanFactoryAware { private String aspectBeanName; private BeanFactory beanFactory; public void setAspectBeanName(String aspectBeanName) { this.aspectBeanName = aspectBeanName; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; if (!StringUtils.hasText(this.aspectBeanName)) { throw new IllegalArgumentException(" 'aspectBeanName' is required"); } } public Object getAspectInstance() throws Exception { return this.beanFactory.getBean(this.aspectBeanName); } }
实现 FactoryBean<Method>
和 BeanFactoryAware
public class MethodLocatingFactory implements FactoryBean<Method>, BeanFactoryAware { }
将 Object adviceObject
替换成 AspectInstanceFactory adviceObjectFactory
,修改 invokeAdviceMethod()
方法,对象是从 benFactory
中根据 aspectBeanName
获取。
ublic abstract class AbstractAspectJAdvice implements Advice { private Method adviceMethod; private AspectJExpressionPointcut pointcut; private Object adviceObject; public AbstractAspectJAdvice(Method adviceMethod, AspectJExpressionPointcut pointcut, Object adviceObject) { this.adviceMethod = adviceMethod; this.pointcut = pointcut; this.adviceObject = adviceObject; } @Override public Pointcut getPointcut() { return pointcut; } public Method getAdviceMethod() { return this.adviceMethod; } public void invokeAdviceMethod() throws Throwable { adviceMethod.invoke(adviceObject); } }
因抽象方法修改,子类构造函数也需要修改, AspectJAfterReturningAdvice
和 AspectJAfterThrowingAdvice
也需要修改
ublic class AspectJBeforeAdvice extends AbstractAspectJAdvice { public AspectJBeforeAdvice(Method adviceMethod, AspectJExpressionPointcut pointcut, Object adviceObject) { super(adviceMethod, pointcut, adviceObject); } ...... }
提出公工方法到一个测试类中
public class AbstractV5Test { protected BeanFactory getBeanFactory(String configFile) { DefaultBeanFactory defaultBeanFactory = new DefaultBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(defaultBeanFactory); reader.loadBeanDefinition(new ClassPathResource(configFile)); return defaultBeanFactory; } protected Method getAdviceMethod(String methodName) throws Exception { return TransactionManager.class.getMethod(methodName); } protected AspectInstanceFactory getAspectInstanceFactory(String targetBeanName) { AspectInstanceFactory factory = new AspectInstanceFactory(); factory.setAspectBeanName(targetBeanName); return factory; } }
动态获取 AspectInstanceFactory
public class CglibAopProxyTest extends AbstractV5Test { private static AspectJBeforeAdvice beforeAdvice = null; private static AspectJAfterReturningAdvice afterAdvice = null; private AspectJExpressionPointcut pc = null; private BeanFactory beanFactory = null; private NioCoderService nioCoderService = null; private AspectInstanceFactory aspectInstanceFactory = null; @Before public void setUp() throws Exception { MessageTracker.clearMsgs(); String expression = "execution(* com.niocoder.service.v5.*.placeOrder(..))"; pc = new AspectJExpressionPointcut(); pc.setExpression(expression); nioCoderService = new NioCoderService(); beanFactory = this.getBeanFactory("bean-v5.xml"); aspectInstanceFactory = this.getAspectInstanceFactory("tx"); aspectInstanceFactory.setBeanFactory(beanFactory); beforeAdvice = new AspectJBeforeAdvice( getAdviceMethod("start"), pc, aspectInstanceFactory); afterAdvice = new AspectJAfterReturningAdvice( getAdviceMethod("commit"), pc, aspectInstanceFactory); } @Test public void testGetProxy() { AopConfig config = new AopConfigSupport(); config.addAdvice(beforeAdvice); config.addAdvice(afterAdvice); config.setTargetObject(nioCoderService); CglibProxyFactory proxyFactory = new CglibProxyFactory(config); NioCoderService proxy = (NioCoderService) proxyFactory.getProxy(); proxy.placeOrder(); List<String> msgs = MessageTracker.getMsgs(); Assert.assertEquals(3, msgs.size()); Assert.assertEquals("start tx", msgs.get(0)); Assert.assertEquals("place order", msgs.get(1)); Assert.assertEquals("commit tx", msgs.get(2)); } }
动态获取 AspectInstanceFactory
public class ReflectiveMethodInvocationTest extends AbstractV5Test{ private AspectJBeforeAdvice beforeAdvice = null; private AspectJAfterReturningAdvice afterReturningAdvice; private AspectJAfterThrowingAdvice afterThrowingAdvice; private NioCoderService nioCoderService; private AspectJExpressionPointcut pc = null; private BeanFactory beanFactory = null; private AspectInstanceFactory aspectInstanceFactory = null; @Before public void setUp() throws Exception { nioCoderService = new NioCoderService(); String expression = "execution(* com.niocoder.service.v5.*.placeOrder(..))"; pc = new AspectJExpressionPointcut(); pc.setExpression(expression); beanFactory = this.getBeanFactory("bean-v5.xml"); aspectInstanceFactory = this.getAspectInstanceFactory("tx"); aspectInstanceFactory.setBeanFactory(beanFactory); MessageTracker.clearMsgs(); beforeAdvice = new AspectJBeforeAdvice(TransactionManager.class.getMethod("start"), pc, aspectInstanceFactory); afterReturningAdvice = new AspectJAfterReturningAdvice(TransactionManager.class.getMethod("commit"), pc, aspectInstanceFactory); afterThrowingAdvice = new AspectJAfterThrowingAdvice(TransactionManager.class.getMethod("rollback"), pc, aspectInstanceFactory); } @Test public void textMethodInvocation() throws Throwable { Method targetMethod = NioCoderService.class.getMethod("placeOrder"); List<MethodInterceptor> interceptorList = new ArrayList<>(); interceptorList.add(beforeAdvice); interceptorList.add(afterReturningAdvice); ReflectiveMethodInvocation mi = new ReflectiveMethodInvocation(nioCoderService, targetMethod, new Object[0], interceptorList); mi.proceed(); List<String> msgs = MessageTracker.getMsgs(); Assert.assertEquals(3, msgs.size()); Assert.assertEquals("start tx", msgs.get(0)); Assert.assertEquals("place order", msgs.get(1)); Assert.assertEquals("commit tx", msgs.get(2)); } @Test public void testAfterThrowing() throws Throwable{ Method targetMethod = NioCoderService.class.getMethod("placeOrderWithException"); List<MethodInterceptor> interceptorList = new ArrayList<>(); interceptorList.add(beforeAdvice); interceptorList.add(afterThrowingAdvice); ReflectiveMethodInvocation mi = new ReflectiveMethodInvocation(nioCoderService, targetMethod, new Object[0], interceptorList); try { mi.proceed(); } catch (Throwable t) { List<String> msgs = MessageTracker.getMsgs(); Assert.assertEquals(2, msgs.size()); Assert.assertEquals("start tx", msgs.get(0)); Assert.assertEquals("rollback tx", msgs.get(1)); return; } Assert.fail("No Exception thrown"); } }
TODO ….
从零开始造Spring