spring IOC / DI
参考:https://www.bilibili.com/read/cv9965357
IOC 容器
基础
点击查看代码
<?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属性标示给bean起名字
class属性表示给bean定义类型
-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--配置server与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照哪一个bean
-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>

配置别名
点击查看代码
<?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">
<!--name:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔-->
<bean id="bookService" name="service service4 bookEbi" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/> ref属性也可以为 bean 的别名(name),建议用 id
</bean>
<!--scope:为bean设置作用范围,可选值为单例singloton,非单例prototype,共有 5 种,其他在 springmvc 会用到-->
<bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl
scope="singleton""/>
</beans>
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//此处根据bean标签的id属性和name属性的任意一个值来获取bean对象
BookService bookService = (BookService) ctx.getBean("service4"); // 不存在报错 NoSuchBeanDefinitionException

spring 中创建 bean 的三种方法
1.构造方法
上述都是默认无参构造创建的 bean
2.静态工厂
(1)准备一个OrderDao和OrderDaoImpl类
public interface OrderDao {
public void save();
}
public class OrderDaoImpl implements OrderDao {
public void save() {
System.out.println("order dao save ...");
}
}
(2)创建一个工厂类OrderDaoFactory并提供一个静态方法
//静态工厂创建对象
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
在spring的配置文件application.properties中添加以下内容:
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
class:工厂类的类全名
factory-mehod:具体工厂类中创建对象的方法名

3.实例工厂
(1)准备一个UserDao和UserDaoImpl类
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao save ...");
}
}
(2)创建一个工厂类OrderDaoFactory并提供一个普通方法,注意此处和静态工厂的工厂类不一样的地方是方法不是静态方法
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
)在spring的配置文件中添加以下内容:
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
实例化工厂运行的顺序是:
-
创建实例化工厂对象,对应的是第一行配置
-
调用对象中的方法来创建bean,对应的是第二行配置
-
factory-bean:工厂的实例对象
-
factory-method:工厂对象中的具体创建对象的方法名,对应关系如下:
-

