目录
一、概念及原理
1、什么是 IOC
(1)控制反转(Inversion of Control),把对象创建(new)和对象之间的调用(.function()),交给 Spring 管理;
(2)使用 IOC 的目的:降低耦合度;
(3)IOC 底层原理:xml 解析、工厂模式、反射;
- 反射:通过获取类的字节码文件(.class),来操作类方法
2、原理
Class 之间的调用形式决定了耦合度的高低,从原始方式到工厂模式到IOC,耦合度做到了越来越低。
(1)原始方式
假如我们现在要编写一个 Dao 和 Service,但是我们不懂任何设计模式,于是做出了在 Service 方法中 new Dao 的操作。如果 Dao 代码更变,那么 Service 也得跟着改变,耦合度较高。
(2)工厂模式
使用一个中间类,写一个 static 方法获取 Dao 对象,然后 Service 调用这个方法获取 Dao 对象,这样可以降低 Service 和 Dao 的耦合度。
虽然产生了 Factory 和 Dao 的耦合,但耦合程度比原始方式低。
(3)IOC 容器底层
IOC 分两步,进一步降低耦合度,降低到最低限度,这样我们只需要修改配置文件即可。
- 第一步:配置 xml 配置文件,配置创建的对象
- 第二步:对应 Service 类和 Dao 类,创建工厂类
在之后的使用中,有一个 getBean 方法,底层就是通过工厂类的 getDao() 这类 get 方法实现的。 (工厂类的 get 方法和 id 值对应)
(4)IOC 接口
(4-1)IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂;
(4-2)Spring 提供 IOC 容器实现两种方式(接口):
- BeanFactory:IOC 容器基本实现方式(开发中不常用,是 Spring 内部实现用的);
- ApplicationContext:BeanFactory 的子接口,提供更多更好的功能;
(4-3)特点:
- BeanFactory 加载配置文件时不会创建对象,使用时才会创建;
- ApplicationContext 加载配置文件时就会创建对象;
(4-4)实现类:
- FileSystemXmlApplicationContext:需要传入 xml 的盘符绝对路径;
- ClassPathXmlApplicationContext:传入 xml 的 src 下的类路径;
二、IOC 操作——Bean 管理
1、什么是 Bean 管理
是指两种操作:创建对象、属性注入。
(1)Spring 创建对象;
- xml 配置创建对象
(2)Spring 属性注入
- 对于一个类属性(private int num;),可以用 setNum(),当交给 Spring 操作,就是属性注入
2、Bean 管理操作有两种方式
(1)基于 xml 配置文件方式实现
(2)基于注解方式实现
3、基于 xml 方式创建对象
在 xml 中使用 bean 标签,添加对应属性,就可以实现创建对象。
(1)属性
- id:表示给对象取一个别名(标识),在 getBean() 方法中传入对应字符串;
- class:对象所在类的全路径;
- name:作用与 id 属性一致,但是 id 不能有特殊字符,name 可以;
(2)创建对象时,默认执行无参构造方法
三、xml 方式注入普通类型属性
DI:依赖注入,就是注入属性。
在 Spring 中,支持两种注入方式:有参构造和 set 方法。
1、使用 set 方法注入
(1)创建类,定义属性和对应的 set 方法
package com.demo.pojo;
public class Book {
private Integer id;
private String name;
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
}
(2)在 spring 配置文件配置对象创建,配置属性注入
在 <bean> 标签中嵌套 <property name="" value=""> 标签:
- name:属性名,要与类内的属性名一致;
- value:属性值;
<?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="book" class="com.demo.pojo.Book">
<property name="id" value="12"></property>
<property name="name" value="时间简史"></property>
</bean>
</beans>
(3)测试代码
这里是把 bean.xml 放到了 resources 文件夹下,并且标记为了源代码根目录。
import com.demo.pojo.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BookTest {
@Test
public void testCreate() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Book book = context.getBean(Book.class);
System.out.println(book.getId() + " " + book.getName());
}
}
2、使用有参构造函数注入
有参构造注入与 set 方法注入的区别仅在于 <bean> 标签内的子标签不同,即给属性注入值的标签不同。
(1)创建类,定义属性,创建属性对应有参数构造方法
package com.demo.pojo;
public class Order {
private String name;
private String address;
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public Order(String name, String address) {
this.name = name;
this.address = address;
}
}
(2)在 spring 配置文件中进行配置
写完 <bean> 标签后会报错,因为没有无参构造,所以要添加 <constructor-arg> 标签:
- name:同类属性名;
- value:属性值;
- index:有参构造函数中的第 index 个参数,可以代替 name,但用 name 更直观;
<?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="order" class="com.demo.pojo.Order">
<constructor-arg name="name" value="Ds5"/>
<constructor-arg name="address" value="China"/>
</bean>
</beans>
(3)测试代码
import com.demo.pojo.Order;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class OrderTest {
@Test
public void testOrder() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Order order = context.getBean("order", Order.class);
System.out.println(order.getName() + " " + order.getAddress());
}
}
四、xml 方式注入其他类型属性
1、注入字面量
字面量就是一些常量。比如 private String s = "123"。
(1)null 值
给 Book 类添加一个属性 price,并且将其设置为 null。
<?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="book" class="com.demo.pojo.Book">
<property name="id" value="12"></property>
<property name="name" value="时间简史"></property>
<property name="price" >
<null></null>
</property>
</bean>
</beans>
(2)属性值包含特殊符号
把 Book 类的 name 属性值,加上 << >>(不是书名号,是两个大于、小于)。可以考虑使用 xml 提供的 CDATA
<?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="book" class="com.demo.pojo.Book">
<property name="id" value="12"></property>
<property name="name">
<value><![CDATA[<<倚天屠龙记>>]]></value>
</property>
<property name="price" >
<null></null>
</property>
</bean>
</beans>
2、注入外部 Bean
(1)创建两个类 Service 类和 Dao 类
(1-1)UserService 类:
package com.demo.service.impl;
import com.demo.dao.UserDao;
import com.demo.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void func() {
System.out.println("调用 UserService 的 func");
userDao.func();
}
}
(1-2)UserDao 类:
package com.demo.dao.impl;
import com.demo.dao.UserDao;
public class UserDaoImpl implements UserDao {
@Override
public void func() {
System.out.println("调用 UserDao 的 func");
}
}
(2)在 spring 配置文件中进行配置
为 userService 注入 userDao 对象:
- name:类的属性名;
- ref:创建 userDao 对象的 <bean> 里的 id 的属性值,这就是注入外部 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.demo.service.impl.UserServiceImpl">
<property name="userDao" ref="xxx-userDaoImpl"></property>
</bean>
<bean id="xxx-userDaoImpl" class="com.demo.dao.impl.UserDaoImpl">
</bean>
</beans>
(3)测试代码
import com.demo.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserServiceTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("Service-Dao-Bean.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.func();
}
}
(4)输出结果
3、注入内部 Bean 和级联赋值
数据库的表与表之间有一对一、一对多、多对一的关系,内部 Bean 和级联赋值就是处理这种数据的。其中主要关注一下级联赋值,因为内部 Bean 和外部 Bean 纯粹只是 xml 内的一点写法区别。
下面用一对多关系,部门与员工(1:n),来做个例子。
(1)Department 类和 Empolyee 类:
package com.demo.pojo;
public class Department {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.demo.pojo;
public class Employee {
private String name;
private String gender;
// 一个员工属于某一个部门
private Department department;
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
(2)Dep-Emp-Bean.xml:
在 <property> 标签中,嵌套一个 <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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 内部 bean -->
<bean id="employee" class="com.demo.pojo.Employee">
<!-- 先设置普通类型属性 -->
<property name="name" value="wyt"></property>
<property name="gender" value="male"></property>
<!-- 设置对象类型属性 -->
<property name="department">
<!-- 嵌套一个 bean -->
<bean id="department" class="com.demo.pojo.Department">
<property name="name" value="金融部门"></property>
</bean>
</property>
</bean>
</beans>
其实我们发现,只要给对象类型的 <property> 加上 ref 属性值,再把 departmen 的 <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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 级联赋值 -->
<bean id="employee" class="com.demo.pojo.Employee">
<!-- 先设置普通类型属性 -->
<property name="name" value="wyt"></property>
<property name="gender" value="male"></property>
<!-- 设置对象类型属性 -->
<property name="department" ref="department"></property>
</bean>
<bean id="department" class="com.demo.pojo.Department">
<property name="name" value="金融部门"></property>
</bean>
</beans>
(3)测试代码:
import com.demo.pojo.Employee;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DepEmpTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("Dep-Emp-Bean.xml");
Employee employee = context.getBean("employee", Employee.class);
System.out.println(employee.getName() + " "
+ employee.getGender() + " "
+ employee.getDepartment().getName());
}
}
(4)输出结果:
(5)级联赋值的第二种写法
本质上就是先通过 ref 连接 department 对象,然后通过 getName() 方法获取 department 的 name 属性。(反射无处不在)
<?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="employee" class="com.demo.pojo.Employee">
<!-- 先设置普通类型属性 -->
<property name="name" value="wyt"></property>
<property name="gender" value="male"></property>
<!-- 设置对象类型属性 -->
<property name="department" ref="department"></property>
<property name="department.name" value="技术部门"></property>
</bean>
<bean id="department" class="com.demo.pojo.Department"> <!-- 这个 bean 一定要写 -->
<!-- 这个 property 是没有影响的,会被 employee 中的 property 覆盖 -->
<property name="name" value="金融部门"></property>
</bean>
</beans>
(6)输出结果
五、xml 方式注入集合类型属性
1、注入数组、List 、Map、 Set 集合类型属性
(1)创建类,定义数组、list、map、set 类型属性,生成对应 set 方法
package com.demo.pojo;
import java.util.*;
public class Student {
private String[] courses;
private List<String> list;
private Map<String, String> map;
private Set<String> set;
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public String[] getCourses() {
return courses;
}
public void setCourses(String[] courses) {
this.courses = courses;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
}
(2)在 spring 配置文件进行配置
对于每一种不同的集合,都有相对应的标签可以使用,在其对应的标签内添加 <value> 即可。
<?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="student" class="com.demo.pojo.Student">
<!-- 数组类型注入 -->
<property name="courses">
<array>
<value>Java编程</value>
<value>Sql教学</value>
</array>
</property>
<!-- List 类型注入 -->
<property name="list">
<list>
<value>list01</value>
<value>list02</value>
</list>
</property>
<!-- Map 类型注入 -->
<property name="map">
<map>
<entry key="1" value="Java编程"></entry>
<entry key="2" value="Sql教学"></entry>
</map>
</property>
<!-- Set 类型注入 -->
<property name="set">
<set>
<value>set01</value>
<value>set02</value>
</set>
</property>
</bean>
</beans>
(3)测试代码
import com.demo.pojo.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Arrays;
public class SetBeanTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("SetBean.xml");
Student student = context.getBean("student", Student.class);
System.out.println(Arrays.toString(student.getCourses()));
System.out.println(student.getList());
System.out.println(student.getMap());
System.out.println(student.getSet());
}
}
(4)输出结果
2、在集合内设置对象类型
集合内保存的一般都是某些自定义的对象类型,所以需要知道如何注入对象到集合类型。
(1)Teacher 类和 Course 类
package com.demo.pojo;
import java.util.List;
public class Teacher {
private List<Course> courseList;
public List<Course> getCourseList() {
return courseList;
}
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
}
package com.demo.pojo;
public class Course {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Course{" +
"name='" + name + '\'' +
'}';
}
}
(2)SetObjectBean.xml
使用 <ref> 的 bean 属性来链接外部 Bean,实现将对象类型属性值注入到 List(Map、Set、……)集合。
<?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.demo.pojo.Teacher">
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
<!-- 创建多个 course bean 对象 -->
<bean id="course1" class="com.demo.pojo.Course">
<property name="name" value="Spring5 框架"></property>
</bean>
<bean id="course2" class="com.demo.pojo.Course">
<property name="name" value="MyBatis 框架"></property>
</bean>
</beans>
(3)测试代码
import com.demo.pojo.Teacher;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SetObjectBeanTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("SetObjectBean.xml");
Teacher teacher = context.getBean("teacher", Teacher.class);
System.out.println(teacher.getCourseList());
}
}
(4)输出结果
3、抽取公共属性值
(1)在 spring 配置文件中引入名称空间 util
先添加名称空间,然后把 xsi 的值复制一份,紧接着后面粘贴上,把复制来的 beans 都改成 util。
(2)使用 util 标签完成 list 集合注入提取
其他类型的集合,输入 util: 会有提示。
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd ">
<!-- 提取list集合类型属性注入 -->
<util:list id="languageList"> <!-- map 就用 <util:map>-->
<!-- 是对象就用 <ref bean="">,是普通类型就用 <value> -->
<value>Java</value>
<value>C++</value>
<value>Python</value>
</util:list>
<bean id="list" class="com.demo.pojo.ProgramLanguage">
<property name="list" ref="languageList"></property>
</bean>
</beans>
(3)ProgramLanguage 类:
package com.demo.pojo;
import java.util.List;
public class ProgramLanguage {
private List<String> list;
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
}
(4)测试代码
import com.demo.pojo.ProgramLanguage;
import com.demo.pojo.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class commonTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("common.xml");
ProgramLanguage programLanguage = context.getBean("list", ProgramLanguage.class);
System.out.println(programLanguage.getList());
}
}
(5)输出结果