AOP
为什么要实现AOP
- 实现代码的过程中,通常只关注业务。而实现业务中不可避免的会需要关注到一些非业务逻辑层面。例如对业务逻辑代码的执行时间的记录,对于业务逻辑代码的日志记录,对业务逻辑代码的异常处理、安全验证。归类上述这些业务逻辑会发现。这些逻辑与要实现的业务逻辑是完全正交的,即X,Y轴,互不影响,但是共存。我们称对这类业务实现的编程为AOP(Aspect Oriented Programming)。
AOP术语
- 2.1 Joint Point
程序运行过程中能够进行插入切面操作的切入点。例如方法调用、异常抛出或字段修改等,在Spring AOP中只能是方法。在实际用的时候,这个具体的代码体现不多。 - 2.2 Pointcut
描述一个通知将被切入的一系列连接点的集合,即代码片段具体切入到哪些类、哪些方法。切入点规定了哪些连接点可以执行哪些植入代码。 - 2.3 Advice
切入到类指定方法或者指定位置的代码片段,即需要增加的功能代码。多数AOP框架,包括Spring将构建成Interceptor,在切入点上,框架维护了一系列的interceptor。 - 2.4 Aspect
AOP中的切面等同于OOP中的类(class),由通知(advice)和切入点(pointcut)组成,其中通知(advice)和切入点(pointcut)既可以是1对1的关系,也可以是1对多的关系。 - 2.5 Before advice
在切入点代码之前执行的代码。 - 2.6 After returning advice
在切入点代码正常执行完后执行的代码。 - 2.7 After throwing advice
在切入点代码报错之后执行的代码。 - 2.8 After(finally) advice
在切入点代码执行完后执行的代码,不管切入点代码执行的结果(normal or exceptional return.) - 2.9 Around Advice
在切入点代码执行前后都会执行的代码。
轻量级 AOP
- 从此开始,结合自己网上学习刘大大(刘欣 - 码农翻身作者)的lite-spring课程,写下学习笔记。
一步步地实现一个轻量级的AOP,采用的是Test-Driven的开发模式。这里只实现xml形式的aop。
1. Pointcut 详解
- 首先最简单的,我们要对一个类的方法动态的植入代码,需要我们先判断什么样的类满足我们的要求。我们下列的xml可以看出对简单的一个pointcut的定义。Pointcut的功能就是简单的表述一系列可被植入代码的method的切入点集合。将它抽象成一个类,Assuming,这个类应该是可以去判断一个method是否满足被植入代码。
<aop:pointcut id="placeOrder"
expression="execution(* org.litespring.service.v5.*.placeOrder(..))" />
- Spring AOP中使用了一个Pointcut接口表示上述代码,Pointcut接口返回一个MethodMatcher接口对象,MethodMatcher接口提供matches方法去比对传入的Method参数是否和表达式匹配。
-
@Test
public void testPointcut() throws Exception{
String expression = "execution(* org.litespring.service.v5.*.placeOrder(..))"
AspectJExpressionPointcut pc = new AspectJExpressionPointcut()
pc.setExpression(expression)
MethodMatcher mm = pc.getMethodMatcher()
{
Class<?> targetClass = PetStoreService.class
Method method1 = targetClass.getMethod("placeOrder")
Assert.assertTrue(mm.matches(method1))
Method method2 = targetClass.getMethod("getAccountDao")
Assert.assertFalse(mm.matches(method2))
}
{
Class<?> targetClass = org.litespring.service.v4.PetStoreService.class
Method method = targetClass.getMethod("getAccountDao")
Assert.assertFalse(mm.matches(method))
}
}
- 上面的unit测试,AspectJExpressionPointcut其实就是Pointcut,它也实现了MethodMatcher。它set了expression,内部得到一个PointExpression,通过它来检测是否和目标的method是否匹配。
2. 定位植入Method 详解
<bean id="tx" class="org.litespring.tx.TransactionManager" />
<aop:config>
<aop:aspect ref="tx">
<aop:pointcut id="placeOrder“ expression="……" />
<aop:before pointcut-ref="placeOrder" method="start" />
</aop:aspect>
</aop:config>
- AspectJExpressionPointcut是确定method是否match。确定好match的方法之后,接下来就需要通过xml确定植入需要用的method。如上,通过Bean的名称“tx”和“start”定位到这个Method,然后通过反射调用。Spring AOP使用了MethodLoatingFactor定位method。
@Test
public void testGetMethod() throws Exception{
DefaultBeanFactory beanFactory = new DefaultBeanFactory()
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory)
Resource resource = new ClassPathResource("petstore-v5.xml")
reader.loadBeanDefinition(resource)
MethodLocatingFactory methodLocatingFactory = new MethodLocatingFactory()
methodLocatingFactory.setTargetBeanName("tx")
methodLocatingFactory.setMethodName("start")
methodLocatingFactory.setBeanFactory(beanFactory)
Method m = methodLocatingFactory.getObject()
Assert.assertTrue(TransactionManager.class.equals(m.getDeclaringClass()))
Assert.assertTrue(m.equals(TransactionManager.class.getMethod("start")))
}
- 这其实就是一个工具类,通过给定的beanName获取beanClass,然后再根据methodName,反射获得method。可以用于任何通过beanName和methodName找到对应的method。