目录
IoC(Inversion of control) 控制反转,是一种思想。
IoC思想:为了解耦,在程序中不直接new获得对象,而是由外部提供对象(控制反转)。如果我们直接通过new的方式获得对象,需要清楚所有的底层对象初始化过程进行依赖处理,才能进行逻辑处理,而通过IoC引入的对象(外部提供),我们通过依赖注入(DI)的方式进行依赖处理就行。
spring技术对IoC思想进行了实现。称为 IoC容器
IoC容器从当IoC思想中的"外部",用来负责对象的创建、初始化等一系列工作,在IoC容器中的对象统称为bean。
DI(Dependency Injection)依赖注入:
在IoC容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入。
使用对象不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系。
测试用的包结构

1.使用spring-IoC我们需要导入坐标:
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-contextartifactId>
- <version>5.3.18version>
- dependency>
2.创建配置文件,在配置文件中加载bean


3.在IoC中创建bean

4.获取IoC中的bean
- public class mainTest {
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext();
- //通过设置的id获取bean
- UserDAO userDAO = (UserDAO) context.getBean("userDAO");
- //bean调用方法
- userDAO.save();
- }
- }
目录结构

userService中引用UserDAO,并且使用其中的方法。
(使用依赖注入形式,原始是new之后使用new的对象调用其中的方法)
1.引入 依赖

2.设置set方法(相当于是一个入口)

3.到配置文件中设置依赖映射(解决依赖)
-
- <bean id="userServiceImpl" class="com.mh.service.UserServiceImpl">
-
-
- <property name="userDAO" ref="userDAO">property>
- bean>
-
- <bean id="userDAO" class="com.mh.dao.UserDAOImpl">bean>
4.测试方法即可。

在IoC中定义的bean,默认为单例模式scope:singleton
设置bean的方式:在

在上述案例中均使用此方式构造bean,使用了默认的空参构造器。


跟静态工厂很相似,实例工厂是非静态的,所以需要先将工厂加载到IoC容器中。


提到bean的生命周期,也就是在bean创建时或销毁时做一些事情。
方式一:使用配置(非标准化)
就是在类中声明两个方法,初始化和销毁的方法,将两个自定义的方法配置到
中。
未控制声明周期时执行
测试代码
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- //测试userDAO方法,无依赖,测试成功
- UserDAO userDAO = (UserDAO) context.getBean("userDAO");
- userDAO.save();
执行结果

设置声明周期的控制
在类中声明方法
- public class UserDAOImpl implements UserDAO{
-
- @Override
- public void save() {
- System.out.println("UserDAO...");
- }
-
- //自定义初始化和销毁方法
- public void init(){
- System.out.println("初始化方法加载...");
- }
-
- public void destroy(){
- System.out.println("销毁方法加载...");
- }
- }
将方法加载到配置中

再次测试执行结果:

会发现一个问题,两个方法,只有初始化方法执行,销毁的方法并没有生效。
方式二:使用接口(标准化)
使用两个接口,实现其中的方法,两个接口分别对应着初始化和销毁的方法。
- public class UserDAOImpl implements UserDAO, InitializingBean, DisposableBean {
-
- @Override
- public void save() {
- System.out.println("UserDAO...");
- }
-
- //销毁方法
- @Override
- public void destroy() throws Exception {
-
- }
-
- //初始化方法(在属性之后执行)
- @Override
- public void afterPropertiesSet() throws Exception {
-
- }
- }
执行测试方法,和方式一 一样:

两种方法可以看出,只有初始化方法执行,销毁的方法没有被执行,也就是声明周期并没有在虚拟机关闭前自动的调用销毁方法,所以我们只能手动将IoC容器关闭,才会执行销毁方法。
1.手动关闭容器
具体使用:close()
测试代码
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- //测试userDAO方法,无依赖,测试成功
- UserDAO userDAO = (UserDAO) context.getBean("userDAO");
- userDAO.save();
- //手动关闭容器
- context.close();
测试代码执行结果(销毁方法执行)

2.注册钩子
测试代码
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- UserDAO userDAO = (UserDAO) context.getBean("userDAO");
- userDAO.save();
- context.registerShutdownHook();
测试代码执行结果和手动关闭结果一致
手动关闭和注册钩子的方式说明:手动关闭需要考虑书写的位置,一般写到代码最后,而注册钩子则没有书写位置要求。
引用数据类型
setter注入,就是给需要的依赖设置set方法,包括基本数据类型和引用数据类型。
依赖:userDAO ,设置set方法
- public class UserServiceImpl implements UserService{
-
- //引用数据类型
- private UserDAO userDAO;
-
- //设置set方法
- public void setUserDAO(UserDAO userDAO) {
- this.userDAO = userDAO;
- }
-
- @Override
- public void save() {
- //在其中调用userDAO(依赖)
- userDAO.save();
- System.out.println("userService...");
- }
- }
配置依赖
- <bean id="userDAO" class="com.mh.dao.UserDAOImpl">bean>
- <bean id="userServiceImpl" class="com.mh.service.UserServiceImpl">
- <property name="userDAO" ref="userDAO">property>
- bean>
简单数据类型
设置简单数据类型依赖,并且设置set方法
- public class UserDAOImpl implements UserDAO {
-
- //设置简单数据类型依赖
- private String uname;
-
- //设置set方法
- public void setUname(String uname) {
- this.uname = uname;
- }
-
- @Override
- public void save() {
- System.out.println("UserDAO..." + uname);
- }
- }
配置依赖
- <bean id="userDAO" class="com.mh.dao.UserDAOImpl">
-
- <property name="uname" value="Tom"/>
- bean>
类中设置简单数据类型和构造器
- public class GoodsDAOImpl implements GoodsDAO {
-
- int num;
-
- public GoodsDAOImpl(int num){
- this.num = num;
- }
-
- @Override
- public void save() {
- System.out.println("goods..." + num );
- }
- }
- <bean id="goodsDAO" class="com.mh.dao.GoodsDAOImpl">
- <constructor-arg name="num" value="100"/>
- bean>
类型装配
此方式存在紧耦合(属性名字需要一致),不利于使用。解决方式:使用类型进行装配
- <bean id="goodsDAO" class="com.mh.dao.GoodsDAOImpl">
- <constructor-arg type="int" value="100"/>
- bean>
此方式解决了名字的耦合问题,但是存在:万一数据类型不唯一的情况就不可用。
比如存在两个int类型的情况。这种情况下:就可以使用索引进行装配。
索引装配
- <bean id="goodsDAO" class="com.mh.dao.GoodsDAOImpl">
- <constructor-arg index="0" value="100"/>
- bean>
依赖注入的方式选择

IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
自动装配的方式:
1.按类型装配

2.按名称装配

自动装配特征