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
Related
Scenario:
I'm supporting an Enterprise application that runs in Wildfly10. The application (.war) uses J2EE technologies (EJBs, JPA, JAX-RS) and SpringBoot features (like, SpringMVC, SpringRest, SpringData, SpringRestData) ... Both stacks co-exists "happily" because they don't interact between them; however, they do share common classes, like utility or Entity Classes (the stacks map to the same database model). Why the application uses those stacks is out the scope of the question.
Currently, I'm trying to improve the performance of a #RestController that pulls some data from the database using a JPA Spring Repository. I found that we're suffering the N + 1 queries problem when calling the #RestController. In past projects (where there were only J2EE technologies), I have used the #BatchSize hibernate annotation to mitigate this problem with total success.
But, in this project, Spring seems to be skipping such annotation. How do I know that? Because I turned on the hibernate SQL logging (hibernate.show_sql) and I can see the N + 1 queries is still happening ...
Key Points:
Here are some insights about the application that you must know before providing (or trying to guess) any answer:
The application has many sub-modules encapsulated as libraries inside WAR file (/WEB-INF/lib) ... Some of these libraries are the jars that encapsulate the entity classes; others are the jars that encapsulate the REST Services (that could be JAX-RS services or Spring Controllers).
The Spring configuration is done in the classes defined in the WAR artifact: in there, we have a class (that extends from SpringBootServletInitializer) annotated with #SpringBootApplication and another class (that extends from RepositoryRestConfigurerAdapter) annotated with #Configuration. Spring customization is done is such class.
The application works with multiple datasources, which are defined in the Wildly server. Spring DATA JPA must address any query pointing to the right datasource. To accomplish this requirement, the application (Spring) was configured like this:
#Bean(destroyMethod="")
#ConfigurationProperties(prefix="app.datasource")
public DataSource dataSource() {
// the following class extends from AbstractRoutingDataSource
// and resolve datasources using JNDI names (the wildfly mode!)
return new DataSourceRouter();
}
#Bean("entityManagerFactory")
public LocalContainerEntityManagerFactoryBean getEntityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean lemfb;
lemfb = new LocalContainerEntityManagerFactoryBean();
lemfb.setPersistenceUnitName("abcd-pu");
lemfb.setDataSource(dataSource());
return lemfb;
}
The last #Bean declaration favors the use of a persistence.xml file, which we do have in the route /WEB-INF/classes/META-INF/ (i.e. Spring does find this file!) ... In such file, we define our domain classes, so that Spring JPA can see such entities. Also, we can define special JPA properties like: hibernate.show_sql and hibernate.use_sql_comments without issues (this is how I detected the N + 1 queries problem in the first place) ...
What I have done so far?
I tried to add the #BatchSize annotation to the problematic collection. No luck!
I created a new JAX-RS Service whose purpose was to mimic the behavior of the #RestController. I confirmed that the #BatchSize annotation does work in the application's deployment, at least, in JAX-RS Services! (NOTE: the service uses it own persistence.xml) ...
Test details (Updated 2020/07/30): What I did here was to create a new JAX-RS Service and deployed it inside the WAR application, next to the #RestController that presents the problem (I mean, it is the same WAR and the same physical JVM). Both services pull from database the same entity (same class - same classloader), which has a lazy Collection annotated with #BatchSize! ... If I invoke both services, the JAX-RS honors the #BatchSize and pulls the collection using the expected strategy, the #RestController does not ... So, what it is happening here? The only thing different between the services is that each one has a different persistence.xml: the persistence.xml for the JAX-RS is picked by Wildfly directly, the other one is picked by Spring and delegated to Wildfly (I guess) ...
I tried to add the properties: hibernate.batch_fetch_style (=dynamic) and hibernate.default_batch_fetch_size (=10), to the persistence.xml read by Spring ... No luck. I debug the Spring startup process and I saw that such properties are passed to the Spring Engine, but Spring does not care about them. The weird thing here is that properties like: hibernate.show_sql, Spring does honor them ... For those who are asking: "What does these properties do?" Well, they are global equivalent to apply #BatchSize to any JPA lazy collection or proxy without declaring such annotation in any entity.
I setup a small SpringBoot Project using the same Spring version as enterprise application (which is 1.5.8.RELEASE, by the way) and both the annotation and properties approach worked as supposed to.
I've been stuck with this issue for two days, any help to fix this will be appreciated ... thanks!
There are 2-3 possible issues that I can think off.
For some reason, whatever you modify isnt picked up by wildfly - Wildfly classpath resolution is a separate Topic and some missing configuration can cause you a nightmare. This you can identify if you have access to debug the query, and in if you put a breakpoint in the constructor of your Entity class, you will get a chance to evaluate the entity configuration being used, somewhere in the execution conetxt.
BatchSize doesnt work on OneToOne, It only works on OneToMany relationships.
A typical way to define BatchSize is to do along with Lazy load as mentioned in the example here. If you are not using Lazy fetch, hibernate assumes that you are willing to make an eager load and makes another select query to fetch all the details.Please confirm you are using the same syntax as given in the example above.
New Addition:
Put Conditional Breakpoints in PropertyBinder#setLazy() function, and may be backtrace it and put relavent breakpoints in CollectionBinder and AnnotationBinder. then restart/redeploy the server and see what data you are getting for the relavent properties. That will give you fair idea where it is failing..
Why conditional breakpoint? Its because you will have thousands of properties and if you do not add condition to the breakpoint, you will take 1 hour to reach your actual breakpoint
What should be the condition - If its property binder, the condition shoud be like `this.name == . For other classes also you can use the same approach.
Sorry for too detailed description on conditional breakpoints, you might find it redundent.
Looks like the only way to debug your problem is to debug hibernate framework from server startup, then only we will be able to find out the rootcause
I have a simple Spring Boot application which reads from Kafka topic and persists the messages to some cache.
I would like to add an integration test, which would launch my original application, generate some messages from embedded Kafka, and then assert cache contents.
I'm struggling with the "launch my original application" part. How does one do that from Spring Boot integration test?
I've tried doing something like that:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = OriginalApplication.class)
#EmbeddedKafka
public class OriginalApplicationIntegrationTest {
#Test
public void test() throws Exception {
...
}
}
But I see no attempts from Spring to launch my original application.
First of all, there are two possible big "areas" that can go wrong:
Spring Boot Test Setup
Kafka Integration
I believe the question is around the first part so I'll concentrate on that part.
For a quick answer:
When you put a #SpringBootTest annotation, try to use it without parameters at all. And make sure the test is put in a correct package, it matters. This will turn on the automatic resolution of your application.
Now I'll try to briefly explain why its important, the topic is really broad and deep.
Spring Boot checks whether the class annotated with #SpringBootConfiguration (its an annotation put on #SpringBootApplication - which is in turn is on your main class) exists in the same package as the integration test ( Lets say, com.abc.myapp.test is where you put a test)
If not found, it goes one package up and checks there (com.abc.myapp). It will do that again and again till the root package however, lets assume the #SpringBootApplication annotated class is in this package. Notice, If this recursive "search" doesn't find #SpringBootApplication annotated class - the test doesn't start. That's why its important to use the package structure offered by spring boot application.
Now when it finds that class it know which packages should be scanned for beans to start the spring boot application. So it tries to find beans according to practices of spring boot (package com.abc.myapp and beneath). It does it again recursively top-to-bottom this time.
It also runs your starters (autoconfigurations) in this mode.
So, bottom line:
Specifying #SpringBootTest without parameters makes spring boot doing its best to mimic the startup of the real application
If you use it with parameters where you put it a configuration however, it behaves totally differently: Its like saying: "I know where my configurations are, don't try to start everything, here is my configuration, load only it".
A totally different thing, no recursive searches, no auto-configurations startup, etc.
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.
How to Configure Dependency Injection in a Library Project?
Let me illustrate this question with the following example.
Maven Library Project
ReservationAPI
com.example.reservation-api
This project contains a convenience class called ReservationApiClient which uses a RestTemplate (from the Spring Framework) for making HTTP calls.
Is it possible to make the RestTemplate field #Autowired in this library project instead of instantiating it myself?
Maven Executable Project
org.company.application
This project is a Spring Boot application and uses the above ReservationAPI as a dependency. This app will create a #Bean for the convenience class ReservationApiClient contained in that library and will then execute its public methods which in turn make HTTP requests.
What is a good strategy and/or best practices for the scenario described above?
You can do this if you include autowiring in your library project although that means it would always need to be used with a Spring application context to get the value unless you also have getter/setter methods to use as well. However, I don't think using RestTemplate as an autowired object makes sense since there is nothing specific about a RestTemplate and unless you name the beans there is only one bean definition for a class. All of the methods for the RestTemplate require the URI there anyhow. So in this case I would just use the bean for your ReservationApiClient in your application.
One other way to do it is if you want to include Spring dependencies in your library (which I guess you already are by using RestTemplate) you can declare your ReservationApiClient as a #Service or #Component and then use the #ComponentScan annotation in your main Spring Boot project to search that library for components to include in the bean registry.
Another option is to use a feature like Spring Boot's Autoconfigure to create factories that use third party libraries and configure them per properties in your application settings. The auto configuration documentation would be a good place to start with this. You can see the starter projects they have on GitHub and then the associated Autoconfigure classes they have associated with these.
Let me know if any of this does not make sense.
I want to create a new Spring Boot web application.
My structure should be the following:
com.base.package
MyApplication
config
domain
repository
service
web
I can go two ways:
Annotate MyApplication with #SpringBootApplication and let it find controllers, JPA repositories etc.
Use #EnableWebMvc, #EnableJpaRepostories and so on along with #ComponentScan(basePackages=...) in order to point the base packeges for different component types explicitly.
I would happily go with the first approach but there is a concern that it will take more time to start the application then (if the app codebase grows significantly) because Spring would scan everything instead of particular packages.
But it anyway should read all these files to find the beans at least once.
So the question is:
Is the Spring (Boot) smart enough to scan all subpackeges for the beans only once or it would add an overhead to leave it with the default configuration?