Spring aop 内部调用、自调用不生效问题与解决方案
2016-07-19 10:38
447 查看
场景
使用 spring cache 框架时 服务类内部方法调用并不触发缓存动作演示
[[@Service](http://my.oschina.net/service)](http://my.oschina.net/service) public class CacheTestService { @Cacheable(value = "test_cache", key = "'method'") public String method() { System.out.println("method"); return method1(1) + "_" + method2(2) + "_" + method3(3) + "_" + method4(4) ; } @Cacheable(value = "test_cache", key = "'method'+#i") public String method1(int i) { System.out.println("method1"); return "method1_" + i; } @Cacheable(value = "test_cache", key = "'method'+#i") protected String method2(int i) { System.out.println("method2"); return "method2_" + i; } @Cacheable(value = "test_cache", key = "'method'+#i") String method3(int i) { System.out.println("method3"); return "method3_" + i; } @Cacheable(value = "test_cache", key = "'method'+#i") private String method4(int i) { System.out.println("method4"); return "method4_" + i; } }
查看redis
127.0.0.1:6379> keys cache:* 1) "cache://test_cache:method"
可以看到 method1、method2、method3、method4 方法的缓存并没有生效
原因:
注意和限制
基于 proxy 的 spring aop 带来的内部调用问题
上面介绍过 spring cache 的原理,即它是基于动态生成的 proxy 代理机制来对方法的调用进行切面,这里关键点是对象的引用问题,如果对象的方法是内部调用 (即 this 引用)而不是外部引用,则会导致 proxy 失效,那么我们的切面就失效,也就是说上面定义的各种注释包括 @Cacheable、@CachePut 和 @CacheEvict 都会失效
解决方案
public interface BeanSelfAware { void setSelf(Object proxyBean); }
@Component public class InjectBeanSelfProcessor implements BeanPostProcessor, ApplicationContextAware { private ApplicationContext context; //① 注入ApplicationContext public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(!(bean instanceof BeanSelfAware)) { //② 如果Bean没有实现BeanSelfAware标识接口 跳过 return bean; } if(AopUtils.isAopProxy(bean)) { //③ 如果当前对象是AOP代理对象,直接注入 ((BeanSelfAware) bean).setSelf(bean); } else { //④ 如果当前对象不是AOP代理,则通过context.getBean(beanName)获取代理对象并注入 //此种方式不适合解决prototype Bean的代理对象注入 ((BeanSelfAware)bean).setSelf(context.getBean(beanName)); } return bean; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } }
服务类:
@Service public class CacheTestService implements BeanSelfAware { @Cacheable(value = "test_cache", key = "'method'") public String method() { System.out.println("method"); return proxySelf.method1(1) + "_" + proxySelf.method2(2) + "_" + proxySelf.method3(3) + "_" + proxySelf.method4(4) ; } @Cacheable(value = "test_cache", key = "'method'+#i") public String method1(int i) { System.out.println("method1"); return "method1_" + i; } @Cacheable(value = "test_cache", key = "'method'+#i") protected String method2(int i) { System.out.println("method2"); return "method2_" + i; } @Cacheable(value = "test_cache", key = "'method'+#i") String method3(int i) { System.out.println("method3"); return "method3_" + i; } @Cacheable(value = "test_cache", key = "'method'+#i") private String method4(int i) { System.out.println("method4"); return "method4_" + i; } CacheTestService proxySelf; @Override public void setSelf(Object proxyBean) { this.proxySelf = (CacheTestService) proxyBean; } }
再次查看 redis
127.0.0.1:6379> keys cache:* 1) "cache://test_cache:method1" 2) "cache://test_cache:method"
方法 method1 缓存动作成功执行,但是以protected、默认、private修饰的方法签名并没有生效 因此 此方案还需注意使用public修饰被调用方法
...
坑
骚年,你以为这样就完了? 不 还有坑!启用 aop 时 请使用
<!-- 用这个 --> <aop:aspectj-autoproxy proxy-target-class="true" /> <!-- 不要用这个 --> <!--<aop:config proxy-target-class="true" />-->
over
参考资料
http://jinnianshilongnian.iteye.com/blog/1487235http://jinnianshilongnian.iteye.com/blog/1901694
https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/
相关文章推荐
- Java 匿名内部类的详解
- 自行实现Map底层结构(数组+链表) --Java版
- Java BigDecimal详解
- JAVA中堆和栈的理解
- Java流结构
- eclipse JDK 版本错误
- Java静态变量思考
- SPRING中的线程池ThreadPoolTaskExecutor
- 将List转为Map 如List<javaBean>转为Map<String,javaBean>
- Java static静态类处理和MAP取键值
- reorder-list java
- Spring注入方式实现AOP
- Java 设计模式-单例
- 部门树状图用JAXB解决
- springmvc拦截器处理
- struts2工作原理
- Java--泛型的原理以及使用场景
- Spring+SpringMVC+Mybatis 多数据源整合
- spring mvc 创建 rest api
- Java并发编程 - 逐级深入 看线程的中断