Spring-源码深入分析(一)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Spring-源码深入分析(一)

重写下Spring-源码分析:

一、首先看下全局图:

  1、首先原材料进入spring工厂,原材料也可以说成是资源,我们可以称之为set方法

  2、然后从spring工厂获得到目标,称之为get方法

  3、而我们的目标全是依赖原材料。

所以就是想要得到某个目标的话,必须要失去某个东西,这也是社会上必然的规律。

那么今天分析下Spring是如何将原材料转换成目标的?我们应该带着问题去学习源码:

二、定个总目标:

从原材料中获取到某个东西,get到对象的实例,如何让Spring工厂帮我们产生对象实例?

1、然后期间分成多个小目标,最后组合起来形成总目标,spring也是这样做的

2、然后看这个目标从哪个母体里面产生出来的,这个概念是非常重要的,也就是说想要得到某个目标,必须要有它的母体,没有母体,目标是无法达到的,母体就是主要依据。

3、我们理解的工厂模式:主要是用来get东西的,spring也是这样的,只不过,spring对它做了非常大的扩展,那么扩展依据又是什么呢?

三、看下spring-beans的体系结构分析图:

在我们java中解决问题,是用接口来解决问题的,接口就是目标和母体之间的桥梁,那么我们抽象为接口,那么接口中的某个方法就是实现目标的某个步骤

总目标的入口

①、首先看下BeanFactory接口:获取基本对象的接口,传个类型,别名,名称进去这个接口是解决总目标的抽象接口,我们要getBean,要get对象实例,我们要有个接口,就可以通过工厂给返回结果。命名为BeanFactory,也是工厂的设计模式,但是它的扩展方式是非常的严格的,这个问题是由具体的场合来解决,在spring中一种是xml,一种是注解,还有一种是外部的spring环境帮我们解决,还有一种是springBoot的环境。

②、原材料就是定义的类,原材料如果放到xml里面,就是xml的原材料,如果放到注解里面,就是注解里面的原材料,但是核心不变,就是类。

③、类有多态,所以接口ListableBeanFactory就是解决多个对象实例问题的。对象有父类,HierarchicalBeanFactory解决继承的问题,解决能够创建一个父工厂,这三个接口是解决获取实例的方法,称之为get。

④、ConfigurableBeanFactory:解决内部依赖配置问题的,就是一些内部组件的配置,所以它是set的操作,set某个资源进去

⑤、AutowireCapableBeanFactory:具有创建对象的能力

然后用ConfigurableListableBeanFactory这个接口把上面的所有的功能的接口进行汇总,由它给暴漏功能。

这些接口解决对象关系创建的问题的,但是还没有和具体的环境联系起来,联系起来是从Bean定义开始的。


四、子目标要依赖总目标的实现

由于一个框架是解决一类问题的,无法预知有多少种情况,我们需要给它规范下,给它统一的一个名称,在spring中就统一定义为BeanDefinition接口,只是原材料加工时产生的对象。

由BeanWrapper接口代替所有具体的类的实例,而我们看到的是Bean标签,从具体到抽象,由接口作为抽象,也可以用抽象类,但是一般用接口来代替

五、那么我么如何获取到BeanWrapper接口呢?我们如何去获取到原材料,原材料在哪里?

①、我们要获取一个对象的实例,我们如何获取类,我们只有得到具体的class,我们才能够new对象。我们现在只知道了实例了,那么我们如何去获取到BeanWrapper对象呢?

②、现在不知道原材料在哪里,我知道Class是我的原材料,现在不能从类里面获取,必须提供统一的入口,那么这个入口是什么呢?一种是用xml来装我们的原材料,第二个是注解

③、以xml为例,xml文件将所有的Class对象全部封装在xml文件里面,只要我们配置了定义的类,也就是原材料,我们就可以从工厂里面获取到BeanWrapper接口,然后进而返回具体的实例。

④、在spring中将Class对象和Bean定义对象进行分开了,但是放在统一的入口里面,只是出口是不同的对象

⑤、原材料,就是定义的类,我们才能够new对象,而类的属性会有基本数据类型,会有复杂的数据类型,还会有一个数据结构,比如下面的原材料:

  1. package com.weizhaoyang.spring5;

  2. import java.util.List;

  3. public class App {
  4.    private App app;
  5.    private Integer id;
  6.    private List<Object> d;
  7. }

⑥、在spring中用BeanDefinition接口代替所有的类,用一个PropertyValue对象代替所有的属性, 类的属性是很多的,在spring中用PropertyValues对象代替很多的属性。这三个抽象也是对xml文件中bean的一个抽象。类是定义在xml文件里面的,在依赖注入的时候,就是对类的属性赋值。属性的复杂类型用ref来表示,字符串用value来表示值。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3.       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.       xsi:schemaLocation="http://www.springframework.org/schema/beans
  5.           http://www.springframework.org/schema/beans/spring-beans.xsd">
  6.    <bean id="xx"  class="com.weizhaoyang.spring5.App"   name="">
  7.        <property name="id" ref="app" value="123"></property>
  8.    </bean>
  9. </beans>

