Spring核心知识清单

目录

Bean作用域

Bean生命周期

ApplicationContext

作用和分类

AnnotationConfigApplicationContext

ClassPathXmlApplicationContext

Bean不一定是放在同一个IOC中的

[Bean Factory](#Bean Factory)

[Bean Definetion](#Bean Definetion)

字节码增强技术

Spring中的字节码增强

JDK自带的字节码增强技术

CGLIB

IOC初始化、Bean注入过程

用到了哪些设计模式

工厂模式

单列模式

代理模式

责任链模式

观察者模式

循环依赖

造成原因

解决方法


Bean作用域

作用域 实例数量 生命周期范围 线程安全性 适用场景
singleton 1个/容器 容器级 线程不安全 Service、DAO、工具类
prototype 多个 使用时创建 线程安全 有状态Bean、模型对象
request 1个/请求 HTTP请求 线程安全 请求参数、表单对象
session 1个/会话 HTTP会话 线程不安全 用户信息、权限
application 1个/应用 ServletContext 线程不安全 全局配置、缓存
websocket 1个/连接 WebSocket 线程安全 实时通信数据

Bean生命周期

  1. Bean 定义解析(BeanDefinition) ↓

  2. 实例化(new 对象) ↓

  3. 属性赋值(populateBean)← 依赖注入发生在这里 ↓

  4. Aware 接口回调(感知容器资源) ↓

  5. BeanPostProcessor#postProcessBeforeInitialization(初始化前) ↓

  6. @PostConstruct / InitializingBean#afterPropertiesSet / init-method(初始化) ↓

  7. BeanPostProcessor#postProcessAfterInitialization(初始化后) ↓

  8. Bean 就绪(放入单例池) ↓

  9. 业务方法调用 ↓

  10. 容器关闭 → @PreDestroy / DisposableBean#destroy / destroy-method(销毁)

初始化前:

java 复制代码
@Component
public class LogTimeProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if (bean instanceof OrderService) {
            System.out.println("OrderService 即将初始化,时间:" + System.currentTimeMillis());
        }
        return bean;  // 你可以返回代理对象
    }
}

初始化:

java 复制代码
@Component
public class DataInitializer {
    
    private List<String> dictionaryCache = new ArrayList<>();
    
    @PostConstruct
    public void loadDictionary() {
        // 从数据库加载数据字典,预热到内存
        dictionaryCache.add("VIP");
        dictionaryCache.add("NORMAL");
        System.out.println("数据字典加载完成,大小:" + dictionaryCache.size());
    }
}

建议只用 @PostConstruct。代码最干净,侵入性最低。

ApplicationContext

作用和分类

ApplicationContext其实就是IOC,也叫应用上下文,用来管理Bean:

  • 生成Bean

  • 存储Bean

由于Spring支持通过配置文件Spring.xml和注解两种形式来生成Bean,所以ApplicationContext有两种:

  • ClassPathXmlApplicationContext,用来解析Spring.xml中的标签,生成Bean

  • AnnotationConfigApplicationContext,用来解析注解生成Bean

所以可以理解为IOC有两种,Spring.xml中声明的Bean和用注解声明的Bean存在不同的IOC中,Spring.xml中声明的Bean用ClassPathXmlApplicationContext来存储,注解声明的Bean用AnnotationConfigApplicationContext来存储。

AnnotationConfigApplicationContext

Spring在设计时考虑的很周全,AnnotationConfigApplicationContext把可能用到的注入方式都实现了:

  • 扫描包路径,将包路径下带有@Component的类注入IOC,必须带@Component

  • 手动注入,手动向IOC中register

  • 配置类注入,通过@Configuration+@Bean的方式来注入

java 复制代码
public class TestCase {
    //扫描包路径注入
    @Test
    public void test01(){
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext("com.eryi");
        TestBean testBean = (TestBean)annotationConfigApplicationContext.getBean("testBean");
        System.out.println(testBean.getName());
    }
    //手动注册
    @Test
    public void test02(){
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
        annotationConfigApplicationContext.register(TestBean.class);
        //必须刷新才生效
        annotationConfigApplicationContext.refresh();
        TestBean testBean = (TestBean)annotationConfigApplicationContext.getBean("testBean");
        System.out.println(testBean.getName());
    }
    //通过配置类注入
    @Test
    public void test03(){
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        TestBean testBean = (TestBean)annotationConfigApplicationContext.getBean("testBean");
        System.out.println(testBean.getName());
    }
}

