Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Programmatic creation of caching proxies using CacheProxyFactoryBean does not work [SPR-16295] #20842

Closed
spring-issuemaster opened this issue Dec 12, 2017 · 3 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Copy link
Collaborator

commented Dec 12, 2017

John Blum opened SPR-16295 and commented

When a user attempts to create caching proxies using the o.s.cache.interceptor.CacheProxyFactoryBean, this fails to work.

A user might want to use the CacheProxyFactoryBean to introduce caching behavior and logic to classes from a 3rd party library. This recently came up in a StackOverflow post. Please read the post and the answers (especially this one) for further details.

Ideally, a user would be able to add caching behavior to any class, programmatically (not declaratively) using...

@Bean
CacheProxyFactoryBean someBean() {

    CacheProxyFactoryBean factoryBean = new CacheProxyFactoryBean();

    factoryBean.setCacheOperationSources(...);
    factoryBean.setTarget(new ThirdPartyClass());

    return factoryBean;
}

Unfortunately, this does not work because the internal CacheInterceptor's afterSingletonesInitialized() method never gets called, thereby setting the initialized bit and therefore allowing the caching behavior/logic to be evaluated. As result, the target object's original operation is always invoked, forgoing any caching behavior, making any caching proxy configuration useless.

This can be fixed by having CacheProxyFactoryBean implement the SmartInitializingSingleton interface along with BeanFactoryAware and delegating said operations to the internal CacheInterceptor.

See Pull Request for fix.


Affects: 4.3.13, 5.0.2

Reference URL: https://stackoverflow.com/questions/47665485/how-can-i-manually-add-a-spring-cacheinterceptor-using-java-config

Issue Links:

  • #16941 @EnableCaching provokes early initialization of any config class declaring a CacheManager

Referenced from: pull request #1624, and commits b160f93, d53ede9

Backported to: 4.3.14

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Dec 12, 2017

John Blum commented

Also, as I pointed out in my post on StackOverflow, another way to create programmatic caching proxies, is to declare Spring's ProxyFactory and CacheInterceptor as @Bean definitions in the Spring @Configuration class, like so...

@Configuration
class ApplicationConfiguration {

    @Bean
    CacheInterceptor cacheInterceptor(CacheManager cacheManager) {

        CacheInterceptor cacheInterceptor = new CacheInterceptor();

        cacheInterceptor.setCacheManager(cacheManager);
        cacheInterceptor.setCacheOperationSources(...);

        return cacheInterceptor;
    }

    @Bean
    Object thirdPartyObject(ConfigurableListableBeanFactory beanFactory, 
            CacheInterceptor cacheInterceptor) {

        ProxyFactory proxyFactory = new ProxyFactory();

        proxyFactory.addAdvisor(new DefaultPointcutAdvisor(cacheInterceptor));
        proxyFactory.setInterfaces(ThirdParty.class);
        proxyFactory.setTarget(new ThirdParty());

        return proxyFactory.getProxy(beanFactory.getBeanClassLoader());
    }
}

This is covered in Spring Framework's Reference Documentation, "6.7 - Creating AOP proxies programmatically with the ProxyFactory". Conveniently, a user could use the o.s.aop.framework.ProxyFactoryBean instead of ProxyFactory, directly.

Still, using the o.s.cache.interceptor.CacheProxyFactoryBean is far more convenient and consistent with Spring's policy on complex instantiation and initialization logic nicely encapsulated with a FactoryBean.

One example of alternative methods for programmatically creating caching proxies, for possibly 3rd party library/framework classes, can be found here.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Dec 12, 2017

John Blum commented

Note: I closed my first PR #1623 in favor of PR #1624.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 14, 2018

Juergen Hoeller commented

CacheProxyFactoryBean is quite a stepchild here: This class essentially never worked (in particular not since #16941), and nobody seems to have noticed and complained... Since I still consider it a rather secondary configuration variant, I went with the simplest possible revision that does the job: For 4.3.14, simply delegating BeanFactoryAware and SmartInitializatingSingleton; for 5.0.3, also adding the three additional setters from CacheInterceptor for common configuration instead of individual operation settings.

For simplicity's sake, we're just doing integration testing (since that's all that really matters here) and without any custom configuration adaptation in CacheProxyFactoryBean itself, both of which significantly streamline the implementation and make it straightforward to backport. This also keeps things very aligned with TransactionProxyFactoryBean, the original inspiration behind our class here. For those reasons, I went with a custom variant instead of your PR, John. Thanks for pointing this out and for the tests, in any case!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.