第一章 Spring5源码分析之标签解析笔记

一、Spring程序入口

Spring 重新定义了 java

1、Spring 的历史

2002年10月,Rod Johnson发布《Expert One-on-One J2EE设计和开发》一书。

2004年3月,Spring1.0发布

2003年6月,Spring Framework第一次以Apache 2.0许可证下发布0.9版本,2004年3月,Spring1.0正式发布;对于Spring1.0,其源码只有一个包,在该包中包含了aop、beans、context、core、jdbc、orm等。对于此时的版本,Spring1.0仅支持XML配置的方式。

2006年10月,Spring2.0发布

对于2.0,Spring主要增加了对注解的支持,实现了基于注解的配置。

在2007年11月,发布Spring2.5,该版本具备的特性有:

添加可扩展的XML配置功能,用于简化XML配置。

支持Java5。

添加额外的IOC容器扩展点,支持动态语言(如groovy、aop增强功能和新的bean范围)。

2009年12月,Spring3.0发布Spring3.0 主要具有的特性有:

模块重组系统

支持Spring表达式语言(Spring Expression)

基于Java的Bean配置(JavaConfig)

支持嵌入式数据库:HSQL、H2等

支持REST

支持Java6

201312月,发布Spring4.0

对于Spring4.0是Spring版本历史上的一重大升级。其特性为:

全面支持Java8

支持Lambda表达式

支持Java8的时间和日期API

支持重复注解

支持Java8的 Optional

核心容器增强

增加泛型依赖注入

增加Map依赖注入

增加List依赖注入

支持lazy注解配置懒加载

支持Condition条件注解

CGLIB动态代理增强

支持基于GroovyDSL定义Bean

Web增强

增强SpringMVC,基于Servlet3.0开发

提供RestController注解

提供AsyncRestTemplate支持客户端的异步无阻塞请求

增加对WebSocket的支持

20179月,Spring5.0发布,Spring5.0 特性如下:

升级到Java8、JavaEE7

废弃低版本,将Java8、JavaEE 7作为最低版本要求

兼容Java9

兼容JavaEE8

反应式编程模型,增加WebFlux模块

升级SpringMVC,增加对最新的API(Jackson等)的支持

增加函数式编程模式

重构源码,部分功能使用Lambda表达式实现。

2、Spring子项目

2.1、Spring IO Platform :

Spring IO是可集成的、构建现代化应用的版本平台。Spring IO是模块化的、企业级的分布式系统,包括一系列依赖,是的开发者仅能对自己所需的部分进行完全的部署控制。

2.2、Spring Boot

Spring应用快速开发工具,用来简化Spring应用开发过程。

2.3、Spring XD

Spring XD(eXtreme Date,极限数据)是Pivotal的大数据产品。它结合了Spring Boot和Grails,组成 Spring IO平台的执行部分。

2.4、Spring Data

Spring Data是为了简化构建基于Spring框架应用的数据访问实现,包括非关系数据库、Map-Reduce框架、云数据服务等;另外,也包含对关系数据库的访问支持。

2.5、Spring Integration

Spring Integration为企业数据集成提供了各种适配器,可以通过这些适配器来转换各种消息格式,并帮助Spring应用完成与企业应用系统的集成。

2.6、Spring Batch

Spring Batch是一个轻量级的完整批处理框架,皆在帮助应用开发者构建一个健壮、高效的企业级批处理应用(这些应用的特点是不需要与用户交互,重复的操作量大,对于大容量的批量数据处理而言,这些操作往往要求较高的可靠性)。

2.7、Spring Security

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文配置的bean,充分利用Ioc和AOP功能,为应用系统提供声明式的安全访问控制功能。

2.8、Spring Hateoas

Spring Hateoas是一个用户支持实现超文本驱动的REST Web服务的开发库,是Hateoas的实现。Hateoas(Hypermedia as the engine of application state)是REST架构风格中最复杂的约束,也是构建成熟REST服务的核心。它的重要性在于打破了客户端和服务器之间严格的契约,是的客户端可以更加智能和自适应。

