Spring can't find CrudRepository beans when using ComponentScan - java

I'm trying to create project structure that would allow me to add/remove modules by simply having them on classpath. With #ComponentScan("com.companyname") annotation in my Spring Application it detects and creates annotated components from modules. But I get errors when trying to autowire my CrudRepository anywhere:
Field repo in com.companyname.somemodule.services.SomeService required a bean of type 'com.companyname.somemodule.repos.SomeRepo' that could not be found.
So I thought that maybe it somehow can't create repos if they are defined in one of modules, so I wen't ahead and added test repo to my base SpringApplication and to my surprise I got:
Field repo in com.companyname.modularapp.TestService required a bean of type 'com.companyname.modularapp.TestRepo' that could not be found.
Then I just removed my #ComponentScan annotation and suddenly TestRepo worked as I intended, I was able to persist and read Test entities normally. So apparently ComponentScan somehow either screw up creation of CrudRepository, or it's later detection.
I define my repos like this:
#Entity
public class Test {
#Id
private long id;
}
public interface TestRepo extends CrudRepository<Test, Long>{}
I'm trying out Spring Boot 2.0.0.M7 with this project but I doubt that's the cause.
Did I miss something?

Also you can define package for Repositories scan by :
#EnableJpaRepositories("com.companyname")
or in XML config
<jpa:repositories base-package="com.companyname"/>

If you are using spring-boot you might as well drop the #ComponentScan annotation, as there is one already defined in the #SpringBootApplication annotation. Maybe there's a conflict of some sort between them, it's hard to tell without looking at the code.

