Spring Java Config | How to fetch IndexedArgumentsValue from BeanDefinition - spring

Consider the following example :
Config Class
#Configuration
public class MyTestConfig {
#Value("${const.arg.a}") String constArgA;
#Value("${const.arg.b}") String constArgB;
#Bean
public static PropertyPlaceholderConfigurer properties(){
SystemPropertyPlaceholderConfigurer propertyConfigurer = new SystemPropertyPlaceholderConfigurer();
propertyConfigurer.setLocation( "test.properties" );
propertyConfigurer.setPropertyPrefixValue("TEST");
propertyConfigurer.setIgnoreUnresolvablePlaceholders( true );
return propertyConfigurer;
}
#Bean
public SpringTest springTest(){
return new SpringTest(constArgA, constArgB);
}
}
Bean
public class SpringTest {
private String constArgA;
private String constArgB;
public SpringTest(String constArgA, String constArgB){
this.constArgA = constArgA;
this.constArgB = constArgB;
System.out.println("constArgA " + constArgA);
System.out.println("constArgB " + constArgB);
}
public void sayHello(){
System.out.println("Hello, I hope this works");
System.out.println("constArgA " + constArgA);
System.out.println("constArgB " + constArgB);
}
}
SystemPropertyPlaceholderConfigurer is a extension of the PropertyPlaceholderConfigurer in which we try to manipulate some of the Bean Constructor arguments.
For my use case I need to access the constructor arguments but I see a empty list if I obtain a BeanDefinition from ConfigurableListableBeanFactory .
To be precise all of the following for the Bean 'SpringTest' are empty :
beanDefinition.getPropertyValues();
beanDefinition.getConstructorArgumentValues().getIndexedArgumentValues();
beanDefinition.getConstructorArgumentValues().getGenericArgumentValues();
I expect to receive constArgA and constArgB in the ConstructorArgumentValues.
Now if I replace Java Based Config with a Xml Based Config 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-2.0.xsd">
<bean
class="com.abc.SystemPropertyPlaceholderConfigurer">
<property name="location">
<value>test.properties</value>
</property>
<property name="propertyPrefixValue">
<value>TEST</value>
</property>
</bean>
<bean id="springTest" class="com.abc.SpringTest" >
<constructor-arg index="0" type="java.lang.String" value="${const.arg.a}" />
<constructor-arg index="1" type="java.lang.String" value="${const.arg.b}" />
</bean>
</beans>
Now if I try to access my BeanDefinition I get the 2 constructor arguments in :
beanDefinition.getConstructorArgumentValues().getIndexedArgumentValues()
Can you please help me to understand what is the difference in these 2 configuration ways and if I want to use a Java Based Config , how do I access the constructor arguments on my beans ? I basically need to inject my properties from a property file as constructor arguments and need to perform some processing on it through my implementation of PropertyPlaceholderConfigurer.

In #configuration class, I usually do it like this
#Configuration
#PropertySource("classpath:/test.properties")
public class MyTestConfig {
#Resource
private Environment env;
#Bean
public SpringTest springTest(){
return new SpringTest(env.getRequiredProperty("const.arg.a"), env.getRequiredProperty("const.arg.b"));
}
}

Related

Cannot get value from properties using #Value in Spring

I have a project template using spring and I need values from properties file, but when I run the program the value is null. Here is my code:
Properties file contains: TOWN=Cluj
public class BaseTest {
#Value("${TOWN}")
public String stringValue;
#BeforeTest
public void beforeTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = context.getBean("studentBean", Student.class);
student.displayName();
student.displayTown();
System.out.println(stringValue); // -> this is null
}
}
Beans file :
<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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:property-placeholder location="classpath:stable.properties"/>
<context:component-scan base-package="com.base" />
<bean name="studentBean" class="com.model.Student">
<property name="name" value="MyName"/>
<property name="town" value="${TOWN}"/>
</bean>
</beans>
public class Student {
private String name;
private String town;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public String getTown() {
return town;
}
public void setTown(String town) {
this.town = town;
}
public void displayName(){
System.out.println("Name: " + name);
}
public void displayTown(){
System.out.println("Town: " + town);
}
}
In BaseTest when displayTown() method is called the value from properties file works, but if a try to use the value as a variable in BaseTest the values is null.
Could you help me please ?
As you have confirmed BaseTest is just a normal class. Values wont be injected automatically.
In xml
<context:property-placeholder location="classpath:stable.properties"/>
It automatically configures PropertyPlaceholderConfigurer, which replaces the ${} placeholders, which are resolved against a specified properties file.
<context:component-scan base-package="com.base" />
Scans the classpath for annotated components that will be
auto-registered as Spring beans. By default, the Spring-provided
#Component, #Repository, #Service, #Controller, #RestController,
#ControllerAdvice, and #Configuration stereotypes will be detected.
<bean name="studentBean" class="com.model.Student">
<property name="name" value="MyName"/>
<property name="town" value="${TOWN}"/>
</bean>
For Student class name and town fields will be automatically picked from property file
Please note this will happen only for Student class.
What you are trying is to get value in BaseTest class which is just a class.
Either Define a bean to inject property for BaseTest class like this
<bean name="baseTestBean" class="classpath.BaseTest">
<property name="stringValue" value="${TOWN}"/>
</bean>
you wont need #Value("${TOWN}") in class
or
add any one configuration and it will automatically scan your class provided its under com.base package

