target
掌握依赖注入的三种方式
掌握基于xml的bean装配
掌握基于注解的bean装配
掌握bean的自动装配
掌握注解 @Autowired 和 @Resource
1. 依赖注入
当一个bean创建出来后,是一个空对象,需要给bean进行赋值,这个bean才有意义。依赖注入就是给IoC中的bean进行赋值,一般可以分为三种。
1.1 set注入
以前的开发中,当创建完bean对象后,一般会使用set方法给对象进行赋值。
set注入,顾名思义就是用实体类的setter方法进行注入。
set注入是用的是最多的注入方式。
注入用的标签是property。
🌰新建 Java项目Spring-02
,给Teacher和Course进行注入:
第一步:在src下新建实体类:
Teacher.java:
package com.lee.spring.bean;
public class Teacher {
private int no;//工号
private String name;//姓名
// getter、setter、toString略
}
Course.java
package com.lee.spring.bean;
public class Course {
private String courseName;//课程名字
private int courseHours;//课时量
private Teacher courseTeacher;//授课老师
/ / getter、setter、toString略
}
第二步:在src下新建配置文件 applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<property name="name" value="张三"></property>
<property name="no" value="1001"></property>
</bean>
<!-- 将课程信息注入到IOC容器 -->
<bean id="course" class="com.lee.spring.bean.Course">
<property name="courseName" value="语文"></property>
<property name="courseHours" value="200"></property>
<property name="courseTeacher" ref="teacher"></property>
</bean>
</beans>
注意:
在赋值的时候,如果是简单类型(8个基本类型 + String),用value赋值。如果是对象类型,用ref(ref是reference的简写,引用的意思)。
第三步:编写一个测试类进行测试:
package com.lee.spring.test;
public class TestDemo {
@Test
public void test01() {
//获取上下文对象:Context
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean对象
Course course = (Course)context.getBean("course");
System.out.println(course);
}
}
输出:
Course [courseName=语文, courseHours=200, courseTeacher=Teacher [no=1001, name=张三]]
原理解释:
setter注入用到了实体类的setter方法,和getter方法、构造方法都没关系。我们可以在setter方法里面加一行输出语句验证。
set注入底层用的是反射技术,bean标签里的class是全类名,所以可以用Class.forName("全类名")拿到这个类的所有信息,然后用new Instance方法创建一个对象,getmethod()方法拿到所有的set方法,在调用setter方法将值注入。
Class clz = Class.forName("全类名");
Object obj = clz.newInstance();//创建对象
Method method = clz.getMethod("set方法名字");//获取setter方法
method.invoke(obj, value里面的值);//调用setter方法赋值
1.2 构造器注入
对象的创建,使用的是构造方法。
构造器注入用的是实体类的构造方法。
注入用的标签是constructor-arg
第一步:在src下新建实体类,并且生成构造方法
Teacher.java
package com.lee.spring.bean;
public class Teacher {
private int no;//工号
private String name;//姓名
public Teacher(int no, String name) {
this.no = no;
this.name = name;
}
public Teacher() {
}
@Override
public String toString() {
return "Teacher [no=" + no + ", name=" + name + "]";
}
}
Course.java
package com.lee.spring.bean;
public class Course {
private String courseName;//课程名字
private int courseHours;//课时量
private Teacher courseTeacher;//授课老师
public Course(String courseName, int courseHours, Teacher courseTeacher) {
this.courseName = courseName;
this.courseHours = courseHours;
this.courseTeacher = courseTeacher;
}
public Course() {
}
@Override
public String toString() {
return "Course [courseName=" + courseName + ", courseHours=" + courseHours + ", courseTeacher=" + courseTeacher
+ "]";
}
}
第二步:在 src下新建配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<constructor-arg name="name" value="李四"></constructor-arg>
<constructor-arg name="no" value="2002"></constructor-arg>
</bean>
<!-- 将课程信息注入到IOC容器 -->
<bean id="course" class="com.lee.spring.bean.Course">
<constructor-arg name="courseName" value="数学"></constructor-arg>
<constructor-arg name="courseHours" value="300"></constructor-arg>
<constructor-arg name="courseTeacher" ref="teacher"></constructor-arg>
</bean>
</beans>
第三步:编写一个测试类,进行测试
package com.lee.spring.test;
public class TestDemo {
@Test
public void test02() {
//获取上下文对象:Context
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean对象
Course course = (Course)context.getBean("course");
System.out.println(course);
}
}
输出:Course [courseName=数学, courseHours=300, courseTeacher=Teacher [no=2002, name=李四]]
补充:配置文件还可以这样写
- 省略name:但是顺序要和构造方法中参数的顺序一致
<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.entity.Teacher">
<constructor-arg value="2002"></constructor-arg>
<constructor-arg value="李四"></constructor-arg>
</bean>
<!-- 将课程信息注入到IOC容器 -->
<bean id="course" class="com.lee.entity.Course">
<constructor-arg value="数学"></constructor-arg>
<constructor-arg value="300"></constructor-arg>
<constructor-arg ref="teacher"></constructor-arg>
</bean>
- 用index:下标要从0开始
<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.entity.Teacher">
<constructor-arg index="0" value="2002"></constructor-arg>
<constructor-arg index="1" value="李四"></constructor-arg>
</bean>
<!-- 将课程信息注入到IOC容器 -->
<bean id="course" class="com.lee.entity.Course">
<constructor-arg index="0" value="数学"></constructor-arg>
<constructor-arg index="1" value="300"></constructor-arg>
<constructor-arg index="2" ref="teacher"></constructor-arg>
</bean>
原理解释:
构造器注入用到了实体类的构造方法。我们可以在构造方法里面加一行输出语句验证。
构造器注入底层用的也是反射技术,bean标签里的class是全类名,所以可以用Class.forName("全类名")拿到这个类的所有信息,然后用getConstructor()方法获取构造器,调用newInstance()方法就可以创造出对象。
Class clz = Class.forName("全类名");
Constructor constructor = clz.getConstructor(int.class,String.class);
Object instance = constructor.newInstance("value里面的值");
1.3 p命名空间注入
首先需要引入p命名空间,在namespaces里将p勾上。然后在bean标签里面写就行了,注意中间要加上空格。此时直接编写配置文件是没有提示的,当在实体类中加上setter方法,就有提示了,所以p命名空间本质上也是用的setter注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.entity.Teacher" p:name="王五" p:no="3003"></bean>
<!-- 将课程信息注入到IOC容器 -->
<bean id="course" class="com.lee.entity.Course" p:courseName="hadoop" p:courseHours="280" p:courseTeacher-ref="teacher"></bean>
</beans>
注意:
没有
-ref
就是简单注入,p:courseName="hadoop"
有-ref
就是对象注入,p:courseTeacher-ref="teacher"
多个属性赋值时,中间要加空格
三种注入方式,我们绝大多数用set注入。
2. bean装配概述
bean的装配其实做的只有两件事:为bean赋值、组合bean之间的关系。
在 Spring 中提供了 3 种方法进行装配:
基于xml配置装配bean
基于注解装配bean
自动装配
在现实的工作中,这 3 种方式都会被用到,并且在学习和工作之中常常混合使用,所以这里给出一些关于这 3 种优先级的建议:
① 最优先:自动装配
基于约定优于配置的原则,这种方式应该是最优先的
好处:减少程序开发者的决定权,简单又不失灵活。
② 其次:基于注解装配bean
在没有办法使用自动装配原则的情况下应该优先考虑此类方法
好处:避免 XML 配置的泛滥,也更为容易。
典型场景:一个父类有多个子类,比如学生类有两个子类,一个男学生类和女学生类,通过 IoC 容器初始化一个学生类,容器将无法知道使用哪个子类去初始化,这个时候可以使用 Java 的注解配置去指定。
③ 最后:XML 方式配置
在上述方法都无法使用的情况下,那么也只能选择 XML 配置的方式。
好处:简单易懂
通俗来讲,现在开发中,优先考虑 注解装配 + 自动装配。老项目的维护,可以考虑使用 xml方式 装配。
3. 基于xml的装配
前面所讲到的装配都是基于xml方式的装配,因为基于xml的装配更容易理解。
3.1 简单装配
简单装配就是给一个8个基本数据类型+String的属性赋值,比如:
<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<property name="name" value="张三"></property>
<property name="no" value="1001"></property>
</bean>
3.2 集合属性装配
对于一些集合属性的赋值,比如list集合、set集合、map集合、数组、Properties等,我们可以这样来赋值:
第一步:新建实体类
public class CollectionInject {
private List<String> listElement;
private Set<String> setElement;
private String[] strElement;
private Map<String,String> mapElement;
private Properties propElement;
//getter、setter省略
@Override
public String toString() {
return "listElement=" + listElement + "\nsetElement=" + setElement + "\nstrElement="
+ Arrays.toString(strElement) + "\nmapElement=" + mapElement + "\npropElement=" + propElement ;
}
}
第二步:在 src新建配置文件 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 将集合注入到IOC容器 -->
<bean id="coll" class="com.lee.spring.bean.CollectionInject">
<!-- list集合注入 -->
<property name="listElement" >
<list>
<value>足球</value>
<value>篮球</value>
<value>乒乓球</value>
</list>
</property>
<!-- set集合注入 -->
<property name="setElement">
<set>
<value>语文</value>
<value>数学</value>
<value>英语</value>
</set>
</property>
<!-- 数组集合注入 -->
<property name="strElement">
<array>
<value>手机</value>
<value>电脑</value>
<value>平板</value>
</array>
</property>
<!-- map集合注入 -->
<property name="mapElement">
<map>
<entry>
<key>
<value>shoes</value>
</key>
<value>鞋子</value>
</entry>
<entry>
<key>
<value>cap</value>
</key>
<value>帽子</value>
</entry>
<entry>
<key>
<value>coat</value>
</key>
<value>外套</value>
</entry>
</map>
</property>
<!-- property注入 -->
<property name="propElement">
<props>
<prop key="bread">面包</prop>
<prop key="chocolate">巧克力</prop>
<prop key="milk">牛奶</prop>
</props>
</property>
</bean>
</beans>
记忆技巧:除了properties,所有的集合赋值都在value标签里进行。
第三步:在测试类中测试
@Test
public void test04() {
//获取上下文对象:Context
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean对象
CollectionInject coll = (CollectionInject)context.getBean("coll");
System.out.println(coll);
}
输出:
listElement=[足球, 篮球, 乒乓球]
setElement=[语文, 数学, 英语]
strElement=[手机, 电脑, 平板]
mapElement={shoes=鞋子, cap=帽子, coat=外套}
propElement={bread=面包, chocolate=巧克力, milk=牛奶}
注意:
其中list、set、array可以混合使用
也可以用构造器赋值,和set注入一样,就是换个标签而已
<constructor-arg name="listElement" >
<list>
<value>足球</value>
<value>篮球</value>
<value>乒乓球</value>
</list>
</constructor-arg>
3.3 特殊值装配
如果我们想注入一些特殊值:>、<、&等,可以考虑<value>
标签。
value和<value>
注入的区别:
<value>标签 | value属性 | |
---|---|---|
参数位置 | 写在首尾标签(<value></value>)的中间,不加双引号 | 写在value的属性值中,必须加双引号 |
参数值包含特殊符号 | 方式一:使用<![CDATA[]]>标记 方式二:使用xml预定义的实体引用 |
使用xml预定义的实体引用 |
参数值带双引号输出 | 可以 | 不可以 |
其中,xml预定义实体引用:
实体引用 | 符号 |
---|---|
> | > |
< | < |
& | & |
注意有分号。
① 字符串
配置文件:
<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<property name="name" value="王五"></property>
<property name="no" value="5005"></property>
</bean>
测试:
@Test
public void test05() {
//获取上下文对象:Context
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean对象
Teacher teacher = (Teacher)context.getBean("teacher");
System.out.println(teacher);
}
输出:[no=5005, name=王五]
还可以使用<value>
写:
<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<property name="name" ><value>王五</value></property>
<property name="no" ><value>5005</value></property>
</bean>
我们如果想输出:[no=5005, name="王五"],就必须用<value>
标签:
<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher2" class="com.lee.spring.bean.Teacher">
<property name="name" ><value>"王五"</value></property>
<property name="no" ><value>5005</value></property>
</bean>
② 特殊字符
a. 使用<value>标签
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<property name="name">
<value><![CDATA[<&>]]></value>
</property>
<property name="no" value="1001"></property>
</bean>
或者
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<property name="name">
<value>><&</value>
</property>
<property name="no" value="1001"></property>
</bean>
b. 使用value属性
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<property name="name" value="><&"></property>
<property name="no" value="1001"></property>
</bean>
开发中绝大多数使用value属性的方式注入
③ 赋空值
a. 空字符串""
装配空字符串有两种方式:
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<property name="name" value=""></property>
</bean>
或者
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<property name="name" ><value></value></property>
</bean>
b. null
null表示该对象是空的,只能这样做:用<null>标签
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<property name="name"> <null/></property>
</bean>
注意:不能用下面方法做
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<property name="name" value="null"></property>
</bean>
这样表示这个人的名字叫null,而不是空。
4. 基于注解的装配
基于注解的装配就是不使用xml配置文件,使用注解来进行装配。
🌰用三层结构中的Service层来模拟一下注解装配。
用配置文件的方法需要这样做:
StudentService.java
package com.lee.spring.service;
public class StudentService {
public void addStudent() {
System.out.println("增加学生。。。");
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="studentService" class="com.lee.spring.service.StudentService"></bean>
</beans>
此时,StudentService类就已经纳入到IOC容器中了,可以通过eclipse左边StudentService类旁边的S来确定是不是纳入成功。
4.1 用注解装配步骤
首先需要在配置文件中打开包扫描功能:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!--开启包扫描-->
<context:component-scan base-package="com.lee.spring.service"></context:component-scan>
</beans>
然后在StudentService类头上加相关注解即可:
package com.lee.spring.service;
import org.springframework.stereotype.Component;
@Component("studentService")
public class StudentService {
public void addStudent() {
System.out.println("增加学生。。。");
}
}
说明
第一点:
@Component("studentService")
就等价于<bean id="studentService" class="com.lee.spring.service.StudentService"></bean>
注解括号里面的值就是bean的id值。
第二点:
在Spring启动的时候,会根据<context:component-scan base-package="com.lee.spring.service"></context:component-scan>
中的base-package去扫描这个包下的所有类,如果发现该包下有哪个类存在组件相关的注解,就将他纳入到IOC容器中。
4.2 其他注解
开发中会用到下面四个注解:
@Component:可以在三层中的任何一层中使用,还有一些功能性的bean使用。
@Controller:控制器层使用。
@Service:service层使用。
@Repository:dao层使用。
下面演示一个用@Component注解给普通的bean赋值:
第一步:直接在实体类上标注 @Component("p") 注解,p是bean的名字。
第二步:用@Value("小花")注解直接在属性上赋值,不需要getter、setter方法。
- 实体类 Person.java:
@Component("p")
public class Person {
@Value("1001")
private int id;
@Value("小花")
private String name;
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
}
}
- 配置文件 applicationContext.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!--开启包扫描-->
<context:component-scan base-package="com.lee.spring.bean"></context:component-scan>
</beans>
- 测试
@Test
public void test08() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person p = (Person)context.getBean("p");
System.out.println(p);
}
输出:Person [id=1001, name=小花]
5. 自动装配
开发中有一个原则:约定优于配置。所以能够按照约定自动去装配的尽量自动装配。
但是需要注意:自动装配仅适用于对象类型,对于简单类型不适用。
自动装配分为两种,一种是基于xml方式的自动装配,一种是基于注解方式的自动装配。基于注解方式的自动装配是比较常用的。
5.1 基于xml的自动装配
自动装配分为三种,分别是 通过名字装配、通过类型装配、使用构造器装配。
① byName
- 新建实体类:
Teacher.java
public class Teacher {
private int no;
private String teaName;
//getter、setter省略
@Override
public String toString() {
return "Teacher [no=" + no + ", teaName=" + teaName + "]";
}
}
Course.java
public class Course {
private String courseName;//课程名字
private int courseHours;//课时量
private Teacher courseTeacher;//授课老师
//getter、setter省略
@Override
public String toString() {
return "[课程名字:" + courseName + ", 课时量:" + courseHours + ", 老师信息:" + courseTeacher + "]";
}
}
- 编写配置文件applicationContext.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.entity.Teacher">
<property name="teaName" value="张三"></property>
<property name="no" value="1001"></property>
</bean>
<!-- 将课程信息注入到IOC容器 -->
<bean id="course" class="com.lee.entity.Course">
<property name="courseName" value="语文"></property>
<property name="courseHours" value="200"></property>
<property name="courseTeacher" ref="teacher"></property>
</bean>
</beans>
以上的装配是手工装配的。
自动装配就是我不写<property name="courseTeacher" ref="teacher"></property>
这行代码,想办法让程序自动在IOC容器中去寻找teacher这个bean,答案是可以的。删掉这行代码,然后在course的标签中加一句 autowire="byName" 。
运行程序,发现老师信息是null,说明没有程序没有找到对应的teacher的bean。既然是按照名字自动装配,所以得保证属性的名字和bean的名字一致
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 将老师信息注入到IOC容器 -->
<bean id="courseTeacher" class="com.lee.entity.Teacher">
<property name="teaName" value="张三"></property>
<property name="no" value="1001"></property>
</bean>
<!-- 将课程信息注入到IOC容器 -->
<bean id="course" class="com.lee.entity.Course" autowire="byName">
<property name="courseName" value="语文"></property>
<property name="courseHours" value="200"></property>
</bean>
</beans>
- 测试
@Test
public void testAutowire() {
//获取上下文对象:Context
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean对象
Course course = (Course)context.getBean("course");
System.out.println(course);
}
按照名字装配,必须保证属性名和被装配bean的id名一致。
② byType
byType自然就是按照类型装配。
在被装配的bean中加入autowire="byType"
:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 将老师信息注入到IOC容器 -->
<bean class="com.lee.entity.Teacher">
<property name="teaName" value="张三"></property>
<property name="no" value="1001"></property>
</bean>
<!-- 将课程信息注入到IOC容器 -->
<bean id="course" class="com.lee.entity.Course" autowire="byType">
<property name="courseName" value="语文"></property>
<property name="courseHours" value="200"></property>
</bean>
</beans>
如果IOC容器中有一个bean的类型恰好是该类的引用类型,则可以按照类型自动装配。
弊端:
如果IOC容器中有两个类型一样的bean,则程序不知道该装配哪一个,就会装配失败。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 将老师信息注入到IOC容器 -->
<bean class="com.lee.entity.Teacher">
<property name="teaName" value="张三"></property>
<property name="no" value="1001"></property>
</bean>
<!-- 将老师2信息注入到IOC容器 -->
<bean class="com.lee.entity.Teacher">
<property name="teaName" value="李四"></property>
<property name="no" value="2002"></property>
</bean>
<!-- 将课程信息注入到IOC容器 -->
<bean id="course" class="com.lee.entity.Course" autowire="byType">
<property name="courseName" value="语文"></property>
<property name="courseHours" value="200"></property>
</bean>
</beans>
会抛这样一个异常:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.lee.entity.Teacher' available: expected single matching bean but found 2: com.lee.entity.Teacher#0,com.lee.entity.Teacher#1
③ constructor
根据构造器去自动装配。所以实体类要有构造方法。
配置文件: autowire="constructor"
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="teacher" class="com.lee.spring.bean.Teacher">
<constructor-arg name="no" value="1001"></constructor-arg>
<constructor-arg name="name" value="张晓三"></constructor-arg>
</bean>
<bean id="course" class="com.lee.spring.bean.Course" autowire="constructor">
<constructor-arg name="courseName" value="Spring"></constructor-arg>
<constructor-arg name="courseHours" value="100"></constructor-arg>
</bean>
</beans>
注意:同样IOC容器中不能有两个teacher类型的bean,否则装配失败,并且抛异常。
全局自动装配
全局自动装配使用:default-autowire
可以给每一个类进行自动装配,也能给全局bean都设置自动装配。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName"
>
<!-- 将老师信息注入到IOC容器 -->
<bean id ="courseTeacher" class="com.lee.entity.Teacher">
<property name="teaName" value="张三"></property>
<property name="no" value="1001"></property>
</bean>
<!-- 将课程信息注入到IOC容器 -->
<bean id="course" class="com.lee.entity.Course" >
<property name="courseName" value="语文"></property>
<property name="courseHours" value="200"></property>
</bean>
</beans>
注意:
局部bean的自动装配可以覆盖全局bean自动装配。
5.2 基于注解的自动装配
基于注解的自动装配有两种,分别是 通过类型自动装配 和 通过名字自动装配。
基于注解的装配在开发中是最常用的,一般三层架构的装配都是使用注解自动装配。
① @Autowired
@Autowired默认根据类型进行装配。和 byType是一样的。
用配置文件方式模拟一个开发中的三层架构:
Dao层:
package com.lee.spring.dao;
public interface StudentDao {
void addStudent();
}
package com.lee.spring.dao.impl;
public class StudentDaoImpl implements StudentDao {
@Override
public void addStudent() {
System.out.println("添加学生...");
}
}
Service层:
package com.lee.spring.service;
public interface StudentService {
void addStudent();
}
package com.lee.spring.service.impl;
public class StudentServiceImpl implements StudentService{
StudentDao studentDao;
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Override
public void addStudent() {
studentDao.addStudent();
}
}
Controller层:
package com.lee.spring.controller;
public class AddStudentController {
public void setStudentService(StudentService studentService) {
this.studentService = studentService;
}
StudentService studentService;
public void addStudent() {
studentService.addStudent();
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 将控制器纳入到IOC容器 -->
<bean id="addStudentController" class="com.lee.spring.controller.AddStudentController">
<property name="studentService" ref="studentService"></property>
</bean>
<!-- 将studentService纳入到IOC容器 -->
<bean id="studentService" class="com.lee.spring.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"></property>
</bean>
<!-- 将studentDao纳入到IOC容器 -->
<bean id="studentDao" class="com.lee.spring.dao.impl.StudentDaoImpl"></bean>
</beans>
测试:
@Test
public void test08() {
//获取上下文对象:Context
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean对象
AddStudentController addStudentController = (AddStudentController)context.getBean("addStudentController");
addStudentController.addStudent();
}
用注解方式实现:
Dao层:
package com.lee.spring.dao;
public interface StudentDao {
void addStudent();
}
package com.lee.spring.dao.impl;
@Repository
public class StudentDaoImpl implements StudentDao {
@Override
public void addStudent() {
System.out.println("添加学生...");
}
}
Service层:
package com.lee.spring.service;
public interface StudentService {
void addStudent();
}
package com.lee.spring.service.impl;
@Service
public class StudentServiceImpl implements StudentService{
@Autowired
StudentDao studentDao;
@Override
public void addStudent() {
studentDao.addStudent();
}
}
Controller层:
package com.lee.spring.controller;
@Controller
public class AddStudentController {
@Autowired
StudentService studentService;
public void addStudent() {
studentService.addStudent();
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.lee"></context:component-scan>
</beans>
如果IOC容器没有StudentDao这个bean,StudentService层源码是这样:
package com.lee.spring.service.impl;
@Service
public class StudentServiceImpl implements StudentService{
@Autowired
StudentDao studentDao;
@Override
public void addStudent() {
studentDao.addStudent();
}
}
当容器获取stuService组件时,程序会报错,因为stuService依赖StudentDao,但是IOC容器还没有StudentDao,所以报错。怎么让程序不依赖StudentDao:在@AutoWired注解后加:required = false
package com.lee.spring.service.impl;
@Service
public class StudentServiceImpl implements StudentService{
@Autowired(required = false)
StudentDao studentDao;
@Override
public void addStudent() {
studentDao.addStudent();
}
}
② @Qualifier
当一个接口有多个实现类时,需要用@Autowired + @Qualifier组合来区分到底调用哪个类的实现。
@Controller("addStudentController")
public class AddStudentController {
@Autowired
@Qualifier("stuService")
StudentService studentService;
public void addStudent() {
studentService.addStudent();
}
}
其中@Qualifier("stuService")括号中的stuService就是我们之前定义@Service注解的名称。
注意:@Qualifier必须和@Autowired一起使用才有效。
③ @Resource
@Resource注解是通过byName注入的。
@Controller("addStudentController")
public class AddStudentController {
@Resource(name = "stuService")
StudentService studentService;
public void addStudent() {
studentService.addStudent();
}
}
④ @Resource和@Autowired区别
① @Autowired为Spring提供的注解。@Resource并不是Spring的注解,它的包是javax.annotation.Resource
② @Autowired按照byType注入,@Resource默认按照ByName自动注入。