Where to put #Bean in Spring Boot? - java

I am wondering what the best place would be for a Spring Boot app to register additional beans. I have a Main class that is annotated with #SpringBootApplication and beans defined in that class are picked up. But when i put those beans in another class it seems that the are not being registered.
When reading the documentation i got the idea that the #SpringBootApplication would implicitly search for classes that have #Bean annotations in them.
So my options are now:
Put all #Bean annotated bean in my main class
#SpringBootApplication
public class MyApplication {
#Bean
public Filter AuthenticationFilter() {
return new AuthenticationFilter();
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Create a configuration class and annotate that with #Configuration
#Configuration
public class MyConfiguration {
#Bean
public Filter AuthenticationFilter() {
return new AuthenticationFilter();
}
}
Is there a better way of doing this?

It is pretty much a matter of preference, but it is generally considered best practice to put exposed beans in configuration classes, which are logically grouped.
For example, you might have several configuration classes with a number of beans contained within each: An authentication configuration class with beans for AuthenticationProvider or UserDetailsService; a Thymeleaf configuration class containing beans for various Thymeleaf dialects, etc.

Actually, it is your choice there is no spring standard present to tell which one is best but while defining a class OOP design principles says A class should be loosely coupled and highly cohesive, should follow Single Responsibility Principle (SRP), Here
Coupling --> Degree of knowledge a class has about another class
Cohesion --> Degree which tells how well focused your class is
SRP --> A class should have only one responsibility, there should be only one reason to change a class.
So according to cohesion and SRP principle class should be well focused and have only one responsibility.
Here in your case you have only 2 beans but in future, these beans might increase. So should follow your second point and create another class for your bean declaration.
And In my choice should even create more configuration classes, So one configuration class should have a similar type of beans.

Yes, including your beans inside the #Configuration class is usually the preferred way for Spring.
This is also one of the ways Spring recommends injecting inter-dependencies between beans is shown in the following sample copied from the Spring's reference guide here:
Additionally, the default scope of #Beans is SINGLETON, if you specify a different scope such as PROTOTYPE the call will be passed to the original method.
Have a look at this section in the Spring Reference Guide

It depends on where the main class is located which has generally #SpringBootApplication annotations. You can annotate this main class with #ComponentScan(basePackageClasses = HelloWorld.class). Here HelloWorld has bean definitions annotated with #Beans and the class is annotated with #Configurations.
Spring container will scan all the sub-packages of the class specified in #ComponentScan arguments. You can also give wild card entries instead of class name.
Ex:
#SpringBootApplication
#ComponentScan(basePackageClasses = HelloWorld.class)
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class);
}
}
**Bean Class:**
#Configuration
public class HelloWorld {
#Bean
public TweetUserSevice tweetUserSevice() {
return new TweetUserSeviceImpl();
}
}

