I want to inject the URL of a classpath resource in a way that does not create a dependency on Spring in the Bean. Meaning, the bean should not use Spring's interfaces/classes. How can I do that?
Spring is able to convert classpath:... values into java.net.URL implicitly:
public class Foo {
private URL url;
...
}
.
<bean class = "Foo">
<property name = "url" value = "classpath:..." />
</bean>
Following on from axtavt's answer, if you will allow yourself Spring annotations in the bean, you can do it like this:
#Value("classpath:myClasspathLocation") private URL url;
create your own implementation of a spring resource by extending the org.springframework.core.io.ClassPathResource like MyClasspathResource extends ClassPathResource and inject this type into your bean. Like this you do not have any dependency to spring and can later reimplement your resource with something else.
<bean class="myBean">
<property name="classPathType">
<bean class="org.test.bla.MyClasspathResource">
<constructor-arg index="0" value="classpath:/org/test/bla/MyUrl" />
</bean>
</property>
</bean>
There is hardly anything non-spring that's equivalent to Spring's resource concept.
You could for example use Guava's InputSupplier as an alternative, but you are missing powerful standard spring features if you do.
Related
I am using a third party library in my application to do some task. They have provided a wrapper that I've added in my project using maven. For using this wrapper we have to give an access key to their client class in order to use it's functionality. For ex:
final WeatherApiService was = new WeatherApiServiceImpl(accessKey);
final WeatherApiClient weatherApiClient = new WeatherApiClient(was);
What I want is to remove the above code (Since it's kind of Singleton and should be registered in spring context when the application is being started) and do something so that I can just autowire the WeatherApiClient and we are good to go. (wrapper isn't using spring FYI). Below is what I did is in my spring context I registered two beans and put the access-key is web.xml.
spring-context.xml
<bean id="was" class="my.librarypath.WeatherApiService ">
<constructor-arg type="java.lang.String" value="${accessKeyFromWebXml}"/>
</bean>
<bean id="weatherApiClient" class="my.librarypath.WeatherApiClient">
<constructor-arg type="my.librarypath.WeatherApiService" value="was"/>
</bean>
my component that will use the third party library
#Component("myComponent")
public class MyComponent IComponent {
#Resource(name = "weatherApiClient") // <--- getting Error here i.e: Couldn't aurtowire, bean should be of String type
private String weatherApiClient;
public void myFunction() {
weatherApiClient.getWeather();
}
}
Can someone confirm if I'm doing it right or is there any best practices options available !?
Ther were two issues:
<bean id="weatherApiClient" class="my.librarypath.WeatherApiClient">
<constructor-arg type="my.librarypath.WeatherApiService" value="was"/>
// ^---- should be ref
</bean>
Secondly, I was using String instead of WeatherApiClient. MY BAD :/
#Resource(name = "weatherApiClient")
private String weatherApiClient;
// ^---- this one should have to be WeatherApiClient
I'm beginner with spring framework, and I'm following this tutorial to applicate DI via setter. All works fine, but I'd like add to my class CsvOutputGenerator a constructor with one dynamic parameter, passed on the fly while I getting bean from Application context.
How can I do that?
I've already change my spring configuration in this way:
...
<bean id="CsvOutputGenerator" class="com.mkyong.output.impl.CsvOutputGenerator">
<constructor-arg type="java.lang.String" value="Test"/>
</bean>
...
but in this way is static value for my constructor.
You can pass it via system property for example
<constructor-arg lazy-init="true" type="java.lang.String" value="#{ systemProperties['some.key']}"/>
Try something else, even though Spring isn't made to be used like this (note the "prototype" scope):
<bean id="CsvOutputGenerator" class="com.mkyong.output.impl.CsvOutputGenerator" scope="prototype" />
And then in your code you can do something like this:
CsvOutputGenerator myBean = (CsvOutputGenerator) context.getBean("CsvOutputGenerator", "testing testing");
This is the method in the API that I used above.
The below content is based on the above question and comments.
Say u have a class URLRepo with attribute String url. url is initialized to value.
Then you can do something like this, to wire your CsvOutputGenerator
public class URLRepo {
private String url = "your value";
getters and setters
}
<bean id="urlRepo" class="com.*.*.MyURLRepo"/>
<bean id="CsvOutputGenerator" class="com.mkyong.output.impl.CsvOutputGenerator">
<constructor-arg type="java.lang.String" value="urlRepo.url"/>
</bean>
hope this is what you are looking for.
I am trying to write a ValidatorFactory which will give me a validator based on its type
public Validator getNewValidator(ValidatorType type){
switch:
case a : new Validator1();
break;
case b : new Validator2();
break;
}
I want to write using spring xml beans definition
I can use method injection but it will let me create only one object and the method does
not take any arguments.
I don't want to use FactoryBean.. I am just looking whether we can do this using spring xml
bean definition.
you can do conditional bean injection with plain xml. The "ref" attribute can be triggered by property values from a property file and thus create conditional beans depending on property values. This feature is not documented but it works perfect.
<bean id="validatorFactory" class="ValidatorFactory">
<property name="validator" ref="${validatorType}" />
</bean>
<bean id="validatorTypeOne" class="Validator1" lazy-init="true" />
<bean id="validatorTypeTwo" class="Validator2" lazy-init="true" />
And the content of the property file would be:
validatorType=validatorTypeOne
To use the property file in your xml just add this context to the top of your spring config
<context:property-placeholder location="classpath:app.properties" />
For complex cases (more complex than the one exposed), Spring JavaConfig could be your friend.
If you are using annotation (#Autowired, #Qualifier etc) instead of xml, you are not able to make conditional beans work (at least at current version 3). This is due to #Qualifier does not support expression
#Qualifier(value="${validatorType}")
More information is at https://stackoverflow.com/a/7813228/418439
I had an slightly different requirements. In my case I wanted to have encoded password in production but plain text in development. Also, I didn't have access to parent bean parentEncoder. This is how I managed to achieve that:
<bean id="plainTextPassword" class="org.springframework.security.authentication.encoding.PlaintextPasswordEncoder"/>
<bean id="shaPassword" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
<constructor-arg type="int" value="256"/>
</bean>
<bean id="parentEncoder" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource">
<bean class="org.springframework.aop.target.HotSwappableTargetSource">
<constructor-arg ref="${password.encoding}Password"/>
</bean>
</property>
</bean>
Of course, I defined password.encoding in a property file with possible values as sha or plainText.
You should be able to do this:
<bean id="myValidator" factory-bean="validatorFactory" factory-method="getNewValidator" scope="prototype">
<constructor-arg><ref bean="validatorType"/></constructor-arg>
</bean>
<bean id="validatorType" ... />
Of course, it uses an automatically configured FactoryBean underneath but you avoid any Spring dependency in your code.
First: I'm using Spring 3.0
I have a problem when configuring my controller class. The controller uses a web service which I want to define the endpoint address using a .properties file.
#Controller
public class SupportController {
#Value("#{url.webservice}")
private String wsEndpoint;
...
In my application context xml-file, I've defined this:
<context:property-placeholder location="/WEB-INF/*.properties" />
I've been reading the documentation, trying different approaches (like adding prefix systemProperties.),but I keep getting an error message telling me that it doesn't exist.
Field or property 'url' cannot be
found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext'
Ok. I've figured it out.
Now, in the controller:
#Value("#{settings['url.webservice']")
Then in the context configuration I have this "helper bean":
<util:properties id="settings"
location="/WEB-INF/supportweb.properties"></util:properties>
This should work, too:
#Value("${url.webservice}")
private String wsEndpoint;
I have this configuration and it works fine:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:application.properties</value>
</list>
</property>
</bean>
and I iniejct the property in this way
#Value("${root.log.level}")
private String prop;
the field is correctly initialized to "DEBUG" value.
you should check that the
<context:property-placeholder location="/WEB-INF/*.properties" />
is defined in webmvc-config.xml where you create instances of the #Controllers
I would really like to annotate a method with a reference to a single property in a property file for injection.
#Resource("${my.service.url}")
private String myServiceUrl;
Of course, this syntax does not work ;) Thats why I'm asking here.
I am aware that I can inject the full properties file, but that just seems excessive, I dont want the property file - I want the configured value.
Edit: I can only see PropertyPlaceholderConfigurer examples where XML is used to wire the property to the given field. I still cannot figure out how this can be achieved with an annotation ?
I know it has been a while since the original post but I have managed to stumble across a solution to this for spring 2.5.x
You can create instances of "String" beans in the spring xml configuration which can then be injected into the Annotated components
#Component
public class SomeCompent{
#Autowired(required=true
#Resource("someStringBeanId")
private String aProperty;
...
}
<beans ....>
<context:component-scan base-package="..."/>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
...
</bean>
<bean id="someStringId" class="java.lang.String" factory-method="valueOf">
<constructor-arg value="${place-holder}"/>
</bean>
</beans>
I've created a project which addresses this problem for Spring 2.5.*:
http://code.google.com/p/spring-property-annotations/
For Spring 3 you can use the #Value("${propery.key}") annotation.
There's a thread about this on the Spring forum. The short answer is that there's really no way to inject a single property using annotations.
I've heard that the support for using annotations will be improved in Spring 3.0, so it's likely this will be addressed soon.
you can do this if you use XML configuration. Just configure PropertyPlaceholderConfigurer and specify property value in configuration
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:com/foo/jdbc.properties</value>
</property>
</bean>
<bean ...>
<property name="myServiceUrl" value="${my.service.url}"/>
</bean>
You could try injecting value of property "my.service.url" to a filed in your bean.
Take a look at: http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-factory-placeholderconfigurer
HTH.