前些日子西安项目中的任务日志,以及最近参与工作流动态表单开发中都使用到了spring的aop。所以,自己简单的进行了一下总结,也算是对前一段时间工作的一个总结吧。
AOP(Aspect-Oriented
Programming,面向切面编程),可以说是OOP(面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
Spring支持四种拦截类型:目标方法调用前(before),目标方法调用后(after),目标方法调用前后(around),以及目标方法抛出异常(throw)。
前置拦截的类必须实现MethodBeforeAdvice接口,实现其中的before方法。
后置拦截的类必须实现AfterReturningAdvice接口,实现其中的afterReturning方法。
环绕拦截的类必须实现MethodInterceptor接口,实现其中的invoke方法。环绕拦截是唯一可以控制目标方法是否被真正调用的拦截类型,也可以控制返回对象。而前置拦截或后置拦截不能控制,它们不能印象目标方法的调用和返回。
但是以上的拦截的问题在于,不能对于特定方法进行拦截,而只能对某个类的全部方法作拦截。所以下面引入了两个新概念:“切入点”和“引入通知”。
”切入点“的定义相当于更加细化地规定了哪些方法被哪些拦截器所拦截,而并非所有的方法都被所有的拦截器所拦截。在ProxyFactoryBean的属性中,interceptorNames属性的对象也由拦截(Advice)变成了引入通知(Advisor),正是在Advisor中详细定义了切入点(PointCut)和拦截(Advice)的对应关系,比如常见的基于名字的切入点匹配(NameMatchMethodPointcutAdvisor类)和基于正则表达式的切入点匹配(RegExpPointcutAdvisor类)。这些切入点都属于”静态切入点“,因为他们只在代理创建的时候被创建一次,而不是每次运行都创建。
下面是spring的配置文件 当然aop的配置方式有许多种,这只是其中一种
<!-- 获取相应的instance对象 并且不被aop拦截 -->
<bean id="oaTaskInstanceService4Log" parent="transactionProxy">
<property name="target">
<bean
class="com.oa.task.service.impl.OaTaskInstanceServiceImpl">
<property name="oaTaskInstanceDao">
<ref local="oaTaskInstanceDao"/>
</property>
</bean>
</property>
</bean>
<!-- instance日志所需DAO -->
<bean id="oaTaskInstanceLogDao" class="com.oa.task.dao.OaTaskInstanceLogDao">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- instance日志所需Service 此接口为 日志的业务操作接口-->
<bean id="oaTaskInstanceLogService" parent="transactionProxy">
<property name="target">
<bean
class="com.oa.task.service.impl.OaTaskInstanceLogServiceImpl">
<property name="oaTaskInstanceLogDao">
<ref local="oaTaskInstanceLogDao"/>
</property>
<property name="sysOperDao">
<ref bean="sysOperDao" />
</property>
<property name="ibatisBase">
<ref bean="ibatisBase" />
</property>
</bean>
</property>
</bean>
<!-- instance日志AOP拦截类 -->
<bean id="oaTaskInstanceLogOper" class="com.oa.task.log.OaTaskInstanceLogOper">
<property name="oaTaskInstanceLogService">
<ref local="oaTaskInstanceLogService" />
</property>
<property name="oaTaskInstanceService">
<ref local="oaTaskInstanceService4Log" />
</property>
</bean>
<!-- instance日志AOP配置绑定 -->
<bean id="oaTaskInstanceLogAutoProxy"
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>oaTaskInstanceService</value> //拦截的业务接口 oaTaskInstanceService内所有方法都进行拦截
</list>
</property>
<property name="interceptorNames">
<list>
<value>oaTaskInstanceLogOper</value> //拦截器
</list>
</property>
</bean>
下面是 拦截器
oaTaskInstanceLogOper类,采用的是环绕拦截的方式。
public class OaTaskInstanceLogOper implements MethodInterceptor{
/**
* 当更新动作发生时的记录
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
String method = invocation.getMethod().getName();
Object result = null;
if(MODIFY_INSTANCE.equals(method)){//修改日志(业务逻辑 判断方法)
......//业务操作,进行日志的记录
result=invocation.proceed();
}else {//不做任何日志
result=invocation.proceed();//调用原来的方法
}
return result;
}
}