zoukankan      html  css  js  c++  java
  • Spring源码解析之beanName

    beanName的确定

    spring 在实例化bean之前,首先需要解析bean的配置,bean的配置要么通过标签配置在xml文件中,要么通过注解的形式声明一个bean.

    要保存每一个bean解析后的配置,以便方便后面的bean的实例化。大家肯定都能想到配置信息必然是保存在map数据结构中,那么key(也就是beanName)是如何确定的?

    1) 首先看bean中有没有配置id属性,如果配置了,那么以该id作为beanName;

    2) 如果没有配置id属性,但是配置了name属性,那么以该属性中的第一个name(name的value可以配置多个,以逗号分隔)作为beanNme;

    3) 如果以上两者都未配置,那么检查bean是否配置了class属性,如果配置了,以class的全路径作为beanName;否则检查是否有parent或者factrory-bean,如果配置了parent,那么以parentBeanName+"$child" 作为beanName;否则以factoryBeanName+"$created"作为beanName;

    4) 如果bean未配置在xml文件中,而是通过注解的形式声明,如:@Service,@Components等,那么首先检测是否配置了value属性,如果配置了,那么以该value作为beanName,否则以class 的shortName作为beanName

    如果beanName重复了怎么办

    对于同一个beans标签下不能有重复的beanName,在允许覆盖的情况下不同beans下可以有重复的beanName,如果不允许覆盖,那么会抛出异常

    beanName 的解析过程

    beanName的确定

    BeanDefinitionParserDelegate.java
    
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
    		String id = ele.getAttribute(ID_ATTRIBUTE);
    		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    
    		List<String> aliases = new ArrayList<String>();
    		if (StringUtils.hasLength(nameAttr)) {
    			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    			aliases.addAll(Arrays.asList(nameArr));
    		}
    
    		String beanName = id;
    		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
    			beanName = aliases.remove(0);
    			if (logger.isDebugEnabled()) {
    				logger.debug("No XML 'id' specified - using '" + beanName +
    						"' as bean name and " + aliases + " as aliases");
    			}
    		}
    
    		if (containingBean == null) {
    			checkNameUniqueness(beanName, aliases, ele);
    		}
    
    		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    		if (beanDefinition != null) {
    			if (!StringUtils.hasText(beanName)) {
    				try {
    					if (containingBean != null) {
    						beanName = BeanDefinitionReaderUtils.generateBeanName(
    								beanDefinition, this.readerContext.getRegistry(), true);
    					}
    					else {
    						beanName = this.readerContext.generateBeanName(beanDefinition);
    						// Register an alias for the plain bean class name, if still possible,
    						// if the generator returned the class name plus a suffix.
    						// This is expected for Spring 1.2/2.0 backwards compatibility.
    						String beanClassName = beanDefinition.getBeanClassName();
    						if (beanClassName != null &&
    								beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
    								!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
    							aliases.add(beanClassName);
    						}
    					}
    					if (logger.isDebugEnabled()) {
    						logger.debug("Neither XML 'id' nor 'name' specified - " +
    								"using generated bean name [" + beanName + "]");
    					}
    				}
    				catch (Exception ex) {
    					error(ex.getMessage(), ele);
    					return null;
    				}
    			}
    			String[] aliasesArray = StringUtils.toStringArray(aliases);
    			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    		}
    
    		return null;
    	}
    

    从代码中可以看出beanName 优先选择id;如果没有设置id,但是设置了name(name属性可以设置多个名称,以逗号或者分号分隔),那么以name属性中的第一个name作为beanName

    如果未设置id且未设置name,如何生成beanName?

    分为两种情况:

    1. bean配置在xml文件中

      如果设置了class: 那么以class全路径作为beanName

      如果没有设置class:检查是否设置了parent属性,如果设置了parent,则beanName=parentName+"$child";否则检查是否设置了factory-bean属性,如果设置了,那么已factory-bean + "$created" 作为beanName

      检测生成的beanName是否已被注册,如果被注册,那么以一个数字作为后缀区分

    public static String generateBeanName(
    			BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
    			throws BeanDefinitionStoreException {
    
    		String generatedBeanName = definition.getBeanClassName();
    		if (generatedBeanName == null) {
    			if (definition.getParentName() != null) {
    				generatedBeanName = definition.getParentName() + "$child";
    			}
    			else if (definition.getFactoryBeanName() != null) {
    				generatedBeanName = definition.getFactoryBeanName() + "$created";
    			}
    		}
    		if (!StringUtils.hasText(generatedBeanName)) {
    			throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
    					"'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
    		}
    
    		String id = generatedBeanName;
    		if (isInnerBean) {
    			// Inner bean: generate identity hashcode suffix.
    			id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
    		}
    		else {
    			// Top-level bean: use plain class name.
    			// Increase counter until the id is unique.
    			int counter = -1;
    			while (counter == -1 || registry.containsBeanDefinition(id)) {
    				counter++;
    				id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
    			}
    		}
    		return id;
    	}
    
    1. bean通过注解声明,如(@Service, @Component, @Repository)

      首先看是否配置了value属性,如(@Service(value="aa")),如果设置了,则以value的值作为beanName;

      否则获取beanClassName的shortName,如class的全路径是com.bluesky.service.UserService,则解析到的beanName 是 userService

    public static String getShortName(String className) {
    		Assert.hasLength(className, "Class name must not be empty");
    		int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
    		int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR);
    		if (nameEndIndex == -1) {
    			nameEndIndex = className.length();
    		}
    		String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
    		shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
    		return shortName;
    	}
    

    beanName 的唯一性

    beanName 要求在同一层级中(如在同一个beans标签中)是唯一的,但是在不同层级中是可以重复的,在重复的并且允许覆盖的情况下,会覆盖。checkNameUniqueness方法中可以看出

    /**
      * Stores all used bean names so we can enforce uniqueness on a per
      * beans-element basis. Duplicate bean ids/names may not exist within the
      * same level of beans element nesting, but may be duplicated across levels.
      */
    private final Set<String> usedNames = new HashSet<String>();
    
    protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) {
    		String foundName = null;
    
    		if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) {
    			foundName = beanName;
    		}
    		if (foundName == null) {
    			foundName = CollectionUtils.findFirstMatch(this.usedNames, aliases);
    		}
    		if (foundName != null) {
    			error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement);
    		}
    
    		this.usedNames.add(beanName);
    		this.usedNames.addAll(aliases);
    	}
    

    从DefaultBeanDefinitionDocumentReader的processBeanDefinition 方法跟进BeanDefinitionReaderUtils.registerBeanDefinition方法,最后找到bean注册的方法

    /**
    	 * Process the given bean element, parsing the bean definition
    	 * and registering it with the registry.
    	 */
    	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    		if (bdHolder != null) {
    			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
    			try {
    				// Register the final decorated instance.
    				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    			}
    			catch (BeanDefinitionStoreException ex) {
    				getReaderContext().error("Failed to register bean definition with name '" +
    						bdHolder.getBeanName() + "'", ele, ex);
    			}
    			// Send registration event.
    			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    		}
    	}
    	
    	public static void registerBeanDefinition(
    			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    			throws BeanDefinitionStoreException {
    
    		// Register bean definition under primary name.
    		String beanName = definitionHolder.getBeanName();
    		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
    		// Register aliases for bean name, if any.
    		String[] aliases = definitionHolder.getAliases();
    		if (aliases != null) {
    			for (String alias : aliases) {
    				registry.registerAlias(beanName, alias);
    			}
    		}
    	}
    	
    	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    			throws BeanDefinitionStoreException {
    
    		Assert.hasText(beanName, "Bean name must not be empty");
    		Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    
    		if (beanDefinition instanceof AbstractBeanDefinition) {
    			try {
    				((AbstractBeanDefinition) beanDefinition).validate();
    			}
    			catch (BeanDefinitionValidationException ex) {
    				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    						"Validation of bean definition failed", ex);
    			}
    		}
    
    		BeanDefinition oldBeanDefinition;
    
    		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
    		if (oldBeanDefinition != null) {
    			if (!isAllowBeanDefinitionOverriding()) {
    				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
    						"': There is already [" + oldBeanDefinition + "] bound.");
    			}
    			else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
    				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
    				if (this.logger.isWarnEnabled()) {
    					this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
    							"' with a framework-generated bean definition: replacing [" +
    							oldBeanDefinition + "] with [" + beanDefinition + "]");
    				}
    			}
    			else if (!beanDefinition.equals(oldBeanDefinition)) {
    				if (this.logger.isInfoEnabled()) {
    					this.logger.info("Overriding bean definition for bean '" + beanName +
    							"' with a different definition: replacing [" + oldBeanDefinition +
    							"] with [" + beanDefinition + "]");
    				}
    			}
    			else {
    				if (this.logger.isDebugEnabled()) {
    					this.logger.debug("Overriding bean definition for bean '" + beanName +
    							"' with an equivalent definition: replacing [" + oldBeanDefinition +
    							"] with [" + beanDefinition + "]");
    				}
    			}
    			this.beanDefinitionMap.put(beanName, beanDefinition);
    		}
    		else {
    			if (hasBeanCreationStarted()) {
    				// Cannot modify startup-time collection elements anymore (for stable iteration)
    				synchronized (this.beanDefinitionMap) {
    					this.beanDefinitionMap.put(beanName, beanDefinition);
    					List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
    					updatedDefinitions.addAll(this.beanDefinitionNames);
    					updatedDefinitions.add(beanName);
    					this.beanDefinitionNames = updatedDefinitions;
    					if (this.manualSingletonNames.contains(beanName)) {
    						Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
    						updatedSingletons.remove(beanName);
    						this.manualSingletonNames = updatedSingletons;
    					}
    				}
    			}
    			else {
    				// Still in startup registration phase
    				this.beanDefinitionMap.put(beanName, beanDefinition);
    				this.beanDefinitionNames.add(beanName);
    				this.manualSingletonNames.remove(beanName);
    			}
    			this.frozenBeanDefinitionNames = null;
    		}
    
    		if (oldBeanDefinition != null || containsSingleton(beanName)) {
    			resetBeanDefinition(beanName);
    		}
    	}
    	
    	public void registerAlias(String name, String alias) {
    		Assert.hasText(name, "'name' must not be empty");
    		Assert.hasText(alias, "'alias' must not be empty");
    		if (alias.equals(name)) {
    			this.aliasMap.remove(alias);
    		}
    		else {
    			String registeredName = this.aliasMap.get(alias);
    			if (registeredName != null) {
    				if (registeredName.equals(name)) {
    					// An existing alias - no need to re-register
    					return;
    				}
    				if (!allowAliasOverriding()) {
    					throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
    							name + "': It is already registered for name '" + registeredName + "'.");
    				}
    			}
    			checkForAliasCircle(name, alias);
    			this.aliasMap.put(alias, name);
    		}
    	}
    
  • 相关阅读:
    Working with macro signatures
    Reset and Clear Recent Items and Frequent Places in Windows 10
    git分支演示
    The current .NET SDK does not support targeting .NET Core 2.1. Either target .NET Core 2.0 or lower, or use a version of the .NET SDK that supports .NET Core 2.1.
    Build website project by roslyn through devenv.com
    Configure environment variables for different tools in jenkins
    NUnit Console Command Line
    Code Coverage and Unit Test in SonarQube
    头脑王者 物理化学生物
    头脑王者 常识,饮食
  • 原文地址:https://www.cnblogs.com/tracer-dhy/p/9866600.html
Copyright © 2011-2022 走看看