spring-bootspring-data-jpajava-11java-platform-module-systemjava-17

Consider defining a bean named 'entityManagerFactory' in your configuration after Update from Spring Boot 2.2.7


After upgrading from Spring Boot Version 2.2.7.RELEASE to 2.7.3 we get the error when we try to start our application.

2022-09-22 11:20:31.896  WARN 60031 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testController': Unsatisfied dependency expressed through field 'bookApi'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'bookApi': Unsatisfied dependency expressed through field 'repository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bookRepository': Cannot create inner bean '(inner bean)#4b3fe06e' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#4b3fe06e': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
2022-09-22 11:20:31.898  INFO 60031 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2022-09-22 11:20:31.908  INFO 60031 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-09-22 11:20:31.941 ERROR 60031 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field repository in com.example.module.api.BookApi required a bean named 'entityManagerFactory' that could not be found.

The injection point has the following annotations:
        - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean named 'entityManagerFactory' in your configuration.

Background

We modularized our application with JPMS, and we start our application with the following command:

java --module-path ./mods:./lib -m app/com.example.app.AppApplication

We copy our application jars to ./mods (with the maven-jar-plugin) and all runtime dependencies to ./lib (with the maven-dependency-plugin).

This works perfectly with Java 11 and with the Spring Boot "2.2.7.RELEASE", but starting with the release "2.2.8.RELEASE" the same project leads to the above error. We get the same error even if we upgrade to the newest Spring Boot Version 2.7.3

Reproducible Examples

To reproduce in a small but running example. I created a jpms application and uploaded a working version (Spring Boot 2.2.7.RELEASE) to the main branch and the non-working version (Spring Boot 2.2.8.RELEASE) to the spring-boot-2.2.8.RELEASE branch.

The project URL is: https://github.com/rudolfgrauberger/jpms-multi-modules-example

Edit: I added the new branch spring-boot-2.7.3-java17 to show the issue with the newest version of Spring Boot and the LTS-Version of Java.

Question

Anyone have any idea how I can find out what exactly the problem is or what has changed between the versions (especially with the EntityManager or with @EnableJpaRepositories)?

I have already searched the Announcement for 2.2.8.Release, the Release Notes for changes and also used the search engine very intensively but I don't found anything regarding to this problem/change.

Happy to share more information, just need to know what exactly would be helpful


