How to add a bean dependency from another module in Spring? - java

I have two modules: module1 and module2.
module2 depends on module1.
Configuration in the module1:
#Configuration
public class ConfigurationInModule1 {
#Bean
public FirstBean firstBean() {
return new FirstBean();
}
#Bean
public SecondBean secondBean(FirstBean firstBean) {
return new SecondBean(firstBean);
}
}
Configuration in the module2:
#Configuration
public class ConfigurationInModule2 {
#Bean
public SomeBeanForModule2 beanForModule2(FirstBean firstBean) {
return new SomeBeanForModule2(firstBean);
}
}
As you can see both beans secondBean and beanForModule2 depends on firstBean.
I need to make sure that when the project is compiled with module2 then beanForModule2 should be initialized before secondBean. If there is no module2 then secondBean should be initialized in a standard flow.
Is it possible to configure it in Spring?
P.S. I need to control the order of been initialization. I know that there is a special annotation #DependsOn which can be used to setup indirect dependency, but in my case I cannot use it on secondBean because the dependency beanForModule2 is optional and is placed in another module.

Spring takes care of order of bean initialization, So if one bean depends on other then Spring will first initialize dependency Beans then it will initialize dependent Beans.
In your case FirstBean will always initialize prior than SomeBeanForModule2 without any additional config.
And if Dependency Bean which is FirstBean in your case is not declared(i.e module1 is not there) then Spring will throw org.springframework.beans.factory.NoSuchBeanDefinitionException. So module2 cannot initialized without module1.
EDIT:-
For Ordering of Bean Initialization, you can use #DependsOn even if Beans are in seperate File.
Just add #import(ConfigurationInModule2.class) in ConfigurationInModule1 class in your module1.
And use #DependsOn("beanForModule2") on secondBean.
This will help:- https://stackoverflow.com/a/16297827/4720870

Found the solution by using BeanFactoryPostProcessor. We need to define our custom BeanFactoryPostProcessor and setup necessary dependencies there.
Spring won't execute beans initialization before calling postProcessBeanFactory method.
To solve the above problem we should define our custom BeanFactoryPostProcessor like this one:
public class JBCDependencyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("secondBean");
beanDefinition.setDependsOn("beanForModule2");
}
}
After that we should make a static bean with our BeanFactoryPostProcessor. Something like this:
#Configuration
public class ConfigurationInModule2 {
#Bean
public static BeanFactoryPostProcessor dependencyBeanFactoryPostProcessor() {
return new JBCDependencyBeanFactoryPostProcessor();
}
#Bean
public SomeBeanForModule2 beanForModule2(FirstBean firstBean) {
return new SomeBeanForModule2(firstBean);
}
}
Spring will search for all beans. Then it will execute postProcessBeanFactory in our BeanFactoryPostProcessor. We will make a dependency from secondBean to beanForModule2 and then spring will call bean initialization by following our dependencies.
P.S. Thanks to #Tarun for sharing the link.

Related

Configuring Spring's #DataJpaTest that overrides default application context beans

