夯实spring(十八): @ComponentScan、@ComponentScans注解详解

本文内容:

  • @ComponentScan注解的作用
  • basePackages的方式和basePackageClasses的方式的区别
  • useDefaultFilters的作用

根据前面的文章我们知道可以使用xml中bean元素的方式和@Bean注解标注方法的方式来注册bean。

不过上面的两在方式都是单个注册的,为了更方便bean的注册,Spring提供了批量的方式注册bean,方便大量bean批量注册,spring中的@ComponentScan就是用来批量的注册bean的。

1,@ComponentScan

@ComponentScan用于批量注册bean。

这个注解会让spring去扫描某些包及其子包中所有的类,然后将满足一定条件的类作为bean注册到spring容器容器中。

具体需要扫描哪些包?以及这些包中的类满足什么条件时被注册到容器中,这些都可以通过这个注解中的参数动态配置

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
	//指定需要扫描的包 如:com.chen
    @AliasFor("basePackages")
    String[] value() default {};
    
	//同value;value和basePackages不能同时存在设置,可二选一
    @AliasFor("value")
    String[] basePackages() default {};
	
	//指定一些类,spring容器会扫描这些类所在的包及其子包中的类
    Class<?>[] basePackageClasses() default {};

	//自定义bean名称生成器
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

	//需要扫描包中的那些资源,默认是:"**/*.class",即会扫描指定包中所有的class文件
    String resourcePattern() default "**/*.class";

	//对扫描的类是否启用默认过滤器,默认为true
    boolean useDefaultFilters() default true;
	
	//过滤器:用来配置被扫描出来的那些类会被作为组件注册到容器中
    ComponentScan.Filter[] includeFilters() default {};

	//过滤器,和includeFilters作用刚好相反,用来对扫描的类进行排除的,被排除的类不会被注册到容器中
    ComponentScan.Filter[] excludeFilters() default {};

	//是否延迟初始化被注册的bean
    boolean lazyInit() default false;

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

使用了@ComponentScan注解后,Spring会扫描指定的包,且会递归下面子包,得到一批类的数组
然后这些类会经过上面的各种过滤器,最后剩下的类会被注册到容器中。

扫描指定的包:
通过value、backPackages、basePackageClasses这3个参数来决定。

各种过滤器:
通过useDefaultFilters、includeFilters、excludeFilters这3个参数来控制过滤器

默认情况下,任何参数都不设置的情况下,此时,会将@ComponentScan修饰的类所在的包作为扫描包;默认情况下useDefaultFilters为true,这个为true的时候,spring容器内部会使用默认过滤器,规则是:凡是类上有@Repository、@Service、@Controller、@Component这几个注解中的任何一个的,那么这个类就会被作为bean注册到spring容器中,所以默认情况下,只需在类上加上这几个注解中的任何一个,这些类就会自动交给spring容器来管理了。

来个案列:

@Component
public class Car {
}
@Controller
public class CarController {
}
@Service
public class CarService {
}

在这里插入图片描述