4.FactoryBean
(1)创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
//返回所创建类的Class对象
public Class<?> getObjectType() {
return UserDao.class;
}
}
(2)在Spring的配置文件中进行配置
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
查看源码会发现,FactoryBean接口其实会有三个方法,分别是:
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
方法一:getObject(),被重写后,在方法中进行对象的创建并返回
方法二:getObjectType(),被重写后,主要返回的是被创建类的Class对象
方法三:没有被重写,因为它已经给了默认值,从方法名中可以看出其作用是设置对象是否为单例,默认true,将其重写返回 false 可为多例。
bean 的生命周期
- bean生命周期是什么?
- bean对象从创建到销毁的整体过程。
- bean生命周期控制是什么?
- 在bean创建后到销毁前做一些事情。
(1)关于Spring中对bean生命周期控制提供了两种方式:
- 在配置文件中的bean标签中添加
init-method和destroy-method属性 - 类实现
InitializingBean与DisposableBean接口
(2)对于bean的生命周期控制在bean的整个生命周期中所处的位置如下:
- 初始化容器
- 1.创建对象(内存分配)
- 2.执行构造方法
- 3.执行属性注入(set操作)
- 4.执行bean初始化方法
- 使用bean
- 1.执行业务操作
- 关闭/销毁容器
- 1.执行bean销毁方法
(3)关闭容器的两种方式:
- ConfigurableApplicationContext是ApplicationContext的子类
- close()方法 运行时调用
- registerShutdownHook()方法 jvm 退出前回调
DI 依赖注入
- setter注入
- 简单类型
- 引用类型
- 构造器注入
- 简单类型
- 引用类型
1.setter 注入
- 在bean中定义引用类型属性,并提供可访问的set方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
- 配置中使用property标签ref属性注入引用类型对象
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
<property name="connectionNum" value="10"/>
</bean>
<bean id="bookDao" class="com.itheima.dao.imipl.BookDaoImpl"/>
value:后面跟的是简单数据类型,对于参数类型,Spring在注入的时候会自动转换,但是不能写成
<property name="connectionNum" value="abc"/>
这样的话,spring在将abc转换成int类型的时候就会报错。
2.构造注入
构造器注入也就是构造方法注入
步骤1:添加带有bookDao参数的构造方法
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public BookServiceImpl(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
步骤2:配置文件中进行配置构造方式注入
在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">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
</beans>
说明:
标签
- name属性对应的值为构造函数中方法形参的参数名,必须要保持一致。
- ref属性指向的是spring的IOC容器中其他bean对象。
- 如果有多个参数注入,两个
<contructor-arg>的配置顺序可以任意 - 简单类型依然是 value 属性
如何将构造函数的参数名与配置文件解耦:
方式一:删除name属性,添加type属性,按照类型注入
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
- 这种方式可以解决构造函数形参名发生变化带来的耦合问题
- 但是如果构造方法参数中有类型相同的参数,这种方式就不太好实现了
方式二:删除type属性,添加index属性,按照索引下标注入,下标从0开始
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg index="1" value="100"/>
<constructor-arg index="0" value="mysql"/>
</bean>
- 这种方式可以解决参数类型重复问题
- 但是如果构造方法参数顺序发生变化后,这种方式又带来了耦合问题
介绍完两种参数的注入方式,具体我们该如何选择呢?
- 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
- 强制依赖指对象在创建的过程中必须要注入指定的参数
- 可选依赖使用setter注入进行,灵活性强
- 可选依赖指对象在创建过程中注入的参数可有可无
- Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
- 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
- 自己开发的模块推荐使用setter注入
这节中主要讲解的是Spring的依赖注入的实现方式:
-
setter注入
-
简单数据类型
<bean ...> <property name="" value=""/> </bean> -
引用数据类型
<bean ...> <property name="" ref=""/> </bean>
-
-
构造器注入
-
简单数据类型
<bean ...> <constructor-arg name="" index="" type="" value=""/> </bean> -
引用数据类型
<bean ...> <constructor-arg name="" index="" type="" ref=""/> </bean>
-
-
依赖注入的方式选择上
- 建议使用setter注入
- 第三方技术根据情况选择
3.自动装配
在<bean>标签中添加autowire属性
首先来实现按照类型注入的配置
<?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 class="com.itheima.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
</beans>
注意事项:
- 需要注入属性的类中对应属性的setter方法不能省略
- 被注入的对象必须要被Spring的IOC容器管理
- 按照类型在Spring的IOC容器中如果找到多个对象,会报
NoUniqueBeanDefinitionException
一个类型在IOC中有多个对象,还想要注入成功,这个时候就需要按照名称注入,配置方式为:将 "byType" 改为 "byName"。
注意事项:
- 按照名称注入中的名称指的是什么?

-
bookDao是private修饰的,外部类无法直接方法
-
外部类只能通过属性的set方法进行访问
-
对外部类来说,setBookDao方法名,去掉set后首字母小写是其属性名
- 为什么是去掉set首字母小写?
- 这个规则是set方法生成的默认规则,set方法的生成是把属性名首字母大写前面加set形成的方法名
-
所以按照名称注入,其实是和对应的set方法有关,但是如果按照标准起名称,属性名和set对应的名是一致的
-
如果按照名称去找对应的bean对象,找不到则注入Null
-
当某一个类型在IOC容器中有多个对象,按照名称注入只找其指定名称对应的bean对象,不会报错
两种方式介绍完后,以后用的更多的是按照类型注入。
最后对于依赖注入,需要注意一些其他的配置特征:
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
- 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
4.集合注入
4.1 注入数组类型数据
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
4.2 注入List类型数据
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
4.3 注入Set类型数据
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
4.4 注入Map类型数据
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
4.6 注入Properties类型数据
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
说明:
- property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写
<array>、<list>、<set>、<map>、<props>标签 - List的底层也是通过数组实现的,所以
<list>和<array>标签是可以混用 - 集合中要添加引用类型,只需要把
<value>标签改成<ref>标签,这种方式用的比较少
加载第三方 bean 用 properties 文件加载属性
步骤1:准备properties配置文件
resources下创建一个jdbc.properties文件,并添加对应的属性键值对
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=root
步骤2:开启context命名空间
在applicationContext.xml中开context命名空间
只需要模仿 xmlns="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
将 xmlns 改为 xmlns:context ,将 beans 改为 context 即可
<?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: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">
</beans>
步骤3:加载properties配置文件
在配置文件中使用context命名空间下的标签来加载properties配置文件
<context:property-placeholder location="jdbc.properties"/>
步骤4:完成属性注入
使用${key}来读取properties配置文件中的内容并完成属性注入
<?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: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:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
核心容器 ApplicationContext 与 BeanFactory
用 XML 文件配置
- 在类路径下找 ClassPathXmlApplicationContext
- 在磁盘路径下找 FileSystemXmlAplicationContext
后面注解开发有配置类配置的介绍
Bean的三种获取方式
方式一,就是目前案例中获取的方式:
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
这种方式存在的问题是每次获取的时候都需要进行类型转换,有没有更简单的方式呢?
方式二:
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
这种方式可以解决类型强转问题,但是参数又多加了一个,相对来说没有简化多少。
方式三:
BookDao bookDao = ctx.getBean(BookDao.class);
BeanFactory
Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
-
BeanFactory是延迟加载,只有在获取bean对象的时候才会去创建
-
ApplicationContext是立即加载,容器加载的时候就会创建bean对象
-
ApplicationContext要想成为延迟加载,只需要按照如下方式进行配置
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" lazy-init="true"/>
小结
-
容器创建的两种方式
- ClassPathXmlApplicationContext[掌握]
- FileSystemXmlApplicationContext[知道即可]
-
获取Bean的三种方式
- getBean("名称"):需要类型转换
- getBean("名称",类型.class):多了一个参数
- getBean(类型.class):容器中不能有多个该类的bean对象
上述三种方式,各有各的优缺点,用哪个都可以。
-
容器类层次结构
- 只需要知晓容器的最上级的父接口为 BeanFactory即可
-
BeanFactory
- 使用BeanFactory创建的容器是延迟加载
- 使用ApplicationContext创建的容器是立即加载
- 具体BeanFactory如何创建只需要了解即可。
- BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
- ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
- ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
- ApplicationContext接口常用初始化类
- ClassPathXmlApplicationContext(常用)
- FileSystemXmlApplicationContext
Bean 相关

依赖注入相关


浙公网安备 33010602011771号