示例需求:当我们调用fastjson里面的toJSONString方法时,我们在这个方法上加上环绕通知,众所周知,这个方法是第三方jar包fastjson里面提供的一个方法,我们要做的就是切到这个方法上,然后加环绕通知。步骤如下:
1、写一个服务,该服务使用了fastjson里面的toJSONString方法,代码如下:
public class JSONService {
public String parse2String(Object obj){
return JSON.toJSONString(obj);
}
}
2、写一个切面,并在该切面上配置环绕通知,代码如下:
@Aspect
public class JSONAspect {
<span style="color:#000099;">public static JSONAspect ajc$perSingletonInstance;
private static Throwable ajc$initFailureCause;
static {
try {
ajc$postClinit();
} catch (Throwable localThrowable) {
ajc$initFailureCause = localThrowable;
}
}
private static void ajc$postClinit() {
ajc$perSingletonInstance = new JSONAspect();
}
public static JSONAspect aspectOf() {
if (ajc$perSingletonInstance == null)
throw new NoAspectBoundException("com.chhliu.myself.spring.staticaop.JSONAspect", ajc$initFailureCause);
return ajc$perSingletonInstance;
}</span>
/**
* attention:
* Details:切入到第三方jar包对应的具体方法上,同时配置环绕通知
* @author chhliu
* 创建时间:2016-7-26 下午5:25:06
* @param join
* @param obj
* @return
* String
*/
@Around("execution(* com.alibaba.fastjson.JSON.toJSONString(java.lang.Object)) && args(obj)")
public String parse2String(ProceedingJoinPoint join, Object obj){
System.out.println("parse to String before");
String str = "";
try {
str = (String) join.proceed(new Object[]{obj});
System.out.println("result:"+str);
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("parse to String after");
return str;
}
}
注意:代码中标蓝的部分是AspectJ反编译后的代码,必须加上,否则会出问题,切不进去,感觉是spring 整合AspectJ时的一个bug。如果将我们的工程转成AspectJ Project的话,反编译出来的类就会自动的加上上面标蓝的代码。
3、在META-INF下增加切面的配置文件,如下:
工程结构目录如下:
|
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver options="-verbose">
<!-- 要改变那些包下的类,有@NoWeave注解的会跳过。-->
<include within="com.alibaba.fastjson.*" />
</weaver>
<aspects>
<!-- 声明的切面 -->
<aspect name="com.chhliu.myself.spring.staticaop.JSONAspect" />
</aspects>
</aspectj>
4、编写测试类,代码如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class JSONAspectTest {
@Resource(name="jsonService")
private JSONService service;
@Test
public void test(){
User u = new User();
u.setName("chhliu");
u.setPassword("123456");
service.parse2String(u);
}
}
5、增加jvm启动项,在虚拟机启动的时候,就启动AOP切面,如下:
6、测试结果如下:
[AppClassLoader@40d7b9] info AspectJ Weaver Version 1.7.2 built on Friday Feb 15, 2013 at 18:40:45 GMT
[AppClassLoader@40d7b9] info register classloader sun.misc.Launcher$AppClassLoader@40d7b9
[AppClassLoader@40d7b9] info using configuration /D:/NTF/spring_test/target/classes/META-INF/aop.xml
[AppClassLoader@40d7b9] info using configuration file:/D:/SoftWare/java%20software/apache-maven-3.0.5-bin/apache-maven-3.0.5/repository/org/springframework/spring-aspects/3.2.5.RELEASE/spring-aspects-3.2.5.RELEASE.jar!/META-INF/aop.xml
<span style="color:#3333FF;">[AppClassLoader@40d7b9] info register aspect com.chhliu.myself.spring.staticaop.JSONAspect</span>
[AppClassLoader@40d7b9] info register aspect org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect
[AppClassLoader@40d7b9] info register aspect org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect
[AppClassLoader@40d7b9] info register aspect org.springframework.transaction.aspectj.AnnotationTransactionAspect
[AppClassLoader@40d7b9] info register aspect org.springframework.cache.aspectj.AnnotationCacheAspect
log4j:WARN No appenders could be found for logger (org.springframework.test.context.junit4.SpringJUnit4ClassRunner).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
[AppClassLoader@40d7b9] warning javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified
around before
beforeTest
parse to String before
result:{"name":"chhliu","password":"123456"}
parse to String after
蓝色字体部分说明,在启动的时候,AppClassLoader将我们自己写的切面加载进去了,并且在调用fastjson的toJSONString方法的时候,切面启了作用,如果我们用spring的动态AOP,是很难切入到第三方jar包里面的方法的。
从上面的示例中,我们可以看到,使用Spring AOP来实现回归测试是可行的,当我们测试完之后,我们把测试的用例都存起来(文件或Redis),当我们在调用真正方法之前,我们截取该方法的参数,从而获取相关的请求报文信息,然后和Redis中的报文头进行一个匹配,匹配成功之后,我们并不真正的执行目的方法,而是直接将匹配的报文返回给用户,从而实现了mock测试,供大家参考。