How to use #Value on Spring non-managed class - java

Here my (simplified) code before explaining my problem :
foo.bar.MyFile
public class MyFile extends MyFileAbstract {
#Value("${FILE_PATH}")
private String path;
[...]
public MyFile(final Date date, final String number, final List<MyElement> elements) {
this.date = date;
this.number = number;
this.elements = elements;
}
#Override
public String getPath() {
return path;
}
[...]
}
foo.bar.MyService
#Service
public class MyService {
[...]
public String createFolder(MyFileAbstract file) throws TechnicalException {
[...]
String path = file.getPath();
[...]
}
[...]
}
the call of service
[...]
#Autowired
MyService service;
public void MyMethod() {
MyFile file = new MyFile();
service.createFolder(file);
[...]
}
[...]
I use a context XML to configure Spring :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util" 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
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<context:property-placeholder
file-encoding="utf-8"
location="file:///[...]/MyProperties.properties" />
<context:annotation-config />
<context:component-scan base-package="foo.bar.classes" />
[...]
</beans>
The problem is that the variable path is null at runtime in both MyService and MyFile file when a instantiate MyFile to call my service MyService.
I am looking a solution to inject my property ${FILE_PATH} inside MyFile.
Here my environment :
Apache Tomcat 7
Java 8
Spring 4.1.6.RELEASE
I have seen that Spring AOP with #Configurable bean could resolve this but don't want to change my Java Agent because I don't want to modify the configuration on the production server.
And I don't know how to use #Service on MyFile with my custom constructor.
Any idea is welcome.

You can add to your MyService
#Autowired
private Environment environment;
and just get the value
environment.getProperty("FILE_PATH");
After that you can set it to the file if necessary.