2.9、Spring Social

Spring Social是Spring框架的扩展,用来方便开发Web社交应用程序,可通过该项目来创建与各种社交网站的交互,如Facebook,LinkedIn、Twitter 等。

2.10、Spring AMQP

Spring AMQP是基于Spring框架的AMQP消息解决方案,提供模版化的发送和接收消息的抽象层,提供基于消息驱动的POJO。这个项目支持Java和.NET连个版本。Spring Source旗下的RabbitMQ就是一个开源的基于AMQP的消息服务器。

2.11、Spring for Android

Spring for Android为Android终端开发应用提供Spring的支持,它提供了一个在Android应用环境中工作、基于Java的REST客户端。

2.12、Spring Mobile

Spring Mobile是基于Spring MVC构建的,为移动端的服务器应用开发提供支持。Spring Web Flow:Spring Web Flow(SWF)一个建立在 Spring MVC 基础上的Web 页面流引擎。

2.13、Spring Web Service

Spring Web Service是基于Spring框架的Web服务框架,主要侧重于基于文档驱动的Web服务,提供SOAP服务开发,允许通过多种方式创建Web服务。

2.14、Spring LDAP

Spring LDAP是一个用户操作LDAP的Java框架,类似Spring JDBC提供了JdbcTemplate方式来操作数据库。这个框架提供了一个LdapTemplate操作模版,可帮助开发人员简化looking up、closing contexts、encoding/decoding、filters 等操作。

2.15、Spring Session 

Spring Session致力于提供一个公共基础设施会话,支持从任意环境中访问一个会话,在Web环境下支持独立于容器的集群会话,支持可插拔策略来确定Session ID,WebSocket活跃的时候可以简单地保持 HttpSession。

2.16、Spring Shell 

Spring Shell提供交互式的Shell,用户可以简单的基于Spring的编程模型来开发命令。

3、Spring 源码下载

1)git clone --branch v5.1.3.RELEASE https://gitee.com/Z201/spring-framework.git

2)gradle下载,gradle要JDK8的版本

3)到下载的spring源码路径执行gradle命令:gradlew :spring-oxm:compileTestJava

4)用idea打开spring源码工程,在idea中安装插件kotlin,重启idea

5)把编译好的源码导入到工程中。

这样我们就可以在源码中写注释并且断点调试源码了。

4、导入jar依赖

spring中最核心的4个jar:

spring-beans、spring-core、spring-context、spring-expression

一个最最简单的spring工程,理论上就只需要一个jar就够了,spring-context包本身就依赖了,spring-aop,spring-beans,spring-core包。

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-contextartifactId>
    <version>${spring.version}version>
dependency>

一个空的 spring 工程是不能打印日志的,要导入 spring 依赖的日志 jar

<dependency>
    <groupId>ch.qos.logbackgroupId>
    <artifactId>logback-classicartifactId>
    <version>LATESTversion>
dependency>

5、spring中XSD引入

1、spring中要引入自定义标签,必须要引入其对应的XSD文件

如:标签

必须要引入:

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

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

在标签对应的jar包下面找到对应的spring.schemas,在这个文件中就会有对应的XSD路径和命名空间

例如:spring-beans/5.1.3.RELEASE/spring-beans-5.1.3.RELEASE.jar!/META-INF/spring.schemas

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd"
        default-lazy-init="false">

6、spring容器加载方式

1类路径获取配置文件

ApplicationContext applicationContext= new ClassPathXmlApplicationContext("spring.xml");

2文件系统路径获取配置文件【绝对路径】

ApplicationContext applicationContext = new FileSystemXmlApplicationContext("绝对路劲");

3无配置文件加载容器

ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.xx.xx");

4springboot加载容器

ApplicationContext applicationContext = new EmbeddedWebApplicationContext();

二、xml解析流程分析

1、spring容器加载核心方法

