Spring中常用的注解

4.3.1 @ComponentScan和ComponentScans(bean批量注册)

  1. @ComponentScan用于批量注册bean,spring会按照这个注解的配置,递归扫描指定包中的所有类,将满足条件的类批量注册到spring容器中
  2. 可以通过value、basePackages、basePackageClasses 这几个参数来配置包的扫描范围
  3. 可以通过useDefaultFilters、includeFilters、excludeFilters这几个参数来配置类的过滤器,被过滤器处理之后剩下的类会被注册到容器中
  4. 指定包名的方式配置扫描范围存在隐患,包名被重命名之后,会导致扫描实现,所以一般我们在需要扫描的包中可以创建一个标记的接口或者类,作为basePackageClasses的值,通过这个来控制包的扫描范围
  5. @CompontScan注解会被ConfigurationClassPostProcessor类递归处理,最终得到所有需要注册的类
4.3.1.1 源码定义
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class) //@1
public @interface ComponentScan {
   
   
    @AliasFor("basePackages")
    String[] value() default {
   
   };
    @AliasFor("value")
    String[] basePackages() default {
   
   };
    Class<?>[] basePackageClasses() default {
   
   };
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
    String resourcePattern() default "**/*.class";
    boolean useDefaultFilters() default true;
    Filter[] includeFilters() default {
   
   };
    Filter[] excludeFilters() default {
   
   };
    boolean lazyInit() default false;
}

