How can I select Spring bean instance at runtime - java

Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.
E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.
Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?

We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:
#Component
public class ImTheClient{
#Autowired
private ImTheFactory factory;
public void doSomething(
Parameters parameters) throws Exception{
IWantThis theInstance = factory.getInstance(parameters);
}
}
So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:
#Component
public class ImTheFactoryImpl implements
ImTheFactory {
#Autowired
private IWantThisBadly anInstance;
#Autowired
private IAlsoWantThis anotherInstance;
#Override
public IWantThis getInstance(Parameters parameters) {
if (parameters.equals(Parameters.THIS)) {
return anInstance;
}
if (parameters.equals(Parameters.THAT)) {
return anotherInstance;
}
return null;
}
}
So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.

Seems like do you want a ServiceLocator using the application context as registry.
See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.
Other option is to use a naming convention or annotation based configuration.
for example, assuming that you annotate Services with #ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.
public class AnnotationServiceLocator implements ServiceLocator {
#Autowired
private ApplicationContext context;
private Map<String, Service> services;
public Service getService(String id) {
checkServices();
return services.get(id);
}
private void checkServices() {
if (services == null) {
services = new HashMap<String, Service>();
Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
for (Object bean : beans.values()) {
ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
services.put(ann.value(), (Service) bean);
}
}
}
}

Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.

If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.
public class BusinessLogic {
private BaseClassOfBeanAandB bean;
public void methodCalledByUserAorB() {
bean.doFoo();
}
}

Related

How can I create multiple Spring beans in a #Bean-annotated method or anything similar?

In a Spring application that uses HTTP remoting, I have a service façade module configured as follows (I made the code generic to improve clarity):
#Configuration
public class MyFacadeConfig {
private HttpInvokerServiceExporter facade(Class<?> cls) {
HttpInvokerServiceExporter bean = new HttpInvokerServiceExporter();
// The service referred to by this exporter is already instantiated as another Spring bean with all its dependencies.
bean.setService(appContext.getBean(cls));
bean.setServiceInterface(cls);
return bean;
}
#Bean("/first.service")
public HttpInvokerServiceExporter firstServiceFacade() {
return facade(FirstService.class);
}
#Bean("/second.service")
public HttpInvokerServiceExporter secondServiceFacade() {
return facade(SecondService.class);
}
// ... and so on for the 37 other services
}
where FirstService and SecondService are interfaces with existing implementations whose detail is not needed here.
I have another module that defines 39 proxies (instances of HttpInvokerProxyFactoryBean) corresponding to each of my services exposed through my façade.
So far, everything works properly.
But I would like to make the code more generic, elegant, and robust while mitigating the risk of error (e.g., a bad mapping between a service and its proxy in the future). The way I would like to do this is as follows:
First, I move the façade/proxy metadata into an enumeration:
public enum ConfigBeansFacade {
FIRST("/first", FirstService.class),
SECOND("/second", SecondService.class)
// ... and so on for the 37 other services
;
private String beanName;
private Class<?> serviceInterface;
// Constructor and getters
public String getCompleteBeanName() {
return beanName + ".service";
}
}
Then the configuration of the façade would be simplified in a style similar to the following:
#Configuration
public class MyFacadeConfig {
#Autowired
private ConfigurableBeanFactory beanFactory;
#Autowired
public void configExporters() {
for (ConfigBeansFacade bean : ConfigBeansFacade.values()) {
HttpInvokerServiceExporter exp = new HttpInvokerServiceExporter();
exp.setService(beanFactory.getBean(bean.getServiceInterface()));
exp.setServiceInterface(bean.getServiceInterface());
beanFactory.registerSingleton(bean.getCompleteBeanName(), exp);
}
}
}
I tried every single recipe I found in online forums, including StackOverflow, but there are two constraints not met elsewhere:
When defining the exporters, the underlying services are other Spring beans that are instantiated, initialized, and registered with their own configuration and dependencies through the standard Spring mechanics. There is no direct class instantiation other than the exporters themselves.
I thought about grouping the exporters into a single collection as suggested by some people. The only problem is that Spring MVC uses the HttpInvokerServiceExporter Spring bean names as endpoint URIs when registering the exporters into its own configuration. I must therefore register each exporter as a “first-class citizen” bean with its own bean name into the application context.
Given these constraints, the problem I have arises in (1) when I try to retrieve the underlying services to be encapsulated into exporters: they are not necessarily ready yet, which results into UnsatisfiedDependencyExceptions.
I tried solutions with a #PostContruct-annotated method, with a BeanPostProcessor, with an #Autowired method (as shown above), nothing is working as required.
Does anyone know about a way or a technique to initialize and register multiple beans inside a single method under my constraints described above? Such a method doesn't need to be annotated with #Bean, #Autowired, or any other specific annotation, it's just an example of what I tried.
In the client module, mercifully, the HttpInvokerProxyFactoryBean instances need only the interfaces and the bean names, so constraint (1) above should not apply.
Thanks in advance for any help you can provide...
I'm not 100% I've understood what you're trying to do but I wonder if you could try autowiring a List of beans that implement an interface?
e.g.
public interface MyService {
String getKey();
void doStuff();
}
Then implement as many of these as you require
e.g.
#Component
public class FirstService implements MyService {
public String getKey() {
return "/first";
}
public void doStuff() {
...
}
}
then have a factory bean with the autowired list
#Component
public class MyServiceFactory {
private final List<MyService> services;
#Autowired
public MyServiceFactory(List<MyService> services) {
this.services = services;
}
}
To add more implementations of MyService, simply add them as #Component and Spring magically picks them up and adds them to the list.
Sometimes I find it useful to access my implementations via a Map
#Component
public class MyServiceFactory {
private final Map<String, MyService> services;
#Autowired
public MyServiceFactory(List<MyService> services) {
this.services = services
.stream()
.collect(toMap(MyService::getKey, Function.identity()));
}
public MyService getServiceByKey(String key) {
return services.get(key);
}
}
I find this keeps each implementation nice and self contained (and easy to test). Spring automatically picks up all the components that implement my interface without the factory having a huge number of imports. And I can test the factory easily by mocking the list of implementations.