AbstractApplicationContext.refresh()方法,refresh()方法是spring容器启动过程中的核心方法,spring容器要加载必须执行该方法。下面我们来主要精读一下这个方法的源码

1.1、设计模式——模板设计模式

在spring中大量的使用了模板设计模式,可以说是用得最多的设计模式。

定义一个算法流程,将一些特定步骤的具体实现、延迟到子类。使得可以在不改变算法流程的情况下,通过不同的子类、来实现“定制”流程中的特定的步骤。

第一章 Spring5源码分析之标签解析笔记_第1张图片

  • 先将主流程框架逻辑(清点商品/计算价目/结算/送货上门)设计完成
  • 再实现各模块小步骤。
  • 不能确实的步骤,作为虚拟方法,甩锅给子类实现。
  • 子类只需要聚焦自己的小步骤逻辑。

1.2、xml源码解析

org.springframework.context.support.AbstractApplicationContext

public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    this.scan(basePackages);
    this.refresh();
}

public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        this.prepareRefresh();

/* 1、创建BeanFactory对象
* 2、xml解析
*  传统标签解析:bean、import等
*  自定义标签解析 如:
*  自定义标签解析流程:
*     a、根据当前解析标签的头信息找到对应的namespaceUri
*     b、加载spring所以jar中的spring.handlers文件。并建立映射关系
*     c、根据namespaceUri从映射关系中找到对应的实现了NamespaceHandler接口的类
*     d、调用类的init方法,init方法是注册了各种自定义标签的解析类
*     e、根据namespaceUri找到对应的解析类,然后调用paser方法完成标签解析
* 3、把解析出来的xml标签封装成BeanDefinition对象
* */
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        this.prepareBeanFactory(beanFactory);
        try {
            this.postProcessBeanFactory(beanFactory);
            this.invokeBeanFactoryPostProcessors(beanFactory);
            this.registerBeanPostProcessors(beanFactory);
            this.initMessageSource();
            this.initApplicationEventMulticaster();
            this.onRefresh();
            this.registerListeners();
            this.finishBeanFactoryInitialization(beanFactory);
            this.finishRefresh();
        } catch (BeansException var9) {
        } finally {
            this.resetCommonCaches();
        }
    }
}

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 该方法主要进行xml解析工作,流程如下:

1.3创建XmlBeanDefinitionReader对象

org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory()

//解析xml,并把xml中的标签封装成BeanDefinition对象
loadBeanDefinitions(beanFactory);

org.springframework.context.support.AbstractXmlApplicationContext

//创建xml的解析器,这里是一个委托模式
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

//主要看这个方法  重要程度 5
loadBeanDefinitions(beanDefinitionReader);

//获取需要加载的xml配置文件
String[] configLocations = getConfigLocations();

1.4、通过Reader对象加载配置文件

org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.xml.XmlBeanDefinitionReader)

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
   }
   //获取需要加载的xml配置文件
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}

1.5、根据加载的配置文件把配置文件封装成document对象

org.springframework.beans.factory.xml.XmlBeanDefinitionReader

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
    try {
        Document doc = this.doLoadDocument(inputSource, resource);
        int count = this.registerBeanDefinitions(doc, resource);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        return count;

1.6、创建BeanDefinitionDocumentReader对象

DocumentReader负责对document对象解析

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadDocument

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler,

this.getValidationModeForResource(resource), this.isNamespaceAware());
}

public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,

ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);
    if (logger.isTraceEnabled()) {
        logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
    }
    DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);
    return builder.parse(inputSource);
}

org.springframework.beans.factory.xml.XmlBeanDefinitionReader

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
    int countBefore = this.getRegistry().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
    return this.getRegistry().getBeanDefinitionCount() - countBefore;
}

1.7、parseDefaultElement(ele, delegate);常规标签解析(bean\import等)

this.parseBeanDefinitions(root, this.delegate);

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        for(int i = 0; i < nl.getLength(); ++i) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element)node;
                if (delegate.isDefaultNamespace(ele)) {
                    //默认标签解析
                    parseDefaultElement(ele, delegate);
                } else {
                   //自定义标签解析
                   delegate.parseCustomElement(ele);
                }
            }
        }
    } else {
        delegate.parseCustomElement(root);
    }
}

