Spring-Boot Generic Constructor Injection Custom - spring

public abstract class BaseController<E,DTO> {
protected final BaseService<E,DTO> service;
public BaseController(final BaseService<E,DTO> service) {
this.service = service;
}
// code ....
}
public class CarController extends BaseController<Car, CarDto> {
public CarController(final CarService service) {
super(service);
}
// code ...
}
#Service
public class CarService extends BaseService<Car, CarDto> {
// code ....
//custom method
public String getName(){
return "Car Sevice";
}
}
Spring-boot generic method develop. Service created in constructor. There is no problem working properly. Problem is custom method of service. Because service extends BaseService and see all base services methods but, it is custom services as CarService. Intellij give error
service.getname();
how can i solve this problem

your service is type of BaseService, not CarService.
When you write something like BaseService service = new CarService() you can't access methods of CarService because your variable is BaseService type.
you would need to change service to be type of generic, same as you did with entity and dto.
public abstract class BaseController<E,DTO, S extends BaseService> {
protected final S service;
public BaseController(S service) {
this.service = service;
}
// code ....
}
public class CarController extends BaseController<Car, CarDto, CarService> {
public CarController(final CarService service) {
super(service);
service.getName();
}
// code ...
}
The extends BaseService in BaseController is only required if you would like to use any methods of BaseService in BaseController, like for example:
public abstract class BaseController<E,DTO, S extends BaseService> {
protected final S service;
public BaseController(S service) {
this.service = service;
service.doSomething();
service.overrideMe();
}
// code ....
}
public class CarController extends BaseController<Car, CarDto, CarService> {
public CarController(final CarService service) {
super(service);
service.getName();
service.overrideMe();
service.doSomething();
}
// code ...
}
public abstract class BaseService<E, DTO> {
public abstract void overrideMe();
public void doSomething() {
System.out.println("hi");
}
}
#Service
public class CarService extends BaseService<Car, CarDto> {
// code ....
//custom method
public String getName(){
return "Car Sevice";
}
#Override
public void overrideMe() {
System.out.println("Overrided");
}
}
If your BaseController doesn't need to know that S is extending BaseService (you won't call any methods of BaseService in BaseController) you can delete extends BaseService part
public abstract class BaseController<E,DTO, S> {
protected final S service;
public BaseController(S service) {
this.service = service;
}
// code ....
}
public class CarController extends BaseController<Car, CarDto, CarService> {
public CarController(final CarService service) {
super(service);
service.getName();
service.overrideMe();
service.doSomething();
}
// code ...
}
public abstract class BaseService<E, DTO> {
public abstract void overrideMe();
public void doSomething() {
System.out.println("hi");
}
}
public class CarService extends BaseService<Car, CarDto> {
// code ....
//custom method
public String getName(){
return "Car Sevice";
}
#Override
public void overrideMe() {
System.out.println("Overrided");
}
}
Also think if you need E and DTO in BaseController

Related

Spring Boot- Autowiring interface implemented by different beans

