Grails 2.4.2 bean spring bean injection - spring

Sample app located here : https://github.com/rushidesai1/Grails2_4_2_BeanIssue
Question:
In resources.groovy if we declare a bean like this
beans = {
testObject(TestObject){bean ->
bean.scope = "prototype"
map = new HashMap() // or [:]
//And also if we declare any object like this
testA = new TestA()
}
}
and Now if we DI testObject bean or do 'Holders.grailsApplication.mainContext.getBean("testObject")', then the bean we get will have singleton 'map' and singelton 'testA' object.
Here testObject is declared as 'prototype' and even then both 'map' and 'testA' are singleton
I want to know if this is a bug or it is working as designed. It is completely counter intuitive that it would work like this since we are specifically doing new and so we expect a new bean being injected everytime.
Use the Unit test case to see more detailed version of my question.
Thanks in advance for clarification !!!

I want to know if this is a bug or it is working as designed.
Yes, I think it is working as designed.
Your testObject bean is a singleton. That singleton bean only has 1 copy of the map and testA properties. The behavior you are describing is exactly what I would expect.
EDIT:
I have reviewed the application in the linked project and this is what is going on...
In resources.groovy you have something like this:
testObject(TestObject) { bean ->
bean.scope = "prototype"
mapIssue1 = ["key1FromResource.groovy": "value1FromResource.groovy"]
}
That testObject bean is a prototype scoped bean so each time you retrieve one, you will get a new instance. However, you have the initialization Map hardcoded in the bean definition so the bean definition that is created has that Map associated with it so every bean created from that bean def will have the same Map. If you want a different Map instance, you could create it in afterPropertiesSet or similar.
The unit test at https://github.com/rushidesai1/Grails2_4_2_BeanIssue/blob/e9b7c9e70da5863f0716b998462eca60924ee717/test/unit/test/SpringBeansSpec.groovy is not very well written. Seeing what is going on relies on interrogating stdout after all of those printlns. The behavior could be more simply verified with something like this:
resources:groovy
import test.TestObject
beans = {
testObject(TestObject) { bean ->
bean.scope = "prototype"
mapIssue1 = ["key1FromResource.groovy":"value1FromResource.groovy"]
}
}
SpringBeansSpec.groovy
package test
import grails.test.mixin.TestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
import spock.lang.Specification
#TestMixin(GrailsUnitTestMixin)
class SpringBeansSpec extends Specification {
static loadExternalBeans = true
void 'test bean properties'() {
setup:
def testObject1 = grailsApplication.mainContext.testObject
def testObject2 = grailsApplication.mainContext.testObject
expect: 'test TestObject beans are not the same instance'
!testObject1.is(testObject2)
and: 'the TestObject beans share values defined in the bean definition'
testObject1.mapIssue1.is(testObject2.mapIssue1)
}
}

On one hand it might be confusing that even if you are using new it should be creating a new Object each time you get testA bean and on the other hand it is working as expected. How?
Alright! So the answer lies in Spring java Configuration. The resources.groovy is using DSL which internally is a Configuration file.
Not sure if you know or remember about springs #Configuration annotation. Using this we are making POJO a configuration file.
Now the rules of Spring are:
Any bean created is singleton by default until unless specified.
Even if you are using new in java configuration file. Spring is made wise enough that it is a spring config file and hence new doesn't mean a new Object always.
Hence, for equivalent configuration file if I skip testObject and map for now is below:
#Configuration
public class JavaConfiguration {
#Bean
public Teacher teacher(){
TestA testA = new TestA();
return teacher;
}
}
Here, we have used new TestA(). But spring will always return same object until you specify explicitly to use scope Prototype.
Hence, above Configuration file would be like below after enabling prototype scope:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
#Configuration
public class JavaConfiguration {
#Bean
#Scope(value="prototype")
public Teacher teacher(){
TestA testA = new TestA();
return teacher;
}
}
and corresponding DSL would be:
testA(TestA){bean->
bean.scope='prototype'
}
Hope it helps!!

Related

spring boot component with string parameters

