spring中是通过DI实现IOC的, 今天我们来学习DI
目录
简介
两种手段
di的语法分类
实现步骤
1. 创建maven项目
2. 加入maven依赖
3. 创建类(接口和它的实现类)
4. 创建spring需要的配置文件
5. 测试spring是否成功创建对象
set注入
set注入(设值注入)实例
简单类型的set注入实例
引用类型的set注入
构造注入 : 在构造实例的同时, 完成对象的实例化
配置文件方式总结
简单类型 : spring中规定基本数据类型和String都是简单类型
1. set注入(注入就是设值)
2. 构造注入 : 调用有参数的构造方法
引用类型的自动注入
1. byName(按名称注入)
2. byType(按类型注入)
测试byName
测试byType
di : 依赖注入, 表示创建对象, 给属性赋值
1. 配置文件的方式: 使用标签和属性完成, 叫做基于XML的di实现
2. 注解: 使用spring中的注解完成, 叫做基于注解的di实现
1. set注入(设值注入): spring中调用类的set方法, 用set方法可以实现属性的赋值
2. 构造注入, spring调用类的构造方法, 创建对象, 在构造方法中完成赋值
spring依赖, 版本为 5.2.5
<!-- Spring核心依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency>
Junit依赖(单元测试)
和没有使用框架一样, 就是普通的类
声明类的信息, 这些类由spring创建和管理
通过spring的语法, 完成属性的赋值
简单类型 : spring中规定基本数据类型和String都是简单类型
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方法, 不管你有没有这个属性
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调用类的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会浏览配置文件第二遍
使用 <constructor-arg> 标签
一个 <constructor-arg> 标签表示构造方法的一个参数
<constructor-arg> 标签属性 :
name : 表示构造方法的形参名
index : 表示构造方法的参数位置, 从左往右从0开始
value : 如果构造方法的形参类型是简单类型, 使用value属性为其赋值
ref : 构造方法的形参的参数如果是引用类型, 使用ref属性为其赋值
引用类型的自动注入
spring框架根据某些规则可以自动给引用类型赋值, 简化程序员工作
常用的规则为byName 和 byType
java类中的引用类型的属性名和spring配置文件<bean>的id相同时
且数据类型一样, 这样容器中的bean可以由spring自动赋值
语法
<bean id="xxx" class="yyy" autowire="byName"> 简单类型数据赋值 </bean>
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--> <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--> <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>