@ComponentScan注解是扫描包,主要是通过basePackages,includeFilters,excludeFilters这三个属性确定扫描哪些类
basePackages(value): 确定扫描哪些包,String[]类型的
includeFilters:包含,值是一个Filter[]数组
excludeFilters:排除,值是一个Filter[]数组
useDefaultFilters:是否保留默认的includeFilters(其中包含@Component)
@Filter是@ComponentScan注解的内部类注解
@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 ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
boolean useDefaultFilters() default true;
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class>[] value() default {};
@AliasFor("value")
Class>[] classes() default {};
String[] pattern() default {};
}
}
这里有一点需要注意@ComponentScan没有标注@Component注解,如果spring包扫面不到该类,那就要想办法将该类放到beanDefinitionMap中 ,比如bean工厂的后置处理器
@Filter中有4个属性,分别是
type:是枚举类,类型共五种
value:根据type的不同,这个表达式的配置方式也不同
classes:当type为ANNOTATION或者ASSIGNABLE_TYPE时,我们可以将对应的类配置在value属性中也可以配置在calsses属性中
pattern:当type是REGEX时,可以将表达式配置的pattern中
type的5中类型
ANNOTATION:指定扫描某个注解的类,这里的注解也可以是我们自定义的
ASSIGNABLE_TYPE:指定扫描某个类及子类,某个类可以是接口也可以是普通的类,如果是接口那么该类不会放进容器中,普通类可以放到容器中,子类(实现类)都会放到容器中
ASPECTJ:指定扫描AspectJ表达式相匹配的类(不常用)
REGEX:指定扫描符合正则表达式的类(不常用)
CUSTOM:指定扫描自定义的实现了org.springframework.core.type.filter.TypeFilter接口的类
ASPECTJ和REGEX两个不常用这里就不举例了,剩下的ANNOTATION, ASSIGNABLE, CUSTOM举例说明
ANNOTATION注解的方式,自定义一个注解,
@ComponentScan.Filter(type = FilterType.ANNOTATION ,classes = MyInterface.class)
只要标注了@ MyInterface的类都会放到IOC容器中
public class ComponentScanAnnotation {
public static void main(String[] args) {
//查找配置类.配置类有扫描类的路径,满足条件的放到容器中
AnnotationConfigApplicationContext configApplicationContext = new AnnotationConfigApplicationContext (ComponentScanConfiguration.class);
Hello hello = ( Hello ) configApplicationContext.getBean ("hello");
WorldSubClass worldSubClass = ( WorldSubClass ) configApplicationContext.getBean ("worldSubClass");
World world = ( World ) configApplicationContext.getBean ("world");
System.out.println (hello.getName ());
System.out.println (worldSubClass.getName ());
System.out.println (world.toString ());
configApplicationContext.close ();
}
}
@ComponentScan(basePackages = "springlearn.springComponentScan",
includeFilters [email protected](type = FilterType.ANNOTATION ,classes = MyInterface.class)
)
public class ComponentScanConfiguration {
}
@MyInterface
@Data
public class Hello {
private String name;
public Hello() {
this.name = "张三";
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface MyInterface {
}
ASSIGNABLE_TYPE 指定扫描某个类及子类,某个类可以是接口也可以是普通的类,如果是接口那么该接口不会放进容器中,普通类可以放到容器中,子类(实现类)都会放到容器中
修改了includeFilters属性,多了一个Filter Type.ASSIGNABLE_TYPE
@ComponentScan(basePackages = "springlearn.springComponentScan",
includeFilters ={@ComponentScan.Filter(type = FilterType.ANNOTATION ,classes = MyInterface.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE ,classes = World.class)
}
)
public class ComponentScanConfiguration {
}
public interface World {
}
@Data
public class WorldSubClass implements World {
private String name;
public WorldSubClass() {
this.name = "李四";
}
}
自定义CUSTOM
@Data
public class DuoDuo {
private String name;
public DuoDuo() {
this.name = "王五";
}
}
@ComponentScan(basePackages = "springlearn.springComponentScan",
includeFilters ={@ComponentScan.Filter(type = FilterType.ANNOTATION ,classes = MyInterface.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE ,classes = World.class ),
@ComponentScan.Filter(type = FilterType.CUSTOM ,classes = MyTypeFilter.class ),
}
)
public class ComponentScanConfiguration {
}
public class MyTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获得当前类的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获得当前正在扫描的类的资源信息
Resource resource = metadataReader.getResource();
//获得当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//全类名
String name = classMetadata.getClassName();
//如果全类名包含DuoDuo,则放到IOC容器中
if(name.contains("DuoDuo")){
return true;
}
return false;
}
}
下面对@ComponentScan进行源码分析
从ComponentScanAnnotationParser#parse方法入手,spring是如何调用到这个方法是一个比较长的调用链,涉及到最重要的类ConfigurationClassPostProcessor的bean工厂的后置注册器postProcessBeanDefinitionRegistry
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
-> ConfigurationClassPostProcessor#processConfigBeanDefinitions
-> ConfigurationClassPostProcessor#processConfigurationClass
-> ConfigurationClassPostProcessor#doProcessConfigurationClass
->ComponentScanAnnotationParser#parse
上面也是简化的调用链,重点分析ComponentScanAnnotationParser#parse方法
public Set parse(AnnotationAttributes componentScan, final String declaringClass) {
//首先是扫描器, componentScan.getBoolean("useDefaultFilters")代表的就是
//@ComponentScan useDefaultFilters的属性,其作用就是, //ClassPathBeanDefinitionScanner在创建的时候,会自己创建3个includeFilters,其中既有
//@Component,其余两个是jdk自带的,一般不会使用
//useDefaultFilters如果为false,那么就不会创建这3个过滤器
//比如只想扫面自定义注解的类,不扫面@Component注释的类,就要将useDefaultFilters设置//为false
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
Class extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
}
else {
Class extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
//获取includeFilters
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
// typeFiltersFor就会根据传入的类型,上面提到type的5中类型,创建filter
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
// 获取excludeFilters
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
//包名
Set basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
for (Class> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
//默认加了不包含过滤器,其作用是在扫面包下的所有类的时候,可能会扫面标注了//@ComponentScan的类,这个类其实就是本身的类, 这里就是ComponentScanConfiguration类
//把它排除掉,不需要重复的扫面, declaringClass= ComponentScanConfiguration
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
//开始扫面,spring中有do字样的,一般都是开始干活了
return scanner.doScan(StringUtils.toStringArray(basePackages));
doScan方法,这个方法我也就关心了一个方法
findCandidateComponents(basePackage)
->scanCandidateComponents(basePackage)
-> isCandidateComponent(metadataReader)
scanCandidateComponents(basePackage)方法
private Set scanCandidateComponents(String basePackage) {
Set candidates = new LinkedHashSet<>();
try {
//获取classPath+包名下所有.class结尾的文件,并解析成源文件
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//判断类是否满足过滤器条件,
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
isCandidateComponent(metadataReader)方法
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
//有默认的过滤器(其中包含@Component),还有自己标注的过滤器
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
源码分析到这,就结束了.