My autowired DataSource is returning null in Spring, how come?

I'm trying to set my dataSource to get a connection but it is returning null on conn=dataSource.getConnection();. Here is the relevant code from my DAO:
#Autowired
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
#Override
public Acronym findByAcronymId(int acronymId) {
String sql = "SELECT * FROM acronym_table WHERE acronymId = ?";
Connection conn = null;
try {
conn = dataSource.getConnection();
Here is the database bean:
<?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 = "dataSource"
class = "org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/db_name?useSSL=false" />
<property name="username" value="username" />
<property name="password" value="password" />
</bean>
</beans>
Here's the acronym bean:
<?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 = "acronymDAO" class ="com.user.dao.JdbcAcronymDAO">
<property name = "dataSource" ref ="dataSource"/>
</bean>
</beans>
Try to move your #Autowired annotation on the field itself, something like this:
#Autowired
private DataSource dataSource;
Or to put it on constructor level, instead of putting it on setter level.
I believe that the data source bean is not being auto-wired because you're using it on setter level which never gets called.
Add this line to your spring context xml
<context:annotation-config/>
So that it can scan your classes for the #Autowired annotation
The problem was that I had an extra bean. Once I deleted the acronym bean and setup #Autowired it worked correctly
I'm going to add this here as this question comes so far up the google search results!
This link was invaluable for helping me solve this:
https://www.moreofless.co.uk/spring-mvc-java-autowired-component-null-repository-service/
To summarise, something somewhere is probably instantiating with a new(). If a service is using an autowired property, the service must be autowired as well.
#Controller
public class Controller {
// !!!!!! This won't work!
// !!!!!! Autowire the service!
#GetMapping("/brokenexample")
public String brokenExample() {
MyService my = new MyService();
my.doStuff();
}
#Autowired
MyService service;
#GetMapping("/example")
public String example() {
service.doStuff();
}
}
#Service
public class MyService() {
#Autowired
MyRepository repo;
public void doStuff() {
repo.findByName( "foo" );
}
}
#Repository
public interface MyRepository extends SomeRepository<My, Long> {
List<My> findByName( String name );
}

aop with spring 2.5 without Aspect J

I am working in a old project with spring 2.5.Application context starts with
.
I need to implment logging using aop I need to log each and every class's method.
tried this link :
http://forum.spring.io/forum/spring-projects/aop/4769-apply-jdkregexpmethodpointcut-to-multiple-beans-how.But didnt work.
and some more options.(but i felt this is taking me some where)
Also, I cant use xsd so i cant use aop namespace.i cant use aspect j as well
Please guide me on how can i achieve this what should be point cut as I have tried . and * as pattern and bean names with the point cut.
spring.xml
<?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="debugInterceptor" class="test.DebugInterceptor" />
<bean id="testBean" class="test.TestBean" />
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*" />
<property name="interceptorNames">
<list>
<value>debugInterceptor</value>
</list>
</property>
</bean>
</beans>
TestBean
package test;
public class TestBean {
public void foo() {
System.out.println("foo");
}
void bar() {
System.out.println("bar");
}
}
DebugInterceptor
package test;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class DebugInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Before: invocation=[" + invocation + "]");
Object rval = invocation.proceed();
System.out.println("Invocation returned");
return rval;
}
}
AopTest
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopTest {
public static void main(String[] args) {
ApplicationContext ap = new ClassPathXmlApplicationContext("classpath:spring.xml");
TestBean bean = (TestBean)ap.getBean("testBean");
bean.foo();
bean.bar();
}
}

How can we switch between different Implementations in Spring Context XML with an Boolean?

