✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。
?个人主页:Java Fans的博客
?个人信条:不迁怒,不贰过。小知识,大智慧。
?当前专栏:SSM 框架从入门到精通
✨特色专栏:国学周更-心性养成之路
?本文内容:一文吃透 Spring 中的IOC和DI
文章目录
Spring 中 IOC 和 DI1. BeanFactory 容器2. ApplicationContext 容器3. Spring Bean定义4. IOC创建对象的方式5. Bean的自动装配6. spring中复杂对象的创建
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 | 元素的子元素,用于设置一个键值对。其 key 属性指定字符串类型的键值,ref 或 value 子元素指定其值 |
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="cn.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="cn.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="cn.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="cn.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">com.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="com.yd.pojo.User" p:age="18" p:name="老王"/><!--需要有属性的set方法--><bean id="student" class="cn.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="com.yd.pojo.User" c:age="23" c:name="中王"/>
<!--需要有有参构造方法--><bean id="student" class="cn.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="cn.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="cn.kgc.spring.entity.Teacher"> <property name="name" value="李老师"/> <property name="teaNo" value="001"/> </bean> <bean id="classRoom1" class="cn.kgc.spring.entity.ClassRoom"> <property name="address" value="学思楼1楼"/> <property name="classNo" value="1"/> </bean> <bean id="student" class="cn.kgc.spring.entity.Student" autowire="byName" ></bean>
【2】autowire="byType"在容器的上下文中寻找与类中属性类型相同的Bean进行装配
<bean id="teacher" class="cn.kgc.spring.entity.Teacher"> <property name="name" value="李老师"/> <property name="teaNo" value="001"/> </bean> <bean id="classRoom1" class="cn.kgc.spring.entity.ClassRoom"> <property name="address" value="学思楼1楼"/> <property name="classNo" value="1"/> </bean> <bean id="student" class="cn.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=truetest.string=abctest.integer=123test.long=123test.float=1.2345678123456test.double=1.2345678123456test.array=1,3,4,5,6,1,2,3test.list=1,3,4,5,6,1,2,3test.set=1,3,4,5,6,1,2,3test.map={name:"zhangsan", age:18}
6. spring中复杂对象的创建
【1】FactoryBean
public class ConnectionFactoryBean implements FactoryBean<Connection> { @Override public Connection getObject() throws Exception { Class.forName("com.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="cn.kgc.spring.ioc.entity.ConnectionFactoryBean"></bean>
【2】实例工厂
public class ConnectionFactoryBean { public Connection getConnection() throws Exception { Class.forName("com.mysql.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:mysql:///java2215?serverTimezone=UTC&useSSL=false", "root", "root"); return connection; }}
xml配置方式:
<bean id="conn" class="cn.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("com.mysql.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:mysql:///java2215?serverTimezone=UTC&useSSL=false", "root", "root"); return connection; }}
xml配置方式:
<bean id="conn" class="cn.kgc.spring.ioc.entity.ConnectionFactoryBean" factory-method="getConnection"></bean>
码文不易,本篇文章就介绍到这里,如果想要学习更多Java系列知识,点击关注博主,博主带你零基础学习Java知识。与此同时,对于日常生活有困扰的朋友,欢迎阅读我的第四栏目:《国学周更—心性养成之路》,学习技术的同时,我们也注重了心性的养成。