@Configuration publicclassSpringConfig { @Bean(value = "person") public Person person01(){ returnnewPerson("zhangsan", 18); }
@Bean(value = "person2") public Person person02(){ returnnewPerson("zhangsan", 18); } }
测试结果
1 2 3 4 5 6 7 8
Person{name='zhangsan', age='18'}
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.vectorx.springannotation.entity.Person' available: expected single matching bean but found 2: person,person2 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1041) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:345) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1090) at com.vectorx.springannotation.SpringAnnotationTest.testBean(SpringAnnotationTest.java:23) <27 internal lines>
很明显,expected single matching bean but found 2 期望匹配 1 个但却找到了 2 个同一类型的 bean ,所以使用 getBean 方法时需要注意使用场景,选择不同的重载方法防止出现不必要的异常问题
/** * Determine whether this filter matches for the class described by * the given metadata. * @param metadataReader the metadata reader for the target class * @param metadataReaderFactory a factory for obtaining metadata readers * for other classes (such as superclasses and interfaces) * @return whether this filter matches * @throws IOException in case of I/O failure when reading metadata */ booleanmatch(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException;
@param metadataReader the metadata reader for the target class @param metadataReaderFactory a factory for obtaining metadata readers for other classes(such as superclasses and interfaces)
/** * Specifies the name of the scope to use for the annotated component/bean. * <p>Defaults to an empty string ({@code ""}) which implies * {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}. * @since 4.2 * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE * @see ConfigurableBeanFactory#SCOPE_SINGLETON * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION * @see #value */ @AliasFor("value") String scopeName()default"";
singleton:单实例的(默认值)。IOC 容器启动会调用方法创建对象放到 IOC 容器中。以后每次获取就是直接从容器(map.get())中拿,
我们可以分别添加打印语句看下输出的先后情况
测试 1
配置类:配置 singleton 单实例
1 2 3 4 5 6 7 8 9 10
@Configuration publicclassSpringConfig2 { @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) @Bean("person") public Person person(){ System.out.println("给 IOC 容器中添加 Person ..."); returnnewPerson("lisi", 25); } }
@Configuration publicclassSpringConfig2 { @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Bean("person") public Person person(){ System.out.println("给 IOC 容器中添加 Person ..."); returnnewPerson("lisi", 25); } }
测试类不变
测试结果
1 2 3 4
IOC 容器创建完成... 给 IOC 容器中添加 Person ... 给 IOC 容器中添加 Person ... false
思考:为什么是针对单实例而言的,道理很容易理解。多实例每次都是从容器中获取实例时,都是创建新的对象,所以没有什么懒加载可言。而目前我们通过测试发现,单实例默认情况下在 IOC 容器创建之前就创建好实例对象了。如果我们既想要单实例对象又不想在 IOC 容器创建之前创建对象实例,那应该怎么办呢?这时候 @Lazy 注解就应用而生了
我们对配置类略作修改,测试类不动
1 2 3 4 5 6 7 8 9 10 11
@Configuration publicclassSpringConfig2 { @Lazy @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) @Bean("person") public Person person(){ System.out.println("给 IOC 容器中添加 Person ..."); returnnewPerson("lisi", 25); } }
/** * All {@link Condition}s that must {@linkplain Condition#matches match} * in order for the component to be registered. */ Class<? extendsCondition>[] value();
}
从源码中可以得知,@Conditional 注解可以作用在类或方法上,其值为一个 Class[] 即 Class 类型的数组,而 Class 类型必须是 Condition 的子类,那 Condition 又是什么呢?继续看源码
Condition
1 2 3 4 5 6 7 8 9 10 11 12 13
publicinterfaceCondition {
/** * Determine if the condition matches. * @param context the condition context * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class} * or {@link org.springframework.core.type.MethodMetadata method} being checked. * @return {@code true} if the condition matches and the component can be registered * or {@code false} to veto registration. */ booleanmatches(ConditionContext context, AnnotatedTypeMetadata metadata);
/** * Return the {@link BeanDefinitionRegistry} that will hold the bean definition * should the condition match, or {@code null} if the registry is not available. */ BeanDefinitionRegistry getRegistry();
/** * Return the {@link ConfigurableListableBeanFactory} that will hold the bean * definition should the condition match, or {@code null} if the bean factory * is not available. */ ConfigurableListableBeanFactory getBeanFactory();
/** * Return the {@link Environment} for which the current application is running, * or {@code null} if no environment is available. */ Environment getEnvironment();
/** * Return the {@link ResourceLoader} currently being used, or {@code null} if * the resource loader cannot be obtained. */ ResourceLoader getResourceLoader();
/** * Return the {@link ClassLoader} that should be used to load additional classes, * or {@code null} if the default classloader should be used. */ ClassLoader getClassLoader();
/** * Determine whether the underlying element has an annotation or meta-annotation * of the given type defined. * <p>If this method returns {@code true}, then * {@link #getAnnotationAttributes} will return a non-null Map. * @param annotationName the fully qualified class name of the annotation * type to look for * @return whether a matching annotation is defined */ booleanisAnnotated(String annotationName);
/** * Retrieve the attributes of the annotation of the given type, if any (i.e. if * defined on the underlying element, as direct annotation or meta-annotation), * also taking attribute overrides on composed annotations into account. * @param annotationName the fully qualified class name of the annotation * type to look for * @return a Map of attributes, with the attribute name as key (e.g. "value") * and the defined attribute value as Map value. This return value will be * {@code null} if no matching annotation is defined. */ Map<String, Object> getAnnotationAttributes(String annotationName);
/** * Retrieve the attributes of the annotation of the given type, if any (i.e. if * defined on the underlying element, as direct annotation or meta-annotation), * also taking attribute overrides on composed annotations into account. * @param annotationName the fully qualified class name of the annotation * type to look for * @param classValuesAsString whether to convert class references to String * class names for exposure as values in the returned Map, instead of Class * references which might potentially have to be loaded first * @return a Map of attributes, with the attribute name as key (e.g. "value") * and the defined attribute value as Map value. This return value will be * {@code null} if no matching annotation is defined. */ Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);
/** * Retrieve all attributes of all annotations of the given type, if any (i.e. if * defined on the underlying element, as direct annotation or meta-annotation). * Note that this variant does <i>not</i> take attribute overrides into account. * @param annotationName the fully qualified class name of the annotation * type to look for * @return a MultiMap of attributes, with the attribute name as key (e.g. "value") * and a list of the defined attribute values as Map value. This return value will * be {@code null} if no matching annotation is defined. * @see #getAllAnnotationAttributes(String, boolean) */ MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);
/** * Retrieve all attributes of all annotations of the given type, if any (i.e. if * defined on the underlying element, as direct annotation or meta-annotation). * Note that this variant does <i>not</i> take attribute overrides into account. * @param annotationName the fully qualified class name of the annotation * type to look for * @param classValuesAsString whether to convert class references to String * @return a MultiMap of attributes, with the attribute name as key (e.g. "value") * and a list of the defined attribute values as Map value. This return value will * be {@code null} if no matching annotation is defined. * @see #getAllAnnotationAttributes(String) */ MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);
}
isAnnotated:判断元素上是否定义了指定类型的注解
getAnnotationAttributes:获取指定类型的注解的属性
getAllAnnotationAttributes:获取指定类型的所有注解的所有属性
通过对源码的阅读,如果我们要实现下列功能就轻而易举了:根据操作系统的不同,注册不同的 Person 类