Spring declare multiple beans - java

I have an HashMap of myobjects which I want to loop through and declare each one of them as bean. At the end I want to choose one particular key as #primary.
Is there anyway to do this in Spring Java Config?
I can't really do this in listeners as these beans are required when context loads up. I was looking at factory beans but could not figure it out.
Any help is appreciated.
Thanks
Abhi

You can create a set of beans by getting a reference to ApplicationContext in one of your Java configuration classes. For example,
#Configuration
public class ServicesConfig {
#PostConstruct
public void onPostConstruct() {
Map<String, MyClass> mapOfClasses = HashMap<>(); // your HashMap of objects
AutowireCapableBeanFactory autowireCapableBeanFactory = context.getAutowireCapableBeanFactory();
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) autowireCapableBeanFactory;
for (Map.Entry<String, MyClass> myClassEntry : mapOfClasses.entrySet()) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(myClassEntry.getValue().getClass());
beanDefinition.setAutowireCandidate(true);
registry.registerBeanDefinition(myClassEntry.getKey(), beanDefinition);
autowireCapableBeanFactory.autowireBeanProperties(myClassEntry,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
}
}
}
Here MyClass is the type of your myobject and each MyClass can also have #Autowired annotations. At this point, I assume that each of these objects will be given their bean name the key of the HashMap. This objects then can be used as dependencies for other beans if you want.
Same thing can be achieved by implementing BeanDefinitionRegistryPostProcessor and overriding postProcessBeanDefinitionRegistry to register your HashMap of objects. In this method you will be able to create BeanDefinitions with BeanDefinitionBuilder.
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MyClass.class).getBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition("key_of_this_object", beanDefinition);
I'm not sure if you can mark one of these beans as #Primary. But based on this post you can define your own bean resolver by extending DefaultListableBeanFactory which I haven't tested myself. As an alternative you may use #Qualifier as you already know which object is going to be the primary bean.
Hope this will help.
P.S If objects are already available to be added in to the ApplicationContext, following approach would add these custom objects to ApplicationContext
ConfigurableListableBeanFactory configurableListableBeanFactory = ((ConfigurableApplicationContext)context).getBeanFactory();
configurableListableBeanFactory.registerSingleton("key_of_this_object", myClassObject);

Related

Spring find beans that are not injected into other beans

  I want to find all beans that are not injected into other beans, thus I can remove them to make spring start up faster. Any ideas? Thanks in advance.
From ConfigurableBeanFactory#getDependentBeans Javadoc, I see that there's a method we can invoke to get an array of beans that depends on the bean name we provide. Tracing backwards to how we can get the bean factory. You could probably do the following if you can get a hold of the GenericApplicationContext:
Get the bean factory from the context.
Iterate through the bean definition names in the bean factory.
Call ConfigurableBeanFactory::getDependentBeans to see if anything depends on it.
#Component
public class Example {
#EventListener
public void contextRefreshed(ContextRefreshedEvent event) {
// Could also just autowire the context directly
GenericApplicationContext context = (GenericApplicationContext) event.getApplicationContext();
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] beanNames = beanFactory.getBeanDefinitionNames();
for(String beanName : beanNames) {
String[] dependentBeanNames = beanFactory.getDependentBeans(beanName);
if (dependentBeanNames.length <= 0) {
// bean with nothing depending on it
}
}
}
}
Edit:
This solution isn't perfect, but would probably be useful as a starting point. There are beans that could have nothing depend on it, but are used in the application. A good example would be your controllers (classes annotated with #Controller). From what I tested out, it had 0 dependent beans but the request mapping methods it holds is clearly being executed and referenced somehow.

Retrieving Classes of Beans prior/during instantiation phase