ClassPathXmlApplicationContext

Spring在设计时,考虑的很周全,ClassPathXmlApplicationContext把可能用到的注入方式都实现了:

  • 加载单个XML

  • 加载多个XML

  • 通配符

java 复制代码
// 加载单个XML
ApplicationContext context = 
    new ClassPathXmlApplicationContext("applicationContext.xml");
​
// 加载多个XML
ApplicationContext context = 
    new ClassPathXmlApplicationContext("services.xml", "daos.xml");
​
// 支持通配符
ApplicationContext context = 
    new ClassPathXmlApplicationContext("classpath*:config/spring-*.xml");

Bean不一定是放在同一个IOC中的

基于xml配置的和注解的Bean在不同的application中,也就是说IOC有两个。Bean初始化的时候,只会从一个ApplicationContext去获取Bean,具体去哪个里面找,取决于当前被初始化这个Bean是在哪个ApplicationContext中的。比如:

  • Spring.xml声明了一个Bean,那个Bean中有@Autowire,那么这个@Autowire,会是去ClassPathXmlApplicationContext中找

  • @Component声明了一个Bean,那个Bean中有@Autowire,那么这个@Autowire,会是去AnnotationConfigApplicationContext中找

Bean Factory

在IOC中ApplicationContext仅仅是外层的壳子,真正干活的是Bean Factory,Bean Factory被封装在各种父类中,各种ApplicationContext自己去实现自己的父类,每个ApplicationContext都是自己的Bean factory在干活儿。

Bean Factory只是一个接口,实现类是DefaultListableBeanFactory。所有核心资源都被封装在DefaultListableBeanFactory中,比如Bean Definetion:

DefaultListableBeanFactory的核心方法是getBean,该方法就会去存放BeanDefinetiond的map中去找对应的BeanDefinetion,然后根据一系列注解比如作用域等来决定这个Bean该如何去拿到。

存放BeanDefinetion的是一个ConcurrentHashMap,这是完全基于并发安全的考虑,因为完全可能在getBean的时候还在register。

Bean Definetion

Bean Definetion是对Bean的全方位描述,会以Spring支持的视角来全方位描述Bean,比如Class、init方法、构造参数有哪些、是否是懒加载、是否要依赖于其他类的存在等等:

java 复制代码
// 包路径:org.springframework.beans.factory.support.AbstractBeanDefinition
public abstract class AbstractBeanDefinition implements BeanDefinition, Cloneable {
​
    // ========== 常量定义 ==========
    public static final String SCOPE_DEFAULT = "";  // 默认作用域(singleton)
    
    // 自动装配模式常量
    public static final int AUTOWIRE_NO = 0;        // 不自动装配
    public static final int AUTOWIRE_BY_NAME = 1;   // byName
    public static final int AUTOWIRE_BY_TYPE = 2;   // byType
    public static final int AUTOWIRE_CONSTRUCTOR = 3; // 构造器
    @Deprecated
    public static final int AUTOWIRE_AUTODETECT = 4;  // 已废弃(自动检测)
​
    // ========== 核心属性 ==========
    
    // Bean的类信息
    private volatile Object beanClass;  // 可以是String(类名)或Class<?>
    
    // 作用域
    private String scope = SCOPE_DEFAULT;
    
    // 是否抽象(抽象Bean不能被实例化,仅作为模板)
    private boolean abstractFlag = false;
    
    // 是否懒加载
    private boolean lazyInit = false;
    
    // 自动装配模式
    private int autowireMode = AUTOWIRE_NO;
    
    // 依赖检查(Spring 3.0后已废弃,保留但不再使用)
    private int dependencyCheck = DEPENDENCY_CHECK_NONE;
    
    // 依赖的Bean(必须在当前Bean之前创建)
    private String[] dependsOn;
    
    // 是否可作为自动装配的候选
    private boolean autowireCandidate = true;
    
    // 是否为主要候选(当有多个同类型Bean时优先注入)
    private boolean primary = false;
    
    // 工厂Bean名称
    private String factoryBeanName;
    
    // 工厂方法名称
    private String factoryMethodName;
    
    // 构造器参数(包装类,处理索引、类型、值)
    private ConstructorArgumentValues constructorArgumentValues;
    
