Spring: Injecting a Scala List
This can be done using a converter wrapper:
import scala.collection.JavaConversions._
object ListConverter {
def toScalaList( jlist: java.util.List[AnyRef] ) = {
jlist.toList
}
}
which uses a implicit def asScalaBuffer
in JavaConversions
to return a Scala List. Then I can make the list injectable as:
<bean id="someItems" class="my.package.ListConverter" factory-method="toScalaList">
<constructor-arg type="java.util.List">
<util:list>
开发者_开发知识库<ref bean="itemOne"/>
<ref bean="itemTwo"/>
</util:list>
</constructor-arg>
</bean>
I would like to find a cleaner way to do this
I can't really use JavaConversions
directly as a static factory call:
<bean id="someItems" class="scala.collection.JavaConversions" factory-method="asScalaBuffer">...</bean>
Since an implicit def
is not really a static method in a Java universe.
P.S. To get it out of the way.. I know there are a lot of annotation driven config lovers, but I am not one of them, and that is not a solution I am looking for: I like my XML for Spring configs, and I like Spring to DI Scala
If you want to convert a java.util.List
to a scala List
you will have to provide your own conversion/factory method as no such method exists.
JavaConversions.asScalaBuffer
could be referenced exactly as you did in your XML definition as the Scala compiler generates static forwarder methods to the object
instance unless you tell it not to do so.
The problem here is actually actually that this method returns a scala.collection.mutable.Buffer
which you still have to convert into a List
.
An alternative is to accept an instance of Seq[A]
to be injected. This would work as Buffer[A]
extends Seq[A]
- but you have to be aware that you are injecting a wrapper around the (possibly mutable) java.util.List<A>
instance then.
If your ListConverter
was an object
rather than a class
, would not toScalaList
be accepted as a static method ?
Your biggest problem is that, behind the scenes, Spring is inherently untyped. That list you're attempting to inject actually starts out as a List<String>
before Spring determines the correct type of the target param/property.
In some cases, it can do this because the relevant type is retained in bytecode (even if it's erased on the JVM), this won't work in the case of injecting to generic methods. In other cases, it can determine the type of a collection by inspecting elements. It usually gets this right, depending on how much polymorphism is involved.
Of course, all that logic is based entirely on java.util.Collection
and subclasses, so Scala's collection types simply don't get to join the party.
You have three solutions here:
Learn to love annotation-driven injection. You won't be bitten by the insidious weak-typing.
Create an overload of the setter method, or an auxiliary constructor that takes equivalent Java collection types. Then use
collection.JavaConverters
to handle the conversion. Spring is then happy because it's still within its Java-only comfort zone.(my personal favourite) Stop using Spring
You're guaranteed to eventually run into problems with your converter, this will happen at around the time you discover the need to deal with the parity mismatch between boxed and unboxed primitives.
精彩评论