Javaconfig bean overiding does not take in account added #DependsOn - spring

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.

Related

Injecting spring bean (service layer class) into ResourceBundle

I created a class using ResourceBundle interface as shown below. This class is dependent on another class. The implementation class for ResourceBundle (QuestionnaireSource as shown below) always has null as dependencies. No matter if I use setter or constructor injection.
Could someone please help me with this issue. I am I missing some configuration here.
#Component
public class QuestionnaireSource extends ResourceBundle {
private final QuestionnaireCache questionnaireCache;
private static final Object lockObject = new Object();
#Override
protected Object handleGetObject(String key) {
// Gets an object for the given key from this resource bundle.
// Returns null if this resource bundle does not contain an object for the given key 0
Object value = null;
try {
value = getString(key, LocaleContextHolder.getLocale());
} catch (Exception ignored) {
}
return value;
}
public Questionnaire getString(String key, Locale locale) {
Locale l = safeLocale(locale);
return getResources(l).get(key);
}
private Locale safeLocale(Locale l) {
if (l.getLanguage().equalsIgnoreCase("DE")) {
return Locale.GERMAN;
} else {
return Locale.ENGLISH;
}
}
protected Map<String, Questionnaire> getResources(Locale locale) {
synchronized (lockObject) {
return questionnaireCache.getQuestionnaireCache().get(locale.getLanguage().toUpperCase());
}
}
#Override
public Enumeration<String> getKeys() {
return null;
}
public QuestionnaireSource(QuestionnaireCache questionnaireCache) {
super();
this.questionnaireCache = questionnaireCache;
}
}
Update:
I found that even simple dependency injection in resourceBundle is failing.
UPdate2:
The way I am using in the main class is as follows:
// ResourceBundle test here
System.out.println("Test here for resource bundle");
Locale locale = new Locale("de", "DE");
ResourceBundle bundle = ResourceBundle.getBundle("com.app.util.QuestionnaireSource", locale);
System.out.println(bundle.getString("some.test.string"));
Update3
I am writing a simple example to convey the scenario:
Some service class
#Service
public class SomeServiceTest {
public String testMethod(){
return "test here and complete";
}
}
Some example implementation of resource bundle
#Component
public class MyResourceBundle extends ResourceBundle {
private final SomeServiceTest someServiceTest;
#Autowired
public MyResourceBundle(SomeServiceTest someServiceTest) {
this.someServiceTest = someServiceTest;
}
#Override
protected Object handleGetObject(String key) {
if(key.equals("test"))
return "test";
return null;
}
#Override
public Enumeration<String> getKeys() {
return null;
}
}
Main.java
main(){
// ResourceBundle test here
System.out.println("Test here for resource bundle");
Locale locale = new Locale("de", "DE");
ResourceBundle bundle = ResourceBundle.getBundle("com.app.util.MyResourceBundle", locale);
System.out.println(bundle.getString("test"));
}
Update4:
I changed the annotation on classes as mentioned by on this post https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects
but still I have the null dependency injection for SomeServiceTest class. The changes are as shown below.
SomeServiceTest.java
#Service
public class SomeServiceTest {
public String testMethod(){
return "test here and complete";
}
}
MyResourceBundle.java
#Configurable
public class MyResourceBundle extends ResourceBundle {
#Autowired
private SomeServiceTest someServiceTest;
public MyResourceBundle() {
}
#Override
protected Object handleGetObject(String key) {
if(key.equals("test"))
return someServiceTest.testMethod();
return null;
}
#Override
public Enumeration<String> getKeys() {
return null;
}
}
still SomeServiceTest class is null.
Can you please post an example on how you are using this class? Is it you (your code) or spring who instanciate it (on startup)?
#Component only works for beans which Spring instanciate. If you want to inject stuff in classes you instanciate in you code you can annotate the class with #Configurable.
Please see https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects for some examples.
Make sure you have initialized the spring context
If you are using spring boot
You can get the application context after it starts and use it to get the bean you want
For example
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(YouApplication.class, args);
MyResourceBundle resConfig = run.getBean("myResourceBundle", MyResourceBundle .class);
resConfig.handleGetObject("test");
}
Unfortunately ResourceBundle.getBundle does not initialize the spring application context

Invoking PostConstruct for each bean in a list