    // 属性值(包装类,处理属性名和值)
    private MutablePropertyValues propertyValues;
    
    // 方法覆盖(用于lookup-method和replaced-method)
    private MethodOverrides methodOverrides;
    
    // 初始化方法
    private String initMethodName;
    
    // 销毁方法
    private String destroyMethodName;
    
    // 角色(应用/支撑/基础设施)
    private int role = BeanDefinition.ROLE_APPLICATION;
    
    // 描述
    private String description;
    
    // 资源来源(哪个文件、哪行定义的)
    private Resource resource;
    
    // 父BeanDefinition名称
    private String parentName;
    
    // ========== 构造方法 ==========
    
    protected AbstractBeanDefinition() {
        this(null, null);
    }
    
    protected AbstractBeanDefinition(ConstructorArgumentValues cargs, 
                                      MutablePropertyValues pvs) {
        // 设置构造器参数
        this.constructorArgumentValues = cargs;
        // 设置属性值
        this.propertyValues = pvs;
    }
    
    // ========== 核心方法实现 ==========
    
    @Override
    public void setBeanClassName(String beanClassName) {
        this.beanClass = beanClassName;
    }
    
    @Override
    public String getBeanClassName() {
        if (this.beanClass instanceof Class) {
            // 如果是Class对象,返回类名
            return ((Class<?>) this.beanClass).getName();
        }
        // 如果是String,直接返回
        return (String) this.beanClass;
    }
    
    /**
     * 获取Bean的Class对象(如果已解析)
     * 这是AbstractBeanDefinition特有的方法,不在接口中
     */
    public Class<?> getBeanClass() throws IllegalStateException {
        if (this.beanClass instanceof Class) {
            return (Class<?>) this.beanClass;
        }
        throw new IllegalStateException("Bean class name [" + this.beanClass + 
            "] has not been resolved into an actual Class");
    }
    
    /**
     * 判断Bean类是否已解析为Class对象
     */
    public boolean hasBeanClass() {
        return (this.beanClass instanceof Class);
    }
    
    @Override
    public void setScope(String scope) {
        this.scope = scope;
    }
    
    @Override
    public String getScope() {
        return this.scope;
    }
    
    @Override
    public boolean isSingleton() {
        return SCOPE_SINGLETON.equals(this.scope) || SCOPE_DEFAULT.equals(this.scope);
    }
    
    @Override
    public boolean isPrototype() {
        return SCOPE_PROTOTYPE.equals(this.scope);
    }
    
    /**
     * 验证BeanDefinition的合法性
     * 在注册到容器前会调用
     */
    public void validate() throws BeanDefinitionValidationException {
        // 如果有工厂方法,则不需要beanClass
        if (hasMethodOverrides() && getFactoryMethodName() != null) {
            throw new BeanDefinitionValidationException(
                "Cannot combine method overrides with factory method: " + this);
        }
        
        // 其他验证逻辑...
    }
    
    /**
     * 克隆BeanDefinition(原型模式)
     */
    @Override
    public Object clone() {
        return cloneBeanDefinition();
    }
    
    public abstract AbstractBeanDefinition cloneBeanDefinition();
    
    // ========== equals 和 hashCode ==========
    
    @Override
    public boolean equals(Object other) {
        if (this == other) return true;
        if (!(other instanceof AbstractBeanDefinition)) return false;
        
        AbstractBeanDefinition that = (AbstractBeanDefinition) other;
        
        // 比较关键属性
        // ... 省略具体比较逻辑
    }
    
    @Override
    public int hashCode() {
        // ... 省略
    }
}

字节码增强技术

Spring中的字节码增强

在Bean的创建和初始化的过程中会去解析Class带有的注解,来判断是不是要织入一些代码内容,诸如事务、切面等,如果要织入一些代码内容会用字节码增强来进行织入。

Bean的创建和初始化会出现在Spring容器启动的时候将Bean装入IOC的时候,或者是getBean的时候判断初Bean的作用域是需要新创建和初始化一个新的Bean的时候。

复制代码
【Bean创建过程】
       ↓
【解析Class上的注解】←────────────┐
  ↓ (比如 @Transactional)          │
【判断是否需要增强】───否──→【返回原始Bean】
  ↓ 是                            │
【织入增强代码】                   │
  ↓ (通过CGLIB生成子类)            │
【返回代理Bean】──────────────────┘

