【学习】Spring 面向切向编程(AOP)

1、业务需求: 因为最近在研究审批流的实现,所以在每次save或update的时候,就要走审批流的处理,在每个service中加入审批流的处理方法,显然不大合适,所以由此想到用AOP切面技术来实现。

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>

[b]解释:[/b]
4.1 首先是方法切点函数:[b]expression[/b],可用通配符进行方法的配置,语法如下:
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
这么说很抽象,举几个实例:
a、execution(* com.baobaotao.*(..)):匹配com.baobaotao包下所有类的所有方法;
b、execution(* com..*.*DAO.find*(..)):匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀。
关于切点函数的通配,网上有很多总结,这里不再举例。

补充:切点函数支持复合运算符,如我上面的XML配置,就是匹配所有以save或update开头的方法名。

4.2 [b]Aspectj中的增强类型[/b]:基本类型有6个。

[table]
|类型|解释|成员|
|Before|增置增强|value,argNames|
|AfterReturning|后置增强|value,pointcut,returning,artNames|
|Around|环绕增强|value,argName|
|AfterThrowing|抛出增强|value,pointcut,throwing,argNames|
|After|Final增强,不管是抛出异常或者是正常退出,该增强都会执行|value,argName|
|DeclareParents|引介增强|value,defaultImpl|
[/table]

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("抛出异常后执行" );
}
}

[b]解释:[/b]
5.1 因为我实现的是审批流的切面,所以重点业务都放在了后置增强类中。我一开始的时候是放在after中来实现的,后来因为需要用到目标对象方法的返回值,所以就重点写了afterreturning方法。

5.2 [b]关于AOP中的session[/b],因为我用的是STRUTS2的框架,所以用上下文ActionContext直接得到了session。

5.3 [b]连接点信息类:JoinPoint。[/b]它包含了很多的信息,包括能得到连接点方法运行时的入参列表,方法签名对象,连接点所在的目标对象,代理对象本身等。
[table]
|java.lang.Object[] getArgs()|获取连接点方法运行时的入参列表|
|Signature getSignature()|获取连接点的方法签名对象|
|java.lang.Object getTarget()|获取连接点所在的目标对象|
|java.lang.Object getThis()|获取代理对象本身|
[/table]
[b]关于以上四点,我在类TestImpl的saveTest方法中测试,在beforeMethod方法中输出:[/b]
当前连接点方法运行时的入参列表:19
当前连接点方法运行时的入参列表:1
当前执行方法:save
获取连接点所在的目标对象:com.testPro.temp.service.impl.TestImpl
获取代理本身:com.testPro.temp.service.impl.TestImpl$$EnhancerByCGLIB$$c7579c95

-----------------------------------------------
[b]【值得注意的地方】[/b]
我在类TestImpl的saveTest方法中实现对POJO类Test的insert操作,然后本来的想法是在切面中获取saveTest相当参数,进行反射后,在具体的审批流中对Test实体类进行再次的查询和修改。

但这一思路并没有成功。 主要原因是在service层的save方法中save的Test实体类,在做切面的时候,并没有实际的插入到数据库中,所以我在用select语句去数据库查找时,得不到该信息。主要也是因为事务没有在service的save方法后进行提交,在切面中再生成一个,而是用了同一个事务。即:REQUIRED(如果有事务,那么加入事务,没有的话新创建一个)。

后来觉得切面中也没必要再对save中的实体进行再操作,所以将实体类的操作统一放到service中实现,切面中专心对审批流进行操作。这样的流程就走通了。


-----------------------------------------------
[b]【参考】[/b]
[url]http://blog.csdn.net/sin90lzc/article/details/7486145[/url]
[url]http://aopalliance.sourceforge.net[/url]
《Spring 3.x企业应用开发实战》第七章 基于@AspectJ和Schema的AOP
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值