Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >逐行阅读Spring5.X源码(番外篇)自定义扫描器, Mybatis是如何利用spring完成Mapper扫描的

逐行阅读Spring5.X源码(番外篇)自定义扫描器, Mybatis是如何利用spring完成Mapper扫描的

作者头像
源码之路
发布于 2020-09-04 02:09:11
发布于 2020-09-04 02:09:11
1.3K0
举报
文章被收录于专栏:源码之路源码之路

上一篇详细讲解了spring的扫描器ClassPathBeanDefinitionScanner,本篇我们我们将模拟mybatis如何通过spring完成Mapper扫描,讲解如何通过spring编写自定义扫描器。 既然ClassPathBeanDefinitionScanner完成了spring的扫描功能,我们完全可以继承这个类来达到创建自定义扫描器的目的。

代码语言:javascript
AI代码解释
复制
public class CustomScanner extends ClassPathBeanDefinitionScanner {
    public CustomScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }
}

这就是我们的自定义扫描器,当然什么功能也没有,再次基础上我们继续进行扩展,上一篇我们讲过ClassPathBeanDefinitionScanner通过includeFilters来过滤符合条件的业务类,那我们增加我们的扫描规则即可。如何includeFilters在添加我们的扫描规则?很简单,实现addIncludeFilter即可

代码语言:javascript
AI代码解释
复制
public class CustomScanner extends ClassPathBeanDefinitionScanner {
    public CustomScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }
    @Override
    public void addIncludeFilter(TypeFilter includeFilter) {
        super.addIncludeFilter(includeFilter);
    }
}

也就是说,我们只要编写我们的规则类TypeFilter 就行了,剩下的交给ClassPathBeanDefinitionScanner来完成,有读者嘲讽:“我还以为你要模拟一个ClassPathBeanDefinitionScanner”,答:“我闲的?mybatis也是利用了ClassPathBeanDefinitionScanner好吗?”。 TypeFilter只是一个接口,它的实现类在哪里?spring启动时会默认生成一个ClassPathBeanDefinitionScanner对象,该对象在初始化时会生成一坨默认的过滤器(上篇讲过):

代码语言:javascript
AI代码解释
复制
    protected void registerDefaultFilters() {
        this.includeFilters.add(new AnnotationTypeFilter(Component.class));
        ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();

        try {
            this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.annotation.ManagedBean", cl), false));
            this.logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
        } catch (ClassNotFoundException var4) {
        }

        try {
            this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.inject.Named", cl), false));
            this.logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
        } catch (ClassNotFoundException var3) {
        }

    }

看到没,就是AnnotationTypeFilter啦!

OK,我们先自定义一个注解:

代码语言:javascript
AI代码解释
复制
public @interface MyAnnotation {
}

再生成两个业务类,加上上面的注解:

代码语言:javascript
AI代码解释
复制
@MyAnnotation
public class MyClass {
}

@MyAnnotation
public class MyClass2 {
}

测试以下:

代码语言:javascript
AI代码解释
复制
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //注册配置类
        context.register(Config.class);
        CustomScanner customScanner = new CustomScanner(context);
        customScanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class));
  //将带有MyAnnotation注解的类扫描到springIOC容器中,并返回扫描的个数
        int num = customScanner.scan("com.scan");
        System.out.println("扫描的个数:"+num);
        context.refresh();

        System.out.println(context.getBean(MyClass.class));

    }
}
打印结果:
扫描的个数:2
com.scan.MyClass@1199fe66

哈哈,简单吧!其实,这就是spring扩展,要扩展就得懂spring源码,欢迎读者关注本专题,我将带大家深入分析并解开spring源码神秘面纱。现在java开发,逃不掉spring全家桶! spring只能扫描实体类,抽象类和接口是不能扫描的,看上篇讲过的源码:

代码语言:javascript
AI代码解释
复制
if (isCandidateComponent(sbd)) {
            if (debugEnabled) {
                    logger.debug("Identified candidate component class: " + resource);
                                }
            //返回BeanDefinition 注册到 BeanFactory
            candidates.add(sbd);
    }

isCandidateComponent这个方法判断 如果是实体类 返回true,如果是抽象类,但是抽象方法 被 @Lookup 注解注释返回true。