在动用字节码增强技术创建代理对象的时候,好像用上了缓存,避免重复创建代理对象,会先去缓存中找代理对象存在不,不存在再创建新的代理对象。

使用字节码增强技术来创建代理对象的时候,会去判断到底是用JDK的动态代理还是CGLIB来进行动态代理

java 复制代码
// AnnotationAwareAspectJAutoProxyCreator.java
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    // 判断这个Bean是否需要代理(是否有@Transactional、AOP切点等)
    if (shouldSkip(beanClass, beanName)) {
        return null;
    }
    
    // 如果需要代理,创建代理对象
    return createProxy(beanClass, beanName);
}
​
private Object createProxy(Class<?> beanClass, String beanName) {
    // 创建代理工厂
    ProxyFactory proxyFactory = new ProxyFactory();
    
    // 添加切面逻辑
    // ...
    
    // **根据情况选择代理方式:**
    // 1. 如果实现了接口,用 JDK 动态代理
    // 2. 如果没有接口,用 CGLIB 字节码增强
    return proxyFactory.getProxy();
}

JDK自带的字节码增强技术

JDK自带的字节码技术主要就是JDK的动态代理技术,其底层核心就是用接口去实现被代理对象的一个包装类,这个包装类就是给被代理对象裹上一层外壳,而这个外壳肯定要和代理对象的形状一样,因此在创建代理对象的时候要给出一个模板,即要传给其一个接口。

https://blog.csdn.net/Joker_ZJN/article/details/129305791?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522c3bed4c12167ec5d6e5a406806370f41%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=c3bed4c12167ec5d6e5a406806370f41&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-129305791-null-null.nonecase&utm_term=%E4%BB%A3%E7%90%86&spm=1018.2226.3001.4450

需要传入接口,是JDK的字节码增强技术最大的一个限制,没有接口就无能为力了。

CGLIB

在CGLIB出现之前,JDK动态代理有一个很"挑食"的毛病:它要求被代理的目标对象必须实现至少一个接口。这对于很多只关心具体实现、没有提取接口的类来说,就无法被代理了。

CGLIB的出现完美地解决了这个痛点。它的核心思想是:既然不能通过接口去代理你,那我就"成为你的后代"------通过继承你,生成一个子类来作为代理对象

CGLIB的底层依赖一个叫ASM的字节码操作框架,它可以在运行时动态地生成新的类。生成一个CGLIB代理主要分为三步:

  • 找到父亲:首先,CGLIB会锁定你的目标类,把它作为即将生成的代理类的"父类"。
  1. 生成"儿子":然后,它使用字节码技术,动态地创建一个这个目标类的子类。这个子类就是代理对象。

  2. 重写"行为":在这个动态生成的子类中,CGLIB会重写父类中所有非final的方法。重写的逻辑就是:不直接干活,而是转手交给一个我们自定义的"拦截器"去处理。

CGLIB通过两个核心组件来完成整个过程:

  • Enhancer(增强器) :这是CGLIB中最核心的类,可以理解为整个过程的"总指挥"。你需要告诉它:要代理哪个类(setSuperclass),以及当方法被调用时,该执行什么逻辑(setCallback)。最后调用create()方法,它就会为你生成代理对象。

  • MethodInterceptor(方法拦截器) :这是一个接口,你需要实现它来编写增强逻辑。它就像是嵌入到代理类中的一个"窃听器",代理对象的所有方法调用,最终都会走到它的intercept方法里来。在这里,你可以自由地编写前置逻辑、后置逻辑,或者决定是否以及如何调用目标对象的原始方法。

代码示例:

java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
​
// 1. 目标类:一个普通的、没有实现任何接口的类
public class UserService {
    public void createUser(String username) {
        System.out.println("正在创建用户: " + username);
    }
    public void updateUser(String username) {
        System.out.println("正在更新用户: " + username);
    }
}
​
// 2. 实现 MethodInterceptor,编写增强逻辑
class LogInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("[日志] 开始执行方法: " + method.getName());
        
        // 调用目标类的原始方法 (注意这里用的是 proxy.invokeSuper)
        Object result = proxy.invokeSuper(obj, args);
        
        System.out.println("[日志] 方法执行完毕: " + method.getName());
        return result;
    }
}
​
// 3. 使用 Enhancer 创建代理对象并使用
public class CglibDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        // 设置目标类为父类
        enhancer.setSuperclass(UserService.class);
        // 设置方法拦截器
        enhancer.setCallback(new LogInterceptor());
