Preface
Bean name generator is a very important component of beans system. Its main function is to calculate bean name from certain conditions. If there is a problem, it can be avoided. It can also be rewritten.
/** Map of bean definition objects, keyed by bean name */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256); /** Map of singleton and non-singleton bean names, keyed by dependency type */ private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64); /** Map of singleton-only bean names, keyed by dependency type */ private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64); /** List of bean definition names, in registration order */ private volatile List<String> beanDefinitionNames = new ArrayList<String>(256); /** List of names of manually registered singletons, in registration order */ private volatile Set<String> manualSingletonNames = new LinkedHashSet<String>(16); /** Cached array of bean definition names in case of frozen configuration */ private volatile String[] frozenBeanDefinitionNames;
As can be seen from the above data, bean management is basically based on bean name. So how to get the beanName is an important key. Therefore, it is very important to have a deep understanding of the BeanNameGenerator system
Source code interpretation
BeanNameGenerator
public interface BeanNameGenerator { String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry); }
BeanNameGenerator is a method declaration. The declaration of generateBeanName is not complicated. Passing BeanDefinition and BeanDefinitionRegistry returns a beanname of string type. So this section is relatively simple to read in depth.
DefaultBeanNameGenerator
public class DefaultBeanNameGenerator implements BeanNameGenerator { @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { return BeanDefinitionReaderUtils.generateBeanName(definition, registry); } }
public static String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { return generateBeanName(beanDefinition, registry, false); } public static String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) throws BeanDefinitionStoreException { // ((class <? > beanclassobject). Getname() returns the fully qualified name of class // It could also be a class name String generatedBeanName = definition.getBeanClassName(); if (generatedBeanName == null) { if (definition.getParentName() != null) { //When generatedBeanName is null,parentName is not empty. Named parentName+"$child" generatedBeanName = definition.getParentName() + "$child"; }else if (definition.getFactoryBeanName() != null) { //When generatedBeanName is null,FactoryBeanName is not empty. The name is FactoryBeanName+"$child" 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; // generatedBeanName + "#" + value // isInnerBean is true. Use the system identityHashCode as the value, and false use the self increasing method as the value if (isInnerBean) { // Inner bean: generate identity hashcode suffix. id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition); }else { int counter = -1; // Go to the container to see if there is a bean definition with the same name while (counter == -1 || registry.containsBeanDefinition(id)) { counter++; id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter; } } return id; }
beanName generation rule
AnnotationBeanNameGenerator
public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component"; @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { // Judgment is the implementation of annotated bean definition, which is obtained from annotation. if (definition instanceof AnnotatedBeanDefinition) { String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); // If it is text, it will return the beanName, but it is also possible that the value of annotation is null, and then it will be obtained from buildDefaultBeanName if (StringUtils.hasText(beanName)) { return beanName; } } return buildDefaultBeanName(definition, registry); } protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) { // Get all annotations on a class or method AnnotationMetadata amd = annotatedDef.getMetadata(); // Get the class names of all annotation s Set<String> types = amd.getAnnotationTypes(); String beanName = null; for (String type : types) { // Interpret the field and value in the annotation as a map. The field name is key and value is value AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type); // Determine whether the annotation is valid or not, and whether there is a value in the field as beanName if (isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) { // Get the value of the value field from the annotation, 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; } } } } return beanName; } protected boolean isStereotypeWithNameValue(String annotationType,Set<String> metaAnnotationTypes, Map<String, Object> attributes) { // Judge whether the type of annotation is these three // org.springframework.stereotype.Component // javax.annotation.ManagedBean // javax.inject.Named boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) || (metaAnnotationTypes != null && metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME)) || annotationType.equals("javax.annotation.ManagedBean") || annotationType.equals("javax.inject.Named"); // And value has a value. Will return true return (isStereotype && attributes != null && attributes.containsKey("value")); } protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { return buildDefaultBeanName(definition); } protected String buildDefaultBeanName(BeanDefinition definition) { // Get class name String shortClassName = ClassUtils.getShortName(definition.getBeanClassName()); // Change the first letter of class name from uppercase to lowercase return Introspector.decapitalize(shortClassName); } }
The scanned notes are
org.springframework.stereotype.Component#value() org.springframework.stereotype.Repository#value() org.springframework.stereotype.Service#value() org.springframework.stereotype.Controller#value() javax.inject.Named#value() javax.annotation.ManagedBean#value()
When there is no value in the value field of the annotation, the first lowercase class name will be the beanName by default
Detailed explanation
Register beannamegenerator object to ApplicationContext
AnnotationConfigApplicationContext
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { this.reader.setBeanNameGenerator(beanNameGenerator); this.scanner.setBeanNameGenerator(beanNameGenerator); getBeanFactory().registerSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator); }
AnnotationConfigWebApplicationContext
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { this.beanNameGenerator = beanNameGenerator; }
Get objects from ApplicationContext through beanName
@Override public Object getBean(String name) throws BeansException { assertBeanFactoryActive(); return getBeanFactory().getBean(name); } @Override public <T> T getBean(String name, Class<T> requiredType) throws BeansException { assertBeanFactoryActive(); return getBeanFactory().getBean(name, requiredType); }
Get objects from BeanFactory through beanName
@Override public Object getBean(String name) throws BeansException { return getBean(name, Object.class); } @Override public <T> T getBean(String name, Class<T> requiredType) throws BeansException { try { if (isSingleton(name)) { return doGetSingleton(name, requiredType); } else { return lookup(name, requiredType); } } catch (NameNotFoundException ex) { throw new NoSuchBeanDefinitionException(name, "not found in JNDI environment"); } catch (TypeMismatchNamingException ex) { throw new BeanNotOfRequiredTypeException(name, ex.getRequiredType(), ex.getActualType()); } catch (NamingException ex) { throw new BeanDefinitionStoreException("JNDI environment", name, "JNDI lookup failed", ex); } } private <T> T doGetSingleton(String name, Class<T> requiredType) throws NamingException { synchronized (this.singletonObjects) { if (this.singletonObjects.containsKey(name)) { Object jndiObject = this.singletonObjects.get(name); if (requiredType != null && !requiredType.isInstance(jndiObject)) { throw new TypeMismatchNamingException( convertJndiName(name), requiredType, (jndiObject != null ? jndiObject.getClass() : null)); } return (T) jndiObject; } T jndiObject = lookup(name, requiredType); this.singletonObjects.put(name, jndiObject); return jndiObject; } }