NoSuchBeanDefinitionException after migrate from spring boot 1.5 to 2.0 - java

Today I want to upgrade from spring boot 1.5.7 to 2.0.1 but at runtime spring cannot find any of my spring-beans that are outside of my ApplicationConfiguration class even if I mentioned these package on SpringBootApplication (scanBasePackages) annotation.
These are the properties of the versions of my pom.xml file:
<properties>
<hibernate.version>5.2.10.Final</hibernate.version>
<spring.version>5.0.5.RELEASE</spring.version>
<springSecurity.version>5.0.4.RELEASE</springSecurity.version>
<springBoot.version>2.0.1.RELEASE</springBoot.version>
<jackson.version>2.9.1</jackson.version>
<slf4j.version>1.7.13</slf4j.version>
<logback.version>1.1.3</logback.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
This is my ApplicationConfiguration class:
package t.i.config;
#SpringBootApplication(scanBasePackages = {
"t.i.DAO",
"t.i.SERVICES",
"t.i.config"
})
#EnableAutoConfiguration(exclude = {
/*HibernateJpaAutoConfiguration.class*/ // old spring-boot 1.5 class
})
#PropertySource({ "classpath:application.mssql.properties" })
#EnableCaching
#EnableTransactionManagement
public class ApplicationConfiguration {
#Primary
#Autowired
#Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource,
MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) {
... code there that need MultiTenantConnectionProvider ...
}
}
SessionFactory bean need MultiTenantConnectionProvider which exists in package t.i.config.multitenancy:
package t.i.config.multitenancy;
#Component
public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider {
... code there ...
}
Despite the annotation on configuration class:
#SpringBootApplication(scanBasePackages = "t.i.config")
Spring throw exception at runtime:
NoSuchBeanDefinitionException: No qualifying bean of type 'org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider' available
This is an exemple with MultiTenantConnectionProvider but spring does not found any of my beans (repositories and services) if they are not explicitly declared in ApplicationConfiguration file.
Maybe it is not important, but I launch my app as a jar and my class SpringBootApplicationLauncher handle commandlines parameters and context loading:
package t.i;
public class SpringBootApplicationLauncher {
public static void main(String[] args) throws Exception {
SpringApplication springApp = new SpringApplication(new Class[]{t.i.config.ApplicationConfiguration.class});
context = springApp.run(args);
}
}
Where I was wrong ? EDIT: It work thanks to Norbert Bicsi answer.

Since you are defining your own datasource in the multi-tenancy app, you cannot let Spring Boot do its magic of autoconfiguration of data sources and connection to databases as defined in the datasources properties.
You should be excluding the DataSourceAutoConfiguration class.
So you need to add the exclude attribute as follows:
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
I have a recently created Spring Boot 2 (with Hibernate) and Spring Security 5 based database-per-tenant multitenancy web app with complete source code. Take a look and let me know if it helps.

You could try the basic approach where you leave you application class at the root level in your project so the #SpringBootApplication does a component scan of all the packages next to it and all subpackages.
Or you could try with the component scan annotation like:
#ComponentScan(basePackages = {
"t.i.DAO",
"t.i.SERVICES",
"t.i.config"
})
#SpringBootApplication
public class ApiApplication {
// ...
}

Related

Spring Boot: ComponentScan vs Declared Bean in an Autoconfigured Jar

