Spring REST controller with service using proxy pattern - java

I have created REST controller with base request mapping on class.
#RestController
#RequestMapping(".../{type}/{typeId}/param..")
public class FooController{
#Autowired
BarServiceProxy proxy;
public List<Foo> getFoo(){
return proxy.get(getType());
}
/*
public Type getType(???){
return type;
}
*/
}
Next I have enum Type which determines what service will be used by proxy service (ie. proxy has injected list of serivces and gets one that supports type). I am wondering if there is any way how to make part of request mapping {type} and get it in getter method below so I don't have to repeat it in every request mapping in this class.
I only figured one alternative solution - make this class abstract and then extend it and return constant. This would however leave me with lot of classes without any added value. For example:
#RequestMapping(".../{typeId}/param..")
public abstract class FooController{
#Autowired
BarServiceProxy proxy;
public List<Foo> getFoo(){
return proxy.get(getType());
}
protected abstract Type getType();
}
#RestController
#RequestMapping("/typeAbc)
public class TypeAbcFooController extends FooController{
public Type getType{
return Type.Abc;
}
}
So is it possible to bind #PathVariable from URL specified on class #RequestMapping in some shared method? Thanks

I hope i've understood your problem, but one way of improving your design could be to implement a strategy per type, to inject them, and to use them corresponding to your type received in your controller.
Exemple:
public enum MyType {
TYPE1,
TYPE2
}
public interface IService {
MyType getHandledType();
List<Foo> getFoo();
}
#Service
public class Type1Service implements IService {
#Override
public MyType getHandledType() {
return MyType.TYPE1;
}
#Override
public List<Foo> getFoo() {
// IMPLEMENTATION FOR TYPE1;
}
}
public class FooController{
#Autowired
List<IService> services;
public List<Foo> getFoo(MyType requestType){
IService service = services.stream().filter(iService -> iService.getHandledType() == requestType).findFirst().get();
return service.getFoo();
}
}
This way your controller is agnostic of the underlying service implementation, which is a big responsability.

Related

Error when trying to make generic method in spring boot app

This is my code Service interface (which i am trying to generify):
#org.springframework.stereotype.Service
public interface Service<T extends Planning> {
List<T> get(Specification<T> spec, Sort sort);
//PagingResponse getOther(Specification<T> spec, HttpHeaders headers, Sort sort);
}
And I created another class that extends to Service class, here are my PlanningServiceImpl class :
#Data
#Service
public class PlanningServiceImpl implements PlanningService, com.qoze.meeting.services.Service<Planning> {
#Autowired
private PlanningRepositoryInterface planningRepository;
#Override
public Iterable<Planning> getAllPlannings() {
return planningRepository.findAll();
}
#Override
public List<Planning> get(Specification<Planning> spec, Sort sort){
return planningRepository.findAll(spec, sort);
}
}
My PlanningService interface :
public interface PlanningService{
Iterable<Planning> getAllPlannings();
}
First the compiler doesn't recognize the spec parameter given in parameter. Then I change to put the extends on T object.
But here with my service I only allowed to work with Planning objects, I wanted at the beginning to have a generic Service and use several objects. Can someone help to have a generic service in spring boot app?

Autowire Spring Bean into interface for default method

I need to add a default method to an interface some classes implement, but my IDE complains (bean may not have been initialized).
Code would be something like this:
public interface IValidator {
MyValidationBean beanToBeAutowired;
...
default Boolean doSomeNewValidations(){
return beanToBeAutowired.doSomeNewValidations();
}
}
Is it just that autowiring into interfaces is not allowed or there's something wrong with the code?
Using #Component on the interface doesn't make any difference.
I'd rather keep this design instead of using an abstract class.
Adding a Variable into interface is not possible in Java. It will be by default a public static final constant. So you have to do either the following:
MyValidationBean beanToBeAutowired = new MyValidationBeanImpl();
or the following:
MyValidationBean beanToBeAutowired();
default Boolean doSomeNewValidations(){
return beanToBeAutowired().doSomeNewValidations();
}
And you can override the beanToBeAutowired method in the implementation class.
i can think of solution as below -
public interface IValidator {
public Service getBeanToBeAutowired();
default Boolean doSomeNewValidations(){
return getBeanToBeAutowired().doSomeNewValidations();
}
}
public class ValidatorClass implements IValidator {
#Autowire private Service service;
#Override
public Service getBeanToBeAutowired() {
return service;
}
}
Just an idea, send validation bean to interface as parameter;
public interface IValidator {
default Boolean doSomeNewValidations(MyValidationBean beanToBeAutowired){
return beanToBeAutowired.doSomeNewValidations();
}
}
Your callerClass;
public class CallerClass implements IValidator{
#Autowired
MyValidationBean beanToBeAutowired;
...
doSomeNewValidations(beanToBeAutowired);
}

