Our software have a specific behaviour that applies to all the functionalities, what change in most cases is the Entity used in operations, and for that we need specific implementation for @Repository layer.
So, we develop an "simple" architecture @RestController
-> @Service
-> @Repository
with some generics, to work for all the functionality. Like this:
@RestController
@RequestMapping("test")
// This is specific implementation
public class DiariasApi implements DefaultApiInterface<DiariasDTO, DiariasDTOFilter, Integer> {
@Autowired
private DefaultService<DiariasDTO, DiariasDTOFilter, Integer> defaultService;
@Override
@GetMapping("/page")
public Page<DiariasDTO> pageSearch(final DiariasDTOFilter filter) {
return this.defaultService.pageSearch(filter);
}
@Override
@GetMapping("/detail")
public DiariasDTO detail(@PathVariable("key") final Integer key) {
return this.defaultService.detail(key);
}
}
@Service
// This is generic implementation
public class DefaultService<D extends Serializable, F extends Serializable, C> {
@Autowired
// The Problem is here.
// Here I want the call to be the specific @Repository.
private DefaultRepositoryInterface<D, F, C> defaultRepository;
@Transactional(readOnly = true)
public Page<D> pageSearch(final F filter) {
return this.defaultRepository.pageSearch(filter);
}
@Transactional(readOnly = true)
public D detail(final C key) {
return this.defaultRepository.detail(key);
}
}
@Repository
// This is specific implementation
public class DiariasRepository implements DefaultRepositoryInterface<DiariasDTO, DiariasDTOFilter, Integer> {
@Override
public Page<DiariasDTO> pageSearch(final DiariasFiltro filtro) {
//some specific code;
}
@Override
public Optional<DiariasDTO> detail(final Integer key) {
//some specific code;
}
We want to implement only the @RestController
and the @Repository
for each functionality, and let the @Service
layer be only one generic bean that knows how to call the specific @Repository
. But when we do that and have more than one implementation we receive the following error message, which tells us the problem with @Autowired
:
Description:
Field defaultRepository in package.DefaultService required a single bean, but 2 were found:
- conveniosRepository: defined in file ...
- diariasRepository: defined in file ...
We want the @Service
layer to be unique, can we do that?
You need to use @Qualifier
which will bind the specific bean instance
@Service
// This is generic implementation
public class DefaultService<D extends Serializable, F extends Serializable, C> {
@Autowired
// The Problem is here.
// Here I want the call to be the specific @Repository.
@Qualifier("YourBeanId")
private DefaultRepositoryInterface<D, F, C> defaultRepository;
EDIT
So you can use another approach which I had used in past for local v/s testing environment
The factory like class that will return the specific instance of the bean at runtime
Create a Factory class
@Component
public class RepositoryFactoryImpl implements RepositoryFactory{
@Autowired
private DefaultRepositoryInterface conveniosRepository;
@Autowired
private DefaultRepositoryInterface diariasRepository;
@Override
public DefaultRepositoryInterface getInstance() {
if (some condition) {
return conveniosRepository;
}
if (some condition) {
return diariasRepository;
}
return null;
}
}
Then in your DefaultService
@Service
// This is generic implementation
public class DefaultService<D extends Serializable, F extends Serializable, C> {
@Autowired
private RepositoryFactory factory;
@Transactional(readOnly = true)
public Page<D> pageSearch(final F filter) {
return this.factory.getInstance().pageSearch(filter);
}
@Transactional(readOnly = true)
public D detail(final C key) {
return this.factory.getInstance().detail(key);
}
}
I think your approach is perfectly fine, I have a similar one. Your problem --I think is, that you want a generic service to access a specific repository. Make a DiariasService
that extends Service<DiariasDto, DiariasFilter, Integer>
and let Spring autowire in a DiariasRepository
that extends Repository<DiariasDto, DiariasFilter, Integer>
in the constuctor and pass it to the abstract service.
You have a new but pretty much empty Service, but correctly resolved the ambiguous dependencies.
My java isn't the freshest, so in kotlin this looks like:
abstract class Service<R : Resource, M : Model>(
protected open val factory: Factory<R, M>,
//...
)
class FooService(
factory: FooFactory, //spring bean type magic happens here and @Qualifier is applicable!
//...
) : Service<FooResource, FooModel>(
factory, //from one constuctor to the superclass constructor
//...
)
abstract class Factory<R : Resource, M : Model>(
//...
)
class FooFactory(
//...
) : Factory<FooResource, FooModel>(
//...
)
I use the same pattern for Controller
/ FooController
and Repository
/ FooRepository
and so on.
Of course there are abstract Model
and Resource
/ Entity
.
You cannot use @Qualifier
in the abstract Service
/ Controller
/ Repository
/ Factory
, but in the concrete classes!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.