spring-attic / spring-native

Spring Native is now superseded by Spring Boot 3 official native support
https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html
Apache License 2.0
2.74k stars 355 forks source link

Thymeleaf doesn't work without spring-web #1640

Closed Delorien84 closed 2 years ago

Delorien84 commented 2 years ago

Hi, when I want to create application without web, I got following exception during AOT compilation. I want to create simple Job application that gather some data from database. I have just following dependencies.

implementation("org.springframework.boot:spring-boot-starter-data-jdbc")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")

This setup works for spring native 0.11.X branch, but not with current 0.12.0 version.

To workaround I need to add web dependency to compile application.

implementation("org.springframework.boot:spring-boot-starter-web")
mhalbritter commented 2 years ago

If you'd like us to spend some time investigating, please take the time to provide a complete minimal sample (something that we can unzip or git clone, build, and deploy) that reproduces the problem.

Delorien84 commented 2 years ago

Here it is: https://github.com/Delorien84/spring-native-bug-1640. During creating sample project I have notice that the problem is caused by org.springframework.boot:spring-boot-starter-thymeleaf dependency.

Delorien84 commented 2 years ago

@mhalbritter adding org.springframework:spring-web dependencies solve the problem. But in 0.11.x it was not needed.

mhalbritter commented 2 years ago

Yep, you're right. Thanks for the report.

The minimal producer is to just add spring-boot-starter-thymeleaf without spring-web on the classpath and it crashes:

2022-06-14 13:22:52.978  INFO 131695 --- [           main] o.s.a.build.ContextBootstrapContributor  : Processing application context
java.lang.TypeNotPresentException: Type org.springframework.http.MediaType not present
        at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117)
        at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125)
        at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
        at java.base/sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68)
        at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138)
        at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
        at java.base/sun.reflect.generics.repository.ConstructorRepository.computeParameterTypes(ConstructorRepository.java:111)
        at java.base/sun.reflect.generics.repository.ConstructorRepository.getParameterTypes(ConstructorRepository.java:87)
        at java.base/java.lang.reflect.Executable.getGenericParameterTypes(Executable.java:298)
        at java.base/java.lang.reflect.Method.getGenericParameterTypes(Method.java:333)
        at java.desktop/java.beans.FeatureDescriptor.getParameterTypes(FeatureDescriptor.java:391)
        at java.desktop/java.beans.MethodDescriptor.setMethod(MethodDescriptor.java:118)
        at java.desktop/java.beans.MethodDescriptor.<init>(MethodDescriptor.java:74)
        at java.desktop/java.beans.MethodDescriptor.<init>(MethodDescriptor.java:58)
        at java.desktop/java.beans.Introspector.getTargetMethodInfo(Introspector.java:1030)
        at java.desktop/java.beans.Introspector.getBeanInfo(Introspector.java:446)
        at java.desktop/java.beans.Introspector.getBeanInfo(Introspector.java:283)
        at java.desktop/java.beans.Introspector.getBeanInfo(Introspector.java:225)
        at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.getBeanInfo(ConfigurationPropertiesNativeConfigurationProcessor.java:251)
        at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.<init>(ConfigurationPropertiesNativeConfigurationProcessor.java:94)
        at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.processNestedType(ConfigurationPropertiesNativeConfigurationProcessor.java:107)
        at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.processNestedType(ConfigurationPropertiesNativeConfigurationProcessor.java:103)
        at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.handleJavaBeanProperties(ConfigurationPropertiesNativeConfigurationProcessor.java:185)
        at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.process(ConfigurationPropertiesNativeConfigurationProcessor.java:138)
        at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.processConfigurationProperties(ConfigurationPropertiesNativeConfigurationProcessor.java:99)
        at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor.processConfigurationProperties(ConfigurationPropertiesNativeConfigurationProcessor.java:72)
        at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor.process(ConfigurationPropertiesNativeConfigurationProcessor.java:66)
        at org.springframework.aot.context.bootstrap.generator.infrastructure.nativex.NativeConfigurationRegistrar.lambda$processBeanFactory$0(NativeConfigurationRegistrar.java:55)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
        at org.springframework.aot.context.bootstrap.generator.infrastructure.nativex.NativeConfigurationRegistrar.processBeanFactory(NativeConfigurationRegistrar.java:55)
        at org.springframework.aot.context.bootstrap.generator.ApplicationContextAotProcessor.bootstrapMethod(ApplicationContextAotProcessor.java:108)
        at org.springframework.aot.context.bootstrap.generator.ApplicationContextAotProcessor.process(ApplicationContextAotProcessor.java:96)
        at org.springframework.aot.build.ContextBootstrapContributor.contribute(ContextBootstrapContributor.java:80)
        at org.springframework.aot.build.BootstrapCodeGenerator.generate(BootstrapCodeGenerator.java:91)
        at org.springframework.aot.build.BootstrapCodeGenerator.generate(BootstrapCodeGenerator.java:71)
        at org.springframework.aot.build.GenerateBootstrapCommand.call(GenerateBootstrapCommand.java:107)
        at org.springframework.aot.build.GenerateBootstrapCommand.call(GenerateBootstrapCommand.java:42)
        at picocli.CommandLine.executeUserObject(CommandLine.java:1953)
        at picocli.CommandLine.access$1300(CommandLine.java:145)
        at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2352)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2346)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2311)
        at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2179)
        at picocli.CommandLine.execute(CommandLine.java:2078)
        at org.springframework.aot.build.GenerateBootstrapCommand.main(GenerateBootstrapCommand.java:112)
Caused by: java.lang.ClassNotFoundException: org.springframework.http.MediaType
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:467)
        at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:114)
        ... 44 more
mhalbritter commented 2 years ago

Just tested with 0.11.6 and it doesn't work either.

mhalbritter commented 2 years ago

The problematic thing is the org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties.Reactive#mediaTypes, which is a private List<MediaType> mediaTypes; field. This can't be introspected by org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor.TypeProcessor#getBeanInfo when the class is not on the classpath.

mhalbritter commented 2 years ago

What is the usecase for using Thymeleaf without spring-web? as far as i can see, the auto-configuration for Boot for Thymeleaf only deals with Web + Webflux. Your sample project unfortunately has no code in it which uses Thymeleaf.

Delorien84 commented 2 years ago

@mhalbritter Previously I used id("org.springframework.experimental.aot") version "0.10.5" and it works (sorry it was not 0.11 :( ). My use case is to query db for data and use Thymeleaf to render html page and send the page via email. Simple reporting application that we execute once per day. I updated the repository to contain all the code we have, sou you have may see our usage of Thymeleaf. Just to sanitize database queries and some name.

It works without problem with 0.10.5 and with 0.12.0 when I add spring-web dependency.

https://github.com/Delorien84/spring-native-bug-1640

mhalbritter commented 2 years ago

Which Spring Boot version did you use with 0.10.5?

Delorien84 commented 2 years ago

id("org.springframework.boot") version "2.5.6"

mhalbritter commented 2 years ago

This will be handled with the upcoming Spring Boot 3.0. In the meantime, please include spring-web to workaround that issue.

Delorien84 commented 2 years ago

Ok, thank you for you time.