What is the best approach to get injected beans with same interface in factory using Spring?

I created one factory to decide what best implementation should be returned, based in some conditional check.
// Factory
#Component
public class StoreServiceFactory {
#Autowired
private List<StoreService> storeServices;
public StoreService getService(){
if(isActiveSale){
return storeServices.get("PublicStoreService")
}
return storeServices.get("PrivateStoreService")
}
}
//Service Implementations
#Service
#Qualifier("PublicStoreService")
public class PublicStoreService implements StoreService {
public getStoreBalanceScore(){
Do Stuff....
}
}
#Service
#Qualifier("PrivateStoreService")
public class PrivateStoreService implements StoreService {
public getStoreBalanceScore(){
Do Stuff....
}
}
// Controller
#Autowired
StoreServiceFactory storeServiceFactory;
#Override
public StoreData getStoreBalance(String storeId) {
StoreService storeService = storeServiceFactory.getService();
return simulationService.simulate(sellerId, simulation);
}
Is this approach good? If yes, how can i get my service from an elegant way?
I would like to use only annotations, without configurations.
You should use a map instead of a List and pass a string parameter to the getService method.
public class StoreServiceFactory {
#Autowired
private Map<String,StoreService> storeServices = new HashMap<>();
public StoreService getService(String serviceName){
if(some condition...){
// want to return specific implementation on storeServices map, but using #Qualifier os something else
storeServices.get(serviceName)
}
}
}
You can prepopulate the map with supported implementations. You can then get an appropriate service instance as follows :
// Controller
#Autowired
StoreServiceFactory storeServiceFactory;
#Override
public StoreData getStoreBalance(String storeId) {
StoreService storeService = storeServiceFactory.getService("private");//not sure but you could pass storeId as a parameter to getService
return simulationService.simulate(sellerId, simulation);
}
If you don't like using Strings, you can define an enum for the supported implementations and use that as the key for your map.
You don't need to create a list or map on your code. You can retrieve it directly from Spring context using GenericBeanFactoryAccessor. This has various method to retrieve a specific bean like based on name, annotation etc. You can take a look at javadoc here. This avoids unnecessary complexity.
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/beans/factory/generic/GenericBeanFactoryAccessor.html

Adding and implementing interfaces with Annotations and Spring AOP?

is it possible to implement an interface using Annotaitons/Spring AOP/Proxies? For example, I have the simplified interface Foo. The first and second method usually only return a value. These values could be annotated on class level like shown in class Bar. The third method will be dynamically implemented by class Bar and annotated with the #CallMethod annotation.
At the end Spring beans of class Bar should implement the interface Foo.
public interface Foo {
public URI getID();
public String getRequires();
public void call(Item item);
}
#FooAnnotation(id = "myID", requires = "text/plain")
public class Bar {
#CallMethod
public void myCallMethod(Item item)
}

injecting generics with roboguice

I'm trying to inject instances with generics and i'm getting the following error:
HasOne<ModelClass> cannot be used as a key; It is not fully specified.
I've read elsewhere that safest way to do this is to explicitly name the class to be used in the generic when using the injector to get an instance but i'd like to be a little cleaner. I'm trying to create Relationship objects between Models.
Here is my simplified Model class
public class Model {
#Inject
Injector injector;
public <ModelClass extends Model> HasOne<ModelClass> hasOne(Class<ModelClass> clazz) {
HasOne<ModelClass> hasOne = injector.getInstance(Key.get(new TypeLiteral<HasOne<ModelClass>>() {
}));
hasOne.init(clazz);
return hasOne;
}
}
My HasOne relationship
public class HasOne<T extends Model> {
Class clazz;
public void init(Class<T> clazz){
this.clazz = clazz;
}
#Inject
Injector injector;
public T get(){
return (T) injector.getInstance(clazz);
}
}
Test Model #1
public class TestModel extends Model {
public HasOne<ExampleModel> exampleModel(){
return hasOne(ExampleModel.class);
}
}
Test Model #2
public class ExampleModel extends Model {
}
I get the error when doing this
TestModel testModel = RoboGuice.getInjector(context).getInstance(TestModel.class);
HasOne<ExampleModel> relationship = testModel.exampleModel();
I'm trying to hide away the ugly relationship creation and keep it in the Model class
You cannot use new TypeLiteral<T>() { } if T is a type parameter, it has to be a fully-specified type. Luckily, since you have an instance of Class<ModelClass>, you can do this:
(Key<HasOne<ModelClass>>) Key.get(TypeLiteral.get(Types.newParameterizedType(HasOne.class, clazz)))
You'll get a warning on the cast but it is safe to suppress it.

Categories

Resources