Skip to content

Spring Integration gateways don't trigger @ConditionalOnBean #2811

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

Closed
chrylis opened this issue Apr 13, 2015 · 21 comments
Closed

Spring Integration gateways don't trigger @ConditionalOnBean #2811

chrylis opened this issue Apr 13, 2015 · 21 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@chrylis
Copy link
Contributor

chrylis commented Apr 13, 2015

I have an MVC controller bean that's conditional on the registration of a bean implementing a handler interface. I'm using a Spring Integration service gateway to implement the interface and importing an XML integration plan in my own autoconfiguration class, and the debug log shows

Loading XML bean definitions from class path resource [META-INF/spring/integration/image-processing/image-processing-gateways.xml]

very early in the loading process, but the condition does not see the service gateway as a matching bean. I have explicitly listed my application's configuration as @AutoConfigureBefore the class that needs the bean, but it still doesn't match. Using @DependsOn and explicitly supplying a bean ID for the gateway also does not work.

@philwebb
Copy link
Member

@ConditionalOnBean annotations can be very picky when it comes to the order that beans are defined and I'm not sure that we have tested them with XML imports in the mix. Can you share a project to demonstrate the issue?

@philwebb philwebb added the status: waiting-for-feedback We need additional information before we can continue label Apr 13, 2015
@chrylis
Copy link
Contributor Author

chrylis commented Apr 14, 2015

I'll try to figure out a minimal reproduction.

@chrylis
Copy link
Contributor Author

chrylis commented Apr 14, 2015

@ConditionalOnBean annotations can be very picky when it comes to the order that beans are defined

I've very much noticed this, and it's especially glaring when the autoconfiguration report shows negative matches with matching beans. I know the dependency algorithms are hairy, but is there any likelihood of a refactor that will reduce the ordering gotchas, or is that performance-prohibitive? Example turning on @ComponentScan for a completely unrelated package results in this:

   SpringBoot2811AutoConfiguration
      - @ConditionalOnBean (types: com.chrylis.sprboot2811.NumberPrinter; SearchStrategy: all) found no beans (OnBeanCondition)
      - @ConditionalOnBean (types: com.chrylis.sprboot2811.NumberPrinter; SearchStrategy: all) found the following [anonymousPrinter] (OnBeanCondition)

@chrylis
Copy link
Contributor Author

chrylis commented Apr 14, 2015

Reproduction here: https://github.com/chrylis/spring-boot-2811

I have an autoconfig class providing a bean through Spring Integration that explicitly indicates configuration before the class with the condition on that bean. Condition doesn't match, the second autoconfiguration doesn't activate, and its bean definitions don't get registered. Flipping on a profile that registers a second matching bean causes the condition to match and then throws on duplicate bean. (Commenting out the condition makes everything resolve and work correctly.)

@wilkinsona wilkinsona removed the status: waiting-for-feedback We need additional information before we can continue label Apr 14, 2015
@wilkinsona
Copy link
Member

Thanks for the reproduction.

At the time that the @ConditionalOnBean(NumberPrinter.class) is evaluated, the definition for the gateway printer is available, but its type is determined to be java.lang.Object which prevents the condition from matching. I don't know why yet.

A workaround is to change the condition to match the bean by name: @ConditionalOnBean(name="gatewayPrinter")

@wilkinsona
Copy link
Member

The gatewayPrinter bean is a Spring Integration GatewayProxyFactoryBean. It's typed as a FactoryBean<Object>. In the absence of factoryBeanObjectType attribute on the bean definition, Boot's best guess is that the bean produced by the factory will be a java.lang.Object.

@wilkinsona
Copy link
Member

I'm not sure why we aren't calling BeanFactory.getType() if we fail to determine the type via generics or the factoryBeanObjectType attribute. Doing so fixes this problem but it may be prohibitive from a performance perspective. /cc @philwebb

@chrylis
Copy link
Contributor Author

chrylis commented Apr 14, 2015

In this specific instance, should Spring Integration be setting the factoryBeanObjectType in the namespace handler?

@wilkinsona
Copy link
Member

I discussed that with @garyrussell and @artembilan earlier today and they're happy to make that change if necessary. However, it'd only be a fix for this specific scenario. Calling BeanFactory.getType() would be a more general purpose solution if it doesn't cause any unwanted side effects.

@chrylis
Copy link
Contributor Author

chrylis commented Apr 15, 2015

I'm not familiar enough with the internals of bean-providing classes to know, but what I'm asking is whether setting that attribute is just incidentally helpful or the Right Thing that ought to happen anyway.

@wilkinsona
Copy link
Member

It entirely depends on whether or not we update Boot to call BeanFactory.getType()

@philwebb
Copy link
Member

Unfortunately we can't call BeanFactory.getType() because it causes early initialization of the factory beans. I think we did try this approach initially but it cause all sorts of problems.

