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

Avoid duplicate JCacheOperationSource bean registration in <cache:annotation-driven /> #27499

Closed
wants to merge 1 commit into from

Conversation

nivolg
Copy link
Contributor

@nivolg nivolg commented Sep 30, 2021

Hello.

In our application we use XML context and <cache:annotation-driven mode="aspectj"/> declaration in combination with JCache API. Also we disable bean definition overrides by setting GenericApplicationContext.setAllowBeanDefinitionOverriding(false) in an ApplicationContextInitializer.

Such combination leads to BeanDefinitionOverrideException because the bean "org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource" is registered twice:

String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
parserContext.registerBeanComponent(new BeanComponentDefinition(sourceDef, sourceName));

In our application we use xml context and <cache:annotation-driven /> declaration. 
Also we disable bean definition duplication by setting GenericApplicationContext.setAllowBeanDefinitionOverriding(false) in ApplicationContextInitializer.
Such combination leads to BeanDefinitionOverrideException because bean "org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource" is registered twice: 
				String sourceName =
						parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

				parserContext.registerBeanComponent(new BeanComponentDefinition(sourceDef, sourceName));
@pivotal-cla
Copy link

@nivolg Please sign the Contributor License Agreement!

Click here to manually synchronize the status of this Pull Request.

See the FAQ for frequently asked questions.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Sep 30, 2021
@nivolg
Copy link
Contributor Author

nivolg commented Sep 30, 2021

@pivotal-cla This is an Obvious Fix

@pivotal-cla
Copy link

@nivolg This Pull Request contains an obvious fix. Signing the Contributor License Agreement is not necessary.

@snicoll
Copy link
Member

snicoll commented Oct 1, 2021

Thanks for the report and PR @nivolg. I wasn't able to reproduce the problem with a vanilla project started from start.spring.io. Please look at it and let us know what is missing.

If it turns out that a change is required in Spring Framework, I unfortunately disagree that this is an obvious fix so you'll have to sign the CLA.

@snicoll snicoll added the status: waiting-for-feedback We need additional information before we can continue label Oct 1, 2021
@pivotal-cla
Copy link

@nivolg Thank you for signing the Contributor License Agreement!

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Oct 1, 2021
@nivolg
Copy link
Contributor Author

nivolg commented Oct 1, 2021

Thanks for quick response. I can't access your vanilla project repopsitory. But I made example project to reproduce the issue. I missed an important details in description: aspectj and jcache are used together.

Also I signed CLA.

@snicoll
Copy link
Member

snicoll commented Oct 1, 2021

@nivolg bummer, it got created as private by default, sorry about that. Thank you for the sample, I missed the aspectJ bits.

@sbrannen
Copy link
Member

sbrannen commented Oct 1, 2021

Thanks for the repro project.

I have confirmed that the following exception occurs.

Exception in thread "main" org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource#0' defined in null: Cannot register bean definition [Root bean: class [org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] for bean 'org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource#0': There is already [Root bean: class [org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] bound.
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:995)
	at org.springframework.beans.factory.support.BeanDefinitionReaderUtils.registerBeanDefinition(BeanDefinitionReaderUtils.java:164)
	at org.springframework.beans.factory.xml.ParserContext.registerBeanComponent(ParserContext.java:125)
	at org.springframework.cache.config.AnnotationDrivenCacheBeanDefinitionParser$JCacheCachingConfigurer.registerCacheAspect(AnnotationDrivenCacheBeanDefinitionParser.java:256)
	at org.springframework.cache.config.AnnotationDrivenCacheBeanDefinitionParser$JCacheCachingConfigurer.access$100(AnnotationDrivenCacheBeanDefinitionParser.java:206)
	at org.springframework.cache.config.AnnotationDrivenCacheBeanDefinitionParser.registerCacheAspect(AnnotationDrivenCacheBeanDefinitionParser.java:100)
	at org.springframework.cache.config.AnnotationDrivenCacheBeanDefinitionParser.parse(AnnotationDrivenCacheBeanDefinitionParser.java:87)
	at org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse(NamespaceHandlerSupport.java:74)
	at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1391)
	at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1371)
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:179)
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:149)
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:96)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:511)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:391)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:338)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:310)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:224)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:195)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:257)
	at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:128)
	at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:94)
	at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
	at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:671)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553)

But I have not yet confirmed the proposed fix.

We'll look into this.

@sbrannen sbrannen added in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on status: feedback-provided Feedback has been provided labels Oct 1, 2021
@sbrannen sbrannen added this to the 5.3.11 milestone Oct 1, 2021
@sbrannen sbrannen self-assigned this Oct 11, 2021
@@ -245,16 +245,20 @@ private static void registerCacheAdvisor(Element element, ParserContext parserCo
private static void registerCacheAspect(Element element, ParserContext parserContext) {
if (!parserContext.getRegistry().containsBeanDefinition(CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME)) {
Object eleSource = parserContext.extractSource(element);

BeanDefinition sourceDef = createJCacheOperationSourceBeanDefinition(element, eleSource);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
String sourceName = parserContext.getReaderContext().generateBeanName(sourceDef);

This would be a simpler fix, since it avoids the undesired side effect of registering the bean when generating the name (and the rest of the code can remain unchanged), but I think your CompositeComponentDefinition proposal is more in line with how components are handled for XML namespaces. So we'll go with that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for review. I understand what you mean.
I thought about such simpler fix but read the methods above and decided that CompositeComponentDefinition is required for some reasons I don't know.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CompositeComponentDefinition is not technically required.

It's really just there for tooling support. Without it, the application would work the same.

@sbrannen sbrannen changed the title Avoid bean name duplication in <cache:annotation-driven /> definition parser Avoid duplicate JCacheOperationSource bean registration in <cache:annotation-driven /> Oct 12, 2021
sbrannen added a commit that referenced this pull request Oct 12, 2021
@sbrannen sbrannen added the for: backport-to-5.2.x Marks an issue as a candidate for backport to 5.2.x label Oct 12, 2021
@spring-projects-issues spring-projects-issues added status: backported An issue that has been backported to maintenance branches and removed for: backport-to-5.2.x Marks an issue as a candidate for backport to 5.2.x labels Oct 12, 2021
@sbrannen sbrannen closed this in 50ccb1b Oct 12, 2021
@sbrannen
Copy link
Member

This has been merged in 50ccb1b and revised in 5bd9053.

Applied to 5.3.x and main (6.0); to be backported to 5.2.x as well (#27547).

Thanks

sbrannen pushed a commit to sbrannen/spring-framework that referenced this pull request Oct 12, 2021
…otation-driven />

In our application we use XML context and <cache:annotation-driven />
declaration. Also we disable bean definition duplication by setting
GenericApplicationContext.setAllowBeanDefinitionOverriding(false) in an
ApplicationContextInitializer. This combination leads to a
BeanDefinitionOverrideException because the
DefaultJCacheOperationSource bean is registered twice.

 - once for: parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
 - once for: parserContext.registerBeanComponent(new BeanComponentDefinition(sourceDef, sourceName));

This commit refactors JCacheCachingConfigurer.registerCacheAspect(...)
so that the JCacheOperationSource bean is registered only once.

Closes spring-projectsgh-27499
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

Successfully merging this pull request may close these issues.

None yet

5 participants