代码语言:javascript
AI代码解释
复制
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        AnnotationMetadata metadata = beanDefinition.getMetadata();
        return (metadata.isIndependent() && (metadata.isConcrete() ||
                (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
    }

也就是说如果你是接口,isCandidateComponent返回的是false,candidates.add(sbd);就不执行了,自然添加不到IOC容器中,mybatis就是修改了这个方法,让Mapper接口能扫描到IOC容器中的,我们先模拟:

代码语言:javascript
AI代码解释
复制
@MyAnnotation
public interface MyInterFace {
    
    @Select("select * from table")
    public void select();
}

public class CustomScanner extends ClassPathBeanDefinitionScanner {
    public CustomScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }

    @Override
    public void addIncludeFilter(TypeFilter includeFilter) {
        super.addIncludeFilter(includeFilter);
    }

    @Override
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
//如果是接口,则返回true,会添加到IOC容器中去
            return beanDefinition.getMetadata().isInterface();

    }
}

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //注册配置类
        context.register(Config.class);
        CustomScanner customScanner = new CustomScanner(context);
        customScanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class));
        int num = customScanner.scan("com.scan");
        System.out.println("扫描的个数:"+num);
        context.refresh();
    }
}
打印结果
扫描的个数:1

然后,spring会根据MyInterFace生成一个动态代理类,并通过反射拿到你所有的方法,拿到方法后再通过反射拿到你的注解信息,还需要我继续说吗? 实际上,mybatis就是这么做的,不信看mybatis源码:

代码语言:javascript
AI代码解释
复制
ClassPathMapperScanner.java // mybatis的自定义扫描类
   protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }

这就是spring扩展开发,插件开发就是这个原理吧,前提是你要对spring足够的掌握,欢迎大家关注本专题,一起精通spring源码。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
逐行阅读Spring5.X源码(六) ClassPathBeanDefinitionScanner扫描器
spring包扫描是通过ClassPathBeanDefinitionScanner类来完成的,它主要工作有两个:
源码之路
2020/09/04
7670
逐行阅读Spring5.X源码(六) ClassPathBeanDefinitionScanner扫描器
Spring源码解析之BeanFactoryPostProcessor(二)
上一章,我们介绍了在AnnotationConfigApplicationContext初始化的时候,会创建AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner两个对象:
huofo
2022/03/18
3090
Spring源码解析之BeanFactoryPostProcessor(二)
SpringFramework之ClassPathBeanDefinitionScanner
                                                                                         图1