​
        // 生成代理对象
        UserService proxy = (UserService) enhancer.create();
​
        // 通过代理对象调用方法
        proxy.createUser("Alice");
        proxy.updateUser("Bob");
    }
}

IOC初始化、Bean注入过程

复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                          Spring IoC容器启动流程                               │
└─────────────────────────────────────────────────────────────────────────────┘
                                    ↓
                    ┌─────────────────────────────────┐
                    │    new ApplicationContext()     │
                    └─────────────────────────────────┘
                                    ↓
                    ┌─────────────────────────────────┐
                    │         refresh()                │
                    │   (容器初始化的总指挥)            │
                    └─────────────────────────────────┘
                                    ↓
        ┌───────────────────────────┼───────────────────────────┐
        ↓                           ↓                           ↓
┌───────────────┐         ┌─────────────────┐         ┌─────────────────┐
│obtainFreshBean│         │invokeBeanFactory│         │registerBeanPost │
│Factory()      │         │PostProcessors() │         │Processors()     │
│创建BeanFactory│         │处理BeanDefinition│         │注册实例处理器    │
│加载BeanDef    │         │(可修改定义)      │         │(准备后续使用)    │
└───────────────┘         └─────────────────┘         └─────────────────┘
        ↓                           ↓                           ↓
┌───────────────┐         ┌─────────────────┐         ┌─────────────────┐
│beanDefinition │         │ConfigurationClass│         │AutowiredAnnota │
│Map已填充      │         │PostProcessor    │         │tionBeanPostProc│
│(图纸已就绪)   │         │处理@Configuration│         │(准备注入)       │
└───────────────┘         └─────────────────┘         └─────────────────┘
                                    ↓
                    ┌─────────────────────────────────┐
                    │   finishBeanFactoryInitialization│
                    │   (开始实例化所有非懒加载Bean)    │
                    └─────────────────────────────────┘
                                    ↓
                    ┌─────────────────────────────────┐
                    │         getBean("A")             │
                    │   (触发递归创建过程)              │
                    └─────────────────────────────────┘
                                    ↓
        ┌───────────────────────────┼───────────────────────────┐
        ↓                           ↓                           ↓
┌───────────────┐         ┌─────────────────┐         ┌─────────────────┐
│createBeanInst │         │  populateBean()  │         │ initializeBean()│
│ance()         │         │  依赖注入         │         │  初始化         │
│反射创建实例    │         │  递归getBean()   │         │  AOP代理创建    │
└───────────────┘         └─────────────────┘         └─────────────────┘
        ↓                           ↓                           ↓
┌───────────────┐         ┌─────────────────┐         ┌─────────────────┐
│原始对象已创建  │         │所有属性已注入    │         │增强/代理完成    │
│(可能不完整)   │         │(依赖已满足)      │         │(Bean就绪)       │
└───────────────┘         └─────────────────┘         └─────────────────┘
                                    ↓
                    ┌─────────────────────────────────┐
                    │       Bean就绪,返回使用          │
                    └─────────────────────────────────┘

Spring是如何确保在创建一个Bean的时候这个Bean成员变量里面需要被注入的对象是在IOC中已经存在的?是遇见一个要是不存在,先将它注入IOC?答:如果依赖不存在,就立即创建它。

用到了哪些设计模式

工厂模式

主要体现在Bean Factory的getBean这个"来料加工,返回成品"的过程中。整个Spring将Bean注入IOC都是用的Bean Factory来getBean。

单列模式

主要体现在生命周期是单例的类的时候去拿这个单列的时候

代理模式

主要体现在用字节码增强技术来生成代理对象,实现一些功能性的注解上,比如将AOP和事务的代码逻辑织入对应被注解注解的地方。

责任链模式

Spring中存在大量的处理器,主要集中在两个核心方面:

  • 对Bean Definition创建过程的介入和控制

  • 对Bean示例创建过程的介入和控制

这些处理器发挥作用都是去编列然后生效,明显的责任链模式。

BeanDefinition的处理器:

处理器 类型 执行时机 主要作用 典型应用
BeanDefinitionRegistryPostProcessor 接口 所有 BeanDefinition 加载完成后,BeanFactoryPostProcessor 之前 注册/移除 BeanDefinition • 动态添加新的 Bean 定义 • 删除已有的 Bean 定义 • 批量导入配置类 @ComponentScan 扫描 MyBatis Mapper 扫描 动态注册 Feign Client
BeanFactoryPostProcessor 接口 BeanDefinitionRegistryPostProcessor 之后,Bean 实例化之前 修改 BeanDefinition 属性 • 修改作用域 (scope) • 修改懒加载 (lazy-init) • 添加/修改属性值 • 改变初始化/销毁方法 占位符替换 ${...} 配置中心动态刷新 环境特定配置覆盖
PropertySourcesPlaceholderConfigurer BeanFactoryPostProcessor 阶段 解析属性占位符 • 将 ${jdbc.url} 替换为实际值 • 加载多个 properties 文件 • 支持环境变量回退 读取 application.properties 多环境配置管理 加密属性解密

Bean的处理器:

处理器 类型 执行时机 主要作用 典型应用
InstantiationAwareBeanPostProcessor 接口 实例化前后 + 属性注入时 控制实例化过程postProcessBeforeInstantiation : 实例化前返回代理,替代真实 Bean • postProcessAfterInstantiation : 实例化后返回 false 可阻止属性注入 • postProcessProperties: 修改要注入的属性值 Mock 对象替换 动态决定是否注入 动态添加属性
MergedBeanDefinitionPostProcessor 接口 实例化后、属性注入前 缓存注解元数据 • 解析 @Autowired@Value 等注解 • 缓存需要注入的字段/方法信息 • 提前准备依赖注入所需数据 @Autowired 处理 @PostConstruct 查找 提前解析注入点
BeanPostProcessor 接口 初始化方法前后 处理 Bean 实例postProcessBeforeInitialization : 初始化前处理(如注入依赖) • postProcessAfterInitialization: 初始化后处理(如创建代理) @Autowired 注入 AOP 代理创建 自定义注解处理
DestructionAwareBeanPostProcessor 接口 Bean 销毁前 销毁前资源清理 • 释放连接池 • 关闭文件流 • 记录销毁日志 • 清理线程池 @PreDestroy 处理 连接池关闭 临时文件删除

观察者模式

Spring支持事件发布和监听器监听。

事件(ApplicationEvent)

所有事件都必须继承ApplicationEvent抽象类,它继承自JDK的EventObject

java 复制代码
// Spring事件基类
public abstract class ApplicationEvent extends EventObject {
    private final long timestamp;  // 事件发生时间戳
    
    public ApplicationEvent(Object source) {
        super(source);  // source是事件源
        this.timestamp = System.currentTimeMillis();
    }
}

内置事件

事件类 触发时机
ContextRefreshedEvent 容器初始化/刷新完成
ContextStartedEvent 容器启动(调用start())
ContextStoppedEvent 容器停止(调用stop())
ContextClosedEvent 容器关闭

监听器(ApplicationListener)

有两种方式创建监听器:

方式一:实现接口

java 复制代码
@Component
public class MyListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("容器刷新完成,可以做一些初始化工作");
    }
}

方式二:使用注解(推荐)

java 复制代码
@Component
public class MyAnnotationListener {
    
    @EventListener
    public void handleContextRefresh(ContextRefreshedEvent event) {
        System.out.println("注解方式:容器刷新完成");
    }
    
    @EventListener(condition = "#event.success")  // 支持条件过滤
    public void handleConditional(MyEvent event) {
        System.out.println("只有success=true的事件才会被处理");
    }
}

事件发布器(ApplicationEventPublisher)

任何需要发布事件的地方都可以注入ApplicationEventPublisher

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private ApplicationEventPublisher publisher;  // 注入发布器
    
    public void registerUser(String username) {
        // 1. 业务逻辑:保存用户
        System.out.println("保存用户: " + username);
        
        // 2. 发布事件
        UserRegisteredEvent event = new UserRegisteredEvent(this, username);
        publisher.publishEvent(event);  // 发布事件
    }
}

源码过程

初始化过程:

java 复制代码
// AbstractApplicationContext.java
public void refresh() {
    // ...
    
    // 1. 初始化事件广播器
    initApplicationEventMulticaster();
    
    // 2. 注册监听器
    registerListeners();
    
    // 3. 完成初始化,发布ContextRefreshedEvent
    finishRefresh();
}
​
protected void initApplicationEventMulticaster() {
    // 默认使用SimpleApplicationEventMulticaster
    this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
}
​
protected void registerListeners() {
    // 将容器中所有ApplicationListener类型的bean注册到广播器
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class);
    for (String name : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(name);
    }
}

