1.源码解析
@ComponentScan从翻译上我们可以知道它是一个组件扫描器,在这个扫描器里我们可以指定扫描的范围,指定系统为我们设置的相应过滤器,甚至是可以使用我们自定义的过滤器。
[1] 这里我们先从源码中几个主要的方法讲起
public @interface ComponentScan {
//这个value值就是指定我们扫描包的路径
//@AliasFor这里指的是使用时可以使用的另一个别名
@AliasFor("basePackages")
String[] value() default {};
//指定包含类型的过滤器,在这里面可以指定Filter来设定我们过滤时通过什么辨别(最常用的是注解和类)
Filter[] includeFilters() default {};
//指定需要排除类型的过滤器
Filter[] excludeFilters() default {};
@Target({})
@interface Filter {
//Filter的类型(默认是注解)
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
[2] FilterType 这个类中表明我们能填写的值
package org.springframework.context.annotation;
public enum FilterType {
/**
* Filter candidates marked with a given annotation.
* @see org.springframework.core.type.filter.AnnotationTypeFilter
* 指定的注解(使用较多)
*/
ANNOTATION,
/**
* Filter candidates assignable to a given type.
* @see org.springframework.core.type.filter.AssignableTypeFilter
* 指定的类型,即我们指定扫描哪个类(使用较多)
*/
ASSIGNABLE_TYPE,
/**
* Filter candidates matching a given AspectJ type pattern expression.
* @see org.springframework.core.type.filter.AspectJTypeFilter
*/
ASPECTJ,
/**
* Filter candidates matching a given regex pattern.
* @see org.springframework.core.type.filter.RegexPatternTypeFilter
* 正则表达式
*/
REGEX,
/** Filter candidates using a given custom
* {@link org.springframework.core.type.filter.TypeFilter} implementation.
* 自定义类型(使用较多)
*/
CUSTOM
}
[3] 一脸懵逼,你在说什么呢?我们还是用一个includeFilters实例说明吧
@Configuration
//@Controller @Service @Respostry @Component
@ComponentScan(value="com.enjoy.cap2",
includeFilters= {@Filter(type=FilterType.ANNOTATION,classes= {Controller.class})}
,useDefaultFilters=false)
public class Cap2MainConfig {
解析: 这个注解使用在类上,其中value指明需要扫描的包(com.enjoy.cap2这是我自己新建的包,这个包中有@Controller @Service @Respostry @Component这几个注解标明的类);接下来includeFilters指明相应的规则,比如通过FilterType.ANNOTATION(即我们的注解进行相应的扫描包含),后面的classes用来表示注解相应的类(Controller类),这样我们最终扫描的到的组件就只有标了@Controller的类了,其余的类就被过滤掉了。
useDefaultFilters=false这又是个什么东西呀,我们带着它再次走进源码
/**
* Indicates whether automatic detection of classes annotated with {@code @Component}
* {@code @Repository}, {@code @Service}, or {@code @Controller} should be enabled.
* 指示是否自动检测用{@code@Component}注释的类应该启用{@code@Repository}、{@code@Service}或{@code@Controller}。
**/
boolean useDefaultFilters() default true;
默认它是打开的,也就是我们设不设定相应的过滤规则,它也会到包下去扫描这几个注解的类。这也就是我们把它设为false的原因了,因为我们现在只需它扫描@Controller相应的类就可以了。(如果这个值为true,我们设定的过滤规则就会失效)
接下来我们用excludeFilters来说明它与上面的区别
@Configuration
//@Controller @Service @Respostry @Component
@ComponentScan(value="com.enjoy.cap2",
excludeFilters= {@Filter(type=FilterType.ANNOTATION,classes= {Controller.class})}
,useDefaultFilters=false)
public class Cap2MainConfig {
有人就会觉得理所当然了,不就是把includeFilters改为excludeFilters就能实现相应的排除功能吗,这又什么好说的。对,你说的没错,不过需要注意的是useDefaultFilters=false这就是不可以的了,因为我们关闭了它的扫描,那我们怎么排除都一样没有效果,因为我们根本就没有对相应的注解进行相应的扫描,这时我们需要开启扫描,即把上面的改为useDefaultFilters=true就能正常使用了。
[4] 从上面我们已经了解了includeFilter和excludeFilter的相关属性,接下来我们我们将深入了解一下这两个属性中的@Fiter可以使用的相关值。
2. 使用@Filter来自定义过滤类
上面我们已经了解到FilterType类型中有ANNOTATION(默认),ASSIGNABLE_TYPE(指定我们需要扫描的类,如xxxController.class);除了这两个之外我们最常使用的就是 CUSTOM(自定义类型),可以通过这个类型来设定我们自己的过滤规则
FilterType 这个类中最常用的三个值
package org.springframework.context.annotation;
public enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
CUSTOM
}
[1] 编写一个自定义的过滤类(这里可以编写我们自己的过滤规则)
MyTypeFilter这个类需要继承TypeFilter
package com.enjoy.cap2.config;
import java.io.IOException;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
public class JamesTypeFilter implements TypeFilter {
/**
* MetadataReader: 读取当前正在扫描类的信息
* MetadataReaderFactory: 可以获取到其他类的信息
*/
@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();
System.out.println("----->"+className);
/**
获取我们扫描的包中的类名,如果我们只需要得到相应的订单类Order,这时候我们就
可以设置它的匹配规则如下
**/
if(className.contains("Order")) {
//当类包含Order字符,则匹配成功,返回true
return true;
}
return false;
}
}
[2] 在主配置类中引入我们的自定义过滤规则
这里的FilterType要使用FilterType.CUSTOM,也就是我们的自定义类型。classes就是我们的类名。这样引入之后我们就能按照自己定义的规则进行相应的过滤(比如这里我们的匹配规则时只有Order相关的类才会的到相应的扫描)
@Configuration
//@Controller @Service @Respostry @Component
@ComponentScan(value="com.enjoy.cap2",includeFilters= {
@Filter(type= FilterType.CUSTOM,classes= {MyTypeFilter.class})
}
,useDefaultFilters=false)
public class Cap2MainConfig {
小结
@ComponentScan(value=“com.enjoy.cap2”,includeFilters= {
@Filter(type= FilterType.CUSTOM,classes= {MyTypeFilter.class})
}
,useDefaultFilters=false)
这基本涵盖了上面所讲的,我们只要知道@ComponentScan是通过value指定的相应的包路径进行扫描的,并通过
includeFilters或excludeFilters来表命满足条件是包含呢还是排除呢,而条件上则是通过@Filte上的FilterType来指定注解,类,自定义类等。