eigil
eigil

Reputation: 1182

Is it possible to make Spring ignore a bean property that is not writable or has an invalid setter method

Is there a way to have Spring not throw exception in below case?

<bean id="myMain" class="pak.Main">
  <property name="name" value="myName"/>
  <property name="age" value="100"/>
  <property name="human" value="true"/>
</bean>


public class Main {
  private String name;
  private Integer age;

  public void setName(String name) {
    this.name = name;
  }

  public void setAge(Integer age) {
    this.age = age;
  }

  public static void main(String[] args) {
     new ClassPathXmlApplicationContext("beans.xml")
        .getBean("myMain", Main.class);
  }
}

This is thrown

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myMain' defined in class path resource [beans.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'human' of bean class [pak.Main]: Bean property 'human' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?

Upvotes: 1

Views: 4895

Answers (1)

FGreg
FGreg

Reputation: 15330

Imagine my surprise that the answer is yes.

Enter InstantiationAwareBeanPostProcessor... or more specifically (and easier to implement) InstantiationAwareBeanPostProcessorAdapter.

This bean post processor is invoked before instantiation of the properties on a bean. The method you can override to achieve what you are trying to do is postProcessPropertyValues.

This method is given the information about it's bean and it's properties. You can then modify the bean's properties in any way you see fit. Specifically there is a PropertyValue.setOptional(boolean) method that looks like it would interest you.

Throwing together a quick example using your scenario, define a class as such:

MyInstantiationAwareBeanPostProcessor.java:

import java.beans.PropertyDescriptor;

import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.stereotype.Component;

@Component
public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        if (bean instanceof pak.Main) {
            for (PropertyValue pv : pvs.getPropertyValues()) {
                if ("human".equals(pv.getName())) {
                    pv.setOptional(true);
                }
            }
        }
        return pvs;
    }

}

Then make sure the following line is in your context somewhere:

<bean id="instantiationAwarePP" class="pak.MyInstantiationAwareBeanPostProcessor"/>

And you are golden.

P.S. You might also need to define <context:annotation-config /> in your context so that post-processors are automatically registered... but I can't recall if that's necessary.

Upvotes: 3

Related Questions