Skip to content

Revise configuration code for better Spring Native support #3502

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
artembilan opened this issue Mar 4, 2021 · 8 comments · Fixed by #3531 or #3532
Closed

Revise configuration code for better Spring Native support #3502

artembilan opened this issue Mar 4, 2021 · 8 comments · Fixed by #3531 or #3532

Comments

@artembilan
Copy link
Member

See more info in Spring Native.

At the moment it is important to revise ImportSelector impls and class presence checks.

Related to: spring-projects/spring-framework#18353

@artembilan
Copy link
Member Author

artembilan commented Mar 26, 2021

        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:83) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1308) ~[na:na]
        ... 17 common frames omitted
Caused by: java.lang.NoSuchMethodException: org.springframework.integration.config.IntegrationConfigurationBeanFactoryPostProcessor.<init>()

Looks like Spring requires explicit public default ctor for that type of beans in Native mode:

		if (NativeDetector.inNativeImage()) {
			this.instantiationStrategy = new SimpleInstantiationStrategy();
		}
		else {
			this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();
		}

@artembilan
Copy link
Member Author

Another more obvious reflection problem:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.springframework.integration.config.SourcePollingChannelAd
apterFactoryBean.<init>()
        at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:154) ~[na:na]
        at org.springframework.integration.dsl.EndpointSpec.<init>(EndpointSpec.java:60) ~[na:na]
        at org.springframework.integration.dsl.SourcePollingChannelAdapterSpec.<init>(SourcePollingChannelAdapterSpec.java:32) ~[na:na]
        at org.springframework.integration.dsl.IntegrationFlows.from(IntegrationFlows.java:203) ~[na:na]
        at org.springframework.integration.dsl.IntegrationFlows.from(IntegrationFlows.java:195) ~[na:na]
        at org.springframework.integration.dsl.IntegrationFlows.fromSupplier(IntegrationFlows.java:166) ~[na:na]
        at org.springframework.integration.demonative.DemoNativeApplication.printSecondsFlow(DemoNativeApplication.java:20) ~[org.springframework.integration.demonative.DemoNativeApplication:na]
        at java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[na:na]
        ... 19 common frames omitted
Caused by: java.lang.NoSuchMethodException: org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean.<init>()
        at java.lang.Class.getConstructor0(DynamicHub.java:3349) ~[na:na]
        at java.lang.Class.getDeclaredConstructor(DynamicHub.java:2553) ~[na:na]
        at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147) ~[na:na]

where the code is like this:

protected EndpointSpec(H handler) {
	Class<?> fClass = ResolvableType.forClass(this.getClass()).as(EndpointSpec.class).resolveGenerics()[1];
	this.endpointFactoryBean = (F) BeanUtils.instantiateClass(fClass);
	this.handler = handler;
}

so, need to figure out how to avoid reflection over there or find out the way to teach it to work with Native image...

@garyrussell
Copy link
Contributor

@artembilan
Copy link
Member Author

Yeah... Better to say this: https://docs.spring.io/spring-native/docs/current-SNAPSHOT/reference/htmlsingle/#how-to-contribute to align with whatever is the latest.

@artembilan
Copy link
Member Author

Reopen: the work is not done yet.

@artembilan artembilan reopened this Mar 30, 2021
@artembilan
Copy link
Member Author

GroovyScriptExecutingMessageProcessor:

		GroovyObject goo = (GroovyObject) BeanUtils.instantiateClass(this.scriptClass);

MapToObjectTransformer:

Object target = (this.targetClass != null)
				? BeanUtils.instantiateClass(this.targetClass)
				: this.getBeanFactory().getBean(this.targetBeanName);

Or even JmsChannelFactoryBean:

AbstractMessageListenerContainer container = BeanUtils.instantiateClass(this.containerType);

and respective Java DSL:

protected JmsListenerContainerSpec(Class<C> aClass) {
		super(BeanUtils.instantiateClass(aClass));

Well, technically every single place where we indeed rely on the reflection at runtime.
This probably can be handled only via @NativeHint in Spring Native project.

@artembilan
Copy link
Member Author

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'errorChannel': Unexpected exception during bean creation; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.Spel
EvaluationException: EL1005E: Type cannot be found 'org.springframework.integration.context.IntegrationContextUtils'
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:537) ~[na:na]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[na:na]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[na:na]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[na:na]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[na:na]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[na:na]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[na:na]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[na:na]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:774) ~[org.springframework.integration.demonative.DemoNativeApplication:2.5.0-SNAPSHOT]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:443) ~[org.springframework.integration.demonative.DemoNativeApplication:2.5.0-SNAPSHOT]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:341) ~[org.springframework.integration.demonative.DemoNativeApplication:2.5.0-SNAPSHOT]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[org.springframework.integration.demonative.DemoNativeApplication:2.5.0-SNAPSHOT]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1341) ~[org.springframework.integration.demonative.DemoNativeApplication:2.5.0-SNAPSHOT]
        at org.springframework.integration.demonative.DemoNativeApplication.main(DemoNativeApplication.java:15) ~[org.springframework.integration.demonative.DemoNativeApplication:na]