is there any likelihood of a refactor that will reduce the ordering gotchas

I can't really see what we can do to improve the situation unfortunately. The general rule that we use internally is @ConditionOnBean (or @ConditionOnMissingBean) can only be used in a auto-configuration class, and if it tests for a bean created by another auto-configuration the @AutoConfigureAfter or @AutoConfigureBefore annotations must be used to guarantee order.

@chrylis
Copy link
Contributor Author

chrylis commented Apr 16, 2015

I can't really see what we can do to improve the situation unfortunately. The general rule that we use internally is @ConditionOnBean (or @ConditionOnMissingBean) can only be used in a auto-configuration class, and if it tests for a bean created by another auto-configuration the @AutoConfigureAfter or @AutoConfigureBefore annotations must be used to guarantee order.

  • Does "created by another auto-configuration" include beans created in imported XML files?
  • @AutoConfigureBefore is being used. I understand that the ordering instructions may be necessary, and I tried adding them before opening this issue.
  • The specific behavior also manifested when importing the XML from a non-auto class.

@snicoll
Copy link
Member

snicoll commented Apr 16, 2015

If the file has been imported in an auto-configuration, yes.

@chrylis
Copy link
Contributor Author

chrylis commented Apr 16, 2015

What this all seems like to me is that the namespace handler for int:service-gateway is buggy--possibly unfixably buggy for now, but buggy nonetheless. The whole purpose of the tag is to provide an implementation of a specific interface, and the magic ought to be able to communicate to the context that type. It doesn't have to do any analysis or resolution; it's right there in the tag.

@wilkinsona
Copy link
Member

BeanTypeRegistry currently only supports factoryBeanObjectType attributes that are Class instances. This isn't good for namespace handlers as they should, where possible, avoid class loading. BeanTypeRegistry should be updated to also support String attributes.

wilkinsona added a commit that referenced this issue Apr 16, 2015
To allow us to determine the type that Spring Integration’s
GatewayProxyFactoryBean will create, the bean definition created by
MessagingGatewayRegistrar needs to set the factoryBeanObjectType
attribute. The current implementation of BeanTypeRegistry requires the
attribute’s value to be a Class, however this would require Spring
Integration’s namespace handler to load the class and class loading
should be avoided in namespace handlers.

This commit updates BeanTypeRegistry so that it supports both Class and
String values for the factoryBeanObjectType. If the value is a String
it will interpret it as a class name and attempt to load it.

See gh-2811
@garyrussell
Copy link
Contributor

Note that the solution of populating the hint in the bean definition attribute will only work as long as the service-interface attribute does not contain a property placeholder. While this may be unlikely, it is possible.

Perhaps code could be added to BeanTypeRegistry.determineTypeFromDefinitionAttribute() to resolve placeholders/SpEL expressions before invoking ClassUtils.forName() ?

@wilkinsona wilkinsona added this to the 1.2.4 milestone May 15, 2015
@wilkinsona wilkinsona self-assigned this May 15, 2015
@wilkinsona
Copy link
Member

Spring Integration 4.1.4 has been released so we can complete the fix for this by upgrading

@wilkinsona wilkinsona added the type: bug A general bug label May 15, 2015
bsodzik pushed a commit to bsodzik/spring-boot that referenced this issue May 23, 2015
To allow us to determine the type that Spring Integration’s
GatewayProxyFactoryBean will create, the bean definition created by
MessagingGatewayRegistrar needs to set the factoryBeanObjectType
attribute. The current implementation of BeanTypeRegistry requires the
attribute’s value to be a Class, however this would require Spring
Integration’s namespace handler to load the class and class loading
should be avoided in namespace handlers.

This commit updates BeanTypeRegistry so that it supports both Class and
String values for the factoryBeanObjectType. If the value is a String
it will interpret it as a class name and attempt to load it.

See spring-projectsgh-2811
bsodzik pushed a commit to bsodzik/spring-boot that referenced this issue May 23, 2015
@akibandali
Copy link

After defining both beans like below it works. But do we have some other good solution.
@MockBean(name = "messageRepository")
private MessageRepository messageRepository;

@MockBean(name = "messageScopeRepository")
private MessageRepository messageScopeRepository;

@wilkinsona
Copy link
Member

@akibandali I can't see the connection between @MockBean, which was introduced in Spring Boot 1.4.0, and this issue which was fixed in Spring Boot 1.2.x. If you believe you have found a bug, please open a new issue and provide a minimal sample that reproduces the problem. If you're looking for some guidance, please ask a question on Stack Overflow.

@akibandali
Copy link

@wilkinsona - Yes I will open a case.
And I asked the question on stack over flow->
https://stackoverflow.com/questions/75182337/define-couchbase-collection-name-based-on-property

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

6 participants