Spring service with cacheable methods gets initialized without cache when autowired in Shiro realm

两盒软妹~` 提交于 2019-12-04 10:44:21
chk

I finally figured out what the problem is and can at least explain its cause in more detail, even though my proposed solution is still a bit hacky.

Enabling the caching aspect in Spring introduces a org.springframework.cache.interceptor.CacheInterceptor, which is essentially an org.aopalliance.aop.Advice used by a org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor that implements org.springframework.aop.Advisor.

The org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor I introduced for Shiro is another Advisor which transitively depends on the DateService via DefaultSecurityManager and MyRealm.

So I have two Advisors for two different aspects - Caching and Security - of which the one for security is initialized first. In fact, whenever I introduce any Advisor dependent on DateService - even if its only a dummy implementation as in the following example - the caching doesn't work anymore for the same reason as it was broken when adding Shiro. This causes the DateService to be loaded before the caching aspect is ready, so it cannot be applied.

@Bean
@Autowired
public Advisor testAdvisor(DateService dateService) {
    return new StaticMethodMatcherPointcutAdvisor() {
        @Override
        public boolean matches(Method method, Class<?> targetClass) {
            return false;
        }
    };
}

Hence, the only proper fix for that is to change the order of aspect initialization. I am aware of the @Order(Ordered.LOWEST_PRECEDENCE) respectively @Order(Ordered.HIGHEST_PRECEDENCE) annotation for the case the multiple Advisors are applicable at a specific joinpoint, but this is not the case for me so this doesn't help. The order of initialization matters for other reasons.

Adding the following code in DateServiceImpl actually solves the problem:

@Autowired
BeanFactoryCacheOperationSourceAdvisor waitForCachingAspect;

With that, the service always waits for the cache before it can be initialized even though this dependency is not used anywhere in the implementation. So now everything is working as it should because the dependency tree now includes Shiro --> DateService --> Cache which makes the Shiro Advisor wait long enough.

It is still not as nice and clean as I would like it to be, but nevertheless, I think this explanation helps to understand the core of the problem and "How can I change the order in which Advisors are initialized in Spring" is a separate question I posted here.

Since Spring 4, @Lazy can be used to achieve the same behavior as in the original question in a more declarative way (see Spring 4 JavaDoc and compare it with earlier versions).

Tested this and it works.

@Component
public class MyRealm extends AuthenticatingRealm {
    private static final String REALM_NAME = "MyRealm";
    @Autowired
    @Lazy
    private DateService dateService;

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("User authenticated at "+dateService.getCurrent());
        return new SimpleAuthenticationInfo("",token.getCredentials(),REALM_NAME);
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!