BeanNameAware源码分析

BeanNameAware

一、基本信息

✒️ 作者 - Lex 📝 博客 - 我的CSDN 📚 文章目录 - 所有文章 🔗 源码地址 - BeanNameAware源码

二、接口描述

BeanNameAware 接口。当一个 Bean 实现了此接口,可以感知其在 Spring 容器中的名称。

三、接口源码

BeanNameAware 是 Spring 框架自 01.11.2003 开始引入的一个核心接口。实现BeanNameAware接口的对象会在Spring容器中被自动注入Bean的名称。

/**
 * 由希望知道其在 bean 工厂中名称的 beans 实现的接口。
 * 注意通常不推荐一个对象依赖于其 bean 名称,因为这可能导致对外部配置的脆弱依赖,
 * 以及可能的不必要的对 Spring API 的依赖。
 *
 * 有关所有 bean 生命周期方法的列表,请参见
 * BeanFactory BeanFactory javadocs。
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 01.11.2003
 * @see BeanClassLoaderAware
 * @see BeanFactoryAware
 * @see InitializingBean
 */
public interface BeanNameAware extends Aware {

	/**
	 * 设置在创建此 bean 的 bean 工厂中的 bean 的名称。
	 * 此方法在填充常规 bean 属性之后被调用,但在如 InitializingBean#afterPropertiesSet() 这样的
	 * 初始化回调或自定义初始化方法之前被调用。
	 * @param name 工厂中的 bean 的名称。注意,这个名称是工厂中使用的实际 bean 名称,
     * 这可能与最初指定的名称不同:尤其对于内部 bean 名称,实际的 bean 名称可能已通过添加 "#..." 后缀变得唯一。
     * 如果需要,可以使用 BeanFactoryUtils#originalBeanName(String) 方法来提取没有后缀的原始 bean 名称。
	 */
	void setBeanName(String name);

}

四、主要功能

  1. 提供 setBeanName 方法

    • 当一个 Bean 实现了 BeanNameAware 接口,它需要提供 setBeanName 方法的实现。这个方法有一个参数,即该 Bean 在 Spring 容器中的名称。
  2. 自动回调

    • 当 Spring 容器创建并配置一个实现了 BeanNameAware 接口的 Bean 时,容器会自动回调 setBeanName 方法,并传入该 Bean 在容器中的名称。这意味着我们不需要显式地调用这个方法;Spring 容器会自动处理。
  3. 获取 Bean 的名称

    • 有时,Bean 可能需要知道其在容器中的名称以执行特定的逻辑或功能,或者为了日志记录或其他目的。通过实现 BeanNameAware,Bean 可以轻松获得此信息。

五、最佳实践

首先来看看启动类入口,上下文环境使用AnnotationConfigApplicationContext(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个MyConfiguration组件类。

public class BeanNameAwareApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
    }
}

使用@ComponentScan注解,告诉 Spring 容器去 “com.xcs.spring.service” 扫描包及其子包

@Configuration
@ComponentScan("com.xcs.spring.service")
public class MyConfiguration {

}

MyBasePayService 是一个抽象类,结合了 Spring 的三个特殊接口:BeanNameAware(让 Bean 知道其名字)、InitializingBean(Bean 属性设置后的初始化操作)和 DisposableBean(Bean 销毁前的操作)。

public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean {

    private String beanName;

    @Override
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Module " + beanName + " has been registered.");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("Module " + beanName + " has been unregistered.");
    }
}

MyAliPayServiceMyWeChatPayService 是两个支付服务类,都继承自 MyBasePayService,因此它们会自动获得与 Spring 容器生命周期相关的基本功能。这种设计方式为多个支付服务提供了一个共同的生命周期管理模式,同时允许每个服务添加其特定的支付逻辑。

@Service
public class MyAliPayService extends MyBasePayService{
    
}

@Service
public class MyWeChatPayService extends MyBasePayService{
    
}