常用参数:

  1. value:指定需要扫描的包,如:com.javacode2018
  2. basePackages:作用同value;value和basePackages不能同时存在设置,可二选一
  3. basePackageClasses:指定一些类,spring容器会扫描这些类所在的包及其子包中的类
  4. nameGenerator:自定义bean名称生成器
  5. resourcePattern:需要扫描包中的那些资源,默认是:**/*.class,即会扫描指定包中所有的class文件
  6. useDefaultFilters:对扫描的类是否启用默认过滤器,默认为true
  7. includeFilters:过滤器:用来配置被扫描出来的那些类会被作为组件注册到容器中
  8. excludeFilters:过滤器,和includeFilters作用刚好相反,用来对扫描的类进行排除的,被排除的类不会被注册到容器中
  9. lazyInit:是否延迟初始化被注册的bean
  10. @Repeatable(ComponentScans.class),这个注解可以同时使用多个
4.3.1.2 工作过程:
  1. Spring会扫描指定的包,且会递归下面子包,得到一批类的数组
  2. 然后这些类会经过上面的各种过滤器,最后剩下的类会被注册到容器中
4.3.1.3 关键问题:
  1. 需要扫描哪些包?
    通过value、backPackages、basePackageClasses这3个参数来控制
  2. 过滤器有哪些?
    通过useDefaultFilters、includeFilters、excludeFilters这3个参数来控制过滤器
4.3.1.4 扫描规则:

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

4.3.1.5 案例1:任何参数未设置

分别在dao,controller,service包下创建类,用@Service,@Controller,@Repository注解标注
UserService

package com.zjhc.componentSacn.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
   
   }

UserController

package com.zjhc.componentSacn.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
   
   }

UserDao

package com.zjhc.componentSacn.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
   
   }

UserModel

package com.zjhc.componentSacn;
import org.springframework.stereotype.Component;
@Component
public class UserModel {
   
   }

ScanBean

package com.zjhc.componentSacn;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan
public class ScanBean {
   
   }

测试

@Test
public void test2(){
   
   
   ApplicationContext context = new AnnotationConfigApplicationContext(ScanBean.class);
   for (String beanName : context.getBeanDefinitionNames()) {
   
   
       System.out.println(beanName+"---->"+context.getBean(beanName));
   }
}

使用AnnotationConfigApplicationContext作为ioc容器,将ScanBean.class作为参数传入,默认会扫描ScanBean类所在的包中的所有类,类上有@Component、@Repository、@Service、@Controller任何一个注解的都会被注册到容器中
在这里插入图片描述

4.3.1.6 案例2:指定需要扫描的包

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

package com.zjhc.componentSacn;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan({
   
   "com.zjhc.componentSacn.controller,
                 com.zjhc.componentSacn.dao"})
public class ScanBean {
   
   }

测试结果
在这里插入图片描述

4.3.1.7 案例:basePackageClasses指定扫描范围

指定包名的方式扫描存在的一个隐患,若包被重名了,会导致扫描会失效,我们可以在需要扫描的包中定义一个标记的接口或者类,他们的唯一的作用是作为basePackageClasses的值,其他没有任何用途。
定义一个类或者接口

package com.zjhc.componentSacn.controller;
public class a {
   
   }
import com.zjhc.componentSacn.controller.a;
import org.springframework.context.annotation.ComponentScan;

//@ComponentScan({"com.zjhc.componentSacn.controller,com.zjhc.componentSacn.dao"})
@ComponentScan(basePackageClasses = a.class)
public class ScanBean {
   
   
}
scanBean---->com.zjhc.componentSacn.ScanBean@67e2d983
userController---->com.zjhc.componentSacn.controller.UserController@5d47c63f
4.3.1.8 includeFilters和excludeFilters的使用

是一个Filter类型的数组,多个Filter之间为或者关系,即满足任意一个就可以了,看一下Filter的代码:

@Retention(RetentionPolicy.RUNTIME)
@Target({
   
   })
@interface Filter {
   
   

    FilterType type() default FilterType.ANNOTATION;

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

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

    String[] pattern() default {
   
   };
}

主要参数:
type:过滤器的类型,是个枚举类型,5种类型
ANNOTATION:通过注解的方式来筛选候选者,即判断候选者是否有指定的注解
ASSIGNABLE_TYPE:通过指定的类型来筛选候选者,即判断候选者是否是指定的类型
ASPECTJ:ASPECTJ表达式方式,即判断候选者是否匹配ASPECTJ表达式
REGEX:正则表达式方式,即判断候选者的完整名称是否和正则表达式匹配
CUSTOM:用户自定义过滤器来筛选候选者,对候选者的筛选交给用户自己来判断
value:和参数classes效果一样,二选一
classes:3种情况如下
当type=FilterType.ANNOTATION时,通过classes参数可以指定一些注解,用来判断被扫描的类上是否有classes参数指定的注解
当type=FilterType.ASSIGNABLE_TYPE时,通过classes参数可以指定一些类型,用来判断被扫描的类是否是classes参数指定的类型
当type=FilterType.CUSTOM时,表示这个过滤器是用户自定义的,classes参数就是用来指定用户自定义的过滤器,自定义的过滤器需要实现org.springframework.core.type.filter.TypeFilter接口
pattern:2种情况如下
当type=FilterType.ASPECTJ时,通过pattern来指定需要匹配的ASPECTJ表达式的值
当type=FilterType.REGEX时,通过pattern来自正则表达式的值

4.3.1.8.1 扫描包含注解的类

我们自定义一个注解,让标注有这些注解的类自动注册到容器中

package com.zjhc.componentSacn.annotation;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
   
   
}

创建一个类,使用这个注解标注

package com.zjhc.componentSacn.annotation;
@MyAnno
public class Service1 {
   
   }

再来一个类,使用spring中的@Compontent标注

package com.zjhc.componentSacn.annotation;
import org.springframework.stereotype.Component;
@Component
public class Service2 {
   
   }

再来一个类,使用@CompontentScan标注

package com.zjhc.componentSacn;
@ComponentScan(includeFilters = {
   
   @ComponentScan.Filter(type= FilterType.ANNOTATION,classes = MyAnno.class)})
public class ScanBean2 {
   
   }

测试用例

 @Test
public void test3(){
   
   
    ApplicationContext context = new AnnotationConfigApplicationContext(ScanBean2.class);
    for (String beanName : context.getBeanDefinitionNames()) {
   
   
        System.out.println(beanName+"---->"+context.getBean(beanName));
    }
}

结果
在这里插入图片描述
问题:Service1上标注了@MyBean注解,被注册到容器了,但是没有标注@MyBean啊,怎么也被注册到容器了?
回答:@CompontentScan注解中的useDefaultFilters默认是true,表示会启用默认的过滤器,默认的过滤器会将标注有@Component、@Repository、@Service、@Controller这几个注解的类也注册到容器中。

修改扫描代码:
如果我们只想将标注有@MyBean注解的bean注册到容器,需要将默认过滤器关闭,即:useDefaultFilters=false

package com.zjhc.componentSacn;
@ComponentScan(useDefaultFilters = false,includeFilters = {
   
   @ComponentScan.Filter(type= FilterType.ANNOTATION,classes = MyAnno.class)})
public class ScanBean2 {
   
   }

再输出:
在这里插入图片描述

4.3.1.8.2 包含指定类型的类

被扫描的类满足IService.class.isAssignableFrom(被扫描的类)条件的都会被注册到spring容器中
@ComponentScan(
useDefaultFilters = false,
includeFilters = {
@ComponentScan.Filter(type= FilterType.ASSIGNABLE_TYPE,classes = IService.class)}
)
接口

package com.zjhc.componentSacn.annotation.componentBytype;
public
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一位不知名民工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值