I'm struggling with configuration for my #DataJpaTest. I'd like to take advantage of auto-configured spring context provided by #DataJpaTest, but I'd like to override some of it's beans.
This is my main class:
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public CommandLineRunner commandLineRunner(BookInputPort bookInputPort) {
return args -> {
bookInputPort.addNewBook(new BookDto("ABC", "DEF"));
bookInputPort.addNewBook(new BookDto("GHI", "JKL"));
bookInputPort.addNewBook(new BookDto("MNO", "PRS"));
};
}
As you can clearly see, I provide my implementation for CommandLineRunner that depends on some service.
I also have a test:
#DataJpaTest
public class BookRepositoryTest {
public static final String TITLE = "For whom the bell tolls";
public static final String AUTHOR = "Hemingway";
#Autowired
private BookRepository bookRepository;
#Test
public void testRepository() {
Book save = bookRepository.save(new Book(TITLE, AUTHOR));
assertEquals(TITLE, save.getTitle());
assertEquals(AUTHOR, save.getAuthor());
}
}
When I run the test I get the following error:
No qualifying bean of type 'com.example.demo.domain.book.ports.BookInputPort' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
That makes perfect sense! Auto-configured test provides implementation only for a 'slice' of the context. Apparently implementation of BookInputPort is missing. I do not need this commandLineRunner in a test context. I get create a commandLineRunner that does not depend on any service.
I can try to solve the issue by adding to my test class nested class :
#TestConfiguration
static class BookRepositoryTestConfiguration {
#Bean
CommandLineRunner commandLineRunner() {
return args -> {
};
}
}
That solves the problem. Kind of. If I had more tests like this I would have to copy-paste this nested class to each test class. It's not an optimal solution.
I tried to externalize this to a configuration that could be imported by #Import
This is the config class:
#Configuration
public class MyTestConfiguration {
#Bean
public CommandLineRunner commandLineRunner() {
return args -> {
};
}
}
But then the application fails with a message:
Invalid bean definition with name 'commandLineRunner' defined in com.example.demo.DemoApplication: Cannot register bean definition
I checked that error and other folks on SO suggested in this case:
#DataJpaTest(properties = "spring.main.allow-bean-definition-overriding=true")
I did that and I got:
No qualifying bean of type 'com.example.demo.domain.book.ports.BookInputPort' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
That is exactly the same problem I started with.
I took all these steps and found myself in the place where I was at the very beginning.
Do you have any idea how to solve this issue? I do not have a vague idea or clue.
#DataJpaTest generally starts scanning from current package of the test class and scans upwards till it finds the class annotated with #SpringBootConfiguration. So creating a SpringBootConfiguration class at the repository root package will create only beans defined within that package. On top of that we can add any customized test bean required for our test class in that configuration class.
#SpringBootConfiguration
#EnableAutoConfiguration
public class TestRepositoryConfig {
#Bean
public CommandLineRunner commandLineRunner() {
return args -> {
};
}
#Bean
public BookInputPort bootInputPort(){
return new BookInputPort();
}
}
In Spring Boot test class you can do it like this
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class YourClassTest {
#Autowired
private YourService yourService;
It does load all the services and I am using it perfectly fine
How to load specific entities in one environment and not the other
You can use #Profile annotation in your entities.
Put #Profile({"prod"}) on entities/services you don't want to load in test run, and
Put #Profile({"prod", "test"}) on entities/services you want to load in both test and prod environments
Then run the test with test profile. It will not load unnecessary entities.
You can put #Profile annotation on other services too.
The annotation #DataJpaTest only loads the JPA part of a Spring Boot application, the application context might not loaded because you have #DataJpaTest annotation. Try to replace the #DataJpaTest with #SpringBootTest.
As per Spring documentation:
By default, tests annotated with #DataJpaTest will use an embedded
in-memory database (replacing any explicit or usually auto-configured
DataSource). The #AutoConfigureTestDatabase annotation can be used to
override these settings. If you are looking to load your full
application configuration, but use an embedded database, you should
consider #SpringBootTest combined with #AutoConfigureTestDatabase
rather than this annotation.

Spring Boot: #TestConfiguration Not Overriding Bean During Integration Test

I have a Bean defined in a class decorated with #Configuration:
#Configuration
public class MyBeanConfig {
#Bean
public String configPath() {
return "../production/environment/path";
}
}
I have a class decorated with #TestConfiguration that should override this Bean:
#TestConfiguration
public class MyTestConfiguration {
#Bean
#Primary
public String configPath() {
return "/test/environment/path";
}
}
The configPath bean is used to set the path to an external file containing a registration code that must be read during startup. It is used in an #Component class:
#Component
public class MyParsingComponent {
private String CONFIG_PATH;
#Autowired
public void setCONFIG_PATH(String configPath) {
this.CONFIG_PATH = configPath;
}
}
While trying to debug this I set a breakpoint inside each method as well as the constructor of the test config class. The #TestConfiguration's constructor breakpoint is hit, so i know that my test configuration class instantiates, however the configPath method of that class is never hit. Instead, the configPath method of the normal #Configuration class is hit and the #Autowired String in MyParsingComponent is always ../production/environment/path rather than the expected /test/environment/path.
Not sure why this is happening. Any thoughts would be greatly appreciated.
As documented in the Detecting Test Configuration section of the Spring Boot reference manual, any beans configured in a top-level class annotated with #TestConfiguration will not be picked up via component scanning. So you have to explicitly register your #TestConfiguration class.
You can do that either via #Import(MyTestConfiguration.class) or #ContextConfiguration(classes = MyTestConfiguration.class) on your test class.
On the other hand, if your class annotated with #TestConfiguration were a static nested class within your test class, it would be registered automatically.
Make sure that the method name of your #Bean factory method does not match any existing bean name. I had issues with method names like config() or (in my case)
prometheusConfig() which collided with existing bean names. Spring skips those factory methods silently and simply does not call them / does not instantiate the beans.
If you want to override a bean definition in your test, use the bean name explicitly as string parameter in your #Bean("beanName") annotation.
Test configuration has to be explicitly imported in the test via #Import({MyTestConfiguration.class}).
The name of the #Bean methods in #Configuration and #TestConfiguration have to be different. At least it makes difference in Spring Boot v2.2.
Also make sure spring.main.allow-bean-definition-overriding=true otherwise the bean could not be overriden.
For me worked this code:
#TestConfiguration // 1. necessary
public class TestMessagesConfig {
#Bean
#Primary // 2. necessary
public MessageSource testMessageSource() { // 3. different method name than in production code e.g. add test prefix
}
}
I struggled with a related problem, whereby even though I was using an inner static class, my test bean was not being registered.
It turns out, You still need to add your inner static class to the #ContextConfiguration class array, otherwise the beans inside the #TestConfiguration doesn't get picked up.
public interface Foo {
String execute();
}
public class FooService {
private final Foo foo;
FooService(Foo foo) {
this.foo = foo;
}
public String execute() {
return foo.execute();
}
}
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {FooService.class, FooTest.FooTestConfig.class})
public class FooTest {
#Autowired
FooService fooService;
#Test
void test() {
Assertions.assertEquals("MY_TEST_BEAN", fooService.execute());
}
#TestConfiguration
static class FooTestConfig {
#Bean
public Foo getFooBean() {
return () -> "MY_TEST_BEAN";
}
}
}
I came across a similar issue recently and got it sorted out by annotating my testing bean with #Primary as well as #Bean. Not sure why it's required, which seems not documented in the Spring doc. The version of my SpringBoot is 2.0.3.
In my case it was an issue with #RunWith(SpringRunner.class), I'm not exactly sure why it wasn't working, I was following this - Testing in Spring Boot
But after replacing that with #ExtendWith(SpringExtension.class) the inner static #TestConfiguration class created the beans as expected.
Maybe a version mismatch - I'm using Spring Boot 2.7.2.
In my case replacing #Import(TestConfig.class) with #ContextConfiguration(classes=TestConfig.class) did the trick. For some reason, some of the beans from TestConfig but 1 wasn't until I replaced #Import with #ContextConfiguration.
This was also mentioned in some comments that were hidden because they had no upvotes.
I found it odd how several answers stated that the names of the #Beans have to be different from each other. How would that make one override the other?
There wasn't one specific answer that worked for me, but I've solved the issue by combining some of their advices.
Here's what worked for me.
Main configuration class:
#Configuration
public class SpringConfiguration {
#Bean
BeanInterface myBean() {
return new BeanImplementation();
}
#Bean
OtherClass otherBean() {
return new OtherClass();
}
}
Test configuration class:
#TestConfiguration
public class TestSpringConfiguration {
#Bean
#Primary
BeanInterface myBean() {
return new TestBeanImplementation();
}
}
Test class:
#SpringBootTest(classes = TestSpringConfiguration.class,
properties = "spring.main.allow-bean-definition-overriding=true")
public class Tests {
#Test
public void test() {
// do stuff
}
}
In this way, the "myBean" bean instance is the one defined in the TestSpringConfiguration class, while "otherBean" is the one defined in the SpringConfiguration class, since it's not overridden.
If I gave two different names to the "myBean" beans, the "real" one would still be initialized and, in my case, would give an error during tests, since it needs something that's only available at runtime in its proper environment.
Once I gave both the same name, Spring would throw an error saying that they were conflicting. Hence why I had to specify the property spring.main.allow-bean-definition-overriding=true in the #SpringBootTest annotation of the test class.
By the way, if you're NOT using Spring Boot, I guess these alternative annotations could work for you:
#ExtendWith(value = SpringExtension.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, // <- not sure about this one
classes = { SpringConfiguration.class, TestSpringConfiguration.class })
public class Tests {
#Test
public void test() {
// do stuff
}
}
Then, you would still have to set the property spring.main.allow-bean-definition-overriding=true in the test application.yml or application.properties file, or in some other way via code on startup.
Note: I'm not 100% sure that you would need the loader = AnnotationConfigContextLoader.class thing. Try without it, first. I needed it in a project of mine which had Spring without Boot, but I can't remember whether it's a standard thing to set or I needed it for some specific reason.