I have the following pseudo code:
#Bean
public List<BeanB> beanB(
List<BeanA> beansA) {
List<BeanB> beansB = new ArrayList<>();
for (BeanA beanA : beansA) {
beansB.add(new BeanB(beanA))
}
return beansB;
}
#Bean
public BeanC beanC(
List<BeanB> beansB) {
return new BeanC(beansB);
}
Now the challenge is when the list of BeansB is constructed post construct is not invoked on those beans in the list. Is there any idiomatic way to trigger post construct invocation on those beans.
This does not work well as #PostConstruct is called by Spring. and from your code, for List<BeanB>, Spring will only try to find #PostConstruct in List. If it can find it, it will execute the #PostConstruct code.
I suspect you are writing beanA as follow in case you have #PostConstruct in beanA's class like below.
#Bean
public BeanA beanA_1() {
return new BeanA();
}
#Bean
public BeanA beanA_2() {
return new BeanA();
}
// which naming List of Bean B as beanB does not seems a good idea though
#Bean
public List<BeanB> beanB(List<BeanA> beansA) {
List<BeanB> beansB = new ArrayList<>();
for (BeanA beanA : beansA) {
beansB.add(new BeanB(beanA))
}
return beansB;
}
To make it work, there are multiple ways, I will just suggests some of them.
Creating a new Class, says BeanBCollectionWrappe to wrap the List of BeanB. And to make the code a bit more robust, I am implementing InitializingBean instead of #PostConstruct which is essentially the same, but allow me to ensure the method is afterPropertiesSet but not any methods.
public class BeanBCollectionWrapper implements InitializingBean {
private List<BeanB> beansB;
public void afterPropertiesSet() throws Exception {
for (BeanB beanB: beansB) {
beanB.afterPropertiesSet();
}
}
// getter and setter
}
public class BeanB implements InitializingBean {
public void afterPropertiesSet() throws Exception {
// ...
}
}
The Wrapper code can definitely be a bit better by use of Generics, like replacing BeanB to <T extends InitializingBean>.
And for the last Part,
#Bean
public BeanC beanC(BeanBCollectionWrapper wrapper) {
return new BeanC(wrapper.getBeansB());
}
Another method can be done by implementing BeanPostProcessor.
For example,
#Component
public class BeanBListBeanPostProcessor implements BeanPostProcessor{
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("beanB")) {
List<BeanB> beanBList = (List) bean;
for (BeanB beanB : beanBList) {
try {
beanB.afterPropertiesSet();
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
return null;
}
}
public class BeanB implements InitializingBean {
public void afterPropertiesSet() throws Exception {
// ...
}
}

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 to register many object as beans in Spring Java Config?

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

Spring #Autowired annotated object value is null

// My Factory class
#Component
public class UserRewardAccountValidatorFactory {
#Autowired
private VirginAmericaValidator virginAmericaValidator;
private static class SingletonHolder {
static UserRewardAccountValidatorFactory instance = new UserRewardAccountValidatorFactory();
}
public static UserRewardAccountValidatorFactory getInstance() {
return SingletonHolder.instance;
}
private UserRewardAccountValidatorFactory() {}
public PartnerValidator getPartnerValidator(Partner partner){
return virginAmericaValidator;
}
}
// My Validator class
#Service
public class VirginAmericaValidator implements PartnerValidator {
#Override
public void validate(String code) throws InvalidCodeException{
//do some processing if processing fails throw exception
if (code.equals("bad".toString())){
throw new InvalidCodeException();
}
}
}
//Usage:
PartnerValidator pv = UserRewardAccountValidatorFactory.getInstance().getPartnerValidator(partner);
if (pv != null){
try {
pv.validate(userRewardAccount);
} catch (InvalidCodeException e){
return buildResponse(ResponseStatus.INVALID_USER_REWARD_ACCOUNT, e.getMessage());
}
}
My package scan level is at much higher level. Whats happening is my virginAmericaValidator is always empty. Why is #Autowired annotation not working.
Your current approach will not work with Spring as you are ultimately using new UserRewardAccountValidatorFactory to create the instance which essentially bypasses Spring context altogether. Two approaches that should possibly work are these:
a. Using a factory-method and using xml to define your bean:
<bean class="package.UserRewardAccountValidatorFactory" name="myfactory" factory-method="getInstance"/>
This will essentially return the instance that you are creating back as a Spring bean and should get autowired cleanly.
b. Using Java #Configuration based mechanism:
#Configuration
public class MyBeanConfiguration {
#Bean
public UserRewardAccountValidatorFactory myFactory() {
return UserRewardAccountValidatorFactory.getInstance();
}
}

Resources