⑦、如何将xml文件中的字符串的值转换成java认识的对象呢?

这时在spring中抽象出BeanDefinitionValueResolver对象:就是将PropertyValue封装的字面量进行转换成执行的类型。

上面的对象只能解析成java认识的String类型,如果java的类的属性是int类型的话,那么如何将xml文件中的字符串的类型转成int类型呢?在Spring中抽象成TypeConverter接口将PropertyValue解析后的属性值转换成指定类型,使用PropertyEdit编辑装换成指定值。

总结:BeanDefinition接口和PropertyBean对象和PropertyValues对象是对xml文件类的封装 ,这三个就是进行set的操作。BeanDefinitionValueResolver对象和TypeConverter接口BeanWrapper接口就是获得对象的操作

六、我们把bean放到了xml文件,那么如何获取到xml文件呢?如何获取到BeanDefinition接口的?上面的只是现象 ,那么如何去产生这个现象呢?

1、xml是母体

2、读取xml,解析xml,封装成BeanDefinition接口和PropertyBean对象和PropertyValues对象,然后再保存对象。又定一个小目标:如何获取到BeanDefinition接口:

我们主要看的是beans的jar包,spring-core主要提供的是spring-beans的基础的功能而已

core里面主要提供两个入口:1、提供基本的bean定义,就是bean策略。

instantiator:提供 bean实例化的功能。lang:是java语言的功能

core:里面有几个是核心的:一个是编码和解码

一个是annotation,将类上的所有的注解做了一个封装

convet:针对类型转换的封装

env:是环境的封装,就是profile,用springBoot的时候用的最多,有测试环境,开发环境,生产环境


io:比如现在的xml文件放到了本地,它也有可能放到远程,io就是将资源进行全部的抽象,里面重要的是Resource和ResourceLoader很重要。


log:对日志的封装。


serializer:对序列化的封装。


style:是对打印日志样式的封装。

task:是对同步和异步线程的封装,里面继承了executor线程池的接口。

name:可以定义别名,别名就是注册在AliasRegistry接口里面,提供了基本的功能,没有具体的场景,所以bean定义接口就继承了这个AliasRegistry接口


总结:spring将java中的对象全部做了一个封装,这也就是spring为什么这么庞大,全部抽象出它自定义的类,比如自己定义的类,方法,属性,它全给进行了封装,这才是面向对象的特点。比如:将属性进行一个包装,代替所有的场合,这是面向对象的思维。

七、如何获取到BeanDefinition接口?

1、写个对于总目标的总入口:resource封装为核心对象,下面的步骤就是set的过程

  1. public class Main {
  2.    public static void main(String[] args) {
  3.        //统一的入口加载资源类
  4.        //set过程
  5.        ResourceLoader  resourceLoader=new DefaultResourceLoader();
  6.        Resource resource = resourceLoader.getResource("spring-mvc.xml");
  7.        BeanFactory  beanFactory=new XmlBeanFactory(resource);
  8.        //get过程
  9.        Object app = beanFactory.getBean("app");
  10.    }
  11. }

2、那么如何得到BeanDefinition对象呢?先从总目标XmlBeanFactory开始看

  1. @Deprecated
  2. @SuppressWarnings({"serial", "all"})
  3. public class XmlBeanFactory extends DefaultListableBeanFactory {

  4.    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


  5.    /**
  6.     * Create a new XmlBeanFactory with the given resource,
  7.     * which must be parsable using DOM.
  8.     * @param resource the XML resource to load bean definitions from
  9.     * @throws BeansException in case of loading or parsing errors
  10.     */
  11.    public XmlBeanFactory(Resource resource) throws BeansException {
  12.        this(resource, null);
  13.    }

  14.    /**
  15.     * Create a new XmlBeanFactory with the given input stream,
  16.     * which must be parsable using DOM.
  17.     * @param resource the XML resource to load bean definitions from
  18.     * @param parentBeanFactory parent bean factory
  19.     * @throws BeansException in case of loading or parsing errors
  20.     */
  21.    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
  22.        super(parentBeanFactory);
  23.        this.reader.loadBeanDefinitions(resource);
  24.    }

  25. }

在上面的类中,首先resource是对资源的抽象,然后把它放入到加载bean定义的方法 reader.loadBeanDefinitions(resource):代表从文档里面加载。总目标是给外部调用的接口,给内部调用的都是子目标。

