micronaut-projects / micronaut-spring

A collection of utilities for Spring users of Micronaut
Apache License 2.0
159 stars 46 forks source link

micronaut-spring-boot:5.4.0 fails with io.micronaut.context.exceptions.NoSuchBeanException: No bean of type [io.micronaut.context.event.ApplicationEventPublisher<io.micronaut.context.event.StartupEvent>] when running a jar file #516

Open donbeave opened 5 months ago

donbeave commented 5 months ago

Expected Behavior

No response

Actual Behaviour

gradle bootRun works well, but building the application as jar file and running it leads to an startup error:

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:119)
    at java.base/java.lang.reflect.Method.invoke(Method.java:578)
    at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91)
    at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53)
    at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:58)
Caused by: io.micronaut.context.exceptions.NoSuchBeanException: No bean of type [io.micronaut.context.event.ApplicationEventPublisher<io.micronaut.context.event.StartupEvent>] exists. Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor).
    at io.micronaut.context.DefaultBeanContext.newNoSuchBeanException(DefaultBeanContext.java:2773)
    at io.micronaut.context.DefaultApplicationContext.newNoSuchBeanException(DefaultApplicationContext.java:304)
    at io.micronaut.context.DefaultBeanContext.resolveBeanRegistration(DefaultBeanContext.java:2735)
    at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:1729)
    at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:856)
    at io.micronaut.context.BeanLocator.getBean(BeanLocator.java:96)
    at io.micronaut.context.DefaultBeanContext.publishEvent(DefaultBeanContext.java:1815)
    at io.micronaut.context.DefaultBeanContext.start(DefaultBeanContext.java:376)
    at io.micronaut.context.DefaultApplicationContext.start(DefaultApplicationContext.java:199)
    at io.micronaut.spring.context.MicronautApplicationContext.start(MicronautApplicationContext.java:458)
    at com.example.demo.DemoApplication.main(DemoApplication.java:14)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    ... 4 more

Steps To Reproduce

  1. Clone micronaut-spring-no-bean-of-type-applicationeventpublisher-exists repository
  2. Build jar
./gradlew bootJar
  1. Run jar
java -jar build/libs/demo-0.0.1-SNAPSHOT.jar

Environment Information

JDK Version:

openjdk 19.0.2 2023-01-17
OpenJDK Runtime Environment Temurin-19.0.2+7 (build 19.0.2+7)
OpenJDK 64-Bit Server VM Temurin-19.0.2+7 (build 19.0.2+7, mixed mode)

Example Application

https://github.com/donbeave/micronaut-spring-no-bean-of-type-applicationeventpublisher-exists

Version

4.2.3

dstepanov commented 5 months ago

You might want to check if it did create a shadow jar and if meta-inf services are merged correctly

donbeave commented 5 months ago

You might want to check if it did create a shadow jar and if meta-inf services are merged correctly

There is only one file in services:

java.nio.file.spi.FileSystemProvider

There is no shadow jar, as it normal Spring Boot application created with Spring Initializer (https://start.spring.io) with micronaut-spring-boot dependency. It's not using Micronaut Gradle plugin and Gradle Shadow plugin. It's using Micronaut as parent context for Spring Boot application.

donbeave commented 5 months ago

@dstepanov I found a related issue in Micronaut Gradle plugin: https://github.com/micronaut-projects/micronaut-gradle-plugin/issues/406, the only difference is that in my case I'm using Micronaut as a parent context without Gradle shadow and Micronaut Gradle plugins, only Spring gradle plugin with micronaut-spring-boot dependency. It worked fine for Micronaut 3, but not for Micronaut 4. Has something changed in 4th version and need to add some extra config?

andrebrov commented 5 months ago

Looks like there was some change in Spring Boot 3. Config below helps to resolve this issue:

tasks.bootJar { loaderImplementation = org.springframework.boot.loader.tools.LoaderImplementation.CLASSIC }

asw12 commented 3 weeks ago

The upstream change in Spring Boot 3.2 is documented in the release notes.

The previous URL format of jar:file:/dir/myjar.jar:BOOT-INF/lib/nested.jar!/com/example/MyClass.class has been replaced with jar:nested:/dir/myjar.jar/!BOOT-INF/lib/nested.jar!/com/example/MyClass.class.

A new NestedFileSystemProvider was added in support of this new scheme.

However, micronaut-spring has code that prepends any jarUri that isn't starting with "file:" with that scheme: https://github.com/micronaut-projects/micronaut-core/blob/0118db96962eaf7a83abbcb81c1afd73139ff777/core/src/main/java/io/micronaut/core/io/IOUtils.java#L156-L162 So you get something like file:/nested:/dir/myjar.jar/!BOOT-INF/lib/nested.jar!/com/example/MyClass.class

That causes the FileSystemProvider for "file:" to be used instead of Spring's NestedFileSystemProvider, and that former one won't be able to load /nested:.../.

This is then causing the ServiceScanner to be unable to find any META-INF/micronaut/io.micronaut.inject.BeanDefinitionReference files listing out bean definitions, causing those NoSuchBeanException.