Springboot: Compilation error while autowiring the parent class - java

All,
I am trying to autowire a parent class which has a child class with additional methods. As far as I know, Spring will inject an instance of a child class to a parent class reference. To be sure, I even added a qualifier, but I am getting a compilation error. Please advice on how to resolve this.
Here is the code
Parent class:
#Service
Class Student {
void doSomething (int i) {
}
}
Child class:
#Service
#Qualifier("childclass")
Class ScholarStudent extends Student {
void doAnotherThing (int i) {
}
}
Controller:
#RestController
Class StudentController {
#Autowired
#Qualifier("childclass")
Student student;
#GetMapping(value = "/students/{studentId}")
public restCallFromFE (#PathVariable int studentId) {
student.doAnotherThing (studentId);
}
}
I get a compilation error here stating
The method doAnotherThing() is undefined for the type Student.
(Will try to attach an image here)
I can understand this is because the method is not defined in the parent class. But how do I resolve this with autowiring?
Thanks in advance to everyone who responds.

You cannot call the method of a child class from the reference to a base class that's not declared in the base class in any way.
To fix this you'll have to make your Student an interface or an abstract class that declares all the methods the child classes will have (and maybe implements some of them itself). If you don't need all of those methods in a single Student interface, just split it into multiple interfaces and autowire those which have the methods required for the concrete usage.

Try this:
#RestController
Class StudentController {
#Autowired
#Qualifier("childclass")
Student student;
#GetMapping(value = "/students/{studentId}")
public restCallFromFE(#PathVariable int studentId) {
if (student instanceof ScholarStudent) {
var scholar = (ScholarStudent)student;
scholar.doAnotherThing(studentId);
}
}
}
Note that you may want to consider looking at polymorphic methods (eg: overriding doSomething(int i) instead of creating a separate method) if you don't want consumers to know the specialised class type.

Related

