Autowire based on a condition when xml bean configuration is used - spring

I am working on Spring auto wiring using spring configuration file(xml configuration). I want to inject beans based on a condition.
Let me go into details.
There are two classes 'EmailSender' and 'SmsSender' which implement the interface IMessageSender. Beans are configured for both classes in the configurations file.
I have another class SenderUser which has a instance variable of type IMessageSender in it.
package org.pradeep.core;
public class SenderUser {
private String name;
private String Type;
private IMessageSender msg;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return Type;
}
public void setType(String type) {
Type = type;
}
public IMessageSender getMsg() {
return msg;
}
public void setMsg(IMessageSender msg) {
this.msg = msg;
}
}
I want to inject IMessageSender into bean of SenderUser based on the value of SenderUser.getType(). That means first SernderUser.type should be set and then based on it's value (if value is 'email' then bean with the name 'email' should be wired else bean with the name 'sms' should be wired.) msg should be wired.
Please help me resolve the issue.

I believe the best approach is to implement a BeanFactory for SenderUser. Take a look at this post for an idea.

Interfaces can't be injected, they aren't beans/instances.

you may use SPEL
<bean id="user" class="SenderUser" autowire="byType">
<property name="type" value="email"/>
<property name="msg" value="#{type != null && type == 'email' ? email : sms}"/>
</bean>
<bean id="email" class="EmailSender"/>
<bean id="sms" class="SmsSender"/>

Related

populate enum fields with application.properties and java configuration

with spring xml configuration, it is possible to define the following in app context xml to populate the status field in the TestEnum:
app context xml
<bean id="blue" class="com.example.demo.test.TestEnum" factory-method="valueOf">
<property name="status" value="${testnum.blue.status}"/>
<constructor-arg>
<value>BLUE</value>
</constructor-arg>
</bean>
<bean id="red" class="com.example.demo.test.TestEnum" factory-method="valueOf">
<property name="status" value="${testnum.red.status}"/>
<constructor-arg>
<value>RED</value>
</constructor-arg>
</bean>
enum class
public enum TestEnum {
BLUE,
RED;
private String status;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
mystatus.properties
testnum.blue.status=good
testnum.red.status=bad
Is this possible with spring boot using application.properties + java configuration without xml?
Yes, a simple environment configuration class (annotated with #ConfigurationProperties for example), will map and do the conversion of your enum automatically.
Example:
#Configuration
#Validated
#ConfigurationProperties(prefix = "my.property.path")
public class TestConfiguration{
public enum TestEnum {
BLUE, RED
}
#NotNull
private TestEnum type;
public TestConfiguration() {
}
public #NotNull TestEnum getType() {
return this.type;
}
public void setType(#NotNull TestEnum type) {
this.type = type;
}
}

Spring beans used and injected if bean class does not have any getter/setter methods?

Here it is stated that Spring beans can be instantiated, configured and injected even if bean class does not have any getter/setter methods.
Is that true?
Could you give an example (or a link to such)?
The reason Spring managed objects are referred to as beans is because
in the very early versions, Spring was intended only for use with
JavaBeans. That is no longer the case of course: Spring can manage
just about any object, even if it doesn’t have JavaBean type
characteristics such as default constructors or mutator methods
(getters and setters). None the less, the term ‘Spring beans’ has
stuck.
I get these exceptions:
org.springframework.beans.factory.BeanCreationException caused by org.springframework.beans.NotWritablePropertyException: Invalid property 'name' of bean class [com.my.pkg1.Student]: Bean property 'name' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
#WebListener
public class MyListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
String metadata = "mybean.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(metadata );
Student student = context.getBean("student", Student.class);
System.out.println(student.age);
System.out.println(student.name);
}
}
and
public class Student {
String name;
int age;
// public String getName() {
// return name;
// }
//
// public void setName(String name) {
// this.name = name;
// }
//
// public int getAge() {
// return age;
// }
//
// public void setAge(int age) {
// this.age = age;
// }
}
and mybean.xml bean definition XML file for Student class:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.my.pkg1.Student">
<property name="name" value="John"></property>
<property name="age" value="23"></property>
</bean>
</beans>
I feel <property name="name" value="John"> is not valid without getters and setters...
since your bean definition sets the name and age properties, a setter is needed to assign those values.
You could also customize the bean using a constructor (<constructor-arg>) or a builder function (factory-method).
Here's an intro to these different construction methods: https://www.baeldung.com/spring-xml-injection

