5. 自定义扫描规则 TypeFilter 与 @Scope 作用域 @Lazy 懒加载

256 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

1. TypeFilter 类

这个类是一个注解类,包含 注解, 正则表达式 用户自定义等等过滤规则

public enum FilterType {
    ANNOTATION,
    
    ASSIGNABLE_TYPE,
    
    ASPECTJ,
    
    REGEX,
    
    CUSTOM
}

在 @Filter 指定过滤规则,其中 type 来表示过滤的类型

FilterType.ASSIGNABLE_TYPE 表示按照给定的类型进行包含或者排除

@ComponentScan(value = {"com.shanggushenlong"}, 
                includeFilters = {
            @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
}))
  • 表示 当 BookService 是一个java类的时候,该类及其子类都会被加载到 spring 容器中
  • 表示 当 BookService 是一个接口的时候,其子接口及其实现类都会被加载到 spring 容器中

FilterType.CUSTOM:按照自定义规则进行包含或者排除

如果实现自定义规则进行过滤时,自定义规则的类必须是实现 org.springframework.core.type.filter.TypeFilter 接口的类

2. 用户自定义过滤类

自定义规则进行过滤的时候,首先创建 MyFilterType 实现 TypeFilter 接口,重写 match 匹配方法

public class MyTypeFilter implements TypeFilter {

    // 使用 @ComponentScan 注解,type = FilterType.ASSIGNABLE_TYPE 用户自定义规则过滤
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        return false;
    }
}

MetadataReader:对一个类的各种元数据都封装成一个 MetadataReader 对象


/**
 * Factory interface for MetadataReader instances.  
 *  Allows for caching a MetadataReader per original resource.
 */
public interface MetadataReaderFactory { }

MetadataReaderFactory:上面注释的意思是 MetadataReaderFactory 是 MetadataReader 的 instance 实例工厂

自定义实现过滤规则,match() 方法规则匹配,返回 true 表示符合规则,会被 spring 扫描包含在容器中;返回 false,表示不匹配,则不会被包含

3. 创建 MyTypeFilter 自定义过滤类

@ComponentScan(value = {"com.shanggushenlong"} , 
                includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM,
                                                    classes = {MyTypeFilter.class})} , 
                useDefaultFilters = false)
@Configuration
public class MainBeanConfig {

    @Bean(value = {"person01"})
    public Person person() {
        return new Person("张三", 28);
    }
}

现在设置 type = FilterType.CUSTOM 用户自定义,classes 指向自定义创建的类,记住要设置 useDefaultFilters = false

image.png

由于 match() 返回值为 false, 表示所有的都不匹配,所以可以看到没有任何 controller service dao 等被扫描出来

现在设置一个自己规则

比如设置 含有 er 字符串的,会被扫描到

public class MyTypeFilter implements TypeFilter {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

        // 获取当前类注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();

        // 获取当前正在扫描类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        // 获取类名
        String className = classMetadata.getClassName();

        // 设置一个规则,如果类名中含有 er ,则将被 spring 扫描进容器中
        if (className.contains("er")) {
            System.out.println("className = " + className);
            return true;
        }

        return false;
    }
}

image (1).png

由于 controller 与 service 都含有 er ,所以此处都能被扫描

4 单实例 组件作用域

1、 spring 容器中的组件默认是单实例,在 spring 容器启动的时候就会去初始化这些对象,并不是等需要的时候再去初始化这些对象

2、 spring 默认是单实例对象,那么默认情况下,每次从容器中获取对象的时候,都不再重新创建,都是同一个对象

3、如果通过 xml 配置文件来配置 组件的作用域 可以在 <bean> 标签设置 scope 属性 <bean id="person" class="com.shanggushenlong.bean.Person" scope="prototype">

4、在 ConfigurableBeanFactory 中值: singleton 单实例 prototype 多实例

  1. 如果单实例:那么 spring 容器创建的时候,就会将 @Scope 注解标注为 singleton 组件进行了实例化,并加载到了 spring 容器中

5. 多实例

public class MainBeanConfig {

    @Scope("prototype")
    @Bean(value = {"person01"})
    public Person person() {
        return new Person("张三", 28);
    }
}

使用 @Scope 注解设置为 prototype 多实例的时候:

  • 当向 spring 容器获取 person 实例对象的时候,spring 容器才会实例化 person 对象,并且再加载到 spring 容器中去
  • 当设置为多实例的时候,每次向spring容器获取对象的时候,都会创建一个新的对象

6. @Lazy 懒加载

spring 在启动时,默认会将单实例 bean 进行实例化,并加载到 spring 容器;如果想要对某个 bean 进行延迟记载,使用 @Lazy 注解

懒加载就是 spring 在启动的时候,先不创建对象,在第一次使用获取bean的时候再来创建对象,并进行初始化;注意:依旧是单实例,有点类似于 按需加载 ,但是不会重复创建