一、项目的结构如下:

spring配置扫描多个包 spring @componentscan扫描范围_spring配置扫描多个包

 

二、使用 @ComponentScan 注解时,如果不给这个注解的任意属性赋值,那么该注解默认的扫描范围是什么?

1、在 com.spring01.config 包下创建两个类 SpringConfiguration、Animal.并且在 UserServiceImpl 上加上注解 @Service ,在 UserDaoImpl 上加上注解 @Repository

// 标记这是一个Spring的配置类
@Configuration
@ComponentScan
public class SpringConfiguration {

    // @Bean 注解用于将 Person 类型的对象注入到 Spring 容器中,注入到容器中的对象类型为方法的返回值类型,默认注入的 id 是方法名
    // 如果想指定注入到 Spring 容器中的 id ,可以使用 @Bean("xiaomaomao") ,代表注入到 Spring 容器的 id="xiaomaomao"
    @Bean
    public Person person01() {
        return new Person("Bill Gates", 66);
    }
	// 这里就代表往 spring 容器中注入了一个 Person 类型的对象, id 为 person02
    @Bean
    public Person person02() {
        return new Person("Linus Torvalds", 44);
    }
}
@Component
public class Animal {
}
@Service
public class UserServiceImpl implements UserService {
}
@Repository
public class UserDaoImpl implements UserDao {
}

2、测试类

public class SpringDemo {
    @Test
    public void springTest01() {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);

        // 获取并打印所有的 Bean 的名称
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}

3、测试结果

// Spring框架自身类
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
// @ComponentScan 注解扫描,注入到 Spring 容器中,并交由 Spring 容器进行管理
// SpringConfiguration上标注的是 @Configuration,该注解实际上底层使用的是 @Component 注解,默认的 id 是类名的首字母小写
springConfiguration
// Animal 上标注的是 @Component 注解,注入到 Spring 容器的时候默认的 id 是类名首字母小写
animal
// @Bean注解,没有显示给值,id 是方法名
person01
person02

结果分析:除了框架自身的类之外,我们实际上注入到 Spring 容器中的对象有 springConfiguration、animal、person01、person02,而使用 @Service 和 @Repository 标注的注解却没有被扫描到并注入到 Spring 容器中,所以说单纯的使用 @ComponentScan 注解标注,而不给该注解的任何属性赋值,那么该注解的扫描范围就是配置类所在的包下面的所有类(配置类是SpringConfiguration ,其所在的包是 com.spring01.config ),而 UserServiceImpl 和 UserDaoImpl 虽然被 @Service 和 @Repository 注解标注,但是它们并不在配置类所在的包下,所以它们没有被扫描到.

总结: @ComponentScan 默认的扫描范围就是当前类所在的包下的所有注解.

 

三、使用 @ComponentScan 注解时,它有哪些属性,这些属性可以赋哪一些值?

@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 {};

	}
}

下面我们就挑几个常用的属性来说一说吧

1、value / basePackages 属性

@AliasFor("basePackages")
String[] value() default {};

@AliasFor("value")
String[] basePackages() default {};

value 和 basePackages 两个属性互为别名,也就是说无论使用它们中的哪一个属性效果都是一样的,它们的作用就是指定 @ComponentScan 这个注解的扫描范围

一般来说 value 和 basePackages 两个属性我们只会给其中的一个赋值,如果你要同时使用这两个属性,那么请保持这两个属性的值是一样的,否则会出现如下错误:

// 错误信息
org.springframework.core.annotation.AnnotationConfigurationException: Different @AliasFor mirror values for annotation 
[org.springframework.context.annotation.ComponentScan] declared on class com.spring01.config.SpringConfiguration; 
attribute 'basePackages' and its alias 'value' are declared with values of [{com.spring01.dao}] and [{com.spring01}].

举例: @ComponentScan(value="com.spring01") / @ComponentScan(basePackages="com.spring01")

它们代表的意思都是扫描 com.spring01 包下的所有注解

 

2、excludeFilters 属性

Filter[] excludeFilters() default {};

excludeFilters 的属性值是一个 Filter 类型的数组,我们可以看一下这个数组里面的取值

@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
  // type 属性的默认值是注解类型
  FilterType type() default FilterType.ANNOTATION;
  // value 属性和 classes 属性互为别名,只需要写一个就可以了
  @AliasFor("classes")
  Class<?>[] value() default {};
	// 与 value 互为别名
  @AliasFor("value")
  Class<?>[] classes() default {};
  // 类型
  String[] pattern() default {};
}

