I have this specific problem in which I can't use a @Qualifier
cause I need the bean in the parent class. My idea is to remove the baseComponent
propertie and make an abstract method in BaseController
like getComponent()
and return the desired bean for BaseComponent
... but perhaps there is a cleaner way to do this through configuration.
@RestController
public abstract class BaseController {
@Autowired
private BaseComponent baseComponent;
@GetMapping("/something")
public void doSomething() {
baseComponent.printSomething();
}
}
@RestController
@RequestMapping(value = "/foo")
public class FooController extends BaseController {
}
@RestController
@RequestMapping(value = "/bar")
public class BarController extends BaseController {
}
public interface BaseComponent {
void printSomething();
}
@Component
public class FooComponent implements BaseComponent {
@Override
public void printSomething() {
System.out.println("foo!");
}
}
@Component
public class BarComponent implements BaseComponent{
@Override
public void printSomething() {
System.out.println("bar!");
}
}
This is one of the reasons I don't like autowiring directly to a private field. I would do this by injecting BaseComponent through the constructor of BaseController:
public abstract class BaseController {
private final BaseComponent baseComponent;
protected BaseController(BaseComponent baseComponent){
this.baseComponent = baseComponent;
}
@GetMapping("/something")
public ResponseEntity<String> getSomething(){
return new ResponseEntity<String>(baseComponent.getSomething(), HttpStatus.OK);
}
}
@RestController
@RequestMapping("/foo")
public class FooController extends BaseController{
@Autowired
public FooController(@Qualifier("fooComponent") BaseComponent baseComponent) {
super(baseComponent);
}
}
@RestController
@RequestMapping("/bar")
public class BarController extends BaseController{
@Autowired
public BarController(@Qualifier("barComponent") BaseComponent baseComponent){
super(baseComponent);
}
}
@Component
public class BarComponent implements BaseComponent {
@Override
public String getSomething() {
return "bar";
}
}
@Component
public class FooComponent implements BaseComponent {
@Override
public String getSomething() {
return "foo";
}
}
Requests to /something/bar will return bar and requests to something/foo will return foo.
Note that the abstract BaseComponent
is not actually declared as any kind of Spring component nor does it have any dependencies automatically injected. Instead, the subclasses are the components and the dependencies are wired into their constructors and passed through super
to BaseComponent
. The subclass constructors provide a place for the @Qualifier
annotation to specify which BaseComponent
you want.
In theory, I don't like declaring two classes that are identical other than the annotations. In practice, though, I have found that sometimes it is simplest just to declare classes to hold the Spring annotations. That's better than the old days of XML configuration.