Spring injection for bean with generic wildcard - spring

I have the following:
public abstract class ReportGenerationStrategy<T extends ReportParameter> {
public abstract void generate(T reportParameter) throws IOException;
}
The subclasses:
#Component
#AllArgsConstructor
public class DeferredRevenueReportGenerationStrategy extends ReportGenerationStrategy<DeferredRevenueReportParameter> {
}
#Component
#AllArgsConstructor
public class OffBalanceExposureReportGenerationStrategy extends ReportGenerationStrategy<OffBalanceExposureReportParameter> {
}
I am creating a map with the subclasses:
#Bean
public Map<ReportType, ReportGenerationStrategy<? extends ReportParameter>> generationStrategies(
#Qualifier("deferredRevenueReportGenerationStrategy") final ReportGenerationStrategy<? extends ReportParameter> deferredRevenue,
#Qualifier("offBalanceExposureReportGenerationStrategy") final ReportGenerationStrategy<? extends ReportParameter> offBalanceExposure
)
{
return ImmutableMap.of(
ReportType.DEFERRED_REVENUE, deferredRevenue,
ReportType.OFF_BALANCE_EXPOSURE, offBalanceExposure
);
}
And trying to inject the map as following:
#AllArgsConstructor
#Slf4j
public class ReportApplicationServices {
private final Map<ReportType, ReportGenerationStrategy<ReportParameter>> generationStrategies; //Empty map injected
}
But I receive an empty map on the field...
Why is this happening?

I believe you missed to use #Autowire annotation on the private final Map that you have declared.
Hope this helps and solves the issue.

Related

How to autowire a method of a generic service

I have a Service class that has a generic type and a setController method that is based on the same generic type. the generic type of the servic object is only known at the time of declaration.
The problem is now when i define a ControllerImpl where the generic type is defined the #Autowired method of setController does not use that component.
Has somebody an idea how to fix it and keep the ServiceImpl generic. (it would work when i define the typ in ServiceImpl as well).
The following example show the problem i'm facing with:
#SpringBootTest
#ActiveProfiles("local")
public class AccessTest {
#Autowired
private ServiceA<BeanA> service;
#Test
void test(){
Assertions.assertNotNull(service.controller);
}
interface ValueGetter{
}
static class BeanA implements ValueGetter{
}
static class AbstractService<B extends ValueGetter>{
Controller<B> controller;
#Autowired
void setController(#Nullable Controller<B> controller){
this.controller = controller;
}
}
interface Controller<B extends ValueGetter>{
void doSomething(B value);
}
//not inner class
#Service
public class ServiceA<B extends AccessTest.ValueGetter> extends AccessTest.AbstractService<B> {
}
//not inner class
#Component
public class ControllerImpl implements AccessTest.Controller<AccessTest.BeanA> {
#Override
public void doSomething(final AccessTest.BeanA value) {
}
}
}

Why can't #Autowired a JPA repository - Spring boot + JPA

I'm giving this error:
Parameter 0 of constructor in x.microservice.module.business.application.BusinessCreator required a bean of type 'x.microservice.module.business.infrastructure.HibernateJpaRepository' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=false)
Action:
Consider defining a bean of type 'x.microservice.module.business.infrastructure.HibernateJpaRepository' in your configuration.
The controller:
#Slf4j
#RestController
public final class BusinessPostController {
#Autowired
private BusinessCreator creator;
#PostMapping(value = "/business")
public ResponseEntity create(#RequestBody Request request){
BusinessCreatorDto businessCreatorDto = new BusinessCreatorDto(IdentifierEpoch.generate(),request.getName());
return ResponseEntity.ok(
creator.create(businessCreatorDto)
);
}
}
The Application Layer:
#AllArgsConstructor
#Service
public class BusinessCreator {
#Autowired
private HibernateJpaRepository repository;
public BusinessResponse create(BusinessCreatorDto dto){
Business business = new Business(dto.getId(), dto.getName());
repository.save(business);
return BusinessResponse.fromAggregate(business);
}
}
In the Infrastructure layer
#Repository
public abstract class HibernateJpaRepository implements JpaRepository<Business, Long> {
}
The boot Application:
#EnableJpaRepositories
#SpringBootApplication
public class MicroserviceApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceApplication.class, args);
}
}
All dependencies are resolved and the others classes I believe that are irrellevant.
Any suggestions? Thank you very much
Probably, the error cause is HibernateJpaRepository - it has to be an interface that extends JpaRepository.
You could write your own Repository in a interface:
#Repository
public interface HibernateJpaRepository extends JpaRepository < Business, Long > {
}
Then your Class:
#AllArgsConstructor
#Service
public class BusinessCreator {
#Autowired
private HibernateJpaRepository repository;
public BusinessResponse create(BusinessCreatorDto dto){
Business business = new Business(dto.getId(), dto.getName());
repository.save(business);
return BusinessResponse.fromAggregate(business);
}
}

