这种方式是最常用的,也是最推荐使用的。除此之外,还有一种声明方式basePackageClasses,它使用的是类的 Class 字节码:
@Repeatable(ComponentScans.class)public @interfaceComponentScan { @AliasFor("basePackages")String[] value()default {}; @AliasFor("value")String[] basePackages()default {};/** * Type-safe alternative to {@link #basePackages} for specifying the packages * to scan for annotated components. The package of each class specified will be scanned. * <p>Consider creating a special no-op marker class or interface in each package * that serves no purpose other than being referenced by this attribute. */Class<?>[] basePackageClasses()default {};// ...}
它的这个 basePackageClasses 属性,可以传入一组 Class 进去,它代表的意思,是扫描传入的这些 Class 所在包及子包下的所有组件。
/** * Indicates whether automatic detection of classes annotated with {@code @Component} * {@code @Repository}, {@code @Service}, or {@code @Controller} should be enabled. */booleanuseDefaultFilters()defaulttrue;
2.2 按注解排除
@Configuration@ComponentScan(basePackages ="com.linkedbear.spring.annotation.f_typefilter", excludeFilters = @ComponentScan.Filter(type =FilterType.ANNOTATION, value =Animal.class))publicclassTypeFilterConfiguration {}
排除型过滤器会排除掉其他过滤规则已经包含进来的 Bean 。
2.3 按类型过滤
@Configuration@ComponentScan(basePackages ="com.linkedbear.spring.annotation.f_typefilter", includeFilters = {@ComponentScan.Filter(type =FilterType.ASSIGNABLE_TYPE, value =Color.class)}, excludeFilters = {@ComponentScan.Filter(type =FilterType.ANNOTATION, value =Animal.class)})publicclassTypeFilterConfiguration {}
编程式自定义过滤,需要编写过滤策略,实现 TypeFilter 接口。这个接口只有一个 match 方法:
@FunctionalInterfacepublicinterfaceTypeFilter { /** * 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 * @throwsIOException in case of I/O failure when reading metadata */booleanmatch(MetadataReader metadataReader,MetadataReaderFactory metadataReaderFactory)throwsIOException;}
/** * Container annotation that aggregates several {@link ComponentScan} annotations. * * <p>Can be used natively, declaring several nested {@link ComponentScan} annotations. * Can also be used in conjunction with Java 8's support for repeatable annotations, * where {@link ComponentScan} can simply be declared several times on the same method, * implicitly generating this container annotation. * * @author Juergen Hoeller * @since 4.3 * @see ComponentScan */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documentedpublic @interfaceComponentScans {ComponentScan[] value();}
其实它就是一次性组合了一堆 @ComponentScan 注解而已了,没啥好说的。
3.2 包扫描的组件名称生成
咱在前面刚学习注解驱动时,就知道默认情况下生成的 bean 的名称是类名的首字母小写形式( Person → person ),可是它为啥就有这个规则呢?这个问题,也可以从 @ComponentScan 注解中找到。
/** * The {@link BeanNameGenerator} class to be used for naming detected components * within the Spring container. * <p>The default value of the {@link BeanNameGenerator} interface itself indicates * that the scanner used to process this {@code @ComponentScan} annotation should * use its inherited bean name generator, e.g. the default * {@link AnnotationBeanNameGenerator} or any custom instance supplied to the * application context at bootstrap time. * @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator) * @see AnnotationBeanNameGenerator * @see FullyQualifiedAnnotationBeanNameGenerator */Class<? extends BeanNameGenerator>nameGenerator()defaultBeanNameGenerator.class;
3.2.1 BeanNameGenerator
从名称上就知道它是 Bean 的名字生成器了,它只有一个 generateBeanName 方法:
/** * Strategy interface for generating bean names for bean definitions. * * @author Juergen Hoeller * @since 2.0.3 */publicinterfaceBeanNameGenerator { /** * 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 */StringgenerateBeanName(BeanDefinition definition,BeanDefinitionRegistry registry);}
/** * Utility method to take a string and convert it to normal Java variable * name capitalization. This normally means converting the first * character from upper case to lower case, but in the (unusual) special * case when there is more than one character and both the first and * second characters are upper case, we leave it alone. * <p> * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays * as "URL". * * @param name The string to be decapitalized. * @return The decapitalized version of the string. */publicstaticStringdecapitalize(String name) {if (name ==null||name.length() ==0) {return name; }if (name.length() >1&&Character.isUpperCase(name.charAt(1)) &&Character.isUpperCase(name.charAt(0))){return name; }char chars[] =name.toCharArray(); chars[0] =Character.toLowerCase(chars[0]);returnnewString(chars); }