Dynamic Dependency Injection Spring - spring

I have following code inside my class
public void startListeners() throws Exception {
List<QueueConfiguration> queueConfigs = queueConfigResolver.getQueueConfigurations();
for(QueueConfiguration queueConfig : queueConfigs){
//TODO : work on this make it more testable
ICustomListener readerListener = new MyCustomListener(queueConfig);
readerListeners.add(readerListener);
readerListener.start();
}
}
I am using Spring for dependency injection(not in this case but overall). Now there two problems with this code.
I cannot put mock for each of the listeners created, while testing.
I dont want to use ApplicationContext.getBean() because it will have same affect. AFAIK spring cannot do this dynamically , but any other pointers?

As far as I can understand, you want to create a new bean instead of
ICustomListener readerListener = new MyCustomListener(queueConfig);
If that is the case, creating a factory for mycustomlistener and using
public abstract TestClient createTestClient();
to create your beans, and defining
<bean id="testClient" class="com.myproject.testbeans.TestClient" scope="prototype">
</bean>
<bean id="testClientFactory" class="com.myproject.testbeans.TestClientFactory">
<lookup-method name="createTestClient" bean="testClient" />
</bean>
in your context will solve your problem. This way, every time the createTestClient method of the factory is called, a new bean is created and given to your code. However, you have to give the config object via a setter instead of the constructor.

Related

Mocked object in a whole spring Batch test

I want to test a whole Spring Batch but Ihave a problem. I have a service to delete rows in a DB and in a LDAP.
For the BD I have implemented a H2 in-memory database so no problems.
For the LDAP it's more difficult to have a in-memory LDAP so I want to fake the DAO LDapRepository calling method "delete" (LDapRepository is the interface and LDapRepositoryImpl annotated by #Repository the implementation)
So I tried to inject a mock in my configuration but it doesn't work.
The test fails with a null pointer exception when I try to make the fake call ldapRepository.removePerson (so ldapRepository is not correctly injected).
How is it possible to substitute the bean LDapRepository in my configuration ?
Here's the test code :
#ContextConfiguration(locations = {"classpath:purge/job-test.xml"})
public class PurgeJobTest {
#Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
#InjectMocks
#Qualifier(value = "ldapRepositoryImpl")
private LdapRepository ldapRepository;
#Test
public void testExampleJob() throws Exception {
Mockito.doNothing().when(ldapRepository.removePerson(any(String.class)));
JobParameters jobParameters = new JobParametersBuilder()
.addString("fichierJob", "/purge/job-test.xml")
.addString("nomJob", "purgeJob").toJobParameters();
JobExecution exec =jobLauncherTestUtils.launchJob(jobParameters);
assertEquals(BatchStatus.COMPLETED, exec.getStatus());
}
}
OK so I admit my question was a little tricky but I want to share the answer to other people who faced a similar problem.
The null pointer exception was quite logic. Indeed the bean was not recognized in the context because nowhere it was correctly injected in it.
It's a common pitfall related by example in this post Injecting Mockito Mock objects using Spring JavaConfig and #Autowired.
or this other post : Injecting Mockito mocks into a Spring bean
So in my job-test.xml I commented the component-scan in which my "real" LdapRepository" and LdapRepositoryImpl were declared and replace them with :
<bean id="ldapRepository" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="purge.batch.repository.LdapRepository" />
</bean>
<bean id="ldapRepositoryImpl" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="purge.batch.repository.LdapRepositoryImpl" />
</bean>
(I could have place these bean declaration before the component-scan to give it priority)
And that works like a charm !

what is the usage of new keyword while using java configuration in spring

