spring IoC容器 扫描组件过滤器 导入器

39 篇文章 0 订阅
19 篇文章 0 订阅

IoC底层核心原理 扫描组件过滤器

分析底层源码的办法

查看底层源码 的层次结构
进入它的底层源码,按F4,在右边会有显示
先看它的接口,再看它接口的实现类是怎么做的

设定组件扫描加载过滤器

名称: @ComponentScan
类型:类注解
位置:类定义上方
作用:设置spring配置加载类扫描规则

范例
:@componentscan(
value=“com.itheima” //设置基础扫描路径
excludeFilters = //设置过滤规则,当前为排除过滤@componentScan.Filter( //设置过滤器
type= FilterType.ANOTATION //设置过滤方式为按照注解进行过滤
classes=Repository.class ) //设置具体的过滤项,过滤所有@Repository修饰的bean

includeFilters:设置包含性过滤器
excludeFilters:设置排除性过滤器
type:设置过滤器类型

//2.设置排除bean,排除的规则是自定义规则(FilterType.CUSTOM),具体的规则定义为(MyTypeFilter.class)

@ComponentScan(
        value = "com.itheima",
        excludeFilters = @ComponentScan.Filter(
                type= FilterType.CUSTOM,
                classes = MyTypeFilter.class
        )
)
public class MyTypeFilter implements TypeFilter {
    @Override
    //加载的类满足要求,匹配成功
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //通过参数获取加载的类的元数据
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //通过类的元数据获取类的名称
        String className = classMetadata.getClassName();
        //如果加载的类名满足过滤器要求,返回匹配成功
        if(className.equals("com.itheima.service.impl.UserServiceImpl")){
            //返回true表示匹配成功,返回false表示匹配失败。此处仅确认匹配结果,不会确认是排除还是加入,排除/加入由配置项决定,与此处无关
            return true;
        }
        return false;
    }
}

MyTypeFilter 这个类
return true 过滤所有的bean
return false 所有的bean都不过滤


自定义导入器

bean只有通过配置才可以进入spring容器,被spring加载并控制

配置bean的方式如下:
XML文件中使用标签配置
使用@Component及衍生注解配置

自定义导入器牵涉的业务问题
企业开发过程中,通常需要配置大量的bean,需要一种快速高效配置大量bean的方式

导入器

名称: ImportSelector
类型:接口
作用:自定义bean导入器

代码

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//      1.编程形式加载一个类
//      return new String[]{"com.itheima.dao.impl.BookDaoImpl"};

//      2.加载import.properties文件中的单个类名
//      ResourceBundle bundle = ResourceBundle.getBundle("import");
//      String className = bundle.getString("className");

//      3.加载import.properties文件中的多个类名
        ResourceBundle bundle = ResourceBundle.getBundle("import");
        String className = bundle.getString("className");
        return className.split(",");
    }
}

编写自定义导入器的类 要实现 ImportSelector接口
再将 需要导入的类的全类名封装成字符串数组 返回
return new String[]{“com.itheima.dao.impl.BookDaoImpl”};
在 SpringConfig 核心配置类上 用@Import(MyImportSelector.class),将需要导入的类转换成字节码文件,进行导入。在@Import里是可以写多个类名.class的

@Configuration
@ComponentScan("com.itheima")
//3.自定义导入器类
@Import(MyImportSelector.class)
public class SpringConfig {
}

通过配置文件统配的形式,定义导入器

import.properties 导入器配置文件

path=com.itheima.dao.impl.*

用通配符指定通配的包路径

接下来编写它的 导入器 的实体类

public class CustomerImportSelector implements ImportSelector {

    private String expression;

    public CustomerImportSelector(){
        try {
            //初始化时指定加载的properties文件名
            Properties loadAllProperties = PropertiesLoaderUtils.loadAllProperties("import.properties");
            //设定加载的属性名
            expression = loadAllProperties.getProperty("path");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //1.定义扫描包的名称
        String[] basePackages = null;
        //2.判断有@Import注解的类上是否有@ComponentScan注解
        if (importingClassMetadata.hasAnnotation(ComponentScan.class.getName())) {
            //3.取出@ComponentScan注解的属性
            Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
            //4.取出属性名称为basePackages属性的值
            basePackages = (String[]) annotationAttributes.get("basePackages");
        }
        //5.判断是否有此属性(如果没有ComponentScan注解则属性值为null,如果有ComponentScan注解,则basePackages默认为空数组)
        if (basePackages == null || basePackages.length == 0) {
            String basePackage = null;
            try {
                //6.取出包含@Import注解类的包名
                basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            //7.存入数组中
            basePackages = new String[] {basePackage};
        }
        //8.创建类路径扫描器
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        //9.创建类型过滤器(此处使用切入点表达式类型过滤器)
        TypeFilter typeFilter = new AspectJTypeFilter(expression,this.getClass().getClassLoader());
        //10.给扫描器加入类型过滤器
        scanner.addIncludeFilter(typeFilter);
        //11.创建存放全限定类名的集合
        Set<String> classes = new HashSet<>();
        //12.填充集合数据
        for (String basePackage : basePackages) {
            scanner.findCandidateComponents(basePackage).forEach(beanDefinition -> classes.add(beanDefinition.getBeanClassName()));
        }
        //13.按照规则返回
        return classes.toArray(new String[classes.size()]);
    }
}

写完导入器实体类 还要在springConfig核心配置类中的 类注解 @import的括号内 写出这个导入器实体类的字节码文件,使用反射的方式来加载。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

普希托夫

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

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

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

打赏作者

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

抵扣说明:

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

余额充值