How #Autowired Work using Reflection and how invoke appropriate Setter Method

My Main Problem is that i can use Reflection API and i can invoke the setter method in my User bean class.But I have Only have UserBean objects. I does not know which setter method to invoke.i want extract all information in my User bean Object and the invoke appropriate Method using Reflection.
//-->This My User Bean Class
public class User {
private String name;
private int age;
private String getName() {
return name;
}
private void setName(String name) {
this.name = name;
}
private int getAge() {
return age;
}
private void setAge(int age) {
this.age = age;
}
void callMethod() { System.out.println("Name :- "+name+" Age :-"+age); }
#Override
public String toString() { return "User [name=" + name + ", age=" + age + "]";}
}
//--->This My Main Method
public class TestCase {
public static Object getBean(String beanClassName) throws Exception
{
Class klass = Class.forName(beanClassName);
Object obj = klass.newInstance();
Method[] b = klass.getDeclaredMethods();
String MethodName1 = "setName";
String MethodName2 = "setAge";
String name ="sanket";
int age = 21;
for(int i=0;i<b.length;i++)
{
if(b[i].getParameterTypes().toString().equals(MethodName1))
{
b[i].setAccessible(true);
b[i].invoke(obj,name);
}
if(b[i].getName().equals(MethodName2))
{
b[i].setAccessible(true);
b[i].invoke(obj,age);
}
}
return obj;
}
public static void main(String ars[]) throws Exception
{
Object obj1 = getBean("www.Fouth.User");
System.out.println(obj1.toString());
}
}
I my case I can invoke the setter method manually because i can invoke the setter method bases of method name.
I want Extract information in User Bean Class, and identify on the bases of value which setter to invoked.
In spring #AutoWired is does that.How they will identify which setter to invoke and inject dependency.
The Spring identifies which setters to inject dependencies by figuring out which setters are annotated with #Autowired , #Inject , #Resources etc.
Then it figures out which value to inject into the setter by checking the type of the setter arguments and injects the bean that has the same type.
If multiple beans have the same type, it then check the bean name. It will inject the bean that has the same name as configured in the injection point using #Qaulifer or #Resources.
If there are still multiple beans that satisfy to inject , NoUniqueBeanDefinitionException happens.
If there are no beans that satisfy to inject, NoSuchBeanDefinitionException happens.

Implement interface in runtime Java EE and Spring

I would like to implement the interface at runtime based on variable.
Example :
Class A implements interface1 {
public getValue() {}
}
Class B implements interface1 {
public getValue() {}
}
So I would like to have variable sitting in configuration..., for example ClasstoImplement=A
So, if ClasstoImplement=A, then I need to call Class A.getValue()
If ClasstoImplement=B, then I need to call Class B.getValue() at runtime. And I should be able to change value of ClasstoImplement at runtime.
My application is Spring based and runs in Tomcat.
Can someone please help me to find out if there is any way??
There are many possible solutions. The one of them is to use org.springframework.aop.target.HotSwappableTargetSource.
take a look at implementation that could be considered:
public class CustomSwappable<T> implements Interface1 {
private HotSwappableTargetSource targetSource;
private String key;
private Map<String, T> swappableBeans;
#PostConstruct
private void init() {
targetSource = new HotSwappableTargetSource(swappableBeans.values().iterator().next()); // first is the default
}
// you need to track changes in config and call this method if any modifications were done
public void configChanged(String key, String value) {
if (!this.key.equals(key)) {
return;
}
if (!swappableBeans.containsKey(value)) {
return;
}
targetSource.swap(swappableBeans.get(value));
}
#Override
public String getValue() {
return ((Interface1)targetSource.getTarget()).execute();
}
#Required
public void setConfigurationKey(String key) {
this.key = key;
}
#Required
public void setSwappableBeans(Map<String, T> swappableBeans) {
this.swappableBeans = swappableBeans;
}
}
and bean declaration should as follows:
<bean id="Interface1Swappable" class="path.to.CustomSwappable">
<property name="configurationKey" value="swappableKey"/>
<property name="swappableBeans">
<util:map value-type="path.toInterface1">
<!-- first is default -->
<entry key="classA">
<bean class="path.to.class.A"/>
</entry>
<entry key="classB">
<bean class="path.to.class.B"/>
</entry>
</util:map>
</property>
</bean>