#Service
public class BeanUtilityService implements ApplicationContextAware {
#Autowired
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Create a Utility class as a service , create a static method and get the bean from the context.Then use that bean to get the properties required

use #PropertySource annotation
#PropertySource("classpath:config.properties") //use your property file name
public class MyFile extends MyFileAbstract {
#Value("${FILE_PATH}")
private String path;
[...]
public MyFile(final Date date, final String number, final List<MyElement> elements) {
this.date = date;
this.number = number;
this.elements = elements;
}
#Override
public String getPath() {
return path;
}
[...]
}

Related

Spring Autowiring when using Configuration Class

I have a xml bean 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: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:annotation-config/>
<bean id="helloWorld" class="com.a.b.HelloWorld">
<property name="attr1" value="Attr1 from XML"></property>
</bean>
<bean id="helloWorld2" class="com.a.b.HelloWorld2">
<property name="attr2" value="Attr2 from XML"></property>
</bean>
</beans>
And I have use constructor autowiring like this
public class HelloWorld2{
private String attr2;
public void setAttr2(String message){
this.attr2 = message;
}
public void getAttr2(){
System.out.println("getAttr2 == " + attr2);
}
}
public class HelloWorld{
private String attr1;
private HelloWorld2 helloWorld2;
public HelloWorld(){
}
#Autowired
public HelloWorld(HelloWorld2 helloWorld2){
System.out.println("hhh");
this.helloWorld2 = helloWorld2;
}
public void setAttr1(String message){
this.attr1 = message;
}
public void getAttr1(){
System.out.println("getAttr1 == " + attr1);
}
public void getH(){
helloWorld2.getAttr2();
}
}
And autowiring is working fine.
Now I want to move my beans to Configuation class.
But then how to move the code so as autowiring works?
I have tried like this, but its not working
#Configuration
public class Config {
#Bean
public HelloWorld helloWorld(){
HelloWorld a = new HelloWorld();
a.setAttr1("Demo Attr1");
return a;
}
#Bean
public HelloWorld2 helloWorld2(){
HelloWorld2 a = new HelloWorld2();
a.setAttr2("Demo Attr2");
return a;
}
}
I think what you want to achieve is the injection of a HelloWorld2 instance into the method that creates the HelloWorld #Bean?
This should do it:
#Configuration
public class Config {
#Bean
public HelloWorld helloWorld(HelloWorld2 helloWorld2){
HelloWorld a = new HelloWorld(helloWorld2);
a.setAttr1("Demo Attr1");
return a;
}
#Bean
public HelloWorld2 helloWorld2(){
HelloWorld2 a = new HelloWorld2();
a.setAttr2("Demo Attr2");
return a;
}
}
This might be a duplication of these questions:
Understanding Spring Autowired usage
Converting Spring XML file to Spring configuration class

In Spring, annotation and xml must be used together?

A class
public class A {
private String name;
public A() {
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
BeanFactory class
public class BeanFactory implements InitializingBean, DisposableBean{
private A a;
public BeanFactory(){
}
public BeanFactory(A a){
this.a = a;
}
public void printAName(){
System.out.println("Class BeanFactory: beanFactory.printAName -> a.getName() = " + a.getName());
}
}
Main
public class Main {
public static void main(String[] args) {
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"ApplicationContext.xml");
BeanFactory beanFactory = applicationContext.getBean("beanFactory",
BeanFactory.class);
beanFactory.printAName();
}
}
ApplicationContext
<?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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<bean id="beanFactory" class="testSpring.BeanFactory">
<constructor-arg ref="a1"/>
</bean>
<bean id="a1" class="testSpring.A">
<property name="name" value="I am A!"></property>
</bean>
</beans>
Result of run: Class BeanFactory: beanFactory.printAName -> a.getName() = I am A!
Like you can see, here I don't use no annotation. But the code works thanks to xml file.
So xml doesn't need annotation..? Can I use one or the other?
If I would use, in this application, the annotation (#Autowired for example) instead of bean xml, it's possible? Can you show me how?
Or the annotation must require xml reference?
So.. annotation and xml must be used together? Thanks
You should use annotation configuration, this is the idea
#Component
class Bean1 {
public Bean1() {
System.out.println(getClass());
}
}
#Configuration
#ComponentScan("test")
public class Config {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
}
}
For details see Spring docs

Spring 4.1.6 - Load ".properties" and initialize a bean with that

I'm new with Spring MVC and I'm doing some tests. I was trying to find some answers about this issues, but most of them make references to Spring 3.11 and I'm using the last release: 4.1.6.
I want to load a ".properties" file when the application starts, and use the information in it to create a bean to access it in all the context of the app.
So far, I reach to load the file in the servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd 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:property-placeholder location="classpath*:resources/Resources.properties" />
</beans:beans>
I think (not really sure) that I correctly declared the bean in the root-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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="Resources" class="ar.com.violenciaesmentir.blog.resources.ResourcesDB"/>
</beans>
And I also think I made the bean correctly, but I don't really know if the annotations are right:
package ar.com.violenciaesmentir.blog.resources;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
#Service
public class ResourcesDB {
#Value("DB.NAME")
private String name;
#Value("DB.TYPE")
private String type;
#Value("DB.USER")
private String user;
#Value("DB.PASS")
private String pass;
#Value("DB.DRIVER")
private String driver;
#Value("DB.URL")
private String url;
#Value("DB.MAXACTIVE")
private String maxActive;
#Value("DB.MAXIDLE")
private String maxIdle;
#Value("DB.MAXWAIT")
private String maxWait;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMaxActive() {
return maxActive;
}
public void setMaxActive(String maxActive) {
this.maxActive = maxActive;
}
public String getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(String maxIdle) {
this.maxIdle = maxIdle;
}
public String getMaxWait() {
return maxWait;
}
public void setMaxWait(String maxWait) {
this.maxWait = maxWait;
}
}
My ".properties" file:
DB.NAME = jdbc/Blog
DB.TYPE = javax.sql.DataSource
DB.USER = blog
DB.PASS = blog
DB.DRIVER = oracle.jdbc.driver.OracleDriver
DB.URL = jdbc:oracle:thin:#localhost:1521:xe
DB.MAXACTIVE = 20
DB.MAXIDLE = 5
DB.MAXWAIT = 10000
I think the reference is ok because it gave me troubles when starting the server, saying that it couldn't find the property for "name", but I was doing the annotation wrong and then I fixed.
What I want is to have that bean initialized and be avaible to have an attribute in the DB class like:
#ManagedAttribute
private ResourcesDB resources;
...
public void foo() {
String dbName = resources.getName();
}
When I try it, resources is null. What I'm doing wrong?
-----UPDATE-----
Ok, I could solve the problem doing some try&fail with the answers given. First of all, I corrected the #Value like ("${DB.NAME}") and added a value to the service annotation #Service(value="Resources").
Then, the only change I got to do was in the servlet-context.xml. Instead of:
<context:property-placeholder location="classpath*:resources/Resources.properties" />
I used:
<beans:bean id="configuracion" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<beans:property name="location" value="classpath:Resources.properties"/>
</beans:bean>
And used #Autowire instead of #ManagedBean to access the bean.
There are 2 things flawed in your code.
Your #Value expressions are wrong
Your <context:property-placeholder /> must be in the same context as the beans
When using #Value you have to use placeholders, by default ${property name} you are just using a name. So update your annotations to reflect that. I.e. #Value("${DB.NAME}.
Next you have defined <context:property-placeholder /> in the context loaded by the DispatcherServlet whereas your beans are loaded by the ContextLoaderListener. The property placeholder bean is a BeanFactoryPostProcessor and it will only operate on bean definitions loaded in the same context. Basically your bean definition are in the parent context and your placeholder in the child context.
To fix move <context:property-placeholder /> to the same context where you bean is defined in.
Instead of #ManagedAttribute which is a JSF annotation use #Autowired or #Inject. And if you don't have a <context:component-scan /> add a <context:annotation-driven />.
Your #Value syntax is incorrect. It should be #Value("${DB.NAME}").
You might also need to add this to your XML config:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:resources/Resources.properties" />
</bean>
The value on the location may vary, not sure on how you are structuring and building your artifacts.

Spring factory bean with #Autowired in superclass

I've implemented a factory bean in Spring to instantiate different subclasses of a superclass. The problem I have is that superclass properties aren't being #Autowired (I guess due to the new command in the factory methods). This is my code:
#Component
public class ConfigBeanImpl implements ConfigBean{
#Override
public String expandParam(String param) {
return String.format("expanded %s", param);
}
}
public abstract class FactoryBean {
#Autowired
protected ConfigBean configBean;
private String property;
protected FactoryBean() {
this.property = configBean.expandParam("property");
}
public abstract String getProperty();
public static FactoryBean GET(int id) {
return new FactoryBeanGet(id);
}
public static FactoryBean POST(String param){
return new FactoryBeanPost(param);
}
}
public class FactoryBeanGet extends FactoryBean {
private int id;
protected FactoryBeanGet(int id) {
this.id = id;
}
#Override
public String getProperty() {
return Integer.toString(id);
}
}
public class FactoryBeanPost extends FactoryBean {
private String param;
protected FactoryBeanPost(String param) {
this.param = param;
}
#Override
public String getProperty() {
return param;
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml"});
FactoryBean bean = (FactoryBean) context.getBean("factoryBeanGet", 12);
System.out.println(bean.getProperty());
bean = (FactoryBean) context.getBean("factoryBeanPost", "test param");
System.out.println(bean.getProperty());
}
}
And the applicationContext.xml:
<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.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:component-scan base-package="com.spring" />
<bean id="factoryBeanGet" scope="prototype" class="com.spring.bean.FactoryBean"
factory-method="GET">
</bean>
<bean id="factoryBeanPost" scope="prototype" class="com.spring.bean.FactoryBean"
factory-method="POST">
</bean>
The protected ConfigBean configBean property in abstract class FactoryBean isn't being #Autowired and hence is null and the constructor throws a NullPointerException. If I place it inside each of the subclasses, it works fine, but it'd be duplicated code. Is there a way to solve this or am I doing something wrong?
Put yourself in Spring's shoes. It must instantiate FactoryBean, and then initialize its configBean field. So, the first thing it will do is call the constructor. And then, once the object exists, it will initialize the object's field. It obviously can't initialize the field if the object doesn't exist yet. So, at the time the constructor is called, the field is still null.
Use constructor injection, or use a method annotated witb #PostConstruct to call the configBean.
That said, the private property field that you're trying to initialize is not used anywhere, so you could as well remove it, and remove the configBean field as well.

Spring DI with Constructor

I have the following code:
#Component
public class MainBean {
#Autowired
private MyTask myTask
#Autowired
private TaskScheduler taskScheduler
public void start() {
String str = "Print something to console";
//somehow call constructor and pass str argument??
taskScheduler.execute(myTask);
}
}
#Component
public class MyTask implements Runnable {
private String str;
#Autowired
public MyTask(String str) {
this.str = str;
}
#Override
public void run() {
System.out.println(str);
}
}
I want to call MyTask and pass the str argument to the constructor. How can I do this? I cant find any good examples anywhere.
If I understand correctly what you are trying to do, a good solution would be the following:
#Component
public class MainBean {
#Autowired
private MyTaskFactory myTaskFactory
#Autowired
private TaskScheduler taskScheduler
public void start() {
String str = "Print something to console";
taskScheduler.execute(myTaskFactory.getTask(str));
}
}
public class MyTaskFactory {
public MyTask getTask(String str) {
return new MyTask(str);
}
}
#Configuration
public class MyTaskFactoryConfig {
#Bean
public MyTaskFactory myTaskFactory() {
return new MyTaskFactory();
}
}
Note that MyTask will then be changed to:
public class MyTask implements Runnable {
private String str;
public MyTask(String str) {
this.str = str;
}
#Override
public void run() {
System.out.println(str);
}
}
As seen from annotations, you've configured MyTask to be Spring managed. Also, str property is Spring managed, it should be injected by Spring to MyTask.
So, whenever you need MyTask instance, you don't create it yourself.
You #Autowire wherever you need.
public class ClassThatNeedsMyTaskInstances{
#Autowired
MyTask myTask;
}
But beware that by default, MyTask will be a singleton, so you may want to change its scope to prototype.
But this may be a good case where you don't let Spring manage MyTasks lifecycle. Instead you manage it yourself by creating instances using new, or factory
The following examples are for injecting the string from and XML and a Java-based configuration:
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="MyTaskConstructor" class="java.lang.String">
<constructor-arg type="char[]" value="My value"></constructor-arg>
</bean>
<bean id="myTask" class="com.package.MyTask">
<constructor-arg ref="MyTaskConstructor"/>
</bean>
</beans>
Java-based
#Configuration
public class MyTaskConfig {
#Bean
public String getMyTaskConstructor() {
return "My value";
}
#Bean
public MyTask myTask() {
return new MyTask(getMyTaskConstructor());
}
}

Categories

Resources