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);
    		}
    	}
    
  • 相关阅读:
    mysql面试知识点
    计算机网络
    BFS
    拓扑排序
    双指针
    回溯算法
    hash表 算法模板和相关题目
    桶排序及其应用
    滑动窗口
    贪心算法
  • 原文地址:https://www.cnblogs.com/tracer-dhy/p/9866600.html
Copyright © 2011-2022 走看看