How to #Autwired MessageSource in spring into Entity class correctly?

I have the following entity in spring boot application:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Audited
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
#Table(name = "currency", catalog = "currency_db")
public class Currency implements java.io.Serializable {
#Autowired
Messages messages;
As for message, it just a container of spring MessageSource here it is:
#ApplicationScope
#Component
#Slf4j
public class Messages {
#Autowired
private MessageSource messageSource;
private MessageSourceAccessor accessor;
#PostConstruct
private void init() {
accessor = new MessageSourceAccessor(messageSource, Locale.ENGLISH);
log.info("Messages initialized");
}
public String get(String code) {
return accessor.getMessage(code);
}
}
I'm getting the following error when run mvn clean install. Any idea what I'm missing here?
org.hibernate.MappingException: Could not determine type for: com.company.currencyservice.Messages, at table: currency, for columns: [org.hibernate.mapping.Column(messages)]
It's looks like hibernate think it's a column. Thanks.
Entities are not Spring beans and therefor you cannot use dependency injection in entities.
If you want to access a Spring bean from within an entity you can use a helper class like this:
#Service
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static <T> T bean(Class<T> beanType) {
return context.getBean(beanType);
}
public static Object bean(String name) {
return context.getBean(name);
}
#Override
public void setApplicationContext(#SuppressWarnings("NullableProblems") ApplicationContext ac) {
context = ac;
}
}
Then you can use ApplicationContextProvider.getBean(Messages.class) to get access to the Messages.

#Qualifier & #Autowired object coming as null

I am having following code below.
#Builder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
#NoArgsConstructor(access = AccessLevel.PRIVATE)
#ToString
#EqualsAndHashCode
#Configurable
public class Employee {
#Autowired
#Qualifier("findEmpByDepartment")
private Function<Long, Long> empByDepartment;
private void save() {
this.empByDepartment.getList();
}
}
and FindEmpByDepartment class below.
#Component("findEmpByDepartment")
public class FindEmpByDepartment implements Function<Long, Long> {
public void getList() {
}
....
}
My problem is I am always getting null when invoke
this.empByDepartment.getList();
line. Here this.empByDepartment is coming as null. Any idea why it is like this?
Thanks
May be you would have missed annotating any class in the flow hierarchy .
#Service, #Repository and #Controller are all specializations of #Component, so any class you want to auto-wire needs to be annotated with one of them.
IoC is like the cool kid on the block and if you are using Spring then you need to be using it all the time .
So make sure you do not have any object created with new operator in the entire flow .
#Controller
public class Controller {
#GetMapping("/example")
public String example() {
MyService my = new MyService();
my.doStuff();
}
}
#Service
public class MyService() {
#Autowired
MyRepository repo;
public void doStuff() {
repo.findByName( "steve" );
}
}
#Repository
public interface MyRepository extends CrudRepository<My, Long> {
List<My> findByName( String name );
}
This will throw a NullPointerException in the service class when it tries to access the MyRepository auto-wired Repository, not because there is anything wrong with the wiring of the Repository but because you instantiated MyService() manually with MyService my = new MyService().
For more details , you can check
https://www.moreofless.co.uk/spring-mvc-java-autowired-component-null-repository-service/

spring framework : expected single matching bean but found 2

This super class DAO:
public class CrudDAO{
}
This child class:
#Repository
public class JnsTimeDao extends CrudDAO {
}
#Repository
public class BatchDAO extends CrudDAO {
}
this super service class
#Transactional(readOnly = true)
public abstract class CrudService<D extends CrudDAO> {
#Autowired
protected D dao;
}
startup error:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
qualifying bean of type [com.gp.dao.CrudDAO] is defined: expected
single matching bean but found 2: batchDAO,jnsTimeDao
There are 2 beans of type CrudDAO. So, Spring won't be able to understand which bean to inject. Can be solved as follows
#Repository("jnsTimeDao")
public class JnsTimeDao extends CrudDAO {
}
#Repository("batchDao")
public class BatchDAO extends CrudDAO {
}
While injecting use #Qualifier
#Transactional(readOnly = true)
public abstract class CrudService<D extends CrudDAO> {
#Autowired
#Qualifier("batchDao")
protected D dao;
}

Resources