Spring框架 _ IoC/DI(控制反转、依赖注入)
一、Spring Framework系统架构图
二、Spring Framework课程学习路线
三、IoC/DI(控制反转、依赖注入)
- 代码书写现状 —— 耦合度偏高
3.1 解决方案
- 使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象。
3.2 IoC (Inversion of Control)控制反转
- 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。
3.3 Spring技术对IoC思想进行了实现
- Spring提供了一个容器,称为IoC容器,用来充当IoC思想中的“外部”。
- IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean。
3.4 DI (Dependency Injection) 依赖注入
3.5 目标 —— 充分解耦
- 使用IoC容器管理bean (IoC)。
- 在IoC容器内将有依赖关系的bean进行关系绑定(DI)。
3.6 最终效果
- 使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系。
3.7 IoC入门案例
1.导入Spring坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
2.定义Spring管理的类(接口)
package com.li.service;
public interface BookService {
public void save();
}
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
@Override
public void save() {
bookDao.save();
}
}
3.创建Spring配置文件,配置对应类作为Spring管理的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">
<!--1.导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
<!--2.配置bean-->
<!--bean标签标示配置bean
id属性标示给bean起名字
class属性表示给bean定义类型-->
<bean id="bookDao" class="com.li.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.li.service.impl.BookServiceImpl"/>
</beans>
注意事项
- bean定义时id属性在同一个上下文中不能重复。
4.初始化IoC容器( Spring核心容器/Spring容器),通过容器获取bean
public class APP2 {
public static void main(String[] args) {
//3. 获取IOC容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//4. 获取bean
BookDao bookDao =(BookDao) ctx.getBean("bookDao");
bookDao.save();
BookService bookService =(BookService) ctx.getBean("bookService");
bookService.save();
}
}
3.8 DI入门案例
1.删除使用new的形式创建对象的代码
1.1.提供依赖对象对应的setter方法
public class BookServiceImpl implements BookService {
//5.删除业务层中使用new的方式创建的dao对象
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
//6.提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
2.配置service与dao之间的关系
<?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">
<!--1.导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
<!--2.配置bean-->
<!--bean标签标示配置bean
id属性标示给bean起名字
class属性表示给bean定义类型-->
<bean id="bookDao" class="com.li.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.li.service.impl.BookServiceImpl">
<!-- 7. 配置server dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照哪一个bean-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
四、IOC相关内容(Bean)
4.1 bean配置
4.1.1 bean基础配置
4.1.2 bean别名配置
注意事项
- 获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常
NoSuchBeanDefinitionExceptionNoSuchBeanDefinitionException: No bean named 'bookServiceImpl' available
4.1.3 bean作用范围配置
bean作用范围说明
为什么bean默认为单例?
适合交给容器进行管理的bean
表现层对象 业务层对象 数据层对象 工具对象
不适合交给容器进行管理的bean
封装实体的域对象
4.2 bean实例化
4.2.1 bean是如何创建的
- bean本质上就是对象,创建bean使用构造方法完成
4.2.2 实例化bean的三种方式 —— ① 构造方法(常用)
4.2.2.1 提供可访问的构造方法
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
public void save(){
System.out.println("book save");
}
}
4.2.2.2 配置
<bean
id="bookDao"
class="com.li.dao.impl.BookDaoImpl"
/>
4.2.2.2 无参构造方法如果不存在,将抛出异常BeanCreationException
4.2.3 实例化bean的三种方式 —— ② 静态工厂(了解即可)
4.2.3.1 静态工厂
//静态工厂创建对象
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup....");
return new OrderDaoImpl();
}
}
4.2.3.2 配置
<!--方式二:使用静态工厂实例化bean-->
<bean
id="orderDao"
class="com.li.factory.OrderDaoFactory"
factory-method="getOrderDao"/>
4.2.4 实例化bean的三种方式 —— ③ 实例工厂(了解即可)
4.2.4.1 实例工厂
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
4.2.4.2 配置
<!--方式三:使用实例工厂实例化bean-->
<bean id="userFactory" class="com.li.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
4.2.4.3 缺点
- 配合使用实际无意义
- 方法名不固定每次需要配置
4.2.5 实例化bean的三种方式 —— ④ FactoryBean(实用)—— 实例工厂的升级
4.2.1.1 FactoryBean
//FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public Class<?> getObjectType() {
return UserDao.class;
}
}
4.2.1.2 配置
<!--方式四:使用FactoryBean实例化bean-->
<bean id="userDao" class="com.li.factory.UserDaoFactoryBean"/>
4.3 bean生命周期
- 生命周期:从创建到消亡的完整过程。
- bean生命周期:bean从创建到销毁的整体过程。
- bean生命周期控制:在bean创建后到销毁前做一些事情。
4.3.1 bean初始化经历阶段
4.3.1.1 初始化容器
1.创建对象(内存分配)
2.执行构造方法
3.执行属性注入(set操作)
4.执行bean初始化方法
4.3.2 bean销毁时机
- 容器关闭前触发bean的销毁
4.3.2.1 关闭容器方式
手工关闭容器
ConfigurableApplicationcontext接口close()操作
注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机
ConfigurableApplicationContext接口registerShutdownHook()操作
4.3.2.1.1 实例
public class AppForLifeCycle {
public static void main( String[] args ) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//关闭容器
ctx.close();
}
}
4.3.2.2 使用bean
1.执行业务操作
4.3.2.3 关闭销毁容器
1.执行bean销毁方法
(1)提供生命周期控制方法
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
System.out.println("destory...");
}
}
(2)配置生命周期控制方法
<!--init-method:设置bean初始化生命周期回调函数-->
<!--destroy-method:设置bean销毁生命周期回调函数,仅适用于单例对象-->
<bean id="bookDao" class="com.li.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
(3)实现InitializingBean, DisposableBean接口(了解即可)
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
System.out.println("set .....");
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
public void destroy() throws Exception {
System.out.println("service destroy");
}
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}
五、依赖注入方式(DI)
5.1 思考 —— 向一个类中传递数据的方式有几种?
- 普通方法(set方法)
- 构造方法
5.2 思考 —— 依赖注入描述了在容器中建立bean与bean之间依赖关系的过程,如果bean运行需要的是数字或字符串呢?
- 引用类型
- 简单类型(基本数据类型与string)
5.3 依赖注入方式
setter注入
- 简单类型
- 引用类型
构造器注入
- 简单类型
- 引用类型
5.4 setter注入 —— 引用类型
在bean中定义引用类型属性并提供可访问的set方法
public class BookServiceImpl implements BookService{
private BookDao bookDao;
private UserDao userDao;
//setter注入需要提供要注入对象的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
配置中使用property标签ref属性注入引用类型对象
<!--注入引用类型-->
<bean id="bookService" class="com.li.service.impl.BookServiceImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--ref属性:设置注入引用类型bean的id或name-->
<property name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.li.dao.impl.BookDaoImpl">
5.5 setter注入 —— 简单类型
在bean中定义引用类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao {
private int connectionNum;
//setter注入需要提供要注入对象的set方法
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
配置中使用property标签value属性注入简单类型数据
<!--注入简单类型-->
<bean id="bookDao" class="com.li.dao.impl.BookDaoImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--value属性:设置注入简单类型数据值-->
<property name="connectionNum" value="100"/>
</bean>
5.6 构造器注入 —— 引用类型(了解即可)
在bean中定义引用类型属性并提供可访问的构造方法
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public BookServiceImpl(BookDao bookDao) {
this.bookDao = bookDao;
}
}
配置中使用constructor-arg标签ref属性注入引用类型对象
<bean id="bookService" class="com.li.service.impl.BookServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
//解决形参名称的问题,与形参名不耦合
<bean id="bookDao" class="com.li.dao.impl.BookDaoImpl">
5.7 构造器注入 —— 简单类型(了解即可)
在bean中定义引用类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao {
private int connectionNum;
public BookDaoImpl(String databaseName, int connectionNum) {
this.connectionNum = connectionNum;
}
}
配置中使用constructor-arg标签value属性注入简单类型数据
<bean id="bookDao" class="com.li.dao.impl.BookDaoImpl">
<constructor-arg name="connectionNum" value="10"/>
</bean>
5.8 依赖注入方式选择
- 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现。
- 可选依赖使用setter注入进行,灵活性强。
- Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨。
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入。
- 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入。
- 自己开发的模块推荐使用setter注入
六、依赖自动装配
- IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配。
6.1 自动装配方式
- 按类型(常用)
- 按名称
- 按构造方法
- 不启用自动装配
6.2 配置中使用bean标签autowire属性设置自动装配的类型
<bean class="com.li.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.li.service.impl.BookServiceImpl" autowire="byType"/>
6.3 依赖自动装配特征
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作。
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用。
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用。
- 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效。
七、集合注入
7.1 数组、List、Set、Map、Properties
<!--数组注入-->
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<!--list集合注入-->
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
<!--set集合注入-->
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
<!--map集合注入-->
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
<!--Properties注入-->
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
Comments | NOTHING