How to get a spring bean in a bean-defining method

I have a java config where ServiceB depends on ServiceA:
#Bean
ServiceA getServiceA() { return new ServiceA(); }
#Bean
ServiceB getServiceB() { return new ServiceB(getServiceA()); }
Then I want to declare ServiceA (but no ServiceB) as a component. I add #ScanPackage to config and annotate ServiceA:
#Component
class ServiceA { .. }
How to declare method getServiceB() now?
Spring auto-injects method parameters by type for Bean defining methods:
#Bean
ServiceB getServiceB(ServiceA serviceA) {
return new ServiceB(serviceA);
}
Now you don't have to worry about how ServiceA is provided.
As Rohan already wrote in his answer, Spring's #Bean annotation can inject dependencies of other Spring beans, in the same manner as constructor-based dependency injection does.
I would just add that there are also other possibilities to do dependency injection, when defining a bean in java config. #Configuration annotated class is a Spring bean as any other Spring bean, so you can auto-wire a dependency, as is usually done in Spring and then use this dependency when defining your #Bean, like:
#Autowired
private ServiceA serviceA;
#Bean
public ServiceB getServiceB() {
return new ServiceB(serviceA);
}
Since Spring Framework 4.3, you are also able to do constructor injection in #Configuration classes - which is yet another way to inject dependencies.
See more details in spring documentation.