1.8、delegate.parseCustomElement(ele);负责自定义标签解析

@Nullable
public BeanDefinition parseCustomElement(Element ele) {
    return this.parseCustomElement(ele, (BeanDefinition)null);
}

1.9、最终解析的标签封装成BeanDefinition并缓存到容器中

@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
    String namespaceUri = this.getNamespaceURI(ele);
    if (namespaceUri == null) {
        return null;
    } else {
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            return null;
        } else {
            return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
        }
    }
}

Xml 流程分析:

第一章 Spring5源码分析之标签解析笔记_第2张图片

第一章 Spring5源码分析之标签解析笔记_第3张图片

2、自定义标签解析

2.1、自定义标签解析大致步骤分析

 1)获取自定义标签的namespace命令空间,例如:

http://www.springframework.org/schema/context String namespaceUri = getNamespaceURI(ele);

 2)根据命令空间获取NamespaceHandler对象。

NamespaceUri和NamespaceHandler之间会建立一个映射,spring会从所有的spring jar包中扫描spring.handlers文件,建立映射关系。

NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve( namespaceUri);

Map handlerMappings = getHandlerMappings();

Object handlerOrClassName = handlerMappings.get(namespaceUri);

3)反射获取NamespaceHandler实例

NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);

4)调用init方法 namespaceHandler.init();

5)调用parse方法handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

2.2、spring.handler文件分析:

spring-beans-5.1.3.RELEASE.jar!\META-INF\spring.handlers

http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

其实就是namespaceUri和类的完整限定名的映射。

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   if (delegate.isDefaultNamespace(root)) {
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
         Node node = nl.item(i);
         if (node instanceof Element) {
            Element ele = (Element) node;
            if (delegate.isDefaultNamespace(ele)) {
               //默认标签解析
               parseDefaultElement(ele, delegate);
            }
            else {
               //自定义标签解析
               delegate.parseCustomElement(ele);
            }
         }
      }
   } else {
      delegate.parseCustomElement(root);
   }
}

2.3、自定义标签解析源码分析入口

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)

@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
   String namespaceUri = getNamespaceURI(ele);
   if (namespaceUri == null) {
      return null;
   }
   NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
   return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

spring-context-5.1.3.RELEASE.jar!/META-INF/spring.handlers

http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler

ContextNamespaceHandler类初始化自定义标签的方法:

public class ContextNamespaceHandler extends NamespaceHandlerSupport {
   @Override
   public void init() {
      registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
      registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
      registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
      registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
      registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
      registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
      registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
      registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
   }
}

org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#resolve

@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
   //获取spring中所有jar包里面的 "META-INF/spring.handlers"文件,并且建立映射关系
   Map handlerMappings = getHandlerMappings();
   //根据namespaceUri:http://www.springframework.org/schema/p,获取到这个命名空间的处理类
   Object handlerOrClassName = handlerMappings.get(namespaceUri);
   if (handlerOrClassName == null) {
      return null;
   } else if (handlerOrClassName instanceof NamespaceHandler) {
      return (NamespaceHandler) handlerOrClassName;
   }else {
      String className = (String) handlerOrClassName;
      try {
         Class handlerClass = ClassUtils.forName(className, this.classLoader);
         if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
            throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                  "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
         }
         NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
         //调用处理类的init方法,在init方法中完成标签元素解析类的注册
         namespaceHandler.init();
         handlerMappings.put(namespaceUri, namespaceHandler);
         return namespaceHandler;
      }
    }
}

//加载"META-INF/spring.handlers"文件过程
Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);

//所有"META-INF/spring.handlers"文件里面的内容建立映射关系
handlerMappings = new ConcurrentHashMap<>(mappings.size());