I have multiple beans with #Component implementing a generic interface.
I have a class where I use methods from these beans. Instead of autowiring each bean separately, I was figuring out of autowiring the interface as a list.
But how do I call the methods of different beans when I just autowire the interface.
public interface Generic {
}
#Component
public class A implements Generic{
.....
public void test{
}
}
#Component
public class B implements Generic{
.....
public void read{}
}
#component class C {
#Autowired
List <Generic> mylist; // type of list is Generic
Now I need to access read from B and test from A
With your new question you CAN do it like this way but I think it's better to use 2 seperate lists instead of the instanceOf.
public interface Generic { }
public interface GenericTestable extends Generic {
void test()
}
public interface GenericReader extends Generic {
void read()
}
#Component
public class A implements GenericTestable {
public void test() { }
}
#Component
public class B implements GenericReader {
public void read() { }
}
#Component
public class C {
private final List<Generic> genericItems;
#Autowired
public C(List<Generic> genericItems) {
this.genericItems = genericItems;
}
public void callThem() {
this.genericItems.forEach(this::callMethodOnSpecificType);
}
private void callMethodOnSpecificType(Generic genericItem) {
if (genericItem instanceof GenericTestable) {
((GenericTestable) genericItem).test();
} else if (genericItem instanceof GenericReader) {
((GenericReader) genericItem).read();
}
}
}
But I think that something like this is a better approach. Cleaner and faster code. Remove the Generic interface and use only the 2 separate interfaces.
public class BetterC {
private final List<GenericTestable> genericTestables;
private final List<GenericReader> genericReaders;
#Autowired
public BetterC(List<GenericTestable> genericTestables, List<GenericReader> genericReaders) {
this.genericTestables = genericTestables;
this.genericReaders = genericReaders;
}
public void callTestables() {
this.genericTestables.forEach(GenericTestable::test);
}
public void callReaders() {
this.genericReaders.forEach(GenericReader::read);
}
}
How about using the Generic interface to route the call ?
From the java-doc api : Ordered
Ordered is an interface that can be implemented by objects that should
be orderable, for example in a Collection. The actual order can be
interpreted as prioritization, with the first object (with the lowest
order value) having the highest priority.
import org.springframework.core.Ordered;
public interface Generic extends Ordered {
void call();
}
----
#Component
public class A implements Generic {
#Override
public int getOrder() {
return 2;
}
#Override
public void call() {
test();
}
public void test() {
System.out.println("Call test()");
}
}
----
#Component
public class B implements Generic {
#Override
public int getOrder() {
return 1;
}
#Override
public void call() {
read();
}
public void read() {
System.out.println("call read()");
}
}
----
#Component
public class C {
List<Generic> list;
public C(List<Generic> list) {
this.list = list;
}
public void testMethod() {
for(Generic g: list) {
g.call();
}
}
}
try this in your implementation
#Component
#Qualifer("a")
public class A implements Generic{ ..... public void test(){ }}
#Component
#Qualifer("b")
public class B implements Generic{ ..... public void read(){ }}
#component
public class C {
#Autowired
Generic a;
#Autowired
Generic b;
... call respective methods
}

factory pattern using spring

How can I choose a service implementation depending on a request parameter on SpringBoot? I can do this by manually instantiating the service, but that's not making use of the Spring Injection feature.
I can also Autowire the two services separately but I'm thinking that if I have more implementations that would pollute the class.
Here's a crude example:
#RestController
class RestControllerTest {
#Autowired
PizzaService pizzaService;
public void bakePizza(#RequestParam("type") String type,#RequestParam("extra") String extra) {
if (type.equals("cheese")) {
//set pizzaService Cheese Implementation
pizzaService = new CheezePizza();
} else {
//set PizzaService vegetable Impleentation;
pizzaService = new VegetablePizza();
}
pizzaService.prepareIngredients(extra);
pizzaService.bakePizza();
}
}
public abstract class PizzaService {
String ingredients;
public abstract void prepareIngredients(String exraIngredient);
public void bakePizza() {
System.out.println("baking pizza with " + ingredients);
}
}
class CheezePizza extends PizzaService {
#Override
public void prepareIngredients(String exraIngredient) {
ingredients = "Cheese " + exraIngredient;
}
}
class VegetablePizza extends PizzaService {
#Override
public void prepareIngredients(String exraIngredient) {
ingredients = "Vegetable " + exraIngredient;
}
}
You could autowire list of beans of same type. So let's say you add getType() to your PizzaService and register every type as spring bean.
public abstract class PizzaService {
abstract String getType();
}
#Component
class CheezePizza extends PizzaService {
#Override
public String getType() {
return "cheese";
}
}
#Component
class VegetablePizza extends PizzaService {
#Override
public String getType() {
return "vegetable";
}
}
#RestController
class RestControllerTest {
private final Map<String, PizzaService> pizzaServices;
public RestControllerTest(List<PizzaService> services) {
pizzaServices = services.stream().collect(Collectors.toMap(PizzaService::getType, Function.identity()));
}
public void bakePizza(#RequestParam("type") String type, #RequestParam("extra") String extra) {
PizzaService pizzaService = pizzaServices.get(type); // remember of handling missing type
pizzaService.prepareIngredients(extra);
pizzaService.bakePizza();
}
}
Another way is to use name your beans by convention i.e. cheesePizza, vegetablePizza and then use ApplicationContext#getBean(type + "Pizza") but I like first approach better, because it's less magical.

