How to override #Configuation which is present under src/main/java with #TestConfiguration during unit tests?
#Configuration
public class AppConfig {
#Bean
public EmployeeService employeeService(){
return new EmployeeService();
}
}
#Component
public class ServerStartSetup implements CommandLineRunner {
#Autowired
private EmployeeService employeeService;
public void run(String... args) {
// do something with employee service
}
}
I would like to override the above bean with some below custom bean for testing purposes.
#TestConfiguration
public class TestAppConfig {
#Bean
public EmployeeService employeeService(){
return new FakeEmployeeService();
}
}
#SpringBootTest
#Import(TestAppConfig.class)
public class UnitTest {
}
However AppConfig does not seem to be skipped. That is , it throws an error saying that there is a bean with same name employeeService. If I rename bean method name in the TestAppConfig, it injects the bean created via AppConfig.
How to fix this.?
Note: One possible solution is using #Profile. I am looking for anything other than using Profiles.
I tested locally and found that changing the method name or #Bean to #Bean("fakeEmployeeService") and adding the #Primary annotation works.
#SpringBootTest
class DemoApplicationTests {
#Autowired
private EmployeeService employeeService;
#TestConfiguration
static class TestConfig {
//#Bean("fakeEmployeeService")
#Bean
#Primary
public EmployeeService employeeServiceTest() {
return new EmployeeService() {
#Override
public void doSomething() {
System.out.println("Do something from test...");
}
};
}
}
...
}
If we want to override a bean definition in #TestConfiguration, we need:
To use the same name as the overridden bean. (Otherwise it would be an "additional" bean and we could get conflict/'d have to qualify/primary)
Since spring-boot:2.1: spring.main.allow-bean-definition-overriding=true (set this in tests ONLY!plz!)
#ref
Then, with:
#TestConfiguration
public class TestAppConfig {
#Bean // when same name, no #Primary needed
public EmployeeService employeeService(){ // same name as main bean!
return new FakeEmployeeService();
}
}
We can do that:
#Import(TestAppConfig.class)
#SpringBootTest(properties = "spring.main.allow-bean-definition-overriding=true")
public class UnitTest {
... // EmployeeService will be "fake", the rest is from "main config"
You can mock the AppConfig bean in your test like this:
#MockBean
private AppConfig config;
Or, like you said, just use profiles.
Using SpringBoot, I have a Component bean that is defined as #Scope("protoype"). It also injects another prototype bean
The class is defined as
#Component
#Scope("prototype")
public class MyClass{
#Autowired
public BeanFactory beanFactory
private InjectedBean injectedBean
public MyClass(DataObj data) {
this.injectedBean = beanFactory.getBean(InjectedBean.class, data)
}
}
However, IntelliJ complains about the data field on the constructor: Could not autowire. No beans of 'DataObj' type found.. But DataObj is a POJO. I pass it in at runtime in order to create the bean. Am I defining the constructor incorrectly?
Update
Had the same problem doing it this way. It still wants to treat DataObj as a bean on the factory constructor class. Doesn't matter if I annotate the class with #Component or #Configuration
#Component
public class MyClass{
#Autowired
public BeanFactory beanFactory
private InjectedBean injectedBean
public MyClass(InjectedBean injectedBean) {
this.injectedBean = injectedBean;
}
#Bean
#Scope("prototype")
public MyClass myClass(DataObj data) {
InjectedBean injectedBean = beanFactory.getBean(InjectedBean.class, data)
return new MyClass(injectedBean);
}
}
Also tried this example from that same link:
#Configuration
public class ServiceConfig {
#Bean
public Function<DataObj, MyClass> thingFactory() {
return data-> myClass(data); //
}
#Bean
#Scope(value = "prototype")
public MyClass myClass(DataObj data) {
return new MyClass(data);
}
}
Update
I think I resolved this with some information in Spring Java Config: how do you create a prototype-scoped #Bean with runtime arguments?. Part of my problem is that I tried to put the factory bean in the Component itself, which doesn't work
In other words
#Component
public class MyClass{
#Autowired
public BeanFactory beanFactory
private InjectedBean injectedBean
public MyClass(InjectedBean injectedBean) {
this.injectedBean = injectedBean;
}
#Bean
#Scope("prototype")
public MyClass myClass(DataObj data) {
InjectedBean injectedBean = beanFactory.getBean(InjectedBean.class, data)
return new MyClass(injectedBean);
}
}
In this cass, Spring tries to create a MyClass bean because of the #Component annotation, but another MyClass bean due to the #Bean annotation.
So I moved the #Bean to another class
#Configuration
public class ServiceConfig {
#Bean
public Function<DataObj, MyClass> thingFactory() {
return data-> myClass(data); //
}
#Bean
#Scope(value = "prototype")
public MyClass myClass(DataObj data) {
return new MyClass(data);
}
}
This appears to work, but IntelliJ still complains about DataObj. This might be an Intellij issue
I have the situation where a protoype bean contains a singleton bean. In order to achieve it, I had to create 2 configuration classes. Is it possible to merge my 2 confgiuration classes into a single one?
Singleton class:
public class MySingleton {
}
Prototype class:
public class MyPrototype {
private MySingleton b;
public MyPrototype(MySingleton b) {
this.b = b;
}
}
Configuration class 1:
#Configuration
public class ConfigClassA {
#Bean
public MySingleton myBean() {
return new MySingleton();
}
}
Configuration class 2:
#Configuration
public class ConfigClassB {
#Autowired
public MySingleton mb;
#Bean
#Scope("prototype")
public MyPrototype myPrototype() {
return new MyPrototype(mb);
}
}
Try this:
#Configuration
public class ConfigClass {
#Bean
public MySingleton myBean() {
return new MySingleton();
}
#Bean
#Scope("prototype")
public MyPrototype myPrototype(MySingleton myBean) {
return new MyPrototype(myBean);
}
}
The BeanFactory should search for a bean of type MySingleton when creating the prototype bean and inject it into the method myPrototype.
With an XML configured Spring bean factory, I can easily instantiate multiple instances of the same class with different parameters. How can I do the same with annotations? I would like something like this:
#Component(firstName="joe", lastName="smith")
#Component(firstName="mary", lastName="Williams")
public class Person { /* blah blah */ }
It's not possible. You get a duplicate exception.
It's also far from optimal with configuration data like this in your implementation classes.
If you want to use annotations, you can configure your class with Java config:
#Configuration
public class PersonConfig {
#Bean
public Person personOne() {
return new Person("Joe", "Smith");
}
#Bean
public Person personTwo() {
return new Person("Mary", "Williams");
}
}
Yes, you can do it with a help of your custom BeanFactoryPostProcessor implementation.
Here is a simple example.
Suppose we have two components. One is dependency for another.
First component:
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
public class MyFirstComponent implements InitializingBean{
private MySecondComponent asd;
private MySecondComponent qwe;
public void afterPropertiesSet() throws Exception {
Assert.notNull(asd);
Assert.notNull(qwe);
}
public void setAsd(MySecondComponent asd) {
this.asd = asd;
}
public void setQwe(MySecondComponent qwe) {
this.qwe = qwe;
}
}
As you could see, there is nothing special about this component. It has dependency on two different instances of MySecondComponent.
Second component:
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
#Qualifier(value = "qwe, asd")
public class MySecondComponent implements FactoryBean {
public Object getObject() throws Exception {
return new MySecondComponent();
}
public Class getObjectType() {
return MySecondComponent.class;
}
public boolean isSingleton() {
return true;
}
}
It's a bit more tricky. Here are two things to explain. First one - #Qualifier - annotation which contains names of MySecondComponent beans. It's a standard one, but you are free to implement your own. You'll see a bit later why.
Second thing to mention is FactoryBean implementation. If bean implements this interface, it's intended to create some other instances. In our case it creates instances with MySecondComponent type.
The trickiest part is BeanFactoryPostProcessor implementation:
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
Map<String, Object> map = configurableListableBeanFactory.getBeansWithAnnotation(Qualifier.class);
for(Map.Entry<String,Object> entry : map.entrySet()){
createInstances(configurableListableBeanFactory, entry.getKey(), entry.getValue());
}
}
private void createInstances(
ConfigurableListableBeanFactory configurableListableBeanFactory,
String beanName,
Object bean){
Qualifier qualifier = bean.getClass().getAnnotation(Qualifier.class);
for(String name : extractNames(qualifier)){
Object newBean = configurableListableBeanFactory.getBean(beanName);
configurableListableBeanFactory.registerSingleton(name.trim(), newBean);
}
}
private String[] extractNames(Qualifier qualifier){
return qualifier.value().split(",");
}
}
What does it do? It goes through all beans annotated with #Qualifier, extract names from the annotation and then manually creates beans of this type with specified names.
Here is a Spring config:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="MyBeanFactoryPostProcessor"/>
<bean class="MySecondComponent"/>
<bean name="test" class="MyFirstComponent">
<property name="asd" ref="asd"/>
<property name="qwe" ref="qwe"/>
</bean>
</beans>
Last thing to notice here is although you can do it you shouldn't unless it is a must, because this is a not really natural way of configuration. If you have more than one instance of class, it's better to stick with XML configuration.
I just had to solve a similar case. This may work if you can redefine the class.
// This is not a #Component
public class Person {
}
#Component
public PersonOne extends Person {
public PersonOne() {
super("Joe", "Smith");
}
}
#Component
public PersonTwo extends Person {
public PersonTwo() {
super("Mary","Williams");
}
}
Then just use PersonOne or PersonTwo whenever you need to autowire a specific instance, everywhere else just use Person.
Inspired by wax's answer, the implementation can be safer and not skip other post-processing if definitions are added, not constructed singletons:
public interface MultiBeanFactory<T> { // N.B. should not implement FactoryBean
T getObject(String name) throws Exception;
Class<?> getObjectType();
Collection<String> getNames();
}
public class MultiBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
Map<String, MultiBeanFactory> factories = beanFactory.getBeansOfType(MultiBeanFactory.class);
for (Map.Entry<String, MultiBeanFactory> entry : factories.entrySet()) {
MultiBeanFactory factoryBean = entry.getValue();
for (String name : factoryBean.getNames()) {
BeanDefinition definition = BeanDefinitionBuilder
.genericBeanDefinition(factoryBean.getObjectType())
.setScope(BeanDefinition.SCOPE_SINGLETON)
.setFactoryMethod("getObject")
.addConstructorArgValue(name)
.getBeanDefinition();
definition.setFactoryBeanName(entry.getKey());
registry.registerBeanDefinition(entry.getKey() + "_" + name, definition);
}
}
}
}
#Configuration
public class Config {
#Bean
public static MultiBeanFactoryPostProcessor() {
return new MultiBeanFactoryPostProcessor();
}
#Bean
public MultiBeanFactory<Person> personFactory() {
return new MultiBeanFactory<Person>() {
public Person getObject(String name) throws Exception {
// ...
}
public Class<?> getObjectType() {
return Person.class;
}
public Collection<String> getNames() {
return Arrays.asList("Joe Smith", "Mary Williams");
}
};
}
}
The bean names could still come from anywhere, such as wax's #Qualifier example. There are various other properties on the bean definition, including the ability to inherit from the factory itself.
Continuing #espen answer, injecting beans with qualifiers and configuring them differently with external values.
public class Person{
#Configuration
public static class PersonConfig{
#Bean
//#Qualifier("personOne") - doesn't work - bean qualifier is method name
public Person personOne() {
return new Person("Joe", "Smith");
}
#Bean
//#Qualifier("personTwo") - doesn't work - bean qualifier is method name
public Person personTwo(#Value("${myapp.second.lastName}") String lastName) {
return new Person("Mary", lastName);
}
}
/* blah blah */
}
#Component
public class SomePersonReference{
#Autowired
#Qualifier("personTwo")
Person marry;
}
Should you need to inject, in the new created object, beans or properties from the spring context, you can have a look at the following section of code in which I have extended the Espen answer by injecting a bean which is created from the spring context:
#Configuration
public class PersonConfig {
#Autowired
private OtherBean other;
#Bean
public Person personOne() {
return new Person("Joe", "Smith", other);
}
}
Have a look at this article for all the possibile scenarios.
I am using Autowired annotation in a Service class to inject Dao dependency. The code I have written is:
public interface Service {
public List<String> getAllCountries() ;
}
public class ServiceImpl implements Service {
#Autowired
private TestDao testDao;
public TestDao getTestDao() {
return testDao;
}
public void setTestDao(TestDao testDao) {
this.testDao = testDao;
}
public List<String> getAllCountries() {
// TODO Auto-generated method stub
System.out.println("coming in here:" + testDao);
return testDao.getAllCountries();
}
}
public interface TestDao {
List<String> getAllCountries() ;
}
public class TestDaoImpl implements TestDao{
#Override
public List<String> getAllCountries() {
// TODO Auto-generated method stub
List<String> ls = new ArrayList<>();
ls.add("test1");
return ls;
}
}
And a controller
public class TestController {
public void doSth() {
Service service = new ServiceImpl();
try {
System.out.println("service obj:" + service);
List<String> list = service.getAllCountries();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Beans.xml:
<context:annotation-config/>
<bean id="controller" class="org.test.TestController"/>
<bean id="testDao" class="org.test.TestDaoImpl"/>
And a main class:
ApplicationContext context = new
ClassPathXmlApplicationContext("beans4.xml");
TestController obj= (TestController) context.getBean("controller");
obj.doSth();
But it is throwing NullPointerException in ServiceImpl class. These all classes are in the same package.
Can somebody help me understand what exactly is the issue?
Solution:
public class TestController {
#Autowired
Service service;
//remaining code as it is
}
Your Service class is not managed by Spring . Hence, the dependency is getting injected.
To make your service class managed,
You can use #Service stereo type annotation and do a component scan.
I mean,
package com;
#Service
public class ServiceImpl implement XXXX{
//Your changes
}
and in spring config file:
<context:component-scan base-package="com"/>
I did not see any bean declaration or annotation for the ServiceImpl.
//You canot do it like the following
#controller
class A{
ServiceImpl simp=new ServiceImpl();
}
class ServiceImpl{
#Autowired
Adao adoa;//throws NPE
}
//it should be like this
#Controller
class A{
#autowired
ServiceImpl simp;
}
#Service
class ServiceImpl{
#Autowired
Adao adoa;
}
#Repository
class Adao{
}
Your testDao dependency bean has not been injected and you need the Spring container that you are using annotations and also specify which packages need to be scanned for Autowiring by adding the following:
<context:annotation-config />
<context:component-scan base-package="org.test" />
You can look here on how you can auto wire spring beans.