Spring Qualifier, service implementation when application starts - java

(spring version is 4.1.6)
I have a service Interface "ContractService" that retreives contracts of a person.
2 service classes ContratServiceImpl & ContratServiceImplWeb implementing this interface.
ContratServiceImpl calls dao to retreive data
ContratServiceImplWeb calls web service...
The Interface ContractService is used by another service (UTService):
#org.springframework.stereotype.Service
#Transactional(value="transactionManager")
public class UTServiceImpl implements UTService {
#Autowired #Qualifier(...<variable.propeties>...)
private ContractService contractService;
...
}
Here are services implementing the interface that can be in
#Service("ContratServiceImplWeb") #Transactional(value="transactionManager")
class ContratServiceImplWeb implements ContratService {
...
}
#Service("ContratServiceImpl") #Transactional(value="transactionManager")
class ContratServiceImpl implements ContratService {
...
}
Configuration class
#Configuration
#EnableTransactionManagement
#ImportResource(value={ "classpath:sessionFactory-datasource-spring.xml", "classpath:contrat_factories.xml"} )
#ComponentScan(basePackages = { "com.xxx.core.service.impl"
, "com.xxx.core.dao.impl"}
)
public class ContextCoreServiceConfiguration {
}
When my application starts I would like to "qualify" ContractService with ContratServiceImpl or ContratServiceImplWeb depending an external configuration file (.properties or XML) where bean names are set.
How can I do this ?
----- Last comments -------
contractServiceImpl & contractServiceImplWeb are set as #service and scanned by configuration.
In "UTServiceImple" class, 1 of the 2 classes need to be #autowired with a discriminating parameter (may Profile ?). This is not the only group of classes that is concerned.
Profile should not be the same from a couple to another.
So, I have to set a profile for each group of classes ?

I guess Spring profiles is exactly what you are looking for.
They let you choose a specific bean implementation dynamically based on a parameter (active profile).
For detailed information, please have a look here if you are using boot or here if using raw spring.

You are close. You want your ContractService declaration to look more like this:
#Autowired
private #Qualifier("contractServiceImpl") ContractService contractServive;
This of course presumes that your ContractServiceImpl bean was correctly configured and created, either manually or using the #Service or #Component annotation on your class.

I post the solution. I used #Profiles. It really feeds my needs.
Thanks for having given me this information.
#org.springframework.stereotype.Service
#Transactional(value="transactionManager")
public class UTServiceImpl implements UTService {
private static final Logger logger = Logger.getLogger(UTServiceImpl.class);
// SERVICES
#Autowired
private VerificationHabilitationService verificationHabilitationService;
#Autowired
private ContratService contratService;
...
}
Interface referenced in the main service
public interface ContratService {
Set rechercheIdentifiantAdherent(String numeroImmatriculation);
...
}
Database Service
#Service("ContratServiceDb") #Transactional(value="transactionManager") #Profile("contratServiceDb")
class ContratServiceImplDb implements ContratService {
private Logger logger = null;
#Autowired
private ContratFactory contratFactory;
...
}
Web Service
#Service("ContratServiceWeb") #Transactional(value="transactionManager") #Profile("contratServiceWeb")
class ContratServiceImplWeb implements ContratService {
private Logger logger = Logger.getLogger(ContratServiceImplWeb.class);
#Autowired
private ContratFactory contratFactory;
...
}
Configuration
#Configuration
#EnableTransactionManagement
#ImportResource(value={ "classpath:sessionFactory-datasource-spring.xml", "classpath:contrat_factories.xml"} )
#ComponentScan(basePackages = { "com.xxx.core.service.impl"
, "com.xxx.core.dao.impl"}
)
public class ContextCoreServiceConfiguration {
}
For each implementation classe I define a Pofile : contratServiceDb, contratServiceWeb. When context is initialized I set active profile (using external properties file). Then, services matching profiles are autowired. See below :
String configCoreClass = servlet.getInitParameter("contextCoreConfigurationClass");
String[] profiles = { PropertiesHelper.getProperty(PropertiesHelper.PROPERTY_SERVICE_CONTRAT)
,PropertiesHelper.getProperty(PropertiesHelper.PROPERTY_SERVICE_ADHERENT)
};
AnnotationContextCoreLocator.getInstance().getEnvironment().setActiveProfiles(profiles);
AnnotationContextCoreLocator.getInstance().register( Class.forName(configCoreClass) );
AnnotationContextCoreLocator.getInstance().refresh();

