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')
Related
I'm new to Kotlin and Spring Boot. I'm trying to call SearchService class with initialized constructors through ServiceClass, everything is fine until it's time to access Autowired env variable in SearchService - it throws Null Pointer Exception. I'm assuming I will have to Autowire SearchService in ServiceClass but then how will I initialize the constructor and name/isAlive variables in the performSearch method.
#Service
class ServiceClass {
#Autowired
lateinit var env: Environment
fun performSearch(req: String): String {
var searchService = SearchService("John", false)
result = searchService.searchAsync()
...
return result
}
}
#Repository
class SearchService(name: String = "", isAlive: Boolean = true) {
private var name: String = name
private var isAlive: Boolean = isAlive
#Autowired
lateinit var env: Environment
fun searchAsync(): String {
var endpoint = env.getProperty("endpoint").toString() + name //Error here
// makes call to get endpoint with Name and isAlive as params
...
return result
}
}
You're right that you need need to autowire the SearchService too. Spring can only autowire dependencies into components that it created. Because you're creating the SearchService yourself, Spring doesn't know that it exists, and won't autowire into it.
Your second question is: if you autowire the SearchService, how can you initialise its other properties like name and isAlive? This is a common problem with dependency injection, which arises when some of the parameters to a constructor are dependencies and others are data.
The solution is to use the factory design pattern. In dependency injection it's also sometimes called "assisted injection". Create another "factory" component whose only job is to create the SearchService for you:
#Component
class SearchServiceFactory(private val env: Environment) {
fun createSearchService(name: String = "", isAlive: Boolean = true) =
SearchService(env, name, isAlive)
}
You should then remove the #Repository annotation from the SearchService, since its creation isn't managed directly by Spring.
The factory can be autowired wherever you need to use it, and in turn has the Environment autowired into it. When you want to create the SearchService, you call createSearchService to provide values for the additional data parameters.
Your service class would change to the following:
#Service
class ServiceClass(private val factory: SearchServiceFactory) {
fun performSearch(req: String): String {
var searchService = factory.createSearchService("John", false)
result = searchService.searchAsync()
...
return result
}
}
Note that in the examples I've used constructor injection instead of #Autowired. Spring will automatically provide values for constructor arguments when instantiating a Kotlin class, the same as if it was an autowired field. Using constructor injection instead of field injection can help to ensure that problems like the one you encountered are detected at compile time rather than causing null pointer exceptions at runtime.
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!!
I'm wanting to test a Grails controller that contains a bean (I'll move it to a Service when I get it working, but I just want to keep it simple now).
//resources.groovy
beans {
myBean(com.me.MyBean)
}
// MyBean.java
// this needs to be in java as it is playing with spring-data-neo4j
package com.me;
public class MyBean {
String show() {
return "Hello";
}
}
// TestController.groovy
package com.me
import com.me.MyBean
class TestController {
def myBean
def index() {
render myBean.show()
}
}
// TestControllerSpec.groovy
package com.me
import grails.test.mixin.TestFor
import spock.lang.Specification
import com.me.*
#TestFor(TestController)
class TestControllerSpec extends Specification {
def myBean
def setup() {
defineBeans {
myBean(com.me.MyBean) {bean->
bean.autowire = true
}
}
}
def cleanup() {
}
def "show() returns Hello"() {
when:
def rc = controller.myBean.show()
def rc2 = myBean.show()
then:
rc == "Hello"
rc2 == "Hello"
}
}
Within TestControllerSpec, myBean is null. controller.myBean is also null. I think this is because Spring is not picking the bean up and wiring it in. I gather that in unit tests not all spring beans are available, but what do I need to do to get controller.myBean to be instantiated and wired up correctly?
You must be mocking the myBean as below
def myBean = Mock(MyBean)
or
MyBean myBean = Mock()
and then stub out method for your need if required as below:
myBean.show >> "test data"
and then assign it to controller object which is already mocked for you.
controller.myBean = myBean
and there you go.
Or optionally you can stub out myBean and give stubbed implementations. For example,
MyBean myBean = Stub(){
show() >> {return "sample text"}
}
controller.myBean = myBean
The reason for doing this is we are not testing the integration of application entities like controller, views or domain but we are testing a single unit i.e. a method and hence we should be just testing it and for integration we should be using integration test cases which would be similar in everything except you won't require any mocking in normal scenarios.
Edit:
found another useful feature to mock services or beans using defineBeans closure as below:
defineBeans {
adapter(Adapter)
helperService(HelperService)
}
This will allow beans to be accessed from grailsApplication.
Hope it helps.
I have code like this...
#Validateable
class RecipientsCommand {
...
EmailService emailService
void getEmailEligibleRecipients(Long accountId){
emailService.loadEligibleEmail(accountId)
}
}
resource.groovy
imports com.test.commands.RecipientsCommand
beans = {
recipientsCommand(RecipientsCommand){bean -> bean.autowire = true}
}
But the service is still always null when I call
new RecipientCommand()
Since the Command Object seems to be an interface between the views and the controller I am creating it, filling it and passing it to the view. Then I am using it to parse and save data. If I change to...
EmailService emailService = new EmailService()
Everything works fine.
The auto wiring only happens when Grails creates the instance for you. You can't just new RecipientCommand() and expect Spring to be involved in that. If you retrieve the recipientsCommand bean from the Spring application context it will be auto wired and if the RecipientCommand is created by the framework and passed as an argument to your controller action, that will also be auto wired. invoking the new RecipientCommand() constructor will result in a new instance being created which is not auto wired.
EDIT:
Examples...
class SomeController {
def someAction(RecipientCommand co) {
// co will already be auto wired
// this approach does NOT require you to have defined the
// recipientsCommand bean in resources.groovy
}
}
class SomeOtherController {
def someAction() {
// rc will be autowired
// this approach requires you to have defined the
// recipientsCommand bean in resources.groovy
def rc = grailsApplication.mainContext.getBean('recipientsCommand')
}
}
class AnotherSomeOtherController {
def recipientsCommand
def someAction() {
// recipientsCommand will be auto wired
// this approach requires you to have defined the
// recipientsCommand bean in resources.groovy
}
}
class YetAnotherController {
def someAction() {
// rc will not be autowired
def rc = new RecipientCommand()
}
}
I hope that helps.
From my controller I would like to dynamically select a service based on a parameter.
Currently I have a base service and some other services that extent this base service. Based on the parameter I call a class that does creates a bean name based on the param and eventually calls the following:
import org.codehaus.groovy.grails.web.context.ServletContextHolder as SCH
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes as GA
class Resolver {
def ctx
def getBean(String beanName) {
if(!ctx) {
ctx = SCH.servletContext.getAttribute(GA.APPLICATION_CONTEXT)
}
return ctx."${beanName}"
}
}
This returns the service I want. However I feel rather dirty doing it this way. Does anyone have a better way to handle getting a service (or any other bean) based on some parameter?
Thank you.
ctx."${beanName}" is added to the ApplicationContext metaclass so you can do stuff like def userService = ctx.userService. It's just a shortcut for ctx.getBean('userService') so you could change your code to
return ctx.getBean(beanName)
and it would be the same, but less magical.
Since you're calling this from a controller or a service, I'd skip the ServletContextHolder stuff and get the context by dependency-injecting the grailsApplication bean (def grailsApplication) and getting it via def ctx = grailsApplication.mainContext. Then pass it into this helper class (remember the big paradigm of Spring is dependency injection, not old-school dependency-pulling) and then it would be simply
class Resolver {
def getBean(ctx, String beanName) {
ctx.getBean(beanName)
}
}
But then it's so simple that I wouldn't bother with the helper class at all :)