Using Polymorphism in Spring Boot services? - java

In my Spring Boot app, I am thinking of using an approach as the following interface and service implementations:
PDFService:
public interface PDFService {
String createPdf(UUID uuid);
}
BrandPDFService:
#Service
#RequiredArgsConstructor
public class BrandPDFService implements PDFService {
private final BrandService brandService;
#Override
public String createPdf(UUID uuid) {
Brand brand = brandService.findByUuid(uuid);
// ... code omitted for brevity
return generateHtml(brand);
}
}
ProductPDFService:
#Service
#RequiredArgsConstructor
public class ProductPDFService implements PDFService {
private final ProductService productService;
#Override
public String createPdf(UUID uuid) {
Product product = productService.findByUuid(uuid);
// ... code omitted for brevity
return generateHtml(product);
}
}
For using these services:
// brand way
PDFService pdfService = new BrandService();
pdfService.createPdf(uuid);
// product way
PDFService pdfService = new ProductService();
pdfService.createPdf(uuid);
So, I think I need to use generic and pass it to PDFService and then their implementations, but I am not sure how to make it properly (using generic or passing via constructor). So, in order to use createPdf efficiently without repeating code (I know I can also use Template Pattern method, but I just wanted to know polymorphism side) how should I apply polymorphism to these Spring Boot Services properly?