type 属性的是一个枚举类,里面的取值如下:
FilterType.ANNOTATION(默认值)
FilterType.ASSIGNABLE_TYPE
FilterType.ASSIGNABLE_TYPE
FilterType.ASPECTJ
FilterType.REGEX(正则表达式)
FilterType.CUSTOM(自定义规则)

其中最经常使用的值就是 FilterType.ANNOTATION、FilterType.CUSTOM,下面我们来演示一下这两个值有什么用

一、FilterType.ANNOTATION

@Configuration
// 扫描 com.spring01 包下面的所有注解
@ComponentScan(value = "com.spring01",
        // 如果使用的是 excludeFilters, useDefaultFilters 的默认值就是 true,这里可以省略不写
        useDefaultFilters = true,
        // 排除的类型是注解类型,排除的注解名称是 Service (也就是排除 @Service 注解标注的类)
        excludeFilters = {
                @ComponentScan.Filter(type=FilterType.ANNOTATION,classes={Service.class})
        }
    )
// 配置类
public class SpringConfiguration {
    @Bean
    public Person person01() {
        return new Person("Bill Gates", 66);
    }

    @Bean
    public Person person02() {
        return new Person("Linus Torvalds", 44);
    }
}

测试结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
springConfiguration
animal
userDaoImpl
person01
person02

结果分析:由于我们使用了排除规则,指明了排除 @Service 注解,所以 UserServiceImpl 这个类没有被扫描,也就没有注入到 Spring 容器中

FilterType.CUSTOM

枚举值上面的注释可以看出,我们需要实现 TypeFilter 这个接口

/** Filter candidates using a given custom
 * {@link org.springframework.core.type.filter.TypeFilter} implementation.
 */
CUSTOM

自定义一个类 MyCustomTypeFilter 实现 TypeFilter 接口

public class MyCustomTypeFilter 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 className = classMetadata.getClassName();
        // 类名中包含"User"的类
        if(className.contains("User")){
			// 返回值为 true ,代表的就是要排除
            return true;
        }
        return false;
    }
}

配置类

@Configuration
// 扫描 com.spring01 包下面的所有注解
@ComponentScan(value = "com.xiaomaomao",
        // 如果使用的是 excludeFilters, useDefaultFilters 的默认值就是 true,这里可以省略不写
        useDefaultFilters = true,
        // 排除的类型是自定义类型,自定义规则在 MyCustomTypeFilte 类中给出
        excludeFilters = {
                @ComponentScan.Filter(type=FilterType.CUSTOM,classes={MyCustomTypeFilter.class})
        }
)
// 配置类
public class SpringConfiguration {
    @Bean
    public Person person01() {
        return new Person("Bill Gates", 66);
    }

    @Bean
    public Person person02() {
        return new Person("Linus Torvalds", 44);
    }
}

测试结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
springConfiguration
animal
person01
person02

结果分析:类名中包含 "User" 字符串的类都被排除了,最终注入 Spring 容器的类就没有 UserServiceImpl 和 UserDaoImpl

注意:排除规则只能排除配置类之外的其它类,配置类里面相应的信息是不能被排除的.

 

3、includeFilters属性

Filter[] includeFilters() default {},代表只扫描哪一些类或者是注解(取决于 type 的类型)用法和 excludeFilter 类似,唯一的区别就是,如果你想使用 includeFilter 进行筛选的时候,切记一定要将 useDefaultFilter 这个属性的值设置为 false.

 

4、@ComponentScan 注解是一个可重复注解,我们可以同时定义多个 @Component 注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 可重复注解
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

例如:

@Configuration
@ComponentScans({
        @ComponentScan(value = "com.spring01",
                useDefaultFilters = false,
                // 只扫描类名中包含 "Dao"
                includeFilters = {
                        @ComponentScan.Filter(type=FilterType.CUSTOM,classes={MyCustomTypeFilter.class})
                }
        ),
        @ComponentScan(value = "com.spring01",
                useDefaultFilters = false,
                // 排除 @Service 注解
                excludeFilters = {
                        @ComponentScan.Filter(type=FilterType.ANNOTATION,classes={Service.class})
                }
        )
    }
)
@ComponentScan(value = "com.spring01",
        // 如果使用的是 excludeFilters, useDefaultFilters的默认值就是 true,这里可以省略不写
        useDefaultFilters = false,
        // 使用自定义的TypeFilter
        includeFilters = {
                @ComponentScan.Filter(type=FilterType.CUSTOM,classes={MyCustomTypeFilter.class})
        }
    )
public class SpringConfiguration {

    @Bean
    public Person person01() {
        return new Person("Bill Gates", 66);
    }

    @Bean
    public Person person02() {
        return new Person("Linus Torvalds", 44);
    }
}