✅作者简介:2022年博客新星 第八。热爱国学的java后端开发者,修心和技术同步精进。
🍎个人主页:Java Fans的博客
🍊个人信条:不迁怒,不贰过。小知识,大智慧。
💞当前专栏:SSM 框架从入门到精通
✨特色专栏:国学周更-心性养成之路
🥭本文内容:一文吃透 spring 中的IOC和DI
Spring 中 IOC 和 DI
IoC 容器是 Spring 的核心,也可以称为 Spring 容器。Spring 通过 IoC 容器来管理对象的实例化和初始化,以及对象从创建到销毁的整个生命周期。
Spring 中使用的对象都由 IoC 容器管理,不需要我们手动使用 new 运算符创建对象。由 IoC 容器管理的对象称为 Spring Bean,Spring Bean 就是 Java 对象,和使用 new 运算符创建的对象没有区别。
Spring 通过读取 XML 或 Java 注解中的信息来获取哪些对象需要实例化。Spring 提供 2 种不同类型的 IoC 容器,即 BeanFactory 和 ApplicationContext 容器
1. BeanFactory 容器
BeanFactory 是最简单的容器,由 org.springframework.beans.factory.BeanFactory 接口定义,采用懒加载(lazy-load),所以容器启动比较快。BeanFactory 提供了容器最基本的功能,配置文件加载时不会创建对象,在获取bean时才会创建对象
为了能够兼容 Spring 集成的第三方框架(如 BeanFactoryAware、InitializingBean、DisposableBean),所以目前仍然保留了该接口。简单来说,BeanFactory 就是一个管理 Bean 的工厂,它主要负责初始化各种 Bean,并调用它们的生命周期方法。BeanFactory 接口有多个实现类 org.springframework.beans.factory.xml.XmlBeanFactory最常见
使用 BeanFactory 需要创建 XmlBeanFactory 类的实例,通过 XmlBeanFactory 类的构造函数来传递 Resource 对象。如下所示。
Resource resource = new ClassPathResource("applicationContext.xml");
BeanFactory factory = new XmlBeanFactory(resource);
2. ApplicationContext 容器
ApplicationContext 继承了 BeanFactory 接口,由 org.springframework.context.ApplicationContext 接口定义,对象在启动容器时加载。ApplicationContext 在 BeanFactory 的基础上增加了很多企业级功能,例如 AOP、国际化、事件支持等。
ApplicationContext 接口有两个常用的实现类,具体如下。
【1】ClassPathXmlApplicationContext
该类从类路径 ClassPath 中寻找指定的 XML 配置文件,并完成 ApplicationContext 的实例化工作,具体如下所示。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);
配置文件加载,则创建对象
在上述代码中,configLocation 参数用于指定 Spring 配置文件的名称和位置,如 Beans.xml。
【2】FileSystemXmlApplicationContext
该类从指定的文件系统路径中寻找指定的 XML 配置文件,并完成 ApplicationContext 的实例化工作,具体如下所示。
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);
二者的主要区别在于,如果 Bean 的某一个属性没有注入,使用 BeanFacotry 加载后,第一次调用 getBean() 方法时会抛出异常,而 ApplicationContext 则会在初始化时自检,这样有利于检查所依赖的属性是否注入。
因此,在实际开发中,通常都选择使用 ApplicationContext,只有在系统资源较少时,才考虑使用 BeanFactory。
【3】spring容器加载多个配置文件
- 使用字符串参数,逗号分隔
//加载spring的配置文件 启动spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("config/student.xml","config/springConfig.xml");
- 使用字符串数组
String[] config = {"config/student.xml","config/springConfig.xml"};
//加载spring的配置文件 启动spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
- 使用导入
<?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">
<import resource="student.xml"/>
<import resource="springConfig.xml"/>
</beans>
//加载spring的配置文件 启动spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("config/applicationContext.xml");
3. Spring Bean定义
由 Spring IoC 容器管理的对象称为 Bean,Bean 根据 Spring 配置文件中的信息创建。
可以把 Spring IoC 容器看作是一个大工厂,Bean 相当于工厂的产品,如果希望这个大工厂生产和管理 Bean,则需要告诉容器需要哪些 Bean,以及需要哪种方式装配 Bean。
Spring 配置文件支持两种格式,即 XML 文件格式和 Properties 文件格式。
- Properties 配置文件主要以 key-value 键值对的形式存在,只能赋值,不能进行其他操作,适用于简单的属性配置。
- XML 配置文件是树形结构,相对于 Properties 文件来说更加灵活。XML 配置文件结构清晰,但是内容比较繁琐,适用于大型复杂的项目。
通常情况下,Spring 的配置文件使用 XML 格式。XML 配置文件的根元素是 ,该元素包含了多个子元素 。每一个 元素都定义了一个 Bean,并描述了该 Bean 如何被装配到 Spring 容器中 元素中可以包含很多属性,其常用属性如下表所示。
属性名称 | 描述 |
---|---|
id | Bean 的唯一标识符,Spring 容器对 Bean 的配置和管理都通过该属性完成。id 的值必须以字母开始,可以使用字母、数字、下划线等符号。 |
name | name 属性中可以为 Bean 指定多个名称,每个名称之间用逗号或分号隔开。Spring 容器可以通过 name 属性配置和管理容器中的 Bean。 |
class | 该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,即类的全限定名。 |
scope | 用于设定 Bean 实例的作用域,属性值可以为 singleton(单例)、prototype(原型)、request、session 和 global Session。其默认值是 singleton |
constructor-arg | 元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的 index 属性指定构造参数的序号(从 0 开始),type 属性指定构造参数的类型 |
property | 元素的子元素,用于调用 Bean 实例中的 setter 方法来属性赋值,从而完成依赖注入。该元素的 name 属性用于指定 Bean 实例中相应的属性名 |
ref | 和 等元素的子元索,该元素中的 bean 属性用于指定对某个 Bean 实例的引用 |
value | 和 等元素的子元素,用于直接指定一个常量值 |
list | 用于封装 List 或数组类型的依赖注入 |
set | 用于封装 Set 类型的依赖注入 |
map | 用于封装 Map 类型的依赖注入 |
entry | |
init-method | 容器加载 Bean 时调用该方法,类似于 Servlet 中的 init() 方法 |
destroy-method | 容器删除 Bean 时调用该方法,类似于 Servlet 中的 destroy() 方法。该方法只在 scope=singleton 时有效 |
lazy-init | 懒加载,值为 true,容器在首次请求时才会创建 Bean 实例;值为 false,容器在启动时创建 Bean 实例。该方法只在 scope=singleton 时有效 |
4. IOC创建对象的方式
【1】无参构造器创建对象 (默认)
【2】有参构造创建对象
- 使用下标传递参数
<!-- 使用索引匹配参数-->
<bean id="student" class="***.kgc.spring.entity.Student">
<constructor-arg index="0" value="20"/>
<constructor-arg index="1" value="李四"/>
<constructor-arg index="2" value="20210814"/>
<constructor-arg index="3" value="2021/08/14"/>
</bean>
- 使用数据类型传递参数
<!--使用数据类型匹配参数 有相同的参数类型可配合索引一起使用-->
<bean id="student" class="***.kgc.spring.entity.Student">
<constructor-arg type="int" value="20"/>
<constructor-arg type="java.lang.String" index="1" value="李四"/>
<constructor-arg type="java.lang.String" value="20210814"/>
<constructor-arg type="java.util.Date" value="2021/08/14"/>
</bean>
- 通过属性名传递参数
<!--使用属性名赋值-->
<bean id="student" class="***.kgc.spring.entity.Student">
<constructor-arg name="age" value="20"/>
<constructor-arg name="name" value="李四"/>
<constructor-arg name="stuNo" value="20210814"/>
<constructor-arg name="birth" value="2021/08/14" />
</bean>
【3】创建对象时属性的其它注入方式
- set注入
<bean id="studentService" class="***.kgc.spring.service.StudentServiceImpl">
<!-- 属性是引用类型 赋值时使用ref-->
<property name="studentDao" ref="studentDao"/>
<!-- 属性是基本类型 String类型 Date类型 赋值时使用value -->
<property name="age" value="20"/>
<property name="stuName" value="张三"/>
<property name="price" value="20.4"/>
<property name="score" value="80.3"/>
<property name="birth" value="2021/8/13"/>
<property name="str" >
<array>
<value>张三</value>
<value>李四</value>
<value>王五</value>
</array>
</property>
<property name="lis">
<list>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</list>
</property>
<!-- spring容器中组件默认都是单例的 全局共享一个对象-->
<property name="lis2">
<list>
<ref bean="studentDao"></ref>
<ref bean="studentDao"></ref>
<ref bean="studentDao"></ref>
</list>
</property>
<property name="books">
<set>
<value>java</value>
<value>php</value>
<value>c#</value>
</set>
</property>
<property name="scores">
<map>
<entry key="math" value="89" ></entry>
<entry key="english" value="90" ></entry>
<entry key="java" value="80" ></entry>
</map>
</property>
<property name="map">
<map>
<entry key="01" value-ref="studentDao"></entry>
<entry key="02" value-ref="studentDao"></entry>
<entry key="03" value-ref="studentDao"></entry>
</map>
</property>
<property name="ps">
<props>
<prop key="driver">***.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql:///mybatis</prop>
<prop key="username">root</prop>
<prop key="password">rooot</prop>
</props>
</property>
</bean>
使用set注入,每个属性必须含有对应的set方法,否则无法进行属性的注入
- Spring的依赖注入之p命名空间和c命名空间
p命名空间是set注入的一种快捷实现方式,想要使用p命名空间注入,需要注意一下几点。
- 实体类中必须有set方法;
- 实体类中必须有无参构造器(默认存在);
- 必须导入p命名空间注入方式依赖。
xml依赖代码:
xmlns:p="http://www.springframework.org/schema/p"
导入后即可使用
<bean id="user" class="***.yd.pojo.User" p:age="18" p:name="老王"/>
<!--需要有属性的set方法-->
<bean id="student" class="***.kgc.spring.entity.Student" p:age="20" p:name="lisi" p:birth="2021/1/1" p:stuNo="20210814001"></bean>
c命名空间是构造器注入的一种快捷实现方式,想要使用c命名空间,需要注意一下几点。
- 实体类中必须存在有参构造器;
- 必须导入c命名空间注入方式依赖。
xml依赖代码:
xmlns:c="http://www.springframework.org/schema/c"
导入后即可使用
<bean id="user2" class="***.yd.pojo.User" c:age="23" c:name="中王"/>
<!--需要有有参构造方法-->
<bean id="student" class="***.kgc.spring.entity.Student" c:age="30" c:name="王五" c:birth="1991/12/08" c:stuNo="20210814001"></bean>
类型转换器的使用:
作用:自定义注入参数和实体类中类型的匹配方式
import org.springframework.core.convert.converter.Converter;
public class MyConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
Date parse = simpleDateFormat.parse(source);
return parse;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
xml文件配置:
<bean id="convert" class="***.kgc.spring.basic.convert.MyConverter"></bean>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="convert"></ref>
</set>
</property>
</bean>
5. Bean的自动装配
【1】autowire="byName"在容器的上下文中寻找与类中属性对应的set方法名字相同的id属性值进行装配
<bean id="teacher" class="***.kgc.spring.entity.Teacher">
<property name="name" value="李老师"/>
<property name="teaNo" value="001"/>
</bean>
<bean id="classRoom1" class="***.kgc.spring.entity.ClassRoom">
<property name="address" value="学思楼1楼"/>
<property name="classNo" value="1"/>
</bean>
<bean id="student" class="***.kgc.spring.entity.Student" autowire="byName" ></bean>
【2】autowire="byType"在容器的上下文中寻找与类中属性类型相同的Bean进行装配
<bean id="teacher" class="***.kgc.spring.entity.Teacher">
<property name="name" value="李老师"/>
<property name="teaNo" value="001"/>
</bean>
<bean id="classRoom1" class="***.kgc.spring.entity.ClassRoom">
<property name="address" value="学思楼1楼"/>
<property name="classNo" value="1"/>
</bean>
<bean id="student" class="***.kgc.spring.entity.Student" autowire="byType" ></bean>
【3】使用注解自动装配
- 导入context约束
<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:c="http://www.springframework.org/schema/c"
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.xsd" >
- 开启注解支持
<context:annotation-config/>
public class Student {
@Value("2021")
private String stuNo;
@Value("wangwu")
private String name;
@Value("20")
private int age;
@Value("2021/12/08")
private Date birth;
@Autowired
private Teacher teacher;
@Resource
private ClassRoom classRoom;
}
获取配置文件中的值
public class Aoo {
@Value("${test.boolean}")
private Boolean testBoolean;
@Value("${test.string}")
private String testString;
@Value("${test.integer}")
private Integer testInteger;
@Value("${test.long}")
private Long testLong;
@Value("${test.float}")
private Float testFloat;
@Value("${test.double}")
private Double testDouble;
@Value("#{'${test.array}'.split(',')}")
private String[] testArray;
@Value("#{'${test.list}'.split(',')}")
private List<String> testList;
@Value("#{'${test.set}'.split(',')}")
private Set<String> testSet;
@Value("#{${test.map}}")
private Map<String, Object> testMap;
}
配置文件 properties
test.boolean=true
test.string=abc
test.integer=123
test.long=123
test.float=1.2345678123456
test.double=1.2345678123456
test.array=1,3,4,5,6,1,2,3
test.list=1,3,4,5,6,1,2,3
test.set=1,3,4,5,6,1,2,3
test.map={name:"zhangsan", age:18}
6. spring中复杂对象的创建
【1】FactoryBean
public class ConnectionFactoryBean implements FactoryBean<Connection> {
@Override
public Connection getObject() throws Exception {
Class.forName("***.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///java2215?serverTimezone=UTC", "root", "root");
return connection;
}
@Override
public Class<?> getObjectType() {
return Connection.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
xml配置方式:
<bean id="conn" class="***.kgc.spring.ioc.entity.ConnectionFactoryBean"></bean>
【2】实例工厂
public class ConnectionFactoryBean {
public Connection getConnection() throws Exception {
Class.forName("***.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///java2215?serverTimezone=UTC&useSSL=false", "root", "root");
return connection;
}
}
xml配置方式:
<bean id="conn" class="***.kgc.spring.ioc.entity.ConnectionFactoryBean"></bean>
<bean id="connection" factory-bean="conn" factory-method="getConnection"></bean>
【3】静态工厂
public class ConnectionFactoryBean {
public static Connection getConnection() throws Exception {
Class.forName("***.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///java2215?serverTimezone=UTC&useSSL=false", "root", "root");
return connection;
}
}
xml配置方式:
<bean id="conn" class="***.kgc.spring.ioc.entity.ConnectionFactoryBean" factory-method="getConnection"></bean>
码文不易,本篇文章就介绍到这里,如果想要学习更多Java系列知识,点击关注博主,博主带你零基础学习Java知识。与此同时,对于日常生活有困扰的朋友,欢迎阅读我的第四栏目:《国学周更—心性养成之路》,学习技术的同时,我们也注重了心性的养成。