继续把剩下的集合注入、方法注入等整理完成,这篇依然以Setter注入为准,关于Java中的集合,我在前文《The Collection Interfaces in Java》已做了简单的介绍,这篇重点来看Map和List这两种常见的集合类型,其他的本人没有接触过,注入的方式也应该不会差太多,就不做介绍了。
1.集合的注入
下面例子是Map和List的注入的配置方式(例子代码部分来自网络):
Map:
<bean id="AdhesiveSpaceConstant" class="com.boco.irms.adhesive.location.bo.SpaceConstant"> <propertyname="fieldMaps"> <map> <entrykey="SITE"> <map> <entrykey="RELATED_LOCATION_CUID" value="resourceId" /> </map> </entry> <entrykey="ROOM"> <map> <entrykey="ROOM_TYPE" value="roomTypeName" /> </map> </entry> </map> </property> </bean>
从上面配置中看出,一个叫fieldMaps的Map注入到了SpaceConstant,那从这个配置中你是否看出了fieldMaps的数据结构呢?它是由两个Map嵌套而成:Map<String,Map<String,String>>。
下面看List:
List:
<bean name="" class=""> <proprety name=""> <list> <value>集合的值</value> </list> </proprety> </bean>
List的注入和Map就差不多了,不想多说。
上面这两种配置方式是我在该系列中篇文章里讲到的内联式的配置方式,他的好处是使配置比较清爽,不足是不能复用,那我们在实际开发中,完全是有可能两个类用到同一个Map或List的,为了复用一个Map总不至于让两个不相干的类上抽象出一个父类吧?这时候大家可能就想到:是否存在外联式的配置方式呢?能不能把一个集合对象能当做正常类一样独立配置呢?带着这些疑问,来看下面的例子:
先配置一个正常的Bean,用来引用Map:
<bean id="AdhesiveSyncBO" class="com.boco.irms.adhesive.location.sync.SyncBO"scope="prototype"> <propertyname="mapSyncInfo" ref="mapUtil"/> </bean>
上面这个配置是一个非常标准的引用其他Bean的配置方式(该系列中篇里讲过),SyncBO引用mapUtil对象。其实mapUtil是一个Map结构,并不是一个对象,继续看下面的配置就知道了:
<util:map id="mapUtil" key-type="java.lang.String" value-type="java.util.HashMap" scope="prototype"> <entrykey="4"> <map> <entrykey="Trans" value-ref="listTrans" /> </map> </entry> <entrykey="3"> <map> <entrykey="Power1" value-ref="listPower1" /> <entrykey="Power2" value-ref="listPower2" /> </map> </entry> </util:map>
上面的配置中看出,它是一个Map结构,但却把Map当成一个对象独立配置了,但和对象的配置有所不同的是用到了<util:map/>标签,而不是<bean/>标签,这样就做到了Map的独立配置,如果有多个对象用到该Map就可以采用外联式的方式使用<ref/>标签引用了。
下面说说这个例子中出现的几个重要属性,scope是一个比较重要的属性,我在该系列文章上篇中提到过bean的注入默认为单例,通过什么控制注入的为非单例呢?就这靠这个scope属性,把它的值设置为prototype,那么每次调用SyncBO对象时,IoC容器会给你一个崭新的bean。配置Map时还会遇到key-type和value-type这两个属性,从名字可以看出他俩是指定map中key和value的数据类型的,在配置中极力推荐明确定义这两个属性!
map也是支持引用别的类,就像对象一样引用别的bean,从上面的例子中看出,不是ref,而是value-ref属性,通过value-ref引用了listTrans、listPower1、listPower2这三个对象,这三个对象不是普通的类,而是List结构,配置如下:
<util:list id="listPower1" list-class="java.util.ArrayList" value-type="com.boco.core.dto.GenericDO" scope="prototype" /> <util:list id="listPower2" list-class="java.util.ArrayList" value-type="com.boco.core.dto.GenericDO" scope="prototype" /> <util:list id="listTrans" list-class="java.util.ArrayList" value-type="com.boco.core.dto.GenericDO" scope="prototype" />
同样使用了<util:list/>标签,可见List也是可以独立配置的,用法和Map雷同,不再赘述。
以上这种配置方式,大家使用的时候,别忘了引入util chema,不然会报错:“The matching wildcard is strict, but no declaration can be found for element 'util:map'.”。下图是Spring可引入的所有schema。
集合的配置方式到此为止。
2.方法注入
先介绍方法注入使用的业务场景:类A的方法methodA调用类B的methodB方法,用Spring管理A和B两个对象;如果现在每执行一次methodA需要创建一个B对象,对象A是不变的哦,那怎么实现呢?上面提到的非单例注入方法只能满足类A被重新创建时,才能创建一个B对象,这个场景不知道我说清楚了没有。还是看看官方对方法注入的描述:“在大部分情况下,容器中的bean都是singleton类型的。如果一个singletonbean要引用另外一个singleton bean,或者一个非singletonbean要引用另外一个非singleton bean时,通常情况下将一个bean定义为另一个bean的property值就可以了。不过对于具有不同生命周期的bean来说这样做就会有问题了,比如在调用一个singleton类型beanA的某个方法时,需要引用另一个非singleton(prototype)类型的beanB,对于beanA来说,容器只会创建一次,这样就没法在需要的时候每次让容器为beanA提供一个新的的beanB实例。”
想要实现此功能,那就必须要方法控制反转,通过getBean方法获取一个新的实例,可是这样就起不到解耦合的作用了。这里Spring提供了方法注入的方式,解决了此问题。
看例子:
package fiona.apple; public abstract class CommandManager { // okay... but where is the implementation of this method? protected abstract Command createCommand(); }
在包含被注入方法的客户类中(此处是CommandManager),此方法的定义必须按以下形式进行:
<public|protected>[abstract] <return-type> theMethodName(no-arguments);
如果方法是抽象的,动态生成的子类会实现该方法。否则,动态生成的子类会覆盖类里的具体方法。下面是配置:
<bean id="commandManager" class="fiona.apple.CommandManager"> <lookup-method name="createCommand" bean="command"/> </bean>
在上面的例子中,标识为commandManager的bean在需要一个新的commandbean实例时,会调用createCommand方法。重要的一点是,必须将command部署为prototype,并且要引入CGLIB的jar。(lookup注入怕说不清楚,更多是参考一些文档来写)
到此为止,Spring的注入基本写完了。