Spring #Value annotation and setter injection difference - java

When I am setting a value using #Value annotation from a property file, its not working, whereas setting using setter injection works.
my properties file like this.
app.work.dir=file:${user.home}/WORK
class file like below.
#Value("#{configProperties['app.work.dir']}")
private String workdir;
and this is my xml setting.
<util:properties id="configProperties" location="classpath:config.properties" />
when use setter injection like below then it works fine.
<bean id="sampleService" class="aaa.SampleService">
<property name="workdir" value="${app.work.dir}" />
</bean>
I'm not sure why and if possible i want to use #Value annotation.
please refer to junit test case below.
xml configulation:
<?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:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
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/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<context:property-placeholder location="classpath:testconfig.properties" />
<util:properties id="sampleProperties" location="classpath:testconfig.properties" />
<bean id="exampleA" class="aa.sample.SampleA" />
<bean id="sampleB" class="aa.sample.SampleB" >
<property name="workdir" value="${work.dir}" />
<property name="tempdir" value="${work.dir.test}" />
<property name="aaa" value="${aaa}" />
</bean>
</beans>
sample service class:
package aa.sample;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
#Service
public class SampleA {
#Value("#{sampleProperties['work.dir']}")
private String workdir;
#Value("#{sampleProperties['work.dir.test']}")
private String tempdir;
#Value("#{sampleProperties['aaa']}")
private String aaa;
//getter setter deleted
}
properties file:
work.dir=file:${user.home}/WORK
work.dir.test=${work.dir}/TEST
aaa=bbb
and junit test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "/testcontext.xml" })
public class ExampleTest {
#Inject
private SampleA sampleA;
#Inject
private SampleB sampleB;
#Test
public void testValueAnnotation() {
assertThat(sampleA.getAaa(), is("bbb"));//ok
String tempdir = sampleA.getTempdir();
String workdir = sampleA.getWorkdir();
assertFalse("[sampleA] temp dir should not have ${work.dir}", tempdir.indexOf("${work.dir}") >= 0);//ng
assertFalse("[sampleA] workdir dir should not have ${user.home}", workdir.indexOf("${user.home}") >= 0);//ng
}
}

The bean generated by
<util:properties id="sampleProperties" location="classpath:spring.properties" />
basically translates to a map. This notation
#Value("#{sampleProperties['work.dir']}")
requests one of its valued mapped with the key work.dir. There's no property resolution that occurs on the value returned. It's taken literally.
This, on the other hand,
<property name="tempdir" value="${work.dir.test}" />
requests a property, called work.dir.test which can be recursively resolved.

Related

Spring's Cacheable not working - Not returning results from Cache

I'm trying to use Spring #Cacheable on a Method but its Not Working.
import org.springframework.cache.annotation.Cacheable;
public class CacheableService {
#Cacheable(value = "entityCount")
public int someEntityCount(final String criteria) {
System.out.println("Inside function : " + criteria);
return 5;
} }
Beans Initialization :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<cache:annotation-driven />
<context:annotation-config/>
<aop:aspectj-autoproxy />
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean name = "impactLevelsCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="impactLevels" />
</bean>
<bean name = "entityCountBean" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="entityCount" />
</bean>
</set>
</bean>
</beans>
I am not Getting what mistake I'm making. It always invoking the method. I have also enabled Trace Logs but its not logging anything specific to Caching.
Also tried calling it from test cases, but cache not working. (Invocation is from different class. There is no Self-Invocation).
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
// Using same Bean configuration for Testing also.
#ContextConfiguration(locations = "/com/amazon/pickuppointcapacity/test/appconfig2.xml")
public class RoughTest {
#Test
public void testSomeEntityCountCalledTwice_shouldCallDaoMethodOnce() {
CacheableService cacheableService = new CacheableService();
cacheableService.someEntityCount("A");
cacheableService.someEntityCount("A");
cacheableService.someEntityCount("A");
cacheableService.someEntityCount("A");
}
}
Please help me out.
You are creating a new instance of CacheableService in your test, which means it is not managed by Spring and non of the Spring annotations will work in it.
For spring to kick in, you need to get your service as a bean, autowired into your test class

Using actual properties file from src/main/resources with Spring #PropertySource in JUnit test