2、然后到了XmlBeanDefinitionReader类:首先对资源进行编码和解码


  1. @Override
  2.    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  3.        return loadBeanDefinitions(new EncodedResource(resource));
  4.    }
  5.   public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  6.        Assert.notNull(encodedResource, "EncodedResource must not be null");
  7.        if (logger.isTraceEnabled()) {
  8.            logger.trace("Loading XML bean definitions from " + encodedResource);
  9.        }

  10.        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  11.        if (currentResources == null) {
  12.            currentResources = new HashSet<>(4);
  13.            this.resourcesCurrentlyBeingLoaded.set(currentResources);
  14.        }
  15.        if (!currentResources.add(encodedResource)) {
  16.            throw new BeanDefinitionStoreException(
  17.                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  18.        }
  19.        try {
  20.            InputStream inputStream = encodedResource.getResource().getInputStream();
  21.            try {
  22.                InputSource inputSource = new InputSource(inputStream);
  23.                if (encodedResource.getEncoding() != null) {
  24.                    inputSource.setEncoding(encodedResource.getEncoding());
  25.                }
  26.                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  27.            }
  28.            finally {
  29.                inputStream.close();
  30.            }
  31.        }
  32.        catch (IOException ex) {
  33.            throw new BeanDefinitionStoreException(
  34.                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
  35.        }
  36.        finally {
  37.            currentResources.remove(encodedResource);
  38.            if (currentResources.isEmpty()) {
  39.                this.resourcesCurrentlyBeingLoaded.remove();
  40.            }
  41.        }
  42.    }

上面的代码是对资源进行的一些解析,主要获取到InputStream,因为文档转成inputStream,才能够进行读取。

  1.    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  2.            throws BeanDefinitionStoreException {

  3.        try {
  4.            Document doc = doLoadDocument(inputSource, resource);
  5.            int count = registerBeanDefinitions(doc, resource);
  6.            if (logger.isDebugEnabled()) {
  7.                logger.debug("Loaded " + count + " bean definitions from " + resource);
  8.            }
  9.            return count;
  10.        }
  11.        catch (BeanDefinitionStoreException ex) {
  12.            throw ex;
  13.        }
  14.        catch (SAXParseException ex) {
  15.            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  16.                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
  17.        }
  18.        catch (SAXException ex) {
  19.            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  20.                    "XML document from " + resource + " is invalid", ex);
  21.        }
  22.        catch (ParserConfigurationException ex) {
  23.            throw new BeanDefinitionStoreException(resource.getDescription(),
  24.                    "Parser configuration exception parsing XML from " + resource, ex);
  25.        }
  26.        catch (IOException ex) {
  27.            throw new BeanDefinitionStoreException(resource.getDescription(),
  28.                    "IOException parsing XML document from " + resource, ex);
  29.        }
  30.        catch (Throwable ex) {
  31.            throw new BeanDefinitionStoreException(resource.getDescription(),
  32.                    "Unexpected exception parsing XML document from " + resource, ex);
  33.        }
  34.    }

在转成document对象要知道xml文件有两种xsl,xsd,上面的代码就开始加载Bean定义了,把输入流转成Document对象,这时开始进行模式校验:getValidationModeForResource

  1.    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
  2.        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
  3.                getValidationModeForResource(resource), isNamespaceAware());
  4.    }
  5.    protected int getValidationModeForResource(Resource resource) {
  6.        int validationModeToUse = getValidationMode();
  7.        if (validationModeToUse != VALIDATION_AUTO) {
  8.            return validationModeToUse;
  9.        }
  10.        int detectedMode = detectValidationMode(resource);
  11.        if (detectedMode != VALIDATION_AUTO) {
  12.            return detectedMode;
  13.        }
  14.        // Hmm, we didn't get a clear indication... Let's assume XSD,
  15.        // since apparently no DTD declaration has been found up until
  16.        // detection stopped (before finding the document's root tag).
  17.        return VALIDATION_XSD;
  18.    }

校验之后,返回出去,获取到了document文档,然后开始解析

  1.  int count = registerBeanDefinitions(doc, resource);
  2.   public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  3.        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  4.        int countBefore = getRegistry().getBeanDefinitionCount();
  5.        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  6.        return getRegistry().getBeanDefinitionCount() - countBefore;
  7.    }

BeanDefinitionDocumentReader:是一个类部的类在操作

  1. BeanDefinitionDocumentReader documentReader =createBeanDefinitionDocumentReader();

初始化documentReaderClass,类部使用的

  1. protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
  2.        return BeanUtils.instantiateClass(this.documentReaderClass);
  3.    }

