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. JavaScript学习思维导图

    JS基本概念 JS操作符 JS基本语法 JS数组 JS Date用法 JS 字符串用法 JS编程风格 JS实践

  2. java 手写 jvm高性能缓存

    java 手写 jvm高性能缓存,键值对存储,队列存储,存储超时设置 缓存接口 package com.ws.commons.cache; import java.util.function.Func ...

  3. 用JSON.stringify去实现深拷贝的弊端

    无法处理function啦还有RegExp啦,还有无法处理循环引用对象(这个我还没有test) 就前两点的话 var a={ a:, b:new RegExp('test'), c:function( ...

  4. OKGo vs RxHttpUtils ...

    jeasonlzy/okhttp-OkGohttps://github.com/jeasonlzy/okhttp-OkGo Android OkGo基本操作https://www.jianshu.co ...

  5. Python Django Vue 项目创建

    环境安装忽略,可参考前面个篇幅介绍 1.创建项目 打开pycharm 终端,输入如下,创建项目 # 进入pycharm 项目目录下 cd pyWeb django-admin startproject ...

  6. Docker 0x08: Docker 命令

    目录 Docker 命令 run 与 start 区别 docker 进程相关命令 Restful API Docker 命令 docker命令容易混淆几个 run 与 start 区别 run: 会 ...

  7. Nginx服务加到systemctl

    当我们编译安装nginx服务后,可以用手执行启动脚本也可以作为服务的形式运行. 添加启动文件:vim /usr/lib/systemd/system/nginx.service [Unit] Desc ...

  8. 网络编程(四)--基于udp协议的套接字、socketserver模块

    一.UDP协议(数据报协议) 1.何为udp协议 不可靠传输,”报头”部分一共只有8个字节,总长度不超过65,535字节,正好放进一个IP数据包. 以太网头 ip头                  ...

  9. Linux用户环境配置文件

    用户操作环境配置文件: 从/etc/skel目录复制过来 .bashrc             打开新终端           /etc/bashrc .bash_profile       用户登 ...

  10. HashMap、HashTable 区别

    区别项 HashMap HashTable 继承和实现 public class HashMap<K,V> extends AbstractMap<K,V> implement ...