Why do I not need #Autowired on #Bean methods in a Spring configuration class?

Why does this work:
#Configuration
public class MyConfig {
#Bean
public A getA() {
return new A();
}
#Bean // <-- Shouldn't I need #Autowired here?
public B getB(A a) {
return new B(a);
}
}
Thanks!
#Autowire lets you inject beans from context to "outside world" where outside world is your application.
Since with #Configuration classes you are within "context world ", there is no need to explicitly autowire (lookup bean from context).
Think of analogy like when accessing method from a given instance. While you are within the instance scope there is no need to write this to access instance method, but outside world would have to use instance reference.
Edit
When you write #Configuration class, you are specifying meta data for beans which will be created by IOC.
#Autowire annotation on the other hand lets you inject initialized beans, not meta-data, in application. So, there is no need for explicit injection because you are not working with Beans when inside Configuration class.
Hi Jan your question is marked as answered over 4 years ago but I've found a better source:
https://www.logicbig.com/tutorials/spring-framework/spring-core/javaconfig-methods-inter-dependency.html
here's another article with the same idea:
https://dzone.com/articles/spring-configuration-and, it also states that such usage is not well documented which I found true. (?)
so basically if beanA's initialization depends on beanB, spring will wire them without explicit #Autowired annotation as long as you declare these two beans in the application context (i.e. #Configuartion class).
A class with #Configuration annotation is where you're defining your beans for the context. But a spring bean should define its own dependencies. Four your case B class should be defining it's own dependencies in class definition. For example if your B class depends on your A class than it should be like below:
public class B
{
#Autowired
A aInstance;
public A getA()
{
return aInstance;
}
public void setA(A a)
{
this.aInstance = a;
}
}
In above case while spring is building its context it looks for the bean which's type is A which is also defined as a Bean at your configuration class and Autowires it to B at runtime so that B can use it when required.

How to inject dependencies into a self-instantiated object in Spring?

