关于Spring Bean创建的一些问题

最近看到了逻辑大概像下面这样的代码:

import org.springframework.stereotype.Component;
import vip.mycollege.spring.aware.ApplicationHolder;

@Component
public class DataComponent {

    private static DataComponent instance;

    public static DataComponent getInstance() {
        if (instance == null) {
            createInstance();
        }
        return instance;
    }


    private static synchronized void createInstance() {
        if (instance != null) {
            return;
        }
        instance = ApplicationHolder.getBean(DataComponent.class);
    }

    public void initData() {
        System.out.println("init data");
    }

    public void destroy() {
        System.out.println("destroy...");
    }
}

其中ApplicationHolder获取bean是通过Spring的ApplicationContext。

看着是不是有点眼熟,是不是像创建类的单例模式,还是使用双重检查方式。

你能看出哪些问题?

最重要的问题是:从注解和代码逻辑我们可以看出,这个Bean是通过Spring管理的。

Spring创建Bean它本身默认就是单例,就是使用@Lazy懒加载也一样,除非使用@Scope(“prototype”)。

所以完全没有必要考虑是否为空的问题,直接使用ApplicationContext.getBean(DataComponent.class)就可以,如果为null,Spring自己会去创建,除非Bean不存在,可以通过ApplicationContextAware注入。

Spring创建Bean已经考虑了线程安全问题,下面是DefaultSingletonBeanRegistry通过三级缓存获取Bean的逻辑。

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Quick check for existing instance without full singleton lock
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) {
                // Consistent creation of early reference within full singleton lock
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

计算使用Spring管理Bean了,并且使用注解方式,那么初始化方法、销毁方法就加上注解,不要手动去调用。我之所以发现这个问题就是因为,他们没有调用初始化方法,报空指针才注意到。

我们对这个类稍微改造一下:

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class DataComponent implements ApplicationContextAware {

    private static ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        DataComponent.applicationContext = applicationContext;
    }

    public static DataComponent getInstance() {
        return applicationContext.getBean(DataComponent.class);
    }

    @PostConstruct
    public void initData() {
        System.out.println("init data");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("destroy...");
    }
}

@PostConstuct方法执行是在afterPropertiesSet之前,下面是相关初始化方法大致先后顺序:

  1. @PostConstuct方法
  2. afterPropertiesSet
  3. init-method

如果,对这些还不太清楚可以看一下下面两篇文章:

SpringBean生命周期
Spring核心流程梳理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值