Since BrandPDFService and ProductPDFService are Spring beans (because you annotated them with the #Service annotation), you should not be instantiating them yourself by using new. Instead, you should let Spring autowire them into the class where you are using them.
Because they are both implementations of interface PDFService, when you autowire them, you need to have something to let Spring distinguish them. Otherwise, if the field you are autowiring them in is of type PDFService, Spring won't know which implementation of the interface to autowire. You can give the beans names and use the #Qualifier annotation:
#Service("brandPDFService")
public class BrandPDFService implements PDFService { ... }
#Service("productPDFService")
public class ProductPDFService implements PDFService { ... }
// Example controller where you autowire them
#RestController
public class MyController {
#Autowired
#Qualifier("brandPDFService")
private PDFService brandPDFService;
#Autowired
#Qualifier("productPDFService")
private PDFService productPDFService;
// ...
}
So, I think I need to use generic and pass it to PDFService and then their implementations
I don't know why you think you need to use generics; this doesn't have anything to do with generics.

Related

Inject multiple beans of the same type and automatically select between them based on generic type

I have two (more in the future) implementations of ImportantService – VeryImportantService and LessImportantService:
public interface ImportantService<T extends ImportantRequest> {}
#Service
public class VeryImportantService implements ImportantService<VeryImportantRequest> {}
#Service
public class LessImportantService implements ImportantService<LessImportantRequest> {}
And then I have a controller, in which I want to inject all of the implementations of ImportantService:
#RequiredArgsConstructor
#RestController
#RequestMapping("/api/important")
public class ImportantController<T extends ImportantRequest> {
private final ImportantService<T> importantService;
#PostMapping
public ResponseEntity<ImportantResponse> create(#RequestBody #Valid T request) {
// very important code here
}
}
Obviously, such king of injecting fails:
UnsatisfiedDependencyException: Error creating bean with name 'importantController' defined in file ...
...
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
What I want is:
Inject all of the implementations of ImportantService, and then, based on the T automatically select required bean. I know I can add method to ImportantService, which returns the type that implementation works with and then inject ImportantService as List<ImportantService> importantServices and then filter like this:
importantServices.stream()
.filter(importantService -> importantService.getType().equals(request.getClass()))
.findFirst()
.ifPresent(importantService -> importantService.doImportantJob(request));
BUT! I have hundreds of services to refactor like this and I really don't want to write additional logic to controllers.
I know about #Conditional annotation and Condition interface, but AFAIK there's no way to make them do what I want.
Why not implement the proxy pattern?
example:
#Service
#Primary
#RequiredArgsConstructor
public class ImportantServiceProxy implements ImportantService<T extends ImportantRequest> {
private final List<ImportantService> importantServices;
private ImportantService getImportantService(ImportantRequest request){
return this.importantServices.stream()
.filter(importantService -> importantService.getType().equals(request.getClass()))
.findFirst()
.get();
}
public void doImportantJob(ImportantRequest request){
this.getImportantService(request).doImportantJob(request);
}
}
Then in your controller you can call the function without check the type.
#RequiredArgsConstructor
#RestController
#RequestMapping("/api/important")
public class ImportantController<T extends ImportantRequest> {
private final ImportantService<T> importantService;
#PostMapping
public ResponseEntity<ImportantResponse> create(#RequestBody #Valid T request) {
importantService.doImportantJob(request);
}
}
what you want is a list of beans which are of type ImportantService
so you have to declare a variable like this.
final List<ImportantService> importantServices;
demoController(List<ImportantService> importantServices) {
this.importantServices = importantServices;
}

Implicitly qualified autowiring in Spring

I have a project with some independent bean X, that is autowired in a bunch of services. Services are used by each other, and finally used in single entry point (controller). Now there is new requirement: implement several versions of X, and decide witch one is to use according to entry point's parameter (enum XType). It would be nice to do it without changing services.
My idea of solution is to create custom scope UsesX and implement BeanFactoryPostProcessor, that will converts each BeanDefinition with UsesX to set of singletons for each XType. Also, it will adds qualifiers to this beans, to make it possible to make factory method for X and parameter-based selection in controller. But how to add this qualifier to #Autowired in services implicitly, without changing their classes?
UPD
Ok, example, I want to use db url "jdbc:mysql://Adb" when A requested, and "jdbc:mysql://Bdb" when B:
enum DatabaseType {A, B}
#Controller
#RequestMapping(/)
class MyController {
#Autowired ServiceProvider provider; // some way to get service by DatabaseType
void foo(#RequestParam DatabaseType dbType) {
ServiceA a = provider.getA(dbType);
a.bar();
ServiceB b = provider.getB(dbType);
b.baz();
}
}
#Service
class ServiceA {
// Don't want to get information about different databases in services
#Autowired ServiceB b;
#Autowired ServiceC c;
#Autowired DaoFoo dao;
//...
}
#Service
class ServiceB {
#Autowired ServiceC c;
#Autowired DaoFoo daoFoo;
#Autowired DaoBar daoBar;
//...
}
#Service
class ServiceC {
#Autowired DaoBar daoBar;
//...
}
#Repository
class DaoFoo {
DaoFoo(String dbURL) {/*...*/}
}
#Repository
class DaoBar {
DaoFoo(String dbURL) {/*...*/}
}
Also, it is required to "jdbc:mysql://Adb" and "jdbc:mysql://Bdb" be configured in XML configuration.
I want to wrap up your requirements so that it would be clear if I'm getting you right.
You have a set of #Services that you don't want to modify.
At this moment you have only one implementation of X type which is used by this services.
The choice of X implementation to be used in services would be defined by XType enum, which in turn would be available from request.
You want X type beans be configurable from xml.
OP: What X implementation should be used in case if one of this services would be called w/o XType?
So if my understanding is correct, it seems like you need Proxy for X type.
Within this Proxy you need to get this XType implicitly (f.ex. through ThreadLocal var).
As #Autowired is used, beans are identified by type in first place. Therefore, you need to use already existing X implementation for proxing and extract your current implementation and new one to different type.
As a result you might end up with following:
interface newX {
void save();
}
#Repository
class DaoFoo implements newX {
public void save() {...};
}
#Repository
class DaoBar implements newX {
public void save() {...};
}
class XImpl implements X, newX {
public final ThreadLocal<XType> currentXType = new ThreadLo...;
Map<XType, newX> mapping = ....
public void save() {mapping.get(currentXType.get()).save();};
}
Your example is a little confusing because your Services are named A and B, but you also use A and B for your DatabaseType. But I think I understand what you want.
I don't think you can do this with Autowired, but you can make your Services as #Scope("prototype") and retrieve them from context. The context should instantiate the Service the first time you request it, then reuse the same bean when the same input is provided.
#Configuration
public class ServiceProvider{
...
#Bean
#Scope("prototype")
public ServiceA serviceA(DatabaseType dbType) {
...
}
#Bean
#Scope("prototype")
public ServiceB serviceB(DatabaseType dbType) {
...
}
}
#Controller
#RequestMapping(/)
class MyController {
#Autowired
ConfigurableApplicationContext context
void foo(#RequestParam DatabaseType dbType) {
AutowireCapableBeanFactory beanFactory = context.getBeanFactory();
ServiceA serviceA = (ServiceA)context.getBean("serviceA", dbType);
...
}
}
Create a service interface like :
interface ServiceInterface{
public boolean isTheOne(String type); // or some suitable name.
}
all your services then need to implement this interface, then in controller
#Controller
#RequestMapping(/)
class MyController {
#Autowired
Set<ServiceInterface> provider;
void foo(#RequestParam DatabaseType dbType) {
ServiceInterface service = provider.stream().filter(s -> s.isTheOne(String dbType));
service.bar();
}
}
You can maintain your XType as an enumerator within a customised Qualifier by developing #Interface.
Please find below a sample which mentions conditional wiring of beans based upon different types of datatype:
#Target({ElementType.FIELD,
ElementType.METHOD,
ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public static #interface DBHost{
public static enum DatabaseType {
A,
B
}
}
#Autowired
#DBHost(DBHost.DatabaseType.A)
ServiceBean serviceInstanceA;
Find additional usage of Qualifier annotation here

How to use a dynamic implementation of an interface in Spring?

