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-projects-issues opened this issue Dec 12, 2017 · 3 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues 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:

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

Backported to: 4.3.14

@spring-projects-issues
Copy link
Collaborator Author

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-projects-issues
Copy link
Collaborator Author

John Blum commented

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

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues 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!

@spring-projects-issues spring-projects-issues added type: bug A general bug status: backported An issue that has been backported to maintenance branches in: core Issues in core modules (aop, beans, core, context, expression) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 5.0.3 milestone Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants