How to register many object as beans in Spring Java Config? - spring

I would like dynamically register multiple objects as Spring beans. Is is possible, without BeanFactoryPostProcessor?
#Configuration public class MyConfig {
#Bean A single() { return new A("X");}
#Bean List<A> many() { return Arrays.asList(new A("Y"), new A("Z"));}
private static class A {
private String name;
public A(String name) { this.name = name;}
#PostConstruct public void print() {
System.err.println(name);
}
}
}
Actual output shows only one bean is working:
X
Expected:
X Y Z
Spring 4.3.2.RELEASE

You should specify your A bean definition kind of prototype with a parameter
#Configuration
public class MyConfig {
#Bean #Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
A template(String seed) {
return new A(seed);
}
#Bean
String singleA() {
return "X";
}
#Bean
List<A> many() {
return asList(template("Y"), template("Z"));
}
private static class A {
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
A a = (A) context.getBean("template");
System.out.println(a);
List<A> l = (List<A>) context.getBean("many");
System.out.println(l);
}
}
prototype scope allows Spring to create a new A with every template execution and register an instance into context.
The result of main execution is as you expect
Y
Z
MyConfig$A#15bfd87
[MyConfig$A#543e710e, MyConfig$A#57f23557]
X

What I want is impossible but requested with https://jira.spring.io/browse/SPR-13348
If you think multiple bean registration is OK please upvote

Related

Comparison of Guice and(move to) Spring

Could someone give me advice, please, how to re-write some method using simple Spring (w/o Boot)?
Here I have some code methods:
1. createInjector
private Injector injector;
someMethod(){
injector = Guice.createInjector(new ExampleClass1(), new ExampleClass2());}
2 setModules(Modules.override
setModules(Modules.override(new ExampleClass3()).with(new ExampleClass4()));
//////////////////////////////////////////////////////////////////
public static void setModules(Module... modules) {
initInjector(modules);
}
private static void initInjector(Module... modules) {
injector = Guice.createInjector(modules);
}
}
Taking the risk that my answer is too general.
Roughly saying you can think Guice modules as equivalent a configuration class with #Configuration annotation, that contains #Bean etc.
The Guice injector can be considered as equivalent to the Spring ApplicationContext.
So for example if we have two configuration files:
#Configuration
public class ConfigA {
#Bean
ExampleClass1 exampleClass1(){
return new ExampleClass1();
}
#Bean
ExampleClass2 exampleClass2(){
return new ExampleClass2();
}
}
#Configuration
public class ConfigB {
#Bean
ExampleClass1 exampleClass1(){
return new ExampleClass1();
}
#Bean
ExampleClass3 exampleClass2(){
return new ExampleClass3();
}
}
And Services ExampleClass4 that you want as alternative of ExampleClass3.
You may use the #Primary annotation
public class ExampleClass4 extends ExampleClass3 {
#Override
public String toString() {
return "ExampleClass4{}";
}
}
#Configuration
public class ConfigC {
#Bean
#Primary
ExampleClass3 exampleClass3(){
return new ExampleClass4();
}
}
So rewriting the app to Spring (core 5.2, not Spring boot) will be:
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ap = initAppContext();
overrideBinding(ap);
System.out.println(ap.getBean(ExampleClass3.class));
//prints ExampleClass4{}
}
private static AnnotationConfigApplicationContext initAppContext() {
AnnotationConfigApplicationContext ap = new AnnotationConfigApplicationContext();
ap.register(ConfigA.class, ConfigB.class);
return ap;
}
private static void overrideBinding(AnnotationConfigApplicationContext ap) {
ap.register(ConfigC.class);
ap.refresh();
}
}
This technic of overriding a binding will work only because ExampleClass3 wasn't defined as primary, if it doesn't that would not work and you need to consider a different approach.
For more information:
https://www.baeldung.com/spring-application-context
https://docs.spring.io/spring-javaconfig/docs/1.0.0.m3/reference/html/modularizing-configurations.html
Override bean definition in java config

Spring-Boot Failing to #Schedule tasks for List of beans created by #Configuration class

