spring-cloud / spring-cloud-function

Apache License 2.0
1.03k stars 614 forks source link

Decouple FunctionExporter from webflux dependency #459

Open aaron-benson-on-code opened 4 years ago

aaron-benson-on-code commented 4 years ago

Describe the bug Following documentation on functional bean registration of spring cloud functions. Traditional bean registration works fine but when using FunctionalSpringApplication an error occurs with unable to create bean with name FunctionExporterAutoConfiguration. spring-cloud-starter-function-web and spring-boot-starter are both on classpath.

full stack trace:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.cloud.function.web.source.FunctionExporterAutoConfiguration': Post-processing of merged bean definition failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [org.springframework.cloud.function.web.source.FunctionExporterAutoConfiguration] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:572) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878) ~[spring-context-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.function.context.FunctionalSpringApplication.run(FunctionalSpringApplication.java:87) [spring-cloud-function-context-3.0.3.RELEASE.jar:3.0.3.RELEASE]
    at org.springframework.cloud.function.context.FunctionalSpringApplication.run(FunctionalSpringApplication.java:82) [spring-cloud-function-context-3.0.3.RELEASE.jar:3.0.3.RELEASE]
    at io.spring.sample.functions.DemoApplication.main(DemoApplication.java:12) [classes/:na]
Caused by: java.lang.IllegalStateException: Failed to introspect Class [org.springframework.cloud.function.web.source.FunctionExporterAutoConfiguration] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]
    at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:481) ~[spring-core-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.util.ReflectionUtils.doWithLocalMethods(ReflectionUtils.java:321) ~[spring-core-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.buildLifecycleMetadata(InitDestroyAnnotationBeanPostProcessor.java:232) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.findLifecycleMetadata(InitDestroyAnnotationBeanPostProcessor.java:210) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(InitDestroyAnnotationBeanPostProcessor.java:149) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(CommonAnnotationBeanPostProcessor.java:310) ~[spring-context-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors(AbstractAutowireCapableBeanFactory.java:1094) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    ... 14 common frames omitted
Caused by: java.lang.NoClassDefFoundError: org/springframework/web/reactive/function/client/WebClient$Builder
    at java.lang.Class.getDeclaredMethods0(Native Method) ~[na:1.8.0_172]
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) ~[na:1.8.0_172]
    at java.lang.Class.getDeclaredMethods(Class.java:1975) ~[na:1.8.0_172]
    at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:463) ~[spring-core-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    ... 21 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.springframework.web.reactive.function.client.WebClient$Builder
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_172]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_172]
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) ~[na:1.8.0_172]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_172]
    ... 25 common frames omitted

Process finished with exit code 1

main class

package io.spring.sample.functions;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.cloud.function.context.FunctionalSpringApplication;

import java.util.function.Function;

@SpringBootConfiguration
public class DemoApplication implements Function<String, String> {

  public static void main(String[] args) {
    FunctionalSpringApplication.run(DemoApplication.class, args);
  }

  @Override
  public String apply(String s) {
    return s.toUpperCase();
  }
}

Full Source Code Url

aaron-benson-on-code commented 4 years ago

I did some more research on this and found a few things.

I downgraded to spring-cloud-dependencies version Greenwich.RC1 which also required a downgrade to spring-boot-starter-parent version 2.1.x. If I did that AND added spring-boot-starter-webflux to the classpath even though I am not creating a flux function I get this to work. However, without spring-boot-starter-webflux even with spring-cloud-starter-function-web the application will start and immediatly exit status 0 with no logs besides the following:

2020-03-14 15:02:07.075  INFO 2341 --- [           main] io.spring.sample.DemoApplication         : Starting DemoApplication on Aarons-MacBook-Pro.local with PID 2341 (/Users/aaron/workspace/spring/test-spring-function/target/classes started by aaron in /Users/aaron/workspace/spring/test-spring-function)
2020-03-14 15:02:07.087  INFO 2341 --- [           main] io.spring.sample.DemoApplication         : No active profile set, falling back to default profiles: default
2020-03-14 15:02:07.508  INFO 2341 --- [           main] io.spring.sample.DemoApplication         : Started DemoApplication in 1.122 seconds (JVM running for 2.199)

Process finished with exit code 0

If I upgrade to any version of Hoxton and Spring 2.2.x I get the original error I opened the defect with.

If I get some time I will do some more digging to find what is actually happening

olegz commented 4 years ago

@aaron-benson-on-code so unfortunately FunctionExporter is kind of coupled to webflux. And just as an FYI it has nothing to do with the type of function you are declaring (reactive or imperative). To address this properly will require a bit more surgery. . .

Meanwhile, changing spring-cloud-starter-function-web to spring-cloud-starter-function-webflux does the trick and you don't need to downgrade anything.

aaron-benson-on-code commented 4 years ago

@olegz Thanks that worked