在使用Spring时,很多初学者不了解Spring对象注入的机制和面向切面编程的原理,很容易犯一些错误。下面就是初学者最容易犯的错误。举例如下:
@Component public class TestClass {
|
这里TestClass类里同时启用了Scheduled注解和@Cacheable注解。如果在外面其他类中调用getData(...)时,缓存机制会生效,但是在task函数内部调用getData(...)时,缓存机制不会生效。网上有人把这归纳为:不能在同一个类中互相调用注解过的方法,否则注解失效。
为什么会有这样的差别呢?背后的原因就是Spring的对象注入机制。
当外部通过@Autowired注解得到一个TestClass对象时,其实得到的是一个Spring包装过的代理对象。如下图所示。
当调用Obj.getData时,实际调用的是Spring的Proxy对象中的getData方法,该方法内置了Cache机制,在Cache检查后就会调用实际的TestClass对象中的getData方法。
同理通过@Scheduled注解表示这是一个任务调度时,Spring Proxy对象中会初始化对应的调度线程池等工作,当触发调度条件时,再调用实际的TestClass对象。
但是当在实际的TestClass对象中再调用getData时,不会触发Cache机制,因为此时不是调用的SpringCacheProxy对象,而是一个实际的TestClass对象,所以不会触发Cache机制。
如果大家深入去读Spring完成注入和AOP编程实现的原理,可以发现动态代理是很重要的一个技术。目前Spring的动态代理主要是通过CGLib来实现的。后面我会再写CGLib的实现思路。
那么我们该如何避免出现上述问题呢?
首先我们应该牢记一个原则:同一个类中的注解方法互相调用时,注解机制可能是无效的。
对于上述示例,我们可以把其拆分为两个类来实现,一个类完成任务调度、一个类完成Cache机制。这样在任务调度中调用的是Spring实现了Cache机制的代理类,可以确保其Cache机制生效。