Suppose I have a jar with a Spring Component called MyComponent. This jar is a Spring Boot "autoconfigured" jar, meaning that it has a Configuration class (annotated with #Configuration), and additionally, a META-INF/spring.factories file on the classpath. This jar is not an executable jar by itself; it is a library that is meant for inclusion in a Spring Boot application.
These files look as follows:
MyComponent.java, in package com.mine.components:
#Component
public class MyComponent {
private static final Logger logger = LoggerFactory.getLogger(MyComponent.class);
#PostConstruct
public void init() {
logger.info("MyComponent inited");
}
}
MyConfiguration.java, in package com.mine.config:
#Configuration
#ComponentScan(basePackages = "com.mine.components")
public class MyConfiguration {
}
spring.factories, in META-INF under src/main/resources:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.mine.config.MyConfiguration
If I include this jar in a Spring Boot project with the above three files, the MyComponent component is NOT detected (the log message never prints).
But if I instead remove the #ComponentScan and declare MyComponent using the #Bean annotation as follows, it is detected:
#Bean
public MyComponent myComponent() {
return new MyComponent();
}
Why?
Difference beetween ComponentScan and declared Bean inside #Configuration class:
#ComponentScan: You enable auto-scanning (default using current folder path), optionally you can specify an basePackage where spring will found yours beans.
#ComponentScan(basePackages = "com.mine.components")
You're saying to Spring that in this package("com.mine.components"), you'll define yours beans typically using annotations (#Component, #Entity, #Controller, #Service, #Repository or more).
#Bean: This way you define your beans manually inside #Configuration class, but Spring has to discover your configuration class, usually using #ComponentScan, #SpringBootApplication.
META-INF/spring.factories: you define an custom autoconfiguration

Is it possible to exclude nested configuration from autoconfiguration in Spring Boot application?

Suppose we have a Spring Boot application and autoconfiguration with several configurations defined inside it
#Configuration
#AutoConfigureBefore(MainAutoConfiguration.class)
public class TestAutoConfiguration {
....
#Configuration
public static class FirstNestedConfiguration {
...
}
#Configuration
public static class SecondNestedConfiguration {
...
}
}
this class is providing via external library dependency and all conditions are satisfied, so all beans in these configurations are loading.
Nevertheless, I need to exclude beans provided in FirstNestedConfiguration
Is it possible to do it?
UPD: as it's simple Spring Boot application, it runs as
#SpringCloudApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
without any explicit #ComponentScan configuration
You might use excludeFilters in #ComponentScan like this:
#ComponentScan(value = {'your.package'},
excludeFilters = #Filter(TestAutoConfiguration.class))
Also, if you want to exclude specific autoconfiguration globally, use properties:
spring:
autoconfigure.exclude: your.package.TestAutoConfiguration
However, please, note that this way you exclude outer configuration. According to this issue it's not possible to exclude inner configuration.
Does the profile approach works for you?
Look for With the #Profile annotation section
#Profile("ConfigOne")
#Configuration
Configuration spring documentation

Spring Boot 1.5.4: exclude configuration class in unit test

I have a Spring Boot project, version 1.5.4, with a MongoDb configuration class:
#Configuration
public class MongoConfig {
#Value("${spring.data.mongo.client.uri:mongodb://localhost:27017/database}")
private String mongoURI;
#Bean
public MongoDbFactory mongoFactory() throws UnknownHostException{
return new SimpleMongoDbFactory(new MongoClientURI(mongoURI));
}
#Bean
public MongoTemplate mongoTemplate() throws UnknownHostException, MongoException{
return new MongoTemplate(mongoFactory());
}
}
In my integration test i want use Embedded Mongo (https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo).
The problem is that the MongoDb configuration class start before the initialitation of Embedded mongo and try to connect to the database, so my test fail. If i remove the MongoConfig class, all test work well.
How can i exclude it only in my test execution?
Exclude the MongoDB autoconfiguration by using below annotation over your test class.
#EnableAutoConfiguration(exclude={MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class})
Then in the same path as your test class create a configuration class and define your mongo bean over there. This will get picked up during application start up
**#Configuration
public class MockConfigurations {
#Bean
#Primary
public MongoTemplate getMongoTemplate() {
//define your bean
return mongoTemplate;
}
}**
Please refer the answers here. It has two ways of excluding configurations.
Spring boot: apply #Configuration to certain package only
Update 1:
Alternatively, the most efficient way that I can think of is to use Spring profiles and load the profile for the tests
Define your TestConfiguration class and import it your test class.
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(MyTestConfiguration.class)
public class MyTests {
#Test
public void exampleTest() {
...
}
}
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-detecting-config
Update 2:
For EmbeddedMongoAutoConfiguration please refer the detailed answers here.
How do you configure Embedded MongDB for integration testing in a Spring Boot application?
I solved it with this configuration on my test class:
#RunWith(SpringRunner.class)
#ComponentScan({"it.app.server.dal","it.app.server.listener"})
#DataMongoTest() //mongoDB
public class ListenerTests {
...
}
The annotation #DataMongoTest() load my Embbedded MongoDb and with #ComponentScan i can just load the services and repositories wich i need in my test.

How to load #Configuration classes from separate Jars

I have a SpringBoot main application, as well as a separate Maven module project that compiles as a separate Jar. The module has a Spring config class annotated with #Configuration, which I want to get loaded, when the main app loads.
Apparently, this does not happen out of the box (by just including the module to the main app). What else do I need to do, to get the module configuration class also get loaded by the main app?
The easiest way is to scan the package that the #Configuration class is in.
#ComponentScan("com.acme.otherJar.config")
or to just load it as a spring bean:
#Bean
public MyConfig myConfig() {
MyConfig myConfig = new MyConfig ();
return myConfig;
}
Where MyConfig is something like:
#Configuration
public class MyConfig {
// various #Bean definitions ...
}
See docs
#ComponentScan annotation will scan all classes with #Compoment or #Configuration annotation.
Then spring ioc will add them all to spring controlled beans.
If you want to only add specific configurations, you can use #import annotation.
example:
#Configuration
#Import(NameOfTheConfigurationYouWantToImport.class)
public class Config {
}
#Import Annotation Doc

How to enforce loading order of spring configuration classes?

I'm working with spring-boot on a multi module project (maven). Each module has it's own #Configuration class. Basically I do have the following layout
Module foo-embedded (runs just calls the SpringApplication.run()) method:
#Configuration
#EnableAutoConfiguration
#ComponentScan("de.foobar.rootpackage")
#Import({ApplicationConfig.class, RepositoryConfig.class, SecurityConfig.class})
public class FooApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(FooApplication.class, args);
}
}
Module foo-common (contains all beans and spring-data-jpa initialization config)
#Configuration
#EnableJpaRepositories
#EnableTransactionManagement(entityManagerFactoryRef="entityManagerFactory")
public class RepositoryConfig {
#Bean(destroyMethod = "shutdown")
public DataSource getDataSource() {
// returning a Hikari CP here
}
#Bean(name = "entityManagerFactory") // overriding spring boots default
public EntityManagerFactory getEntityManagerFactory() {
// returning a new LocalEntityManagerFactoryBean here
}
}
Module foo-security (containing spring-securiy configuration and related domain classes), which has a maven dependency on foo-common
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// configuring HTTP security and defining my UserDetailsService Bean
}
When I start the application using the FooApplication class, everything works as expected. The above mentioned UserDetailsServiceImpl get's autowired with my UserRepository which is being created through the #EnableJpaRepositories annotation.
Since I want to write some integration tests I've added a test clss to one of my modules.
Module foo-media (containing some domain related stuff plus test cases for that module)
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {RepositoryConfig.class, SecurityConfig.class})
#WebAppConfiguration
#IntegrationTest
public class DirectoryIntegrationTest {
// my test code
}
When I run the test it seems that the SecurityConfiguration get's loaded before the RepositoryConfig.class does. Since the security config defined the UserServiceImpl which must be autowired, the test fails to start with a
NoSuchBeanDefinitionException telling me: No qualifying bean of type [com.foo.rootpackage.security.repository.UserRepository]
I already tried to add #DependsOn("UserRepository") at the bean definition of UserDetailsService, telling me that spring can't find a bean by that name.
Any hints or help would be greatly appreciated! Thanks in advance!
---- EDIT (since I was asked to provide more code) ----
For testing I do not use the actual RepositoryConfig.class, but have a TestRepositoryConfig.class in the common module. Looking like this
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", basePackages = "de.foobar.rootpackage")
public class TestRepositoryConfig extends RepositoryConfig {
#Bean
#Override
public DataSource getDataSource() {
// returning the ds for testing
}
}
You can use #Order annotation on your configuration classes to define load ordering. But it's strange because Spring should resolve proper order - so please check if you property inject UserRepository in UserDetailsService
So I was able to solve this. As it pointed out it had nothing to do with the loading order of the configuration classes (which was my first thought).
As you may notice, the only Configuration that had a #ComponentScan annotation was the FooApplication.class
Spring was not able to find the Repositories, as it didn't know where to look for. Providing the basePackages attribute like this:
#EnableJpaRepositories(basePackages = "de.foobar.rootpackage")
at the TestRepositoryConfig.class did the trick here.

Categories