In an Spring Application it is possible to retrieve all (?) Beans with applicationContext.getBeansOfType(Object.class). This is of course only possible, after all Beans have been created.
So, if I call this method in the constructor of a Bean, I have to be lucky, to be the last Bean to be created, to have access to all of them.
As far as I understand the life cycle of Spring Beans, there is a phase in which BeanDefinitions are created, before the Beans are initialized.
How is it possible to retrive all created BeanDefinitions in the constructor of a Bean?
Can I also retrive the types (as Class) of those BeanDefinitions? The type BeanDefinition seems to only provide the "current bean class name of this bean definition".
Or is the only way to get those types after all Beans have been constructed (e.g. #PostConstruct)?
Maybe this code could help
for (String name : applicationContext.getBeanFactory().getBeanDefinitionNames()) {
BeanDefinition beanDefinition = applicationContext.getBeanFactory().getBeanDefinition(name);
String className = beanDefinition.getBeanClassName();
Class<?> clazz = Class.forName(className);
}
The loop gets you all the BeanDefinitions and then you load the class for each and do what you want?
By the way this might not be a good way to use Spring but it will work.
You can create a last bean by putting it for example in an #Configuration class with a minimum initialization order, so that it is the last one with
#Order(Ordered.LOWEST_PRECEDENCE), that would be it:
#Configuration
#Order(Ordered.LOWEST_PRECEDENCE)
public class Last {
#Autowired
ApplicationContext applicationContext;
#Bean
public String lastBean() {
applicationContext.getBeanDefinitionNames(); //retrive all created BeanDefinitions in the constructor of a Bean
applicationContext.getBeansOfType(Object.class); //retrive the types (as Class)
return "lastBean";
}
}

How to make #ConditionalOnMissingBean work by behavior of dynamic create spring bean (eg: beanfactory register)

I am writing a spring boot starter, I want to implement dynamic creation of bean injection into spring ioc, and can make #ConditionalOnMissingBean take effect without creating default bean.
I tried to weave the beanfactory directly in the configuration class and dynamically create the bean by calling the registerSingleton method via the ConfigurableBeanFactory, but the result is wrong, #ConditionalOnMissingBean does not take effect. #Bean is ok, this should be related to the spring boot scan #Configuration class and the corresponding #Bean method, but #Bean can not achieve dynamic creation of the bean, because the bean I need is generated according to the content of the configuration file, the number Uncertain, the content is uncertain.
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
while (iterator.hasNext()) {
// Generate a bean based on the configuration
...
SpringBean bean = ...
configurableBeanFactory.registerSingleton(SpringBeanName, bean);
}
I hope that #ConditionalOnMissingBean will still take effect if the bean is dynamically created.
As its stated in the API documentation:
The #Conditional annotation may be used in any of the following ways:
as a type-level annotation on any class directly or indirectly annotated with #Component, including #Configuration classes
as a meta-annotation, for the purpose of composing custom stereotype annotations
as a method-level annotation on any #Bean method
That means it won't have an effect on dynamically created beans that registers in the ApplicationContext after the start up.
So when you want to achieve a similar kind of behavior try checking if a bean is present in the ApplicationContext like that:
#Autowired
private ApplicationContext context;
...
context.getBeansOfType(YourType.class).isEmpty()
Update:
When you want to get rid of the default configuration of that bean you need to exclude it. There are two ways to achieve that. Either by excluding the whole configuration file where the bean is defined by specifying it like #SpringBootApplication(exclude = SomeConfig.class) or by implementing your own exclude filter on #ComponentScan.

set map as a Spring bean

I need to set a map as a spring bean. I have a map which is intialized in init() method with #PostConstruct keyword.
public class CustomerService
{
#Autowired
TestService testService;
static Map<String, String> someMap;
#PostConstruct
public void initIt() throws Exception {
someMap = new HashMap<String, String>();
someMap = // some code by calling testService
}
#PreDestroy
public void cleanUp() throws Exception {}
}
I call this from applicationContext.xml as a bean.
<bean id="customerService" class="test.com.customer.CustomerService"/>
Initializing the map is working properly, i need to assign the value of this map into bean in order to access the value of the bean in somewhere else in the application.
I found examples of setting up map as a bean, but all were done in XML configurations.
How to inject a Map in java springs
How can i achieve this task. Any knowledge is highly appreciated.
You should just create a getter for that map. And you can #autowire your bean somewhere else and call yourbean.getMap()
and you will have it.
In your other class you can:
#Autowired
CustomerService customerService;
And of course greate a getter method in your Customer service for the map.
Then in your controller or other service you should autowire your bean with the annotation above. And then use it in your method like that:
Map m = customerService.getMap();
You can create a Flyweight Design Pattern later in your app with that approach(as you are creating a bean to hold a map). Read a tutorial about Flyweight Design Pattern here

Programmatically set a specific bean object - Spring DI

In my program I need to programmatically configure an ApplicationContext. Specifically, I have a reference to an instance of MyClass and I want to define it as a new bean called "xxyy".
public void f(MyClass mc, ApplicationContext ac) {
// define mc as the "xxyy" bean on ac ???
...
...
// Now retrieve that bean
MyClass bean = (MyClass) ac.getBean("xxyy");
// It should be the exact same object as mc
Assert.assertSame(mc, bean);
}
The BeanDefinition API let's me specify the class of the new bean, so it does not work for me since I want to specify the instance.
I managed to find a solution but it took two additional factory beans which seems like too much code for such an eartly purpose.
Is there a standard API that addresses my needs?
You need to jump through a few hoops to do this. The first step is to obtain a reference to the context's underlying BeanFactory implementation. This is only possible if your context implements ConfigurableApplicationContext, which most of the standard ones do. You can then register your instance as a singleton in that bean factory:
ConfigurableApplicationContext configContext = (ConfigurableApplicationContext)appContext;
SingletonBeanRegistry beanRegistry = configContext.getBeanFactory();
beanRegistry.registerSingleton("xxyy", bean);
You can "insert" any object into the context like this.
you can use this context:
GenericApplicationContext mockContext = new GenericApplicationContext();
which has a
mockContext.getBeanFactory().registerSingleton("name", reference);
and plug it in the real context
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "real-context.xml" }, mockContext);
and the classes are:
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;

Categories

Resources