1.1 未设置参数
```java
@ComponentScan
public class Scan1 {
}

默认会扫描Scan1 类所在的包中的所有类,类上有@Component、@Repository、@Service、@Controller任何一个注解的都会被注册到容器中

测试输出

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Scan1.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(context.getBean(name));
        }
    }
}
org.springframework.context.annotation.ConfigurationClassPostProcessor@710726a3
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@646007f4
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@481a15ff
org.springframework.context.event.EventListenerMethodProcessor@78186a70
org.springframework.context.event.DefaultEventListenerFactory@306279ee
com.chen.Scan1@545997b1
com.chen.bean.Car@4cf4d528
com.chen.controller.CarController@77846d2c
com.chen.service.CarService@548ad73b
1.2 指定需要扫描的包
@ComponentScan(basePackages = {"com.chen.controller","com.chen.service"})
public class Scan1 {
}

指定需要扫面哪些包,可以通过value或者basePackage来配置,二者选其一,都配置运行会报错

测试输出

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Scan1.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(context.getBean(name));
        }
    }
}
org.springframework.context.annotation.ConfigurationClassPostProcessor@149494d8
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@710726a3
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@646007f4
org.springframework.context.event.EventListenerMethodProcessor@481a15ff
org.springframework.context.event.DefaultEventListenerFactory@78186a70
com.chen.Scan1@306279ee
com.chen.controller.CarController@545997b1
com.chen.service.CarService@4cf4d528

可以看到Car没有注册进来

1.3 basePackageClasses指定扫描范围
@ComponentScan(basePackageClasses = CarService.class)
public class Scan1 {
}

测试输出

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Scan1.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(context.getBean(name));
        }
    }
}
org.springframework.context.annotation.ConfigurationClassPostProcessor@10a035a0
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@67b467e9
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@47db50c5
org.springframework.context.event.EventListenerMethodProcessor@5c072e3f
org.springframework.context.event.DefaultEventListenerFactory@4d1b0d2a
com.chen.Scan1@954b04f
com.chen.service.CarService@149494d8

可以看到只有CarService注册进来了,所以我们可以在需要扫描的包中定义一个标记的接口或者类,作用是作为basePackageClasses的值,用来指定扫描的包。

2,@ComponentScan的includeFilters

 ComponentScan.Filter[] includeFilters() default {};

@ComponentScan的includeFilters是一个Filter类型的数组,多个Filter之间为或者关系,即满足任意一个就可以了

    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
    	//过滤器的类型,是个枚举类型
        FilterType type() default FilterType.ANNOTATION;

		//和参数classes效果一样,二选一
        @AliasFor("classes")
        Class<?>[] value() default {};

        @AliasFor("value")
        Class<?>[] classes() default {};

        String[] pattern() default {};
    }

可以看到Filter 也是一个注解,其中的属性type是枚举,它的值是FilterType 中的一个,来看一下FilterType :

public enum FilterType {
	//通过注解的方式来筛选候选者,即判断候选者是否有指定的注解
    ANNOTATION,
    //通过指定的类型来筛选候选者,即判断候选者是否是指定的类型
    ASSIGNABLE_TYPE,
    //ASPECTJ表达式方式,即判断候选者是否匹配ASPECTJ表达式
    ASPECTJ,
    //正则表达式方式,即判断候选者的完整名称是否和正则表达式匹配
    REGEX,
    //用户自定义过滤器来筛选候选者,对候选者的筛选交给用户自己来判断
    CUSTOM;

    private FilterType() {
    }
}

比如可以扫描包含我们自定义注解的类才注册进来

自定义一个注解:

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
}

使用自定义注解标注:

@MyAnno
public class Car {
}

定义过滤器:

@ComponentScan(useDefaultFilters = false,includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyAnno.class)
})
public class Scan1 {
}

测试输出:

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Scan1.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(context.getBean(name));
        }
    }
}
org.springframework.context.annotation.ConfigurationClassPostProcessor@2641e737
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@727803de
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@704921a5
org.springframework.context.event.EventListenerMethodProcessor@df27fae
org.springframework.context.event.DefaultEventListenerFactory@24a35978
com.chen.Scan1@16f7c8c1
com.chen.bean.Car@2f0a87b3

可以看到只有标注了自定义注解@MyAnno的Car注册进来了

总结:

  • @ComponentScan用于批量注册bean,spring会按照这个注解的配置,递归扫描指定包中的所有类,将满足条件的类批量注册到spring容器中

  • 可以通过value、basePackages、basePackageClasses 这几个参数来配置包的扫描范围

  • 可以通过useDefaultFilters、includeFilters、excludeFilters这几个参数来配置类的过滤器,被过滤器处理之后剩下的类会被注册到容器中

  • 指定包名的方式配置扫描范围存在隐患,包名被重命名之后,会导致扫描失败,所以一般我们在需要扫描的包中可以创建一个标记的接口或者类,作为basePackageClasses的值,通过这个来控制包的扫描范围

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值