Spring AOP做基于注解的缓存,不生效的可能原因小结

背景

AOP在处理一些共性业务的时候提供了十分便利的扩展性,有着十分广泛的应用场景。
在Java中常用的两种框架主要就是Spring AOP和原生的Aspectj,当然Spring AOP本身也借鉴和引用了很多Aspectj中的东西。

虽然AOP本身的使用很便利,但是在使用中主要还是有以下一些注意点,需要在使用Spring AOP(Aspectj由于了解不多暂不讨论)注意一些很容易被忽略的细节点和原因:

  1. AOP的切面方法,不能用于private的方法,但是aspectj是可以的;
  2. AOP是基于代理机制的,虽然可以通过xml声明究竟是使用jdk动态代理还是cglib的动态代理,但是是否可能存在一些场景使得代理不生效呢?
  3. AOP在作用自身类内部的方法调用的时候,为什么会失效呢?

虽然以上都是很简单的细节点,但是有时候忽略的话,会导致我们很难找到问题点从而改变我们的代码。笔者在实际的基于AOP的项目中就遇到了一些类似的问题。

好吧,终于我还是忍不住了,其实我主要就是来安利一个自己写的基于Annotation的AOP缓存插件的,以上问题是在运用插件的时候可能导致不生效的一些原因!所以我把不生效的原因都写这么具体了,你考虑看一下么?(p.s. 看都看了,不考虑试一下么~ 逃~)

所以,重要的事情说三遍,有兴趣的同学请参考基于基于Annotation的AOP缓存插件:

以下是项目说明:

基于AOP注解实现的缓存插件 在需要缓存的地方使用@SimpleCache注解即可实现缓存

  • 提供自定义的expiretime
  • 提供多种存储实现
  • 通过xml bean和AOP的方式来实现,侵入性小

不过既然为了凑字数,还是写到底吧~ 下面是刚刚三个小问题的答案~


为什么spring AOP不能应用于private方法

其实如果单纯说原因的话,可能主要还是基于安全性的考虑,因为私有的方法从本身机制上来说就应该是不可见的。
当然后来由于反射等的出现,特别是cglib直接能修改字节码这种神器,非要能代理到private的方法也是可以实现的(例如aspectj中就可以达到这个目的),但是这还是从根本上破坏了java对private的定义,因此spring可能考略这些因素,对aop的时候做了一些权限的检验限制,如下代码所示

//  有很多对权限做检验的地方
    /**
     * Implementation of AOP Alliance MethodInvocation used by this AOP proxy.
     */
    private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

        private final MethodProxy methodProxy;

        private final boolean publicMethod;

        public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
                Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
            super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
            this.methodProxy = methodProxy;
            this.publicMethod = Modifier.isPublic(method.getModifiers());
        }

        /**
         * Gives a marginal performance improvement versus using reflection to
         * invoke the target when invoking public methods.
         */
        @Override
        protected Object invokeJoinpoint() throws Throwable {
            if (this.publicMethod) {
                return this.methodProxy.invoke(this.target, this.arguments);
            }
            else {
                return super.invokeJoinpoint();
            }
        }
    }

所以,在使用spring AOP的时候需要注意,不要切到private这些方法上去了(别问我怎么知道的……)


是否有存在使得代理失效导致AOP不成功的场景

这种情况主要还是出现于动态代理本身出现了问题导致,主要还是需要我们对动态代理的原理有一个比较好的了解(比如哪种代理是基友哪种实现方式的,以及什么场景下可能会导致无法代理):

  1. jdk动态代理,是jvm自身实现的一个代理,最核心的点在于是一种基于接口的代理,所以如果没有实现接口的话……

  2. cglib代理,核心原理是修改字节码,通过继承来实现代理对象,也就是说如果没法继承(如final修饰符),那么……(此刻补充一句题外话,在上面提到的插件里,设计的时候就是基于cglib的,所以如果你在配置文件里面没有设置使用cglib为true的话……请私信我,我考虑优化一下……)


为什么类的内部使用AOP方法会不走AOP流程

对于这个问题的理解主要还是要对代理方法的主要实现有个理解,如下图所示(图来源于Aspect Oriented Programming with Spring,建议有兴趣的同学可以深入看一下):

AOP使用代理的实际生效原理

代理方法直接上是通过对使用对象的做了一个封装之后,别的所有引入都是通过引用了代理类从而实现走了代理的流程的。那么是否内部调用就没有办法走代理的流程了呢?答案当然是否定的,其实只要在内部调用代理类的方法就可以了(虽然最终实现结果看起来有点奇怪),至于在spring中的实现可以参考之前写的这篇文章AOP切面时BeanPostProcessor返回Bean未被CGlib代理

后记

都看到这里,不点进项目链接里面看看么?(逃~)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容