Solution

  • Short

    The issue came with the HikariCP dependency update in Spring Boot 2.2.8.RELEASE. More information about the background of the issue in HikariCP can you found in the HikariCP-Issue here.

    With Spring Boot 2.5.0 and Spring Boot JDBC 2.5.0 the HikariCP dependency was already updated to the recommended version for java 8 and that is 4.0.3. As of Spring Boot 2.5.0, you can only add requires com.zaxxer.hikari; to module-info.java of your JPMS "app" project to make it all work.

    Solution for Spring Boot Version >= 2.5.0

    Add requires com.zaxxer.hikari; to your module-info.java.

    module app {
    
        requires com.zaxxer.hikari;
    }
    

    For Spring Boot versions >= 2.2.8.RELEASE and < 2.5.0 you can downgrade the HikariCP dependency back to 3.4.3.

        <dependencyManagement>
            <dependency>
                <groupId>com.zaxxer</groupId>
                <artifactId>HikariCP</artifactId>
                <version>3.4.3</version>
            </dependency>
        </dependencyManagement>
    

    Or upgrade to 4.0.3 and add requires com.zaxxer.hikari; to module-info.java.

        <dependencyManagement>
            <dependency>
                <groupId>com.zaxxer</groupId>
                <artifactId>HikariCP</artifactId>
                <version>4.0.3</version>
            </dependency>
        </dependencyManagement>
    
    module app {
       // ...
       requires com.zaxxer.hikari;
    
       // ...
    }
    

    I also added the fixes for 2.2.8.RELEASE and 2.7.3 to the new branches spring-boot-2.2.8.RELEASE-fixed and spring-boot-2.7.3-java17-fixed.

    Background

    First I searched for how can I debug the bean loading/instantiation and I found the gread Spring: Framework in Depth video course by Frank P Moley III and I added an own implementation of the BeanFactoryPostProcessor-Interface and debug a while, after that I logged all defined packages and bean names from the BeanClassLoader for both Spring Boot versions (2.2.7.RELEASE and 2.2.8.RELEASE).

    Here is my implementation:

    package com.example.app;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.List;
    
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
        @Configuration
        public static class AppConfig {
            @Bean
            public static BeanFactoryPostProcessor beanFactoryPostProcessor() {
                return new MyBeanFactoryPostProcessor();
            }
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
            final ClassLoader beanClassLoader = configurableListableBeanFactory.getBeanClassLoader();
    
            System.out.println("############################################################");
            System.out.println("");
            System.out.println("Defined packages");
            System.out.println("------------------------------------------------------------");
            List.of(beanClassLoader.getDefinedPackages()).forEach(System.out::println);
            System.out.println("------------------------------------------------------------");
    
            System.out.println("Bean names");
            System.out.println("------------------------------------------------------------");
            configurableListableBeanFactory.getBeanNamesIterator().forEachRemaining(System.out::println);
            System.out.println("------------------------------------------------------------");
    
            System.out.println("");
            System.out.println("############################################################");
        }
    }
    

    Here the packages/beans that are exists in my app with Spring Boot 2.2.7.RELEASE but not exists in my app with Spring Boot 2.2.8.RELEASE:

    ############################################################    
    Defined packages    
    ------------------------------------------------------------
    package org.springframework.boot.orm.jpa
    package com.zaxxer.hikari
    package org.springframework.jdbc.core.namedparam
    package org.springframework.transaction.interceptor
    package org.springframework.boot.jdbc.metadata
    package org.springframework.orm.jpa.persistenceunit
    package org.aopalliance.intercept
    package org.hibernate.boot.model.naming
    package org.springframework.transaction.event
    package com.zaxxer.hikari.pool
    
    ------------------------------------------------------------    
    Bean names  
    ------------------------------------------------------------
    org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari  
    dataSource  
    org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration$Hikari   
    org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration  
    org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceConfiguration   
    org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration 
    hikariPoolDataSourceMetadataProvider
    org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration$JpaWebConfiguration 
    openEntityManagerInViewInterceptor
    openEntityManagerInViewInterceptorConfigurer
    org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration
    transactionManager
    jpaVendorAdapter    
    entityManagerFactoryBuilder
    entityManagerFactory
    spring.jpa.hibernate-org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties
    dataSourceInitializedPublisher
    org.springframework.boot.autoconfigure.jdbc.JdbcTemplateConfiguration   
    jdbcTemplate
    org.springframework.boot.autoconfigure.jdbc.NamedParameterJdbcTemplateConfiguration 
    namedParameterJdbcTemplate
    org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration   
    spring.jdbc-org.springframework.boot.autoconfigure.jdbc.JdbcProperties
    org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration$DataSourceTransactionManagerConfiguration
    org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration  
    org.springframework.transaction.config.internalTransactionAdvisor   
    transactionAttributeSource
    transactionInterceptor
    org.springframework.transaction.config.internalTransactionalEventListenerFactory    
    org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration$CglibAutoProxyConfiguration
    org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration    
    org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$TransactionTemplateConfiguration    
    transactionTemplate
    org.springframework.orm.jpa.SharedEntityManagerCreator#0
    

    I wasn't particularly surprised by the beans, or rather, they didn't provide that much insight, but the information about the packages seemed promising to me.

    So I gradually added the missing packages as "requires" to the project "app" until I got the message Module metrics.healthchecks not found, required by com.zaxxer.hikari.

    With this message, I found with Google directly the related issue in HikariCP: https://github.com/brettwooldridge/HikariCP/issues/1582.

    For testing I downgraded the HikariCP dependency to version 3.4.3 and it works again (with Spring Boot 2.2.8.RELEASE).

    The update to the recommended version 4.0.3 and the upgrade to the recommended version for Java 11+ works after adding requires com.zaxxer.hikari to the module-info.java in my app project.

    A good article with an explanation of how you can upgrade/downgrade or unifies the transitive dependency version, you found here.

    I hope this background information helps anyone. Even if it's just a suggestion of what you can try when it comes to checking the existence of the beans or if you want to intervene in the bean creation process.