bean的实例化:就是反射实例化的过程


  1.   public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
  2.        Assert.notNull(clazz, "Class must not be null");
  3.        if (clazz.isInterface()) {
  4.            throw new BeanInstantiationException(clazz, "Specified class is an interface");
  5.        }
  6.        try {
  7.            return instantiateClass(clazz.getDeclaredConstructor());
  8.        }
  9.        catch (NoSuchMethodException ex) {
  10.            Constructor<T> ctor = findPrimaryConstructor(clazz);
  11.            if (ctor != null) {
  12.                return instantiateClass(ctor);
  13.            }
  14.            throw new BeanInstantiationException(clazz, "No default constructor found", ex);
  15.        }
  16.        catch (LinkageError err) {
  17.            throw new BeanInstantiationException(clazz, "Unresolvable class definition", err);
  18.        }
  19.    }
  20.    public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
  21.        Assert.notNull(ctor, "Constructor must not be null");
  22.        try {
  23.            ReflectionUtils.makeAccessible(ctor);
  24.            return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
  25.                    KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
  26.        }
  27.        catch (InstantiationException ex) {
  28.            throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
  29.        }
  30.        catch (IllegalAccessException ex) {
  31.            throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
  32.        }
  33.        catch (IllegalArgumentException ex) {
  34.            throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
  35.        }
  36.        catch (InvocationTargetException ex) {
  37.            throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
  38.        }
  39.    }

然后回到XmlBeanDefinitionReader

createReaderContext:需要通过XmlReaderContext接口来解析xml中自定义的标签,除了beans的剩下的都是自定义的。接口:NameSpaceHandler:自定义标签解析器,这个接口在spring里面非常的核心,但是在SpringBoot里面就不核心了。

  1. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  2.    public XmlReaderContext createReaderContext(Resource resource) {
  3.        return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
  4.                this.sourceExtractor, this, getNamespaceHandlerResolver());
  5.    }

回到XmlBeanDefinitionReader类,进行注册BeanDefinitions对象

  1. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));


  1.    @Override
  2.    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  3.        this.readerContext = readerContext;
  4.        doRegisterBeanDefinitions(doc.getDocumentElement());
  5.    }

然后看下XmlReaderContext这个类,里面封装了标签解析器和阅读Bean定义对象


  1.    final XmlBeanDefinitionReader reader;

  2.    private final NamespaceHandlerResolver namespaceHandlerResolver;

然后撤回来:获取完文档,就开始注册Bean定义,参数root:就是xml文件里面的beans标签,但是委托BeanDefintionParserDelegate,类部进行委托的:用这个对象产生Bean定义接口的。


  1. doRegisterBeanDefinitions(doc.getDocumentElement());


  1.     protected void doRegisterBeanDefinitions(Element root) {
  2.        // Any nested <beans> elements will cause recursion in this method. In
  3.        // order to propagate and preserve <beans> default-* attributes correctly,
  4.        // keep track of the current (parent) delegate, which may be null. Create
  5.        // the new (child) delegate with a reference to the parent for fallback purposes,
  6.        // then ultimately reset this.delegate back to its original (parent) reference.
  7.        // this behavior emulates a stack of delegates without actually necessitating one.
  8.        BeanDefinitionParserDelegate parent = this.delegate;
  9.        this.delegate = createDelegate(getReaderContext(), root, parent);

  10.        if (this.delegate.isDefaultNamespace(root)) {
  11.            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  12.            if (StringUtils.hasText(profileSpec)) {
  13.                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  14.                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  15.                // We cannot use Profiles.of(...) since profile expressions are not supported
  16.                // in XML config. See SPR-12458 for details.
  17.                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
  18.                    if (logger.isDebugEnabled()) {
  19.                        logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
  20.                                "] not matching: " + getReaderContext().getResource());
  21.                    }
  22.                    return;
  23.                }
  24.            }
  25.        }

  26.        preProcessXml(root);
  27.        parseBeanDefinitions(root, this.delegate);
  28.        postProcessXml(root);

  29.        this.delegate = parent;
  30.    }
  31.    protected BeanDefinitionParserDelegate createDelegate(
  32.            XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {

  33.        BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
  34.        delegate.initDefaults(root, parentDelegate);
  35.        return delegate;

上面的是创建一个委托的方法,默认的创建了BeanDefinitionParserDelegate,Bean定义的解析委托类,把xml自定义标签的解析器传进来。然后初始化委托,这时委托类就创建成功了。

  1.       BeanDefinitionParserDelegate parent = this.delegate;
  2.        this.delegate = createDelegate(getReaderContext(), root, parent);

  3.        if (this.delegate.isDefaultNamespace(root)) {
  4.            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  5.            if (StringUtils.hasText(profileSpec)) {
  6.                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  7.                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  8.                // We cannot use Profiles.of(...) since profile expressions are not supported
  9.                // in XML config. See SPR-12458 for details.
  10.                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
  11.                    if (logger.isDebugEnabled()) {
  12.                        logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
  13.                                "] not matching: " + getReaderContext().getResource());
  14.                    }
  15.                    return;
  16.                }
  17.            }
  18.        }