I have a question around the usage of new keyword being used when using java configuration in spring. What is the need of using new keyword
Refer below mentioned example:
Code implemented using Java Config
#Configuration
public class HelloWorldConfig {
#Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}
The above code will be equivalent to the following XML configuration
<beans>
<bean id = "helloWorld" class = "com.test.HelloWorld" />
</beans>
In XML config, we do not use new keyword whereas in java config we are using new keyword. can someone please explain the difference
In the XML configuration, you explain to the system what class should be instanciated (there is a "new" but it is behind the scene) but in the Java Config you actually have to return an instance so that is why we use the 'new' keyword. 'new' simply creates an instance of your class.
The two examples shown in question are not really equivalent.
What the
<beans>
<bean id="helloWorld"
class="com.test.HelloWorld" />
</beans>
really does, is it tells Spring to instantiate class com.test.HelloWorld, and name the resulting bean "helloWorld".
Then the java-config approach is not really doing this. Instead this follows the factory-method pattern, when we tell Spring, that the return value of the method is the bean, and the method name is the name of that bean.
An equivalent of that in XML would be the mentioned factory-method approach, which in this case would look something like this:
<beans>
<bean id="helloWorldConfig"
class="com.test.HelloWorldConfig" />
<bean id="helloWorld"
factory-bean="helloWorldConfig"
factory-method="helloWorld" />
</beans>
Note that there are several approaches to factory-method. In the above, we are assuming, the `helloWorldConfig" is the factory, and we're specifying the method on that bean. Theare are cases with static factory methods too. See here for more examples.
<beans>
<bean id = "helloWorld" class = "com.test.HelloWorld" />
</beans>
This XML configurations tells Spring to "create an instance of com.test.HelloWorld and put it in the bean context with bean id helloWorld".
#Configuration
public class HelloWorldConfig {
#Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}
In this Java configuration, we are returning an instance of com.test.HelloWorld. Because of the #Bean annotation, this instance is put into the bean context. As no specific bean id is given, the bean id is derived from the method hellowWorld() and thus becomes helloWorld.
As you can see, both configurations require an instance of com.test.HelloWorld. The XML configuration implicitly creates the instance whereas in the Java configuration you have to explicitly do it yourself.

How can I create the spring bean after all other beans?

For example, I have 3 beans in my spring configuration: A, B, C. And I want to create bean B and C as usual. And than (when all others beans were created) I want to ask spring to create bean A.
Any suggestion ?
Thanks.
Spring framework triggers a ContextRefreshedEvent once the contexts has been fully refreshed and all the configured beans have been created.
You could try to create a listener to catch that event and initialise bean A.
#Component
public class ContextRefreshedEventListener implements
ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
// Init your bean here
}
}
You should try #DependsOn adnotation
For example
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
I know it's not really a bean ordering answer, but maybe you can achieve your goal with a #PostConstruct method that will be called just after a bean is constructed, dependencies are injected and all properties are set.
best nas
Easier way to do this would be using #Lazy annotation to your bean. This makes your bean do not get initialized eagerly during context initialization. In simple words,your bean will get created when you ask for it, not before.
#Bean
#Lazy
public A beanA() {
//some code here
}

spring bean creation for java builder pattern objects

I'm using Google Gson(gson) library form reading/writing json files and spring mvc 3 in my web application side.
So In controller, I want to create a singleton instance of Gson with pretty printing. In java the code would be,
Gson gson = new GsonBuilder().setPrettyPrinting().create();
In Controller, I created an autowired entry as below,
#Autowired
private Gson gson;
and the xml bean configuration is as below,
<bean id="gsonBuilder" class="com.google.gson.GsonBuilder">
<property name="prettyPrinting" value="true"/>
</bean>
<bean id="gson" factory-bean="gsonBuilder" factory-method="create"/>
It throws the following exception in catalina logs,
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'prettyPrinting' of bean class [com.google.gson.GsonBuilder]: Bean property 'prettyPrinting' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1024)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:900)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:76)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:58)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1358)
I knew that the setter signature of setPrettyPrinting() is different than spring expects, that's why spring is throwing the exception.
public GsonBuilder setPrettyPrinting() {
prettyPrinting = true;
return this;
}
But I'm unable to find a way to wire the builder pattern beans. I'm pretty new to spring. Can any one let me know, whether it is possible to solve this problem in xml bean approach ?
Simply use a static factory method as described in the documentation and use Java code to create Java objects: it's sooo much easier and safe:
<bean id="gson"
class="com.foo.bar.MyGsonFactory"
factory-method="create"/>
and in MyGsonFactory:
public static Gson create() {
return new GsonBuilder().setPrettyPrinting().create();
}
The setPrettyPrinting method doesn't take a parameter, so it doesn't look like a java bean property setter. That's why your approach didn't work. You can use the factory method mentioned in the other answer or use a method invoking bean in the config file like so:
<bean id="myStarter" class="org.springframework.beans.factory.config.MethodInvokingBean">
<property name="targetObject" ref="gsonBuilder"/>
<property name="targetMethod" value="setPrettyPrinting"/>
</bean>
The factory approach seems more straightforward and idiomatic to me, but I include this approach for the sake of completeness.

why there is no corresponding set method in this case?

I am new to Spring and working on a project which is consisting of Spring in it .
It has got this piece of code inside the xml file
<bean id="quotClient" class="com..at.client.QuoteClient" scope="singleton" />
<bean id="streamClient" class="com.at.client.StreamClient" scope="singleton" />
And inside the java class it has got this piece of code
#Autowired
#Qualifier("streamClient")
private StreamClient sclient;
#Autowired
#Qualifier("quotClient")
private QuoteClient quotesClient;
public void setQuotesClient(QuoteClient quotesClient) {
this.quotesClient = quotesClient;
}
Please let me know why there is no method by name set for the StreamClient class , but which has got corresponding set method for QuoteClient .
Since you're using annotation driven Autowiring of the beans you don't need any setters for injunction (these are set by using reflection). Even setQuotesClient isn't needed by Spring DI framework to inject those 2 bean instances.
PS: From spring version 3.0, you can start using #Inject instead of #Autowired.
Check: How does Spring #Autowired work
i think that setter method wrote by mistake.
remove that setter and test the application. it should work.

Resources