Why does my direct autowire property injection turns into null? [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 7 months ago.
So I try to inject an interface implemention through a field. But can't figure it out why it's null.
Package
com.a
Interfacex
com.b
Interfaceximpl
Interfacex.java
public interface Interfacex {
void doA ();
}
Interfaceximpl.java
#Component
public class Interfaceximpl implements interfacex {
#Override
void doA(){
// do something
}
}
Main.java
public class Main {
#Autowired
Interfacex interfacex;
public static void main (String args[]){ //....}
}
This interfacex seems to be null.
#Configuration
#ComponentScan("com")
public class AppConfig { // nothing here}
There is no such setter injection in my case. I just inject the interfacex with #Autowired. Why is it null?.
Are you really placing the #Autowired on the field of the main class or its just an illustration? If you do - it won't work because the class on which #Autowired can happen must be by itself managed by Spring. And in this case its obviously not, because its a special class - an entry point of the application...
I suggest to use something like this instead:
#Component
public class EntryPoint {
#Autowired
Interfacex interfacex;
public void foo() {
// interfacex shouldn't be null
// because everything is managed by spring now
interfacex.doA();
}
}
public class Main {
public static void main(..) {
ApplicationContext ctx = ...
EntryPoint ep = ctx.getBean(EntryPoint.class);
ep.foo();
}
}
#Autowired annotation will not be recognized and acted upon by spring unless the class is a bean.
In this case, the 'Main' class is not a bean. So, #Autowired will do nothing.
You need to make 'Main' a bean. One way to do that is to add #Component annotation to that class.
Also, this seems like just something you are trying out to understand the working. In that case, you can try my above suggestion.
If you are doing this for prod, then you should change the class structure, class and variable names and the way you are creating beans.

Use case of BeanNameAware

I can't figure out any use case of BeanNameAware interface except for logging the name of bean itself.
I did my research but I could not find a single person who wrote something other than just printing the bean name after the bean has been initialized. Does it have any real use case?
BeanNameAware can be used where we have an multiple classes subclassing a abstract class and would like to know the name of those particular bean in order to use their functionality, do something if bean name follows certain pattern, manipulate them, etc. Let's take an example and understand it:
abstract class Parent implements BeanNameAware {
String beanName;
void setBeanName(String beanName) {
this.beanName = beanName;
}
abstract void doFilter();
}
#Component
class Child1 extends Parent {
#Override
void doFilter() {
// some impl
}
}
#Component
class Child2 extends Parent {
#Override
void doFilter() {
// some impl
}
}
And we have a service method that takes instance of all Parent class and invokes abstract void doFilter() method implementation:
#Service
class SomeService{
#Autowired
Parent[] childs; // injecting all Child*
void doSomethingWithChilds() {
for(Parent child: childs) {
child.doFilter(); // invoking their doFilter() impl
String currentChildName = child.beanName;
// We now know the name of current child* bean
// We can use it for manipulating the child* instance
// This is useful because each child instance will have a different bean name
if(currentChildName.equals("child2")) {
// do something special with child2
}
}
}
}

Nested #RestController (inner class) - any side effects?

I have a class annotated with #RestController. Inside that class I have an inner class, which is again annotated with #RestController. Is this fine or are there any unintended side-effects with regards bean creation (using Spring)?
#RestController
#RequestMapping("/api/v1/internal")
public class ClientController {
#GetMapping("/clients/{id}")
public ClientDTO.OutDetail findOne(#PathVariable String id) {
return clientService.findOne(id, ClientDTO.OutDetail.class);
}
#RestController
#RequestMapping("/api/v1/external")
public class ExternalApi {
#GetMapping("/clients/{id}")
public ClientDTO.OutDetailExt findOne(#PathVariable String id) {
return clientService.findOne(id, ClientDTO.OutDetailExt.class);
}
}
}
You can do this. First remove public from inner class and now your URL will be http://ip:port/appName/api/v1/external/clients/{id}. But my suggestion is that please create different RestController class so you can easily track it.

How to specify which subclass Spring should use

In my spring-based project I have a core module ('core') with a class
#Component
public class Superclass {
// stuff
}
instances of which are injected by type throughout the code like this:
public class AService {
#Autowired
private Superclass superclass;
// service stuff
}
I also have two other modules that depend on the core module and one of which (let's call it 'module1') extends Superclass:
#component
public class Subclass extends Superclass {
// overridden stuff
}
The other module ('module2') uses Superclass as is.
Now I want that when I compile and run 'child1' an instance of Subclass is used everywhere an instance of Superclass is expected. So I write a configuration class:
#Configuration
public class Module2Configuration {
#Bean
public Superclass superclass(){
return new Subclass();
}
}
When I run this I see both Superclass and Subclass instantiated which is definitely not what I want. How do specify in 'module1' which type Spring should instantiate?
You can use #Qualifier("some name") annotation.
There is more information about that: http://blogs.sourceallies.com/2011/08/spring-injection-with-resource-and-autowired/
Spring eagerly instantiates singleton beans as stated in the documentation:
By default, ApplicationContext implementations eagerly create and configure all singleton beans as part of the initialization process.
which might explain why both #Components are created.
To specifiy which implementation is provided as a dependency you might want to check on Qualifiers that enable to choose between different implementations. In combination with lazy loading this should do the trick.
Depending on your personal taste you could also use delegation instead of inheritance using a separated interface:
public interface MyService {
public String foobar(int baz);
}
public static class CommonBehavior {
// whatever is used by Superclass and Subclass
}
#Component #Lazy
public class FormerSuperClass implements MyService {
private final CommonBehavior ...;
...
}
#Component #Lazy
public class FormerSubClass implements MyService {
private final CommonBehavior ...;
...
}
Good luck!
There are 2 methods: Use #Qualifier("SubclassName") Or Mark your subclass as #Component and declare the subclass when #Autowired
In your case:
Use #Qualifier("SubclassName")
#Component
public class Superclass {
// stuff
}
#component
public class Subclass extends Superclass {
// overridden stuff
}
public class AService {
#Autowired
#Qualifier("Subclass")
private Superclass superclass;
// service stuff
}
2.Mark your subclass as #Component and declare the subclass when #Autowired
public class Superclass {
// stuff
}
#component
public class Subclass extends Superclass {
// overridden stuff
}
public class AService {
#Autowired
private Subclass subclass;
// service stuff
}

Providing DI methods in abstract classes

In most cases I have a lot of components which are having the same classes to be injected by an OSGi Declarative Service. The services will be used to execute some logic which is the same for all derived components. Therefore to avoid duplicated code it would be the best to use abstract classes. Is there any possibility to move the DI reference methods (set/unset) to an abstract class. I'm using Bnd.
For Example:
#Component
public class B implements IA {
private ServiceC sc;
#Reference
public void setServiceC(ServiceC sc) {
this.sc = sc;
}
public void execute() {
String result = executeSomethingDependendOnServiceC();
// do something with result
}
protected String executeSomethingDependendOnServiceC() {
// execute some logic
}
}
#Component
public class D implements IA {
private ServiceC sc;
#Reference
public void setServiceC(ServiceC sc) {
this.sc = sc;
}
public void execute() {
String result = executeSomethingDependendOnServiceC();
// do something different with result
}
protected String executeSomethingDependendOnServiceC() {
// execute some logic
}
}
I want to move the setter for ServiceC and the method executeSomethingDependendOnServiceC() to an abstract class. But how does it look like in OSGi in connection with Bnd annotation. Just annotate the class with #Component is not working, because A and D will create different instances of the abstract class and the #Component is alsp creating an instance.
Maybe someone experience the same problem and give me some advices how a workaround could look like. At least a best practice solution would be fine as well :)
The DS annotations must be on the class being instantiated for the component. Annotations on super classes are not supported. There is a proposal to change the in a future spec release.
What you can do is move the method to the super class, but you will need to trivially override the method in the subclass so that you can annotate it in the subclass.

Categories