It depends on where the main class is located which has generally #SpringBootApplication annotations. You can annotate this main class with #ComponentScan(basePackageClasses = HelloWorld.class). Here HelloWorld has had definitions annotated with #Beans and the class is annotated with #Configurations.
Spring container will scan all the sub-packages of the class specified in #ComponentScan arguments. You can also give wild card entries for basePackageClasses argument instead of class name as specified above. E.g.
#SpringBootApplication
#ComponentScan(basePackageClasses = HelloWorld.class)
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class);
}
}
Bean Class:
#Configuration
public class HelloWorld {
#Bean
public TweetUserSevice tweetUserSevice() {
return new TweetUserSeviceImpl();
}
}
Another approach:
Generally in big projects, we will have multiple spring config classes containing bean definitions. We can avoid worrying about that all the bean class should be in sub-package of main class. What we can do is that we can have a single master spring config class(but make sure this master spring config class is under sub-package of main class so that #SpringBootApplication annotations automatically detects the master config) and import all the other bean classes.
I have 2 bean classes (TweetBeansConfig, TweetSystemHealthBeansConfig) in the package com.ronak.tweet (This package is not sub-package where main class exists). I have one master spring config class (TweetMasterSpringConfig) and this class resides in package which is sub-package where my main class resides.
package com.ronak.tweet.beans;
#Configuration
#Order(value=1)
#Import({
TweetBeansConfig.class,
TweetSystemHealthBeansConfig.class
})
public class TweetMasterSpringConfig {
public TweetMasterSpringConfig() {
System.out.println("Initilaizing master spring config");
}
}
package com.ronak.beans;
#Configuration
public class TweetBeansConfig {
#Bean
{
//
}
}
package com.ronak.beans;
#Configuration
public class TweetSystemHealthBeansConfig {
#Bean
{
//
}
}
Main class
package com.ronak.tweet;
#SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer {
/**This is for registing REST layer for Jersey which implements jaxb. It will register all the classes which is in the pacakage com.ronak.tweet.rest. You can add comma separated package names too.
#Bean
ResourceConfig resourceConfig() {
return new ResourceConfig().packages("com.ronak.tweet.rest");
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class);
}
}

It depends on personal choices and there is no good or bad way to do it.
As it is preferred or showed way in documentation of Spring Boot references.
As annotating main class with #SpringBootApplication makes it convenient to Bootstrap spring app. But it does only looks for subpackages so nesting configuration inside subfolder would not detect the #Bean automatically that is the only thing to remember other than it's all about personal preferences.

Related

Spring boot test with multiple configuration

In my Spring boot 2.1 project I have different #Configurations for different test (ConfigurationA and ConfigurationB), that reside in different packages. Both configurations define the same set of beans but in a different manner (mocked vs. the real thing)
As I am aware of the Bean overriding mechanism introduced in Spring Boot 2.1, I have set the property: spring.main.allow-bean-definition-overriding=true.
However I do have a test with the following the setup of the following configuration and test class. First there is a #Configuration in the productive part (I'm using Maven):
package com.stackoverflow;
#Configuration
public class ProdConfiguration{
...
}
Then in the test branch there is a general Test #Configuration on the same package level:
package com.stackoverflow
#Configuration
public class TestConfiguration {
#Bean
public GameMap gameMap() {
return Mockito.mock(GameMap.class);
}
}
And in a subpackage I have another #Configuration:
package com.stackoverflow.impl;
#Configuration
public class RealMapTestConfiguration {
#Bean
public GameMap gameMap() {
return new GameMap("testMap.json");
}
}
And then of course there is the test that is troubling me:
package com.stackoverflow.impl;
#ExtendWith(SpringExtension.class)
#SpringBootTest
#ContextConfiguration(classes={RealMapTestConfiguration.class, ProdConfiguration.class})
#ActiveProfiles("bug") // spring.main.allow-bean-definition-overriding=true
public class MapImageServiceIT {
#Autowired
private GameMap map;
}
It turns out that the injected GameMap into my test is a mock instance from TestConfiguration instead of the real thing from RealMapTestConfiguration. Aparrently in my test I have the configuration from ProdConfiguration and TestConfiguration, when I wanted ProdConfiguration and RealMapTestConfiguration. As the beans defined in the ProdConfiguration and *TestConfiguration are different the combination works, but TestConfiguration and RealMapTestConfiguration define the same been. It seems like the TestConfiguration is picked up by component scanning as it is in the same package as ProdConfiguration.
I was under the impression that when overriding beans the bean definition that is closer to the test class would be preferred. However this seems not to be the case.
So here are my questions:
When overriding beans, what is the order? Which bean overrides which one?
How to go about to get the correct instance in my test (using a different bean name is not an option, as in reality the injected bean is not directly used in the test but in a service the test uses and there is no qualifier on it.)
I've not used the spring.main.allow-bean-definition-overriding=true property, but specifying specific config in a test class has worked fine for me as a way of switching between objects in different tests.
You say...
It turns out that the injected GameMap into my test is a mock instance from TestConfiguration instead of the real thing from RealMapTestConfiguration.
But RealMapTestConfiguration does return a mock
package com.stackoverflow.impl;
#Configuration
public class RealMapTestConfiguration {
#Bean
public GameMap gameMap() {
return Mockito.mock(GameMap.class);
}
}
I think the problem here is that including ContextConfiguration nullifies (part of) the effect of #SpringBootTest. #SpringBootTest has the effect of looking for #SpringBootConfiguration in your application (starting from the same package, I believe). However, if ContextConfiguration is applied, then configurations are loaded from there.
Another way of saying that: because you have ContextConfiguration in your test, scanning for #Configuration classes is disabled, and TestConfiguration is not loaded.
I don't think I have a full picture of your configuration setup so can't really recommend a best practice here, but a quick way to fix this is to add TestConfiguration to your ContextConfiguration in your test. Make sure you add it last, so that it overrides the bean definitions in the other two configurations.
The other thing that might work is removing #ContextConfiguration entirely and letting the SpringBootApplication scanning do its thing - that's where what you said about the bean definition that is closest may apply.
In that case just don't use #Configuration on configuration class and import it to the test manually using #Import, example:
#SpringBootTest
#Import(MyTest.MyTestConfig.class)
public class MyTest {
#Autowired
private String string;
#Test
public void myTest() {
System.out.println(string);
}
static class MyTestConfig {
#Bean
public String string() {
return "String";
}
}
}

Spring Boot auto configuration order from external dependency

I have a problem trying to get my autoconfiguration working. I have two jars as follows, each have a spring.factories file where these two are enabled for EnableAutoConfigurationProperties.
This configuration is in my-package-mock.jar, it depends on my-package-real.jar below:
package org.packages.package.packageA;
#Configuration
#AutoConfigureBefore(AutoConfigurationB.class)
public class AutoConfigurationA {
#Bean
public MyService mockService() {
return new MyMockService();
}
}
This configuration is in my-package-real.jar:
package org.packages.package.packageB;
#Configuration
#ConditionalOnMissingBean(MyService.class)
public class AutoConfigurationB {
#Bean
public MyService realService() {
return new MyRealService();
}
}
Now the idea is that if my-package-mock.jar is included then AutoConfigurationB will not be processed as A is ordered to be before and by the time it gets to B MyService is already defined.
However, it does not work when used in a third project that includes these jars. It looks like the AutoConfigureOrder annotation is skipped when loading these jars from the classpath and these configurations are processed in the order the jvm loads these classes. In my particular case it does B first and at that point MyService is not yet defined and thus will instantiate the RealService bean. How can I get this to work?
Obviously this is a small example where a #Primary annotation on the mock will do the job, but that is not what I'm looking for.
Edit: it seems if the #SpringBootApplication annotated main is not a part of the package where these configurations are then things do work. E.g. the annotation is not in "org.packages.package" but "org.somewhereelse" then things work.
package org.packages.package;
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(Collections.singletonList(TestApplication.class).toArray(), args);
}
}
#AutoConfigureBefore and #AutoConfigureAfter only apply when a configuration class is loaded as a result of auto-configuration being enabled and it being listed in spring.factories. When your auto-configuration classes are in org.packages.package (or a sub-package) and your main application class is in the same package, they're being found by Spring Framework's standard component scanning. This happens because #SpringBootApplication enables component scanning for the package of the class that it's annotating. As a result of this the auto-configuration-specific ordering doesn't apply.
To avoid the problem, you should places your auto-configuration classes in a package that isn't used by any application code.

Spring: override beans in integration tests with isolation

I have a problem with overriding beans in integration tests in Spring (with Spock).
Let's say this is my application config:
#EnableWebMvc
#SpringBootApplication
#Configuration
class Main {
#Bean
Race race(Car car) {
// ...
}
#Bean
Car car() {
// ...
}
}
And I have 2 separate integration tests that I want to have to separate Car implementations provided.
#Slf4j
#SpringApplicationConfiguration
class OneIntegrationSpec extends AbstractIntegrationSpec {
#Configuration
#Import(Main.class)
static class Config {
#Bean
Car oneTestCar() {
return new FerrariCar();
}
}
}
#Slf4j
#SpringApplicationConfiguration
class OtherIntegrationSpec extends AbstractIntegrationSpec {
#Configuration
#Import(Main.class)
static class Config {
#Bean
Car otherTestCar() {
return new TeslaCar();
}
}
}
When I run one of these I am getting: NoUniqueBeanDefinitionException cause Spring detects there are multiple car implementations.
How to make test inner class Config with #Configuration annotation being loaded only for that particular test?
I saw the approach with #Profile but that would mean creating separate profiles names for each IntegrationSpec which is a little bit violating a DRY. Is there another approach than #ActiveProfiles?
I'm finding it hard to understand your use-case. Do you have to initialize entire applicationContext to test FerrariCar and TeslaCar? Can't you test them in isolation?
If integration test is the only way to go, you could try excludeFilters in #ComponentScan to disable auto-detecting your test config, as illustrated in https://stackoverflow.com/a/30199808/1553203 . You can then add specific test #Configuration for each Spec/Test by using #Import/#ComponentScan.

Spring Boot override interface in test

I have a springboot app (v1.3.5)
My main application goes:
#SpringBootApplication
#ComponentScan({"com.akrome"})
public class InboxCoreApplication {
public static void main(String[] args) {
SpringApplication.run(InboxCoreApplication.class, args);
}
}
Somewhere in the packages I have a repo defined as an implementation:
#Repository
public class CouchbaseServiceImpl implements CouchbaseService {
And somewhere else I have a class using that interface:
#Repository
public class InboxObjectRepositoryImpl {
#Autowired
private CouchbaseService couchbaseService;
I also have a test version of the interface:
#Repository
public class CouchbaseServiceTestImpl implements CouchbaseService {
Now. In my test, I want to simply re-point the interface mapping to the test implementation, while keeping everything else as defined by the main config component scan. But it keeps going bananas. The closest I got is:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {InboxCoreApplication.class, TestConfig.class})
public class InboxServiceTest {
#Autowired
CouchbaseServiceTestImpl couchBaseServiceTestImpl;
Where TestConfig is:
#Configuration
public class TestConfig {
CouchbaseServiceTestImpl couchbaseServiceTestImpl = new CouchbaseServiceTestImpl();
public CouchbaseService couchbaseService() {
return couchbaseServiceTestImpl;
}
}
But every time either it complains about duplicated bean (bc it sees both implementations), or it injects 2 different instances of the test implementation, one in the test class and one in the actual program (wat?).
Any suggestion?
i think your problem is with following class. why are you re-implement the CouchbaseService interface and make it as a test version. What is the real point of doing that? you Unit test class should be developed to test the functionality of your main source. you are creating a test version of that source class and create a unit test to test that test version. What is the point of doing that?
#Repository
public class CouchbaseServiceTestImpl implements CouchbaseService {
So the better implementation should be, remove the CouchbaseServiceTestImpl class. write the unit test to directly Test the CouchbaseServiceImpl. (you can remove the TestConfig class also)
so the code should be like this.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {InboxCoreApplication.class})
public class InboxServiceTest {
#Autowired
CouchbaseServiceImpl couchbaseServiceImpl ;
The real reason behind the complaining about the duplicated bean can be described as follows.
If you annotated any class with #Repository, #Component or #Service, the spring IOC container will automatically instantiate those classes and register then in the application context when the Spring application context is loaded.
So you have two implementation of the CouchbaseService interface.
1. CouchbaseServiceImpl
2. CouchbaseServiceTestImpl
since both of these classes have been annotated with #Repository, the spring application container will instantiate both of these classes and will maintain both of those objects in the IOC container. both these instance can be assigned to CouchbaseService interface reference.
look at the following line in your code.
#Autowired
private CouchbaseService couchbaseService;
Now the spring application container is in a doubtful situation to find out the relevant matching bean( whether it should use CouchbaseServiceImpl or CouchbaseServiceTestImpl. both of the beans are eligible for the assignment). Therefore it warns about the duplicate and does not do the assignment.
You have two options.
option 01.
remove the #Repository annotation in one of those classes. preferably remov it in CouchbaseServiceTestImpl. (better to remove that class as i have explained earlier)
option 02
keep all the classes as you wanted. user #Qualifier to tell the spring application container about the real matching bean for the assignment.
so the code should be changed as follows.
#Repository("couchbaseServiceTestImpl")
public class CouchbaseServiceTestImpl implements CouchbaseService {
#Repository("couchbaseServiceImpl")
public class CouchbaseServiceImpl implements CouchbaseService {
#Repository
public class InboxObjectRepositoryImpl {
#Autowired
#Qualifier("couchbaseServiceTestImpl")
private CouchbaseService couchbaseService;
So it is going to assign an instance of CouchbaseServiceTestImpl to CouchbaseService reference.
I think I found it.
It's supposed to user profiles:
#Repository
#Profile("application")
public class CouchbaseServiceImpl implements CouchbaseService {
and
#Repository
#Profile("test")
public class CouchbaseServiceTestImpl implements CouchbaseService {
and in my test I simply:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {InboxCoreApplication.class})
#ActiveProfiles("test")
public class InboxObjectRepositoryImplTest {

What is the use case of #Import annotation?

According to official doc:
Annotation Type Configuration
Indicates that a class declares one or more #Bean methods and may be
processed by the Spring container to generate bean definitions...
#Configuration classes may be composed using the #Import annotation,
not unlike the way that works in Spring XML. Because
#Configuration objects are managed as Spring beans within the
container..
But i can also use #Configuration annotation without #Import. I have tested the code listed below and it works as expected. So what is the purpose to use #Import?
DispatcherServletInitializer
public class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
WebMvcConfigurerAdapter
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "package.name" })
// #Import(OptionalConfig.class)
public class WebConfig extends WebMvcConfigurerAdapter {
// ...
}
OptionalConfig
#Configuration
public class OptionalConfig {
#Bean(name = "myClass")
public MyClass myClass() {
return new MyClass();
}
}
Service
#Service
public class MyServiceImpl implements MyService {
#Autowired
private MyClass myClass; // yes, it works
// ...
}
If component scanning is enabled, you can split bean definitions in multi #Configuration classes without using #Import. And you don't need to provide all of them to the application context constructor.
I think the main purpose for #Import is to provide you a way to simplify multiple configurations registration if you'd like to avoid component scanning (as of Spring Framework 4.2, per reference manual).
There's a note in Spring Reference Documentation about #Import usage:
As of Spring Framework 4.2, #Import also supports references to regular component classes, analogous to the AnnotationConfigApplicationContext.register method. This is particularly useful if you’d like to avoid component scanning, using a few configuration classes as entry points for explicitly defining all your components.
Thus far, we've seen how to break up bean definitions into multiple #Configuration classes and how to reference those beans across #Configuration boundaries. These scenarios have required providing all #Configuration classes to the constructor of a JavaConfigApplicationContext, and this is not always ideal. Often it is preferable to use an aggregation approach, where one #Configuration class logically imports the bean definitions defined by another.
The #Import annotation provides just this kind of support, and it is the direct equivalent of the <import/> element found in Spring beans XML files.
http://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch04s03.html
I found a use of using the #Import annotation. I don't think it's the use case.
If you are developing a set of libraries using Spring, probably you don't have a SpringBootApplication. So, you have not enabled any auto-scan to resolve beans.
If a bean declared in a configuration file in the library library-a is referred through dependency injection in library-b you need to use #Import to tell Spring how to resolve the bean.
As we said, in library-a you have this configuration file.
#Configuration
public class ConfigurationA {
#Bean
public BeanA beanA() {
return new BeanA();
}
}
In library-b you must have this configuration file if you want to use BeanA
#Configuration
#Import(ConfigurationA.class)
public class ConfigurationB {
#Bean
public BeanB beanB(BeanA beanA) {
return new BeanB(beanA);
}
}
Hope it helps.
With component scanning enabled it's difficult to immediately see where #Import adds value if your view of the world is limited to your own application and its packages. Where it can help is if you are importing bean libraries with their own package structure that you don't want to component scan.
You can place such libraries on your classpath and use #Import to cherry-pick #Configuration classes from within them. That's why it's often referred to as composition because you are composing your #Configuration class from multiple sources.
A typical use case of #Import is when teams develop REST services and realize they need some common configuration.
Every service will have its own namespace i.e. org.mybusiness.specific and will have its #SpringBootApplication (and therefore component scan root) start at this package.
On the other hand, the common library will have its namspace set to org.mybusiness.common and will therefore be out of reach for component scanning.
Thus the need to reference the common configuration class using #Import(CommonConfig.class)
#Import works great when for some reasons you need to register not all but some of the components from a package. In such case #ComponentScan would be too complicated.
See details in the Spring boot component scan include a single class question.

Categories

Resources