事件发布过程:

java 复制代码
// SimpleApplicationEventMulticaster.java
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
    // 1. 获取所有匹配的监听器
    Collection<ApplicationListener<?>> listeners = getApplicationListeners(event, type);
    
    // 2. 获取线程池(如果有)
    Executor executor = getTaskExecutor();
    
    // 3. 遍历执行监听器
    for (ApplicationListener<?> listener : listeners) {
        if (executor != null) {
            // 异步执行
            executor.execute(() -> invokeListener(listener, event));
        } else {
            // 同步执行(默认)
            invokeListener(listener, event);
        }
    }
}
​
private void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    try {
        // 最终调用监听器的onApplicationEvent方法
        listener.onApplicationEvent(event);
    } catch (Exception ex) {
        // 异常处理
    }
}

如何将事件准确的推个监听对应事件的监听器:

在推送事件的时候会通过反射来判断泛型的类型是不是和事件的类型匹配。

java 复制代码
private boolean supportsEvent(ApplicationListener<?> listener, 
                              ApplicationEvent event, 
                              ResolvableType eventType) {
    
    if (listener instanceof GenericApplicationListener) {
        // 支持泛型的监听器
        return ((GenericApplicationListener) listener).supportsEventType(eventType);
    }
    
    if (listener instanceof SmartApplicationListener) {
        // 智能监听器
        return ((SmartApplicationListener) listener).supportsEventType(event.getClass());
    }
    
    // **普通监听器:通过反射判断泛型参数是否匹配**
    ResolvableType type = ResolvableType.forClass(listener.getClass())
        .as(ApplicationListener.class).getGeneric();
    return type.isAssignableFrom(eventType);
}

循环依赖

造成原因

所谓循环依赖无非就是对象之间相互持有,相互之间为对方的成员变量。

首先在创建对象的时候的循环依赖,也叫"构造器循环依赖",这种谁也绝决不了,会直接报StackOverflowError,比如下面这种:

java 复制代码
public class BeanA {
    private BeanB beanB;
    public BeanA() {
        this.beanB = new BeanB();
    }
}
public class BeanB {
    private BeanA beanA;
    public BeanB() {
        this.beanA = new BeanA();
    }
}

所谓的循环依赖,其实叫"循环注入"更为恰当,不是出现在创建对象的时候,而是出现在注入依赖的时候,因为Bean没被完全注入好,所以在IOC中其实是不存在的(也就是存放完全能用的Bean的那个singletonObjects中不存在),在IOC中拿不到,就回去创建,由此造成了死循环:

java 复制代码
@Service
public class A {
    @Autowired
    private B b;
   
}
​
@Service
public class B {
    @Autowired
    private A a;
}

解决方法

spring用三层缓存解决了这个问题:

java 复制代码
// DefaultSingletonBeanRegistry.java
public class DefaultSingletonBeanRegistry {
    
    // **一级缓存:完全创建好的单例Bean**
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    // **二级缓存:早期暴露的Bean(尚未完全初始化)**
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    
    // **三级缓存:单例工厂(用于生成早期暴露的Bean)**
    private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);
}

源码过程:

总结起来就是把半成品都放到缓存中,一点点的推进、完成整个Bean的初始化。

