How to inject values to various constructors in spring - java

Hi I am new to spring technology.
I have a class called Employee as follows which has 2 constructors with different argument types.
I am able to inject values to one of the constructors as described in the xml file.
May I know how to inject value to other constructor as well using constructor injection.
I tried various possibilities but unable to figure out how to do it.
public class Employee {
private int eno ;
private String name ;
private double salary ;
private String desig ;
public Employee(int eno, String name) {
this.eno = eno;
this.name = name;
}
public Employee(double salary, String desig) {
this.salary = salary;
this.desig = desig;
}
public void showInjectedValues() {
System.out.println("Eno : " + eno);
System.out.println("name : " + name);
System.out.println("salary : " + salary);
System.out.println("desig : " + desig);
}
}
Trying to inject with spring.xml and the Java class for Injection is as follows:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class InjectionTest {
static ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springconfig.xml");
public static void main(String[] args) {
Employee employee = (Employee) applicationContext.getBean("employee");
employee.showInjectedValues();
}
}
applicationContext.xml is as follows:
<?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-3.2.xsd">
<bean id="employee" class="com.vidvaan.spring.Employee">
<constructor-arg value="2000" index="0" type="double" />
<constructor-arg value="team lead" index="1"
type="java.lang.String" />
</bean>
</beans>

Well, that is not possible. What you are asking is
call two constructors to create one object.
This doesn't make any sense. (Just read the above line again).
You can always place multiple objects of the same class on the spring context, calling a different constructor in each case.
<?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-3.2.xsd">
<bean id="otherEmployee" class="com.vidvaan.spring.Employee">
<constructor-arg value="100" index="0" type="int" />
<constructor-arg value="team lead" index="1"
type="java.lang.String" />
</bean>
<bean id="employee" class="com.vidvaan.spring.Employee">
<constructor-arg value="2000" index="0" type="double" />
<constructor-arg value="team lead" index="1"
type="java.lang.String" />
</bean>
</beans>
what you can do is create a constructor with all four arguments and pass null for objects which you don't want to initilize
or You can have a constructor with some arguments and others you can set through Field Injection <property name = ...>

You can create a another bean like this for different arguments:
<bean id = "employeeBean" class = "com.vidvaan.spring.Employee">
<constructor-arg type = "int" value = "2001"/>
<constructor-arg type = "java.lang.String" value = "Employee"/>
</bean>

Related

Defining additional placeholder/property only in beans.xml