org.springframework.beans.factory.xml.NamespaceHandlerSupport#parse

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
   BeanDefinitionParser parser = findParserForElement(element, parserContext);
   return (parser != null ? parser.parse(element, parserContext) : null);
}

@Nullable
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
   String localName = parserContext.getDelegate().getLocalName(element);
   BeanDefinitionParser parser = this.parsers.get(localName);
   if (parser == null) {
      parserContext.getReaderContext().fatal(
            "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
   }
   return parser;
}

注意parser获取的标签类就是前面spring.handlers对应的ContextNamespaceHandler的init方法初始化进去的,最后放到Map parsers中。

2.4、component-scan标签扫描初始化过程分析

例如标签component-scan的初始化如下:

registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());

org.springframework.beans.factory.xml.NamespaceHandlerSupport#registerBeanDefinitionParser

protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
   this.parsers.put(elementName, parser);
}

public abstract class NamespaceHandlerSupport implements NamespaceHandler {
   private final Map parsers = new HashMap<>();
   private final Map decorators = new HashMap<>();
   private final Map attributeDecorators = new HashMap<>();

接下来我们继续分析parse方法,这里我们选择自定义标签ComponentScanBeanDefinitionParser分析:

org.springframework.beans.factory.xml.NamespaceHandlerSupport#parse

第一章 Spring5源码分析之标签解析笔记_第4张图片

2.5、BeanDefinition 扫描封装过程

org.springframework.context.annotation.ComponentScanBeanDefinitionParser#parse

/**
 * 1、扫描路劲 .class后缀文件
 * 2、判断类上是否有注解
 * 3、 GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
 *     genericBeanDefinition.setBeanClass(BeanClass.class);
 * 4、完成beanDefinition注册
 */
public BeanDefinition parse(Element element, ParserContext parserContext) {
   String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
   basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
   String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
         ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
   // 创建注解扫描器 Actually scan for bean definitions and register them.
   ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
   //TODO 扫描类并把扫描到的类放到beanDefinitions中 核心方法5
   Set beanDefinitions = scanner.doScan(basePackages);

// 类属性上面的很多注解(@Resource、@Autowrite\@Bean等等)都是在这个方法里面完成注册的 核心方法5
   registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
   return null;
}

扫描类并把扫描到的类放到beanDefinitions中:

protected Set doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set beanDefinitions = new LinkedHashSet<>();
   for (String basePackage : basePackages) {
      // 扫描到有注解的类并封装成BeanDefinition对象
      Set candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
         //
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
         if (candidate instanceof AbstractBeanDefinition) {
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
         if (candidate instanceof AnnotatedBeanDefinition) {
            // 支持了@Lazy @DependOn注解等
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
         }
         if (checkCandidate(beanName, candidate)) {
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            definitionHolder =
                  AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
            // 注册BeanDefinition
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }
   return beanDefinitions;
}

注意:Bean的所有信息包括注解都会加载到metadata对象中AnnotatedTypeMetadata

2.6、完成BeanDefinition的注册

protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
   BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}

public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {
   // Register bean definition under primary name.
   String beanName = definitionHolder.getBeanName();
   //完成BeanDefinition的注册,重点看,重要程度 5
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
   //建立别名和 id的映射,这样就可以根据别名获取到id Register aliases for bean name, if any.
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         registry.registerAlias(beanName, alias);
      }
   }
}

2.7、把beanDefinition缓存到beanDefinitionMap中

org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

