Spring源码阅读-IOC容器初始化过程

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Spring IOC容器的初始化过程:Resource定位,BeanDefinition载入,向IOC容器注册BeanDefinition。整个过程由refresh()方法触发,三个过程由不同的模块完成,使用户更加灵活的对这三个过程剪裁和扩展。

Spring IOC容器的初始化过程:Resource定位,BeanDefinition载入,向IOC容器注册BeanDefinition。整个过程由refresh()方法触发,三个过程由不同的模块完成,使用户更加灵活的对这三个过程剪裁和扩展。


BeanDefinition 就是POJO对象在IOC容器中的抽象。通过BeanDefinition 这个数据结构,使IOC容器能够方便的对POJO对象也就是Bean进行管理。将BeanDefinition 注册到hashmap中,也就是一个IOC容器中,通过hashmap持有这些BeanDefinition结构。就好比我们对物品整理归类,苹果,桃子,草莓都是水果,可以归到水果类,然后对水果进行统一的处理,比如放到冰箱保鲜。这里BeanDefinition就是苹果或桃子或李子,容器就是冰箱。


IOC容器的初始化过程不包括Bean依赖注入。Bean定义的载入和依赖注入是两个独立的过程。依赖注入一般发生在应用第一次通过getBean向容器索取Bean的时候。(配置lazyinit属性除外)


具体过程(以FileSystemXmlApplicationContext为例):



Resource定位:

指BeanDefinition资源定位。它由ResourceLoader通过统一的Resource接口完成,这个Resource对各种形式的BeanDefinition的使用提供了统一接口。在文件系统中的BeanDefinition可以用FileSystemResource抽象;在类路径中的BeanDefinition可以用ClassPathResource来抽象。Resource定位就是找水果的过程,水果可能在树上,也可能在地里长着。


BeanDefinition载入:

指把用户定义好的Bean表示成IOC容器的数据结构,这个数据结构就是BeanDefinition。



注册:

指向IOC容器注册这些BeanDefinition的过程。通俗的讲就是将BeanDefinition注入到一个hashmap中。



以FileSystemXmlApplicationContext为例来看下IOC容器初始化的过程。

我们先看下FileSystemXmlApplicationContext继承关系:



02055ddcbd5bc96297b0c47b030a80f4.png




FileSystemXmlApplicationContext源码:

package org.springframework.context.support;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
/**
 * FileSystemXmlApplicationContext是一个支持xml定义BeanDefinition的ApplicationContext,
 * 并且可以指定以文件形式的BeanDefinition的读入,这些文件可以使用文件路径和URL定义来表示。
 * 在测试环境和独立应用环境中,这个ApplicationContext非常有用。
 */
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
    public FileSystemXmlApplicationContext() {
    }
    public FileSystemXmlApplicationContext(ApplicationContext parent) {
        super(parent);
    }
    /**
     * 从给定的xml文件中加载Bean定义,创建一个FileSystemXmlApplicationContext,并自动刷新上下文
     * configLocation包含的是BeanDefinition所在的文件路径 
     */
    public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }
    public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, null);
    }
    /**
     * 允许configLocation包含多个BeanDefinition的文件路径的同时,还允许指定自己的双亲IOC容器
     */
    public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
        this(configLocations, true, parent);
    }
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
        this(configLocations, refresh, null);
    }
    /**
     * 在对象的初始化过程中,调用refresh函数载入BeanDefinition,
     * 这个refresh()启动了BeanDefinition的载入过程
     */
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {
        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }
    /**
     * 通过构造一个FileSystemResource来得到一个在文件系统中定位的BeanDefinition
     */
    @Override
    protected Resource getResourceByPath(String path) {
        if (path != null && path.startsWith("/")) {
            path = path.substring(1);
        }
        return new FileSystemResource(path);
    }
}



从前面的类图中我们可以看出,FileSystemXmlApplicationContext已经通过继承AbstractApplicationContext具备了ResourceLoader读入以Resource定义的BeanDefinition的能力。因为AbstractApplicationContext的基类是DefaultResourceLoader。


BeanDefinitionReader

在介绍IOC容器初始化过程之前,我们先了解一个类:BeanDefinition读取器-BeanDefinitionReader。BeanDefinitionReader利用ResouceLoader从指定位置加载Bean定义,将物理资源转化生成Resource。


a2e1b76ebe9fba3d20540b8f3107eb98.png



