Spring中Bean命名源码分析
Spring中Bean命名源码分析
一、案例代码
首先是demo的整体结构

其次是各个部分的代码,代码本身比较简单,不是我们关注的重点
配置类
/**
* @Author Helius
* @Create 2019-10-25-20:16
*/
@Configuration
@ComponentScan(basePackages = {"service"})
public class SpringConfiguration {
}
接口的实现类
public interface UserService {
public void sayHello();
}
@Service(value = "userService")
public class UserServiceImpl implements UserService {
@Override
public void sayHello() {
System.out.println("hello");
}
}
测试类
public class SpringBootConfigurationTest {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = ac.getBean(UserService.class);
userService.sayHello();
}
}
我们主要探究两个bean:一个是我们的SpringConfiguration类,它是配置类,也是容器中的bean
一个是UserServiceImpl类,这个不同在于我们通过@Service(value = "userService")手动指定了bean的名字。我们探究两种情况下,spring容器中bean的名字如何生成的。
二、BeanNameGenerator
我们主要着眼于spring中bean的命名如何生成的,这个接口BeanNameGenerator是用来给容器中的bean进行命名的。类结构如下
public interface BeanNameGenerator {
/**
* Generate a bean name for the given bean definition.
* @param definition the bean definition to generate a name for
* @param registry the bean definition registry that the given definition
* is supposed to be registered with
* @return the generated bean name
*/
String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
}

我们采用的是注解方式,用到的实现类是AnnotationBeanNameGenerator
三、 源码调试
首先debug启动测试类

