1.核心概念
当前项目中的问题
下面代码的实现十分简单,但是业务层需要调用数据层的方法,就要在业务层new数据层的对象,如果数据层的实现类发生变化,业务层的代码也需要跟着改变,意味着要编译打包和重新部署
// 数据层实现
public class FoodDaoImpl implements FoodDao{
public void save(){
...
}
}
// 业务层实现
public class FoodServiceImpl implements FoodService{
private FoodDao fd = new FoodDaoImpl();
public void save(){
fd.save();
}
}
IOC、IOC容器、Bean、DI*
针对上述问题,Spring提出了一个解决方案:使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象(Spring的核心概念)
IOC(Inversion of Control)
- 什么是控制反转
IOC
?使用对象时,由主动new
产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部
-
Spring
和IOC
之间的关系是什么?
-
Spring
技术对IOC
思想进行了实现
- Spring提供了一个容器(即
IOC
容器),用来充当IOC
思想中的外部
-
IOC
容器的作用以及内部存放的是什么?
-
IOC
容器负责对象的创建、初始化等一系列工作,其中包含了数据层和业务层的类对象
- 被创建或被管理的对象在
IOC
容器中统称为Bean
- 当
IOC
容器中创建好service
和dao
对象后,程序能正确执行么?不行!service
运行需要依赖dao
对象,IOC
容器中虽然有service
和dao
对象,但service
对象和dao
对象没有任何关系,需要将二者进行绑定
DI(Dependency Injection)
上述提到需要绑定对象间的关系,使用的就是DI
目标
充分解耦:
- 使用
IOC
容器管理bean
- 在
IOC
容器内将有依赖关系的bean
进行关系绑定
- 使用对象时不仅可以直接从
IOC
容器中获取,并且获取到的bean
已经绑定了所有的依赖关系
2.入门案例
IOC入门
思路分析
-
Spring
是使用容器来管理bean
对象的,管什么?主要管理项目中所使用到的类对象,比如Service
和Dao
- 如何将被管理的对象告知
IOC
容器?使用配置文件
- 被管理的对象交给
IOC
容器,要想从容器中获取对象先要获取IOC
容器,如何获取?Spring
框架提供相应的接口
-
IOC
容器得到后,如何从容器中获取bean
?调用Spring
框架提供对应接口中的方法
代码实现
参考Spring_01_quickstart
DI入门
分为思路和代码
思路分析
- 要想实现依赖注入,必须要基于
IOC
管理bean
-
Service
中使用new
形式创建的Dao
对象是否保留?删除,因为要使用IOC
容器中的bean
对象
-
Service
中需要的Dao
对象如何进入到Service
中?在Service
中提供方法(set
方法),让IOC
容器可以通过该方法传入bean
对象
-
Service
与Dao
间的关系如何描述?使用配置文件
代码实现
参考Spring_01_quickstart
3.IOC相关内容
bean基础配置
主要掌握:
-
bean
标签的id
和class
属性的使用
- 对于是否设置单例的思考
代码参考Spring_02_base_config
-
id
:bean
的id
,在容器中唯一
-
class
:bean
的类型(即bean
的全路径类名),注意不能使用接口(因为接口无法创建对象)
-
name
:定义bean
的别名,存在多个就使用,;
或者空格分隔,之后在ref
和getBean
方法中中也可以使用别名
-
scope
:定义bean
的作用范围,singleton
表示单例(默认),prototype
表示非单例
bean实例化
主要掌握:
代码参考Spring_03_bean_instance
-
bean
如何创建?使用反射,因为把构造方法设置为私有也可以使用bean
,所以使用的是反射
-
实例化bean
的三种方式:
- 构造方法实例化:调用的是无参构造方法,因为把无参构造方法删除,并且设置一个有参构造方法,运行时会报错
<bean id="bookDao" class="com.psj.dao.impl.BookDaoImpl"/>
<bean id="orderDao" class="com.psj.factory.OrderDaoFactory" factory-method="getOrderDao"/>
-
实例工厂实例化(了解即可):和静态工厂的区别在于工厂中的方法一个是静态一个不是,并且配置也不同
<bean id="userFactory" class="com.psj.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
-
FactoryBean
的使用:上述配置过于复杂,因为factory-method
名称不固定每次都要配置,并且还需要特意创建id=userFactory
的bean
配合使用
// FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
// 代替原始实例工厂中创建对象的方法(相当于统一了方法名为getObject)
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public Class<?> getObjectType() {
return UserDao.class;
}
// 如果要设置为单例/非单例,还可以实现isSingleton方法(不实现就取默认值-单例)
}
<bean id="userDao" class="com.psj.factory.UserDaoFactoryBean"/>
bean的生命周期
bean
生命周期指bean
对象从创建到销毁的整体过程;bean
生命周期控制指在bean
创建后到销毁前做的事
主要掌握:
代码参考Spring_04_bean_lifecycle
4.DI相关内容
依赖注入的两种方式
主要掌握:
代码参考Spring_05_di_set和Spring_06_di_constructor
-
setter
注入:
<bean id="bookDao" class="com.psj.dao.impl.BookDaoImpl">
<property name="connectionNum" value="100"/>
<property name="databaseName" value="mysql"/>
</bean>
-
构造器注入:在实体类中添加有参构造器,主要修改在于applicationContext.xml
-
引用类型:详情见代码中的applicationContext.xml
,对于构造方法中形参名称和类型重复的问题也有说明
-
基本数据类型:和引用类型的区别在于applicationContext.xml
中将ref
改为value
-
使用选择:
- 强制依赖(指对象在创建的过程中必须要注入指定的参数)使用构造器进行,使用
setter
注入有概率不进行注入导致null对象出现
- 可选依赖(指对象在创建过程中注入的参数可有可无)使用
setter
注入进行,灵活性强
-
Spring
框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
- 如果受控对象没有提供setter方法就必须使用构造器注入,自己开发的模块推荐使用setter注入
自动装配
IOC
容器根据bean
所依赖的资源在容器中自动查找并注入到bean
中的过程称为自动装配
主要掌握:
代码参考Spring_07_di_autoware
集合注入
前面完成的是引用类型和基本数据类型的注入,还有集合类型(既可装基本数据类型也可装引用数据类型)未说明
主要掌握:
代码参考Spring_08_di_collection
参考
https://www.bilibili.com/video/BV1Fi4y1S7ix?p=1-16