I am attempting to create List of beans of the same type in a class annotated by #Configuration. What I need is to execute #Scheduled function declared in those beans.
#SpringBootApplication
#EnableScheduling
public class DemoApplication
{
public static void main(String[] args)
{
SpringApplication.run(DemoApplication.class, args);
}
}
#Configuration
public class Config
{
#Bean
public List<Monitoring> mon()
{
List<Monitoring> list = new ArrayList<>();
for (int x = 0; x < 5; ++x)
{
list.add(new First());
}
return list;
}
}
public class First implements Monitoring
{
private static final Logger logger = LoggerFactory.getLogger(First.class);
#Override
public void doSth()
{
logger.info("first monitoring bean");
}
#Scheduled(fixedRate = 50)
private void init()
{
logger.info("scheduled task");
}
}
What am I expecting from these code snippets is for my 5 Beans of Monitoring to print "scheduled task" every 50ms, but I never see this output.
You are only creating a list as a bean with unmanaged Monitoring instances. That won't work.
You need to properly create the Monitoring beans (so that Spring can do its magic):
#Configuration
public class Config {
#Bean
public Monitoring first() {
return new First();
}
#Bean
public Monitoring second() {
return new First();
}
}

What is the actual usage of #configuration in spring?

I just wanted to know why the below program is working even if I dont use #Configuration annotation on AppConfig class. Can you please let me know how it is working ?
Case 1:
AppConfig.java(with #Configuration)
#Configuration
public class AppConfig {
#Bean
public Item item(){
Item item = new Item();
item.setItemNo(46789);
item.setItemName("chair");
item.setItemType("ART");
item.setItemSize("A4");
return item;
}
}
Item.java
public class Item {
int itemNo;
String itemName;
String itemType;
String itemSize;
public int getItemNo() {
return itemNo;
}
public void setItemNo(int itemNo) {
this.itemNo = itemNo;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public String getItemType() {
return itemType;
}
public void setItemType(String itemType) {
this.itemType = itemType;
}
public String getItemSize() {
return itemSize;
}
public void setItemSize(String itemSize) {
this.itemSize = itemSize;
}
}
ItemTest.java
public class ItemTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ct = new AnnotationConfigApplicationContext(AppConfig.class);
Item item = ct.getBean(Item.class);
System.out.println(item.getItemNo());
}
}
Case 2 :
AppConfig.java ( without #Configuration)
public class AppConfig {
#Bean
public Item item(){
Item item = new Item();
item.setItemNo(46789);
item.setItemName("chair");
item.setItemType("ART");
item.setItemSize("A4");
return item;
}
}
When you remove the #Configuration annotation from AppConfig class, calling item() method will be a plain java method call and you will get a new instance of Item and it won’t remain singleton.
To prove this point, first add a constructor to Item class as following:
public class Item {
...
public Item() {
System.out.println("Item instance created")
}
...
}
then define another bean that will be using Item instance as following:
public class ItemConsumer {
public ItemConsumer(Item item) {
System.out.println("ItemConsumer created");
}
}
and use it as bean in AppConfig class as following:
public class AppConfig {
#Bean
public Item item(){
Item item = new Item();
item.setItemNo(46789);
item.setItemName("chair");
item.setItemType("ART");
item.setItemSize("A4");
return item;
}
#Bean
public ItemConsumer itemConsumer() {
return new ItemConsumer(item());
}
}
change ItemTest as below:
public class ItemTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ct = new AnnotationConfigApplicationContext(AppConfig.class);
Item item = ct.getBean(Item.class);
ItemConsumer itemConsumer = ct.getBean(ItemConsumer.class);
}
}
Now when you run the ItemTest class, it generates following output:
Item instance created
Item instance created
ItemConsumer created
So Item class instantiated twice that means it is not singleton.
now annotate AppConfig class with #Configuration annotation again and run the ItemTest class. This time output will be like below:
Item instance created
ItemConsumer created
#Bean Lite Mode
#Bean methods may also be declared within classes that are not
annotated with #Configuration. For example, bean methods may be
declared in a #Component class or even in a plain old class. In such
cases, a #Bean method will get processed in a so-called 'lite' mode.
Bean methods in lite mode will be treated as plain factory methods by
the container (similar to factory-method declarations in XML), with
scoping and lifecycle callbacks properly applied. The containing class
remains unmodified in this case, and there are no unusual constraints
for the containing class or the factory methods.
Source - Spring Documentation.

How does Spring beans work with Prototype scope?

