一般而言,spring大哥会有两种方式让你需要的值注入给对象,分别是 设置注入和构造注入。
1.设置注入
此方法要求在类中要给成员变量设置setter方法,使之变成可以操作的属性。
在这里先给集合以及数组属性注入,想创建一个类,包含有数组以及三种集合属性,再设置setter/getter 以及重写toString方便测试。
public class CollectionSetTest {
private int[] myInt;
private List<String> myList;
private Set<String> mySet;
private Map<String,Object> myMap;
public void setMyInt(int[] myInt) {
this.myInt = myInt;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, Object> myMap) {
this.myMap = myMap;
}
@Override
public String toString() {
return "CollectionSetTest [myInt=" + Arrays.toString(myInt) + ", myList=" + myList + ", mySet=" + mySet
+ ", myMap=" + myMap + "]";
}
}
完了后再去配置文件中注册这个类,命名为myCollection,再去给每个属性赋值,如下所示:
<!-- 注册CollectionSetTest -->
<bean id="myCollection" class="com.czp.ioc_di.CollectionSetTest">
<!-- property 的name要与类中的成员变量一样 -->
<property name="myInt">
<!-- 给数组注入值 -->
<array>
<value>1</value>
<value>2</value>
<value>3</value>
</array>
</property>
<!-- 给list注入 -->
<property name="myList">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<!-- 给set注入,跟数组,list一样,在value上写上值即可 -->
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
<!-- 给Map集合注入使用key/value键值对形式 -->
<property name="myMap">
<map>
<entry key="key1" value="value1"></entry>
<entry key="key2" value="value2"></entry>
<entry key="key3" value="value3"></entry>
</map>
</property>
</bean>
完了时候测试,在这里我用了常用的new ClassPathXmlApplicationContext(".."); 拿到bean容器。
再通过这个容器对象的getBean()方法获得配置好的myCollection这个Bean
@Test
public void Test1(){
ApplicationContext ac = new ClassPathXmlApplicationContext("com/czp/ioc_di/applicationContext.xml");
CollectionSetTest c= (CollectionSetTest)ac.getBean("myCollection");
System.out.println(c);
}
测试结果:
CollectionSetTest [myInt=[1, 2, 3], myList=[list1, list2, list3], mySet=[set1, set2, set3], myMap={key1=value1, key2=value2, key3=value3}]
----------------------------------------------------------------------------------------------------------------------------------------------------------
虽然这样子可以给每个属性注入值没错,但有没有感觉太麻烦了,一个值就要一对<value> </value>,有没有简单点的啊?哈哈 强大的Spring老大当然有了,像这样:
<!-- 注册CollectionSetTest -->
<bean id="myCollection" class="com.czp.ioc_di.CollectionSetTest">
<!-- property 的name要与类中的成员变量一样 -->
<!-- 给数组注入值 -->
<property name="myInt" value="1,2,3" />
<!-- 给list注入 -->
<property name="myList" value="list1,list2,list3" />
<!-- 给set注入,跟数组,list一样,在value上写上值即可 -->
<property name="mySet" value="set1,set2,set3" />
<!-- 给Map集合注入使用key/value键值对形式 -->
<property name="myMap" >
<map>
<entry key="key1" value="value1"></entry>
<entry key="key2" value="value2"></entry>
<entry key="key3" value="value3"></entry>
</map>
</property>
</bean>
数组,List,Set都能在<property>标签里的 value属性直接按顺序给属性注入值,而Map有key 跟 value两个属性,所以无法采用这种方法。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
给域属性注入
先创建两个类,Student 和 School, 在Student中有School school这个属性,再配置文件中是如何给它注入值的呢?
其实也很简单,
<!-- 注册School -->
<bean id="mySchool" class="com.czp.ioc_di.School">
<property name="sid" value="001" />
<property name="sname" value="蓝鸟"></property>
</bean>
<!-- 注册Student -->
<bean id="myStudent" class="com.czp.ioc_di.Student">
<!-- 给属性name注入值 -->
<property name="name" value="小明" />
<!-- 给域属性school注入 -->
<property name="school" ref="mySchool"></property>
</bean>
需要注意的是如果将 ref 改为 value的话,Spring会直接将value的值,比如在这里就是“mySchool” 注入给了school,但因为school是School类型的,value的值确实String类型,所以会报转换异常
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required
type 'com.czp.ioc_di.School' for property 'school': no matching editors or conversion strategy found
2.构造注入
所谓构造注入,就是说在类里面需要提供一个构造器,将需要交给spring的属性都当做参数传进去,这样的话大哥在初始化容器的时候就会调用你的构造器并且注入相对应的值。
依然是拿刚才那个例子,Student和School类,这一次给这两个类一个带参的构造方法以及一个不带参的构造方法
如下所示:
School类:
public class School {
private String sid;//学校编号
private String sname;//学校校名
//不带参的构造方法
public School() {
}
//带参的构造方法,将两个属性作为参数传进来
public School(String sid, String sname) {
this.sid = sid;
this.sname = sname;
}
public String getSid() {
return sid;
}
public String getSname() {
return sname;
}
@Override
public String toString() {
return "School [sid=" + sid + ", sname=" + sname + "]";
}
}
Student类:
public class Student {
private String name;// 学生姓名
private School school;//学生所读学校
//不带参的构造方法
public Student() {
}
//带参的构造方法
public Student(String name, School school) {
super();
this.name = name;
this.school = school;
}
public String getName() {
return name;
}
public School getSchool() {
return school;
}
@Override
public String toString() {
return "Student [name=" + name + ", school=" + school + "]";
}
}
需要注意的是,因为现在是测试构造注入,所以我把setter方法删掉。
现在在配置文件中配置
<!-- 注册School -->
<bean id="mySchool" class="com.czp.ioc_di.School">
<!-- 选择 constructor-arg标签 name属性为该bean的构造器的属性名字,value则为值 -->
<constructor-arg name="sid" value="001" />
<constructor-arg name="sname" value="蓝鸟"></constructor-arg>
</bean>
<!-- 注册Student -->
<bean id="myStudent" class="com.czp.ioc_di.Student">
<constructor-arg name="name" value="小明" />
<constructor-arg name="school" ref="mySchool" />
</bean>
测试结果
Student [name=小明, school=School [sid=001, sname=蓝鸟]]
事实上,指定哪一个属性时除了用name之外还有一个index,值得是该bean的构造器内一串参数的下标,索引是从0开始的。
<constructor-arg index="0" value="001" />
<constructor-arg index="1" value="蓝鸟" />
但这个非常不好,因为万一你把下标记混了呢,又或者忘记是从0还是1开始的呢。。
好,现在看一个很有趣的现象,我在刚刚说构造注入的时候,分别给每个类一个不带参的和带参的构造方法,那竟然spring老大是调用带参才能注入的,我要是不给不带参的会怎样呢?
下面我这两个类都把不带参的构造方法删掉:
School类
public class School {
private String sid;//学校编号
private String sname;//学校校名
//带参的构造方法,将两个属性作为参数传进来
public School(String sid, String sname) {
this.sid = sid;
this.sname = sname;
}
public String getSid() {
return sid;
}
public String getSname() {
return sname;
}
@Override
public String toString() {
return "School [sid=" + sid + ", sname=" + sname + "]";
}
}
Student类
public class Student {
private String name;// 学生姓名
private School school;//学生所读学校
//带参的构造方法
public Student(String name, School school) {
super();
this.name = name;
this.school = school;
}
public String getName() {
return name;
}
public School getSchool() {
return school;
}
@Override
public String toString() {
return "Student [name=" + name + ", school=" + school + "]";
}
}
配置文件我不动,看看结果是什么....
测试结果
Student [name=小明, school=School [sid=001, sname=蓝鸟]]
天啦撸,竟然一毛一样!!这是为什么啊~说好的 老大默认会调用无参构造器进行容器的初始化的呢???为什么现在有了有参就不用无参了呢?
哈哈,这是因为有了有参之后,spring底层就不会调用 Class.forName().newInstance() 方法,而是直接调用了带参构造器!
当然,为了规范以及不出错,最好将无参,setter方法都给加上。
3.针对域属性的自动注入
除了上述方法的域属性注入外,老大还提供了一种自动注入的方法,自动注入又可分两种,byName和byType
3.1 byName的域属性自动注入
在这里也还是用Student跟School两个类来举例子,而且分别给回了Setter方法,代码块就不引用了,直接看配置文件:
<!-- 注册School -->
<bean id="school" class="com.czp.ioc_di.School" >
<property name="sid" value="001" />
<property name="sname" value="蓝鸟"></property>
</bean>
<!-- 注册Student -->
<bean id="myStudent" class="com.czp.ioc_di.Student" autowire="byName">
<property name="name" value="小花" />
<!--<property name="school" ref="mySchoool"/>-->
</bean>
当设置了自动注入后就不用再给Student的school手动引用了,那么spring是怎么知道这个域属性的呢?原因就在于上面修改的School的bean id,一定要与Student类中的 School school;名字一样,这样的话容器就能从实体类中找与bean的id相同名字的属性,找到之后便把值赋值给他。
3.2 byType的域属性自动注入
完全一样,只不过把byName改成byType就可以了,这时候可以不用id,因为是根据类型查找的,所以id并不影响。
<!-- 注册School -->
<bean class="com.czp.ioc_di.School" >
<property name="sid" value="001" />
<property name="sname" value="蓝鸟"></property>
</bean>
<!-- 注册Student -->
<bean id="myStudent" class="com.czp.ioc_di.Student" autowire="byType">
<property name="name" value="小花" />
<!-- <property name="school" ref="mySchoool" /> -->
</bean>
我这里吧id删掉了,结果一眼
Student [name=小花, school=School [sid=001, sname=蓝鸟]]