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命名源码分析的更多相关文章

  1. spring boot 2.0 源码分析(一)

    在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例: 1.先从创建示例中的main函数开始读起: package com.exam ...

  2. Spring JPA实现逻辑源码分析总结

    1.SharedEntityManagerCreator: entitymanager的创建入口 该类被EntityManagerBeanDefinitionRegistrarPostProcesso ...

  3. spring boot 2.0 源码分析(四)

    在上一章的源码分析里,我们知道了spring boot 2.0中的环境是如何区分普通环境和web环境的,以及如何准备运行时环境和应用上下文的,今天我们继续分析一下run函数接下来又做了那些事情.先把r ...

  4. Spring Cloud 学习 之 Spring Cloud Eureka(源码分析)

    Spring Cloud 学习 之 Spring Cloud Eureka(源码分析) Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 ...

  5. Spring Boot 自动配置 源码分析

    Spring Boot 最大的特点(亮点)就是自动配置 AutoConfiguration 下面,先说一下 @EnableAutoConfiguration ,然后再看源代码,到底自动配置是怎么配置的 ...

  6. Spring基础系列-AOP源码分析

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...

  7. spring boot 2.0 源码分析(三)

    通过上一章的源码分析,我们知道了spring boot里面的listeners到底是什么(META-INF/spring.factories定义的资源的实例),以及它是创建和启动的,今天我们继续深入分 ...

  8. 设计模式(十五)——命令模式(Spring框架的JdbcTemplate源码分析)

    1 智能生活项目需求 看一个具体的需求 1) 我们买了一套智能家电,有照明灯.风扇.冰箱.洗衣机,我们只要在手机上安装 app 就可以控制对这些家电工作. 2) 这些智能家电来自不同的厂家,我们不想针 ...

  9. Springboot中注解@Configuration源码分析

    Springboot中注解@Configuration和@Component的区别 1.先说结论,@Configuration注解上面有@Component注解,所以@Component有的功能@Co ...

随机推荐

  1. .Net IOC框架入门之——Unity

    一.概述 IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection). 作用:将各层的对象以松耦合的方式组织在一 ...

  2. 2019 海看java面试笔试题 (含面试题解析)

      本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.海看等公司offer,岗位是Java后端开发,因为发展原因最终选择去了海看,入职一年时间了,也成为了面试官,之 ...

  3. ES6入门系列 ----- 对象的遍历

    工作中遍历对象是家常便饭了,遍历数组的方法五花八门, 然而很多小伙伴是不是和我之前一样只会用for ...in.... 来遍历对象呢, 今天给大家介绍五种遍历对象属性的方法: 1, 最常用的for  ...

  4. QT多线程中使用QTcpSocket遇到的读写数据问题

    多线程中使用QTcpSocket在run()方法中new QTcpSocket;然后监听readyRead()信号connect(m_pTcpSocket,SIGNAL(readyRead()),th ...

  5. OKHttp和NumberProgressbar组建强大的Android版本更新功能

    你们看过韩国电影<奇怪的她>不?女主角是不是超级漂亮的.......好啦,扯正事吧,先看看女神照片. 公司新项目用到了OKHttp网络框架,在下载文件这块都蒙圈啦,再查查资料就一个Reso ...

  6. React Navigation 导航栏样式调整+底部角标消息提示

    五一佳节匆匆而过,有人选择在外面看人山人海,有人选择宅在家中度过五一,也有人依然坚守在第一线,致敬! 这是坚持学习react-native的第二篇文章,可能会迟到,但是绝不会缺席,这篇要涉及到的是re ...

  7. Docker搭建Wordpress

    搭建 wordpress 1.拉取 wordpress 镜像docker pull wordpress:latest 2.运行 wordpress 镜像 docker run --name wordp ...

  8. 工作必备之正则匹配、grep、sed、awk

    常用正则:匹配空行:^\s*\n 匹配www开头:^www 添加行号:awk '$0=""NR". "$0' /etc/yum.conf 1.所有域名前加www ...

  9. Kth Minimum Clique(2019年牛客多校第二场D题+k小团+bitset)

    目录 题目链接 题意 思路 代码 题目链接 传送门 题意 找第\(k\)小团. 思路 用\(bitset\)来标记每个结点与哪些结点直接有边,然后进行\(bfs\),在判断新加入的点与现在有的点是否都 ...

  10. LOJ 3160: 「NOI2019」斗主地

    题目传送门:LOJ #3160. 简要题意: 有一个长度为 \(n\) 的序列 \(a\),初始时 \(a_i=i\) 或 \(a_i=i^2\),这取决于 \(\mathrm{type}\) 的值. ...