Spring Bean Factory Configuration passing input parameter

I'm trying to create a BeanFactory called TaskBeanFactory that I can Autowire into another prototype class that's running on a thread. I want a different instance of a bean returned by the Factory based on a taskName that i want to pass into it but when i start the application i get a null pointer exception because the taskName is null. I had a look at this article but i'm confused about how I should configure the Factory and then pass in the taskName.
The Factory:
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.stereotype.Component;
#Data
#Component
#NoArgsConstructor
public class TaskBeanFactory extends AbstractFactoryBean<GenericTask>{
private TaskNameEnum taskName;
public TaskBeanFactory(TaskNameEnum taskName) {
setSingleton(false);
}
#Override
public Class<?> getObjectType() {
return GenericTask.class;
}
#Override
protected GenericTask createInstance() throws Exception {
switch (taskName) {
case FILE_OPERATION:
return new FileTask();
case DATA_OPERATION:
return new DataTask();
default:
return new GenericTask();
}
}
}
The classes used by the Factory:
#Data
public class GenericTask {
private String idTask;
public void executeTask(Work work) {};
}
#Component
#Scope(value="prototype")
public class FileTask extends GenericTask {
#Override
public void executeTask(Work work) {
//some processing
}
}
#Component
#Scope(value="prototype")
public class DataTask extends GenericTask {
#Override
public void executeTask(Work work) {
//some processing
}
}
and the thread that's calling the Factory:
#Slf4j
#Data
#Scope("prototype")
#Component
public class WorkerThread implements Runnable {
#Autowired
private TaskBeanFactory taskBeanFactory;
#Autowired
private DataService dataService;
#Override
public void run() {
//iterate a Map of taskIds from the dataService
taskBeanFactory.setTaskName(TaskNameEnum.valueOf(taskEntry.getKey()));
GenericTask genericTask = taskBeanFactory.getObject();
//expecting genericTask to be of Type FileTask if called with one Key
//or of Type DataTask if called with another
}
}
}

#Configurable not working on Subclass

Take the following general abstract class:
#Configurable
public abstract class TestEntityRoot {
public abstract String print();
}
And a subclass:
#Configurable
public class TestEntity extends TestEntityRoot{
private TestEntityService testEntityService;
#Autowired
public void setTestEntityService(TestEntityService testEntityService) {
this.testEntityService = testEntityService;
}
#Override
public String print() {
return testEntityService.print();
}
}
When call controller:
#RestController
public class TestEntityController {
#GetMapping(name = "/test")
public String print() {
TestEntity entity = new TestEntity();
return entity.print();
}
}
everything ok. But if call like this:
#RestController
public class TestEntityController {
#GetMapping(name = "/test")
public String print() {
TestEntityRoot entity = new TestEntity();
return entity.print();
}
}
i get null pointer. Is it possible that second example work?
In the second case you create manually the class rather than using spring's bean. Autowire the bean instead. See
#RestController
public class TestEntityController {
#Autowired
private TestEntity entity
#GetMapping(name = "/test")
public String print() {
return entity.print();
}
}

How to test service class with #Autowired

I have a service class MyService which is defined and being used in controller like so:
public interface MyService {
public String someMethod()
}
#Service("myService")
public class MyServiceImpl implements MyService {
public String someMethod() {
return "something";
}
}
#Controller
public class MyController {
#Autowired
public MyService myService;
#RequestMapping(value="/someurl", method=RequestMethod.GET)
public String blah () {
return myService.getsomeMethod();
}
}
I'd like to write a test case for the someMethod method, however, the following doesn't work. How can I wire in the implementation class?
public class MyServiceImplTest {
#Autowired
private MyService myService;
#Test
public void testSomeMethod() {
assertEquals("something", myService.someMethod());
}
}
public class MyServiceImplTest {
private MyService myService = new MyServiceImpl();
#Test
public void testSomeMethod() {
assertEquals("something", myService.someMethod());
}
}
Why inject the bean in your test rather than creating an instance by yourself?
Try this:
#RunWith(SpringJUnit4ClassRunner.class)
// specifies the Spring configuration to load for this test fixture
#ContextConfiguration("yourapplication-config.xml")
Also see the Spring.IO docs for more detail.

Resources