美文网首页
Spring AOP代理

Spring AOP代理

作者: Minato666 | 来源:发表于2020-07-09 23:01 被阅读0次

    最近在做项目的时候使用了一下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的时候调用情况如下图所示:

    Direct Call 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耦合的太高。

    相关文章

      网友评论

          本文标题:Spring AOP代理

          本文链接:https://www.haomeiwen.com/subject/onlecktx.html