以编程的方式使用DefaultListableBeanFactory时,我们定义一个Resource来定位容器使用的BeanDefinition:ClassPathResource res = new ClassPathResource("beans.xml");


这里定义的Resource并不能由DefaultListableBeanFactory直接使用,而是通过BeanDefinitionReader来对这些信息进行处理。Spring为我们提供了一系列加载不同Resource的读取器的实现,使不同的容器可以配置相应的BeanDefinitionReader。比如FileSystemXmlApplicationContext的父类AbstractXmlApplicationContext用的读取器是XmlBeanDefinitionReader类型。



AbstractXmlApplicationContext

1.  
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }
    // 用给定的XmlBeanDefinitionReader加载BeanDefinition
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }



推荐阅读:SpringIOC源码解析(4)—— Resource、ResourceLoader、容器之间的微妙关系


下面我们进入正题,看一下FileSystemXmlApplicationContext的初始化流程:


FileSystemXmlApplicationContext的初始化流程由父类AbstractApplicationContext的refresh()方法触发。


AbstractApplicationContext类refresh()方法:

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();
            // 子类启动refreshBeanFactory()
            // FileSystemXmlApplicationContext 是AbstractApplicationContext的子类
            // 在这一步执行refreshBeanFactory
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // 准备用于此上下文的beanFactory
            prepareBeanFactory(beanFactory);
            try {
                // 设置beanFactory的后置处理
                postProcessBeanFactory(beanFactory);
                // 在上下文中调用注册为bean的后置处理器。
                invokeBeanFactoryPostProcessors(beanFactory);
                // 注册拦截bean创建的bean处理器。
                registerBeanPostProcessors(beanFactory);
                // 初始化上下文中的消息源
                initMessageSource();
                // 为上下文初始化事件多播机制
                initApplicationEventMulticaster();
                // 初始化其他特殊的Bean
                onRefresh();
                // 检查监听的Bean并注册他们到容器中
                registerListeners();
                // 实例化所有剩余的(非懒加载)的单例
                finishBeanFactoryInitialization(beanFactory);
                // 最后:发布容器事件
                finishRefresh();
            }
            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }
                // 为避免资源占用销毁在前面创建的单例
                destroyBeans();
                // 重置Reset 'active' 表示
                cancelRefresh(ex);
                // 向调用方抛出异常
                throw ex;
            }
            finally {
                // 重置Spring核心中的常见缺省缓存,因为我们可能不再需要单例bean的元数据。。。
                resetCommonCaches();
            }
        }
    }


先看一张流程图(为了突出关键步骤,只画了关键类,省略了参数):



网络异常,图片无法展示
|

FileSystemResource定位



image.png



结合流程图可以看到,最后getResourceByPath()方法会被子类FileSystemXmlApplicationContext实现,返回一个FileSystemResource对象,完成BeanDefinition的定位。Spring通过FileSystemResource对象,可以进行相关的IO操作。


定位完成后,就可以用返回的Resource对象来进行BeanDefinition的载入了。


BeanDefinition载入



image.png


所谓BeanDefinition载入,就是把xml文件中定义的Bean定义,进过转换和层层解析,根据解析结果,将属性值封装成Property value对象设置到BeanDefinition中,处理成IOC容器中的数据结构。


入口在BeanDefinitionParserDelegate类的parseBeanDefinitionElement(Element ele, BeanDefinition containingBean)方法。


核心处理方法是:


parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)


我们重点看下:

// 解析bean定义本身,不考虑名称或别名。如果在解析bean定义期间出现问题可能返回null
// 只是把Bean的属性设置载入到BeanDefinition,不涉及对象的实例化过程。
// 对象的实例化在依赖注入时完成
public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, BeanDefinition containingBean) {
        this.parseState.push(new BeanEntry(beanName));
        String className = null;
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }
        try {
            String parent = null;
            if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                parent = ele.getAttribute(PARENT_ATTRIBUTE);
            }
            // 生成需要的BeanDefinition对象,为Bean定义信息的载入做准备
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            // 对当前的Bean元素进行属性解析,并设置description信息
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
            // 对各种<bean>元素的信息进行解析
            parseMetaElements(ele, bd);
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            // 解析<bean>的构造函数设置
            parseConstructorArgElements(ele, bd);
            // 解析<bean>的property设置
            parsePropertyElements(ele, bd);
            parseQualifierElements(ele, bd);
            bd.setResource(this.readerContext.getResource());
            bd.setSource(extractSource(ele));
            return bd;
        }
        // 这里是我们经常见到的异常处理(原来是在载入BeanDefinition时处理的)
        catch (ClassNotFoundException ex) {
            error("Bean class [" + className + "] not found", ele, ex);
        }
        catch (NoClassDefFoundError err) {
            error("Class that bean class [" + className + "] depends on not found", ele, err);
        }
        catch (Throwable ex) {
            error("Unexpected failure during bean definition parsing", ele, ex);
        }
        finally {
            this.parseState.pop();
        }
        return null;
    }