How to dynamically call a method on a qualified service?

I have an interface as following:
public interface Solver {
void execute(final String definition);
}
I have several solver implementations. For example:
#Qualifier("naive")
#Service
public class NaiveSolver implements Solver {
#Override
public void execute(final String definition) {
}
}
#Qualifier("optimal")
#Service
public class OptimalSolver implements Solver {
#Override
public void execute(final String definition) {
}
}
Now, in my database I have the data for these solvers as following:
type: textual - examples: simple, naive
definition: textual
When fetching this data, I need to map the type column to the actual service instance, so it can solve the provided definition.
So, my question boils down to this: how can I, given a type string, get the instance of the corresponding Solver service instance of which the qualifier is equal to that type? I believe that #Autowired cannot be used here, as I need to find beans dynamically at runtime.
Since Spring 4 you can autowire multiple implemetations into a Map where bean qualifier is a key, and the bean itself is a value
#Autowired
private Map<String, Solver> solvers;
void doStuff() {
String type = ... // obtain type
Solver solver = solvers.get(type);
solver.execute(...)
}
Update
Correct way of naming a bean is not
#Qualifier("naive")
#Service
but
#Service("naive")
#Qualifier is used along with #Autowired to ensure the correct bean is injected
#Autowired
#Qualifier("naive")
private Solver naiveSolver;
You can create configuration which will just hold mapping to your solvers:
#Autowired
#Qualifier("optimal")
private Solver naiveSolver;
#Bean
public Map<String, Solver> mapSolver() {
Map<String, Solver> map = new HashMap<>();
map.put("naive", naiveSolver);
return map;
}
Or even going further you can follow factory pattern which will provide you different instances of solvers.
Another way you can dynamically get those beans from application context.
You could merge the following solutions:
How to inject dependencies into a self-instantiated object in Spring?
Creating an instance from String in Java
private #Autowired AutowireCapableBeanFactory beanFactory;
public void doStuff() {
Class c= Class.forName(className);
MyBean obj = c.newInstance();
beanFactory.autowireBean(obj);
// obj will now have its dependencies autowired.
}

Stateless Factory with EJB