How can we switch between different Implementations in Spring Context XML with an Boolean?
for example:
<bean id="detailsController" class="com.something.detailsController" >
if true then
<property name="dao" ref="firstDao"/>
else
<property name="dao" ref="secoundDao"/>
I know in Spring3 we can work with profiles
You could do that by modifying your Java code and use Spring EL together with ApplicationAware and InitializingBean.
public class DetailsController implements ApplicationContextAware, InitializingBean {
private DetailsControllerDAO dao;
private String daoName;
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void afterPropertiesSet() {
dao = applicationContext.getBean(daoName);
}
public void setDaoName(String daoName) {
this.daoName = daoName;
}
}
In XML:
<bean id="detailsController" class="com.something.detailsController">
<property name="daoName" value="#{myCondition ? 'firstDao' : 'secondDao'}" />
</bean>
Of course, this solution has the disadvantage to add dependency to Spring code in your controller. To avoid that, you could move that code in a proxy class, as described by Guillaume Darmont.
I dont think this can be done at the XML level.
Spring really cannot do that. See the bean lifecycle. Been classes are created, than properties are injected and than afterPropertiesSet() or #PostConstructor methods are invoked. Of course when I omit lazy initialized beans.
But if you want for testing etc. and so you need just the firstDao or the secondDao in your application at the sametime that depends just on your settings, you can use a bean factory. The bean factory creates your bean as you want. I also use it for to split development environment, test environment and production environment.
package com.dummyexample.config;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Factory bean to create real or test dao.
* The result depends on realDaoEnabled configuration parameter.
*
* #author Martin Strejc
*/
#Configuration
public class DaoBeanFactory {
// mapping to servlet context configuration
#Resource(mappedName = "realDaoEnabled")
private Boolean realDaoEnabled = true;
// TestDao extends or implements Dao
#Autowired
private TestDao testDao;
// ProdDao extends or implements Dao
#Autowired
private ProdDao prodDao;
public DaoBeanFactory() {
}
#Bean(name="dao")
public Dao getDao() {
if(realDaoEnabled) {
return prodDao;
}
return testDao;
}
}
Since your DAOs are exchangeable, they inherits the same type (abstract class or interface). Thus you can write a RoutingDetailsControllerDAO.
Let's say that your common interface is named DetailsControllerDAO, with two methods getDetails and getMoreDetails inside, the code would be :
public class RoutingDetailsControllerDAO implements DetailsControllerDAO {
private DetailsControllerDAO firstDAO;
private DetailsControllerDAO secondDAO;
protected DetailsControllerDAO getDAOToUse() {
return YOUR_BOOLEAN_CONDITION ? firstDAO : secondDAO;
}
#Override
public Details getDetails() {
return getDAOToUse().getDetails();
}
#Override
public Details getMoreDetails() {
return getDAOToUse().getMoreDetails();
}
// Insert firstDAO and secondDAO setters below
...
}
Your Spring XML config is now :
<bean id="detailsController" class="com.something.detailsController" >
<property name="dao" ref="routingDetailsControllerDAO"/>
</bean>
<bean id="routingDetailsControllerDAO" class="com.something.RoutingDetailsControllerDAO">
<property name="firstDao" ref="firstDao"/>
<property name="secondDao" ref="secondDao"/>
</bean>
Few possibilities:
You can either use profiles (<beans profiles="profileOne">).
You can use FactoryBean to create the correct DAO
You can use SPeL
The last one is the easiest:
<bean id="detailsController" class="com.something.detailsController">
<property name="dao" ref="#{condition ? 'firstDao' : 'secondDao'}" />
</bean>
Of course you can load bean name from properties file via property configurer:
<bean id="detailsController" class="com.something.detailsController">
<property name="dao" ref="${bean.name.from.properties.file}" />
</bean>

Spring #Autowired not working

