一、BeanDefinition

1. bean定义都定义了什么?

2、BeanDefinition的继承体系

 父类:

AttributeAccessor:

可以在xml的bean定义里面加上DTD文件里面没有的属性,如

    <bean id="cbean" class="com.study.spring.samples.CBean" name="leeSamll" age="18">
<constructor-arg type="String" value="cbean01"></constructor-arg>
</bean>

BeanMetadataElement :

定义bean定义来源于哪里,在BeanDefinition 里面的getResourceDescrption里面获取

子类:

类图:

请思考:为什么要增加AnnotatedBeanDefinition?用GenericBeanDefinition不可以吗?是不是注解方式的Bean定义信息的存放及使用方式与通用的Bean定义方式不一样了?

GenericBeanDefinition要定义的东西太多了,xml方式的处理和注解方式的处理可能不太一样了,所以做了扩展加入AnnotatedBeanDefinition,是为了只定义自己需要的东西,后面直接从AnnotatedBeanDefinition获取就行了

二、Annotation 方式配置的BeanDefinition的解析

1. 扫描的过程,如何扫描

    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> 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) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}

findCandidateComponents方法说明:

组件索引方式获取bean定义:在pom.xml里面加入spring-context-indexer这个依赖,在编译的时候就会生成META-INF/spring.components文件(加了注解的类),然后bean定义就可以从spring.components里面获取,而不用在启动的时候去包下面扫描获取bean定义,变得很快

scanCandidateComponents方法说明:

MetadataReader说明:

说明:

获取类的信息,不只只有反射,ASM也可以获取,ASM通过获取类的字节码,参观Class(ClassVisistor)可以获取到类上的注解和类对应的信息(类的属性、类的方法等),用到的ASM组件有ClassReader和ClassVisistor

2. 注解的解析

2.1 如何从扫到的 .class 文件中获得注解信息?

我们自己实现是如何做的:

1)Class.forname("className")加载类获得Class对象

2)反射获取注解

3)判断是否存在组件,存在为其创建BeanDefinition

4)看指定了名字没,如果没有,应用名字生成策略生成一个名字

5)注册BeanDefinition

2.2 Spring的解析过程

spring解析注解利用的是ASM,ASM是一个低层次的字节码操作库

2.3 提问:spring 中通过 ASM 字节码操作库来读取的类信息、注解信息。它没有加载类,为什么不用加载类的方式?

  因为加载类都要放到内存里面,用不到的话内存就会浪费了,用ASM加载类不会进内存里面。在spring的org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.scanCandidateComponents(String)的方法里面的这段代码读取类的信息、注解信息的

MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
 * Copyright 2002-2009 the original author or authors.

package org.springframework.core.type.classreading;

import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata; /**
* Simple facade for accessing class metadata,
* as read by an ASM {@link org.springframework.asm.ClassReader}.
*
* @author Juergen Hoeller
* @since 2.5
*/
public interface MetadataReader { /**
* Return the resource reference for the class file.
*/
Resource getResource(); /**
* Read basic class metadata for the underlying class.
*/
ClassMetadata getClassMetadata(); /**
* Read full annotation metadata for the underlying class,
* including metadata for annotated methods.
*/
AnnotationMetadata getAnnotationMetadata(); }

2.4 MetadataReader读取到类信息、注解信息后,如何进行判断及创建BeanDefinition的,往BeanDefintion中给入了哪些信息。

在spring的org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.scanCandidateComponents(String)的方法里面的这段代码判断和创建BeanDefinition的

                        if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new
ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}

判断是否是候选组件的方法:

    /**
* Determine whether the given class does not match any exclude filter
* and does match at least one include filter.
* @param metadataReader the ASM ClassReader for the class
* @return whether the class qualifies as a candidate component
*/
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}

2.5 请思考,我们可以自己定义标注组件的注解吗?【扩展点】

可以,示例如下:

package com.study.leesamll.spring.ext;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import org.springframework.stereotype.Component; @Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface MyComponetAnno { String value() default "";
}

使用:

package com.study.leesamll.spring.service;

import java.util.Locale;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import com.study.leesmall.spring.ext.MyComponetAnno; //@Componet
//@Service
@MyComponetAnno
public class Abean { @Autowired
private ApplicationContext applicationContext; public Abean() {
System.out.println("-----------------Abean 被实例化了。。。。。。。。。");
} public void doSomething() {
System.out.println(this + " do something .....mike.love="
+ this.applicationContext.getEnvironment().getProperty("mike.love"));
System.out
.println("-----------mike.name=" + this.applicationContext.getMessage("mike.name", null, Locale.CHINA));
}
}

2.6 扫描的过滤这块你在实际项目中是否用过?如何使用的?【扩展点】

示例:

package com.study.leesmall.spring.ext;

import java.io.IOException;

import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter; import com.study.leesmall.spring.service.Abean; public class MyTypeFilter implements TypeFilter { @Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// 使用metadataReader中的类信息、注解信息来进行你的过滤判断逻辑
return metadataReader.getClassMetadata().getClassName().equals(Abean.class.getName());
} }

TypeFilter的子类:

请思考:像@Controller 注解,它和@Service、@Component 注解有不同的意图,这种不同的意图将会在哪里实现?如果我们自己也有类似的需要自定义组件注解,是不就可以模仿@Controller。猜想 spring是如何做到灵活扩展这个的?

3、BeanDefinition注册

在前面我们已经拿到BeanDefinition了,下面就是注册BeanDefinition了

1.目标

1.1 搞清楚BeanFactory中BeanDefinition是如何存储的?
1.2 搞清楚注册的过程是怎样的。

2. 思路

2.1 思考:我们原来是如何来存储beanName,BeanDefinition的?
  用并发的Map来存,beanName作为键,BeanDefinition作为值
2.2 思考:我们注册BeanDefinition的处理逻辑是怎样的?
  首先判断beanName是不是合法的,如果是合法的再放到Map里面

3. 看spring的注册过程

入口:DefaultListableBeanFactory.registerBeanDefinition(String beanName,BeanDefinitionbeanDefinition)

还是先拿到调用栈来分析:

处理BeanDefinition的过程:

String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

说明:Spring里面默认的bean的名字的生成策略是拿类的名称来当bean的名称

看一下AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);方法里面是怎么处理注解BeanDefinition的:

下面就来看具体的注册bean定义逻辑:

PS:

1.Maven怎么加依赖时怎么同时下载源码?
Eclipse-window-preference-maven-勾选download artifact sources
下载慢的话把maven的setting.xml配置文件的镜像地址换成阿里的更快

完整代码获取地址:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-source-study

框架源码系列七:Spring源码学习之BeanDefinition源码学习(BeanDefinition、Annotation 方式配置的BeanDefinition的解析)的更多相关文章

  1. Spring系列(七) Spring MVC 异常处理

    Servlet传统异常处理 Servlet规范规定了当web应用发生异常时必须能够指明, 并确定了该如何处理, 规定了错误信息应该包含的内容和展示页面的方式.(详细可以参考servlet规范文档) 处 ...

  2. Spring框架系列(七)--Spring常用注解

    Spring部分: 1.声明bean的注解: @Component:组件,没有明确的角色 @Service:在业务逻辑层使用(service层) @Repository:在数据访问层使用(dao层) ...

  3. Spring Boot系列(一) Spring Boot准备知识

    本文是学习 Spring Boot 的一些准备知识. Spring Web MVC Spring Web MVC 的两个Context 如下图所示, 基于 Servlet 的 Spring Web M ...

  4. Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制

    Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制 201311.27 代码下载 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 前言 ...

  5. 框架源码系列六:Spring源码学习之Spring IOC源码学习

    Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的  1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...

  6. 框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)

    一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...

  7. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

  8. 事件机制-Spring 源码系列(4)

    事件机制-Spring 源码系列(4) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProcess ...

  9. spring源码分析系列 (3) spring拓展接口InstantiationAwareBeanPostProcessor

    更多文章点击--spring源码分析系列 主要分析内容: 一.InstantiationAwareBeanPostProcessor简述与demo示例 二.InstantiationAwareBean ...

随机推荐

  1. php 生成xml文件

    <?php class Xml{        /*      *  $aData     要格式化的数组      *  $path      xml信息要写入的文件路径     *  $ve ...

  2. 潭州课堂25班:Ph201805201 django框架 第十课 GET,POST 请求 文件上传,HttpResponse,cookie (课堂笔记)

    在项目中新建个APP, 在主目录中的配置文件中进行 APP 注册 在主目录中的 urls 文件中进行路径分配 新建 urrls 文件 进行分路由配置 创建模板文件 html 文件 写个 form 表单 ...

  3. Django restful

    1.restful api的规范 API与用户的通信协议,总是使用HTTPs协议. 域名  https://api.example.com                         尽量将API ...

  4. 多角度对比 ES5与ES6的区别

    ES5与ES6的对比不同点整理 本文关键词:ES6,javascript, 1.Default Parameters(默认参数) es6之前,定义默认参数的方法是在一个方法内部定义 var link ...

  5. 卡在checking installable status

    npm install卡在checking installable status 笔者在使用NPM过程中经常会用到npm install命令,发现有时候会卡在checking installable ...

  6. Java 服务端监控方案(四. Java 篇)

    http://jerrypeng.me/2014/08/08/server-side-java-monitoring-java/ 这个漫长的系列文章今天要迎来最后一篇了,也是真正与 Java 有关的部 ...

  7. vscode rn代码格式化配置

    安装Beautify和React-beautify扩展程序,并在项目根目录创建配置文件.jsbeautifyrc,并复制下面代码到此配置文件里: { "mode": "b ...

  8. sql注入--基于报错的注入

    这是经典的sqli-labs 中的less-5 问题首先通过几个常见的进行测试, 发现只要正确的话就会输出you are in.... 并不能绕过,因此不能出现敏感信息,因此要用一种新思路(参考白帽学 ...

  9. golang基础学习及web框架

    golang的web框架 web框架百花齐放:对比 Go Web 编程 Go Web Examples Golang 适合做 Web 开发吗? beego beego简介 go-restful gol ...

  10. 【Spring】Springboot监听器,启动之后初始化工作

    package com.laplace.laplace.common.starter.config; import java.io.IOException; import org.slf4j.Logg ...