i have a component that reads a configuration value from application.properties and accepts a string parameter in its constructor as such..
#Component
public class Person
{
#Value("${greeting}")
String greeting;
String name;
public Person(String name)
{
this.name = name;
onGreet( greeting + ", " + name );
}
public void onGreet(String message)
{
}
}
I need to instantiate this component as follows and override its "onGreet" event in the calling code as follows:
Person jack = new Person("jack")
{
public void onGreet(String message)
{
System.out.println( message );
}
};
However I end up getting this..
Parameter 0 of constructor in demo11.Person required a bean of type 'java.lang.String' that could not be found.
My application.properties is as follows:
greeting=hello
What am I missing here? Thank you.
It is literally telling you that the only constructor that you have requires a parameter that Spring knows nothing about.
Add a #Value to that String name in the constructor (right before the parameter) like so public Person(#Value("${name}") String name) if you want Spring to initalize it or remove that constructor
EDIT: some more explanation:
Spring is a dependency injection container. Meaning you define beans and let Spring create and inject them for you. Defining beans can be done in several ways (Java configuration, annotations or xml) here you are using annotation way via #Component.
Now that you have defined your bean (aka component) for Spring it will create it. For it to create it it needs to call a constructor. For that you need to provide it with all information necessary for constructor call - meaning all parameters. If parameters are other classes they need to be defined as beans as well (For example via #Component) if they are simple types like String you need to provide #Value for them.
Lastly if you ever use new ... to define Spring managed beans then the whole Spring magic disappears since Spring doesnt know about this bean instantiation anymore and will not autowire anything into it. For all intenses and purposes Spring is not aware of any objects you create with new.

StateMachineRuntimePersister Instantiation getting failed because Spring not able to find its dependent bean MongoDbStateMachineRepository

I am new to spring state machines. I am trying to setup state machine for my transaction data and externalise it to mongo database. But i am getting error while creating "StateMachineRuntimePersister" bean.
Error says - Parameter 0 of method mongoPersist in com.pws.funder.config.PersistConfig required a bean of type 'org.springframework.statemachine.data.mongodb.MongoDbStateMachineRepository' that could not be found
#Configuration
public class PersistConfig {
#Bean(name="runtime")
public StateMachineRuntimePersister<WalletGatewayStates, WalletGatewayEvents, UUID> mongoPersist(
MongoDbStateMachineRepository mongoRepository) {
return new MongoDbPersistingStateMachineInterceptor<WalletGatewayStates,WalletGatewayEvents,UUID>(mongoRepository);
}
}
Any leads would be helpful.
Just create interface like this:
public interface StateMachineRepository extends MongoDbStateMachineRepository {
}
and pass it into mongoPersist method.
Spring automatically creates implementation from your repository interface and put this bean in the context.

Multiple Constructor injection using Java Annotations

The SPRING doc says the following
Spring Framework 4.3, an #Autowired annotation on such a constructor
is no longer necessary if the target bean only defines one constructor
to begin with. However, if several constructors are available, at
least one must be annotated to teach the container which one to use.
As i understand if there are multiple constructors and we have not annotated any of them then i will get an error . I ran the following code
#Component // this is bean id
public class TennisCoach implements Coach {
private FortuneService fortuneservice;
public TennisCoach(FortuneService thefortuneservice) {
System.out.println(" inside 1 arg constructter");
fortuneservice = thefortuneservice;
}
public TennisCoach() {
System.out.println(" inside 0 arg constructter");
}
I call that using the below code
TennisCoach theCoach = myapp.getBean("tennisCoach", TennisCoach.class);
But i didn't get the error .I got the O/P as
inside 0 arg constructter
Why?
It looks like the text you've quoted from the Spring docs doesn't apply to a case where one of the constructors is a no-args (default) constructor. You can see this very easily if you try and add an bean reference parameter to it.
Spring attempts to determine the candidate constructors using AutowiredAnnotationBeanPostProcessor and in your scenario, will not find any single or autowired constructor, so it will record the no-args one and instantiate it in SimpleInstantiationStrategy.

Spring - Retrieve all scanned packages

I'm creating a Spring Starter project and need to get all classes which are marked with a custom annotation. The annotated class is not a spring bean.
My current solution is to use the ClassPathScanningCandidateComponentProvider to find the required classes.
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(CustomAnnotation.class));
candidates = scanner.findCandidateComponents("THE MISSING PACKAGE NAME");
The problem is that I'm currently provide an empty package String so that all packages/classes are scanned which slows the startup down.
I need to access the packages which are scanned by Spring to avoid the scanning of all packages and classes.
Is there a way to retrieve all packages programmatically which are scanned by Spring or is there an alternative solution to retrieve custom annotated classes which are not Spring beans.
Greets
One solution without the need to make a full classpath scan is to use the AutowiredAnnotationBeanPostProcessor:
private List<Class<?>> candidates = new ArrayList<>();
#Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if(beanClass.isAnnotationPresent(YourAnnotation.class)){
candiates.add(beanClass));
System.out.println(beanClass);
return new Object();
}
}
#Bean
public CandiateHolder candidates() {
return new CandidateHolder(candidates);
}
You can check if the bean class which should be instantiated has the required annotation. If its the case you add the class to a property to expose it later as a bean. Instead of returning null you have to return an instance of a new Object. The returned object can be used to wrap the class in a proxy. Cause I don't need an instance I will return a simple new object. Its maybe a dirty hack but it works.
I have to use this kind of hack cause an instantiation of the needed object will result in an runtime error cause it has to be instantiated in the framework I use.

