Spring conditional Injection at run time - java

I was looking at the spring annootation #Conditional to make a runtime conditional wiring of my dependency. I have a service that takes a value in the constructor. I want to create 2 instances of the service with different constructor inputs, then based on a condition at run time, I want to use this bean or that bean. Looks like #Conditional is evaluated on startup time. Is there another way to make my example work on runtime?

You want to create 2 (or more, for that matter) instances and then only use one of them at runtime (what means, it could possibly change over the life of your application).
You can create a holder bean that would delegate the calls to the correct bean.
Let's assume you have:
interface BeanInterface {
// some common interface
void f();
};
// original beans a & b
#Bean
public BeanInterface beanA() {
return new BeanAImpl();
}
#Bean
public BeanInterface beanB() {
return new BeanBImpl();
}
Then create a wrapper bean:
class Wrapper implements BeanInterface {
public Wrapper(BeanInterface... beans) { this.delegates = beans };
private BeanInterface current() { return ... /* depending on your runtime condition */ }
#Override
public void f() {
current().f();
}
}
And obviously you need to create the wrapper in your configuration
#Bean
public BeanInterface wrapper() {
return new Wrapper(beanA(), beanB());
}

Related

Spring Bean depending on initialization of non-Spring class

Say I have some Java class named SomeClassConfig in which I want to define a dependency injection like the following;
#Configuration
public class SomeClass {
#Bean
SomeOtherClass someOtherClass() {
FactoryClass factory = UtilFactoryClass.getDefaultFactory();
return factory.create();
}
}
Here UtilFactoryClass denotes some library that allows me to create a factory class, which in turn allows me to create an instance of the object I am interested in. My problem is that the above-mentioned default factory is initialized after some time, so I would like for the bean to be instantiated / injected after the instantiation the default FactoryClass instance. Is this possible? UtilFactoryClass has no knowledge of the Spring Boot framework, and I tried to apply the #DependsOn annotation, but realized it only allows for me to depend on other Beans.
The concrete 'getDefaultFactory' method I am trying to apply is the following: Link. It is associated to a library named Keycloak. My problem is that the factory obtained from this method is null until a certain point in the life cycle of the application.
Do you mean:
#Configuration
public class SomeClass {
#Bean
SomeOtherClass someOtherClass() { //argument injection also possible
return factory().create();
}
#Bean
FactoryClass factory() {
//check/wait for condition e.g.:
while (!UtilityFactory.isInitialized()) {
try {
Thread.sleep(1000);
// better: TimeUnit.XXX.sleep(), and even better use an (spring managed) executor
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
return UtilFactoryClass.getDefaultFactory();
}
}
"Let spring manage the factory"!? ;)
For the check/wait part, see: https://www.baeldung.com/java-delay-code-execution

How to make a singleton by generic type with class argument in Spring?

Let's suppose I have a Wrapper with generic type:
#Component
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class Wrapper<T> {
private final Class<T> wrappedClass;
public Wrapper(Class<T> wrappedClass) {
this.wrappedClass = wrappedClass;
}
}
And I want to use this Wrapper with many classes (for example > 100). Is it possible to make Spring create singleton of wrapper for each generic type and pass generic class as parameter to constructor? For example, Spring must always inject the same instance of Wrapper<Foo>. If it is possible, please give example with java code configuration, but not with xml.
If I understood correctly you want to add beans of wrapper dynamically based on some criteria that some beans (like Foo / Bar) adhere to and some don't.
This is a kind of advanced stuff in spring, but in a nutshell you will have to implement a Bean Factory Post Processor that will be called automatically by spring during the startup.
This is a point where you could analyze the beans by iterating over all the "accessible" beans (like Foo / Bar and others) and for beans that should be wrapped you will create a bean definition of the wrapper, despite the fact that the wrapper itself is not a bean.
I've created a simple example to illustrate this. In my sample project I've put everything under package "wrappers":
#Wrappable
public class Foo {
}
#Wrappable
public class Bar {
}
public class ShouldNotBeWrapped {
}
Note that I've put an annotation #Wrappable - a custom annotation that will serve as a "differentiator" of what should be wrapped and what not. The processing of the annotation will be done in Bean Factory Post Processor.
The annotation is nothing special really, it should be acessible in runtime (spring is a runtime framework and be put on classes):
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface Wrappable {
}
The java config will add Foo, Bar, ShouldNotBeWrapped as beans and also Bean Factory Post Processor that I'll describe below:
#Configuration
public class WrappersJavaConfig {
#Bean
public Foo foo () {
return new Foo();
}
#Bean
public Bar bar () {
return new Bar();
}
#Bean
public ShouldNotBeWrapped shouldNotBeWrapped () {
return new ShouldNotBeWrapped();
}
#Bean
public WrappersEnrichmentBFPP wrappersEnrichmentBFPP () {
return new WrappersEnrichmentBFPP();
}
}
The Wrapper class itself for the sake of example has toString but it doesn't differ much from your wrapper presented in the question:
public class Wrapper<T> {
private T wrapped;
public Wrapper(T wrapped) {
this.wrapped = wrapped;
}
#Override
public String toString() {
return "Wrapper for" + wrapped;
}
}
And the Main class will list all the loaded beans and get their classes + call toString so that we could see that the wrappers are defined correctly:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(WrappersJavaConfig.class);
String[] names = ctx.getBeanDefinitionNames();
for(String name : names) {
Object bean = ctx.getBean(name);
if(bean.getClass().getPackage().getName().startsWith("wrappers")) {
System.out.println(ctx.getBean(name).getClass() + " ==> " + ctx.getBean(name));
}
}
}
}
Sidenote, the "if" condition in the main method is because I don't want to print the beans that spring loads by itself (infra stuff, etc) - only my beans which all reside in package "wrappers" as I've mentioned above.
Now the BeanFactoryPostProcessor - is a regular bean in a sense that it gets registered in the java config and it looks like this (your implementation might be different but the idea is the same):
public class WrappersEnrichmentBFPP implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] bddNames = beanFactory.getBeanDefinitionNames();
for(String bddName : bddNames) {
Object bean = beanFactory.getBean(bddName);
if(bean.getClass().isAnnotationPresent(Wrappable.class)) {
BeanDefinition wrappedBdd = BeanDefinitionBuilder.genericBeanDefinition(Wrapper.class)
.addConstructorArgReference(bddName)
.getBeanDefinition();
((BeanDefinitionRegistry)beanFactory).registerBeanDefinition("wrapperFor" + bddName, wrappedBdd);
}
}
}
}
So I'm getting all the beans one by one in for-each loop, then I'm asking whether the bean has an annotation "wrappable" on it in the if condition. If it has - it must be wrapped.
In this case I create an "artificial" bean definition for Wrapper and add a constuctor that will reference my bean that should be wrapped.
Then I register the bean definition by adding it to the application context.
Run the code above and you'll see the output similar to mine:
class wrappers.WrappersJavaConfig$$EnhancerBySpringCGLIB$$f88f147d ==> wrappers.WrappersJavaConfig$$EnhancerBySpringCGLIB$$f88f147d#1283bb96
class wrappers.Foo ==> wrappers.Foo#74f0ea28
class wrappers.Bar ==> wrappers.Bar#f6efaab
class wrappers.ShouldNotBeWrapped ==> wrappers.ShouldNotBeWrapped#3c19aaa5
class wrappers.WrappersEnrichmentBFPP ==> wrappers.WrappersEnrichmentBFPP#3349e9bb
class wrappers.Wrapper ==> Wrapper forwrappers.Foo#74f0ea28
class wrappers.Wrapper ==> Wrapper forwrappers.Bar#f6efaab
As you see, two last lines are lines that correspond to the wrapper beans created for the same instances of Foo and Bar but nothing was created for the ShouldNotBeWrapped bean
The APIs used are somewhat obscure and look outdated, but again its pretty advanced stuff and works at the level of spring container infra itself. Having said that, there are a lot of tutorials about BeanFactoryPostProcessor-s.
Since Using BFPPs is not a usual task, and although I've provided the solution, I don't see any real usage of it, wrappers can't be used "instead" of Foo or Bar classes, do not have their APIs, etc. Maybe you could explain why do you need wrappers over some beans. Usually people use Aspects/BeanPostProcessors (not BFPP but BPP) to wrap the class into dynamic proxy (cglib / java.lang.Proxy) and add an additional behavior, stuff like #Transactional, cache handling and so forth is implemented in spring with BeanPostProcessors, so consider checking this direction as well.
It is possible and in fact a feature in spring.
Spring can inject your dependency with the correct generic type.The following example is from spring documentation.
Suppose you have an interface
public interface Store<T>{...}
and two beans. One implements Store,one implemenets Store.
#Configuration
public class MyConfiguration {
#Bean
public StringStore stringStore() {
return new StringStore();
}
#Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
You can declare the type with the correct type parameter and spring will inject the right bean for you.
#Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
#Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

customizable aspects in spring

I’d like to apply a customizable aspect on two different services (spring bean). My problem is how/where to set/define the pointcut expression. One normally defines the pointcut expression on a ‘dummy method’ or directly on the advice method. However that means the pointcut is static (not customizable).
I’d like to define the pointcut at the bean creation level to be able to create the same kind of advice for different targets. Ideally I’d like to do something like this:
#Aspect
public class ServiceAspect {
private static final Logger LOG = LoggerFactory.getLogger(ServiceAspect.class);
private final String discriminator;
// no advice defined here!!!
public ServiceAspect(String discriminator) { this.discriminator = discriminator; }
public Object around(ProceedingJoinPoint jp) throws Throwable {
LOG.info(discriminator + " called");
return jp.proceed();
}
}
#Configuration
#EnableAspectJAutoProxy
#PropertySource("classpath:application.properties")
public class ServiceConfiguration {
#Bean
public MyService service1() { return new MyServiceImpl(); }
#Bean
#Around("bean(service1)") // define the advice when bean is created
#ConditionalOnProperty("aspect1Enbaled")
public ServiceAspect aspect() {
return new ServiceAspect("Aspect-1");
}
#Bean
public YourService service2() { return new YourServiceImpl(); }
#Bean
#Around("bean(service2)") // define a different advice when bean is created
#ConditionalOnProperty("aspect2Enbaled")
public ServiceAspect aspect() {
return new ServiceAspect("Aspect-2");
}
}
Notice that the #Around annotation is on the definition of the bean. I can thus reuse the aspect for different target. Using the #ConditionalOnProperty, this would enable me to turn on/off individual aspect base on a property.
Can anyone help me with this? I suspect I’ll need to create some kind of factory but can’t seem to see how I can REPLACE an already defined bean (the service bean) with a proxy!

How to inject singleton objects produced into factory

Even after going through similar questions on factory and dependency injection, I couldn't find an answer.
Let's say I have a EventHandler factory which gives out different kind of EventHandler based on event type. Each type of EventHandler has it's own dependencies used in handling that event and these could be different from other EventHandler type.
public interface EventHandler {
void handle(Event e);
}
public class EventHandlerA implements EventHandler{
private DependencyA depA;
//constructors for depA
}
public class EventHandlerB implements EventHandler{
private DependencyB depB;
//constructors for depB
}
public class Factory {
public EventHandler getHandler(EventType type) {
if(type.equals("A")) {
//return instance of EventHandlerA
}
}
}
#Configuration
public class Configuration {
#Bean
public EventHandler eventHandlerA() { return new EventHandler(depA()); }
#Bean
public Factory factory() { //.. ?? }
}
My question is - I am using Spring #Configuration with #Bean annotation to create singleton beans of all these event handlers and factory. How do I inject these beans of various event handlers into the factory so that factory can return these instances based on event type.
One direct approach I can think of is to just pass all the event handlers to factory during factory instantiation (via constructor) and maintain a mapping of type to handler and just do a get on that map. Downside of this approach is that whenever I need to add new event handler to the factory, I'll have to modify the constructor or add a new setter. Is there any better approach?
There are alot of ways to go about doing this but here are 2 ways.
In your question you have this:
public EventHandler eventHandlerA() { return new EventHandler(depA()); }
Now I dont know if you intended to have this but your bean will be a different instance at every injection because it creates a new one every return statement. You've essentially made your configuration into a factory. I am going to assume that this is unintended.
Method 1
I think this is the strategy I would use because you can add a boat load of handlers, you dont pollute your app context namespace and its clean and easy to follow. For this method I renamed EventHandlerFactory to EventHandlerRepository to make it more appropriate to its functionality.
When the configuration is loaded all the event handlers are added to the factory and then you can #Autowire that factory around your application then call repository.getHandler(/*Type*/).
This same design pattern could be used to make a real factory by simply renaming EventHandlerRepository to EventHandlerFactory then passing it Class<? extends EventHandler> instead of actual instances and creating a new instance on getHandler.
EventType:
public enum EventType {
A, B;
}
EventHandlerRepository:
public class EventHandlerRepository {
private Map<EventType, EventHandler> handlers;
public EventHandlerRepository() {
handlers = new HashMap<EventType, EventHandler>();
}
void synchronized registerHandler(EventType type, EventHandler handler) {
handlers.put(type, handler);
}
public synchronized EventHandler getHandler(EventType type) {
return handlers.get(type);
}
}
Configuration:
#Configuration
public class Configuration {
private EventHandlerRepository repository = new EventHandlerRepository();
public Config() {
// register all your event handlers here
factory.registerHandler(EventType.A, new EventHandlerA());
}
#Bean
public EventHandlerRepository getEventHandlerRepository() {
return repository;
}
}
Method 2
Simply add #Component to all your event handlers and make sure they are in your component scan path.
#Component
public class EventHandlerA implements EventHandler

How can I select Spring bean instance at runtime

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();
}
}

Categories

Resources