I have following spring bean with Prototype scope. In the AppRunner class, I want a new bean to injected by spring within the for loop (if loop count is 2, then i want only 2 new beans to be injected).
But spring injects a new bean every time the setter methods of the SimpleBean is called.
SimpleBean.java
#Component
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode =
ScopedProxyMode.TARGET_CLASS)
public class SimpleBean {
private String id;
private Long value;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Long getValue() {
return value;
}
public void setValue(Long value) {
this.value = value;
}
}
AppRunner.java
#Component
public class AppRunner {
#Autowired
SimpleBean simpleBean;
public void execute(List<Output> results){
List<SimpleBean> finalResults = new ArrayList<SimpleBean>();
for(Output o : results){
simpleBean.setId(o.getAppId());
simpleBean.setValue(o.getAppVal());
finalResults.add(simpleBean);
}
}
}
Output.java
public class Output {
private String appId;
private Long appVal;
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public Long getAppVal() {
return appVal;
}
public void setAppVal(Long appVal) {
this.appVal = appVal;
}
}
Unfortunately prototype scope doesn't work like this. When your AppRunner bean is instantiated by the container it is asking for its dependencies. Then a new instance of SimpleBean is created. This instance stays as dependency. Prototype scope starts working when you will have multiple beans with dependency on SimpleBean. Like:
#Component
class BeanOne {
#Autowired
SimpleBean bean; //will have its own instance
}
#Component
class BeanTwo {
#Autowired
SimpleBean bean; //another instance
}
There is one rather straightforward update which can lead to your desired behaviour. You can remove autowired dependency and ask for a new dependency in your loop from context. It would look like this.
#Component
public class AppRunner {
#Autowired
ApplicationContext context;
public void execute(List<Output> results){
List<SimpleBean> finalResults = new ArrayList<SimpleBean>();
for(Output o : results) {
SimpleBean simpleBean = context.getBean(SimpleBean.class);
simpleBean.setId(o.getAppId());
simpleBean.setValue(o.getAppVal());
finalResults.add(simpleBean);
}
}
}
Other option could be technique called Method injection. It is described in the relevant documentation for prototype scope. You can take a look here 7.5.3 Singleton beans with prototype-bean dependencies

Javaconfig bean overiding does not take in account added #DependsOn

While overrding a Javaconfig Bean by extending the original #Configuration class, I would like to add a #DependsOn for the new Bean definition.
However, this depends-on seems not to be taken in account. here is a TestCase reproducing my issues:
public class SpringTest {
#Test
public void testDependsOnTakenInAccount() {
AnnotationConfigApplicationContext ctx2 = new AnnotationConfigApplicationContext(AConfig.class, CConfig.class);
Assert.assertEquals("overriden", ctx2.getBean("bean"));
}
#Configuration
public static class AConfig {
#Bean
public Object bean() {
return "not overriden";
}
}
#Configuration
public static class CConfig extends AConfig {
protected boolean isInitialized = false;
#Bean
public Void doInit() {
isInitialized = true;
return null;
}
#Bean
#DependsOn("doInit")
public Object bean() {
if (!isInitialized) {
throw new RuntimeException("Not initialized");
}
return "overriden";
}
}
}
Is this an expected behavior? If yes, how can I add dependency while overriding a bean?
For me seems like a bug.
When overriding a #Bean factory method in a Configuration class, the parent BeanDefinition wins and get registered on the BeanFactory overriding the child one.
So you cannot configure the bean with annotaions (because it will be overriden).
The following Test result on
expected:<[doInit]> but was:<[otherBean]>
#RunWith(JUnit4ClassRunner.class)
public class DependOnTest {
#Test
public void testBeanDefinitionOverriding() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
BeanDefinition bd = ctx.getBeanDefinition("bean");
Assert.assertEquals("doInit", bd.getDependsOn()[0]);
}
#Configuration
public static class ParentConfig {
#Bean
#DependsOn("otherBean")
public String bean() {
return "not overriden";
}
#Bean
public String otherBean() {
return "otherBean";
}
}
#Configuration
public static class Config extends ParentConfig {
#Bean
public String doInit() {
return "doInit";
}
#Bean
#DependsOn("doInit")
public String bean() {
return "overriding";
}
}
}
I think that problem start on ConfigurationClassParser:
// recursively process the configuration class and its superclass hierarchy
do {
metadata = doProcessConfigurationClass(configClass, metadata);
}
while (metadata != null);
That result on overriden method added to CongurationClass.beanMethods
It could be fixed checking if the beanMethod was already added from a superclass in ConfigurationClass.addBeanMethod()
public void addBeanMethod(BeanMethod method) {
// Check if already added a bean method from superclass
for (BeanMethod beanMethod : beanMethods) {
if (beanMethod.getMetadata().getMethodName().equals(method.getMetadata().getMethodName()) &&
!(beanMethod.getMetadata().getDeclaringClassName()).equals(method.getMetadata().getDeclaringClassName()))
// log and return.
return;
}
this.beanMethods.add(method);
}
As pointed out by Jose Luis Martin, this has been confirmed as a bug by Spring team.
I've workarounded it with:
#DependsOn("doInit")
#Bean
public Void notOverridingBean() {
return null;
}
#Bean
public Object bean(Object notOverridingBean) {
return "overriden";
}
an alternative is to override the bean in another #Configuration class.

Resources