Autowire list of beans of a type in Grails - spring

I am trying to autowire a list of beans of a given type into my Bootstrap.groovy.
Say, I have the below interface and classes.
interface Vehicle {
}
#Component
class Car implements Vehicle {
boolean byName = false
}
#Component
class Van implements Vehicle {
boolean byName = false
}
And my Bootstrap.groovy looks like this:
class Bootstrap {
List<Vehicle> vehicles
def init = { servletContext ->
println "Vehicles are ${vehicles}" // prints null
}
}
I am sure I have set up component scanning correctly as I can see normal beans being wired up. However, it looks like autowiring by type isn't happening, causing the Spring container to try to wire the list of Vehicles by name and failing in the process.
Any insights would be appreciated.
I am using Grails 2.4.3

Related

Autowire a Bean in spring-boot-starter library which is not created yet | TransformerSupplier Kafka Streams

I am creating a spring-boot-starter kafka streams library.
In that I am defining a configuration class which will have a list of storeBuilders.
class CustomConfiguration {
private String A;
private String B;
private Set<StoreBuilder<?>> transformationStoreBuilders;
private Set<StoreBuilder<?>> processorStoreBuilders;
// Constructor and Getter
}
The client which will import this spring-boot-starter library will have to create a bean of the above configuration class.
Now I am creating a custom TransformerSupplier and I want to auto-set the stores by autowiring stores from CustomConfiguration.
I am doing something like this:
public abstract class CustomTransformerSupplier<A, B, C, D> implements TransformerSupplier<A, B, KeyValue<C, D>> {
#Autowired
private CustomConfiguration configuration;
public abstract CustomTransformer<A, B, KeyValue<C, D>> get();
public Set<StoreBuilder<?>> stores() {
return configuration.getTransformationStoreBuilders();
}
}
However the CustomTransformerSupplier bean will be created by the client and we cannot Autowire bean in a non Bean class.
How do I auto set the store builders in the transformer supplier?
Why am I following the approach mentioned is because:
The client can have n store builders
There is going to be a ProcessorSupplier and a TransformerSupplier. They can share common state stores.
I want the client to just create the state stores and not worry about injecting the state stores in the TransformerSupplier and the processorSupplier.
Thanks in advance!
See if #Configurable may answer to your question: https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-atconfigurable

Trying to call a class with constructor but #Autowired var is throwing Null Pointer Exception in Kotlin/Spring Boot

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.

Im geting the error Parameter 0 of constructor required a single bean, but 2 were found but one of the beans "found" has been deleted by me