运行结果发现,当 Spring 容器启动并初始化 Beans 时,它正确地识别并实例化了 MyAliPayServiceMyWeChatPayService 这两个服务。由于这两个服务都继承自 MyBasePayService,在 Bean 的属性被设置之后(即在 afterPropertiesSet() 方法中),它们分别打印出了 “Module myAliPayService has been registered.” 和 “Module myWeChatPayService has been registered.” 这两条信息,提供了一个共同的生命周期管理模式。

Module myAliPayService has been registered.
Module myWeChatPayService has been registered.

六、时序图

BeanNameAwareApplication AnnotationConfigApplicationContext AbstractApplicationContext DefaultListableBeanFactory AbstractBeanFactory DefaultSingletonBeanRegistry AbstractAutowireCapableBeanFactory MyBasePayService AnnotationConfigApplicationContext(componentClasses) 创建上下文 refresh() 刷新上下文 finishBeanFactoryInitialization(beanFactory) 初始化Bean工厂 preInstantiateSingletons() 实例化单例 getBean(name) 获取Bean doGetBean(name,requiredType,args,typeCheckOnly) 执行获取Bean getSingleton(beanName,singletonFactory) 获取单例Bean getObject() 获取Bean实例 createBean(beanName,mbd,args) 创建Bean doCreateBean(beanName,mbd,args) 执行Bean创建 initializeBean(beanName,bean,mbd) 负责bean的初始化 invokeAwareMethods(beanName, bean) 调用Aware方法 setBeanName(beanName) 设置Bean名称 返回Bean对象 返回Bean对象 初始化完成 BeanNameAwareApplication AnnotationConfigApplicationContext AbstractApplicationContext DefaultListableBeanFactory AbstractBeanFactory DefaultSingletonBeanRegistry AbstractAutowireCapableBeanFactory MyBasePayService BeanNameAware时序图

七、源码分析

首先来看看启动类入口,上下文环境使用AnnotationConfigApplicationContext(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个MyConfiguration组件类。

public class BeanNameAwareApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
    }
}

org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext构造函数中,执行了三个步骤,我们重点关注refresh()方法

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    this();
    register(componentClasses);
    refresh();
}

org.springframework.context.support.AbstractApplicationContext#refresh方法中我们重点关注一下finishBeanFactoryInitialization(beanFactory)这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。

@Override
public void refresh() throws BeansException, IllegalStateException {
    // ... [代码部分省略以简化]
    // Instantiate all remaining (non-lazy-init) singletons.
    finishBeanFactoryInitialization(beanFactory);
    // ... [代码部分省略以简化]
}

org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization方法中,会继续调用DefaultListableBeanFactory类中的preInstantiateSingletons方法来完成所有剩余非懒加载的单列Bean对象。

/**
 * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。
 * 
 * @param beanFactory 要初始化的bean工厂
 */
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // ... [代码部分省略以简化]
    // 完成所有剩余非懒加载的单列Bean对象。
    beanFactory.preInstantiateSingletons();
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中getBean(beanName)是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用getBean方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。

public void preInstantiateSingletons() throws BeansException {
    // ... [代码部分省略以简化]
    // 循环遍历所有bean的名称
    for (String beanName : beanNames) {
        getBean(beanName);
    }
    // ... [代码部分省略以简化]
}

org.springframework.beans.factory.support.AbstractBeanFactory#getBean()方法中,又调用了doGetBean方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,doGetBean负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。

@Override
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。

protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {
    // ... [代码部分省略以简化]

    // 开始创建bean实例
    if (mbd.isSingleton()) {
        // 如果bean是单例的,我们会尝试从单例缓存中获取它
        // 如果不存在,则使用lambda创建一个新的实例
        sharedInstance = getSingleton(beanName, () -> {
            try {
                // 尝试创建bean实例
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                // ... [代码部分省略以简化]
            }
        });
        // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    // ... [代码部分省略以简化]

    // 确保返回的bean实例与请求的类型匹配
    return adaptBeanInstance(name, beanInstance, requiredType);
}

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的ObjectFactory创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    // 断言bean名称不能为空
    Assert.notNull(beanName, "Bean name must not be null");

    // 同步访问单例对象缓存,确保线程安全
    synchronized (this.singletonObjects) {
        // 从缓存中获取单例对象
        Object singletonObject = this.singletonObjects.get(beanName);

        // 如果缓存中没有找到
        if (singletonObject == null) {
            // ... [代码部分省略以简化]

            try {
                // 使用工厂创建新的单例实例
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                // ... [代码部分省略以简化]
            }
            catch (BeanCreationException ex) {
                // ... [代码部分省略以简化]
            }
            finally {
                // ... [代码部分省略以简化]
            }

            // ... [代码部分省略以简化]
        }

        // 返回单例对象
        return singletonObject;
    }
}

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()方法中,主要的逻辑是调用 doCreateBean,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    throws BeanCreationException {
    
    // ... [代码部分省略以简化]
    
    try {
        // 正常的bean实例化、属性注入和初始化。
        // 这里是真正进行bean创建的部分。
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        // 记录bean成功创建的日志
        if (logger.isTraceEnabled()) {
            logger.trace("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
    catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
        // ... [代码部分省略以简化]
    }
    catch (Throwable ex) {
        // ... [代码部分省略以简化]
    }
}

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法中,initializeBean方法是bean初始化,确保bean是完全配置和准备好的。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {

    // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象
    Object exposedObject = bean;

    // ... [代码部分省略以简化]
    
    try {
        // ... [代码部分省略以简化]
        
        // bean初始化
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    } 
    catch (Throwable ex) {
        // ... [代码部分省略以简化]
    }

    // 返回创建和初始化后的bean
    return exposedObject;
}

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean方法中,invokeAwareMethods(beanName, bean)是一个非常重要的步骤。这个方法是为了处理实现了Spring的Aware接口族的Beans(例如BeanNameAware, BeanFactoryAware等)。如果提供的bean实现了任何这些接口,该方法会回调相应的Aware方法。

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {

    // ... [代码部分省略以简化]
    
    invokeAwareMethods(beanName, bean);
    
    // ... [代码部分省略以简化]

    return wrappedBean;
}

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods方法中,用于处理实现了 Spring Aware 接口族的 beans。当一个 bean 实现了如 BeanNameAwareBeanClassLoaderAwareBeanFactoryAware 等接口时,此方法确保正确的回调方法被调用,从而为 bean 提供关于其运行环境或其他相关信息。

private void invokeAwareMethods(String beanName, Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        if (bean instanceof BeanClassLoaderAware) {
            // ... [代码部分省略以简化]
        }
        if (bean instanceof BeanFactoryAware) {
            // ... [代码部分省略以简化]
        }
    }
}

最后执行到我们自定义的逻辑中,我们将这个名称存储在 beanName 变量中,以便后续使用。

public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean {

    private String beanName;

    @Override
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Module " + beanName + " has been registered.");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("Module " + beanName + " has been unregistered.");
    }
}

八、注意事项

  1. 与其他生命周期方法的顺序

    • setBeanName 方法的调用是在其他许多生命周期方法之前的,例如 InitializingBean#afterPropertiesSet 和任何定义的初始化方法。因此,我们不应该在 setBeanName 方法内部预期其他配置或初始化逻辑已经完成。
  2. 仅在容器管理的 Beans 中有效

    • 只有当 bean 是由 Spring 容器管理时,BeanNameAware 才会生效。简单地创建一个类的实例(例如通过 new 关键字)并不会触发 BeanNameAware 功能。
  3. 与其他 Aware 接口的组合使用

    • 当一个 bean 同时实现多个 Aware 接口时,需要注意它们的调用顺序。例如,BeanNameAwareBeanFactoryAwareApplicationContextAware 的回调方法调用顺序是固定的。
  4. Bean 名称的唯一性

    • Spring 容器内的 bean 名称是唯一的,但如果使用别名,同一个 bean 可能会有多个名称。当实现 BeanNameAware 时,我们获得的是 bean 的主要名称。

九、总结

