最近在做項(xiàng)目的時(shí)候使用了一下AOP碰到了一個(gè)問題罩缴,在這里分享一下。現(xiàn)在有一個(gè)類: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
}
}
然后定義了一個(gè)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;
}
}
最后測試的時(shí)候update
方法執(zhí)行的時(shí)候,方法encodeResultWithUtf8
并沒有執(zhí)行龄毡,通過查詢AOP文檔得知這種情況下spring aop不支持。官方舉了一個(gè)例子:
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...
}
}
當(dāng)我們直接通過SimplePojo
實(shí)例調(diào)用foo
的時(shí)候調(diào)用情況如下圖所示:
調(diào)用的代碼為:
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();
}
}
當(dāng)客戶端代碼的引用是一個(gè)代理時(shí),情況稍有變化荒勇。
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();
}
}
這里要理解的關(guān)鍵是Main
類的main(..)
方法中的客戶端代碼引用了代理。這意味著對該對象引用的方法調(diào)用是對代理的調(diào)用闻坚。因此沽翔,代理可以委托給與該特定方法調(diào)用相關(guān)的所有攔截器(Advice
)。但是窿凤,一旦調(diào)用最終到達(dá)目標(biāo)對象(在本例中是SimplePojo
)仅偎,在其內(nèi)部的對自己方法的調(diào)用并沒有經(jīng)過代理,比如this.foo()
或者this.bar()
雳殊,都是通過this
調(diào)用的橘沥,這意味著自我調(diào)用不會導(dǎo)致與方法調(diào)用相關(guān)聯(lián)的Advice
獲得執(zhí)行的機(jī)會。
通過這個(gè)例子我們可以知道為什么之前在FWRestfulResource.update
上定義的advice
-encodeResultWithUtf8
, 因?yàn)?code>FWRestfulResource.update是在create
中通過this
調(diào)用的夯秃。
當(dāng)然Spring也給這種情況提供了一些解決辦法:
- 重構(gòu)一下座咆,將
update
移到另外一個(gè)class
中痢艺,這樣自我調(diào)用就不會存在了。 - 以下面這種方式調(diào)用
bar
:
public class SimplePojo implements Pojo {
public void foo() {
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}
public void bar() {
// some logic...
}
}
不過這種方式會導(dǎo)致和spring耦合的太高介陶。