2、本文环境是struts2 + spring 2.5
3、就像其它的流行技术一样,有注解和XML两种实现方式,aop的实现,也可以通过这两种方式实现,本文用XML的方式开发。
4、先来看看AOP的XML配置:
<!-- 配置日志切面 --> <bean id="diaryAspect" class="com.testPro.aop.DiaryAspect"></bean> <aop:config> <!-- 声明一个切面 --> <aop:aspect id="myDiaryAspect" ref="diaryAspect"> <aop:pointcut id="Operation" expression="(execution(* com.testPro.*.service.*.save*(..))) or (execution(* com.weboa.*.service.*.update*(..)))" /> <aop:before pointcut-ref="Operation" method="beforeMethod" /> <aop:after pointcut-ref="Operation" method="afterMethod" /> <aop:after-returning pointcut-ref="Operation" returning="result" method="afterReturning" /> <aop:after-throwing pointcut-ref="Operation" method="throwException" /> </aop:aspect> </aop:config>
解释:
4.1 首先是方法切点函数: expression,可用通配符进行方法的配置,语法如下:
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
这么说很抽象,举几个实例:
a、execution(* com.baobaotao.*(..)):匹配com.baobaotao包下所有类的所有方法;
b、execution(* com..*.*DAO.find*(..)):匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀。
关于切点函数的通配,网上有很多总结,这里不再举例。
补充:切点函数支持复合运算符,如我上面的XML配置,就是匹配所有以save或update开头的方法名。
4.2 Aspectj中的增强类型:基本类型有6个。
类型 | 解释 | 成员 |
Before | 增置增强 | value,argNames |
AfterReturning | 后置增强 | value,pointcut,returning,artNames |
Around | 环绕增强 | value,argName |
AfterThrowing | 抛出增强 | value,pointcut,throwing,argNames |
After | Final增强,不管是抛出异常或者是正常退出,该增强都会执行 | value,argName |
DeclareParents | 引介增强 | value,defaultImpl |
4.3 我上面的XML中定义了before,after,afterreturning和afterthrowing的增强(当然直接在切面的实现类里用ANNOTATION配置也是可以的。)
即标签:<aop:before/><aop:after/><aop:after-returning/><aop:after-throwing />
其中属性pointcut-ref是指定义的切面bean的ID,method是指具体定义的切面实体类中的方法。标签中<aop:after-returning/>的returning是指方法返回值。具体和实现类中的参数对应。
5、Aspect实现类(核心)
package com.testPro.aop; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.struts2.ServletActionContext; import org.aspectj.lang.JoinPoint; import com.opensymphony.xwork2.ActionContext; /** * 日志切面类 */ public class DiaryAspect { // 方法调用前执行 public void beforeMethod(JoinPoint point) { Object[] args = point.getArgs(); for(Object arg : args){ System.out.println("当前连接点方法运行时的入参列表:" + arg); } System.out.println("当前执行方法:"+ point.getSignature().getName()); System.out.println("获取连接点所在的目标对象:"+ point.getTarget().getClass().getName()); System.out.println("获取代理本身:" + point.getThis().getClass().getName()); } // 方法调用结束后执行 public void afterMethod(JoinPoint point) { System.out.println("final增强"); } public void afterReturning(JoinPoint point,int result){ //得到方法名 String className = point.getTarget().getClass().getName(); //得到SESSION HttpServletRequest request = (HttpServletRequest) ActionContext.getContext().get(ServletActionContext.HTTP_REQUEST); HttpSession session = request.getSession(); int userid = Integer.parseInt(session.getAttribute("userid").toString()); System.out.println("后置增强类:目标对象方法返回值:" + result); } // 抛出异常后执行 public void throwException(JoinPoint point) { System.out.println("抛出异常后执行" ); } }
解释:
5.1 因为我实现的是审批流的切面,所以重点业务都放在了后置增强类中。我一开始的时候是放在after中来实现的,后来因为需要用到目标对象方法的返回值,所以就重点写了afterreturning方法。
5.2 关于AOP中的session,因为我用的是STRUTS2的框架,所以用上下文ActionContext直接得到了session。
5.3 连接点信息类:JoinPoint。它包含了很多的信息,包括能得到连接点方法运行时的入参列表,方法签名对象,连接点所在的目标对象,代理对象本身等。
java.lang.Object[] getArgs() | 获取连接点方法运行时的入参列表 |
Signature getSignature() | 获取连接点的方法签名对象 |
java.lang.Object getTarget() | 获取连接点所在的目标对象 |
java.lang.Object getThis() | 获取代理对象本身 |
关于以上四点,我在类TestImpl的saveTest方法中测试,在beforeMethod方法中输出:
当前连接点方法运行时的入参列表:19
当前连接点方法运行时的入参列表:1
当前执行方法:save
获取连接点所在的目标对象:com.testPro.temp.service.impl.TestImpl
获取代理本身:com.testPro.temp.service.impl.TestImpl$$EnhancerByCGLIB$$c7579c95
-----------------------------------------------
【值得注意的地方】
我在类TestImpl的saveTest方法中实现对POJO类Test的insert操作,然后本来的想法是在切面中获取saveTest相当参数,进行反射后,在具体的审批流中对Test实体类进行再次的查询和修改。
但这一思路并没有成功。 主要原因是在service层的save方法中save的Test实体类,在做切面的时候,并没有实际的插入到数据库中,所以我在用select语句去数据库查找时,得不到该信息。主要也是因为事务没有在service的save方法后进行提交,在切面中再生成一个,而是用了同一个事务。即:REQUIRED(如果有事务,那么加入事务,没有的话新创建一个)。
后来觉得切面中也没必要再对save中的实体进行再操作,所以将实体类的操作统一放到service中实现,切面中专心对审批流进行操作。这样的流程就走通了。
-----------------------------------------------
【参考】
http://blog.csdn.net/sin90lzc/article/details/7486145
http://aopalliance.sourceforge.net
《Spring 3.x企业应用开发实战》第七章 基于@AspectJ和Schema的AOP