If you customizing package scans in your project, then probably you need to manually configure bean which requires path to scan, e.g. for JPA you can create your own bean of LocalContainerEntityManagerFactoryBean (you can find auto-configuration example here -
org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration#entityManagerFactory - class link from spring-boot 1.5.*).
But, if you do not require package scan customization, just put class annotated with #SpringBootApplication in project root, and pass it to spring::run method.

Related

Convert library to autoconfiguration/starter while in use

Our project has a library that defines several beans.
Historically, it's a simple artifact, no autoconfig enabled and all other applications are using this artifact together with #ComponentScan.
Now, I realize, that it should rather be an autoconfiguration dependency, with #ConditionalOnMissingBean etc., to provide more flexibility for the application.
According to the docs, autoconfigurations should not be a candidate for component scanning. As far as I understand, it is only critical for situations when your configuration is #ConditionalOn something, so it does not get scanned twice.
What would be the correct way to perform the transition from library + #ComponentScan to autoconfiguration, assuming all the consumers of the same library cannot be updated instanly?
Will it cause any major issues if the autoconfig get scanned? Or can it be limited to cases with the usage of #ConditionalOn etc?
I know it should be possible to create another maven artifact, make it dependant on the first one and define autoconfiguration, but I would like to avoid creating another now.
Thanks, Pavlo
So I have this solution in my mind:
Start simple in each starter library start defining one #Configuration class with a #ComponentScan(basePackages=“com.acme.libn) when done release all the libriaries on the nexus (Note that this is a bad practice the starters should define the beans in the #Configuration classes but for this step will be used as a workaround).
Go on the consumers and remove #ComponentScan, update library and deploy. This will be the fastest way to remove the scanning of the beans on the main com.acme package.
Now you are free to work on each library as you prefer. Example: refactor the packages, declaring beans in the #Configuration, use #ConditionalOn…, remove #ComponentScan, use more #Configuration classes (you have to link them in the spring.factories file), start using prefix for using propeties defined in the consumers …, you can work in parallel on this task with your team. And release one library when it’s ready if you don’t want to release all of them together.
Re go in the consumers and refactor if needed, update the library that are ready and deploy. Go to step 3 till the libriaries are finished.
Otherwise you have to refactor com.acme package in each library and in each consumers, keeping the #ComponentScan in the consumers. When done with all the libriaries you can then remove it. You don’t have to do it all together, you can do it one library at time and one consumer at time.
PS: if you have entities and you are using spring-data in your libraries let me know I will update the answer because there is extra work to do.
I’ve read the documentation you have linked and I don’t know if it’s outdated or not. I’m using spring.factories file as for example stated here.I’m using spring boot 2.7.0. So check the correct configuration for the version of spring boot you are using
EDIT
I've read better the documentation and digged into the spring boot code META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.import can be used to tell spring where the #Configuration\#AutoCOnfiguration classes are. You can also use META-INF\spring.factories to declare the #Configuration\#AutoConfiguration classes. In the source code they are using the first option. Honestly I've not undersood the difference.
I think I've also discovered how to expose #RestController or any other component in a starter without using #ComponentScan as they say in the doc you must use #Import example:
#Configuration
#Import({RestController.class, SomeService.class})
class ConfigurationClass {
}
#RestController
class RestController {
}
#Service
class SomeService {
}
This will make the SomeService avaiable for injection, and I think this will expose automatically the endpoint defined in RestController, like spring-boot-starter-actuator.
For the repositories of spring-data-jpa you have to use this in the ConfigurationClass example:
#Configuration
#AutoConfigureAfter(JpaRepositoriesAutoConfiguration.class)
#EnableJpaRepositories(basePackages = "com.example")
#Import({Entity1.class, Entity2.class})
class ConfigurationClass {
}
#Entity
public class Entity1 {
}
#Entity
public class Entity2 {
}
In this way the repositories interfaces present in com.example will be ready for injection in the configuration class or outside the jar. In my project I was able to load entities only using #EntityScan in the consumer of the starter I have to make a try with #Import.
All the things that I've writed in the EDIT section must be tested, the only exception is the configuration for jpa repositories with #EntityScan on the consumer. I've had no time for test right now.
So in the end you can remove then #ComponentScan in the first step I've writed and use #Import, but you have to list all the classes that are annotated like #Component\#Service.... In this way you don't have to declare and construct all the beans of the step 3 in the configuration classes.
EDIT 2 I've made some test you will find the code here
what i discovered:
Adding the configuration classes in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.import doesn't work, you have to use META-INF\spring.factories if you check the code I've commented the configuration class in the file
#Import with entities doesn't work, unlikly you have to put #EntityScan in the cosnumer declaring all packages where the entities are.
For the rest everithing is fine. You can play with it:
POST localhost:8080/someEntities
request body
{
"name" : "entity1"
}
response
{
"id": 1,
"name": "entity1"
}
{
"id": 1,
"name": "entity1"
}
GET localhost:8080/someEntities/1
{
"id": 1,
"name": "entity1"
}
EDIT 3 from spring boot v2.7 the classes annotated with #AutoConfiguration are migrated to a new file META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (nothe the final s so imports) see this question for more info. The configuration classes declared in spring.factories will still be honored

Lazy load spring auto configuration

I have dependency in my spring boot project which fetches values of some properties using Spring's EnvironmentPostProcessor.
Now these properties are database credentials and not everyone has access to the credential since there is no dev environment for the db in question. I just want to change the configuration that the credentials don't get fetched on dev or local environment on application startup as that would result in a error and the application will fail to start.
Class A implements EnvironmentPostProcessor{}
I tried to use #Lazy annotation on the Class Annoteted with #ConfigurationProperties. I also tried using my own BeanFactoryPostProcessor (with #Order(HighestPrecedence) to programmatically set the A to lazy load, but it gets called before my BeanFactoryPostProcessor's postProcessBeanFactory method.
Is what I'm trying to achieve possible and am I going about it the wrong way?
#Lazy is only to be used with #Bean or #Component (Or any #Component-based annotations ex. #Service)
Take note: You can also add it to a #Configuration class, but that just means that all Beans in the class are annotated with #Lazy
#Lazy is a bit of a weird annotation in general; it should be seen as an IF possible then lazy load. If some other bean needs the lazy bean, the lazy bean will be initialized. (It's like the Pirate code, more of a guideline than an enforced rule)
Finally, marking #ConfigurationProperties with #Lazy seems a bit odd. As Spring will need these Configuration property "beans" to create the Spring Context.
However, the common use case for #Lazy is a failing database connection, preventing the application from starting. See the question if that is what you are running into.
Summary:
You can configure your repositories to be lazy-loaded with:
spring.data.jpa.repositories.bootstrap-mode=lazy
Last remark (Me just guessing)
If you wish to change properties once your application is already running, I would look at the following tutorial. It goes into manually reloading configuration and also #RefreshScope.
According to documentation EnvironmentPostProcessors must be registered via META-INF/spring.factories:
Allows for customization of the application's Environment prior to the
application context being refreshed. EnvironmentPostProcessor
implementations have to be registered in META-INF/spring.factories,
using the fully qualified name of this class as the key.
Implementations may implement the Ordered interface or use an #Order
annotation if they wish to be invoked in specific order.

Configuring spring repository in different spring boot starter

I decided that i will split spring boot application into smaller pieces. One of those pieces was spring repository (models as entities). I decided that it will be done using autoconfiguration. so if i will attach that starter/dependency to project database with proper tables will be created automatically.
Before changes whole project was working fine.
So i moved my models to that new repository starter (They have entity annotation).
#Entity
#Getter
#Setter
public class Account implements Serializable {
I created spring.factories and placed it in proper place with proper values
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.application.RepositoryAutoconfiguration
The autoconfiguration class looks following:
#Configuration
#ComponentScan(basePackages = {"com.application.model"})
public class RepositoryAutoconfiguration {
}
I thought if i will do like that then entities will be created automatically in database.
So i attached that newly built depednecny to my main project and when i started that app i recievied:
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.application.model.Account
It looks like it is not working.
Do you have any hints how to handle that case ? I checked the stackoverlow and i could notice like in some cases the issue was connected with missing entity annotation, but it is not my case.
Thanks,
Jan

Spring creating bean without proxy

In my some project I run into problem. Annotation #Transactional in some classes didn't work. After restarted module annotation is work. I open class in debug and saw that the CGLIB proxy is not used in the class, and used if I restart module without changes!
Eventually the problem was because project has custom library when in some class in #PostConstruct was registered beans duplicates of my beans with beanDefinitionRegistry.registerBeanDefinition() and after bean override in the part time class work without proxy. But I can't understand why? Why Spring create/ or replace/ or use bean without CGLIB proxy?
I was fixed that in my context, but I don't understand.

How to prevent spring boot from auto creating instance of bean 'entityManagerFactory' at startup?

I am working on a Spring boot application that uses Spring JPA with PostgreSQL. I am using #SpringBootTest(classes = <my package>.Application.class) to initialize my unit test for a controller class.
The problem is that this is causing the entityManagerFactory bean (and many other objects related to jpa, datasource, jdbc, etc.) to be created which is not needed for unit tests. Is there a way to prevent Spring from automatically creating these objects till they are actually used the first time?
I spent a lot of time trying to load up only the beans I need for my unit test but ran into many errors. I am relatively new to Spring and I am hoping someone else has run into this before...and can help. I can post code snippets if needed.
Update: I am not sure if I should edit or answer my own question...choosing to edit since I ended up changing my approach to unit tests. I added this to my test config class.
#Configuration
#ComponentScan(basePackages = {"api.controller", "api.config", "api.utils"})
public class TestControllerConfig {
}
and I mocked out the service and repository classes.
You can disable auto configuration in spring-boot using exclude attribute of #EnableAutoConfiguration, as follows:
#Configuration
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class TestConfig {
}
From #EnableAutoConfiguration documentation:
If the class is not on the classpath, you can use the excludeName attribute of the annotation and specify the fully qualified name instead. Finally, you can also control the list of auto-configuration classes to exclude via the spring.autoconfigure.exclude property.

Categories