玩转Spring扫描创建bean

本文介绍了Spring如何通过@ComponentScan注解扫描包并创建bean,详细讲解了如何指定过滤规则,包括自定义注解、指定类、正则匹配和自定义过滤规则来决定bean的创建。同时,文章还探讨了includeFilters、excludeFilters和useDefaultFilters的使用,以及不同类型的过滤条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring如何玩转扫描包?

当我们指定包名basePackages, 如@ComponentScan(basePackages = “cn.zhutan.myspringtest”), Spring会去加载该文件夹下面的class文件, 封装成MetadataReader元信息, 根据元信息判断哪些类需要创建bean, 哪些类不需要创建bean, 而这个判断条件, Spring提供了很强大的扩展, 有以下几种情况

  1. 根据贴上某注解判断创建或者不创建bean
  2. 指定某些类创建或者不创建bean
  3. 根据正则匹配哪些bean创建或者不创建
  4. 根据Aspectj判断bean的创建与否
  5. 根据用户写的规则判断bean的创建与否

Spring如何指定过滤规则?

在注解@ComponentScan中有几个核心属性

  1. includeFilters, 表示满足该过滤条件时, 要创建bean
  2. excludeFilters, 表示满足该过滤条件时, 不创建bean
  3. useDefaultFilters, 表示是否使用默认的过滤器, 默认是true, Spring内部则相当于在includeFilters加入了过滤规则, 即满足加了@Component的类会创建bean, 如果我们置为false, 即使我们加了@Component也不会创建bean
    查看注解@ComponentScan源码
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    // 省略其他...
    // 是否使用默认过滤器
    boolean useDefaultFilters() default true;
    // 满足条件要注入的bean
    ComponentScan.Filter[] includeFilters() default {};
    // 满足条件不注入的bean
    ComponentScan.Filter[] excludeFilters() default {};
    // 省略其他...
    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;
        @AliasFor("classes")
        Class<?>[] value() default {};
        @AliasFor("value")
        Class<?>[] classes() default {};
        String[] pattern() default {};
    }
}

includeFilters和excludeFilters需要参数为数组ComponentScan.Filter[], 表示可以配置多个过滤条件,
每个过滤条件都是ComponentScan.Filter对象, 而是过滤条件的种类是以type属性指定的

public enum FilterType {
   // 1. 根据贴上某注解判断创建或者不创建bean, 解析类AnnotationTypeFilter
   ANNOTATION,
   // 2. 指定某些类创建或者不创建bean, 解析类AssignableTypeFilter
   ASSIGNABLE_TYPE,
   // 3. 根据正则匹配哪些bean创建或者不创建, 解析类AspectJTypeFilter
   ASPECTJ,
   // 4. 根据Aspectj判断bean的创建与否, 解析类RegexPatternTypeFilter
   REGEX,
   // 5. 根据用户写的规则判断bean的创建与否, 自定义实现TypeFilter
   CUSTOM
}

useDefaultFilters为true时, Spring使用默认的过滤规则, 源码如下:

@SuppressWarnings("unchecked")
protected void registerDefaultFilters() {
   // 注册@Component注解
   this.includeFilters.add(new AnnotationTypeFilter(Component.class));
   ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
   // 注册@ManagedBean注解, 找不到则不注入
   try {
      this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
      logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
   }
   catch (ClassNotFoundException ex) {
      // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
   }
   // 注册@Named注解, 找不到则不注入
   try {
      this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
      logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
   }
   catch (ClassNotFoundException ex) {
      // JSR-330 API not available - simply skip.
   }
}

以下案例针对注解, 指定类, 正则, 自定义过滤这4种进行介绍

自定义注解创建bean

@MyAnnotation
public class User { }
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation { }
@Configuration
@ComponentScan(basePackages = {"cn.zhutan.myspringtest"},
      includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyAnnotation.class)},
      useDefaultFilters = false)
public class SpringTest {
   public static void main(String[] args) {
      ApplicationContext context = new AnnotationConfigApplicationContext(SpringTest.class);
      System.out.println(context.getBean(User.class)); // 可创建bean
      System.out.println(context.getBean(UserController.class)); // 不创建bean
   }
}

指定类创建bean

@Configuration
@ComponentScan(basePackages = {"cn.zhutan.myspringtest"},
      includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = User.class)},
      useDefaultFilters = false)
public class SpringTest {
   public static void main(String[] args) {
      ApplicationContext context = new AnnotationConfigApplicationContext(SpringTest.class);
      System.out.println(context.getBean(User.class)); // 可创建bean
      System.out.println(context.getBean(UserController.class)); // 不创建bean
   }
}

正则匹配创建bean

如: 包cn.zhutan.myspringtest下面带User的都需要创建bean

@Configuration
@ComponentScan(basePackages = {"cn.zhutan.myspringtest"},
      includeFilters = {@ComponentScan.Filter(type = FilterType.REGEX, pattern = "cn.zhutan.myspringtest.*User.*")},
      useDefaultFilters = false)
public class SpringTest {
   public static void main(String[] args) {
      ApplicationContext context = new AnnotationConfigApplicationContext(SpringTest.class);
      // 以下这些类均位于cn.zhutan.myspringtest包或者子包下面, 普通的pojo, 无贴任何注解和无其他处理
      System.out.println(context.getBean(User.class)); // 可创建bean
      System.out.println(context.getBean(UserController.class)); // 可创建bean
      System.out.println(context.getBean(UserService.class)); // 可创建bean
      System.out.println(context.getBean(Employee.class)); // 无法创建bean
   }
}

自定义过滤规则创建bean

public class MyTypeFilter implements TypeFilter {
   @Override
   public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
      ClassMetadata classMetadata = metadataReader.getClassMetadata();
      // 全限定名包含User的就创建, 其它不创建
      return classMetadata.getClassName().contains("User");
      // return true // 理论上代表所有类都会创建, 但是Spring里面还会对一些抽象类, 接口等等进行二层过滤
   }
}
@Configuration
@ComponentScan(basePackages = {"cn.zhutan.myspringtest"},
      includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class)},
      useDefaultFilters = false)
public class SpringTest {
   public static void main(String[] args) {
      ApplicationContext context = new AnnotationConfigApplicationContext(SpringTest.class);
      System.out.println(context.getBean(User.class)); // 可创建bean
      System.out.println(context.getBean(UserController.class)); // 可创建bean
      System.out.println(context.getBean(UserService.class)); // 可创建bean
      System.out.println(context.getBean(Employee.class)); // 无法创建bean
   }
}

总结

在@ComponentScan或者xml配置标签中, 我们可以指定哪些包下面的类需要扫描, 并通过配置includeFilters和excludeFilters判断哪些类创建bean, 过滤的规则可以使用以下5种

  1. 根据贴上某注解判断创建或者不创建bean
  2. 指定某些类创建或者不创建bean
  3. 根据正则匹配哪些bean创建或者不创建
  4. 根据Aspectj判断bean的创建与否
  5. 根据用户写的规则判断bean的创建与否
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值