最佳实践总结
  1. 启动及配置

    • 我们使用了 AnnotationConfigApplicationContext 作为 Spring 容器的入口,专门为基于 Java 的配置设计。该容器被初始化并加载了 MyConfiguration 类,它定义了应用的主要配置。
  2. 组件扫描

    • 通过在 MyConfiguration 类中使用 @ComponentScan 注解,我们告诉 Spring 容器去扫描 “com.xcs.spring.service” 包及其子包,以找到和管理 Beans。
  3. 生命周期管理

    • MyBasePayService类展示了如何利用 Spring 的特殊接口,例如 BeanNameAwareInitializingBeanDisposableBean,来插入到 Bean 的生命周期的特定阶段。当一个 Bean 实例被创建并管理 by Spring, 它会被赋予一个名称(通过 BeanNameAware)、在所有属性设置后初始化(通过 InitializingBean)以及在应用结束或 Bean 被销毁时执行特定操作(通过 DisposableBean)。
  4. 具体的服务实现

    • 有两个具体的支付服务,MyAliPayServiceMyWeChatPayService,它们都继承了 MyBasePayService。这意味着它们都自动继承了上述的生命周期管理功能。当 Spring 容器启动时,这两个服务的相关生命周期方法会被调用,如我们从打印的消息中所看到的。
  5. 实际效果

    • 当应用运行时,每个服务类都会打印出其已经被注册和注销的消息,这是由于它们都继承了 MyBasePayService 中定义的生命周期方法。
源码分析总结
  1. 启动和上下文初始化

    • 使用AnnotationConfigApplicationContext初始化Spring容器,其中传递了配置类MyConfiguration
  2. 注册和刷新上下文

    • AnnotationConfigApplicationContext构造函数中,register()方法注册配置类,而refresh()方法开始加载和初始化beans。
  3. 开始bean的实例化

    • refresh()方法进一步调用了finishBeanFactoryInitialization(beanFactory),该方法负责预先实例化所有非懒加载的单例bean。
  4. 实例化单例bean

    • preInstantiateSingletons()方法遍历所有bean名称,并通过调用getBean(beanName)来实例化和初始化bean。
  5. 创建bean实例

    • doGetBean()是实际进行bean创建的核心方法,它处理了bean的实例化、依赖注入和初始化等逻辑。
  6. 处理Aware接口族

    • 在bean的初始化过程中,invokeAwareMethods(beanName, bean)被调用,负责处理实现了Aware接口族的beans。这是我们的BeanNameAware接口发挥作用的地方,当bean实现此接口时,其setBeanName方法会被调用。
  7. 用户定义的逻辑

    • MyBasePayService类中,我们实现了BeanNameAware接口,并重写了setBeanName方法来保存bean的名称。此外,还使用了InitializingBeanDisposableBean接口来在bean的生命周期的特定时刻执行自定义的逻辑。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BeanNameAware 是一个 Spring 框架中的接口,可以让一个 Bean 对象获取到自己在容器中的唯一标识(即 Bean 的名称)。在 Spring 容器中,每一个 Bean 都有一个唯一的标识符,这个标识符可以是在配置文件中定义的 Bean 名称,也可以是自动生成的。当一个 Bean 实现了 BeanNameAware 接口时,Spring 容器实例化该 Bean 后会自动将该 Bean 的名称传递给它,通过这种方式,Bean 对象就可以获取到自己在 Spring 容器中的名称,从而可以在运行时根据名称来获取其他 Bean 的引用。 BeanNameAware 接口只包含一个方法: ```java void setBeanName(String name) throws BeansException; ``` 当 Spring 容器实例化 Bean 后,会自动调用 setBeanName 方法并将该 Bean 的名称作为参数传递给它。实现该接口的 Bean 可以利用该方法获取到自己在 Spring 容器中的名称,并进行相关操作。通常情况下,我们可以在 setBeanName 方法中将 Bean 的名称存储到实例变量中,以便在需要时使用。例如: ```java public class MyBean implements BeanNameAware { private String beanName; @Override public void setBeanName(String name) { this.beanName = name; } public String getBeanName() { return this.beanName; } } ``` 在上面的例子中,MyBean 类实现了 BeanNameAware 接口,并在 setBeanName 方法中将 Bean 的名称存储到实例变量 beanName 中。通过 getBeanName 方法,我们可以在需要时获取该 Bean 在容器中的名称。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值