I am trying to unit test a DAO class by using a H2 database instead of an actual database. I am facing an issue while trying to make my test case use a properties file that is present in the src/main/resources/properties/ folder :
Test class
#RunWith(SpringJUnit4ClassRunner.class)
#PropertySource("classpath:properties/common.properties")
#ContextConfiguration(locations = { "/spring/common-context.xml" })
public class ConfigDAOImplTest {
#Autowired
private ConfigDAOImpl configDAO;
#Spy
private ContextParamDAO contextParamDAO = new ContextParamDAOImpl();
private static final String SCHEMA_CONFIG = "classpath:data/CONFIG_SCHEMA.sql";
private static final String DATA_CONFIG = "classpath:data/CONFIG_DATA.sql";
#Before
public void init() {
MockitoAnnotations.initMocks(this);
DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript(SCHEMA_CONFIG)
.addScript(DATA_CONFIG)
.build();
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//override the jdbcTemplate for the test case
configDAO.setJdbcTemplate(jdbcTemplate);
configDAO.setContextParamDAO(contextParamDAO);
}
//.. more coode
}
common-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="commonAppProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="locations">
<list>
<value>file:${conf_folder_path}/common.properties</value>
</list>
</property>
</bean>
<bean id="configDAO"
class="com.myproject.common.dataaccess.impl.ConfigDAOImpl" scope="step">
<property name="jdbcTemplate" ref="jdbcTemplate" />
<property name="corePoolSize" value="${threadpool.size}"/>
</bean>
</beans>
When I run the test class, I get the following exception :
Caused by: org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'corePoolSize'; nested exception is java.lang.NumberFormatException: For input string: "${threadpool.size}"
One reason for the test case not being able to find the required property is because :
The PropertyPlaceholderConfigurer bean refers to
{conf_folder_path}/common.properties which is the path where
src/main/resources/properties/common.properties gets copied to by Maven build system.
However , in Eclipse, there is no {conf_folder_path} as this is created by Maven.
Question : Assumnig that the above reason is the root cause of the issue, how do I make the test case find the properties considering the path refereed to in the Spring context is different from that in the source code.
You can create something like this:
#Configuration
public class TestConfiguration {
private static final Logger log = LoggerFactory.getLogger(TestConfiguration.class);
#Autowired
private Environment env;
/**
* This bean is necessary in order to use property file from src/main/resources/properties
* #param env environment
* #return property source configurator with correct property file
*/
#Bean
public PropertySourcesPlaceholderConfigurer placeholderConfigurerDev(ConfigurableEnvironment env) {
final String fileName = "common.properties";
Path resourceDirectory = Paths.get("src","main","resources", "properties");
String absolutePath = resourceDirectory.toFile().getAbsolutePath();
final File file = new File(absolutePath.concat("/").concat(fileName));
if (file.exists()) {
try {
MutablePropertySources sources = env.getPropertySources();
sources.addFirst(new PropertiesPropertySource(fileName, PropertiesLoaderUtils.loadAllProperties(file.getName())));
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
throw new RuntimeException(ex.getMessage(), ex);
}
}
this.env = env;
return new PropertySourcesPlaceholderConfigurer();
}
Thanks to #Lemmy for the direction on how to solve this issue.
My final solution was to create a new common-test-context.xml file where I can look for the property file in the properties folder on the class path. I placed this file in src/test/resources/spring folder and imported the actual common-context.xml from src/main/resources/spring folder into it.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="classpath*:/spring/common-context.xml" />
<bean id="commonAppProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="locations">
<list>
<value>classpath:/properties/common.properties</value>
</list>
</property>
</bean>
</beans>

Spring - autowired fields are null

I faced a problem with Spring Framework: #Autowired are null, but Spring doesn't throw any exceptions, and I can't understand, why are these fields null.
I have a class:
package com.processing.gates;
public class Main {
private final ApplicationContext context = Context.getContext();
#Autowired private PaymentGateFactory paymentGateFactory;
#Autowired private CalculatorChooser calculatorChooser;
//...
}
And for example I have the following class:
package com.processing.gates.comission;
#Component
public class CalculatorChooser {
//...
}
Here is my Spring configuration xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
<context:annotation-config />
<context:component-scan base-package="com.processing.gates"/>
<bean id="logPath" class="java.lang.String"> <!-- путь к папке с логами -->
<constructor-arg value="/home/yoba/NetBeansProjects/processing/gates/log/"/>
</bean>
<!-- ... -->
</beans>
When I try to write in xml:
<bean id="calculator" class="com.processing.gates.comission.CalculatorChooser"/>
and get it from code:
CalculatorChooser cc = (CalculatorChooser)Context.getContext().getBean("calculator");
it works fine. But #Autowired doesn't work. How can I fix this? Thanks!
Let Spring manage the bean by either declaring a Main bean in the application context file or by using the #Component annotation as you've already done for CalculatorChooser
<bean id="mainBean" class="com.processing.gates.Main"/>

Spring #Autowired comes as null

I have #Component, A, which AutoWires a class which contains simple configurations for class A. I've created a bin for configuration file. But for some reason it comes as null. Could you please help me to find out the problem?
#Component
public class SearchEngineDriver {
#Autowired(required = true)
private EngineContext context;
public SearchEngineDriver(){
String clusterName = context.getClusterName();
}
}
public class EngineContext {
private String clusterName;
public EngineContext(String clusterName){
this.clusterName = clusterName;
}
public String getClusterName(){
return this.clusterName;
}
}
3rd class.
#Autowired
private SearchEngineDriver searchEngineDriver;
mvc-dispatcher-servlet.xml
<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"
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
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="org.electronsoftware" />
<mvc:annotation-driven/>
<mvc:resources mapping="/resources/**" location="/resources/" />
<import resource="classpath*:/application-context.xml"/>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
application-context.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"
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-3.2.xsd">
<context:property-placeholder location="classpath:application.properties"/>
<bean id="searchEngineContext" class="org.electronsoftware.KGB.search.context.EngineContext" >
<constructor-arg value="${kgb.search.engine.clustername}"/>
</bean>
</beans>
You are accessing the autowired field from the constructor. At the time the constructor runs, Spring has not yet got the chance to initialize the field. Instead use a #PostConstruct method to perform logic which depends on the autowired value.

Spring 3 how to run a scheduled task with an interval from property file

I am using Spring 3 to create a scheduled-task.
I have to use XML based wiring to configure it, and would like scheduled task to run with an interval that is set in a properties file.
Spring Context file:
<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.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task">
<context:property-placeholder location="classpath:connector.properties"/>
<task:scheduler id="connectorScheduler" pool-size="10"/>
<task:scheduled-tasks scheduler="connectorScheduler">
<task:scheduled ref="connector" method="checkConnection" fixed-rate="${connector.connectionAttemptDelayMillis}"/>
</task:scheduled-tasks>
<bean id="connector" class="com.test.Connector" scope="singleton">
<constructor-arg index="0" value="${connector.user}"/>
<constructor-arg index="1" value="${connector.password}"/>
<constructor-arg index="2" value="${connector.connectionAttemptDelayMillis}"/>
</bean>
</beans>
The problem is that the ${connector.connectionAttemptDelayMillis} is not allowed in the fixed-rate value. This will work fine if i was to put a number in its place, but i need this value to come from a loaded property file.
Any help is much appreciated.
You could do it like that (downside, you can only do your task every 1s or more):
Connector class
package com.test.Connector;
#Component
public class Connector {
#Value("${connector.user}")
private String user;
#Value("${connector.password}")
private String password;
#Value("${connector.connectionAttemptDelayMillis:0}")
private long attemptDelayMillis;
#Scheduled(cron = "${connector.connectionAttemptCron}")
public checkConnection() {
// do the check
}
}
Xml context
<?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" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<context:annotation-config />
<context:component-scan base-package="com.test" />
<context:property-placeholder location="classpath:connector.properties" />
<task:scheduler id="connectorScheduler" pool-size="10"/>
<task:annotation-driven scheduler="connectorScheduler" />
</beans>
Properties
connector.user = someuser
connector.password = somepassword
connector.connectionAttemptDelayMillis = 5000
connector.connectionAttemptCron = */5 * * * * *
This works because cron need a string.
References
http://static.springsource.org/spring/docs/3.0.x/reference/scheduling.html
http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/scheduling/annotation/Scheduled.html#cron()

Categories