spring中是通过DI实现IOC的, 今天我们来学习DI
目录
简单类型 : spring中规定基本数据类型和String都是简单类型
简介
di : 依赖注入, 表示创建对象, 给属性赋值
两种手段
1. 配置文件的方式: 使用标签和属性完成, 叫做基于XML的di实现
2. 注解: 使用spring中的注解完成, 叫做基于注解的di实现
di的语法分类
1. set注入(设值注入): spring中调用类的set方法, 用set方法可以实现属性的赋值
2. 构造注入, spring调用类的构造方法, 创建对象, 在构造方法中完成赋值
实现步骤
1. 创建maven项目
2. 加入maven依赖
spring依赖, 版本为 5.2.5
<!-- Spring核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
Junit依赖(单元测试)
3. 创建类(接口和它的实现类)
和没有使用框架一样, 就是普通的类
4. 创建spring需要的配置文件
声明类的信息, 这些类由spring创建和管理
通过spring的语法, 完成属性的赋值
5. 测试spring是否成功创建对象
set注入
set注入(设值注入)实例
简单类型 : spring中规定基本数据类型和String都是简单类型
简单类型的set注入实例
Student.java如下
public class Student {
private String name;
private int age;
public void setName(String name) {
System.out.println("调用name的set方法");
this.name = name;
}
public void setAge(int age) {
System.out.println("调用age的set方法");
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
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">
<!-- 声明student对象-->
<!--
di : 给属性赋值
简单类型 : spring中规定基本数据类型和String都是简单类型
1. set注入(注入就是设值) : spring调用类的set方法(没有set方法会运行时异常), 可以在set方法中完成属性赋值
简单类型的set注入
<bean id="xx" class="yyyy">
一个property只能给一个属性赋值
<property name="属性的名字" value="属性的值" />
</bean>
-->
<bean id="myStudent" class="org.example.ba01.Student"><!--相当于调用无参构造-->
<property name="name" value="张三" /><!--相当于调用name的set方法 setName("张三")-->
<!--不管value是什么类型, 必须加双引号, 这个是xml文件的规定-->
<property name="age" value="20" /><!--相当于调用age的set方法 setAge(20)-->
</bean>
</beans>
测试代码如下:
public class MyTest {
@Test
public void test01() {
String config = "ba01/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
// 从容器中获取myStudent对象
Student myStudent = (Student) ac.getBean("myStudent");
System.out.println(myStudent);
}
}
控制台打印结果如下:
调用name的set方法
调用age的set方法
Student{name='张三', age=20}
前面两句说明标签<property>实际上是spring容器自己调用set方法
重要问题 : 如果没有属性, 但是有set方法, spring可以执行吗
给出例子:
测试代码不变,
Student.java如下:
public class Student {
private String name;
private int age;
public void setName(String name) {
System.out.println("调用name的set方法");
this.name = name;
}
public void setAge(int age) {
System.out.println("调用age的set方法");
this.age = age;
}
// 注意这里, 没有email属性, 但是有set方法
public void setEmail(String string) {
System.out.println("执行setEmail方法");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
applicationContext.xml文件如下:
<bean id="myStudent" class="org.example.ba01.Student"><!--相当于调用无参构造-->
<property name="name" value="张三" /><!--相当于调用name的set方法 setName("张三")-->
<property name="age" value="20" /><!--相当于调用age的set方法 setAge(20)-->
<property name="email" value="123@123.com" />
</bean>
执行测试代码:
结果如下:
调用name的set方法
调用age的set方法
执行setEmail方法
Student{name='张三', age=20}
打印结果可见, setEmail方法也会调用, 所以<property>标签只是执行set方法, 不管你有没有这个属性
引用类型的set注入
Student.java信息如下:
public class Student {
private String name;
private int age;
// 声明一个引用类型
private School school;
public void setName(String name) {
System.out.println("调用name的set方法");
this.name = name;
}
public void setAge(int age) {
System.out.println("调用age的set方法");
this.age = age;
}
public void setSchool(School school) {
this.school = school;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + school +
'}';
}
}
School.java信息如下:
public class School {
private String name;
private String address;
public void setName(String name) {
this.name = name;
}
public void setAddress(String address) {
this.address = address;
}
}
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">
<!-- 声明student对象-->
<!--
di : 给属性赋值
简单类型 : spring中规定基本数据类型和String都是简单类型
1. set注入(注入就是设值) : spring调用类的set方法, 可以在set方法中完成属性赋值
引用类型的set注入
<bean id="xx" class="yyy">
<property name="属性的名字" ref="bean的id(对象的名称)" />
</bean>
-->
<bean id="myStudent" class="org.example.ba02.Student">
<property name="name" value="张三"/>
<property name="age" value="20"/>
<property name="school" ref="mySchool"/>
<!--注意这里的mySchool在下面, spring扫描文件如果没有找到, 会再次扫描,
这里的顺序并不影响-->
</bean>
<!-- 声明School类型的对象-->
<bean id="mySchool" class="org.example.ba02.School" >
<property name="name" value="北大"/>
<property name="address" value="北京"/>
</bean>
</beans>
测试代码如下:
public class MyTest {
@Test
public void test01() {
String config = "ba02/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
// 从容器中获取myStudent对象
Student myStudent = (Student) ac.getBean("myStudent");
System.out.println(myStudent);
}
}
打印结果为:
调用name的set方法
调用age的set方法
Student{name='张三', age=20, school=org.example.ba02.School@1b26f7b2}
构造注入 : 在构造实例的同时, 完成对象的实例化
给出代码例子:
Student.java如下:
public class Student {
private String name;
private int age;
// 声明一个引用类型
private School school;
public Student() {
System.out.println("这是无参构造");
}
public Student(String name, int age, School school) {
System.out.println("这是有参构造");
this.name = name;
this.age = age;
this.school = school;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + school +
'}';
}
}
School.java如下:
public class School {
private String name;
private String address;
public School(String name, String address) {
this.name = name;
this.address = address;
}
@Override
public String toString() {
return "School{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
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">
<!-- 声明student对象-->
<!--
di : 给属性赋值
简单类型 : spring中规定基本数据类型和String都是简单类型
构造注入 :
使用 <constructor-arg> 标签
一个 <constructor-arg> 标签表示构造方法的一个参数
<constructor-arg> 标签属性 :
name : 表示构造方法的形参名
index : 表示构造方法的参数位置, 从左往右从0开始
value : 如果构造方法的形参类型是简单类型, 使用value属性为其赋值
ref : 构造方法的形参的参数如果是引用类型, 使用ref属性为其赋值
-->
<!--使用属性name-->
<bean id="myStudent" class="org.example.ba03.Student" >
<constructor-arg name="age" value="20"/>
<constructor-arg name="name" value="张三"/>
<constructor-arg name="school" ref="mySchool"/>
</bean>
<!--使用属性index-->
<bean id="myStudent1" class="org.example.ba03.Student">
<constructor-arg index="0" value="李四"/>
<constructor-arg index="1" value="21"/>
<constructor-arg index="2" ref="mySchool"/>
</bean>
<!--省略属性index-->
<bean id="myStudent2" class="org.example.ba03.Student">
<constructor-arg value="王五"/>
<constructor-arg value="21"/>
<constructor-arg ref="mySchool"/>
</bean>
<bean id="mySchool" class="org.example.ba03.School">
<constructor-arg name="name" value="北京大学"/>
<constructor-arg name="address" value="北京"/>
</bean>
</beans>
测试代码如下:
public class MyTest {
@Test
public void test01() {
String config = "ba03/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
// 从容器中获取myStudent对象
Student myStudent = (Student) ac.getBean("myStudent");
System.out.println(myStudent);
// 从容器中获取myStudent1对象
Student myStudent1 = (Student) ac.getBean("myStudent1");
System.out.println(myStudent1);
// 从容器中获取myStudent2对象
Student myStudent2 = (Student) ac.getBean("myStudent2");
System.out.println(myStudent2);
}
}
控制台打印结果如下:
这是有参构造
这是有参构造
这是有参构造
Student{name='张三', age=20, school=School{name='北京大学', address='北京'}}
Student{name='李四', age=21, school=School{name='北京大学', address='北京'}}
Student{name='王五', age=21, school=School{name='北京大学', address='北京'}}
配置文件方式总结
di : 给属性赋值
简单类型 : spring中规定基本数据类型和String都是简单类型
1. set注入(注入就是设值)
spring调用类的set方法(没有set方法会运行时异常), 可以在set方法中完成属性赋值
简单类型的set注入
<bean id="xx" class="yyyy">
一个property只能给一个属性赋值
<property name="属性的名字" value="属性的值" />
</bean>
引用类型的set注入
<bean id="xx" class="yyy">
<property name="属性的名字" ref="bean的id(对象的名称)" />
</bean>
问题 : 如果没有属性, 但是有set方法, spring可以执行吗?
答案是可以
两个bean标签有依赖关系, 那么他们在配置文件中的先后顺序有影响吗
答案是没有影响, 因为如果第一遍没有找到对应的对象, 那么spring会浏览配置文件第二遍
2. 构造注入 : 调用有参数的构造方法
使用 <constructor-arg> 标签
一个 <constructor-arg> 标签表示构造方法的一个参数
<constructor-arg> 标签属性 :
name : 表示构造方法的形参名
index : 表示构造方法的参数位置, 从左往右从0开始
value : 如果构造方法的形参类型是简单类型, 使用value属性为其赋值
ref : 构造方法的形参的参数如果是引用类型, 使用ref属性为其赋值
引用类型的自动注入
引用类型的自动注入
spring框架根据某些规则可以自动给引用类型赋值, 简化程序员工作
常用的规则为byName 和 byType
1. byName(按名称注入)
java类中的引用类型的属性名和spring配置文件<bean>的id相同时
且数据类型一样, 这样容器中的bean可以由spring自动赋值
语法
<bean id="xxx" class="yyy" autowire="byName">
简单类型数据赋值
</bean>
2. byType(按类型注入)
java类中引用类型的数据类型和spring配置文件<bean>的class属性
是同源关系, 这样的bean能够赋值给引用类型
同源的意思是:
1. java类中引用类型和spring配置文件bean的class属性一样
2. java类中引用类型和spring配置文件bean的class属性是父子关系, java类为父类, class是子类
3. java类中引用类型和spring配置文件bean的class属性是接口和实现类关系
语法:
<bean id="xxx" class="yyy" autowire="byType">
简单类型数据赋值
</bean>
注意, 在byType中, 需要自动注入的属性对象在容器中只能有一个, 要不然不会自动注入
因为spring不知道你要注入的结果是哪一个
测试byName
语法如下:
<!--测试byName-->
<bean id="school" class="org.example.ba04.School" >
<property name="name" value="北大"/>
<property name="address" value="北京"/>
</bean>
<bean id="myStudent" class="org.example.ba04.Student" autowire="byName">
<property name="name" value="张三"/>
<property name="age" value="20"/>
</bean>
测试byType
语法如下:
<!--测试byType-->
<bean id="mySchool" class="org.example.ba04.School" >
<property name="name" value="清华"/>
<property name="address" value="北京"/>
</bean>
<!--如果需要注入的类型, 在实例中有多个对象, 无法自动连线. “school”类型的bean不止一个.-->
<bean id="myStudent" class="org.example.ba04.Student" autowire="byType">
<property name="name" value="张三"/>
<property name="age" value="20"/>
</bean>