Related

Spring: Implement two instances of a #Service with two instances of #Configuration linked to each respective service

An existing utility exists that I am importing to my project. It is written similarly as these two classes
#Service
public class ServiceAccessorImpl implements ServiceAccessor {
#Autowired private ServiceConfiguration serviceConfiguration;
public Response executeCall(){
return callEndPoint(serviceConfiguration.getServiceEndPoint());
}
}
#Configuration
#Data //automatically adds getters and setters
#ConfigurationProperties(prefix="config")//pulls serviceEndPoint value from external config
//Assume external config has config.serviceEndPoint = "www.endpoint1.com"
public class ServiceConfiguration {
private String serviceEndPoint;
}
In a separate project below I am importing the above into my project. I would like to have two instances of the same service with two unique and respective configuration classes. so that service1 is linked to config1 and service2 is linked to config2. My reasoning is I want one instance that only pulls the endpoint from the external configuration and another instance that I can use to set the endpoint. I have tried using things like #Qualifier but I cant figure out how to link the correct config with the correct service. I have a feeling that this may not be possible because ServiceConfiguration is privately scoped within ServiceAccessorImpl and I have no access through setters or constructors.
Controller Endpoint. The below is psuedo code of how I would like to implement my design. Autowiring in a single instance and using either endpoint works for that endpoint but not for both as shown below.
#ComponentScan(basePackageClass = ServiceAccessorImpl.class)
public class ServiceAccessorController {
#Autowired private ServiceAccessor serviceAccessor1;
#Autowired private ServiceConfiguration serviceConfiguration1;
#Autowired private ServiceAccessor serviceAccessor2;
#Autowired private ServiceConfiguration serviceConfiguration2;
Response CallEndpoint1(){
//www.endpoint1.com is already set here from external config
return serviceAccessor1.executeCall();
}
Response CallEndpoint1(){
serviceConfiguration2.setServiceEndPoint("www.endpoint2.com")
return serviceAccessor2.executeCall();
}
}
Thank you in advance
If you need multiple instances of the same implementation, it's easier to not annotate it as a bean, and have a #Configuration provide the beans instead.
public class ServiceAccessorImpl implements ServiceAccessor {
private ServiceConfiguration serviceConfiguration;
public ServiceAccessorImpl(ServiceConfiguration configuration) {
this.serviceConfiguration = configuration;
}
public Response executeCall(){
return callEndPoint(serviceConfiguration.getServiceEndPoint());
}
}
// this one should just have #ConfigurationProperties, not #Configuration
#Data
#ConfigurationProperties(prefix="config")
public class ServiceConfiguration {
private String serviceEndPoint;
}
then in your service you can have a Configuration providing both instances:
#Configuration
public class BeansConfiguration {
#Qualifier("service1")
#Primary // optional, Spring will autowire this instance by default if no qualifier is given
#Bean
public service1(#Autowired ServiceConfiguration config) {
// use the default config bean
return new ServiceAccessorImpl(config);
}
#Qualifier("service2")
#Bean
public service2() {
return new ServiceAccessorImpl(new ServiceConfiguration("www.endpoint2.com"));
}
}
then you may consume both by using the qualifiers (note that you don't have to inject the configs here):
public class ServiceAccessorController {
#Autowired
private ServiceAccessor serviceAccessor1;
#Autowired
#Qualifier("service2")
private ServiceAccessor serviceAccessor2;
Response CallEndpoint1(){
return serviceAccessor1.executeCall();
}
Response CallEndpoint2(){
return serviceAccessor2.executeCall();
}
}

Dynamically inject service implementation with Spring

I've already gone through some tips on how to do this, like this one: stack overflow; but my problem is that my own implementation needs other things to be injected in it. Here's the example:
public interface MyService {}
public class ServiceImplA implements MyService {
#Autowired
private final SomeStuffA a_stuff;
}
public class ServiceImplB implements MyService {
#Autowired
private final SomeStuffB b_stuff;
}
#Configuration
public class SpringConfig {
#Bean
#Scope("singleton")
public MyService getService() {
boolean useA = // read config file and decide impl
return useA ? new ServiceImplA() : new ServiceImplB();
// I can't instantiate this, so i need them to be injected as well
}
}
I'm familiar with Google Guice, where I would do something like this:
bind(MyServicle.class).to(useA ? ServiceImplA.class : ServiceImplB.class);
So, I need a way to do this using Spring
I think your problem is that your base class MyService is not marked for any profile.
When you define profile the beans that have such specification will override beans that implement the same interface and do not have profile definition. This does not work this way.
When working with active profile X spring starts all beans that are not targeted for any profile and beans targeted for current profile. In your case you can choose wisely.
I think that if you want to use profiles you should define at least 2: A and B (the names are taken just for example.)
Now mark ServiceImplA as A and ServiceImplB as B:
#Service
#Profile("A")
public ServiceImplA interface MyService { ... }
and some development implementation
#Service
#Profile("B")
public ServiceImplB interface MyService { ... }
More about profiling you will get here

Spring specify implementation in configuration

I'm writing application using spring mvc/boot, and I have two storage implementations: database storage and in memory storage. My global idea is choose in configuration file what storage application should use.
My idea is
put #Qualifier annotation on each storage implementation
create two configurations, like databaseStorageConfiguration and InMemoryStorageConfiguration
depends on profile, apply first or second configuration
The thing is I don't know how to bind implementation and configuration.
I tried something like this:
#Configuration
public class InMemoryStorageConfig {
#Autowired
#Qualifier("inMemoryStorage")
private Storage storage;
#Bean
public Storage getStorage() {
return storage;
}
}
But I get an error, that 3 beans were found: 2 beans with dfferent implementation and the 3rd one - in config
UPDATE 1
I've added #Profile("InMemory") to Configuration and activated that profile in properties. That gave no changes but looks more logical now
UPDATE 2
Full configuration:
#SpringBootApplication
#ImportResource("classpath:spring-config.xml")
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
}
#Service
public class WidgetService {
private WidgetCache widgetCache;
#Autowired
public WidgetService(WidgetCache widgetCache) {
this.widgetCache = widgetCache;
}
....
#Qualifier("databaseWidgetCache")
#Transactional
#Repository
public class DatabaseWidgetCache implements WidgetCache {
private WidgetRepository widgetRepository;
#Autowired
public DatabaseWidgetCache(WidgetRepository widgetRepository) {
this.widgetRepository = widgetRepository;
}
#Qualifier("inMemoryWidgetCache")
#Repository
public class InMemoryWidgetCache implements WidgetCache {
private WidgetLayersStorage widgetLayersStorage;
#Autowired
public InMemoryWidgetCache(WidgetLayersStorage widgetLayersStorage) {
this.widgetLayersStorage = widgetLayersStorage;
}
#Profile("InMemory")
#Configuration
public class InMemoryStorageConfig {
#Autowired
#Qualifier("inMemoryWidgetCache")
private WidgetCache widgetCache;
#Bean
public WidgetCache getWidgetCache() {
return widgetCache;
}
}
Stacktrace:
Parameter 0 of constructor in
com.widgets.service.widget.WidgetService required a single
bean, but 3 were found:
- inMemoryWidgetCache: defined in file [..../MemoryWidgetCache.class]
- databaseWidgetCache: defined in file [..../DatabaseWidgetCache.class]
- getWidgetCache: defined by method 'getWidgetCache' in class path resource
[......../InMemoryStorageConfig.class]
Action:
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
Your WidgetService should be changed to
#Service
public class WidgetService {
private WidgetCache widgetCache;
/** or
private List<WidgetCache> widgetCaches;
public WidgetService(List<WidgetCache> widgetCaches) {
this.widgetCaches = widgetCaches;
}
*/
public WidgetService(#Qualifier(<desired impl>) WidgetCache widgetCache) {
this.widgetCache = widgetCache;
}
}
and need to annotate your InMemoryWidgetCache and DatabaseWidgetCache with #Qualifier annotation. since you are using default convention.
and please remove
#Bean
public WidgetCache getWidgetCache() {
return widgetCache;
}
i don't see a real use there
In order to specify implementation in Configuration class, you don't need "Qualifier" annotation, and configuration should be changed to:
#Profile("inMemoryStorage")
#Import(InMemoryWidgetCache.class)
#Configuration
public class InMemoryStorageConfig {
}
thus, by activating profile, you choose the desire implementation

Single API, multiple Elasticsearch instances

We have a Spring Boot Restful API that needs to get data from 2 different Elasticsearch instances (on different servers), 1 for "shared" data (with about 5 different indexes on it) and 1 for "private" data (with about 3 different indexes). Currently running against just the "private" data instance, everything is good. But we now need to get at the "shared" data now.
In our Spring Boot application, we have enabled Elasticsearch repositories like this
#SpringBootApplication
#EnableElasticsearchRepositories(basePackages = {
"com.company.core.repositories", //<- private repos here...
"com.company.api.repositories" //<-- shared repos here...
})
public class Application { //... }
Then we access the "private" data with an ElasticsearchRepository like:
package com.company.core.repositories
public interface DocRepository extends ElasticsearchRepository<Doc, Integer> { ... }
In our endpoint, we have...
#RestController
#CrossOrigin
#RequestMapping("/v2/statuses/")
public class StatusEndpoint {
#Resource
private ElasticsearchTemplate template;
#Autowired
private DocRepository docRepository;
#Autowired
private Validator validator;
//...
}
Now we want to add another repository like:
package com.company.api.repositories
public interface LookupRepository extends ElasticsearchRepository<Lookup, Integer> { ... }
Then in our API layer we would add an auto-wired instance...
#Autowired
private LookupRepository lookupRepo;
We were thinking that we could define multiple Beans with different names, but how do we associate each of the "elasticsearchTemplate" beans with the different ElasticsearchRepository instances that need them? Also, how do we associate the "private" bean/configuration with injected instances of
#Resource
private ElasticsearchTemplate template;
Where we need to use that natively?
You can solve this with 2 unique Elasticsearch configuration beans and an #Resource(name="XXX") annotation for the template injection in your StatusEndpoint controller.
If you segregate your repositories into different packages depending on which Elasticsearch cluster they should use, you can associate them with different configurations using the #EnableElasticsearchRepositories annotation.
For example:
If you have these packages and classes:
com.company.data.repositories.private.YourPrivateRepository
com.company.data.repositories.shared.YourSharedRepository
And then these Configurations:
#Configuration
#EnableElasticsearchRepositories(
basePackages = {"com.company.data.repositories.private"},
elasticsearchTemplateRef = "privateElasticsearchTemplate")
public class PrivateElasticsearchConfiguration {
#Bean(name="privateElasticsearchTemplate")
public ElasticsearchTemplate privateTemplate() {
//code to create connection to private ES cluster
}
}
#Configuration
#EnableElasticsearchRepositories(
basePackages = {"com.company.data.repositories.shared"},
elasticsearchTemplateRef = "sharedElasticsearchTemplate")
public class SharedElasticsearchConfiguration {
#Bean(name="sharedElasticsearchTemplate")
public ElasticsearchTemplate sharedTemplate() {
//code to create connection to shared ES cluster
}
}
Because of the elasticsearchTemplateRef parameter in the #EnableElasticsearchRepositories annotation, the JPA code that implements the repositories will use the specified template for repositories in the basePackages list.
For the StatusEndpoint portion, you would just provide your #Resource annotation with the correct template bean name. Your StatusEndpoint would look like this:
#RestController
#CrossOrigin
#RequestMapping("/v2/statuses/")
public class StatusEndpoint {
#Resource(name="privateElasticsearchTemplate")
private ElasticsearchTemplate template;
#Autowired
private DocRepository docRepository;
#Autowired
private Validator validator;
//...
}
There might be multiple ways to do this. Here is one that takes advantage of the #Bean name and the #Resource name.
#Configuration
public class MyElasticConfig{
#Bean //this is your private template
public ElasticsearchTemplate template(){
//construct your template
return template;
}
#Bean //this is your public template
public ElasticsearchTemplate publicTemplate(){
//construct your template
return template;
}
}
then you can get them like this...
#Resource
private ElasticsearchTemplate template;
#Resource
private ElasticsearchTemplate publicTemplate;
or
#Resource(name="template")
private ElasticsearchTemplate anyName;
#Resource(name="publicTemplate")
private ElasticsearchTemplate anyOtherName;
also you can name your #Bean's directly instead of relying on the #Bean's method name.
#Bean (name="template")
public ElasticsearchTemplate myPrivateTemplate(){
//construct your template
return template;
}
#Bean (name="publicTemplate")
public ElasticsearchTemplate myPubTemplate(){
//construct your template
return template;
}
Check out these wonderful resources on the topic.
SPRING INJECTION WITH #RESOURCE, #AUTOWIRED AND #INJECT
Bean Annotation Type
Autowired vs Resource

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