在基于 XML 的配置元信息中,开发人员可用 id 或者 name 属性来规定 Bean 的标识符。通常 Bean 的标识符由字母组成,允许出现特殊字符。如果要想引入 Bean 的别名的话,可在 name 属性使用半角逗号(“,”)或分号(“;”)来间隔。Bean 的 id 或 name 属性并非必须指定,如果留空的话,容器会为 Bean 自动生成一个唯一的名称。Bean 的命名尽管没有限制,不过官方建议采用驼峰的方式,更符合 Java 的命名约定。
AnnotationBeanNameGenerator: 基于注解扫描的 BeanNameGenerator 实现,起始于 Spring Framework 2.5,关联的官方文档:
With component scanning in the classpath, Spring generates bean names for unnamed components,following the rules described earlier: essentially,taking
the simple class name and turning its initial character to lower-case. However, in the (unusual) special case when there is more than one character and
both the first and second characters are upper case, the original casing gets preserved. These are the same rules as defined by
java.beans.Introspector.decapitalize (which Spring uses here).
/** * 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);
/** * Default implementation of the {@link BeanNameGenerator} interface, delegating to * {@link BeanDefinitionReaderUtils#generateBeanName(BeanDefinition, BeanDefinitionRegistry)}. * * @author Juergen Hoeller * @since 2.0.3 */ publicclassDefaultBeanNameGeneratorimplementsBeanNameGenerator{
/** * A convenient constant for a default {@code DefaultBeanNameGenerator} instance, * as used for {@link AbstractBeanDefinitionReader} setup. * @since 5.2 */ publicstaticfinal DefaultBeanNameGenerator INSTANCE = new DefaultBeanNameGenerator();
/** * {@link org.springframework.beans.factory.support.BeanNameGenerator} * implementation for bean classes annotated with the * {@link org.springframework.stereotype.Component @Component} annotation * or with another annotation that is itself annotated with * {@link org.springframework.stereotype.Component @Component} as a * meta-annotation. For example, Spring's stereotype annotations (such as * {@link org.springframework.stereotype.Repository @Repository}) are * themselves annotated with * {@link org.springframework.stereotype.Component @Component}. * * <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and * JSR-330's {@link javax.inject.Named} annotations, if available. Note that * Spring component annotations always override such standard annotations. * * <p>If the annotation's value doesn't indicate a bean name, an appropriate * name will be built based on the short name of the class (with the first * letter lower-cased). For example: * * <pre class="code">com.xyz.FooServiceImpl -> fooServiceImpl</pre> * * @author Juergen Hoeller * @author Mark Fisher * @since 2.5 * @see org.springframework.stereotype.Component#value() * @see org.springframework.stereotype.Repository#value() * @see org.springframework.stereotype.Service#value() * @see org.springframework.stereotype.Controller#value() * @see javax.inject.Named#value() */ publicclassAnnotationBeanNameGeneratorimplementsBeanNameGenerator{
/** * A convenient constant for a default {@code AnnotationBeanNameGenerator} instance, * as used for component scanning purposes. * @since 5.2 */ publicstaticfinal AnnotationBeanNameGenerator INSTANCE = new AnnotationBeanNameGenerator();
@Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry){ if (definition instanceof AnnotatedBeanDefinition) { 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); }
/** * Derive a bean name from one of the annotations on the class. * @param annotatedDef the annotation-aware bean definition * @return the bean name, or {@code null} if none is found */ @Nullable protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef){ AnnotationMetadata amd = annotatedDef.getMetadata(); Set<String> types = amd.getAnnotationTypes(); String beanName = null; for (String type : types) { AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type); if (attributes != null && isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) { Object value = attributes.get("value"); if (value instanceof String) { String strVal = (String) value; if (StringUtils.hasLength(strVal)) { if (beanName != null && !strVal.equals(beanName)) { thrownew IllegalStateException("Stereotype annotations suggest inconsistent " + "component names: '" + beanName + "' versus '" + strVal + "'"); } beanName = strVal; } } } } return beanName; }
/** * Check whether the given annotation is a stereotype that is allowed * to suggest a component name through its annotation {@code value()}. * @param annotationType the name of the annotation class to check * @param metaAnnotationTypes the names of meta-annotations on the given annotation * @param attributes the map of attributes for the given annotation * @return whether the annotation qualifies as a stereotype with component name */ protectedbooleanisStereotypeWithNameValue(String annotationType, Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes){
/** * Derive a default bean name from the given bean definition. * <p>The default implementation delegates to {@link #buildDefaultBeanName(BeanDefinition)}. * @param definition the bean definition to build a bean name for * @param registry the registry that the given bean definition is being registered with * @return the default bean name (never {@code null}) */ protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry){ return buildDefaultBeanName(definition); }
/** * Derive a default bean name from the given bean definition. * <p>The default implementation simply builds a decapitalized version * of the short class name: e.g. "mypackage.MyJdbcDao" -> "myJdbcDao". * <p>Note that inner classes will thus have names of the form * "outerClassName.InnerClassName", which because of the period in the * name may be an issue if you are autowiring by name. * @param definition the bean definition to build a bean name for * @return the default bean name (never {@code null}) */ protected String buildDefaultBeanName(BeanDefinition definition){ String beanClassName = definition.getBeanClassName(); Assert.state(beanClassName != null, "No bean class name set"); String shortClassName = ClassUtils.getShortName(beanClassName); return Introspector.decapitalize(shortClassName); }
/** * The value may indicate a suggestion for a logical component name, * to be turned into a Spring bean in case of an autodetected component. * @return the suggested component name, if any (or empty String otherwise) */ @AliasFor(annotation = Component.class) String value()default "";
}
我们再来看它的生成方式:
1 2 3 4 5 6 7 8 9 10 11 12
@Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry){ if (definition instanceof AnnotatedBeanDefinition) { 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); }
// 配置 XML 信息 // 启动 Spring 应用上下文 BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml"); User user = (User) beanFactory.getBean("user"); System.out.println("XML 方式注册 Spring Bean,User:" + user); } }
Java 注解配置元信息
@Bean
@Component
@Import
@Bean 方式:
这里的 new AnnotationConfigApplicationContext(Config.class); 实现了将 Config 类配置为 Spring 的 Bean 对象,并且会将 Config 类中标记为 @Bean 注解的类也加载成 Bean 对象。
new AnnotationConfigApplicationContext(Config.class);传参的方式相比无参构造的话省去了 refresh() 方法。他其实内部执行两步:
// 配置 XML 信息 // 启动 Spring 应用上下文 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class); System.out.println("Configs:"+applicationContext.getBeansOfType(Config2.class));
}
// 在配置类上加上组件扫描注解,等效于在 XML 配置文件中开启扫描注解 @ComponentScan("tech.fengjian.ioc.container.overview.dependency.lookup") publicstaticclassConfig{
@Bean public User user(){ User user = new User(); user.setId(11L); user.setName("林黛玉"); user.setAge(15); return user; } }
@Component publicstaticclassConfig2{
} }
@Import 方式
在 Spring 中,当我们使用 applicationContext.register(Config.class) 方法手动注册配置类时,该配置类中定义的 Bean 也不会被后续导入的配置类所覆盖。