This question is intended to make an answer for a useful issue.
Suppose we have a Spring application with a #Controller, an interface and different implementations of that interface.
We want that the #Controller use the interface with the proper implementation, based on the request that we receive.
Here is the #Controller:
#Controller
public class SampleController {
#RequestMapping(path = "/path/{service}", method = RequestMethod.GET)
public void method(#PathVariable("service") String service){
// here we have to use the right implementation of the interface
}
}
Here is the interface:
public interface SampleInterface {
public void sampleMethod(); // a sample method
}
Here is one of the possibile implementation:
public class SampleInterfaceImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
And here is another one:
Here is one of the possibile implementation:
public class SampleInterfaceOtherImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
Below I'll show the solution that I've found to use one of the implementations dynamically based on the request.
The solution I've found is this one.
First, we have to autowire the ApplicationContext in the #Controller.
#Autowired
private ApplicationContext appContext;
Second, we have to use the #Service annotation in the implementations of the interface.
In the example, I give them the names "Basic" and "Other".
#Service("Basic")
public class SampleInterfaceImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
#Service("Other")
public class SampleInterfaceOtherImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
Next, we have to obtain the implementation in the #Controller.
Here's one possible way:
#Controller
public class SampleController {
#Autowired
private ApplicationContext appContext;
#RequestMapping(path = "/path/{service}", method = RequestMethod.GET)
public void method(#PathVariable("service") String service){
SampleInterface sample = appContext.getBean(service, SampleInterface.class);
sample.sampleMethod();
}
}
In this way, Spring injects the right bean in a dynamic context, so the interface is resolved with the properly inmplementation.
I solved that problem like this:
Let the interface implement a method supports(...) and inject a List<SampleInterface> into your controller.
create a method getCurrentImpl(...) in the controller to resolve it with the help of supports
since Spring 4 the autowired list will be ordered if you implement the Ordered interface or use the annotation #Order.
This way you have no need for using the ApplicationContext explicitly.
Honestly I don't think the idea of exposing internal implementation details in the URL just to avoid writing some lines of code is good.
The solution proposed by #kriger at least adds one indirection step using a key / value approach.
I would prefer to create a Factory Bean (to be even more enterprise oriented even an Abstract Factory Pattern) that will choose which concrete implementation to use.
In this way you will be able to choose the interface in a separate place (the factory method) using any custom logic you wish.
And you will be able to decouple the service URL from the concrete implementation (which is not very safe).
If you are creating a very simple service your solution will work, but in an enterprise environment the use of patterns is vital to ensure maintenability and scalability.
I'm not convinced with your solution because there's an implicit link between an HTTP parameter value and a bean qualifier. Innocent change of the bean name would result in a disaster that could be tricky to debug. I would encapsulate all the necessary information in one place to ensure any changes only need to be done in a single bean:
#Controller
public class SampleController {
#Autowired
private SampleInterfaceImpl basic;
#Autowired
private SampleInterfaceOtherImpl other;
Map<String, SampleInterface> services;
#PostConstruct
void init() {
services = new HashMap()<>;
services.put("Basic", basic);
services.put("Other", other);
}
#RequestMapping(path = "/path/{service}", method = RequestMethod.GET)
public void method(#PathVariable("service") String service){
SampleInterface sample = services.get(service);
// remember to handle the case where there's no corresponding service
sample.sampleMethod();
}
}
Also, dependency on the ApplicationContext object will make it more complicated to test.
NB. to make it more robust I'd use enums instead of the "Basic" and "Other" strings.
However, if you know you'll only have two types of the service to choose from, this would be the "keep it simple stupid" way:
#Controller
public class SampleController {
#Autowired
private SampleInterfaceImpl basic;
#Autowired
private SampleInterfaceOtherImpl other;
#RequestMapping(path = "/path/Basic", method = RequestMethod.GET)
public void basic() {
basic.sampleMethod();
}
#RequestMapping(path = "/path/Other", method = RequestMethod.GET)
public void other() {
other.sampleMethod();
}
}

Spring #Controller and RequestMapping calling different services depending on a given parameter