Where I did wrong? Why PropertEditorSupport is not working for me?

Why I am getting action in PropertyEditorSupport? Could anyone please help me here because i am new in Spring. Below is the error report
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cont' defined in class path resource [propertyEdit.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type [java.lang.String] to required type [Phone] for property 'phone'; nested exception is java.lang.IllegalArgumentException: 888-555-1212
Caused by: org.springframework.beans.TypeMismatchException: Failed to convert property value of type [java.lang.String] to required type [Phone] for property 'phone'; nested
exception is java.lang.IllegalArgumentException: 888-555-1212
Caused by: java.lang.IllegalArgumentException: 888-555-1212
at java.beans.PropertyEditorSupport.setAsText(Unknown Source)
at org.springframework.beans.TypeConverterDelegate.doConvertTextValue(TypeConverterDelegate.java:326)
at org.springframework.beans.TypeConverterDelegate.doConvertValue(TypeConverterDelegate.java:305)
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:192)
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:138)
at org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:380)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1111)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:861)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:421)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:251)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:156)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:248)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:160)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:287)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:352)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:91)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:75)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:65)
at ShowContact.main(ShowContact.java:9)
I have used java.beans.PropertyEditorSupport in below way
public class PhoneEditor extends java.beans.PropertyEditorSupport{
public void setAsTest(String textValue)
{
String stripped = stripNonNumeric(textValue);
String areaCode=stripped.substring(0,3);
String prefix=stripped.substring(3,6);
String number=stripped.substring(6);
Phone phone=new Phone(areaCode,prefix,number);
setValue(phone);
}
private String stripNonNumeric(String original)
{
StringBuffer allNumeric = new StringBuffer();
for(int i=0; i<original.length(); i++)
{
char c=original.charAt(i);
if(Character.isDigit(c))
{
allNumeric.append(c);
}
}
return allNumeric.toString();
}
}
My Config file is below
<bean name="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="Phone">
<bean id="Phone" class="PhoneEditor">
</bean>
</entry>
</map>
</property>
</bean>
<bean id="cont" class="Contact">
<property name="name" value="Dhirendra"/>
<property name="phone" value="888-555-1212" />
</bean>
</beans>
Phone Class is below
public class Phone {
private String areaCode;
private String prefix;
private String number;
public Phone(){}
public Phone(String areaCode, String prefix, String number)
{
this.areaCode=areaCode;
this.prefix=prefix;
this.number=number;
}
public String getPhoneNumber()
{
return prefix+"-"+areaCode+"-"+number;
}
}
I am calling in the below way
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.beans.factory.config.CustomEditorConfigurer;
public class ShowContact {
public static void main(String[] args)
{
ApplicationContext context = new ClassPathXmlApplicationContext("propertyEdit.xml");
Employee employee=(Employee)context.getBean("cont");
employee.PrintEmpDetails();
}
}
Below is my Contact class which is calling
public class Contact implements Employee {
private Phone phone;
private String name;
public void setPhone(Phone phone) {
// TODO Auto-generated method stub
this.phone=phone;
}
public void setName(String name) {
// TODO Auto-generated method stub
this.name=name;
}
public void PrintEmpDetails()
{
System.out.println("Name of Employee :"+ name);
System.out.println("Contact Number of Employee :"+ phone.getPhoneNumber());
}
}
In PhoneEditor, you've implemented setAsTest, rather than overriding setAsText. As a result, Spring is calling the setAsText implementation in PropertyEditorSupport, which throws the exception.
This is why you should always use #Override annotations, and set your compiler to at least report a warning if you don't do it.
The problem is that you have a typo in your class. It's setAsText, not setAsTest:
#Override
public void setAsText(String textValue) throws IllegalArgumentException {
final String stripped = stripNonNumeric(textValue);
final String areaCode=stripped.substring(0,3);
final String prefix=stripped.substring(3,6);
final String number=stripped.substring(6);
final Phone phone=new Phone(areaCode,prefix,number);
setValue(phone);
}
(always use #Override, as skaffman suggested)

Resources