micronaut-projects/micronaut-spring

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

donbeave opened this issue · 5 comments

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

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

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.

@dstepanov I found a related issue in Micronaut Gradle plugin: micronaut-projects/micronaut-gradle-plugin#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?

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 }

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.