BeanDefinitionParserDelegate类:


解析<bean>的property设置,对property子元素进行解析,Array、List、Set、Map、prop等各种元素都会在这里解析,生成对应的数据对象。比如MangedList、ManagedArray、ManageSet等,是Spring对具体的BeanDefinition的数据封装。

    /**
     * 分析给定元素子元素属性
     */
    public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
        NodeList nl = beanEle.getChildNodes();
        // 遍历处理所以的子元素
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
                parsePropertyElement((Element) node, bd);
            }
        }
    }
public void parsePropertyElement(Element ele, BeanDefinition bd) {
        // 取得property的名字
        String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
        if (!StringUtils.hasLength(propertyName)) {
            error("Tag 'property' must have a 'name' attribute", ele);
            return;
        }
        this.parseState.push(new PropertyEntry(propertyName));
        try {
           // 如果同一个Bean中已经有同名的property存在,则不进行解析,直接返回。
            if (bd.getPropertyValues().contains(propertyName)) {
                error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
                return;
            }
            Object val = parsePropertyValue(ele, bd, propertyName);
            PropertyValue pv = new PropertyValue(propertyName, val);
            parseMetaElements(ele, pv);
            pv.setSource(extractSource(ele));
            bd.getPropertyValues().addPropertyValue(pv);
        }
        finally {
            this.parseState.pop();
        }
    }
/**
* 取得每个property的值,也许是一个list或其他
*/
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
        String elementName = (propertyName != null) ?
                        "<property> element for property '" + propertyName + "'" :
                        "<constructor-arg> element";
        // 应该只有一个子节点: 引用类型, 值类型, 集合等.
        NodeList nl = ele.getChildNodes();
        Element subElement = null;
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
                    !nodeNameEquals(node, META_ELEMENT)) {
                // Child element is what we're looking for.
                if (subElement != null) {
                    error(elementName + " must not contain more than one sub-element", ele);
                }
                else {
                    subElement = (Element) node;
                }
            }
        }
        // 这里判断property的属性,是ref还是value。不允许同时是ref和value
        boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
        boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
        if ((hasRefAttribute && hasValueAttribute) ||
                ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
            error(elementName +
                    " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
        }
        // 如果是ref,创建一个RuntimeBeanReference类型的对象,封装ref的信息
        if (hasRefAttribute) {
            String refName = ele.getAttribute(REF_ATTRIBUTE);
            if (!StringUtils.hasText(refName)) {
                error(elementName + " contains empty 'ref' attribute", ele);
            }
            RuntimeBeanReference ref = new RuntimeBeanReference(refName);
            ref.setSource(extractSource(ele));
            return ref;
        }
        // 如果是value,创建一个TypedStringValue类型的对象,这个对象封装了value的信息
        else if (hasValueAttribute) {
            TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
            valueHolder.setSource(extractSource(ele));
            return valueHolder;
        }
        // 如果还有其他子元素,触发对子元素的解析
        else if (subElement != null) {
            return parsePropertySubElement(subElement, bd);
        }
        else {
            // Neither child element nor "ref" or "value" attribute found.
            error(elementName + " must specify a ref or value", ele);
            return null;
        }
    }


