(文章目录)
1.@ComponentScan介绍
@ComponentScan 是 Spring 框架提供的一种自动化扫描和加载 Bean 的机制。它通过指定一个或多个包路径,自动扫描这些路径下的所有类,并将被注解标记的 Bean(如 @Component、@Service、@Repository、@Controller 等等)实例化并加入到 Spring 容器中。这样的话,我们就可以在其他地方直接使用这些 Bean,而不需要手动创建它们。
需要注意的是,@ComponentScan 并不是默认开启的,我们需要在配置文件中进行配置才能启用它。一般来说,我们会在 Spring 配置文件(如 applicationContext.xml)中配置 @ComponentScan 注解,指定需要扫描的包路径。例如:
<context:component-scan base-package="com.example"/>
这里,我们指定了要扫描的包路径为 com.example,并通过使用 context:component-scan 标签来开启扫描机制。当 Spring 启动时,它会自动扫描该路径下的所有组件,并将它们自动装载到 Spring 容器中。
需要注意的是,在使用 @ComponentScan 注解时,我们可以通过指定 includeFilters 和 excludeFilters 参数来过滤需要扫描的类。例如,如果我们只想扫描某些特定的类,可以通过如下方式进行配置:
@ComponentScan(basePackages = "com.example", includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {Bean1.class, Bean2.class}))
这里,我们只扫描了 Bean1 和 Bean2 两个类,其他类不会被扫描到。
总的来说,@ComponentScan 是 Spring 框架中非常重要的一部分,它简化了我们的开发流程,提高了应用程序的可维护性和可扩展性。同时,对于 Java 开发人员来说,深入理解 @ComponentScan 的原理和运行机制,有助于提高我们的 Java 技能水平。
下面是一个简单的 @ComponentScan 的使用示例:
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
// 配置其他 Bean
}
这个示例中,@ComponentScan 注解会自动扫描 com.example 包下面的所有子包,查找所有被注解标记的 Bean,然后将它们装载到 Spring 容器中,供其他组件使用。
需要注意的是,@ComponentScan 要放在一个 @Configuration 标记的类中,这样 Spring 才会在配置类中查找和扫描 Bean。同时,@ComponentScan 也可以根据需要进行更精细的配置,比如指定扫描路径、排除指定的类、添加过滤器等等。
2.@ComponentScan底层实现
@ComponentScan是Spring框架中用于扫描指定包及其子包中的bean定义的注解。它的底层工作原理可以简单概括如下:
-
获取指定包及其子包的所有文件路径,创建Resource对象,将其保存到资源列表中。
-
遍历资源列表,通过过滤器筛选出符合条件的类,获取类的名称和注解信息,将其保存到BeanDefinition对象中。
-
根据BeanDefinition对象中的信息,使用BeanDefinitionReader将其转换为BeanDefinition对象。
-
将所有的BeanDefinition对象保存到BeanDefinitionRegistry中,完成bean的注册。
在源码级别,@ComponentScan注解的具体实现可以参考以下代码:
public class ClassPathBeanDefinitionScanner extends ResourceLoaderAwareBeanFactoryPostProcessor {
// 注册中心
private BeanDefinitionRegistry registry;
// 过滤器
private TypeFilter[] includeFilters;
// 排除过滤器
private TypeFilter[] excludeFilters;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 扫描指定的包
scan("com.example.demo");
}
public void scan(String... basePackages) {
// 文件路径列表
Set<BeanDefinitionHolder> beanDefinitions = doScan(basePackages);
// 注册bean
registerBeanDefinitions(beanDefinitions, registry);
}
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 逐个扫描指定的包,获取文件路径列表
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + DEFAULT_RESOURCE_PATTERN;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
// 对文件列表进行过滤,筛选出符合条件的bean
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
if (matches(metadataReader)) {
// 将符合条件的bean保存到BeanDefinitionHolder中
BeanDefinitionHolder holder = new BeanDefinitionHolder(
getBeanDefinitionReader().createBeanDefinition(metadataReader),
metadataReader.getClassMetadata().getClassName());
beanDefinitions.add(holder);
}
}
}
}
return beanDefinitions;
}
private boolean matches(MetadataReader metadataReader) throws IOException {
// 对bean进行过滤
for (TypeFilter filter : this.excludeFilters) {
if (filter.match(metadataReader, this.getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter filter : this.includeFilters) {
if (filter.match(metadataReader, this.getMetadataReaderFactory())) {
return true;
}
}
return false;
}
}
以上是对@ComponentScan底层工作原理的一个简单解释,当然其中涉及到的一些类和方法还需要深入了解。
3.@ComponentScan高级用法
@ComponentScan注解是Spring框架中用于扫描指定包路径下的所有使用@Component注解的类,并将其注册为Bean的注解。除了指定包路径外,@ComponentScan还支持一些高级用法。
1. 指定排除和包含的类
使用excludeFilters和includeFilters属性可以指定要排除和包含的类,支持正则表达式。例如,
@ComponentScan(basePackages = "com.example",
excludeFilters = {
@ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Exclude.*"),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {ExcludeBean.class})
},
includeFilters = {
@ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Include.*"),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {IncludeBean.class})
})
上述例子中,excludeFilters属性指定了要排除的类,包括类名包含"Exclude"的所有类和ExcludeBean类;includeFilters属性指定了要包含的类,包括类名包含"Include"的所有类和IncludeBean类。
2. 指定扫描的类
使用basePackageClasses属性可以指定扫描的类,Spring框架会自动扫描这些类所在的包。例如,
@ComponentScan(basePackageClasses = {Service1.class, Service2.class})
上述例子中,指定了Service1和Service2两个类,Spring框架会自动扫描它们所在的包。
3. 指定扫描的特定注解
使用annotation属性可以指定扫描特定注解的类。例如,
@ComponentScan(basePackages = "com.example", annotation = MyAnnotation.class)
上述例子中,指定了扫描所有使用@MyAnnotation注解的类。
总之,@ComponentScan注解支持非常灵活的配置方式,可以根据应用的实际需求来灵活配置。
@ComponentScan之扫描索引
@ComponentScan注解用于定义Spring应用程序的组件扫描范围,以查找并加载所有的@Component、@Service、@Repository和@Controller注解的类。
在@ComponentScan注解中,可以使用多个value属性来指定要扫描的基础包。如果没有指定value属性,则默认扫描当前类所在的包及其子包。例如:
@Configuration
@ComponentScan(basePackages = { "com.example.demo.service", "com.example.demo.controller" })
public class AppConfig {
// 配置类的其他内容
}
在上面的例子中,我们指定了两个要扫描的基础包:com.example.demo.service和com.example.demo.controller。这意味着Spring会扫描这两个包及其子包中所有带有@Component、@Service、@Repository和@Controller注解的类,并将它们加载到应用程序上下文中。
此外,@ComponentScan还有其他可选属性,可以用来指定要扫描的类路径匹配模式、要排除的类和包、要包含的类和包等。例如:
@Configuration
@ComponentScan(basePackages = "com.example.demo",
excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX,
pattern = "com.example.demo.exclude.*"))
public class AppConfig {
// 配置类的其他内容
}
在上面的例子中,我们指定了一个要排除的类路径匹配模式,即以com.example.demo.exclude为前缀的所有类都将被排除在外。这意味着Spring不会扫描这些类,也不会将它们加载到应用程序上下文中。
4.实战中@ComponentScan的问题与解决方案
问题:
@ComponentScan是Spring框架用来扫描指定包及其子包下被@Component、@Service、@Repository、@Controller等注解修饰的类,并将其注册为Spring容器中的Bean。但在实战中,可能会出现以下问题:
-
扫描包路径不正确:如果包路径设置不正确,Spring无法扫描到所需的类。
-
扫描到非目标包下的类:如果扫描到了不应该被注册为Bean的类,会造成不必要的内存浪费。
-
扫描结果重复:如果不同的包中存在相同的类名,同样的类可能会被扫描到多次,也会造成不必要的内存浪费。
解决方案:
-
确认包路径:在使用@ComponentScan时,需要确保扫描到正确的包路径,或者使用通配符设置多个包路径。
-
排除不需要注册的类:可以通过excludeFilters设置排除不需要注册的类,例如排除所有被@Deprecated注解修饰的类。
-
改变Bean名称:如果扫描到了相同名称的类,可以通过设置@Bean注解来改变Bean的名称,例如:@Bean(name="myBean")。这样可以避免重复加载,同时可以更明确地标识Bean的作用。
总之,使用@ComponentScan时,需要注意包路径,避免扫描到不必要的类,并通过设置合适的Bean名称来避免重复加载。
Java代码示例:
- 确认包路径
使用@ComponentScan注解时,可以设置其value属性来指定需要扫描的包路径,例如:
@ComponentScan(value = {"com.example.package1", "com.example.package2"})
其中,value属性可以接受多个包路径,使用数组形式表示。也可以使用通配符来指定多个包路径,例如:
@ComponentScan(value = {"com.example.package.*"})
这样就会扫描所有以com.example.package开头的包路径。
- 排除不需要注册的类
使用@ComponentScan时,可以通过excludeFilters属性来排除不需要注册的类,例如:
@ComponentScan(value = {"com.example.package"}, excludeFilters = {@Filter(type = FilterType.ANNOTATION, value = Deprecated.class)})
上述代码会排除所有被@Deprecated注解修饰的类。
- 改变Bean名称
如果扫描到了相同名称的类,可以通过设置@Bean注解来改变Bean的名称,例如:
@Bean(name="myBean")
public MyClass myClass(){
return new MyClass();
}
这样就可以避免重复加载,并更明确地标识Bean的作用。
标签:会用,Spring,扫描,ComponentScan,Bean,注解,com From: https://blog.51cto.com/liaozhiweiblog/6537527