Im trying to set up a h2 database using jpa/jdbc, after creating an implemntation for a query interface using jpa as opposed to jdbc i am now getting the error:
Parameter 0 of constructor in com.nsa.charitystarter.service.CharityQueries required a single bean, but 2 were found:
- charityRepoJDBC: defined in file [C:\Users\V La Roche\Desktop\assessment-1-starter\out\production\classes\com\nsa\charitystarter\data\CharityRepoJDBC.class]
- charityRepoJPA: defined in null
Im unsure as to what has gone wrong and am not really sure where to go from here, i havent been able to find many people with a similar issue to me online.
My implementation using jdbc
#Repository
public class CharityRepoJDBC implements CharityRepository {
private JdbcTemplate jdbc;
private RowMapper<Charity> charityMapper;
#Autowired
public CharityRepoJDBC(JdbcTemplate aTemplate) {
jdbc = aTemplate;
charityMapper = (rs, i) -> new Charity(
rs.getLong("id"),
rs.getString("name"),
rs.getString("registration_id"),
rs.getString("acronym"),
rs.getString("purpose")
);
}
#Override
public List<Charity> findCharityBySearch(String searchTerm) {
String likeSearch = "%" + searchTerm + "%";
return jdbc.query(
"select id, acronym, name, purpose, logo_file_name, registration_id " +
"from charity " +
"where concat(name, acronym, purpose, registration_id) like ?",
new Object[]{likeSearch},
charityMapper);
}
#Override
public Optional<Charity> findById(Long id) {
return Optional.of(
jdbc.queryForObject(
"select id, acronym, name, purpose, logo_file_name, registration_id from charity where id=?",
new Object[]{id},
charityMapper)
);
}
}
Charity finder implementation:
#Service
public class CharityQueries implements CharityFinder {
private CharityRepository charityRepository;
public CharityQueries(CharityRepository repo) {
charityRepository = repo;
}
public Optional<Charity> findCharityByIndex(Integer index) {
return charityRepository.findById(index.longValue());
}
public List<Charity> findCharityBySearch(String searchTerm) {
return charityRepository.findCharityBySearch(searchTerm);
}
}
CharityFinder interface
public interface CharityFinder {
public Optional<Charity> findCharityByIndex(Integer index);
public List<Charity> findCharityBySearch(String searchTerm);
}
error log :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.nsa.charitystarter.service.CharityQueries required a single bean, but 2 were found:
- charityRepoJDBC: defined in file [C:\Users\V La Roche\Desktop\assessment-1-starter\out\production\classes\com\nsa\charitystarter\data\CharityRepoJDBC.class]
- charityRepoJPA: defined in null
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Process finished with exit code 0
You have following definition currently,
#Repository
public class CharityRepoJDBC implements CharityRepository {
And you are injecting CharityRepository in your service layer CharityQueries
#Service
public class CharityQueries implements CharityFinder {
private CharityRepository charityRepository;
Hence when you deploy your application the container is confused which bean you are trying to autowire into the service.
By default spring autowires by type and hence by that there are 2 beans which are qualified to be injected by spring container.
CharityRepository itself and other
CharityRepoJDBC
So you need to either explicitly tell container which bean you are trying to autowire in this case.
So you can try adding qualifiers as below to solve the issue.
#Service
public class CharityQueries implements CharityFinder {
#Qualifier("CharityRepoJDBC")
private CharityRepository charityRepository;
and at the same time modify your CharityRepoJDBC to be,
#Repository(value = "CharityRepoJDBC")
public class CharityRepoJDBC implements CharityRepository {
You seem to have the Spring Data JDBC starter on the classpath and the Spring Data JPA starter.
Spring Data JDBC has a bug which causes it to produce implementation for repository interfaces even if it shouldn't, thus you end up with one implementation from JPA and another one from JDBC.
If you really want to use Spring Data JDBC and Spring Data JPA you can limit the #EnableJdbcRepositories and #EnableJpaRepositories annotations using the include and exclude filters.
But from your code and the tags you used I suspect you might be not at all interested in Spring Data Jdbc but only in Spring Jdbc.
If this is the case look for a dependency spring-boot-starter-data-jdbc and change it to spring-boot-starter-jdbc.
In case all this Spring (Data) JDBC/JPA confuse you this question and its answers might help: Spring Data JDBC / Spring Data JPA vs Hibernate
I solved it putting #Primary annotation in the repository interface.
In your case it would be the following:
#Primary
public interface CharityFinder {
public Optional<Charity> findCharityByIndex(Integer index);
public List<Charity> findCharityBySearch(String searchTerm);
}

Grails DataBindingListener not hearing binding events

I added a class in src/groovy that implements the DataBindingListener interface. My init/Application file registers the Spring bean in the doWithSpring method
#Override
Closure doWithSpring() {
def beans = {
applicationBindingListener(org.mkv.ApplicationBindingListener)
}
return beans
}
I confirmed the bean is registered. However it doesn't seem to be receiving any of the binding events.
I'm using grails 4.0.0
In my case, I edit config/spring/resources.groovy. It's the source.
beans = {
dataBindingListener org.mkv.ApplicationBindingListener
}
And, The code below is added as a test case method to ensure that the bean configured in the test code is used.
boolean loadExternalBeans() {
true
}

Grails 2.4.2 bean spring bean injection

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!!

Resources