I have a requirement to get pdf documents from my system. I'm using Apache Fop for this - and this library is using 2 files to generate pdf - xsl file with structure and styling and xml with data. So I'm getting xsl file from web resources, but now I need to generate xml with data from database. I tried this solution:
I have this interface:
public interface PrintableDocument {
Object getJaxBOjbect(Long personId);
}
That's one of the stateless bean to get object, I need 10 more beans like this to get different data for different documents.
#Stateless
#PrintableDocumentOneQualifier
public class PrintableDocumentOne implements PrintableDocument {
#Inject
private SomeRepository repository;
public Object getJaxBOjbect(Long personId) {
// Getting information from database
// formulating Object with data and returning it
}
}
So now I want to create Factory like this one:
#Stateless
#LocalBean
public class PrintableDocumentsFactory {
#Inject
#PrintableDocumentOneQualifier
private PrintableDocument printableDocumentOne;
#Inject
#PrintableDocumentTwoQualifier
private PrintableDocument printableDocumentTwo;
private Map<String, PrintableDocument> map = new HashMap<>();
#PostConstruct
public void init() {
map.put("one", printableDocumentOne);
map.put("two", printableDocumentTwo);
}
public PrintableDocument getPrintableDocument(String type) {
return map.get(type);
}
}
And on the service bean I want to use this factory:
#Stateless
#Local(DocumentService.class)
public class DocumentServiceBean {
#Inject
private PrintableDocumentsFactory factory;
public byte[] getPdf(InputStream xsl, Long id, String type) {
PrintableDocument printableDocument =
factory.getPrintableDocument(type);
Object jaxBOject = printableDocument.getJaxBObject(id);
//Use this object to get pdf and return it to web controller.
}
}
But now I'm getting null from getPrintableDocument from factory. I think the problem is that I need stateless beans, and they are getting picked back to EJB container, when getPrintableDocument method ends. So my question is: how can I manage this kind of situation?
EDIT 1: Missed PostConstruct annotation on init in Factory. Fixed that, still have the problem.
EDIT 2: If I will have #Singleton on my Factory will it hold just one by one instances of stateless PrintableDocument beans or it will return pooled instances instead? Because now I have to refill strategy holder map on factory when system will need another been to answer the request.
You could try to use #EJB instead of #Inject to inject the PrintableDocumentsFactory into your DocumentServiceBean.
Try adding a #PostConstruct annotation to PrintableDocumentsFactory.init() method. Currently the init method won't be called, so no get registered in the map.

Is there a way to re-inject / update injected bean fields?