上面的方法这时会判断是否是默认的命名空间,然后从默认的命名空间里面去取值。

下面的两个就是默认的命名空间,默认的命名空间只有bean标签

  1. xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

像下面的就是自定义的命名空间

  1.  xmlns:aop="http://www.springframework.org/schema/aop"
  2.  xmlns:context="http://www.springframework.org/schema/context"

在解析xml文件之前和之后都有扩展接口,下面的方法就是开始真正的解析,把委托传进来:

  1. preProcessXml(root);
  2. parseBeanDefinitions(root, this.delegate);
  3. postProcessXml(root);
  4.        protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  5.        if (delegate.isDefaultNamespace(root)) {
  6.            NodeList nl = root.getChildNodes();
  7.            for (int i = 0; i < nl.getLength(); i++) {
  8.                Node node = nl.item(i);
  9.                if (node instanceof Element) {
  10.                    Element ele = (Element) node;
  11.                    if (delegate.isDefaultNamespace(ele)) {
  12.                        parseDefaultElement(ele, delegate);
  13.                    }
  14.                    else {
  15.                        delegate.parseCustomElement(ele);
  16.                    }
  17.                }
  18.            }
  19.        }
  20.        else {
  21.            delegate.parseCustomElement(root);
  22.        }
  23.    }

BeanDefinitionParserDelegate:专门用来解析xml文件里面的bean标签的,一种是默认的命名空间的标签的解析,一种是自定义标签的解析,在默认的的命名空间里面的解析,获取所有的子元素就是所有的bean标签,还有aop,springmvc标签的,然后通过委托类来判断节点是否是默认的命名空间,parseDefaultElement:解析普通的默认的bean标签,如果不是默认的命名空间就走parseCustomElement,解析自定义的标签

  1.    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  2.        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  3.            importBeanDefinitionResource(ele);
  4.        }
  5.        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  6.            processAliasRegistration(ele);
  7.        }
  8.        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  9.            processBeanDefinition(ele, delegate);
  10.        }
  11.        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  12.            // recurse
  13.            doRegisterBeanDefinitions(ele);
  14.        }
  15.    }

上面的是解析默认的命名空间的方法,有四种if-else,就是解析四种标签:例如下面的文件,import,alias,beans,bean,这里一定是同级的,有四个根,别名可以在外部引用,也可以在内部引用,如果name的值一个的话,就和bean的id一样,如果是多个的话,就称之为别名,id称之为bean的name。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3.       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.       xmlns:aop="http://www.springframework.org/schema/aop"
  5.       xmlns:context="http://www.springframework.org/schema/context"
  6.       xsi:schemaLocation="http://www.springframework.org/schema/beans
  7.           http://www.springframework.org/schema/beans/spring-beans.xsd
  8.           http://www.springframework.org/schema/context
  9.           http://www.springframework.org/schema/context/spring-context.xsd">
  10.    <bean id="xx"  class="com.weizhaoyang.spring5.App"   name="app1,app2">
  11.        <property name="id" value="123"></property>
  12.    </bean>
  13.    <import resource=""></import> <!--从外部导入的xml文件-->
  14.    <alias name="" alias=""></alias><!--从外部定义的别名-->
  15.    <beans></beans><!--迭代定义bean-->
  16. </beans>