I have a list of strings, that i would like to define in beans.xml.
<util:list id="myFractions" value-type="java.lang.String">
<value>#{ T(com.myapp.longname.verylong.WelcomeController).RED_FRACTION }</value>
<value>#{ T(com.myapp.longname.verylong.WelcomeController).BLUE_FRACTION }</value>
<value>#{ T(${my.prefix}).GREEN_FRACTION }</value>
</util:list>
It works fine, but each time I need to write the full qualified constant's name com.myapp.longname.verylong.WelcomeController. I would like to write it only once. One solution I have found is to replace it with a property like my.prefix so I can write only my short prefix instead of the real full path. But then I will need to pollute the global "namespace" with property that is only needed once. I would like to define a placeholder only for this list or at least only for this beans.xml file. I have already tried to define a property directly in beans.xml with PropertyPlaceholderConfigurer and it works, but then all my inital properties are not available anymore.
So how can I avoid to writing com.myapp.longname.verylong.WelcomeController each time in a list as a prefix and only define it once? Ideally something like
<util:list id="myFractions" value-type="java.lang.String">
<define-local-placeholder name="my.prefix" value="com.myapp.longname.verylong.WelcomeController" />
<value>#{ T(${my.prefix}).RED_FRACTION }</value>
<value>#{ T(${my.prefix}).BLUE_FRACTION }</value>
<value>#{ T(${my.prefix}).GREEN_FRACTION }</value>
</util:list>
Please give a try on this
<context:property-placeholder properties-ref="shorthandHelperConstants"/>
<util:properties id="shorthandHelperConstants">
<prop key="my.prefix">com.myapp.longname.verylong.WelcomeController</prop>
</util:properties>
<util:list id="myFractions" value-type="java.lang.String">
<value>#{ T(${shorthandHelperConstants['my.prefix']}).RED_FRACTION }</value>
<value>#{ T(${shorthandHelperConstants['my.prefix']}).BLUE_FRACTION }</value>
<value>#{ T(${shorthandHelperConstants['my.prefix']}).GREEN_FRACTION }</value>
</util:list>
Try defining your prefix in properties file and use it in your beans.xml as shown here:
Best ways to deal with properties values in XML file in Spring, Maven and Eclipses
and here
Using Variable Substitution from Configuration Files in Spring
Another solution is using SpEL
<property name="userCountry" value="#{'India'}" />
Spring Expression Language (SpEL) Example
Checkout out Constants. Which allows you to extract constants and values from classes, this way you only need to define your class onetime instead for each property.
<bean id="someConstants" class="org.springframework.core.Constants">
<constructor-arg value="your-class-name=here" />
</bean>
<bean id="myFractions" factory-bean="someConstants" factory-method="getValuesForSuffix">
<constructor-arg value="FRACTION" />
</bean>
This will expose all of your constants which end with FRACTION as a Set in your context. Without you needing to define each and every constant. You could also place this into a FactoryBean making it a little easier
public class FractionExporter extends AbstractFactoryBean<List<String>> {
#Override
public Class<List> getObjectType() {
return List.class;
}
protected List<String> createInstance() {
Constants constants = new Constants(YourClass.class);
return new ArrayList(constants.getValuesForSuffix("FRACTION"));
}
}
You could now define this FactoryBean in XML
<bean id="myFractions" class="FractionExporter" />
A solution is to implement a FactoryBean,
by extending the AbstractFactoryBean class:
package test;
import java.util.*;
import org.springframework.beans.factory.config.AbstractFactoryBean;
public class ConstantListFactoryBean extends AbstractFactoryBean<List<Object>>
{
private Class<?> targetClass;
private List<String> constantNames;
public void setTargetClass(Class<?> targetClass)
{
this.targetClass = targetClass;
}
public void setConstantNames(List<String> constantNames)
{
this.constantNames = constantNames;
}
#Override
public Class<List> getObjectType()
{
return List.class;
}
#Override
protected List<Object> createInstance() throws Exception
{
ArrayList<Object> list = new ArrayList<Object>();
for(String name : constantNames)
list.add(targetClass.getField(name).get(null));
return list;
}
}
Here is a sample beans.xml that uses the ConstantListFactoryBean:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="colors" class="test.ConstantListFactoryBean">
<property name="targetClass" value="test.Colors"/>
<property name="constantNames">
<list>
<value>RED</value>
<value>BLUE</value>
<value>GREEN</value>
</list>
</property>
</bean>
</beans>
And the sample class that holds the constants:
package test;
public class Colors
{
public static final String RED = "red";
public static final String BLUE = "blue";
public static final String GREEN = "green";
}
And finally, some code that shows that it works:
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test
{
public static void main(String[] args)
{
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
List colors = (List)context.getBean("colors");
System.out.println(colors);
}
}
Output:
[red, blue, green]
Another simpler way is to create an object that wrap a class instance (e.g call ClassReflector). Then define a method on it to return the value of the constant field.
For example :
import org.springframework.util.ReflectionUtils;
public class ClassReflector {
private Class<?> clazz;
public ClassReflector(String className) throws Exception {
this.clazz = Class.forName(className);
}
public String getFieldVal(String fieldName) throws Exception {
return (String) ReflectionUtils.getField(clazz.getField(fieldName), null);
}
}
Then in the XML, use this method to get the value of different constant fields.
<bean id="cf" class="com.myapp.longname.verylong.ClassReflector">
<constructor-arg value="com.myapp.longname.verylong.WelcomeController" />
</bean>
<util:list id="myFractions" value-type="java.lang.String">
<value>#{cf.getFieldVal('RED_FRACTION')}</value>
<value>#{cf.getFieldVal('BLUE_FRACTION')}</value>
<value>#{cf.getFieldVal('GREEN_FRACTION')}</value>
</util:list>
As per comments & question, as your requirement is quite minimal , I would define one general purpose map & put key-value there & then access it inside your list:
<bean id="map" class="java.util.HashMap">
<constructor-arg>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="my.prefix" value="com.myapp.longname.verylong.WelcomeController" />
</map>
</constructor-arg>
</bean>
<util:list id="myFractions" value-type="java.lang.String">
<value>#{ T(${map['my.prefix']}).RED_FRACTION }</value>
<value>#{ T(${map['my.prefix']}).BLUE_FRACTION }</value>
<value>#{ T(${map['my.prefix']}).GREEN_FRACTION }</value>
</util:list>
Benefit I can see here is you can put as many key-values in this map if you have multiple prefixes / properties of this kind & you want to access it in different beans without defining any global scoped beans.
Please let me know if this is of any help.

Java Spring AutoWire byName