Let's say we have a class:
public class MyClass {
#Autowired private AnotherBean anotherBean;
}
Then we created an object of this class (or some other framework have created the instance of this class).
MyClass obj = new MyClass();
Is it possible to still inject the dependencies? Something like:
applicationContext.injectDependencies(obj);
(I think Google Guice has something like this)
You can do this using the autowireBean() method of AutowireCapableBeanFactory. You pass it an arbitrary object, and Spring will treat it like something it created itself, and will apply the various autowiring bits and pieces.
To get hold of the AutowireCapableBeanFactory, just autowire that:
private #Autowired AutowireCapableBeanFactory beanFactory;
public void doStuff() {
MyBean obj = new MyBean();
beanFactory.autowireBean(obj);
// obj will now have its dependencies autowired.
}
You can also mark your MyClass with #Configurable annotation:
#Configurable
public class MyClass {
#Autowired private AnotherClass instance
}
Then at creation time it will automatically inject its dependencies. You also should have <context:spring-configured/> in your application context xml.
Just got the same need and in my case it was already the logic inside non Spring manageable java class which had access to ApplicationContext. Inspired by scaffman.
Solved by:
AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(manuallyCreatedInstance);
I used a different approach. I had spring loaded beans that I wanted to call from my extended classes of a third-party library that created its own threads.
I used approach I found here https://confluence.jaytaala.com/display/TKB/Super+simple+approach+to+accessing+Spring+beans+from+non-Spring+managed+classes+and+POJOs
In the non-managed class:
{
[...]
SomeBean bc = (SomeBean) SpringContext.getBean(SomeBean.class);
[...]
bc.someMethod(...)
}
And then as a helper class in the main application:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class SpringContext implements ApplicationContextAware
{
private static ApplicationContext context;
public static <T extends Object> T getBean(Class<T> beanClass)
{
return context.getBean(beanClass);
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException
{
SpringContext.context = context;
}
}
I wanted to share my solution that follows the #Configurable approach as briefly mentioned in #glaz666 answer because
The answer by #skaffman is nearly 10 years old, and that does not mean not good enough or does not work
The answer by #glaz666 is brief and didn't really help me solve my problem but, did point me in the right direction
My setup
Spring Boot 2.0.3 with Spring Neo4j & Aop starts (which is irrelevant anyway)
Instantiate a bean when Spring Boot is ready using #Configurable approach (using ApplicationRunner)
Gradle & Eclipse
Steps
I needed to follow the steps below in order to get it working
The #Configurable(preConstruction = true, autowire = Autowire.BY_TYPE, dependencyCheck = false) to be placed on top of your Bean that is to be manually instantiated. In my case the Bean that is to be manually instantiated have #Autowired services hence, the props to above annotation.
Annotate the Spring Boot's main XXXApplicaiton.java (or the file that is annotated with #SpringBootApplication) with the #EnableSpringConfigured and #EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
Add the dependencies in your build file (i.e. build.gradle or pom.xml depending on which one you use) compile('org.springframework.boot:spring-boot-starter-aop') and compile('org.springframework:spring-aspects:5.0.7.RELEASE')
New+up your Bean that is annotated with #Configurable anywhere and its dependencies should be autowired.
*In regards to point #3 above, I am aware that the org.springframework.boot:spring-boot-starter-aop transitively pulls the spring-aop (as shown here mavencentral) but, in my case the Eclipse failed to resolve the #EnableSpringConfigured annotations hence, why I explicitly added the spring-aop dependency in addition to the starter. Should you face the same issue, just declare the dependency or go on adventure of figuring out
Is there a version conflict
Why the org.springframework.context.annotation.aspect.* is not available
Is your IDE setup properly
Etc etc.
This worked for me:
#Configuration
public class AppConfig {
#Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
See more information: https://docs.spring.io/spring-javaconfig/docs/1.0.0.m3/reference/html/creating-bean-definitions.html
Found the following way useful for my use case. Sharing here for reference, credit goes to the blogger entirely. This creates a static field and populates that from Spring and then provides a public static method which returns the field populated above.
https://sultanov.dev/blog/access-spring-beans-from-unmanaged-objects/

Categories

Resources