else {
   //把beanDefinition缓存到map中Still in startup registration phase
   this.beanDefinitionMap.put(beanName, beanDefinition);
   //把beanName放到beanDefinitionNames list中,这个list着重记住,bean实例化的时候需要用到
   this.beanDefinitionNames.add(beanName);
   this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;

2.8、beanDefinitionbeanName存放的结合:

/** Map of bean definition objects, keyed by bean name. */
private final Map beanDefinitionMap = new ConcurrentHashMap<>(256);

/** List of bean definition names, in registration order. */
private volatile List beanDefinitionNames = new ArrayList<>(256);

3、设计模式——委托模式

有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。在spring中用得比较多,本质上也是java多态的一种实现方式。

public interface Company {
    void product();
}

public class Oracle implements Company {
    @Override
    public void product() {
        System.out.println("oracle product");
    }
}

public class Sun implements Company {
    @Override
    public void product() {
        System.out.println("sun product");
    }
}

public class Boss implements Company {
    Sun sun = new Sun();
    // 老板直接委托sun公司去生产
    @Override
    public void product() {
        sun.product();
    }
}

4、设计模式——装饰模式

装饰模式的定义与特点

装饰(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

装饰(Decorator)模式的主要优点有:

  • 采用装饰模式扩展对象的功能比采用继承方式更加灵活。
  • 可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。

其主要缺点是:装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。

模式结构:

装饰模式包含如下角色:

装饰模式主要包含以下角色。

  • 抽象构件Component角色:定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件Concrete Component角色:实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰Decorator角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰ConcreteDecorator角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

第一章 Spring5源码分析之标签解析笔记_第5张图片

5、BeanDefinition简介

BeanDefinition在spring中贯穿始终,spring要根据BeanDefinition对象来实例化bean,只要把解析的标签,扫描的注解类封装成BeanDefinition对象,spring才能实例化 bean。

beanDefinition实现类ChildBeanDefinition, GenericBeanDefinition, RootBeanDefinition

5.1、ChildBeanDefinition

ChildBeanDefinition是一种bean definition,它可以继承它父类的设置,即ChildBeanDefinition对RootBeanDefinition有一定的依赖关系。ChildBeanDefinition从父类继承构造参数值,属性值并可以重写父类的方法,同时也可以增加新的属性或者方法(类同于 java 类的继承关系)。若指定初始化方法,销毁方法或者静态工厂方法,ChildBeanDefinition 将重写相应父类的设置。depends on,autowire mode,dependency check,sigleton,lazy init一般由子类自行设定。

5.2、GenericBeanDefinition

注意:从spring 2.5开始,提供了一个更好的注册bean definition类GenericBeanDefinition,它支持动态定义父依赖,方法是GenericBeanDefinition.setParentName(java.lang.String),GenericBeanDefinition可以有效的替代ChildBeanDefinition的绝大分部使用场合。

GenericBeanDefinition是一站式的标准bean definition,除了具有指定类、可 选的构造参数值和属性参数这些其它bean definition一样的特性外,它还具有通过parenetName属性来灵活设置parent bean definition。

通常GenericBeanDefinition用来注册用户可见的bean definition(可见的bean definition意味着可以在该类bean definition上定义post-processor来对bean进行操作,甚至为配置parent name做扩展准备)。RootBeanDefinition / ChildBeanDefinition用来预定义具有parent/child关系的bean definition。

5.3、RootBeanDefinition

一个RootBeanDefinition定义表明它是一个可合并的bean definition:即在spring beanFactory运行期间,可以返回一个特定的bean。RootBeanDefinition可 以作为一个重要的通用的bean definition视图。RootBeanDefinition用来在配置阶段进行注册bean definition。然后从spring 2.5后,编写注册bean definition有了更好的的方法:GenericBeanDefinition。GenericBeanDefinition支持动态定义父类依赖,而非硬编码作为root bean definition。

GenericBeanDefinition创建实例

@Component
public class BeanDefinitionTest implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        genericBeanDefinition.setBeanClass(BeanClass.class);
        MutablePropertyValues propertyValues = genericBeanDefinition.getPropertyValues();
        propertyValues.addPropertyValue("username","Hnakin");
        registry.registerBeanDefinition("beanClass",genericBeanDefinition);
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
        //TODO 将自定义的注解@MyService添加到ClassPathBeanDefinitionScanner
        // private final List includeFilters = new LinkedList<>();
        scanner.addIncludeFilter(new AnnotationTypeFilter(MyService.class));
        scanner.scan("com.chj");
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}

5.4、BeanDefinition中的属性

第一章 Spring5源码分析之标签解析笔记_第6张图片

1)id:Bean的唯一标识名,它必须是合法的XMLID,在整个XML文档中唯一。

