Constructor injection on abstract class and children - java

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);
}
}

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?

I am trying to Mock an autowired map of instances in spring but I get null pointer exception

In one of my controller
#Autowired
private Map<String, ABC> abcMap;
now I want mock it in one of the unit test but I always get null pointer exception.
This map contains implementations of ABC abstract class.
Can anyone suggest a possible solution?
I'm not sure what Unit test Framework you are using but there are ways of making it inject the mock details. You'll have to give us more information before before we can answer.
Personally I don't much like Autowired private fields, so at the risk of answering a different question can I suggest you consider using an Autowired constructor instead. From Springs POV it won't make a difference, your object will be create and all the appropriate data wired in. (OK, there is a slight change in the order things are done, but generally you won't notice). You will have to write a constructor to copy the constructor parameters to private fields, but:
Those fields could be made final, which could make your class safer
Your Unit tests wont need any 'magic' to initialise the Autowired fields - just pass parameters
If you refactor you class to remove add/remove/modify an Autowired field then you have to remember to change your test code. With an Autowired constructor you test code has to be changed or it won't compile, and your IDE might even help you do it.
Update
The Autowired constructor alternative looks something like:
#Controller
class MyClass {
private final Class1 bean1;
private final Object value2;
#Autowired
MyClass(Class1 bean1, Class2 bean2) {
this.bean1 = bean1;
this.value2 = bean2.getValue();
}
}
Keys points are:
The class has just one constructor and it requires parameters.
The fields are not annotated #Autowired, because Spring is not assigning values to them; the constructor does that.
The constructor IS annotated as #Autowired to tell Spring to pass the beans as parameters
The first parameter is stored in a final variable - you code can't accidentally over write it, so your code is safer
In my example the second parameter is only used in the constructor, so we don't have to store it as a field in your controller. I often to this if the Bean is an object that passes configuration around.
A No-argument constructor is not required
At test time your code will have to pass parameters to the class.
Your test code will look something like:
class MyClassTest {
private Class1 bean1;
private Class2 bean2;
private MyClass objectUnderTest;
#Before
public void setUp() throws Exception {
bean1 = mock(Class1.class);
bean2 = mock(Class2.class);
// Train mocks here
objectUnderTest = new MyClass(bean1, bean2)
}
#Test
public void myTest() {
// Do something with objectUnderTest
}
}
Key points are:
There are no #MockBean annotations
The Unit test is only using the API that your Controller bean defines; No black magic is required
It's not possible to create a MyClass with out providing the required data. This is enforced by the compiler
I think you can try it.
The sample of code:
public interface Animal {
}
#Service
public class Cat implements Animal{
}
#Service
public class Dog implements Animal{
}
#Service
public class Clinic {
#Autowired
private final Map<String, Animal> animalMap = new HashMap<>(2);
}
Sample of test
#Configuration
public class TestEnvConfig {
#Bean
public Clinic create(){
return new Clinic();
}
#MockBean // you can do it without mock or use #ComponentScan
Dog dog;
#MockBean
Cat cat;
}
#SpringBootTest(classes = TestEnvConfig.class)
#RunWith(SpringRunner.class)
public class ClinicTest {
#Autowired
private Clinic clinic;
}

Invoke abstract class constructor only once and not in every specific class

I have something like this:
public abstract class Handler {
protected ItemRepository itemRepository;
public Handler(ItemRepository itemRepository) {
this.itemRepository = itemRepository;
}
}
public class SpecificHandler1 extends Handler {
public SpecificHandler1(ItemRepository itemRepository) {
super(itemRepository);
}
}
public class SpecificHandler2 extends Handler {
public SpecificHandler2(ItemRepository itemRepository) {
super(itemRepository);
}
}
I don't want to pass ItemRepository as an argument for each specific handler as it'll always be the same instance of itemRepository. I'd like to pass the ItemRepository only once. I'm not working with Spring boot which would make my life easier using #Autowired annotation. Any idea? Thanks
I don't want to pass ItemRepository as an argument for each specific
handler as it'll always be the same instance of itemRepository.
While that is not DRY (don't repeat yourself), what you do is the correct way.
Dependencies of classes should ideally be set when the object is instantiated or when not suitable via a setter.
To achieve that the subclass has to declare theitemRepository as parameter of the constructor or at worst as setter.
With or without Spring Boot you would encounter the same "problem" (that is not a problem in fact).
A simple way to be DRY (among others) that I would not recommend would be to "hide"/strong couple the itemRepository dependency in the abstract class :
public abstract class Handler {
protected ItemRepository itemRepository;
public Handler() {
this.itemRepository = new ItemRepository();
}
}
But as a side effect, you get a class that becomes not unit testable since itemRepository is not a visible dependency.

Assisted inject to inject different dependencies based on runtime check

I have an application class "Application", one abstract class "AbstractClass" extended by "Impl1" and "Impl2".
The application class gets the impl1 or impl2 to perform some task based on the input it receives.
Currently I am injecting both the classes into the application class as shown below.
Then based on input, I either ask impl1 OR impl2 to perform the task.
public class Application {
private static final Data data1 = DATA_CONFIG.data_1;
private final AbstractClass impl1;
private final AbstractClass impl2;
#Inject
Application(final AbstractClass impl1, final AbstractClass impl2){
this.impl1 = impl1;
this.impl2 = impl2;
}
public void mainTask(final Data data){
if(data == data1){
impl1.performTask();
}else{
impl2.performTask();
}
}
}
But, is there any way I could use assisted inject or a similar concept to inject only the dependency required, so for example input is data1, I only inject impl1 and not impl2.??
So, what you want is to select injected object depending to some context of injection point - value of Data object in particular. I didn't do such things and can't guarantee success, but you can try custom injections.
Also you can do something like factory. But IMHO, this approach is not much better than original, cause it will just move selection between impl1 and impl2 to a factory class you have to create first.
Sketch:
#Inject
Application(IAbstractClassFactory factory){
this.factory = factory
}
void mainTask(final Data data){
impl = factory.create(data)
}

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.

Categories

Resources