依赖注入之setter注入:
依赖注入是IOC具体的一种实现方式, 这是针对资源获取的方式角度来说的,之前我们是被动接受,现在IOC具体的实现叫做依赖注入,从代码的角度来说,原来创建对象的时候需要new,而现在并不需要这样做,只需要将所依赖的对象给它设置相对应的方法,set方法也行,有参构造也行,以我们设置好的方法来接受spring为我们所注入的对象
比如,我们此时创建一个实体类,其中包含id,age,name等属性,那我们就说student对象是依赖于id/name/age这些属性的,既然student依赖于这些属性,那么我们就可以在IOC容器中为其进行赋值,依赖注入通俗点说就是为类中的属性赋值的过程
student类:
package xysfxy; public class student implements person { private Integer sid; private String sname; private Integer age; private String gender; public student() { } public Integer getSid() { return sid; } public void setSid(Integer sid) { this.sid = sid; } public String getSname() { return sname; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public void setSname(String sname) { this.sname = sname; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } @Override public String toString() { return "studnet{" + "sid=" + sid + ", sname=" + sname + ", age=" + age + ", gender='" + gender + '\'' + '}'; } public student(Integer sid, String sname, Integer age, String gender ) { this.age=age; this.sname=sname; this.gender=gender; this.sid = sid; } }
如下所示,当我们为student类中的name属性赋值,注意:name在这里是属性,那么什么叫属性呢?找到当前类中set和get方法,把方法中的set和get去掉,剩余部分的首字母变成小写的结果就是属性,由于我们当前是给属性赋值,因此需要寻找set方法
在spring.xml文件中为属性赋值:
<!-- property:通过成员变量的set方法进行赋值,name:设置需要赋值的属性名(和set方法有关),value:设置为属性所赋的值--> <bean id="student1" class="xysfxy.student" > <property name="sid" value="1001"></property> <property name="sname" value="张三"></property> <property name="age" value="20"></property> <property name="gender" value="男"></property> </bean>
创建测试类:
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import xysfxy.student; public class student_text { @Test public void test(){ ApplicationContext ioc=new ClassPathXmlApplicationContext("applicantsContext.xml"); student student= ioc.getBean(student.class); System.out.println(student); } }
输出如下所示:
studnet{sid=1001, sname=张三, age=20, gender='男'}
依赖注入之构造器注入:
在spring.xml文件中赋值:
<!-- 赋值时,一定要与构造器中的属性相对应--> <bean id="student1" class="xysfxy.student" > <constructor-arg value="1002"></constructor-arg> <constructor-arg value="李四"></constructor-arg> <constructor-arg value="19"></constructor-arg> <constructor-arg value="男"></constructor-arg> </bean>
其他不变,运行结果如下所示:
studnet{sid=1002, sname=李四, age=19, gender='男'}
需要注意以下两个问题:
1:如果出现了部分字段的值为null的情况,问题很可能出现在**有参构造器的参数名不一致问题** 2:使用<constructor-arg>标签赋值时,需要注意赋值的顺序和有参构造中参数列表顺序一致
在上述student类中,添加score属性,并对有参构造进行重载:
//新有参构造 public student(Integer sid, String sname, String gender , double score) { this.sid = sid; this.sname=name; this.gender=gender; this.score=score; } //原有参构造 public student(Integer sid, String sname, String gender, Integer age ) { this.sid = sid; this.sname=sname; this.gender=gender; this.age=age; } //重写tostring方法 @Override public String toString() { return "student{" + "sid=" + sid + ", sname='" + sname + '\'' + ", age=" + age + ", gender='" + gender + '\'' + ", score=" + score + '}'; } //为新添加的属性设置set和get方法 public double getScore() { return score; } public void setScore(double score) { this.score = score; }
spring.xml文件中的参数设置不变:
<bean id="student1" class="xysfxy.student" > <constructor-arg value="1002"></constructor-arg> <constructor-arg value="李四"></constructor-arg> <constructor-arg value="男"></constructor-arg> <constructor-arg value="24"></constructor-arg> </bean>
在当有参构造器中包含参数相同的情况下,spring.xml文件中的第四个数据24既可以匹配给整形age,也可以匹配给double类型的score,测试结果如下:
我们发现默认情况下,24.0匹配给了score,但事实我们是想实现将24.0匹配给年龄, 那么该如何实现呢?
关于这种情况网上的解决方法有很多,常用的有以下两种
方法1:spring.xml文件的bean标签的对应的属性值设置时,指定name="age"
方法2:将有参构造中包含age的构造方法放在包含score构造器的前面
依赖注入之特殊值处理:
字面量赋值:
什么是字面量?
例如:
int a=10;
声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字,当我们引用a的时候,我们实际上拿到的值是10,而如果a是带引号的"a",那么它现在不是一个变量,它就是代表a这个字母本身,这就是字面量,字面量没有引申含义,就是我们看到的这个数据本身
举例:
我们在spring.xml文件中设置gender属性的值为"null":
<bean id="student1" class="xysfxy.student" > <property name="sname" value="张三" ></property> <property name="sid" value="1002" ></property> <property name="gender" value="null"> </property> <property name="age" value="24" ></property> </bean>
输出如下所示:
student{sid=1002, sname='李四', age=24, gender='null', score=0.0}
我们发现,gender最终输出的值就是null,那他表示的是null对象还是值为null的字符串呢?
我们可通过将其值转化为字符串,如果没有报错,则证明当前的null为值是null的字符串:
如果它是null对象的话,应该报错为空指针异常,而不是输出null
System.out.println(student.getGender().toString());
输出如下所示:
正确的将属性设置为null对象的方法如下:
<bean id="student1" class="xysfxy.student" > <property name="sname" value="张三" ></property> <property name="sid" value="1002" ></property> <!-- 将gender属性设置为null对象,使用null标签表示,由于该标签中的内容是空,因此我们可以写为单标签 --> <property name="gender"><null/></property> </bean>
输出如下:
注意:将属性设置为null对象是无法通过构造器注入的方式进行的
xml实体:
小于号在XML文档中用来定义标签的开始,不能随便使用,
如下所示:
我们想输出name的值设置为一个带"<>
"的,如果直接添加带符号在XML文档是不可行的
解决方案:使用XML实体来代替,<表示小于号{lt:less than},>{great than}表示大于号,
修改上述代码如下所示:
<property name="sname" value="<张三>" ></property>
输出结果如下所示:此时sname的值中的特殊字符被成功显示出来
student{sid=1002, sname='<张三>', age=11, gender='null', score=0.0}
关于特殊符号无法被解析的问题,我们还有另外一种办法,就是使用CDATA节,CDATA中的C代表Character,是文本,字符的含义,CDATA就代表纯文本数据,XML解析器看到CDATA就知道这里是纯文本,就不会当做XML标签或属性来解析,所以CDATA节中写什么都任意,在IDE中,我们可以通过快捷键使用这个功能,在编译区域输CD[一定要大写],回车即可,如下所示:
<property name="sname" ><value><![CDATA[<张三>]]></value></property>
输出如下:
student{sid=1002, sname='<张三>', age=11, gender='null', score=0.0}
这种方式也可以正确的显示特殊字符,但需要注意的是它是XML中一个特殊的标签,我们不能将其当做value属性的值写在value属性的后面,而是要将其写在<value>
标签的后面
依赖注入之为类类型的属性赋值:
创建实体类班级:
package poij; public class clazz { private Integer cid; private String name; public Integer getCid() { return cid; } public void setCid(Integer cid) { this.cid = cid; } public String getName() { return name; } public void setName(String name) { this.name = name; } public clazz(Integer cid,String name) { this.cid = cid; this.name=name; } public clazz() { } @Override public String toString() { return "clazz{" + "cid=" + cid + ", name='" + name + '\'' + '}'; } }
修改student类:
//班级和学生之间是一对多的关系,将班级作为学生的一个属性加入到学生类中 private clazz clazz; //并为其设置set和get方法 public poij.clazz getClazz() { return clazz; } public void setClazz(poij.clazz clazz) { this.clazz = clazz; } //重写toString方法 @Override public String toString() { return "student{" + "sid=" + sid + ", sname='" + sname + '\'' + ", age=" + age + ", gender='" + gender + '\'' + ", clazz=" + clazz + '}'; }
那么对于student类来说,clazz属性该如何赋值呢?
我们不能使用value对其进行赋值,因为value是给字面量赋值的,但clazz对应的是一个对象
那么该如何给类类型赋值呢?
引用外部的bean给类类型赋值:
修改spring文件中的代码:
<!-- 在给类student中的某一个为引用类型数据的属性赋值时,使用引用外部bean的方式 ref:引用IOC容器中的某个bean--> <!-- 通过id找到我们要引用的bean对象--> <property name="clazz" ref="clazz1"></property> <!-- 创建类型为clazz的bean对象--> <bean id="clazz1" class="poij.clazz"> <property name="cid" value="1111"></property> <property name="name" value="菜鸟班"></property> </bean>
输出如下所示:
通过级联方式给类类型的属性赋值:
在mybatis中,我们也曾经使用过级联的方式为其赋值,也就是通过类.属性
的方式对其进行赋值,那么在spring中,也可以使用该方法吗?
如下所示:尝试使用类.属性
的方式直接赋值
<property name="clazz.cid" value="2222"></property> <property name="clazz.name" value="卷心菜班"></property>
运行报错:由此可见使用级联的方式给属性赋值可以在mybatis中使用,但是在spring中是不可取的
使用级联方式给类类型属性赋值的前提条件是:
1:引用类型属性的依赖对象必须在Spring容器中被正确创建和配置
:依赖对象必须在Spring容器中声明为一个bean
,并且通过配置文件或注解进行正确的配置。
2:引用类型属性的依赖对象必须可用:在给引用类型属性赋值之前,依赖对象必须已经被正确创建和初始化
,以确保可以正确注入。
方法1:先赋值再修改
在IOC容器中先创建clazz实体类的bean对象,并对其进行正确的配置,然后再student类中引用已经配置好的bean对象,我们可以通过级联的方式这个已经创建好的bean对象的属性值进行修改
<bean id="student1" class=" poij.student" > <property name="sid" value="1001"></property> <property name="sname" value="张三"></property> <property name="age" value="20"></property> <property name="gender" value="男"></property> <!-- 先引用外部已经创建好的bean对象 --> <property name="clazz" ref="clazz1"></property> <!-- 再对上述引用的bean对象的属性值进行修改--> <property name="clazz.cid" value="2222"></property> <property name="clazz.name" value="卷心菜班"></property> </bean> <!-- 创建外部的bean对象--> <bean id="clazz1" class="poij.clazz"> <property name="cid" value="1111"></property> <property name="name" value="菜鸟班"></property> </bean>
方法2:在student类中对clazz进行实例化
private clazz clazz= new clazz( );
在spring.xml文件中可直接通过级联的方式进行赋值[如下所示],未赋值的属性值默认为null:
<property name="clazz.cid" value="2222"></property> <property name="clazz.name" value="卷心菜班"></property>
上述任意一种,输出均为如下所示:
student{sid=1001, sname='张三', age=20, gender='男', clazz=clazz{cid=2222, name='卷心菜班'}}
虽然以级联的方式也可以对类类型进行赋值,但这种方法我们并不常用,我们通常使用内部bean的方式
,下面我们就具体学习一下内部bean的方式如何使用:
使用内部bean的方式为类类型赋值:
修改spring.xml中类型为student的bean标签,修改的部分为clazz属性所对应的:
<bean id="student1" class=" poij.student" > <property name="sid" value="1001"></property> <property name="sname" value="张三"></property> <property name="age" value="20"></property> <property name="gender" value="男"></property> <!-- 内部bean的方式为:通过设置一个内部的bean对象而为clazz对象进行赋值--> <property name="clazz"> <!-- 内部bean的属性赋值方法与外部的相同--> <bean id="clazz_inner" class="poij.clazz"> <property name="cid" value="333"></property> <property name="name" value="卷王班"></property> </bean> </property> </bean>
输出如下所示:
student{sid=1001, sname='张三', age=20, gender='男', clazz=clazz{cid=333, name='卷王班
内部bean就类似于我们在java中学过的内部类一样,它只能在bean的内部进行使用
,那么内部bean可以通过IOC容器进行获取吗?
修改测试类中的代码:
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import poij.clazz; public class student_text { @Test public void test(){ ApplicationContext ioc=new ClassPathXmlApplicationContext( "spring.xml"); //尝试获取内部bean clazz clazz1= ioc.getBean( clazz.class); System.out.println(clazz1); } }
输出如下所示:
看来内部bean确实无法通过IOC容器直接获取
虽然内部bean无法通过IOC容器直接获取,但是我们可以间接获取,方法如下所示:
public void test(){ ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applications.xml"); //先通过IOC容器直接获取类型为student的bean对象 student student=applicationContext.getBean(student.class); //再通过获取到的类型为student的bean对象获取clazz clazz clazz=student.getClazz(); System.out.println(clazz); }
说到这里,为类类型的属性赋值的三种方式我们已经说完了,引用外部的bean以及使用内部bean是我们所推荐的方法
,第二种使用级联并不是我们所推荐的
,原因是,它需要先赋值再修改,这样并不是真正意义上的赋值,而是修改已有的值
依赖注入之为数组类型的属性赋值:
修改student类中的代码—加入hobby属性:
//添加新的属性 private String [] hobby; //为新的属性编写set和get方法 public String[] getHobby() { return hobby; } public void setHobby(String[] hobby) { this.hobby = hobby; } //重写toString方法 @Override public String toString() { return "student{" + "sid=" + sid + ", sname='" + sname + '\'' + ", age=" + age + ", gender='" + gender + '\'' + ", clazz=" + clazz + ", hobby=" + Arrays.toString(hobby) + '}'; }
在spring.xml中为hobby属性赋值:
<bean id="student1" class=" poij.student" > <property name="sid" value="1001"></property> <property name="sname" value="张三"></property> <property name="age" value="20"></property> <property name="gender" value="男"></property> <property name="clazz"> <bean id="clazz_inner" class="poij.clazz"> <property name="cid" value="333"></property> <property name="name" value="卷王班"></property> </bean> </property> <!-- 为类型为数组的hobby属性赋值,数组的值为字面量,则使用value标签,如果为对象类型,则使用ref标签,通过的id从而获取对应的bean --> <property name="hobby"> <array> <value>吃饭</value> <value>睡觉</value> <value>打豆豆</value> </array> </property> </bean>
输出如下所示:
student{sid=1001, sname='张三', age=20, gender='男', clazz=clazz{cid=333, name='卷王班'},
依赖注入之为集合类型的属性赋值:
修改clazz类:
//1个班级可以有多个学生,因此将学生作为属性添加到班级中是以集合的形式 private List<student> students; //为其编写set和get方法 public List<student> getStudents() { return students; } public void setStudents(List<student> students) { this.students = students; } //重写toString方法 @Override public String toString() { return "clazz{" + "cid=" + cid + ", name='" + name + '\'' + ", students=" + students + '}'; }
内部list集合为集合类型的属性赋值:
修改spring.xml文件:
<!-- student1学生--> <bean id="student1" class="poij.student"> <property name="sid" value="1"></property> <property name="sname" value="张三"></property> <property name="age" value="20"></property> <property name="gender" value="女"></property> <property name="hobby"> <array> <value>吃饭</value> <value>睡觉</value> </array> </property> </bean> <!-- student2学生--> <bean id="student2" class="poij.student"> <property name="sid" value="2"></property> <property name="sname" value="李四"></property> <property name="age" value="12"></property> <property name="gender" value="男"></property> <property name="hobby"> <array> <value>唱歌</value> <value>打游戏</value> </array> </property> </bean> <!-- student3学生--> <bean id="student3" class="poij.student"> <property name="sid" value="3"></property> <property name="sname" value="lisa"></property> <property name="age" value="21"></property> <property name="gender" value="女"></property> <property name="hobby"> <array> <value>弹钢琴</value> <value>跳舞</value> </array> </property> </bean> <!-- clazz对象--在内部使用list标签,将上述的3个学生添加到班级当中--> <bean id="clazz1" class="poij.clazz"> <property name="cid" value="111"></property> <property name="name" value="软件1班"></property> <property name="students"> <list> <ref bean="student1"></ref> <ref bean="student2"></ref> <ref bean="student3"></ref> </list> </property> </bean>
输出如下所示:
clazz{cid=111, name='软件1班', students=[student{sid=1, sname='张三', age=20, gender=
引用list集合的bean为集合类型的属性赋值:
如果在IOC容器中存在一个list集合类型的bean,那么我们是否可以直接通过ref来引用呢?如下所示:
上述这种方式是不对的, 我们现在要创建的是一个list集合类型的bean,最主要的是要往list集合中去存储数据,但如果我们单独创建一个bean,类型是ArrayList,我们能做的只是为当前这个类型为ArrayList的类中的属性赋值,而不能通过bean标签为当前的这个集合去存取数据
正确方法:
我们去配置一个集合类型的bean,但这需要使用util约束
添加前spring.xml文件中是没有该约束的:
添加之后新增util约束:
修改spring.xml文件中的内容—修改clazzbean标签中的内容:
<!-- student1 --> <bean id="student1" class="poij.student"> <property name="sid" value="1"></property> <property name="sname" value="张三"></property> <property name="age" value="20"></property> <property name="gender" value="女"></property> <property name="hobby"> <array> <value>吃饭</value> <value>睡觉</value> </array> </property> </bean> <!-- student2 --> <bean id="student2" class="poij.student"> <property name="sid" value="2"></property> <property name="sname" value="李四"></property> <property name="age" value="12"></property> <property name="gender" value="男"></property> <property name="hobby"> <array> <value>唱歌</value> <value>打游戏</value> </array> </property> </bean> <!-- student3 --> <bean id="student3" class="poij.student"> <property name="sid" value="3"></property> <property name="sname" value="lisa"></property> <property name="age" value="21"></property> <property name="gender" value="女"></property> <property name="hobby"> <array> <value>弹钢琴</value> <value>跳舞</value> </array> </property> </bean> <!-- clazz对象--> <bean id="clazz1" class="poij.clazz"> <property name="cid" value="111"></property> <property name="name" value="软件1班"></property> <!-- 引用list集合的bean--> <property name="students" ref ="studentsList"></property> </bean> <!-- 配置一个集合类型的bean,需要使用util约束--> <util:list id="studentsList"> <ref bean="student1"></ref> <ref bean="student2"></ref> <ref bean="student3"></ref> </util:list>
输出如下所示:
clazz{cid=111, name='软件1班', students=[student{sid=1, sname='张三', age=20, gender='女', cla
依赖注入之为map集合类型的属性赋值:
创建新的teacher类:
package poij; public class teacher { private Integer tid; private String name; public Integer getTid() { return tid; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void setTid(Integer tid) { this.tid = tid; } public teacher() { } public teacher(Integer tid,String name) { this.tid = tid; this.name=name; } @Override public String toString() { return "teacher{" + "tid=" + tid + ", name='" + name + '\'' + '}'; } }
修改student类中的代码:
//将老师作为学生的一个属性添加进来 private Map<String,teacher> stringteacherMap; //为其设置set和get方法 public Map<String, teacher> getStringteacherMap() { return stringteacherMap; } public void setStringteacherMap(Map<String, teacher> stringteacherMap) { this.stringteacherMap = stringteacherMap; } //重写toString方法 @Override public String toString() { return "student{" + "sid=" + sid + ", sname='" + sname + '\'' + ", age=" + age + ", gender='" + gender + '\'' + ", clazz=" + clazz + ", hobby=" + Arrays.toString(hobby) + ", stringteacherMap=" + stringteacherMap + '}'; }
方法1:通过map标签进行设置
修改spring.xml文件:
假设我们现在要给id为student3的bean对象的stringteachermap属性赋值,如下所示,我们需要使用map标签而不是util:map,因为util:map的功能类似于util:list,它是用来配置一个类型为map的bean对象
由于map中的数据是以键值对的形式存取的,那么如何设置键值对呢?
在map标签中我们输入<
,显示的结果有以下两种,看到entry不知道大家是否会感到格外的熟悉,因为这是我们在java中就已经接触过的,它表示的是一个类型,map中的键和值,我们可以使用一个entry来表示map集合中的一个键值对
entry中可包含的参数有以下几种:和我们上面学习的其他类型是一样的,凡是带有-ref的,则表示这个参数的类型是一个对象而不是字面量,若仅仅是key和value,则证明这个参数是字面量
修改spring.xml文件中的内容:
<!-- 为id为student3的bean对象的stringteachermap属性赋值--> <bean id="student3" class="poij.student"> <property name="sid" value="3"></property> <property name="sname" value="lisa"></property> <property name="age" value="21"></property> <property name="gender" value="女"></property> <property name="hobby"> <array> <value>弹钢琴</value> <value>跳舞</value> </array> </property> <!-- 在student中,map<String,teacher>--> <property name="stringteacherMap"> <map > <!-- teacher则使用引用外部bean的方式--> <entry key="1号老师" value-ref="teacher1"></entry> <entry key="2号老师" value-ref="teacher2"></entry> </map> </property> </bean>
<!-- 创建类型为老师的bean对象--> <!-- teacher1--> <bean id="teacher1" class="poij.teacher"> <property name="tid" value="10086"></property> <property name="name" value="大宝"></property> </bean> <!-- teacher2--> <bean id="teacher2" class="poij.teacher"> <property name="tid" value="10010"></property> <property name="name" value="小宝"></property> </bean>
方法2:通过util:map进行设置
对spring.xml进行修改:
<!-- 创建了类型为老师的bean对象--> <bean id="teacher1" class="poij.teacher"> <property name="tid" value="10086"></property> <property name="name" value="大宝"></property> </bean> <bean id="teacher2" class="poij.teacher"> <property name="tid" value="10010"></property> <property name="name" value="小宝"></property> </bean>
<!-- 为id为student3的bean对象的stringteachermap属性赋值--> <bean id="student3" class="poij.student"> <property name="sid" value="3"></property> <property name="sname" value="lisa"></property> <property name="age" value="21"></property> <property name="gender" value="女"></property> <property name="hobby"> <array> <value>弹钢琴</value> <value>跳舞</value> </array> </property> <!-- 直接通过id值引用util:map对象--> <property name="stringteacherMap" ref="teacherMap"></property> </bean> <!-- 创建util:map对象--> <util:map id="teacherMap"> <entry key="1号老师" value-ref="teacher1"></entry> <entry key="2号老师" value-ref="teacher2"></entry> </util:map>
无论上述那种方法,输出均为如下所示:
student{sid=3, sname='lisa', age=21, gender='女', clazz=null, hobby=[弹钢琴, 跳舞], stringteacherMap={1
依赖注入之为p命名空间:
在spring.xml文件中创建新的bean对象:
<bean id="student4" class="poij.student" p:sid="12" p:sname="小白" p:stringteacherMap-ref
但我们使用上述这种p属性开头的方法,需要注意一定要引入p
。它必须有约束的支持才能够进行,如下所示:
使用p:开头的这种方式,我们会发现每个属性都有两个:一个是不带-ref后缀,一个是带-ref后缀
,在前面的学习中,我们就说过带-ref表示该属性的值是引用类型,而不带-ref的表示该属性的值是一个字面量,注意在选择的时候,不要选择错误了
在测试类中获取id为student4的bean对象:
student student=ioc.getBean("student4", poij.student.class); System.out.println(student);
输出如下所示,我们未赋值的属性是以null显示:
student{sid=12, sname='小白', age=null, gender='null', clazz=null, hobby=null, stringteacherMap={1号老师=teacher{tid=10086, name='大宝'}, 2号老师=teacher{tid=10010, name='小宝'}}}
这种方式虽然可行,但并不是常用的,因此我们了解一下即可!