2)name用来为id创建一个或多个别名,它可以是任意的字母符合,多个别名之间用逗号或空格分开。

3)class用来定义类的全限定名(包名+类名),只有子类 Bean不用定义该属性。

4)parent子类Bean定义它所引用它的父类Bean,这时前面的class属性失效,子类Bean会继承父类Bean的所有属性,子类Bean也可以覆盖父类Bean的属性。

注意:子类Bean和父类Bean是同一个Java类。

5)abstract(默认为”false”):用来定义Bean是否为抽象Bean。它表示这个Bean将不会被实 例化,一般用于父类Bean,因为父类Bean主要是供子类Bean继承使用。

7)lazy-init默认为“default”:用来定义这个Bean是否实现懒初始化。如果为“true”,它将 在BeanFactory启动时初始化所有的SingletonBean。反之,如果为“false”,它只在Bean请求时才开始创建SingletonBean。

8)autowire(自动装配,默认为“default”):它定义了Bean的自动装载方式。

参数属性说明:

  • “no”:不使用自动装配功能。
  • “byName”:通过 Bean 的属性名实现自动装配。
  • “byType”:通过 Bean 的类型实现自动装配。
  • “constructor”:类似于 byType,但它是用于构造函数的参数的自动组装。
  • “autodetect”:通过 Bean 类的反省机制(introspection)决定是使用“constructor”还是使用“byType”。

10)depends-on依赖对象:这个Bean在初始化时依赖的对象,这个对象会在这个Bean初始 化之前创建。

11)init-method:用来定义Bean的初始化方法,它会在Bean组装之后调用。它必须是一个无参数的方法。

12)destroy-method:用来定义Bean的销毁方法,它在BeanFactory关闭时调用;同样它也必须是一个无参数的方法,它只能应用于singletonBean。

13)factory-method:定义创建该Bean对象的工厂方法。它用于下面的“factory-bean”,表示这个Bean 是通过工厂方法创建。此时“class”属性失效。

14)factory-bean:定义创建该Bean对象的工厂类,如果使用了“factory-bean”则“class”属性失效。

15)autowire-candidate:采用xml格式配置bean时,将元素的autowire-candidate属性设置为false,这样容器在查找自动装配对象时,将不考虑该bean,即它不会被考虑作为其它bean自动装配的候选者,但是该bean本身还是可以使用自动装配来注入其它bean的。

16)MutablePropertyValues:

用于封装标签的信息,其实类里面就是有一个list,list里面是PropertyValue对象,PropertyValue就是一个name和value属性,用于封装标签 的名称和值信息

17)ConstructorArgumentValues:

用于封装标签的信息,其实类里面就是有一个map,map中用构造函数的参数顺序作为key,值作为value存储到map中。

18)MethodOverrides:

用于封装lookup-method和replaced-method标签的信息,同样的类里面有一个Set对象添加LookupOverride对象和ReplaceOverride对象。

6Spi设计思想

自定义标签解决就是一个SPI设计思想,通过加装全文配置文件,做到代码灵活的调用。

6.1、创建实例:

public interface SpiService {
    public String query(String param);
}

public class SpiServiceImpl implements SpiService {
    @Override
    public String query(String param) {
        System.out.println("=======SpiServiceImpl.query======");
        return "OK";
    }
}

6.2、创建com.chj.spi.SpiService

在Resources/META-INF/services/的目录下面创建文件com.chj.spi.SpiService,内容如下:

添加内容:com.chj.spi.SpiServiceImpl

6.3、创建测试类

public class SPiTest {
    public static void main(String[] args) {
        //TODO 通过配置的方式要唯一确定一个类
        ServiceLoader load = ServiceLoader.load(SpiService.class);
        for (SpiService spiService : load) {
            spiService.query("90");
        }
    }
}

你可能感兴趣的:(Spring框架源码解读)