Spring AOP代理

最近在做項(xiàng)目的時(shí)候使用了一下AOP碰到了一個(gè)問(wèn)題,在這里分享一下?,F(xiàn)在有一個(gè)類(lèi):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;
    }
}

最后測(cè)試的時(shí)候update方法執(zhí)行的時(shí)候,方法encodeResultWithUtf8并沒(méi)有執(zhí)行,通過(guò)查詢(xún)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)我們直接通過(guò)SimplePojo實(shí)例調(diào)用foo的時(shí)候調(diào)用情況如下圖所示:

Direct Call Foo

調(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í),情況稍有變化。


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();
    }
}

這里要理解的關(guān)鍵是Main類(lèi)的main(..)方法中的客戶端代碼引用了代理。這意味著對(duì)該對(duì)象引用的方法調(diào)用是對(duì)代理的調(diào)用。因此,代理可以委托給與該特定方法調(diào)用相關(guān)的所有攔截器(Advice)。但是,一旦調(diào)用最終到達(dá)目標(biāo)對(duì)象(在本例中是SimplePojo),在其內(nèi)部的對(duì)自己方法的調(diào)用并沒(méi)有經(jīng)過(guò)代理,比如this.foo()或者this.bar(),都是通過(guò)this調(diào)用的,這意味著自我調(diào)用不會(huì)導(dǎo)致與方法調(diào)用相關(guān)聯(lián)的Advice獲得執(zhí)行的機(jī)會(huì)。

通過(guò)這個(gè)例子我們可以知道為什么之前在FWRestfulResource.update上定義的advice-encodeResultWithUtf8, 因?yàn)?code>FWRestfulResource.update是在create中通過(guò)this調(diào)用的。

當(dāng)然Spring也給這種情況提供了一些解決辦法:

  • 重構(gòu)一下,將update移到另外一個(gè)class中,這樣自我調(diào)用就不會(huì)存在了。
  • 以下面這種方式調(diào)用bar
public class SimplePojo implements Pojo {

    public void foo() {
        // this works, but... gah!
        ((Pojo) AopContext.currentProxy()).bar();
    }

    public void bar() {
        // some logic...
    }
}

不過(guò)這種方式會(huì)導(dǎo)致和spring耦合的太高。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容