Spring深入理解之ComponentScan___@ComponentScan 详解

x33g5p2x  于2021-12-16 转载在 Spring  
字(4.7k)|赞(0)|评价(0)|浏览(404)

Spring深入理解之ComponentScan

一、概述

ComponentScan顾名思义包扫描,底层其实就可以通过递归算法+反射将其装载成bean来实现的,实在开发过程中,Spring已经帮我们实现好了,我们其实就可以直接使用XML或者注解的形式来进行业务处理。

二、@ComponentScan的作用

@ComponentScan注解有两个作用

作用一:扫描含有@Component,@Controller,@Service和@Repository的类,并将其注入到spring容器中。

作用二:扫描含有@Configuration的类,并使其生效。

分析:作用一相信大家都明白其含义,再次不在赘述。作用二举例说明,例如Config2可以扫描到config2_test目录,该目录下不仅含有@Component,@Controller,@Service和@Repository注解标注的四个类,还包含Config1配置类。Config1配置类,可以扫描到config1_test目录,并且里面还包含一个@Bean注解。

三、同一个配置类上多个@ComponentScan可能会报错

下面的配置类在我公司电脑的idea版本里是可以将config3_test目录下的所有类都注册到spring容器中,但是在我家里的电脑上以及另一个同事的电脑上会报编译期错误,代码运行也会出错。

四、@ComponentScans的使用

由于同一个类上直接写多个@ComponentScan可能会报错,因此建议使用@ComponentScans,它可以解决直接在一个配置类上写多个@ComponentScan在有些电脑或idea版本上报错的问题。

@ComponentScan 详解

@ComponentScan 的作用就是根据定义的扫描路径,把符合扫描规则的类装配到spring容器中,注解定义如下。

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target({ElementType.TYPE})
  3. @Documented
  4. @Repeatable(ComponentScans.class)
  5. public @interface ComponentScan {
  6. @AliasFor("basePackages")
  7. String[] value() default {};
  8. @AliasFor("value")
  9. String[] basePackages() default {};
  10. Class<?>[] basePackageClasses() default {};
  11. Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
  12. Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
  13. ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
  14. String resourcePattern() default "**/*.class";
  15. boolean useDefaultFilters() default true;
  16. ComponentScan.Filter[] includeFilters() default {};
  17. ComponentScan.Filter[] excludeFilters() default {};
  18. boolean lazyInit() default false;
  19. @Retention(RetentionPolicy.RUNTIME)
  20. @Target({})
  21. public @interface Filter {
  22. FilterType type() default FilterType.ANNOTATION;
  23. @AliasFor("classes")
  24. Class<?>[] value() default {};
  25. @AliasFor("value")
  26. Class<?>[] classes() default {};
  27. String[] pattern() default {};
  28. }
  29. }

basePackages与value: 用于指定包的路径,进行扫描

basePackageClasses: 用于指定某个类的包的路径进行扫描

nameGenerator: bean的名称的生成器

useDefaultFilters: 是否开启对@Component,@Repository,@Service,@Controller的类进行检测

includeFilters: 包含的过滤条件

  • FilterType.ANNOTATION:按照注解过滤
  • FilterType.ASSIGNABLE_TYPE:按照给定的类型
  • FilterType.ASPECTJ:使用ASPECTJ表达式
  • FilterType.REGEX:正则
  • FilterType.CUSTOM:自定义规则

excludeFilters: 排除的过滤条件,用法和includeFilters一样

我的工程结构如下,测试对controller和service的扫描,其中HelloController没有加@Controller等任何注解,就是一个普通类。

修改配置类如下:应用默认的过滤器,扫描service包:

  1. @Configuration
  2. @ComponentScan(value = "com.xhx.spring.service",
  3. useDefaultFilters = true
  4. )
  5. public class MyConfig {
  6. }

系统注入了两个service进去

改成如下所示:HelloController所在的包的类也被扫描了进去

  1. @Configuration
  2. @ComponentScan(value = "com.xhx.spring.service",
  3. useDefaultFilters = true,
  4. basePackageClasses = HelloController.class
  5. )
  6. public class MyConfig {
  7. }

系统中会注入下面就给类

把默认的过滤器关掉,扫描带Controller注解的。

  1. @Configuration
  2. @ComponentScan(value = "com.xhx.spring",
  3. useDefaultFilters = false,
  4. includeFilters = {
  5. @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
  6. }
  7. )
  8. public class MyConfig {
  9. }

按照类的类型扫描,虽然HelloController没有加注解,但是被注入到了spring容器中

  1. @Configuration
  2. @ComponentScan(value = "com.xhx.spring",
  3. useDefaultFilters = false,
  4. includeFilters = {
  5. @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {HelloController.class})
  6. }
  7. )
  8. public class MyConfig {
  9. }

自定义扫描过滤器

  1. package com.xhx.spring.componentscan.config;
  2. import org.springframework.core.type.classreading.MetadataReader;
  3. import org.springframework.core.type.classreading.MetadataReaderFactory;
  4. import org.springframework.core.type.filter.TypeFilter;
  5. import java.io.IOException;
  6. public class MyTypeFilter implements TypeFilter {
  7. @Override
  8. public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
  9. String className = metadataReader.getClassMetadata().getClassName();
  10. if(className.contains("Controller")){
  11. return true;
  12. }
  13. return false;
  14. }
  15. }

修改配置类

  1. @Configuration
  2. @ComponentScan(value = "com.xhx.spring",
  3. useDefaultFilters = false,
  4. includeFilters = {
  5. @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
  6. }
  7. )
  8. public class MyConfig {
  9. }

输出结果:

输出spring容器中的bean的测试类:只过滤输出了名字中含有hello的类。

  1. package com.xhx.spring.componentscan;
  2. import com.xhx.spring.componentscan.config.MyConfig;
  3. import org.junit.Test;
  4. import org.junit.runner.RunWith;
  5. import org.springframework.boot.test.context.SpringBootTest;
  6. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  7. import org.springframework.test.context.junit4.SpringRunner;
  8. import java.util.Arrays;
  9. import java.util.List;
  10. import java.util.stream.Collectors;
  11. @RunWith(SpringRunner.class)
  12. @SpringBootTest
  13. public class ComponentScanApplicationTests {
  14. @Test
  15. public void testLoads() {
  16. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
  17. List<String> hello = Arrays.stream(context.getBeanDefinitionNames()).collect(Collectors.toList());
  18. hello.stream().filter(name->name.contains("hello")).peek(System.out::println).count();
  19. }
  20. }

相关文章