AnnotationBeanNameGenerator#generateBeanName
AnnotationBeanNameGenerator#determineBeanNameFromAnnotation
AnnotationBeanNameGenerator#determineBeanNameFromAnnotation
AnnotationBeanNameGenerator#buildDefaultBeanName(BeanDefinition)
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
// 判断这个bean是不是注解所标注的bean,显然我们这里是true
if (definition instanceof AnnotatedBeanDefinition) {
//从注解获取beanName,具体见下个方法,
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
if (StringUtils.hasText(beanName)) {
// Explicit bean name found.
return beanName;
}
}
// Fallback: generate a unique default bean name.
return buildDefaultBeanName(definition, registry);
}
protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
//获取bean定义
AnnotationMetadata amd = annotatedDef.getMetadata();
//获取到类上注解的名字,存于set集合中,
// types: 有两个值
// 1. org.springframework.context.annotation.Configuration
// 2.org.springframework.context.annotation.ComponentScan
Set<String> types = amd.getAnnotationTypes();
String beanName = null;
//遍历上面这两个注解
for (String type : types) {
// 获取注解的属性
// 第一次是我们的@Configuration注解,我们在SpringConfiguration中没有加属性,其
// 只有 默认的属性值value,且为“”.
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
// isStereotypeWithNameValue检查该注解是否有指定bean名称的资格
if (attributes != null && isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
// 1.value为空
//2.这里其实就是与UserServiceImpl类差异的地方,它只有一个注解@Service,且只有一个属
//性value(我们默认没写),其值为userService。直接就返回了,
Object value = attributes.get("value");
if (value instanceof String) {
String strVal = (String) value;
if (StringUtils.hasLength(strVal)) {
if (beanName != null && !strVal.equals(beanName)) {
throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
"component names: '" + beanName + "' versus '" + strVal + "'");
}
beanName = strVal;
}
}
}
}
// 1. beanNamef为null,接下来将进入generateBeanName的最后一句:buildDefaultBeanName()方法
return beanName;
}
// 生命默认的beanName,
//对于我们的springBootConfiuration类,其上的两个注解都没有指定bean名称
protected String buildDefaultBeanName(BeanDefinition definition) {
// 获取这个bean的类名:config.SpringConfiguration
String beanClassName = definition.getBeanClassName();
// 段言
Assert.state(beanClassName != null, "No bean class name set");
// 获取类的简短类名SpringConfiguration
String shortClassName = ClassUtils.getShortName(beanClassName);
// springConfiguration将作为默认的Bean的名称返回,
// 这里其实就是bean默认名称的生成规则,见下文
return Introspector.decapitalize(shortClassName);
}
这个类其实是JDK自带的一个类,
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
// 长度大于1且前两个字母是大写,直接返回,意思就是比如HEllo直接就返回hello
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
char chars[] = name.toCharArray();
// 首字母转小写
chars[0] = Character.toLowerCase(chars[0]);
// 所以SpringBootConfiguration转为了springBootConfiguration返回做为bean名称
return new String(chars);
}
到此SpringbootConfuration的bean命名就结束了,
至于UserServiceimpl实现类,
四、自定义BeanName生成规则
参考上面的AnnotationBeanNameGenerator
Spring中Bean命名源码分析的更多相关文章
- spring boot 2.0 源码分析(一)
在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例: 1.先从创建示例中的main函数开始读起: package com.exam ...
- Spring JPA实现逻辑源码分析总结
1.SharedEntityManagerCreator: entitymanager的创建入口 该类被EntityManagerBeanDefinitionRegistrarPostProcesso ...
- spring boot 2.0 源码分析(四)
在上一章的源码分析里,我们知道了spring boot 2.0中的环境是如何区分普通环境和web环境的,以及如何准备运行时环境和应用上下文的,今天我们继续分析一下run函数接下来又做了那些事情.先把r ...
- Spring Cloud 学习 之 Spring Cloud Eureka(源码分析)
Spring Cloud 学习 之 Spring Cloud Eureka(源码分析) Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 ...
- Spring Boot 自动配置 源码分析
Spring Boot 最大的特点(亮点)就是自动配置 AutoConfiguration 下面,先说一下 @EnableAutoConfiguration ,然后再看源代码,到底自动配置是怎么配置的 ...
- Spring基础系列-AOP源码分析
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...
- spring boot 2.0 源码分析(三)
通过上一章的源码分析,我们知道了spring boot里面的listeners到底是什么(META-INF/spring.factories定义的资源的实例),以及它是创建和启动的,今天我们继续深入分 ...
- 设计模式(十五)——命令模式(Spring框架的JdbcTemplate源码分析)
1 智能生活项目需求 看一个具体的需求 1) 我们买了一套智能家电,有照明灯.风扇.冰箱.洗衣机,我们只要在手机上安装 app 就可以控制对这些家电工作. 2) 这些智能家电来自不同的厂家,我们不想针 ...
- Springboot中注解@Configuration源码分析
Springboot中注解@Configuration和@Component的区别 1.先说结论,@Configuration注解上面有@Component注解,所以@Component有的功能@Co ...
随机推荐
- jenkins 配置ssh remotes 报Failed to authenticate with public key
问题:jenkins配置ssh远程连接时候报 ERROR: Failed to authenticate with public key com.jcraft.jsch.JSchException: ...
- Spring IOC 复习
Inversion of Control 将创建对象的权利交给框架,包括DI(Dependency Injection,依赖注入)和DL(Dependency Lookup,依赖查找),能削减计算机程 ...
- 分享几套2019年各大公司最新的PHP面试题,几斤几两一试便知
从面试题发现不足,进而查漏补缺,比通过面试更难得 PHP面试题2019年奇虎360面试题和答案解析 PHP面试题2019年京东工程师面试题和答案解析 PHP面试题2019年新浪工程师面试题和答案解析 ...
- ElasticSearch(九)e代驾使用Elasticsearch流程设计(Yii1版本)
一.控制器层的更新.添加.删除 class AddKnowledgeAction extends CAction { //add and update public function actionPo ...
- Vue详细介绍模板语法和过滤器的使用!
表达式 {{ XXX }}使用过滤器 {{ XXX | yyy}}使用多个过滤器 {{ XXX | yyy | yyy1}}过滤器带参数 {{ XXX | yyy(123,"zhuiszhu ...
- 算法-memcopy与memmove的区别
memcpy()和 memmove()都是C语言中的库函数,在头文件string.h中,作用是拷贝一定长度的内存的内容,原型如下 void *memcpy(void *dst, const void ...
- Elasticsearch高版本安装head插件
安装Elasticsearch 1.安装Elasticsearch-6.5.4.tar.gz [merce@info5 ~]$ cd /appmerce/zrapp/ [merce@info5 zra ...
- Redis五大数据类型详解
关于Redis的五大数据类型,它们分别为:String.List.Hash.Set.SortSet.本文将会从它的底层数据结构.常用操作命令.一些特点和实际应用这几个方面进行解析.对于数据结构的解析, ...
- 安装elasticsearch+kibana+searchguard
---------------------------------安装es的search-guard-------------------------------------------------- ...
- JDK1.8 LocalDate 使用方式;LocalDate 封装Util,LocalDate工具类(四)
未完待续 ........ 前言: 加班了好几天,终于结束上一个坑的项目了,项目交接人员全部离职,代码一行注释没有,无人问津的情况下,完成了项目,所以好的规范真的很重要. 继续日期改写 一 ...