I have some beans (of multiple types, CDI, #Stateless and #Singleton beans). Some of their fields shall get injected from database values.
public class MyBean {
#Inject
#DbConfigValue(MyConfig.HOST)
String host;
}
So I added a custom #Qualifier (DbConfigValue) used by a Producer. The producer reads and caches config values from a database and injects them into the beans.
#Singleton
#Lock(LockType.READ)
public class Configuration {
#Produces
#Dependent
#DbConfigValue
public String getDbConfigValue(InjectionPoint point) {
// get key for the config-value from qualifier-annotation of the injected field
String key = point.getAnnotated().getAnnotation(DbConfigValue.class).value();
// i have read+cached database config values in #PostConstruct before
return cachedConfigValues.get(key);
}
}
This works well for initial injection / bean construction. Some web tutorials out there are suggesting this approach.
Now, I think it is reasonable to assume that config values, if stored in DB, might change at runtime. So, whenever an admin changes a database config value, I currently do fire a CDI-event.
Question: is there any way to re-inject values into fields of already-initialized bean-instances? Or is injection always related to instance-creation only?
E.g. I had s.th. similar to this in mind:
public class MyEventListener {
#Inject
BeanManager beanManager;
#Asynchronous
public void onDbConfigValueChangedEvent (#Observes(during = TransactionPhase.AFTER_SUCCESS) DbConfigValueChangedEvent event) {
try {
// could be filtered by custom qualifier:
Set<Bean<?>> beans = beanManager.getBeans(Object.class,new AnnotationLiteral<Any>() {});
for (Bean<?> bean : beans) {
Set<InjectionPoint> points = bean.getInjectionPoints();
// What now? javax.enterprise.inject.spi.Bean is the
// bean-representation only.
// Can I somehow resolve the actual bean-instances here?
// Then update Field via Reflection?
}
}
catch(Exception e){
// ...
}
}
}
I also considered DeltaSpike which has some methods for injection-control. However, I did only find methods to inject into new bean instances, or even with new- or null-CreationalContexts (beans not CDI-managed afterwards)
Please note: I am aware that I can solve this specific use-case by injecting the configuration and explicitly getting the current values on each request like this:
public class MyBean {
#Inject
Configuration config;
public void someMethod(){
String host = config.getConfig(MyConfig.HOST);
// ...
}
}
However, I am wondering about the question in general: is there any support for re-injection? Or if not, do the specs (CDI or Java EE) forbid it?
Depending on how fast/slow your db is, this may be expensive. You could probably leverage some cacheing mechanism in the producer method.
Leverage on Instance injection mechanims, which lazily loads the actual injected bean.
Your Producer (Probably leveraging on some of cache to avoid db calls all the tome)
#Singleton
#Lock(LockType.READ)
public class Configuration {
#Produces
#RequestScoped //May fail if not in a request-context, but for ejb-calls, it is guaranteed to work as CDI has EJB Request Context
#DbConfigValue
public String getDbConfigValue(InjectionPoint point) {
// get key for the config-value from qualifier-annotation of the injected field
String key = point.getAnnotated().getAnnotation(DbConfigValue.class).value();
// i have read+cached database config values in #PostConstruct before
return cachedConfigValues.get(key);
}
}
And the injection points:
#SessionScoped
public class MyBean {
#Inject
#DbConfigValue(MyConfig.HOST)
private Instance<String> host;
public void doSomething() {
String myHost = host.get(); // of course will throw exception if value is failing. It will be resolved with every request.
}
}

Guice FactoryModuleBuilder an instance with constructor parameters

I´m using Guice to initalize a class with some arguments from a config file
#Provides
#Singleton
RetryServiceCaller provideMaxRetryAttempts(#Named("config") JsonObject config) throws IOException {
JsonObject retryDetails = config.getJsonObject("retry_details");
return new RetryServiceCaller(retryDetails.getInteger("maxRetryAttempts"), retryDetails.getInteger("upperBoundary"), retryDetails.getInteger("lowerBoundary"),
retryDetails.getLong("multiplicationFactor"), retryDetails.getInteger("timeout"), retryDetails.getInteger("increaseTimeout"));
}
This class is injected in another class which is singleton as well.
class A{
#Inject private RetryServiceCaller retryServiceCaller;
}
But now the problem is that since this new class A is singleton, I need to clone the retryServiceCaller every time that somebody use this class A.
I´ve been investigating FactoryModuleBuilder to use it and create a factory for this class. But since the class has parameters from the config file I could not find the way to make it works.
Something like this
class A{
#Inject private RetryServiceCaller.Factory retryServiceCallerFactory;
}
Then in my RetryServiceCaller implement this
public interface Factory {
#Inject
RetryServiceCaller create();
}
#Inject
public RetryServiceCaller(int maxRetryAttempts, int upperBoundary, int lowerBoundary, long multiplicationFactor, int timeout, int incrementTimeout) {
this.maxRetryAttempts = maxRetryAttempts;
this.upperBoundary = upperBoundary;
this.lowerBoundary = lowerBoundary;
this.multiplicationFactor = multiplicationFactor;
this.timeout = timeout;
this.incrementTimeout = incrementTimeout;
}
But guice throw me errors saying
No implementation for com.proxy.handlers.RetryServiceCaller$Factory was bound
Guice can automatically provide a zero-argument factory: Instead of injecting Foo, you can always inject Provider<Foo>. This allows you to call fooProvider.get() to create an instance whenever and wherever you'd like. You don't have to bind to a Provider or use a Provides method to get access to this; you can inject Foo or Provider<Foo> whether you use a bind(...).to(...) type binding, a toProvider binding, a toInstance binding, a #Provides method, or anything else, and Guice will call get or return an internal Provider automatically.
(The returned Provider will also respect scopes, so you'll need to drop your #Singleton scope in order to get more than one instance, and be aware that toInstance bindings will always return the same instance.)
This is not a job for FactoryModuleBuilder; only use FactoryModuleBuilder when you need to mix injected and non-injected constructor parameters in the same type.
Your finished binding should look like this:
#Provides
/* NOT #Singleton */
RetryServiceCaller provideMaxRetryAttempts(#Named("config") JsonObject config) throws IOException {
JsonObject retryDetails = config.getJsonObject("retry_details");
return new RetryServiceCaller(retryDetails.getInteger("maxRetryAttempts"), retryDetails.getInteger("upperBoundary"), retryDetails.getInteger("lowerBoundary"),
retryDetails.getLong("multiplicationFactor"), retryDetails.getInteger("timeout"), retryDetails.getInteger("increaseTimeout"));
}
And in your class:
#Inject public YourCallerConsumer(Provider<RetryServiceCaller> callerProvider) {
this.callerProvider = callerProvider;
}
public void doAction() {
RetryServiceCaller newCaller = callerProvider.get();
// interact with caller
}
Your first approach should work just fine. If you don't want the RetryServiceCaller to be a singleton, remove the #Singleton annotation from the provider method, and a new instance will be created for every injection point.
Assisted inject could work here too, but it's overkill. If you want to go that route:
interface RetryServiceCallerFactory {
RetryServiceCaller create(String configParam1, String configParam2);
}
public class RetryServiceCaller {
#AssistedInject
public RetryServiceCaller(String configParam1, String configParam2) {}
}
then, in your module
install(new FactoryModuleBuilder().build(Factory.class);
and in your injection points
#Inject RetryServiceCallerFactory factory;
RetryServiceCaller create(JsonObject config) {
return factory.create(config.getFirstParam(), config.getSecondParam());
}
You can refer to the documentation for more extensive examples.

Categories

Resources