I'm currently doing novice tutorial about Spring, it seems i have everything the same like person who makes this tutorial but i keep getting errors NullPointerException , could someone help me?
There is section in my xml that is commented if you uncomment it, program will work. But without it, when I try to use autowire byName it doesn't work.
Main Class:
public class MainSpring {
public static void main(String[] args) {
ApplicationContext context= new ClassPathXmlApplicationContext("konfiguracja.xml");
SomeBean bean = context.getBean("SomeBean",SomeBean.class);
System.out.println(bean.getInjectedBean1().getName());
System.out.println(bean.getInjectedBean2().getName());
}
}
InjectedBean Class
package springTutorial;
public class InjectedBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
SomeBean Class
import org.springframework.beans.factory.annotation.Autowired;
public class SomeBean {
private InjectedBean InjectedBean1;
private InjectedBean InjectedBean2;
public InjectedBean getInjectedBean1() {
return InjectedBean1;
}
public void setInjectedBean1(InjectedBean injectedBean1) {
this.InjectedBean1 = injectedBean1;
}
public InjectedBean getInjectedBean2() {
return InjectedBean2;
}
public void setInjectedBean2(InjectedBean injectedBean2) {
this.InjectedBean2 = injectedBean2;
}
}
Configuration.xml file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
<bean id="InjectedBean1" class="springTutorial.InjectedBean" >
<property name="name" value="asasa" >
</property>
</bean>
<bean id="InjectedBean2" class="springTutorial.InjectedBean" >
<property name="name" value="vbvb">
</property>
</bean>
<bean id="SomeBean" class="springTutorial.SomeBean" autowire="byName" >
<!-- <property name="injectedBean1"> -->
<!-- <ref bean="InjectedBean1"/> -->
<!-- </property> -->
<!-- <property name="injectedBean2"> -->
<!-- <ref bean="InjectedBean2"/> -->
<!-- </property> -->
</bean>
</beans>
As per Configuration.xml ; InjectedBean1,InjectedBean2 are not injected with any values.
So basically InjectedBean1 and InjectedBean2 are null.
On null you are trying for get Operation which is leading to null pointer exception.
Adding ref to InjectedBean1 and InjectedBean2 will solve problem.
<bean id="SomeBean" class="com.stackoverflow.SomeBean" autowire="byName">
<property name="injectedBean1">
<ref bean="inject1" />
</property>
<property name="injectedBean2">
<ref bean="inject2" />
</property>
</bean>

Spring Autowiring can't see context beans unless component scanning defined on base package