克虏伯
2019/06/28
9900
SpringFramework之ClassPathBeanDefinitionScanner
Spring源码:bean的生命周期(一)
本节将正式介绍Spring源码细节,将讲解Bean生命周期。请注意,虽然我们不希望过于繁琐地理解Spring源码,但也不要认为Spring源码很简单。在本节中,我们将主要讲解Spring 5.3.10版本的源代码。如果您看到的代码与我讲解的不同,也没有关系,因为其中的原理和业务逻辑基本相同。为了更好地理解,我们将先讲解Bean的生命周期,再讲解Spring的启动原理和流程,因为启动是准备工作的一部分。
努力的小雨
2024/04/30
2130
Spring IoC 源码分析 (基于注解)(二) 之 包扫描
在上篇文章Spring IoC 源码分析 (基于注解) 一我们分析到,我们通过AnnotationConfigApplicationContext类传入一个包路径启动Spring之后,会首先初始化包扫描的过滤规则。那我们今天就来看下包扫描的具体过程。
周同学
2019/08/29
9400
Spring IoC 源码分析 (基于注解)(二) 之 包扫描
Spring注解Component原理源码解析
在实际开发中,我们经常使用Spring的@Component、@Service、@Repository以及 @Controller等注解来实现bean托管给Spring容器管理。Spring是怎么样实现的呢?我们一起跟着源码看看整个过程吧!
GreizLiao
2019/10/14
1.5K0
Spring注解Component原理源码解析
这一次搞懂Spring自定义标签以及注解解析原理
在上一篇文章中分析了Spring是如何解析默认标签的,并封装为BeanDefinition注册到缓存中,这一篇就来看看对于像context这种自定义标签是如何解析的。同时我们常用的注解如:@Service、@Component、@Controller标注的类也是需要在xml中配置<context:component-scan>才能自动注入到IOC容器中,所以本篇也会重点分析注解解析原理。
夜勿语
2020/09/07
6070
从Spring源码探究IOC初始化流程
本文是基于注解的IOC初始化,不是XML!!! 代码的含义我都以注释的形式写在代码块中了,请放心查阅
向着百万年薪努力的小赵
2022/12/02
6130
@ComponentScan原理分析
这是@ComponentScan的官方介绍,大致意思就是扫描注册bean的一个注解,会扫描对应路径下被@Component标注的类,和xml方式的<context:component-scan>作用相似,常用的方式是basePackages方式。
叔牙
2022/01/04
9730
@ComponentScan原理分析
Spring源码解析(二):bean容器的创建、注册默认后置处理器、bean定义扫描类
Java微观世界
2025/01/21
4840
Spring源码解析(二):bean容器的创建、注册默认后置处理器、bean定义扫描类
【小家Spring】Spring容器加载Bean定义信息的两员大将:AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
在分析Spring IOC容器启动流程的时候,在加载Bean定义信息BeanDefinition的时候,用到了两个非常关键的类:AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner。它俩完成对Bean信息的加载。
YourBatman
2019/09/03
2.4K0
【小家Spring】Spring容器加载Bean定义信息的两员大将:AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
源码揭秘!Spring中Bean扫描的原理!
Spring和MyBatis整合的时候用到的Bean扫描是它Spring本身提供的。这一篇文章就写一下Spring是如何实现Bean扫描的。 不得不说Bean扫描是一个很重要的技术,在SpringMVC中的Controller扫描,和SpringBoot中的Bean扫描,Component扫描,Configuration扫描,原理应该都是由这个实现的。
PHP开发工程师
2022/04/07
7120
源码揭秘!Spring中Bean扫描的原理!
《Spring核心技术》第2章:@ComponentScan注解,又是三万字!!
作者:冰河 星球:http://m6z.cn/6aeFbs 博客:https://binghe.gitcode.host 文章汇总:https://binghe.gitcode.host/md/all/all.html 源码地址:https://github.com/binghe001/spring-annotation-book/tree/master/spring-annotation-chapter-02
冰河
2023/02/21
8810
《Spring核心技术》第2章:@ComponentScan注解,又是三万字!!
Spring IoC 源码分析 (基于注解) 一
IoC 全称为 Inversion of Control,翻译为 “控制反转”,它还有一个别名为 DI(Dependency Injection),即依赖注入。
周同学
2019/08/29
1.2K0
Spring  IoC 源码分析 (基于注解) 一
【小家Spring】Spring解析@ComponentScan注解源码分析(ComponentScanAnnotationParser、ClassPathBeanDefinitionScanner)
前面我在这篇博文:【小家Spring】Spring解析@Configuration注解的处理器:ConfigurationClassPostProcessor(ConfigurationClassParser) 解释Spring解析@Configuration的时候,提到过了解析:@PropertySource、@ComponentScan、@Import…等等的解析过程。
YourBatman
2019/09/03
1.6K0
《Spring6核心技术》第10章:深度解析@Component注解(注解+案例+时序图+源码)
@Component注解可以说是Spring中使用的比较频繁的一个注解了。在项目开发过程中,我们自己编写的类如果想注入到Spring中,由Spring来管理Bean的生命周期,就可以使用@Component注解将其注入到IOC容器中。
冰河
2023/05/24
2.5K0
《Spring6核心技术》第10章:深度解析@Component注解(注解+案例+时序图+源码)
同事问我:为什么我的Service无法注入进来?
我其实已经知道是啥情况了,但是怕他不知道,所以还是耐心的跟她解释了一下,她听完后说:能不能写下来啊,免得我下次还会忘。
敖丙
2022/03/24
1.6K0
同事问我:为什么我的Service无法注入进来?
OpenFeign源码解读
前言: 参考学习链接: 1.https://www.bilibili.com/video/BV11D4y1C73V/?spm_id_from=333.1007.top_right_bar_window
小爽只会CRUD
2023/05/09
1.1K0
OpenFeign源码解读
牛逼哄哄的Spring是怎么被MyBatis给征服了?
其实前几篇文章已经写了好多有关于Spring源码的文章,事实上,很多同学虽然一直在跟着阅读、学习这些Spring的源码教程,但是一直都很迷茫,这些Spring的源码学习,似乎只是为了面试吹逼用,我大概问过一些同学,很多同学看了很长时间的Spring但是依旧不知道如何将这些学到的知识运用到实际的案例上!
止术
2020/09/15
5540
牛逼哄哄的Spring是怎么被MyBatis给征服了?
Mybatis整合Spring
前面我们已经知道如果mybatis中进行查询时,在getMapper之后,会执行查询动作,而执行查询动作的时候,会触发动态代理操作,这个过程是在binding中完成的,即此时会触发MapperMethod#invoke操作->mapperMethod.execute(sqlSession, args),这个操作的过程中,会执行到具体的查询操作,而具体的查询动作中,我们可以此时会走到一个sqlCommandType的判断中,也即case语句中:
路行的亚洲
2021/03/04
1K0
推荐阅读
相关推荐
逐行阅读Spring5.X源码(六) ClassPathBeanDefinitionScanner扫描器
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档