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. Mybatis源码解析(一) —— mybatis与Spring是如何整合的?

    Mybatis源码解析(一) -- mybatis与Spring是如何整合的?   从大学开始接触mybatis到现在差不多快3年了吧,最近寻思着使用3年了,我却还不清楚其内部实现细节,比如: 它是如 ...

  2. 写给自己的 SOA 和 RPC 理解

    1.SOA SOA(Service-Oriented Architecture)面向服务架构,将应用程序不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和契约联系起来. SOA 不是 ...

  3. c#打开颜色对话框

    用button时间 调出颜色对话框来: private void btnForeColor_Click(object sender, EventArgs e)        {            ...

  4. 面试官:优化代码中大量的if/else,你有什么方案?

    一个快速迭代的项目,时间久了之后,代码中可能会充斥着大量的if/else,嵌套6.7层,一个函数几百行,简!直!看!死!人! 这个无限循环嵌套,只是总循环的一部分...我已经绕晕在黄桷湾立交 仔细数了 ...

  5. 华为企业级AS111-S,比较垃圾的地方

    今天换了一个华为企业级AS111-S 路由器,比较垃圾的地方: 1. 网页管理界面是https,却用一个无效的证书,chrome直接不能访问,IE可以访问,但第一次登陆改密码的时候就出错了. 然后怎么 ...

  6. poi坑点(springboot)

    工作上需要写了一个将数据库数据生成excel表的接口,在此过程中遇到了一些坑点,现在此纪录 PS:一部分可能是因为我没用明白 1. 样式问题 自动调整尽量不要使用,部分列留白过多,空列列宽过窄,可能是 ...

  7. cmake设定boost python3

    在mac上操作的.python3是anaconda环境下装的,3.7.1. boost是用brew装的,1.71.0版本. 按照FindBoost.cmake官方写法的CMakeLists.txt: ...

  8. HDU 1372 Knight Moves 题解

    Knight Moves Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tota ...

  9. system.exit(int status)中status值不同时的区别

    status为0时为正常退出程序,也就是结束当前正在运行中的java虚拟机. status为非0的其他整数(包括负数,一般是1或者-1),表示非正常退出当前程序. 可以明确的是,无论status是什么 ...

  10. 中国网络安全行业分类及全景图2019H1

    中国网络安全行业分类及全景图2019H1   概述 中国网络安全行业分类及全景图: 一级分类包含了端点安全.网络安全.应用安全.数据安全.身份与访问管理和安全管理六个一级分类,这些一级分类分别对应了网 ...