This is probably more of a documentation issue but when developing a reactive application on Tomcat (and probably others) and you exclude starter-reactor-netty like this
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-reactor-netty</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
then WebClient won't work. The class is on the path but creating a new one with return WebClient.create("http://localhost:8080"); will fail:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.reactive.function.client.WebClient]: Factory method 'webClient' threw exception; nested exception is java.lang.NoClassDefFoundError: reactor/ipc/netty/http/client/HttpClient
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:182)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:575)
... 43 common frames omitted
Caused by: java.lang.NoClassDefFoundError: reactor/ipc/netty/http/client/HttpClient
at org.springframework.http.client.reactive.ReactorClientHttpConnector.<init>(ReactorClientHttpConnector.java:47)
at org.springframework.web.reactive.function.client.DefaultWebClientBuilder.initExchangeFunction(DefaultWebClientBuilder.java:139)
at org.springframework.web.reactive.function.client.DefaultWebClientBuilder.build(DefaultWebClientBuilder.java:115)
at org.springframework.web.reactive.function.client.WebClient.create(WebClient.java:173)
at com.example.demo.NewClass$CustomConfig.webClient(NewClass.java:29)
at com.example.demo.NewClass$CustomConfig$$EnhancerBySpringCGLIB$$7b5a2896.CGLIB$webClient$0(<generated>)
at com.example.demo.NewClass$CustomConfig$$EnhancerBySpringCGLIB$$7b5a2896$$FastClassBySpringCGLIB$$783fa80e.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:359)
at com.example.demo.NewClass$CustomConfig$$EnhancerBySpringCGLIB$$7b5a2896.webClient(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:155)
... 44 common frames omitted
Caused by: java.lang.ClassNotFoundException: reactor.ipc.netty.http.client.HttpClient
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
Comment From: bclozel
Yes, indeed, reactor-netty is still required if you want to use the WebClient.
I'll turn this into a documentation improvement. Is there a particular piece of documentation that you find misleading? Or did you just apply the same reasoning that you'd use with spring-boot-starter-web?
Comment From: michael-simons
I expected only the runtime plattform to change. I understand what's happening and I'd know how to fix it, but it's annoying and should be part of the docs. Something like: "if you want to use tc or undertow do this, but take care of including Reactor-Netty if you need web client"
Comment From: dancingfrog
This is actually a bigger issue in that if we include the spring-boot-starter-reactor-netty or spring-webflux dependencies in order to use WebClient in our application, then the application itself (@SpringBootApplication) will always start an embedded web server listening on port 8080. There seems to be no exclusion that will prevent this behavior and still allow the application to use WebClient or other reactive web classes. The cause is pretty well documented here: https://www.changchao.me/?p=316
Comment From: wilkinsona
@dancingfrog You can set the web application type to none when creating your SpringApplication.
Comment From: dancingfrog
@wilkinsona Thank you! @bclozel Thanks for the answer you submitted on stack exchange https://stackoverflow.com/questions/43574234/how-to-prevent-embedded-netty-server-from-starting-with-spring-boot-starter-webf
Comment From: rameshsunkara
How to use reactive webclient in an existing Spring Web Application which run on Tomcat ?
I cannot set the web application type to None
Comment From: philwebb
@rameshsunkara Please ask questions on stackoverflow or gitter. We prefer to use the issue tracker only for bugs and enhancements.
Comment From: rameshsunkara
Please ask questions on stackoverflow
Thanks, I posted in stack overflow, Reactive WebClient Stack Overflow Question
Comment From: madhtr
@dancingfrog You can set the web application type to none when creating your SpringApplication.
True. But if I am creating a project intended to be used as a maven dependency in another project, the developer on the dependent project will have to do this as well.
Excluding spring-boot-starter-tomcat worked with spring boot web. But it does not work with webflux. This is not ideal IMHO.
Respectfully.
Comment From: gfinger
I use the WebClient in a library which is used in console applications. A web server is not needed and only increases the overall size of the jar. According to me requiring a web server when you only need the client contradicts the idea of autoconfiguration. If there is no server in the classpath everything depending on it should not be configured. This is the idea of the "@ConditionalOn..." annotations, no?
Comment From: bclozel
@gfinger since this issue got resolved, Spring Boot can now auto-configure WebClient using Reactor Netty, Apache HttpComponent, Jetty client and the JDK 11 java.net.http.HttpClient. If it's not working as expected, can you create a new issue with a sample application showing the problem?
Comment From: gfinger
@bclozel this might be a misunderstanding. It is possible to configure the http-client, yes. But you can not have the http-client without the server. I get the error message:
Web application could not be started as there was no org.springframework.boot.web.reactive.server.ReactiveWebServerFactory bean defined in the context.
I know that I could use the whole webflux-starter dependency and disable the start of the server, for example in a console application. But this still gets me a lot of things in my jar, that are not needed. And, additionally, if I add the webflux-starter in a reuse-library, I have to advise all consumers of my library to take care about the required server. Not nice.
As said above: I would expect that If there is no http-server in the classpath, the autoconfiguration considers this correctly and does not try to configure any component that might need the server.
I open another issue about this topic.
Comment From: bendathierrycom
@gfinger We cannot have everything with a small footprints. Maybe you can use other webclient impl available like okhttp or httpcomponents. I think you will then have a smaller stuff embedded inside your java application's classpath.