I have some problems wth autowire annotation. My app looks like this:
Here is controller:
#Controller
public class MyController {
#Autowired
#Qualifier("someService")
private SomeService someService;
....
}
It's a service layer:
public interface SomeService {
...
}
#Service
public class SomeServiceImpl implements SomeService{
#Autowired
#Qualifier("myDAO")
private MyDAO myDAO;
....
}
And DAO layer:
public interface MyDAO{
....
}
#Repository
public class JDBCDAOImpl implements MyDAO {
#Autowired
#Qualifier("dataSource")
private DataSource dataSource;
....
}
This is a app-service.xml file:
....
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/jdbc.properties" />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<bean id="SomeService" class="com.service.SomeServiceImpl" />
<bean id="myDAO" class="com.db.JDBCDAOImpl" />
So... When I'm launching a web-app, MyController Autowires correctly (the someService field correctly injected by SomeServiceImpl class object), but myDAO feild of someService has null value (not injected properly).
Could you help me to find a problem?
P.S. Its interesting, but when I'm changing a "bean id" from myDAO to some another (e.g. myDAO2), system gives me an error, that injecting could not be done, because bean myDAO doesn't exist. So, Spring make an injection, but where it is? And why it's not work correctly?
I found the solution. As Javi said (thanks a lot for you, Javi), I have to annotate DAO and Service layer classes with #Repository and #Service annotation. Now I've tried to write like this:
#Service("someService")
public class SomeServiceImpl implements SomeService{
#Autowired
#Qualifier("myDAO")
private MyDAO myDAO;
....
}
and
#Repository("myDAO")
public class JDBCDAOImpl implements MyDAO {
#Autowired
#Qualifier("dataSource")
private DataSource dataSource;
....
}
and all works fine!!!
But I still not found an answer for this quesion: if application will be more complex, and will have more complex structure, where #Repositore and #Service annotation are not preferred for some classes, how to inject correctly beans, which located in lower levels (in a fields of classes, or in a field of fields of classes) (with #Autowire annotation, of course)?
I guess you need <context:annotation-config />.
You can use
<context:component-scan base-package="PATH OF THE BASE PACKAGE"/>
entry in your configuration .xml file. This entry will scan/read all the stated type and annotations from the java classes.
Important points:
Sometimes, #Component may leads to a problem where it might say no default constructor found.
The class which is defined as a #Component annotation, it must have a default constructor.
Suppose, we have applied #Autowired annotation at field which is a user defined class reference.
Now, if we also apply #Component to that class then it will always be initialized with null.
So, a field with #Autowired should not have #Component at its class definition.
By default #Autowired is byType.
Address bean is autowired at Student class.
Let’s see what happens if we apply #Component at Address.java.
CollegeApp.java:
package com.myTest
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.bean.Address;
import com.bean.Student;
//Component scanning will for only those classes
//which is defined as #Component. But, all the class should not use
//#Component always even if the class is enabled with auto
//component scanning, specially the class which is Autowired
//Or which is a property of another class
#Configuration
#ComponentScan(basePackages={"com.bean"})
public class CollegeApp {
#Bean
public Address getAddress(){
return new Address("Elgin street");
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(CollegeApp.class);
Student student=context.getBean(Student.class);
System.out.println(student.toString());
context.close();
}
}
We want Elgin street to be autowired with Student address.
Address.java:
package com.bean;
import org.springframework.stereotype.Component;
#Component
public class Address {
private String street;
public Address()
{
}
public Address(String theStreet)
{
street=theStreet;
}
public String toString()
{
return (" Address:"+street);
}
}
Student.java:
package com.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class Student {
private String name;
private int age;
private Address address;
public Student()
{
}
public Student(String theName,int theAge)
{
name=theName;age=theAge;
}
#Autowired
public void setAddress(Address address) {
this.address = address;
}
public String toString()
{
return ("Name:"+name+" Age:"+age+ " "+address);
}
}
Output: - Name:null Age:0 Address:null //Address not Autowired here.
To resolve the issue, only change the Address.java as below:
Address.java:
package com.bean;
public class Address {
private String street;
public Address(String theStreet)
{
street=theStreet;
}
public String toString()
{
return (" Address:"+street);
}
}
Output:-
Name:null Age:0 Address:Elgin street
You should include this section of XML code in spring-config.xml :
<context:component-scan base-package="Fully.Qualified.Package.Name" />
but you should know the difference between <context:annotation-config> vs <context:component-scan> as most people are suggesting these two :
1) First big difference between both tags is that <context:annotation-config> is used to activate applied annotations in already registered beans in application context. Note that it simply does not matter whether bean was registered by which mechanism e.g. using <context:component-scan> or it was defined in application-context.xml file itself.
2) Second difference is driven from first difference itself. It does register the beans in context + it also scans the annotations inside beans and activate them. So <context:component-scan>; does what <context:annotation-config> does, but additionally it scan the packages and register the beans in application context.
There can be two reasons for this.
When you have not annotated the injected object or say service with proper #Service/#Component/#Repository annotations.
Once you have made sure of point 1 ,next check for whether the class package of your annotated service class is included in the class-path for your spring boot application in the main class.You can configure this using the following annotation.
#SpringBootApplication(scanBasePackages = {
"com.ie.efgh.somepackage","com.ie.abcd.someotherpackage" })
Doing this you tell spring to look into the packages for the classes during class loading.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Specifying base package of the Components like Controller, Service,
DAO -->
<context:component-scan base-package="com.jwt" />
<!-- Getting Database properties -->
<context:property-placeholder location="classpath:application.properties" />
<!-- DataSource -->
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"
id="dataSource">
<property name="driverClassName" value="${database.driver}"></property>
<property name="url" value="${database.url}"></property>
<property name="username" value="${database.user}"></property>
<property name="password" value="${database.password}"></property>
</bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
</props>
</property>
</bean>
</beans>

Resources