I currently have my spring application setting using #SpringBootApplication, everything is working however, I cannot inject beans defined in an xml using autowiring.
If I define the dependency injection through the xml configuration for the injection works like
<bean id="dao" class="com.elevations.dao.Dao">
<property name="dataSource" ref="dataSource"/>
</bean>
However, if I denote my Application with #ComponentScanning( "elevations" ), (elevations is my base package) autowiring works, however then my controller end points stop working. Why is this the case?
My application com.elevations.Application is defined as
#SpringBootApplication
public class Application
{
public static void main( String[] args )
{
SpringApplication.run( Application.class, args );
ApplicationContext context = new ClassPathXmlApplicationContext( "context.xml" );
}
}
xml configuration defined as
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.elevations.dao"/>
<!--postgresql jdbc bean-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" autowire="byType">
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql://localhost:5432/elevationdb"/>
<property name="username" value="postgres"/>
<property name="password" value=""/>
</bean>
<bean id="dao" class="com.elevations.dao.Dao"/>
</beans>
The test class I'm trying to autowire com.elevations.dao.Dao
#Component
public class Dao
{
private DataSource m_dataSource;
#Autowired
public void setDataSource( DataSource dataSource )
{
m_dataSource = dataSource;
}
}
Controller com.elevations.controllers.ApplicationController
#Controller
public class ApplicationController
{
#RequestMapping( value = "/elevations", method = RequestMethod.GET )
public String pageGet()
{
return "elevationMaps";
}
#RequestMapping( value = "/elevationData", method = RequestMethod.GET )
#ResponseBody
public LatLng mapGet( #RequestParam( "bounds" ) String bounds, #RequestParam( "diameter") String diameter )
{
JsonParser parser = new JsonParser();
JsonElement viewBounds = parser.parse( bounds );
return new LatLng( 1, 0 );
}
}
If you want to use xml add #ImportResource to your application class.
#ImportResource("context.xml")
#SpringBootApplication
public class Application {
public static void main( String[] args ) {
SpringApplication.run( Application.class, args );
}
}
However I would strongly suggest ditching your xml, simply add an application.properties to src/main/resources and add the following properties.
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/elevationdb
spring.datasource.username=postgres
spring.datasource.password=
Then remove #ImportResource from your class and restart.

Null pointer exception while using simple example of jdbcTemplate in Spring

I am learning JdbcTemplate with Spring and when i am running my java application i am getting null pointer on this line:
return jdbcTemplate.queryForMap(sql, id);
Please find the whole class below:
public class CustomerDaoImpl implements CustomerDAO{
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource ds) {
dataSource = ds;
}
public Map getCustomerById(String id) {
String sql = "SELECT * FROM CUSTOMER WHERE CUST_ID = ?";
return jdbcTemplate.queryForMap(sql, id);
}
Please find my SpringContext file below:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- Scans within the base package of the application for #Components to
configure as beans -->
<context:property-placeholder location="classpath:db.properties" />
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
<bean id="customerDAO" class="com.tuto.dao.impl.CustomerDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
Please find my Main method below:
public class JdbcTemplateApp {
public static void main(String[] args) {
// if you have time,
// it's better to create an unit test rather than testing like this :)
ApplicationContext context = new ClassPathXmlApplicationContext(
"integration.xml");
CustomerDAO customerDAO = (CustomerDAO) context.getBean("customerDAO");
Map customerA = customerDAO.getCustomerById("1");
System.out.println("Customer A : " + customerA);
}
}
Please find below my exception in eclipse console:
INFO: Loaded JDBC driver: oracle.jdbc.driver.OracleDriver
Exception in thread "main" java.lang.NullPointerException
at com.tuto.dao.impl.CustomerDaoImpl.getCustomerById(CustomerDaoImpl.java:23)
at com.tuto.main.JdbcTemplateApp.main(JdbcTemplateApp.java:23)
Any idea why it is null please?
Thanks in advance for any help
Change setDataSource to:
public void setDataSource(DataSource ds) {
jdbcTemplate = new JdbcTemplate(ds);
}
You can extend org.springframework.jdbc.core.support.JdbcDaoSupport in your DAOImpl Class and use inherited getJdbcTemplate() method to get the jdbcTemplate. in this case you can remove the variable jdbcTemplate from your class
You never initialize jdbcTemlpate instance variable. In this case it's initialized as null and you're getting NullPointerException (you can't call methods on null). You need to initialize jdbcTemplate by yourself.
To quote Java Language Specification:
Each class variable, instance variable, or array component is
initialized with a default value when it is created [...] For all
reference types the default value is null.

NPE when injection class in spring

I have problem when injection class.
In my configuration I have one class which setting level of login and then one variable for setting level:
<bean id="config" class="..." init-method="init">
<property name="log4jConfig" ref="log4j" />
<property name="levelLogging" value="9" />
</bean>
and code:
public void setlevelLogging(Integer level) {
if (level == null) {
set0();
} else {
switch (level) {
case 0:
set0();
break;
case 9:
set9();
}
}
this.level = level;
}
private void set0() {
log4jConfig.setLoggerLevel("org.springframework.ws.server.MessageTracing", "OFF");
log4jConfig.setLoggerLevel("org.app", "INFO");
log4jConfig.setLoggerLevel("org.springframework.ws.client.MessageTracing", "OFF");
}
public void setLog4jConfig(Log4jConfigJmx log4jConfig) {
this.log4jConfig = log4jConfig;
}
when I want to run this code I got NPE because log4jConfig is null when is setlevelLogging calling.
How I can solve this exception ?
now I exclude this class from properties and creating new class in configClass:
Log4jConfigJmx log4jConfig = new Log4jConfigJmx()
but I dont think this is good idea
EDIT:
I try example below but I have still some problem:
first I got this exception:
[ERROR] java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
because I am using transactional and AOP so I add default constructor to the class so I have two of them:
public Config() {
}
public Config(Log4jConfigJmx log4jConfig, Integer level) {
this.log4jConfig = log4jConfig;
setlevelLoggin(level);
}
setlevelLogging ...
<bean id="config" class="..." init-method="init">
<constructor-arg index="0" ref="log4j" />
<constructor-arg index="1" value="9" />
</bean>
but now I have still NPE
pls help
You should place the code from the method setLoggingLevel in the init method.
Only leave this.level = level so it is a plain setter.
The init method is called after all the properties have been set.
----EDIT after comment----
After you comment I suggest you use a constructor:
public Class(Integer level, Log4jConfigJmx log4jConfig){
this.log4jConfig = log4jConfig;
setLevelLogging(level);
}
<bean id="config" class="..." init-method="init">
<constructor-arg index="0" value="9"/>
<constructor-arg index="1" ref="log4j"/>
</bean>
You can use constructor-args like this:
<bean id="config" class="..." init-method="init">
<constructor-arg><ref bean="anotherExampleBean"/></constructor-arg>
<constructor-arg type="int"><value>9</value></constructor-arg>
</bean>
Then your constructor can initialise both variables whilst keeping your setter methods in place.
E.g.
public MyClass(Log4jConfigJmx log4jConfig, Integer level) {
this.log4jConfig = log4jConfig;
this.setLevelLogging(level);
}
You can also try making the log4jConfig autowired.
Change your bean config back to:
<bean id="config" class="..." init-method="init">
<property name="levelLogging" value="9" />
</bean>
And then simply annotate a field in your class to be autowired:
#Autowired
private Log4jConfigJmx log4jConfig;
Add this at the top of your spring context to enable annotations:
<context:annotation-config />

Categories