java 复制代码
// **步骤1:开始创建A**
doCreateBean("A") {
    // 1.1 实例化A(调用构造器,得到原始对象)
    Object a = createBeanInstance("A");
    
    // 1.2 **将A的工厂放入三级缓存**(提前暴露)
    addSingletonFactory("A", () -> getEarlyBeanReference("A", a));
    // 此时缓存状态:
    // 一级缓存: {}
    // 二级缓存: {}
    // 三级缓存: {"A" -> ObjectFactory}
    
    // 1.3 开始属性注入,发现需要B
    populateBean("A") {
        // 递归调用getBean("B")
        getBean("B") {
            // **步骤2:开始创建B**
            doCreateBean("B") {
                // 2.1 实例化B
                Object b = createBeanInstance("B");
                
                // 2.2 将B的工厂放入三级缓存
                addSingletonFactory("B", () -> getEarlyBeanReference("B", b));
                // 此时缓存:
                // 一级: {}
                // 二级: {}
                // 三级: {"A"->factory, "B"->factory}
                
                // 2.3 属性注入,发现需要A
                populateBean("B") {
                    // **关键:查找A的依赖**
                    Object a = beanFactory.resolveDependency(A.class);
                    
                    // resolveDependency内部调用getBean("A")
                    getBean("A") {
                        // **步骤3:再次获取A(此时A在三级缓存)**
                        Object sharedA = getSingleton("A");
                        
                        // getSingleton源码:
                        protected Object getSingleton(String beanName, boolean allowEarlyReference) {
                            // 3.1 查一级缓存(没有)
                            Object singletonObject = this.singletonObjects.get(beanName);
                            
                            // 3.2 查二级缓存(没有)
                            if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
                                singletonObject = this.earlySingletonObjects.get(beanName);
                                
                                // 3.3 **查三级缓存,拿到工厂,创建早期引用**
                                if (singletonObject == null && allowEarlyReference) {
                                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                                    if (singletonFactory != null) {
                                        // **调用工厂,获取早期引用**
                                        singletonObject = singletonFactory.getObject();
                                        
                                        // **放入二级缓存,删除三级缓存**
                                        this.earlySingletonObjects.put(beanName, singletonObject);
                                        this.singletonFactories.remove(beanName);
                                    }
                                }
                            }
                            return singletonObject;
                        }
                    }
                    
                    // **拿到A的早期引用(半成品)**
                    // **注入到B中**
                    field.set(b, a);
                }
                
                // 2.4 初始化B
                initializeBean("B", b);
                
                // 2.5 **将B放入一级缓存**
                addSingleton("B", b);
                // 一级: {"B"->完整B}
                // 二级: {"A"->半成品A}
                // 三级: {}
                
                return b;
            }
            
            // **1.4 将B注入到A**
            field.set(a, b);
        }
    }
    
    // 1.4 初始化A
    initializeBean("A", a);
    
    // 1.5 **将A放入一级缓存**
    addSingleton("A", a);
    // 一级: {"A"->完整A, "B"->完整B}
    // 二级: {}
    // 三级: {}
    
    return a;
}

为什么要三级缓存?而不是二级缓存?

主要是处理需要生成动态代理的Bean的初始化,如果都是一些简单Bean,都没有被功能性注解(如AOP和事务等注解)所注解,那么二级缓存完全够了。三级缓存完全是基于性能的考虑,因为如果要生成代理对象,提前创建代理,但可能没人用,浪费性能。

比如下面这种:

java 复制代码
@Service
@Transactional  // 需要AOP代理!
public class A {
    @Autowired
    private B b;
}
​
@Service
public class B {
    @Autowired
    private A a;
}

二级缓存就会陷入纠结:

  • 放原始对象:B拿到的是原始A,但A需要代理,后面还得改

  • 放代理对象:提前创建代理,但可能没人用,浪费性能

所以干脆加一层三级缓存,里面放Bean Factory,实际要用到时,再去找Bean Factory拿,就不会存在提前创建代理,但可能没人用,浪费性能这种情况。

相关推荐
yaoxin5211232 小时前
329. Java Stream API - 打开 Optional 的正确方式:如何安全提取值?
java·安全·rpc
山北雨夜漫步2 小时前
点评day04 Redisson
java·jvm
利刃大大2 小时前
【SpringCloud】Gateway Filter Factories && 过滤器执行顺序 && 自定义过滤器
java·后端·网关·spring cloud·gateway
Andy Dennis2 小时前
Java&Go 内存管理
java·jvm·go
xuzhiqiang07249 小时前
Java进阶之路,Java程序员职业发展规划
java·开发语言
时艰.9 小时前
订单系统历史数据归档方案
java
一只叫煤球的猫11 小时前
ThreadForge v1.1.0 发布:让 Java 并发更接近 Go 的开发体验
java·后端·性能优化
014.11 小时前
2025最新jenkins保姆级教程!!!
java·运维·spring boot·spring·jenkins
浣熊88811 小时前
天机学堂虚拟机静态ip无法使用(重启后ip:192.168.150.101无法使用连接Mobaxterm数据库等等,或者无法使用修改之后的Hosts域名去访问nacos,jenkins)
java·微服务·虚拟机·天机学堂·重启之后静态ip用不了