Java Spring 生命周期

1585364631
2023-03-04 / 0 评论 / 99 阅读 / 正在检测是否收录...

Java Spring 生命周期

1. 概述

Spring Bean的生命周期是从 Bean 实例化之后,即通过反射创建出对象之后,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期。Spring Bean的生命周期大体上分为三个阶段:

  • Bean的实例化阶段: Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是sinaleton的是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化;
  • Bean的初始化阶段: Bean创建之后还仅仅是个"半成品”,还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,后面要学习的Spring的注解功能等spring高频面试题Bean的循环引用问题都是在这个阶段体现的:
  • Bean的完成阶段: 经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池singletonObjects中去了,即完成了Spring Bean的整个生命周期

2. 初始化阶段

2.1 Bean的初始化阶段涉及过程

Spring Bean的初始化过程涉及如下几个过程

  • Bean实例的属性填充
  • Aware接口属性注入
  • BeanPostProcessor的before()方法回调InitializingBean接口的初始化方法回调
  • 自定义初始化方法init回调
  • BeanPostProcessor的after()方法回调

2.2 Bean实例属性填充

Spring在进行属性注入时,会分为如下几种情况:

  • 注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;
  • 注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;
  • 注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题

2.2.1 propertyValues

BeanDefinition 中有对当前Bean实体的注入信息通过属性propertyValues进行了存储

PersonDao.java

package org.example;

public interface PersonDao {
}

UserDao.java

package org.example;

public interface UserDao {
}

UserDaoImpl.java

package org.example;
public class UserDaoImpl implements UserDao {
}

PersonDaoImpl.java

package org.example;

public class PersonDaoImpl implements PersonDao{
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    private String name;
    private UserDao userDao;
}

beans.xml

<bean class="org.example.UserDaoImpl" id="userDao"/>
<bean class="org.example.PersonDaoImpl" id="personDao">
    <property name="userDao" ref="userDao"/>
    <property name="name" value="名字"/>
</bean>

主函数调用

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");

例如PersonDao的属性信息如下:

image-20230302202257853


2.2.2 循环引用

多个实体之间相互依赖并形成闭环的情况就叫做”循环依赖”,也叫做”循环引用。

image-20230302203813390


死循环原理图

image-20230302204138687

UserDaoImpl.java

package org.example;
public class UserDaoImpl implements UserDao {
    private UserService userService;

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

UserServiceImpl.java

package org.example;
public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

beans.xml

<bean class="org.example.UserDaoImpl" id="userDao">
    <property name="userService" ref="userService"/>
</bean>
<bean class="org.example.UserServiceImpl" id="userService">
    <property name="userDao" ref="userDao"/>
</bean>

2.2.3 三级缓存解决循环引用问题

  • Spring提供了三级缓存存储完整Bean实例和半成品Bean实例,用于解决循环引用问题
  • 在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:

image-20230302205021083

image-20230302205227516


UserService和UserDao循环依赖的过程结合上述三级缓存描述一下

  • UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存
  • UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao;
  • UserDao实例化对象,但尚未初始化,将UserDao存储到到三级缓存;
  • UserDao属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移入二级缓存:UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存
  • UserService 注入UserDao:
  • UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存

image-20230302210052015

2.3 常用的Aware接口

Aware接口是一种框架辅助属性注入的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接触到的一般都是业务代码,一个底层功能API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了就可以使用框架提供的类似Aware的接口,让框架给我们注入该对象。

image-20230304144240003

在pom.xml添加ServletContext依赖jar包

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>

UserServiceImpl.java

package org.example;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class UserServiceImpl implements UserService, ApplicationContextAware, BeanNameAware, BeanFactoryAware {

    public UserServiceImpl() {
        System.out.println("构造方法执行");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactory--------" + beanFactory.toString());
    }

    @Override
    public void setBeanName(String s) {
        System.out.println("BeanName--------" + s);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("ApplicationContext--------" + applicationContext.toString());
    }
}

主函数调用

    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        System.out.println(userService);
    }

执行结果

构造方法执行
BeanName--------userService
BeanFactory--------org.springframework.beans.factory.support.DefaultListableBeanFactory@319b92f3: defining beans [userDao,userService]; root of factory hierarchy
ApplicationContext--------org.springframework.context.support.ClassPathXmlApplicationContext@1a93a7ca, started on Sat Mar 04 15:19:28 CST 2023
org.example.UserServiceImpl@436a4e4b

0

评论 (0)

取消