框架源码系列七:Spring源码学习之BeanDefinition源码学习(BeanDefinition、Annotation 方式配置的BeanDefinition的解析)
一、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的解析)的更多相关文章
- Spring系列(七) Spring MVC 异常处理
Servlet传统异常处理 Servlet规范规定了当web应用发生异常时必须能够指明, 并确定了该如何处理, 规定了错误信息应该包含的内容和展示页面的方式.(详细可以参考servlet规范文档) 处 ...
- Spring框架系列(七)--Spring常用注解
Spring部分: 1.声明bean的注解: @Component:组件,没有明确的角色 @Service:在业务逻辑层使用(service层) @Repository:在数据访问层使用(dao层) ...
- Spring Boot系列(一) Spring Boot准备知识
本文是学习 Spring Boot 的一些准备知识. Spring Web MVC Spring Web MVC 的两个Context 如下图所示, 基于 Servlet 的 Spring Web M ...
- Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制
Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制 201311.27 代码下载 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 前言 ...
- 框架源码系列六:Spring源码学习之Spring IOC源码学习
Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的 1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...
- 框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)
一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...
- 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...
- 事件机制-Spring 源码系列(4)
事件机制-Spring 源码系列(4) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProcess ...
- spring源码分析系列 (3) spring拓展接口InstantiationAwareBeanPostProcessor
更多文章点击--spring源码分析系列 主要分析内容: 一.InstantiationAwareBeanPostProcessor简述与demo示例 二.InstantiationAwareBean ...
随机推荐
- 全局解释器锁 GIL
1.什么是GIL? GIL本质上是互斥锁,可以将并发运行变为串行,以此来控制同一时间内共享数据只能被一个任务修改,保证时间安全 2.GIL应用场景 使用原因:Cpython解释器自带垃圾回收机制不是线 ...
- 网络吞吐量 [CQOI2015] [网络流]
Description 路由是指通过计算机网络把信息从源地址传输到目的地址的活动,也是计算机网络设计中的重点和难点.网络中实现路由转发的硬件设备称为路由器.为了使数据包最快的到达目的地,路由器需要选择 ...
- 利用Ajax和JSON实现关于查找省市名称的二级联动功能
功能实现的思路:我们经常碰见网上购物时候填写收件地址会用到这个查找省市县的三级联动查找功能,我们可以利用Ajax和JSON技术模拟这个功能,说白了同样是使用Ajax的局部数据更新功能这个特性.因为省市 ...
- db2 load报文件系统满
使用db2 load导入数据 数据量比较大时常常会报文件系统已满错误. 原因分析:导入表建有索引,在load的“索引复制”阶段会从系统临时表空间拷贝到目标表空间,导致系统临时表空间所在的文件系统满,l ...
- Vue(七)发送Ajax请求
发送AJAX请求 1. 简介 vue本身不支持发送AJAX请求,需要使用vue-resource.axios等插件实现 axios是一个基于Promise的HTTP请求客户端,用来发送请求,也是vue ...
- 彻底搞清楚javascript中的require、import和export
为什么有模块概念 理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块. 但是,Javascript不是一种模块化编程语言,在es6以前,它是不支持”类”(class),所以也 ...
- springboot拦截器@Autowired为null解决
问题原因 拦截器加载的时间点在springcontext之前,所以在拦截器中注入自然为null 文件解决 在spring配置文件中这样写 @Bean public HandlerInterceptor ...
- netty中的ChannelHandler和ChannelPipeline
netty中的ChannelHandler和ChannelPipeline ChannelHandler 家族 https://www.w3cschool.cn/essential_netty_in_ ...
- 3D打印开源软件Cura分析(1) 【转】
http://www.sohu.com/a/236241465_100000368 Cura是Ultimaker公司开发的3D打印开源软件,在所有的3D打印开源软件中应属上乘之作,很有研究的价值.国内 ...
- MySQL中的isnull、ifnull和nullif函数用法
isnull(expr) 如expr为null,那么isnull()的返回值为1,否则返回值为0. mysql>select isnull(1+1); ->0 mysql>selec ...