在委托类里面是定义了bean标签的和bean标签的属性,还有子标签和子标签的属性

  1.    public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";

  2.    public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";

  3.    /**
  4.     * Value of a T/F attribute that represents true.
  5.     * Anything else represents false. Case seNsItive.
  6.     */
  7.    public static final String TRUE_VALUE = "true";

  8.    public static final String FALSE_VALUE = "false";

  9.    public static final String DEFAULT_VALUE = "default";

  10.    public static final String DESCRIPTION_ELEMENT = "description";

  11.    public static final String AUTOWIRE_NO_VALUE = "no";

  12.    public static final String AUTOWIRE_BY_NAME_VALUE = "byName";

  13.    public static final String AUTOWIRE_BY_TYPE_VALUE = "byType";

  14.    public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor";

  15.    public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect";

  16.    public static final String NAME_ATTRIBUTE = "name";

  17.    public static final String BEAN_ELEMENT = "bean";

  18.    public static final String META_ELEMENT = "meta";

  19.    public static final String ID_ATTRIBUTE = "id";

  20.    public static final String PARENT_ATTRIBUTE = "parent";

  21.    public static final String CLASS_ATTRIBUTE = "class";

  22.    public static final String ABSTRACT_ATTRIBUTE = "abstract";

  23.    public static final String SCOPE_ATTRIBUTE = "scope";

  24.    private static final String SINGLETON_ATTRIBUTE = "singleton";

  25.    public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";

  26.    public static final String AUTOWIRE_ATTRIBUTE = "autowire";

  27.    public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";

  28.    public static final String PRIMARY_ATTRIBUTE = "primary";

  29.    public static final String DEPENDS_ON_ATTRIBUTE = "depends-on";

  30.    public static final String INIT_METHOD_ATTRIBUTE = "init-method";

  31.    public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";

  32.    public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method";

  33.    public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";

  34.    public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";

  35.    public static final String INDEX_ATTRIBUTE = "index";

  36.    public static final String TYPE_ATTRIBUTE = "type";

  37.    public static final String VALUE_TYPE_ATTRIBUTE = "value-type";

  38.    public static final String KEY_TYPE_ATTRIBUTE = "key-type";

  39.    public static final String PROPERTY_ELEMENT = "property";

  40.    public static final String REF_ATTRIBUTE = "ref";

  41.    public static final String VALUE_ATTRIBUTE = "value";

  42.    public static final String LOOKUP_METHOD_ELEMENT = "lookup-method";

  43.    public static final String REPLACED_METHOD_ELEMENT = "replaced-method";

  44.    public static final String REPLACER_ATTRIBUTE = "replacer";

  45.    public static final String ARG_TYPE_ELEMENT = "arg-type";

  46.    public static final String ARG_TYPE_MATCH_ATTRIBUTE = "match";

  47.    public static final String REF_ELEMENT = "ref";

  48.    public static final String IDREF_ELEMENT = "idref";

  49.    public static final String BEAN_REF_ATTRIBUTE = "bean";

  50.    public static final String PARENT_REF_ATTRIBUTE = "parent";

  51.    public static final String VALUE_ELEMENT = "value";

  52.    public static final String NULL_ELEMENT = "null";

  53.    public static final String ARRAY_ELEMENT = "array";

  54.    public static final String LIST_ELEMENT = "list";

  55.    public static final String SET_ELEMENT = "set";

  56.    public static final String MAP_ELEMENT = "map";

  57.    public static final String ENTRY_ELEMENT = "entry";

  58.    public static final String KEY_ELEMENT = "key";

  59.    public static final String KEY_ATTRIBUTE = "key";

  60.    public static final String KEY_REF_ATTRIBUTE = "key-ref";

  61.    public static final String VALUE_REF_ATTRIBUTE = "value-ref";

  62.    public static final String PROPS_ELEMENT = "props";

  63.    public static final String PROP_ELEMENT = "prop";

  64.    public static final String MERGE_ATTRIBUTE = "merge";

  65.    public static final String QUALIFIER_ELEMENT = "qualifier";

  66.    public static final String QUALIFIER_ATTRIBUTE_ELEMENT = "attribute";

  67.    public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";

  68.    public static final String DEFAULT_MERGE_ATTRIBUTE = "default-merge";

  69.    public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";

  70.    public static final String DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE = "default-autowire-candidates";

  71.    public static final String DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method";

  72.    public static final String DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method";


  73.    protected final Log logger = LogFactory.getLog(getClass());

  74.    private final XmlReaderContext readerContext;

  75.    private final DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition();

  76.    private final ParseState parseState = new ParseState();

  77.    /**
  78.     * Stores all used bean names so we can enforce uniqueness on a per
  79.     * beans-element basis. Duplicate bean ids/names may not exist within the
  80.     * same level of beans element nesting, but may be duplicated across levels.
  81.     */
  82.    private final Set<String> usedNames = new HashSet<>();
  83.       <bean id="xx"  class="com.weizhaoyang.spring5.App"   name="" parent="" lazy-init="default"
  84.         abstract="true" autowire-candidate="default" autowire="default" depends-on="" destroy-method=""
  85.              factory-bean="" factory-method="" init-method="" primary="true" scope="">
  86.        <property name="id" value="123"/>

bean定义对象就是对上面的属性进行get和set操作

然后开始看解析bean标签的属性:BeanDefinitionHolder:用了装饰器的模式将BeanDefinition对象封装成Bean定义持有者对象,来进行Bean定义注册的。然后通过委托类来解析bean定义的元素

  1. processBeanDefinition(ele, delegate);
  2.  protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  3.        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  4.        if (bdHolder != null) {
  5.            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  6.            try {
  7.                // Register the final decorated instance.
  8.                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  9.            }
  10.            catch (BeanDefinitionStoreException ex) {
  11.                getReaderContext().error("Failed to register bean definition with name '" +
  12.                        bdHolder.getBeanName() + "'", ele, ex);
  13.            }
  14.            // Send registration event.
  15.            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  16.        }
  17.    }

BeanDefinitionHolder:这个类封装了别名name,id,还有一个bean定义对象,通过这三种 元素来实现Bean注册


  1.    private final BeanDefinition beanDefinition;

  2.    private final String beanName;

  3.    @Nullable
  4.    private final String[] aliases;

