Build strings with Spring? - java

I'm getting reacquainted with Spring and looking at dependency injection and IoC in a way I haven't before.
If I want to build a string, say for a file name, and I already have a Spring bean which contains the directory, what is the best way to append the file name?
Writing a bean to do this myself seems fairly trivial, but I would think that Spring might already have the capability to do this somewhere though its API. If this is possible, how?
Just for kicks, here is the implementation of the fairly simple bean....
public class MySimpleStringAppender {
private final StringBuffer myString = new StringBuffer();
public MySimpleStringAppender(List<String> myStrings) {
for (String string : myStrings) {
myString.append(string);
}
}
public String getMySimpleString() {
return myString.toString();
}
}
and configured with...
<bean id="filename" class="MySimpleStringAppender">
<constructor-arg ref="filenameStrings"/>
</bean>
<util:list id="filenameStrings">
<ref bean="directory"/>
<value>filename.txt</value>
</util:list>
<bean id="directory" class="java.lang.String">
<constructor-arg value="C:/myDirectory/"/>
</bean>
So while it's not a lot of work or code, I'd think there would be something available so I wouldn't need to write this at all.

Nope never seen such a thing. You can also make your XML simpler by combining all that into one:
<bean id="filename" class="MySimpleStringAppender">
<constructor-arg>
<list>
<value>C:/myDirectory</value>
<value>filename.txt</value>
</list>
</constructor-arg>
</bean>
But you probably already knew that.

Maybe define "c:/myDirectory" as a property and do:
<bean id="filename" class="java.lang.String">
<constructor-arg value="${dir}/filename.txt"/>
</bean>
Will it work?

Related

declare spring bean of string type one line

I want to declare a bean of String type in an spring xml context in the shortest way possible. So far this is the shortest way I've found
<bean id="bean1" class="java.lang.String">
<constructor-arg type="String" value="someword" />
</bean>
However, for readability, I would like to 'stack' multiple of these neatly, kinda like so, and I can't just put all the code on one line because of team standards about line code length:
<bean id="bean1" class="java.lang.String">blah blah </bean>
<bean id="bean2" class="java.lang.String">blah blah </bean>
<bean id="bean3" class="java.lang.String">blah blah </bean>
For readability I would still prefer keeping it all in the context file and also I can redeploy the context file separately to the classpath without having to redeploy the whole .jar
If you want to keep everything in the context file and want to simplify it then you should be using PropertyPlaceholderConfigurer instead of String beans.
Here is an example,
Create a properties file named as project.properties.
Put all your Strings inside that file as {key=value} pair. For Example,
myStrings.cnst1=FOO
myStrings.cnst2=BAR
myStrings.cnst3=FOOBAR
Now, declare a PropertyPlaceholderConfigurer inside your context file as follows:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>project.properties</value>
</property>
Now, you can easily access the values as follows:
#Value("${myStrings.cnst1}")
private String someString;
Refer this link and this link for more information.

How can I inject an instance of List in Spring?

What works
Suppose I have a spring bean definition of an ArrayList:
<bean id="availableLanguages" class="java.util.ArrayList">
<constructor-arg>
<bean class="java.util.Arrays" factory-method="asList">
<constructor-arg>
<list>
<value>de</value>
<value>en</value>
</list>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
Now I can inject this into all kinds of beans, e.g. like this:
#Controller
class Controller {
#Autowired
public Controller(ArrayList<String> availableLanguages) {
// ...
}
}
This works great.
How it breaks
However if I change my controller a tiny bit and use the type List instead of ArrayList like this:
#Controller
class Controller {
#Autowired
public Controller(List<String> availableLanguages) {
// ...
}
}
Then instead I get a list of all beans of type String rather then the bean I defined. However I actually want to wrap my List into an unmodifiable List, but this will only be possible if I downgrade my dependency to a list.
So far discovered workaround
The following XML file:
<bean id="availableLanguages" class="java.util.Collections" factory-method="unmodifiableList">
<constructor-arg>
<bean class="java.util.Arrays" factory-method="asList">
<constructor-arg>
<list>
<value>de</value>
<value>en</value>
</list>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
works together with this controller:
#Controller
class Controller {
#Autowired
public Controller(Object availableLanguages) {
List<String> theList = (List<String>)availableLanguages;
}
}
While this works the extra type cast is ugly.
Findings so far
I figured that there is a special handling for collections in Spring 4.2.5 (the currently most recent version) which seems to cause all the trouble. It creates special behaviour when a parameter is an interface that extends Collection. Thus I can workaround by using Object or a concrete implementation as parameter type.
Question
Is there any way to directly inject a list into a bean? How?
Using #Qualifier will inject the bean with the given qualifier. You can name the list which you want to be a bean and that will work fine.

