自定义资源加载器ClassPathBeanDefinitionScanner

思想来源

其实这个思想是来源于mybatis和spring的整合,为什么通过@MapperScan(basePackages = { “demo.mapper” })就能扫描到自定义的Mapper接口,这一节只讲扫描过程,不讲Mapper接口是如何创建Bean对象的,接下来就开始探讨自定义ClassPathBeanDefinitionScanner资源加载器吧

自定Mapper接口

package com.source.controller.ClassPathBeanDefinitionScannerTest.mapper;

public interface TestClassPathScannerEmpMapper {
}

package com.source.controller.ClassPathBeanDefinitionScannerTest.mapper;

public interface TestClassPathScannerUserMapper {
}

MyMapperScan注解

package com.source.controller.ClassPathBeanDefinitionScannerTest;

import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//所有第三方框架都通过@Import进行整合到spring的
@Import(MyMapperScannerRegistrar.class)
public @interface MyMapperScan {
    //要扫描的包
    String[] basePackages() default {};
}

启动类

package com.source;

import com.source.controller.ClassPathBeanDefinitionScannerTest.MyMapperScan;
import com.source.controller.ClassPathBeanDefinitionScannerTest.MyMapperScannerConfigurer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
/**
 * 添加自己的注解,并指定要扫描的包
 */
@MyMapperScan(basePackages = {"com.source.controller.ClassPathBeanDefinitionScannerTest.mapper"})
public class SpringSourceApplication {

    public static ConfigurableApplicationContext context;

    public static void main(String[] args) {
        context = SpringApplication.run(SpringSourceApplication.class, args);
        MyMapperScannerConfigurer bean = context.getBean("myMapperScannerConfigurer", MyMapperScannerConfigurer.class);
    }
}

@Import导入的类

package com.source.controller.ClassPathBeanDefinitionScannerTest;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;

import java.beans.Introspector;
import java.util.Arrays;
import java.util.Map;

/**
 * MyMapperScannerRegistrar:这个类会在一开始解析SpringSourceApplication启动类的时候
 * 通过解析@MyMapperScan注解中的@Import导入的,那么spring就会回调registerBeanDefinitions()方法
 */
public class MyMapperScannerRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * 1:importingClassMetadata:是什么,就是当前正在解析的beanClass类的元信息,这里是:SpringSourceApplication类
     * 2:该方法是在ConfigurationClassPostProcessor解析beanDefinition时候回调的
     * 3:为什么能回调?因为该类实现了ImportBeanDefinitionRegistrar接口,具体去看源码
     * 4:registerBeanDefinitions()能做什么?能注册beanDefinition到容器中
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //importingClassMetadata:这个就是通过哪个类来导入的当前MyMapperScannerRegistrar
        //获取@MapperScan注解中的所有属性值,返回map
        Map<String, Object> sacnMap = importingClassMetadata.getAnnotationAttributes(MyMapperScan.class.getName());
        //转成AnnotationAttributes的目的是不用做map中value的强制类型转换了
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(sacnMap);
        //执行beanDefinition的注册
        registerBeanDefinitions(attributes,registry);
    }

    void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry){
        //构建bd对象
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyMapperScannerConfigurer.class);
        //此处利用内置的javaBean将字符串首字母小写
        String beanName=Introspector.decapitalize(MyMapperScannerConfigurer.class.getSimpleName());
        String[] basePackages = annoAttrs.getStringArray("basePackages");
        //向db中设置属性,StringUtils这个方法就是将集合一 " , " 分割 拼成字符串
        builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(Arrays.asList(basePackages)));
        //注册bean定义信息,后期会创建MyMapperScannerConfigurer的bean对象
        registry.registerBeanDefinition(beanName,builder.getBeanDefinition());
    }
}

资源扫描配置类后置处理器

package com.source.controller.ClassPathBeanDefinitionScannerTest;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;

/**
 * 该类是bean工厂的后置处理器
 * 1:要知道程序先执行了ConfigurationClassPostProcessor后置处理器(因为排序了),解析了所有的资源文件,并且注册了beanDefinition
 * 2:当前这个类,也生成了beanDefition对象,但是主要的一点是这个类实现了BeanDefinitionRegistryPostProcessor接口
 * 3:实现了BeanDefinitionRegistryPostProcessor接口,那么在刷新12步骤中就会创建所有的bean工厂后置处理器对象,并注册到一级缓存中
 * 4:并且会回调postProcessBeanDefinitionRegistry()方法
 */
public class MyMapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor {

    private String basePackage;

    public String getBasePackage() {
        return basePackage;
    }

    public void setBasePackage(String basePackage) {
        this.basePackage = basePackage;
    }

    /**
     *会回调bean工厂的增强方法,并把容器对象传过来
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        //创建自定义资源解析器对象
        MyClassPathMapperScanner mapperScanner = new MyClassPathMapperScanner(registry);
        //注册过滤器,目的是检查所有的mapper接口是否是备选组件
        mapperScanner.registerFilters();
        //对该包下的资源进行扫描
        mapperScanner.scan(basePackage);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

自定义资源扫描器实现

package com.source.controller.ClassPathBeanDefinitionScannerTest;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.AnnotationMetadata;
import java.util.Set;

public class MyClassPathMapperScanner extends ClassPathBeanDefinitionScanner {

    public MyClassPathMapperScanner(BeanDefinitionRegistry registry) {
        super(registry,false);
    }

    public void registerFilters() {
        boolean acceptAllInterfaces = true;
        if (acceptAllInterfaces) {
            //直接返回true,让当前包下的所有类都通过,这一步只要是重写TypeFilter中的match()方法
            //在super.doScan()中会回调这个函数接口的match()
            addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
        }
        //排除package-info.java
        addExcludeFilter((metadataReader, metadataReaderFactory) -> {
            String className = metadataReader.getClassMetadata().getClassName();
            return className.endsWith("package-info");
        });
    }

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        //这里又去调用父类的doScan()方法,就是让spring 去加载我们的mapper接口
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        processBeanDefinitions(beanDefinitions);
        return beanDefinitions;

    }

    /**
     * 在加载beanDefinition的时候,主要是这个判断,用来判断是否是候选组件的
     * 只有是候选组件spring才会为当前资源类创建beanDefinition对象
     */
    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition){
        //获取当前bean的大Class中的元信息
        AnnotationMetadata metadata = beanDefinition.getMetadata();
        //若是一个接口那么就返回true
        return metadata.isInterface();
    }

    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions){
        BeanDefinitionRegistry registry = super.getRegistry();
        for (BeanDefinitionHolder holder : beanDefinitions) {
            BeanDefinition beanDefinition = holder.getBeanDefinition();
            String beanClassName = beanDefinition.getBeanClassName();
            Class<? extends BeanDefinition> mapperClass = beanDefinition.getClass();
            //TODO 到这里就完成了mapper接口的加载,后面是为接口生成代理对象,后期会讲到
        }
    }
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值