如何去解析Bean


  1.    @Nullable
  2.    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
  3.        return parseBeanDefinitionElement(ele, null);
  4.    }
  5.    @Nullable
  6.    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
  7.        String id = ele.getAttribute(ID_ATTRIBUTE);
  8.        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

  9.        List<String> aliases = new ArrayList<>();
  10.        if (StringUtils.hasLength(nameAttr)) {
  11.            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  12.            aliases.addAll(Arrays.asList(nameArr));
  13.        }

  14.        String beanName = id;
  15.        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
  16.            beanName = aliases.remove(0);
  17.            if (logger.isTraceEnabled()) {
  18.                logger.trace("No XML 'id' specified - using '" + beanName +
  19.                        "' as bean name and " + aliases + " as aliases");
  20.            }
  21.        }

  22.        if (containingBean == null) {
  23.            checkNameUniqueness(beanName, aliases, ele);
  24.        }

  25.        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
  26.        if (beanDefinition != null) {
  27.            if (!StringUtils.hasText(beanName)) {
  28.                try {
  29.                    if (containingBean != null) {
  30.                        beanName = BeanDefinitionReaderUtils.generateBeanName(
  31.                                beanDefinition, this.readerContext.getRegistry(), true);
  32.                    }
  33.                    else {
  34.                        beanName = this.readerContext.generateBeanName(beanDefinition);
  35.                        // Register an alias for the plain bean class name, if still possible,
  36.                        // if the generator returned the class name plus a suffix.
  37.                        // This is expected for Spring 1.2/2.0 backwards compatibility.
  38.                        String beanClassName = beanDefinition.getBeanClassName();
  39.                        if (beanClassName != null &&
  40.                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
  41.                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
  42.                            aliases.add(beanClassName);
  43.                        }
  44.                    }
  45.                    if (logger.isTraceEnabled()) {
  46.                        logger.trace("Neither XML 'id' nor 'name' specified - " +
  47.                                "using generated bean name [" + beanName + "]");
  48.                    }
  49.                }
  50.                catch (Exception ex) {
  51.                    error(ex.getMessage(), ele);
  52.                    return null;
  53.                }
  54.            }
  55.            String[] aliasesArray = StringUtils.toStringArray(aliases);
  56.            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
  57.        }

  58.        return null;
  59.    }

上面的方法是对bean标签的属性进行完整的解析,参数BeanDefinition默认是null,因为还没有完全解析,首先获取id,name,然后定义一个别名的集合,然后解析逗号,返回一个名称的数组,然后把名称添加到别名的集合中。如果没有逗号的话,就把id的值覆盖beanName

  1.   checkNameUniqueness(beanName, aliases, ele);

上面的方法检查bean名称是否是独立性,定义的id,要映射,要存在map里面,必须要map里面保证它的唯一性。

就是检验是否是已经使用的名称,如果是的话就添加到已经使用的集合里面。

  1.    protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) {
  2.        String foundName = null;

  3.        if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) {
  4.            foundName = beanName;
  5.        }
  6.        if (foundName == null) {
  7.            foundName = CollectionUtils.findFirstMatch(this.usedNames, aliases);
  8.        }
  9.        if (foundName != null) {
  10.            error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement);
  11.        }

  12.        this.usedNames.add(beanName);
  13.        this.usedNames.addAll(aliases);
  14.    }


  1. private final Set<String> usedNames = new HashSet<>();

解析完成之后,bean定义对象开始创建:

  1.   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

看下这个类,实现了BeanDefinition接口

  1.    public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
  2.        implements BeanDefinition, Cloneable

看下如何去解析的:this.parseState.push():标识解析前的初始状态。this.parseState.pop():

代表解析完成后的状态  这样做为了解析安全性的。方法的参数:元素,id,传一个BeanDefinition为空的对象,然后获取到class属性,再判断是否有父类,然后创建抽象的bean定义对象,然后下面是解析子标签属性的,就是每一个类里面的元素,就是解析字段的。

  1.    @Nullable
  2.    public AbstractBeanDefinition parseBeanDefinitionElement(
  3.            Element ele, String beanName, @Nullable BeanDefinition containingBean) {

  4.        this.parseState.push(new BeanEntry(beanName));

  5.        String className = null;
  6.        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
  7.            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
  8.        }
  9.        String parent = null;
  10.        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
  11.            parent = ele.getAttribute(PARENT_ATTRIBUTE);
  12.        }

  13.        try {
  14.            AbstractBeanDefinition bd = createBeanDefinition(className, parent);

  15.            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
  16.            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

  17.            parseMetaElements(ele, bd);
  18.            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
  19.            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

  20.            parseConstructorArgElements(ele, bd);
  21.            parsePropertyElements(ele, bd);
  22.            parseQualifierElements(ele, bd);

  23.            bd.setResource(this.readerContext.getResource());
  24.            bd.setSource(extractSource(ele));

  25.            return bd;
  26.        }
  27.        catch (ClassNotFoundException ex) {
  28.            error("Bean class [" + className + "] not found", ele, ex);
  29.        }
  30.        catch (NoClassDefFoundError err) {
  31.            error("Class that bean class [" + className + "] depends on not found", ele, err);
  32.        }
  33.        catch (Throwable ex) {
  34.            error("Unexpected failure during bean definition parsing", ele, ex);
  35.        }
  36.        finally {
  37.            this.parseState.pop();
  38.        }

  39.        return null;
  40.    }
  41.   protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
  42.            throws ClassNotFoundException {

  43.        return BeanDefinitionReaderUtils.createBeanDefinition(
  44.                parentName, className, this.readerContext.getBeanClassLoader());
  45.    }