How to create java.util.Optional<T> object from Spring bean?

Java
public class MyObject{}
public class MyFactory{
private Optional<MyObject> myproperty;
public Optional<MyObject> getMyproperty{...}
public void setMyproperty{...}
}
Spring config xml (doesn't work)
<bean id="myproperty" class="java.util.Optional">
<constructor-arg>
<value>com.MyObject</value>
</constructor-arg>
</bean>
<bean id="myfactory" class="com.Myfactory">
<property name="myproperty" ref="myproperty" />
</bean>
Does spring support generics beans?
The reason for using Optional is it provide some useful features such as checking value if null. You can complete checking and further action in one line of code.
getMyproperty().ifPresent(id -> call.setId(id));
Seems the problem have nothing to do with generics.
You simply need to properly tell Spring to create the bean using a factory method, as Optional can only be created though factory methods. Something like:
<bean id="myproperty" class="java.util.Optional" factory-method="of">
<constructor-arg type="java.lang.Object" value="com.MyObject" />
</bean>
for which it is supposed to mean creating the myproperty bean by Optional.of(com.MyObject.class) (Change the factory-method to ofNullable if that's the one you want to use)
Another option is to use SpEL (Spring Expression Language):
<bean id="mybean" ...>
<property name="optProp" value="#{ T(java.util.Optional).of( #wrapme) }"/>
</bean>
Where "wrapme" is the name of a bean defined elsewhere that you want to wrap in java.util.Optional.

Multiple Bean Instances in Spring

I was wondering if it is possible to specify x amount of the same bean in a list in Spring. For example, instead of having beans with ids: stage1, stage2,... stageN, as here:
<bean id="stage1" class="Stageclass"/>
<bean id="stage2" class="Stageclass"/>
<bean id="stages" class="java.util.ArrayList">
<constructor-arg>
<list>
<ref bean="stage1" />
<ref bean="stage2" />
</list>
</constructor-arg>
</bean>
Would it be possible to do something like the following?:
<bean id="stage1" class="Stageclass"/>
<bean id="stages" class="java.util.ArrayList">
<constructor-arg>
<list>
<ref bean="stage1" duplicate="20 times"/>
</list>
</constructor-arg>
</bean>
Thanks in advance.
If you use annotation based configuration and you specified list of objects with same interface as dependency for some class then spring will auto-wire aut-wire then for free. Example:
interface StageInterface {
//...
}
class StageImpl1 implements StageInterface {
//...
}
class StageImpl2 implements StageInterface {
//...
}
#Component
class StageContainer {
private final List<StageInterface> stages;
#Autowired
public StageContainer(List<StageInterface> stages) {
this.stages = stages;
}
public List<StageInterface> getStages() {
return stages;
}
}
This is a spring version 3+ feature.
I believe the same is possible with xml configuration as well. In your case that's probably will be the same class(StageClass), but with different configuration parameters.
Lookup method injection from http://static.springsource.org/spring/docs/2.5.x/reference/beans.html solved the problem. Just needed to make sure the bean I wanted multiple instances of had scope="prototype"
You can't do that using standard Spring's default namespace. However you can implement your own custom namespace where you could support such syntax.
Alternatively, you can implement a static method that would create an ArrayList instance with duplicated elements.

Spring syntax for setting a Class object?

Is there a way to set a property in spring to, not an instance of a class, but the class object itself? i.e.
Rather than
<bean>
<property name="prototype" class="a.b.c.Foo">...
giving you an instance of "Foo", something like:
<bean>
<property name="prototype" class="java.lang.Class" value="a.b.c.Foo.class"...
edit:
best (working) solution so far - use the normal instantiation and derive the class in the setter. In terms of solutions I think this we'd describe this as "cheating":
<bean class="Bar">
<property name="prototype" class="a.b.c.Foo">...
public class Bar{
public void setPrototype(Object o){
this.prototypeClass=o.getClass();
edit:
dtsazza's method works as well.
edit:
pedromarce's method works as well.
<bean>
<property name="x">
<value type="java.lang.Class">a.b.c.Foo</value>
</property>
</bean>
That should work.
You could certainly use the static factory method Class.forName(), if there's no more elegant syntax (and I don't believe there is):
<property name="x">
<bean class="java.lang.Class" factory-method="forName">
<constructor-arg value="a.b.c.Foo"/>
</bean>
</property>
No. With a bean tag you instruct Spring on how to instantiate a class.
Would <property name="x" class="a.b.c.Foo.class"> work? That should be an instance of a Class object...

Categories