Caused by: org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1005E: Type cannot be found 'org.springframework.integration.context.IntegrationContextUtils'
        at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:169) ~[na:na]
        at org.springframework.integration.config.DefaultConfiguringBeanFactoryPostProcessor.resolveExpression(DefaultConfiguringBeanFactoryPostProcessor.java:595) ~[na:na]
        at org.springframework.integration.config.DefaultConfiguringBeanFactoryPostProcessor.createErrorChannel(DefaultConfiguringBeanFactoryPostProcessor.java:283) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1231) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1173) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[na:na]
        ... 13 common frames omitted
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1005E: Type cannot be found 'org.springframework.integration.context.IntegrationContextUtils'
        at org.springframework.expression.spel.support.StandardTypeLocator.findType(StandardTypeLocator.java:117) ~[na:na]
        at org.springframework.expression.spel.ExpressionState.findType(ExpressionState.java:155) ~[na:na]
        at org.springframework.expression.spel.ast.TypeReference.getValueInternal(TypeReference.java:69) ~[na:na]
        at org.springframework.expression.spel.ast.CompoundExpression.getValueRef(CompoundExpression.java:55) ~[na:na]
        at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:91) ~[na:na]
        at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:112) ~[na:na]
        at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:272) ~[na:na]
        at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:166) ~[na:na]
        ... 19 common frames omitted

We probably can figure out something on the framework level how to be with this and similar SpEL expressions we use for out-of-the-box and support components, but it is definitely has to be somehow addressed in the target projects for end-user expressions and their types. Probably the same @NativeHint can help but already in the end-user code...

artembilan added a commit to artembilan/spring-integration that referenced this issue Apr 1, 2021
artembilan added a commit to artembilan/spring-integration that referenced this issue Apr 1, 2021
Fixes spring-projects#3502

* Move `ChannelInitializer` bean registration into an `AbstractIntegrationNamespaceHandler` -
it was never used for annotations and Java DSL...
* Rework `IntegrationFlows.fromSupplier()` to call a provided `Supplier` directly -
not via reflection in the `MethodInvokingMessageSource`
* Resolve new Sonar smells
artembilan added a commit to artembilan/spring-integration that referenced this issue Apr 1, 2021
Fixes spring-projects#3502

* Move `ChannelInitializer` bean registration into an `AbstractIntegrationNamespaceHandler` -
it was never used for annotations and Java DSL...
* Rework `IntegrationFlows.fromSupplier()` to call a provided `Supplier` directly -
not via reflection in the `MethodInvokingMessageSource`
* Resolve new Sonar smells
* Rework `EndpointSpec` to accept an expected factory bean instance via ctor arg
instead of reflection
* Rework `Jackson2JsonObjectMapper` to use well-known module instances directly -
not via reflection from their class names
@artembilan artembilan modified the milestones: 6.0.x, 5.5.0-RC1 Apr 1, 2021
@artembilan
Copy link
Member Author

I'm moving this issue back to 5.5 with more priority at the moment since there is a demand from the community.

With the latest PR the basic app works well.
We probably need to go over a basic Spring Cloud Stream app with this solution to be sure where we are with the rest of reflection in the framework

garyrussell pushed a commit that referenced this issue Apr 6, 2021
* GH-3502: More refactoring to avoid reflection

Fixes #3502

* Move `ChannelInitializer` bean registration into an `AbstractIntegrationNamespaceHandler` -
it was never used for annotations and Java DSL...
* Rework `IntegrationFlows.fromSupplier()` to call a provided `Supplier` directly -
not via reflection in the `MethodInvokingMessageSource`
* Resolve new Sonar smells
* Rework `EndpointSpec` to accept an expected factory bean instance via ctor arg
instead of reflection
* Rework `Jackson2JsonObjectMapper` to use well-known module instances directly -
not via reflection from their class names

* * Revert `DefaultMethodInvokingMethodInterceptor.methodHandleCache` property definition wrap
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment