最近在做项目的时候使用了一下AOP碰到了一个问题,在这里分享一下。现在有一个类:FWRestfulResource.java
:
class FWRestfulResource{
@POST
@Produces({"application/json", "application/xml", "text/xml"})
@Consumes({"application/json"})
public Response create(@Context HttpHeaders httpheader, T obj) {
String result = this.update(obj);
...
}
public String update(T obj){
// doupate
}
}
然后定义了一个Aspect:
@Aspect
public class EncodeResourceCreateReturnValueAspect {
public List<String> needToEncodeReturnValueMethodNames = Lists.newArrayList("create", "createJson", "createXml");
@Around(value = "execution(* com.oocl.csc.frm.bsf.service.FWRestfulResource.update(..))")
public Object encodeResultWithUtf8(ProceedingJoinPoint point) throws Throwable {
Object retValue = point.proceed();
try {
MethodSignature methodSignature = (MethodSignature) point.getSignature();
Class returnType = methodSignature.getReturnType();
String methodName = methodSignature.getMethod().getName();
if (returnType.isAssignableFrom(String.class) && needToEncodeReturnValueMethodNames.indexOf(methodName) != -1) {
retValue = URLEncoder.encode((String) retValue, "utf-8");
}
} catch (Exception e) {
FWExceptionLoggerFactory.getLogger().logException(e);
}
return retValue;
}
}
最后测试的时候update
方法执行的时候,方法encodeResultWithUtf8
并没有执行,通过查询AOP文档得知这种情况下spring aop不支持。官方举了一个例子:
public class SimplePojo implements Pojo {
public void foo() {
// this next method invocation is a direct call on the 'this' reference
this.bar();
}
public void bar() {
// some logic...
}
}
当我们直接通过SimplePojo
实例调用foo
的时候调用情况如下图所示:
调用的代码为:
public class Main {
public static void main(String[] args) {
Pojo pojo = new SimplePojo();
// this is a direct method call on the 'pojo' reference
pojo.foo();
}
}
当客户端代码的引用是一个代理时,情况稍有变化。
Proxy call
public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
Pojo pojo = (Pojo) factory.getProxy();
// this is a method call on the proxy!
pojo.foo();
}
}
这里要理解的关键是Main
类的main(..)
方法中的客户端代码引用了代理。这意味着对该对象引用的方法调用是对代理的调用。因此,代理可以委托给与该特定方法调用相关的所有拦截器(Advice
)。但是,一旦调用最终到达目标对象(在本例中是SimplePojo
),在其内部的对自己方法的调用并没有经过代理,比如this.foo()
或者this.bar()
,都是通过this
调用的,这意味着自我调用不会导致与方法调用相关联的Advice
获得执行的机会。
通过这个例子我们可以知道为什么之前在FWRestfulResource.update
上定义的advice
-encodeResultWithUtf8
, 因为FWRestfulResource.update
是在create
中通过this
调用的。
当然Spring也给这种情况提供了一些解决办法:
- 重构一下,将
update
移到另外一个class
中,这样自我调用就不会存在了。 - 以下面这种方式调用
bar
:
public class SimplePojo implements Pojo {
public void foo() {
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}
public void bar() {
// some logic...
}
}
不过这种方式会导致和spring耦合的太高。
网友评论