zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    大战设计模式【13】—— 组合模式
    大战设计模式【12】—— 迭代器模式
    大战设计模式【11】—— 模板方法模式
    大战设计模式【10】—— 外观模式
    linux命令进阶
    ansible普通用户su切换
    Ansible 进阶技巧
    ansible playbook对错误的处理
    ansible示例,离线安装etcd
    (原)centos7安装和使用greenplum4.3.12(详细版)
  • 原文地址:https://www.cnblogs.com/heliusKing/p/11741403.html
Copyright © 2011-2022 走看看