Spring的ComponentScan注解
1 源码
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default "**/*.class";
boolean useDefaultFilters() default true;
ComponentScan.Filter[] includeFilters() default {};
ComponentScan.Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
2 说明
作用:
用于指定创建容器时要扫描的包。该注解在指定扫描的位置时,可以指定包名,也可以指定扫描的类。同时支持定义扫描规则,例如包含哪些或者排除哪些。同时,它还支持自定义Bean的命名规则
属性:
value:
用于指定要扫描的包。当指定了包的名称之后,spring会扫描指定的包及其子包下的所有类。
basePackages:
它和value作用是一样的。
basePackageClasses:
指定具体要扫描的类的字节码。
nameGenrator:
指定扫描bean对象存入容器时的命名规则。详情请参考第五章第4小节的BeanNameGenerator及其实现类。
scopeResolver:
用于处理并转换检测到的Bean的作用范围。
soperdProxy:
用于指定bean生成时的代理方式。默认是Default,则不使用代理。
ScopedProxyMode枚举。
resourcePattern:
用于指定符合组件检测条件的类文件,默认是包扫描下的 **/*.class
useDefaultFilters:
是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的。
includeFilters:
自定义组件扫描的过滤规则,用以扫描组件。
FilterType有5种类型:
ANNOTATION, 注解类型 默认
ASSIGNABLE_TYPE,指定固定类
ASPECTJ, ASPECTJ类型
REGEX,正则表达式
CUSTOM,自定义类型
excludeFilters:
自定义组件扫描的排除规则。
lazyInit:
组件扫描时是否采用懒加载 ,默认不开启。
3 自动检测类并注册 Bean 定义
Spring 可以自动检测构造型类,并向ApplicationContext
注册相应的BeanDefinition
实例。例如,以下两个类别有资格进行这种自动检测:
@Service
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
@Repository
public class JpaMovieFinder implements MovieFinder {
// implementation elided for clarity
}
要自动检测这些类并注册相应的 bean,需要将@ComponentScan
添加到@Configuration
类中,其中basePackages
属性是两个类的公共父包。 (或者,您可以指定一个逗号分隔,分号分隔或空格分隔的列表,其中包括每个类的父包.)
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
...
}
以下替代方法使用 XML:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.example"/>
</beans>
此外,当您使用 component-scan 元素时,AutowiredAnnotationBeanPostProcessor
和CommonAnnotationBeanPostProcessor
都隐式包含在内。这意味着两个组件将被自动检测并连接在一起,而所有这些都不需要 XML 中提供任何 bean 配置元数据。
您可以通过包含 Comments 设置属性false
来禁用AutowiredAnnotationBeanPostProcessor
和CommonAnnotationBeanPostProcessor
的注册。
4 使用过滤器自定义扫描
默认情况下,唯一检测到的候选组件是用@Component
,@Repository
,@Service
,@Controller
Comments 的类或本身用@Component
Comments 的定制 Comments。但是,您可以通过应用自定义过滤器来修改和扩展此行为。将它们添加为@ComponentScan
注解的includeFilters
或excludeFilters
参数(或component-scan
元素的include-filter
或exclude-filter
子元素)。每个过滤器元素都需要type
和expression
属性。下表描述了过滤选项:
过滤器类型
Filter Type | Example Expression | Description |
---|---|---|
annotation (default) | org.example.SomeAnnotation |
在目标组件的类型级别上存在的 Comments。 |
assignable | org.example.SomeClass |
目标组件可分配给(扩展或实现)的类(或接口)。 |
aspectj | org.example..*Service+ |
目标组件要匹配的 AspectJ 类型表达式。 |
regex | org\.example\.Default.* |
要与目标组件类名称匹配的正则表达式。 |
custom | org.example.MyTypeFilter |
org.springframework.core.type .TypeFilter 接口的自定义实现。 |
以下示例显示了忽略所有@Repository
注解并使用“存根”存储库的配置:
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
...
}
以下清单显示了等效的 XML:
<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
您还可以通过在 Comments 上设置useDefaultFilters=false
或通过提供use-default-filters="false"
作为<component-scan/>
元素的属性来禁用默认过滤器。实际上,这将禁用对带有@Component
,@Repository
,@Service
,@Controller
或@Configuration
Comments 的类的自动检测。
5 命名自动检测的组件
在扫描过程中自动检测到某个组件时,其 bean 名称由该扫描器已知的BeanNameGenerator
策略生成。默认情况下,任何包含名称value
的 Spring 构造型 Comments(@Component
,@Repository
,@Service
和@Controller
)都会将该名称提供给相应的 bean 定义。
如果这样的 Comments 不包含名称value
或任何其他检测到的组件(例如,由自定义过滤器发现的组件),则缺省 bean 名称生成器将返回不使用大写字母的非限定类名称。例如,如果检测到以下组件类,则名称将为myMovieLister
和movieFinderImpl
:
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
如果不想依赖默认的 Bean 命名策略,则可以提供自定义 Bean 命名策略。首先,实现BeanNameGenerator接口,并确保包括默认的 no-arg 构造函数。然后,在配置扫描器时提供完全限定的类名,如以下示例 Comments 和 Bean 定义所示:
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
...
}
其中DefaultBeanNameGenerator是给资源文件加载bean时使用(BeanDefinitionReader中使用);AnnotationBeanNameGenerator是为了处理注解生成bean name的情况。
6 提供自动检测组件的范围
通常,与 SpringManagement 的组件一样,自动检测到的组件的默认且最常见的范围是singleton
。但是,有时您需要由@Scope
Comments 指定的其他范围。您可以在注解中提供范围的名称,如以下示例所示:
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
@Scope
Comments 仅在具体的 bean 类(对于带 Comments 的组件)或工厂方法(对于@Bean
方法)上进行内省。与 XML bean 定义相反,没有 bean 定义继承的概念,并且在类级别的继承层次结构与元数据目的无关。
要提供用于范围解析的自定义策略,而不是依赖于基于 Comments 的方法,您可以实现ScopeMetadataResolver接口。确保包括默认的无参数构造函数。然后,可以在配置扫描程序时提供完全限定的类名,如以下 Comments 和 Bean 定义示例所示:
@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
...
}
<beans>
<context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>
使用某些非单作用域时,可能有必要为作用域对象生成代理。推理在范围 bean 作为依赖项中描述。为此,在 component-scan 元素上可以使用 scoped-proxy 属性。三个可能的值是:no
,interfaces
和targetClass
。例如,以下配置产生标准的 JDK 动态代理:
@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
...
}
<beans>
<context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
</beans>