上面的 方法的参数传的是className,通过Bean定义阅读工具类去创建Bean定义对象。

GenricBeanDefinition:代表通用的bean定义对象,凡是xml的基本都是通用,下面就是创建了默认的BeanClass,类路径。

  1.    public static AbstractBeanDefinition createBeanDefinition(
  2.            @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

  3.        GenericBeanDefinition bd = new GenericBeanDefinition();
  4.        bd.setParentName(parentName);
  5.        if (className != null) {
  6.            if (classLoader != null) {
  7.                bd.setBeanClass(ClassUtils.forName(className, classLoader));
  8.            }
  9.            else {
  10.                bd.setBeanClassName(className);
  11.            }
  12.        }
  13.        return bd;
  14.    }

  1.    public class GenericBeanDefinition extends AbstractBeanDefinition

开始解析Bean定义的属性,把属性全部解析出来,下面的方法就是如何进行解析的

是否是单例的,是否是多例的,如果有scope的话,就set值,是否是抽象的,是否是懒加载

等等,然后进行一个一个的set方法,工厂方法再进行bean实例化的时候用。全部设置在BeanDefinition对象里面

  1.  parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
  2.  public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
  3.            @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

  4.        if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
  5.            error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
  6.        }
  7.        else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
  8.            bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
  9.        }
  10.        else if (containingBean != null) {
  11.            // Take default from containing bean in case of an inner bean definition.
  12.            bd.setScope(containingBean.getScope());
  13.        }

  14.        if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
  15.            bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
  16.        }

  17.        String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
  18.        if (isDefaultValue(lazyInit)) {
  19.            lazyInit = this.defaults.getLazyInit();
  20.        }
  21.        bd.setLazyInit(TRUE_VALUE.equals(lazyInit));

  22.        String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
  23.        bd.setAutowireMode(getAutowireMode(autowire));

  24.        if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
  25.            String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
  26.            bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
  27.        }

  28.        String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
  29.        if (isDefaultValue(autowireCandidate)) {
  30.            String candidatePattern = this.defaults.getAutowireCandidates();
  31.            if (candidatePattern != null) {
  32.                String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
  33.                bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
  34.            }
  35.        }
  36.        else {
  37.            bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
  38.        }

  39.        if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
  40.            bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
  41.        }

  42.        if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
  43.            String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
  44.            bd.setInitMethodName(initMethodName);
  45.        }
  46.        else if (this.defaults.getInitMethod() != null) {
  47.            bd.setInitMethodName(this.defaults.getInitMethod());
  48.            bd.setEnforceInitMethod(false);
  49.        }

  50.        if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
  51.            String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
  52.            bd.setDestroyMethodName(destroyMethodName);
  53.        }
  54.        else if (this.defaults.getDestroyMethod() != null) {
  55.            bd.setDestroyMethodName(this.defaults.getDestroyMethod());
  56.            bd.setEnforceDestroyMethod(false);
  57.        }

  58.        if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
  59.            bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
  60.        }
  61.        if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
  62.            bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
  63.        }

  64.        return bd;
  65.    }

第一个目标获取到:BeanDefinition接口的实现类在解析xml文件来得到的beanDefinition对象。

相关文章
|
15天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
45 2
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
21天前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
44 9
|
2月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
120 5
|
2月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
2月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
487 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
2月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
135 9
|
2月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
186 2
|
2月前
|
设计模式 JavaScript Java
Spring 事件监听机制源码
Spring 提供了事件发布订阅机制,广泛应用于项目中。本文介绍了如何通过自定义事件类、订阅类和发布类实现这一机制,并展示了如何监听 SpringBoot 启动过程中的多个事件(如 `ApplicationStartingEvent`、`ApplicationEnvironmentPreparedEvent` 等)。通过掌握这些事件,可以更好地理解 SpringBoot 的启动流程。示例代码展示了从事件发布到接收的完整过程。
|
2月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
50 1