Spring AOP 详解
为方便阅读,将原博客抄了过来,
原博客网址:http://pandonix.iteye.com/blog/336873
博客分类: J2EE
AOPSpringjunitBeanJDK
此前对于AOP的使用仅限于声明式事务,除此之外在实际开发中也没有遇到过与之相关的问题。最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP 来解决。一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容。本文是权当本人的自己AOP学习笔记,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智。
对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况
监控部分重要函数,若抛出指定的异常,需要以短信或邮件方式通知相关人员
金控部分重要函数的执行时间
事实上,以上需求没有AOP也能搞定,只是在实现过程中比较郁闷摆了。
需要打印日志的函数分散在各个包中,只能找到所有的函数体,手动添加日志。然而这些日志都是临时的,待问题解决之后应该需要清除打印日志的代码,只能再次手动清除^_^!
类 似1的情况,需要捕获异常的地方太多,如果手动添加时想到很可能明天又要手动清除,只能再汗。OK,该需求相对比较固定,属于长期监控的范畴,并不需求临 时添加后再清除。然而,客户某天要求,把其中20%的异常改为短信提醒,剩下的80%改用邮件提醒。改之,两天后,客户抱怨短信太多,全部改成邮件提 醒...
该需求通常用于监控某些函数的执行时间,用以判断系统执行慢的瓶颈所在。瓶颈被解决之后,烦恼同情况1
终于下定决心,采用AOP来解决!代码如下:
切面类TestAspect
Java代码 收藏代码
package com.spring.aop;
/**
* 切面
*
*/
public class TestAspect {
public void doAfter(JoinPoint jp) {
System.out.println("log Ending method: "
+ jp.getTarget().getClass().getName() + "."
+ jp.getSignature().getName());
}
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
long time = System.currentTimeMillis();
Object retVal = pjp.proceed();
time = System.currentTimeMillis() - time;
System.out.println("process time: " + time + " ms");
return retVal;
}
public void doBefore(JoinPoint jp) {
System.out.println("log Begining method: "
+ jp.getTarget().getClass().getName() + "."
+ jp.getSignature().getName());
}
public void doThrowing(JoinPoint jp, Throwable ex) {
System.out.println("method " + jp.getTarget().getClass().getName()
+ "." + jp.getSignature().getName() + " throw exception");
System.out.println(ex.getMessage());
}
private void sendEx(String ex) {
//TODO 发送短信或邮件提醒
}
}
Java代码 收藏代码
package com.spring.service;
/**
* 接口A
*/
public interface AService {
public void fooA(String _msg);
public void barA();
}
Java代码 收藏代码
package com.spring.service;
/**
*接口A的实现类
*/
public class AServiceImpl implements AService {
public void barA() {
System.out.println("AServiceImpl.barA()");
}
public void fooA(String _msg) {
System.out.println("AServiceImpl.fooA(msg:"+_msg+")");
}
}
Java代码 收藏代码
package com.spring.service;
/**
* Service类B
*/
public class BServiceImpl {
public void barB(String _msg, int _type) {
System.out.println("BServiceImpl.barB(msg:"+_msg+" type:"+_type+")");
if(_type == 1)
throw new IllegalArgumentException("测试异常");
}
public void fooB() {
System.out.println("BServiceImpl.fooB()");
}
}
ApplicationContext
Java代码 收藏代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"
default-autowire="autodetect">
<aop:config>
<aop:aspect id="TestAspect" ref="aspectBean">
<!--配置com.spring.service包下所有类或接口的所有方法-->
<aop:pointcut id="businessService"
expression="execution(* com.spring.service.*.*(..))" />
<aop:before pointcut-ref="businessService" method="doBefore"/>
<aop:after pointcut-ref="businessService" method="doAfter"/>
<aop:around pointcut-ref="businessService" method="doAround"/>
<aop:after-throwing pointcut-ref="businessService" method="doThrowing" throwing="ex"/>
</aop:aspect>
</aop:config>
<bean id="aspectBean" class="com.spring.aop.TestAspect" />
<bean id="aService" class="com.spring.service.AServiceImpl"></bean>
<bean id="bService" class="com.spring.service.BServiceImpl"></bean>
</beans>
测试类AOPTest
Java代码 收藏代码
public class AOPTest extends AbstractDependencyInjectionSpringContextTests {
private AService aService;
private BServiceImpl bService;
protected String[] getConfigLocations() {
String[] configs = new String[] { "/applicationContext.xml"};
return configs;
}
/**
* 测试正常调用
*/
public void testCall()
{
System.out.println("SpringTest JUnit test");
aService.fooA("JUnit test fooA");
aService.barA();
bService.fooB();
bService.barB("JUnit test barB",0);
}
/**
* 测试After-Throwing
*/
public void testThrow()
{
try {
bService.barB("JUnit call barB",1);
} catch (IllegalArgumentException e) {
}
}
public void setAService(AService service) {
aService = service;
}
public void setBService(BServiceImpl service) {
bService = service;
}
}
运行结果如下:
Java代码 收藏代码
log Begining method: com.spring.service.AServiceImpl.fooA
AServiceImpl.fooA(msg:JUnit test fooA)
log Ending method: com.spring.service.AServiceImpl.fooA
process time: 0 ms
log Begining method: com.spring.service.AServiceImpl.barA
AServiceImpl.barA()
log Ending method: com.spring.service.AServiceImpl.barA
process time: 0 ms
log Begining method: com.spring.service.BServiceImpl.fooB
BServiceImpl.fooB()
log Ending method: com.spring.service.BServiceImpl.fooB
process time: 0 ms
log Begining method: com.spring.service.BServiceImpl.barB
BServiceImpl.barB(msg:JUnit test barB type:0)
log Ending method: com.spring.service.BServiceImpl.barB
process time: 0 ms
log Begining method: com.spring.service.BServiceImpl.barB
BServiceImpl.barB(msg:JUnit call barB type:1)
log Ending method: com.spring.service.BServiceImpl.barB
method com.spring.service.BServiceImpl.barB throw exception
测试异常
《Spring参考手册》中定义了以下几个AOP的重要概念,结合以上代码分析如下:
切面(Aspect) :官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”,在本例中,“切面”就是类TestAspect所关注的具体行为,例如,AServiceImpl.barA()的调用就是切面TestAspect所关注的行为之一。“切面”在ApplicationContext中<aop:aspect>来配置。
连接点(Joinpoint) :程序执行过程中的某一行为,例如,AServiceImpl.barA()的调用或者BServiceImpl.barB(String _msg, int _type)抛出异常等行为。
通知(Advice) :“切面”对于某个“连接点”所产生的动作,例如,TestAspect中对com.spring.service包下所有类的方法进行日志记录的动作就是一个Advice。其中,一个“切面”可以包含多个“Advice”,例如TestAspect
切入点(Pointcut) :匹配连接点的断言,在AOP中通知和一个切入点表达式关联。例如,TestAspect中的所有通知所关注的连接点,都由切入点表达式execution(* com.spring.service.*.*(..))来决定
目标对象(Target Object) :被一个或者多个切面所通知的对象。例如,AServcieImpl和BServiceImpl,当然在实际运行时,Spring AOP采用代理实现,实际AOP操作的是TargetObject的代理对象。
AOP代理(AOP Proxy) 在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。默认情况下,TargetObject实现了接口时,则采用JDK动态代理,例如,AServiceImpl;反之,采用CGLIB代理,例如,BServiceImpl。强制使用CGLIB代理需要将 <aop:config> 的 proxy-target-class 属性设为true
通知(Advice)类型
前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。例如,TestAspect中的doBefore方法
后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。例如,TestAspect中的doAfter方法,所以AOPTest中调用BServiceImpl.barB抛出异常时,doAfter方法仍然执行
返回后通知(After return advice) :在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext中在<aop:aspect>里面使用<after-returning>元素进行声明。
环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。例如,TestAspect中的doAround方法。
抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。 ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。例如,TestAspect中的doThrowing方法。
切入点表达式
通常情况下,表达式中使用”execution“就可以满足大部分的要求。表达式格式如下:
Java代码 收藏代码
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
modifiers-pattern:方法的操作权限
ret-type-pattern:返回值
declaring-type-pattern:方法所在的包
name-pattern:方法名
parm-pattern:参数名
throws-pattern:异常
其中,除ret-type-pattern和name-pattern之外,其他都是可选的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值为任意类型;方法名任意;参数不作限制的所有方法。
通知参数
可以通过args来绑定参数,这样就可以在通知(Advice)中访问具体参数了。例如,<aop:aspect>配置如下
Java代码 收藏代码
<aop:config>
<aop:aspect id="TestAspect" ref="aspectBean">
<aop:pointcut id="businessService"
expression="execution(* com.spring.service.*.*(String,..)) and args(msg,..)" />
<aop:after pointcut-ref="businessService" method="doAfter"/>
</aop:aspect>
</aop:config>
TestAspect的doAfter方法中就可以访问msg参数,但这样以来AService中的barA()和BServiceImpl中的barB()就不再是连接点,因为execution(* com.spring.service.*.*(String,..))只配置第一个参数为String类型的方法。其中,doAfter方法定义如下:
Java代码 收藏代码
public void doAfter(JoinPoint jp,String msg)
访问当前的连接点
任何通知(Advice)方法可以将第一个参数定义为 org.aspectj.lang.JoinPoint 类型。JoinPoint 接口提供了一系列有用的方法, 比如 getArgs() (返回方法参数)、getThis() (返回代理对象)、getTarget() (返回目标)、getSignature() (返回正在被通知的方法相关信息)和 toString() (打印出正在被通知的方法的有用信息。
分享到:
相关推荐
spring aop
spring AOP的实例代码,有着详细的讲解,
使用SpringAOP完成日志记录功能的实例
SpringAop实例,包括xml和配置两种方式.jar齐全
spring 采用aop 的实例
Spring Aop的使用实例
基于xml代码的SpringAOP实例详解,适合初步学习springAOP的开发人员了解该功能的使用。
NULL 博文链接:https://306963591.iteye.com/blog/1129837
里面有Spring-AOP的介绍(word文档),还有写了一些Spring的实例(MyEclipse项目),直接导入项目就可以运行了。
一个基于@AspectJ的spring2.0 AOP应用实例,很小很简单,没有任何额外信息,最适合AOP入门学习。使用log4j打印信息。把项目直接import进myeclipse就可以使用啦......
spring aop 拦截 实例spring aop 拦截 实例spring aop 拦截 实例
Spring AOP 实例源码!!!!!!!!!!!!!!!!!!!!!!!!
Spring AOP 几个不同使用方法的完整例子,使用Junit4c测试, 在我的博客上有不同配置组合的说明,可以参考
Spring AOP配置的实例,最基本的Spring AOP配置
Spring AOP 日志管理 实例LoggingThrowsAdvice.java
spring aop 经典例子(原创),无论新手还是老手,可以快速对Spring AOP有个总体的认识。基于JDK1.6,Spring2.0.5,Eclipse IDE。
1、spring切入点 2、spring前置织入,传入参数处理 3、spring后置织入,传入参数处理 4、环绕织入,参数及返回值处理 5、返回后织入,返回值处理 6、异常织入,异常处理 maven环境下,测试用例可直接运行
spring3.2.8使用aop的简单实例
用spring AOP做的最简单的日志记录小例子,特别简单,可直接运行。
Spring Aop实例(AOP 如此简单)@Aspect、@Around 注解方式配置