DI doesn't inject the collection of abstract classes - java

I have an abstract class
public abstract class SecSchema {
........
}
I have some child classes. I have following service with all implementations of interfaces.
#Service
public class SecSchemaService {
#Autowired
Collection<SecSchema> secSchemas;
....getters and setters....
}
When I do DI in controller:
#Autowired
private SecSchemaService secSchemaService;
it works properly.
However I have another abstract class:
public abstract class Currecny {
#Autowired
SecSchemaService secSchemaService;
...... }
In child class I have the following code:
#Component
public class USD extends Currecny implements PreValidateListener {
#PostConstruct
public void registerListeners() { secSchemaService.getSecSchemas(); }
I've got NullPointerException because collection
secSchemaService.secSchemas
is null.
I don't know why but the the collection is initialized after class USD. I tried to use annotation #Dependson but it didn't help.
If I'd inject
#Autowired
Collection<SecSchema> secSchemas;
in class USD it works ok. So It's not working only when I'm injecting the wrapper of the collection SecSchemaService

I've had great fun and games with post constructs trying to use other beans that haven't yet been initialised.
A guaranteed way of getting it working is to forget about post constructs and have a bean that implements ApplicationListener for ContextRefreshedEvent, and then do your final initialisation in there. You can inject any existing beans in there and do any manual wiring you want. Not ideal, but works fine.

There was one more class child of USD and it was marked as #Component.
That was the problem. Only the last class in the hierarchy must be marked as #Component because I need only this class.

Related

Error with Quarkus CDI, Injection of inherited class

I am attempting to implement dependency injection in a class that is inherited from a base abstract class, and I get the following error: "javax.enterprise.inject.spi.DeploymentException: It's not possible to automatically add a synthetic no-args constructor to an unproxyable bean class. You need to manually add a non-private no-args constructor to com...ExampleIntegrationProvider in order to fulfill the requirements for normal scoped/intercepted/decorated beans."
From what I researched, it seems that this happens because classes that are annotated with normal scoped beans such as #ApplicationScoped for example, need a non-private constructor without arguments, so that CDI can create a proxy of that class when it is injected in lazy mode. I was a bit surprised to find out, as I've never had problems with code like this:
#ApplicationScoped
public class UserService {
private final UserRepository userRepository;
#Inject
public UserUseCase(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User findById(long id) { ... }
}
Even though Quarkus magically adds a synthetic no-args constructor, how could it overcome the final field UserRepository? There would be a problem with its initialization as it needs to receive a value, I imagine it might end up being passed a null value..
Anyway, going straight to my case, here's an example of my current situation, with 3 important base classes and 1 implementation for a "Example" partner:
PartnerIntegration.java
#MappedSuperclass
public abstract class PartnerIntegration {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
// other common fields ...
}
PartnerIntegrationRepository.java
public interface PartnerIntegrationRepository<I extends PartnerIntegration> extends PanacheRepository<I> {
// common methods ...
}
PartnerIntegrationProvider.java
public abstract class PartnerIntegrationProvider<I extends PartnerIntegration> {
protected final PartnerIntegrationRepository<I> partnerIntegrationRepository;
protected PartnerIntegrationProvider(PartnerIntegrationRepository<I> partnerIntegrationRepository) {
this.partnerIntegrationRepository = partnerIntegrationRepository;
}
// code ...
}
And finally the final implementation ExampleIntegrationProvider.java:
#ApplicationScoped
public class ExampleIntegrationProvider extends PartnerIntegrationProvider<ExampleIntegration> {
#Inject
public ExampleIntegrationProvider(ExampleIntegrationRepository exampleIntegrationRepository) {
super(exampleIntegrationRepository);
}
// code ...
}
The abstract class PartnerIntegrationProvider is not annotated with any scope as I did not consider it necessary since it only receives the PartnerIntegrationRepository interface and not a concrete repository. But ExampleIntegrationProvider receives a concrete repository that is implementing that interface, so I annotated it with #ApplicationScoped to be able to inject the concrete repository with #Inject. But this generated that error.
I tried creating a public constructor method that passes the null parameter to the parent class, and the error disappeared, but this ends up polluting my code, since I do not want to allow that class to be instantiated without receiving a repository, so I removed the #ApplicationScoped and #Inject annotations from ExampleIntegrationProvider and created that class to produce the instance that will be injected from it:
ExampleIntegrationProviderBean.java
#Dependent
public class ExampleIntegrationProviderBean {
private static ExampleIntegrationProvider INSTANCE;
private final ExampleIntegrationRepository exampleIntegrationRepository;
#Inject
public ExampleIntegrationProviderBean(ExampleIntegrationRepository exampleIntegrationRepository) {
this.exampleIntegrationRepository = exampleIntegrationRepository;
}
#Produces
public ExampleIntegrationProvider exampleIntegrationProvider() {
return INSTANCE == null ?
(INSTANCE = new ExampleIntegrationProvider(exampleIntegrationRepository)) : INSTANCE;
}
}
But the number of partners may grow quickly, I wouldn't like to have to create other classes like this one to produce the instances, and even if it was just one partner, it doesn't seem like a very elegant solution...
Does anyone have any ideas on how I can resolve this in a better way?

How can I make a field which is generic field autowired with dynamic generate in Spring?

I defined some interfaces with generic, and I have some classes injected in Spring context as Beans, could I dynamic create a manager bean to manage them, and it could be autowired in fields without any Bean def code of this manager?
I have tried FactoryBean way to implement it, but not worked, it couldn't transmit generic class info and the FactoryBean bean couldn't transmit any changable arguments.
I have tried BeanFactory way to implement it, when I getBeansOfType, these objects created without autowired, not worked...
Now I have a finally method which I think it's not very smart that is using ImportBeanDefinitionRegistrar and ClassPathBeanDefinitionScanner to scan all classes, then insert the manager's beanDefinition.
I'll be very appreciate if you supply any method, Thank you very much !
I want to implement it like this:
public interface Strategy<E extends BaseEnum>
{
public E getType();
}
public interface LoginStrategy extends Strategy<LoginType>
{
public LoginStrategy getType();
}
#Strategy
public class ALoginStrategy implements LoginStrategy
{
public getType()
{
return LoginType.OTP;
}
}
#Strategy
public class BLoginStrategy implements LoginStrategy
{
#Autowired
private UserMapper;
public getType()
{
return LoginType.PASSWORD;
}
}
public LoginServiceImpl implements LoginService
{
#Autowired
private StrategyManage<LoginType, LoginStrategy> strategyManager;
}
I want the strategyManager in LoginServiceImpl which is marked Autowired could be auto generated.
I also have a other question. It may be easier to explain what I want.
I have a model convertor implements a ModelConvertor interface, TL is lowerModel's class, TU is upperModel's class.
now there is a bean include code like this:
#Autowired
private ModelConvertor<UserPO, UserDO> userConvertor;
normally Spring frame would throw a Exception with a "no such bean" message, so I want to make this field could auto inject a value like this:
#Autowired
private ModelConvertor<UserPO, UserDO> userConvertor[ = new DefaultModelConvertor(UserPO.class, UserDO.class)];
How can I do to solve these problems, thanks a lot again!
I have resolved this problem, scan specific packages and dynamic generate beans to put on context.

Constructor injection on abstract class and children

I have a class as follows
#Component
public abstract class NotificationCenter {
protected final EmailService emailService;
protected final Logger log = LoggerFactory.getLogger(getClass());
protected NotificationCenter(EmailService emailService) {
this.emailService = emailService;
}
protected void notifyOverEmail(String email, String message) {
//do some work
emailService.send(email, message);
}
}
EmailService is a #Service and should be auto-wired by constructor injection.
Now I have a class that extends NotificationCenter and should also auto-wire components
#Service
public class NotificationCenterA extends NotificationCenter {
private final TemplateBuildingService templateBuildingService;
public NotificationCenterA(TemplateBuildingService templateBuildingService) {
this.templateBuildingService = templateBuildingService;
}
}
Based on the above example the code won't compile because there is no default constructor in the abstract class NotificationCenter unless I add super(emailService); as the first statement to NotificationCenterA constructor but I don't have an instance of the emailService and I don't intend to populate the base field from children.
Any idea what's the proper way to handle this situation? Maybe I should use field injection?
NotificationCenter is not a real class but an abstract class, so you can't create the instance of it. On the other hand, it has a field (final field!) EmailService that has to be initialized in constructor! Setter won't work here, because the final field gets initialized exactly once. It's Java, not even Spring.
Any class that extends NotificationCenter inherits the field EmailService because this child "is a" notification center
So, you have to supply a constructor that gets the instance of email service and passes it to super for initialization. It's again, Java, not Spring.
public class NotificationCenterA extends NotificationCenter {
private final TemplateBuildingService templateBuildingService;
public NotificationCenterA(EmailService emailService, TemplateBuildingService templateBuildingService) {
super(emailService);
this.templateBuildingService = templateBuildingService;
}
}
Now spring manages beans for you, it initializes them and injects the dependencies.
You write something that frankly I don't understand:
...as the first statement to NotificationCenterA constructor but I don't have an instance of the emailService and I don't intend to populate the base field from children.
But Spring will manage only a NotificationCenterA bean (and of course EmailService implementation), it doesn't manage the abstract class, and since Java puts the restrictions (for a reason) described above, I think the direct answer to your question will be:
You can't use setter injection in this case (again, because of final, it is Java, not because of Spring)
Constructor injection, being in a general case better than setter injection can exactly handle your case
First point :
#Component is not designed to be used in abstract class that you will explicitly implement. An abstract class cannot be a component as it is abstract.
Remove it and consider it for the next point.
Second point :
I don't intend to populate the base field from children.
Without Spring and DI, you can hardcoded the dependency directly in the parent class but is it desirable ? Not really.
It makes the dependency hidden and also makes it much more complex to switch to another implementation for any subclass or even for testing.
So, the correct way is injecting the dependency in the subclass and passing the injected EmailService in the parent constructor :
#Service
public class NotificationCenterA extends NotificationCenter {
private final TemplateBuildingService templateBuildingService;
public NotificationCenterA(TemplateBuildingService templateBuildingService, EmailService emailService) {
super(emailService);
this.templateBuildingService = templateBuildingService;
}
}
And in the parent class just remove the useless #Component annotation.
Any idea what's the proper way to handle this situation? Maybe I
should use field injection?
Not it will just make your code less testable/flexible and clear.
using field injection would be the way to go since you mentioned you don't want to have the emailService in child class.
The other way you can try is to inject the EmailService bean into NotificationCenterA constructor, and then pass it to super(emailService).
So, it would be something like:
#Autowired
public NotificationCenterA(EmailService emailService, TemplateBuildingService templateBuildingService) {
super(emailService);
this.templateBuildingService = templateBuildingService;
}
You can also achieve this by using #Lookup annotation.
public abstract class NotificationCenter {
protected final Logger log = LoggerFactory.getLogger(getClass());
#Lookup
protected EmailService getEmailService() {
return null; // Spring will implement this #Lookup method using a proxy
}
protected void notifyOverEmail(String email, String message) {
//do some work
EmailService emailService = getEmailService(); // This is return the service bean.
emailService.send(email, message);
}
}

How to use Spring Dependency Injection with class inheritance

I have a parent class called BaseService and I have other services that inherit from BaseService as they all need those methods to do their jobs. As always the methods of the superclass are available inside the subclass... However, when I use #Autowired DI to inject the subclass I am not able to use the methods defined by the parent class, I am only able to use what is defined separately in the subclass. How can I inject the subclass and have it properly instantiate the subclass such that the methods of the parent class are available to me?
Ex. Parent Class
#Service
public class BaseService{
public BooleanExpression combineBools(Predicate predicate, BooleanExpression bool){
BooleanExpression result = runupBool.and(predicate);
return result;
}
}
Ex. Child Class
#Service
public class EqServiceImpl extends BaseService implements EqService{
public EqServiceImpl(){
super();
}
#Override
public Iterable getAllData(Map<String, String> params, Predicate predicate) {
// Some Method Specific to Child Class
}
}
Ex. Controller
#RestController
public class EqController {
#Autowired
EqService eqService
...
}
If I wanted to access the method eqService.combineBools() inside the controller I am unable to. Why is this? How can I fix it?
As DarrenForsythe pointed out I am instantiating with EqService as the type so it would not have all of the methods of the BaseService since it does not extend that class, rather it is the EqServiceImpl that extends that class. Therefore I would need the type to be EqServiceImpl. Without making some other changes #Autowired is not the best choice for DI here.

Limit a collection of autowired components using Package

I am working on a project for parsing files that uses Chains of Responsibility of an abstract class called EntityMapper that are used for parsing. Currently we have 2 types of Files/Entities:
GrantEntity
BillEntity
All EntityMappers extend the abstract class:
public abstract class EntityMapper<T extends AbstractBaseEntity> implements Function<MapperExchange, T>
Soon we will have a DonationEntity that will represent a file that has some structural characteristics as grantEntity.
Instead of creating new classes of Type extends EntityMapper<DonationEntity> I wanted to ask if there is a way to filter an #AutoWired collection using package names or a regex.
Something like ?:
#Autowired
#ComponentScan("com.my.package.first,com.my.package.second")
private List<EntityMapper<GrantEntity>> entityMappers;
I unfortunately did not find an answer in the link below except to do it by hand and remove the elements from the collection:
How to filter a collection of beans using a collection of regex in Spring?
You can use #Qualifer annotation to indicate logically similar components. Then specify a matching #Qualifier to the injection target. For example
class DependencyToInject{
}
#Configuration
public class AppConfig{
#Bean
#Qualifier("main")
public DependencyToInject dependency1(){
//return instance
}
#Bean
#Qualifier("main")
public DependencyToInject dependency2(){
//return instance
}
#Bean
#Qualifier("sub")
public DependencyToInject dependency3(){
//return instance
}
}
#Component
public class DependentClass{
#Autowired
#Qualifier("main")
private List<DependencyToInject> mainList;
#Autowired
#Qualifier("sub")
private List<DependencyToInject> subList;
}

Categories

Resources