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

原文地址:https://www.cnblogs.com/heliusKing/p/11741403.html

时间: 2024-11-05 21:54:03

Spring中Bean命名源码分析的相关文章

Django中CBV和Restful API中的APIView源码分析

Django中CBV和Restful API中的APIView源码分析 python的Django框架的视图处理可以用FBV, 也可以采用CBV.首先定义一个CBV视图: from django.views import Viewfrom django.http import JsonResponseclass Book(View):    def get(self, request):        ll = [{'key':value}]        return JsonResponse

Spring MVC初始化部分源码分析

首先定位到org.springframework.context.support.AbstractApplicationContext中的refresh()方法: public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh

spring boot 2.0 源码分析(三)

通过上一章的源码分析,我们知道了spring boot里面的listeners到底是什么(META-INF/spring.factories定义的资源的实例),以及它是创建和启动的,今天我们继续深入分析一下SpringApplication实例变量中的run函数中的其他内容.还是先把run函数的代码贴出来: /** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. *

Netty中的ChannelPipeline源码分析

ChannelPipeline在Netty中是用来处理请求的责任链,默认实现是DefaultChannelPipeline,其构造方法如下: 1 private final Channel channel; 2 private final ChannelFuture succeededFuture; 3 private final VoidChannelPromise voidPromise; 4 final AbstractChannelHandlerContext head; 5 final

【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五)Android事件分发机制(上)Touch三个重要方法的处理逻辑][下文简称(五),请先阅读完(五)再阅读本文],我们通过示例和log来分析了Android的事件分发机制.这些,我们只是看到了现象,如果要进一步了解事件分发机制,这是不够的,我们还需要透过现象看本质,去研究研究源码.本文将从源码(基

Spring AOP介绍及源码分析

一.AOP介绍 举个例子来说明一下吧!现在系统中有很多的业务方法,如上传产品信息.修改产品信息.发布公司库等:现在需要对这些方法的执行做性能监控,看每个业务方法的执行时间:在不改变原业务代码的基础上,也许我们会这么做: Offer接口: Offer实现: Offer代理: 我们要通过下面的方式来使用: 上面的例子的输出为: 上面的例子中,OfferProxy实现了IOffer,而所有的业务实现均委托给其成员offer:可以想像,这应该就是最简单的AOP的实现了:但这种方式会存在一个问题:如果有非

spring boot 2.0 源码分析(五)

在上一篇文章中我们详细分析了spring boot是如何准备上下文环境的,今天我们来看一下run函数剩余的内容.还是先把run函数贴出来: /** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @re

Spring整合MyBatis(二)源码分析

在Spring配置Mybatis的文件中我们可以看到如下代码: <!-- 扫描dao --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="org.tarena.note.dao"> </property> MapperScannerConfig

spring事务管理 TransactionProxyFactoryBean源码分析

J2EE,当然离不开事务,事务又当然少不了Spring声明式事务.spring声明式事务,很多码农门,应该和笔者一样,停留在使用上,及仅仅了解点原理.如:Spring事务管理原理"代理+AOP",再深入了解就不太清楚了.一直对声明式事务实现特别感兴趣,今天抽时间,剖析一下下. 1.准备 BeanFactory,及对象生成周期 AOP代理对象生成过程 1.1.BeanFactory 及生命周期 Factory class name 作用 ListableBeanFactory 枚举所有的