Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicitly qualified autowiring in Spring

Tags:

java

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.

like image 432
Adamovskiy Avatar asked Aug 22 '16 08:08

Adamovskiy


People also ask

How to use @AutoWired and @qualifier in spring?

How to Use @Autowired and @Qualifier in Spring 1 Create Service Interface. Let’s create an EmployeeService interface with only one method. ... 2 Implement Service Interface. Lets now create two Service classes which will implement the above mentioned EmployeeService interface. 3 Use @Qualifier to Differentiate Beans in RestController. ...

What is autowiring in Spring Framework?

Autowiring feature of spring framework enables you to inject the object dependency implicitly. It internally uses setter or constructor injection. Autowiring can't be used to inject primitive and string values. It works with reference only.

Can spring resolve AutoWired entries by type?

Although it's useful, there are use cases for which this annotation alone isn't enough for Spring to understand which bean to inject. By default, Spring resolves autowired entries by type.

What is spring bean autowiring?

The Spring framework enables automatic dependency injection. In other words, by declaring all the bean dependencies in a Spring configuration file, Spring container can autowire relationships between collaborating beans. This is called Spring bean autowiring.


1 Answers

I want to wrap up your requirements so that it would be clear if I'm getting you right.

  1. You have a set of @Services that you don't want to modify.
  2. At this moment you have only one implementation of X type which is used by this services.
  3. The choice of X implementation to be used in services would be defined by XType enum, which in turn would be available from request.
  4. 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();};
}
like image 165
hahn Avatar answered Oct 18 '22 09:10

hahn