/**
     * 分析属性或构造函数arg元素的值、ref或集合子元素。
     * @param ele 属性元素的子元素;我们还不知道是哪个
     * @param defaultValueType 任何的默认类型(类名)
     * {@code <value>} 可能创建的标记
     */
    public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
        if (!isDefaultNamespace(ele)) {
            return parseNestedCustomElement(ele, bd);
        }
        // 节点是Bean
        else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
            BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
            if (nestedBd != null) {
                nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
            }
            return nestedBd;
        }
        // 节点是引用类型
        else if (nodeNameEquals(ele, REF_ELEMENT)) {
            // 对任何bean的任何名称的通用引用。
            String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
            boolean toParent = false;
            if (!StringUtils.hasLength(refName)) {
                // 对同一XML文件中另一个bean的id的引用。
                refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
                if (!StringUtils.hasLength(refName)) {
                    // 对父上下文中另一个bean的id的引用。
                    refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
                    toParent = true;
                    if (!StringUtils.hasLength(refName)) {
                        error("'bean', 'local' or 'parent' is required for <ref> element", ele);
                        return null;
                    }
                }
            }
            if (!StringUtils.hasText(refName)) {
                error("<ref> element contains empty target attribute", ele);
                return null;
            }
            RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
            ref.setSource(extractSource(ele));
            return ref;
        }
        // 节点是idref类型
        else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
            return parseIdRefElement(ele);
        }
        // 节点是value
        else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
            return parseValueElement(ele, defaultValueType);
        }
        // 节点是null
        else if (nodeNameEquals(ele, NULL_ELEMENT)) {
            // 对象是null,创建一个TypedStringValue类型的对象为其保留地址空间
            TypedStringValue nullHolder = new TypedStringValue(null);
            nullHolder.setSource(extractSource(ele));
            return nullHolder;
        }
        // 节点是数组类型
        else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
            return parseArrayElement(ele, bd);
        }
        // 节点是List集合类型
        else if (nodeNameEquals(ele, LIST_ELEMENT)) {
            return parseListElement(ele, bd);
        }
        // 节点是Set集合类型
        else if (nodeNameEquals(ele, SET_ELEMENT)) {
            return parseSetElement(ele, bd);
        }
        // 节点是Map类型
        else if (nodeNameEquals(ele, MAP_ELEMENT)) {
            return parseMapElement(ele, bd);
        }
        // 节点是props类型
        else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
            return parsePropsElement(ele);
        }
        else {
            error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
            return null;
        }
    }


我们看一下List这样的属性配置是如何被解析的:

// 返回ManagedList类型的对象
public List<Object> parseListElement(Element collectionEle, BeanDefinition bd) {
        String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
        NodeList nl = collectionEle.getChildNodes();
        ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
        target.setSource(extractSource(collectionEle));
        target.setElementTypeName(defaultElementType);
        target.setMergeEnabled(parseMergeAttribute(collectionEle));
        // 具体的List元素解析过程
        parseCollectionElements(nl, target, bd, defaultElementType);
        return target;
    }
    protected void parseCollectionElements(
            NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {
        for (int i = 0; i < elementNodes.getLength(); i++) {
            Node node = elementNodes.item(i);
            // node需要是element类型
            if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
                // 将node添加到taget中,并递归解析下一层子元素 
                target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
            }
        }
    }


经过这样层层解析,我们就将xml文件中定义的Definition加载到了IOC容器中,并在容器中建立了映射,完成了IOC容器管理Bean对象的准备工作。这些数据结构可以以AbstractBeanDefinition为入口,让IOC容器执行索引、查询和操作。你没有看错,这一小节,经过了这么多的处理,就是对xml文件解析,把内容处理成BeanDefinition。看迷茫的同学可以结合类图,往回倒一下处理的入口。


这时候IOC容器BeanDefinition中存在的还只是一些静态的配置信息,要想让容器起作用,还需完成数据向容器的注册


BeanDefinition在IOC容器中的注册


image.png



我们回到DefaultBeanDefinitionDocumentReader类

    /**
     * 处理给定的bean元素,解析bean定义并将其注册到注册表中。
     */
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        // BeanDefinition载入
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // 注册实例
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }



到此,完成了BeanDefinition的注册,完成了IOC容器的初始化过程。此时使用的IOC容器DefaultListableBeanFactory中已经建立了整个Bean的配置信息,而且这些BeanDefinition已经可以被容器使用了,并且可以在BeanDefinitionMap中被检索到和使用。通过容器对这些Bean信息进行处理和维护


相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
71 2
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
18天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
38 2
|
1月前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
1月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
63 9
|
1月前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
1月前
|
前端开发 Java Docker
使用Docker容器化部署Spring Boot应用程序
使用Docker容器化部署Spring Boot应用程序
|
1月前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
49 0
|
1月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
39 0
|
12天前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
142 77
下一篇
DataWorks