What's the best pattern for injecting a bean with arguments?

I have a number of cases in my app where client code wants to create a bean on-demand. In each case, the bean has 1 or 2 constructor arguments which are specified by the client method, and the rest are autowired.
Ex:
//client code
MyQuery createQuery() {
new MyQuery(getSession())
}
//bean class I want to create
//prototype scoped
class MyQuery {
PersistenceSession session
OtherBeanA a
OtherBeanB b
OtherBeanC c
}
I want A, B, and C to be autowired, but I have the requirement that 'session' has to be specified by the calling code. I want a factory interface like this:
interface QueryFactory {
MyQuery getObject(PersistenceSession session)
}
What's the most efficient way to wire up the factory? Is it possible to avoid writing a custom factory class that does new MyQuery(...)? Can ServiceLocatorFactoryBean be used for something like this?
You can use the #Autowired annotation on your other beans and then use the ApplicationContext to register the new bean. This assumes otherBeanA is an existing bean.
import org.springframework.beans.factory.annotation.Autowired
class MyQuery {
#Autowired
OtherBeanA otherBeanA
PersistenceSession persistenceSession
public MyQuery(PersistenceSession ps){
this.persistenceSession = ps
}
}
I'm not positive if this is the most efficient way to create a new bean, but it seems to be the best way at runtime.
import grails.util.Holders
import org.springframework.beans.factory.config.ConstructorArgumentValues
import org.springframework.beans.factory.support.GenericBeanDefinition
import org.springframework.beans.factory.support.AbstractBeanDefinition
import org.springframework.context.ApplicationContext
class MyQueryFactory {
private static final String BEAN_NAME = "myQuery"
static MyQuery registerBean(PersistenceSession ps) {
ApplicationContext ctx = Holders.getApplicationContext()
def gbd = new GenericBeanDefinition(
beanClass: ClientSpecific.MyQuery,
scope: AbstractBeanDefinition.SCOPE_PROTOTYPE,
autowireMode:AbstractBeanDefinition.AUTOWIRE_BY_NAME
)
def argumentValues = new ConstructorArgumentValues()
argumentValues.addGenericArgumentValue(ps)
gbd.setConstructorArgumentValues(argumentValues)
ctx.registerBeanDefinition(BEAN_NAME, gbd)
return ctx.getBean(BEAN_NAME)
}
}
Instead of using Holders, it's advised to use the ApplicationContext from dependecy inject if available, you could then pass this to the registerBean method.
static MyQuery registerBeanWithContext(PersistenceSession ps, ApplicationContext ctx) {
...
}
Calling class:
def grailsApplication
...
PersistenceSession ps = getRuntimePersistenceSession()
MyQueryFactory.registerBean(ps, grailsApplication.mainContext)
I changed the name of the method to truly reflect what it's doing - registering a spring bean as opposed to instantiating a MyQuery. I pass back the bean using the getBean method, but you also have access to the same bean using the ApplicationContext once it's been created.
def myQueryBean = MyQueryFactory.registerBean(ps)
// or somewhere other than where the factory is used
def grailsApplication
def myQueryBean = grailsApplication.mainContext.getBean('myQuery')

Resources