Let's assume I have this code:
#Controller
#RequestMapping("/something")
public class SomeController {
#Autowired
private SomeService aService;
#RequestMapping("/doStuff")
public void doStuff(#RequestParam("type") String type) {
aService.doStuff();
}
}
In my application I need to call a specific service depending on the specified type. All services implements the same interface. If I understand correctly SomeService cannot be an interface. I could use a service factory and instantiate the service depending on the type every time a new request is done, but this doesn't look very efficient.
Alternatively I could use a different controller for each different type of service (and encode the type in the REST URI), but this would imply a lot of code duplication since all the services basically implements the same interface.
My question is, assuming the called service depends on the passed parameter, what is the best pattern to adopt for this scenario?
Similar to RC.'s answer, instead of using a Map and adding the values by you, just let the Spring BeanFactory handle this for you:
#Controller
#RequestMapping("/something")
public class SomeController {
#Autowired
private BeanFactory beanFactory;
#RequestMapping("/doStuff")
public void login(#RequestParam("type") String type) {
SomeService aService = (SomeService)beanFactory.getBean(type);
aService.doStuff();
}
}
You could use a map here, something along this:
#Controller
#RequestMapping("/something")
public class SomeController {
#Autowired
private SomeService someService;
#Autowired
private SomeOtherService someOtherService;
// ...
private final Map<String, ServiceCommonInterface> serviceMap = new HashMap<>();
#PostConstruct
private void postConstruct() {
serviceMap.put(typeForSomeService, someService);
serviceMap.put(typeForSomeOtherService, someOtherService);
}
#RequestMapping("/doStuff")
public void login(#RequestParam("type") String type) {
// TODO: ensure type is correct (an enum might be handy here)
serviceMap.get(type).doStuff();
}
}
Or better, as stated in comments you can leverage qualifiers:
#Controller
#RequestMapping("/something")
public class SomeController {
#Autowired
private ApplicationContext applicationContext;
#RequestMapping("/doStuff")
public void login(#RequestParam("type") String type) {
// TODO: ensure type is a correct bean name
applicationContext.getBean(type, ServiceCommonInterface.class).doStuff();
}
}
Depending on the number of types you wish to support there are two options I see.
1) Autowire in a factory as you mentioned and lazily create each service as needed. If the services are stateless you could keep a reference to the object after created so would only need to create once per type.
2) Autowire in a Spring Map with the key being your types and the value being the correct service to use. Then when your receive the type, can retrieve the correct service impl for your map.
For example for map: http://www.mkyong.com/spring/spring-collections-list-set-map-and-properties-example/ or see How to inject a Map<String, List> in java springs?
Both of these require you to create an interface for your service which is something you said is possible.

What's the proper way to inject Entity-based classes in Spring IoC

Please bear with me:
We have a setup of Hibernate and Spring IoC, in which for each entity (User, Customer, Account, Payment, Coupon, etc) there's a bunch of "singleton" interfaces and implementation classes that support it.
For example: forCustomer:
#Entity
public class Customer extends BaseEntity {
...
public name();
}
/* base API */
public interface Service {
public create();
public list();
public find();
public update();
public delete();
}
/* specific API */
public interface CustomerService extends Service {
public findByName();
}
/* specific implementation */
public class CustomerServiceImpl extends BaseService implements CustomerService {
...
}
And this pattern goes on and on (CustomerManager, CustomerDataProvider, CustomerRenderer, etc.).
finally, in order work against an instance of a specific API (e.g. CustomerService.findByName()), a static global holder had evolved - which makes references like the following available:
public class ContextHolder {
private static AbstractApplicationContext appContext;
public static final CustomerService getCustomerService() {
return appContext.getBean(CustomerService.class);
}
//... omitting methods for each entity class X supporting class
}
#Configuration
public class ServicesConfiguration {
#Bean(name = "customerService")
#Lazy(false)
public CustomerService CustomerService() {
return new CustomerServiceImpl();
}
//... omitting methods for each entity class X supporting class
}
So, the question is:
what would be the proper way to inject those supporting classes, e.g. CustomerService, given an entity instance, for the following uses:
I have a specific entity (e.g. a Customer), and would like to get a service and call a specific API (e.g. findByName())?
I have an entity (don't care which one in specific), and would like to call a general API (e.g. find())
All this, while avoiding global static references (and thus, swap implementations in e.g. tests, and simplify the caller code).
So i can get a any supporting class if I have an entity instance
BaseEntity entity = ... // not injected
Iservice service = ...// should be injected
service.create(entity);
or, get all the supporting classes I need for a given entity type
/* specific implementation */
public class CustomerServiceImpl extends BaseService implements CustomerService {
// inject specific supporting classes
#Autowire CustomerManager manager;
#Autowire CustomerDataProvider provider;
#Autowire CustomerRenderer renderer;
#Autowire CustomerHelper helper;
...
}
and, change the configuration a bit in other scenarios
// how to configure Spring to inject this double?
Class CustomerManagerDouble extends CustomerManager {...}
#Autowired #Test public void testSpecificAPI(CustomerService service) {
service.doSomethingSpecific();
assert ((CustomerManagerDouble) service.getManager()).checkSomething();
}
I'm not entirely sure what you're asking, but I think you want to inject entity objects (created by Hibernate) with services, right?
If that's the case, use the #Configurable annotation as described in the Spring 3.1 documentation:
http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/aop.html#aop-atconfigurable
Note that you have to use AspectJ to weave the entity classes (load-time or compile-time) for this to work.

Categories