当前位置: 首页 > news >正文

【微服务专题】SpringBoot自动配置简单源码解析

目录

  • 前言
  • 阅读对象
  • 阅读导航
  • 前置知识
    • 什么是自动配置
      • 0.1 基本概念
      • 0.2 SpringBoot中的【约定大于配置】
      • 0.3 从SpringMVC看【约定大于配置】
      • 0.4 从Redis看【约定大于配置】
      • 0.5 小结
  • 笔记正文
    • 一、@EnableAutoConfiguration源码解析
    • 二、SpringBoot常用条件注解源码解析
      • 2.1 自定义条件注解
      • 2.2 @ConditionalOnClass原理解析
      • 2.3 ConditionalOnBean原理解析
    • 三、SpringBoot之Mybatis自动配置源码解析
    • 四、SpringBoot之AOP自动配置类
  • 学习总结
  • 感谢

前言

想要搞懂【自动配置】,需要当前系列前两篇笔记内容打底。不过我上一篇笔记写的不好,没有什么突出的重点,主要也是当时跟着课程看了源码之后,也没发现什么特别的,直到在看自动装配源码的时候,才后知后觉遗漏了一些重要的知识点。但是总体来说不影响这一节课的笔记记录吧。
另外说到【自动配置】,以前我总是掉入一个思维陷阱里面,认为这里说到的配置就是xml配置,这让我在理解【自动配置】的时候总是走了一些弯路,所以这里也是给大家提前提个醒了,这个配置不仅仅是xml配置而已,而是包括任何形式的,对服务接口的配置。

阅读对象

  1. 对自动配置感兴趣的朋友

阅读导航

系列上一篇文章:《【微服务专题】Spring启动过程源码解析》

前置知识

什么是自动配置

我们说起SpringBoot,总会不自觉的联想到自动配置约定大于配置等标签,但说实在,我是有一点困惑的,这个【约定和配置】到底是什么意思,或者说,它们具体指的是哪些内容啊?!
我在想,像我这种,只是用过那么一年多SSM/SSH,甚至完全没有经历过的人来说,是很难理解的。虽然我也确实经历过SSM/SSH,但那时候初出茅庐,根本就没在意过这些玩意好吧…

不过话说回来,后面再回来继续做Java的时候,已经是SpringBoot的时代了嘛,所以单从片面印象来说的话:SpringBoot确实方便了很多。最最最直观的就是启动,给我的感觉启动一个web应用跟简单运行了一个main方法一样便捷。而在以前SSM/SSH时代,各种眼花缭乱的xml配置,还有那个令人烦躁的tomcat配置,就已经让我很难受了。

好吧,不懂嘛,于是我就去稍微学习了一下。下面,我就按照我自己的理解,再拼上一些百度的、起码能说服我自己的答案给大家说道说道。另外,也想举一个真正直观的例子给大家说说,【约定和配置】到底使了什么魔法

0.1 基本概念

【约定大于配置】是一种开发思想、设计理念,它强调使用默认约定和规则,减少开发者需要做的【显式】配置。根据这一原则,大部分情况下,开发者只需关注那些【不符合默认约定】的配置,而不需要为每个细节都进行显式配置。

开天眼给大家说一下:既然结果是减少了需要程序员【显示】配置的工作量,那反过来就证明了,在SpringBoot之前的时代,有一些配置是不得不(必须)需要程序员去配置的!
另外,这里说的【配置】,不仅仅是指xml配置,准确来说是对引入的服务的所有形式的配置,xml只是一种形式而已。我们还可以是txtyml,还有java文件的配置方式。
我认为这个结论很重要!我后面研究案例就是往这方向去看的

综上所述,可以这样简单地理解【约定】和【配置】:

  1. 默认规则
  2. 【显示】配置(不得不设置的配置 / 没办法使用默认规则的配置)。再重申一遍:这里说的【配置】,不仅仅是指xml配置,准确来说是对引入的服务的所有形式的配置,xml只是一种形式而已。我们还可以是txtyml,还有java文件的配置方式。

这个理念,有如下显著的优势:

  1. 提高开发效率: 通过遵循默认约定,开发者可以快速启动项目并进行开发,无需花费大量时间在繁琐的配置上
  2. 减少决策负担: 【约定大于配置】减少了开发者需要做出的决策,使开发过程更加流畅,不需要在每个细节上做出选择
  3. 减少错误: 默认约定可以减少配置错误的机会,因为开发者只需要在特定情况下进行配置,从而降低了出错的可能性

0.2 SpringBoot中的【约定大于配置】

我们都知道,SpringBoot是基于Spring的,严格来说,SpringBoot的出现其实是为了降低Spring的使用门槛。

  1. 使用maven的目录结构。默认有src-main-resources文件夹,存放资源配置文件;src-main-java存放项目java源码;target文件夹存放编译、打包内容(其实,Maven的设计本身就是遵循【约定大于配置】的原则)

这是约定的一种,通过约定这些内容,使得项目结构统一,减少了开发人员学习成本。
试想一下,如果我们在不同的公司,他们的项目结构五花八门,甚至没有src/main/resources,没有src/main/java等目录,你学习成本是不是变高了??博主之前写过2、3年C/C++,那时候接触的项目就是这个鸟样子的,都是自定义资源存放目录,打包目录,源码目录,真的吐血… … 我转战Java很大一个原因就是奔着Java的规范性去的。当然,我现在相当后悔,当时忍一忍就不会掉进Java的坑里了

  1. 使用application.properties/yml文件设置配置内容

这也是一种约定,拒绝程序员五花八门的配置文件

  1. 默认使用Tomcat容器

接触过SSM吗?SSM项目通常来说,需要额外配置启动一个Tomcat,然后将SSM项目打包部署到Tomcat上
而在SpringBoot中,我们启动项目就跟运行一个普通的main函数一样

  1. 提供了大量的自动配置接口,自动配置类,注解等,帮助程序员配置。严格来说,自动配置接口以及注解等,都是为了自动配置类服务的。SpringBoot提供的大量自动配置类,在里面默认设置了很多值用以配置第三方接口服务,这些默认配置甚至能让开发做到开箱即用,只有一些不得不需要用户设置的内容才需要开发人员自己设置。这就是上面概念所谓的开发者只需要关注那些不符合默认约定的配置的意义。

这是SpringBoot【约定大于配置】的重要实现。我们先稍微解释一下,这些所谓的接口、类、注解是指什么:

  • 自动配置接口:AutoConfigurationImportFilter、AutoConfigurationImportListener等
  • 自动配置类:DispatcherServletAutoConfiguration、HttpEncodingAutoConfiguration等
  • 注解:诸如@AutoConfiguration、@AutoConfigurationPackage等

    上述我只是简单列举了一些内容而已,其实具体的,可以看:org.springframework.boot:spring-boot-autoconfigure这个包。里面就是SpringBoot为大家做自动配置提供的默认配置,和相关接口

0.3 从SpringMVC看【约定大于配置】

说实在,SpringMVC我不是很熟悉,所以我不是很保证自己说的是对的,在网上也没看到非常令人信服的答案。但是关于【约定大于配置】的体现我觉得还是正确的。

有人说spring-boot-starter-web其实就是免配置的SSM。在SSM时代,我们想要开发一个web应用,免不了如下配置:

  1. 新增web.xml,在里面配置前端控制器DispatcherServlet、视图解析器ViewResolver、过滤器等
  2. 新增核心配置文件**-servlet.xml
  3. Tomcat容器的配置
  4. 等等…

其实说需要新增这种那种xml配置是有点狭隘的说法,我前面说过了xml只是其中一种配置形式而已。
准确来说,传统的SSM项目,需要我们开发人员显式、准确地向Spring容器注册DispatcherServletViewResolverSqlSessionFactoryBeanDriverManagerDataSource
如果我们没有【显式】地去声明、注册这些Bean,那我们项目直接启动失败!他会说找不到这个找不到那个。但是,在SpringBoot中,我们只需要在pom中引入spring-boot-starter-web就可以了,这个jar包,会自动向Spring中注册上述关键bean

怎么注册?我们简单看一下关于SpringMVC的自动配置类。

1)WebMvcAutoConfiguration.java:SpringMVC的自动配置入口
在这里插入图片描述
在这里我们无需关心上面的诸多注解的具体含义先,具体意义我会在后面的源码解析中介绍。
但是可以预见的是,在某种情况下,spring-boot-stater-web会进入DispatcherServletAutoConfiguration.class里面

2)DispatcherServletAutoConfiguration.java:在这里,有一个@Bean,暂不关心他什么时候会被触发,但至少可以预见的是:在某种情况下,DispatcherServletAutoConfiguration类会向Spring中注册一个DispatcherServlet类型的bean
在这里插入图片描述
啊,对的,本质上就是如此而已。SpringBoot的自动配置,实际上就是SpringBoot的源码中预先写好了一些配置类,预先定义好了一些Bean,我们在用SpringBoot时,这些配置类就已经在我们项目的依赖中了,而这些自动配置类或自动配置Bean到底生不生效,就看具体所指定的条件了。

不过还有一点需要说明,那就是SpringBoot可不是简简单单的给你注册了一个默认的bean,它还会给部分【符合约定】的基础参数设置默认的、至少有意义的、比较符合一般应用的初始值来初始化bean。这个就是SpringBoot提供的各种XxxProperties.java。例如:WebMvcProperties
在这里插入图片描述
O对了,说到了这个XxxProperties.java文件,其实它也是约定的一种体现。以前SSM我们相当于直接配置了服务接口的一些参数;而在SpringBoot里面,我们直接配置的其实是XxxProperties,然后再由SpringBoot使用XxxProperties配置属性去初始化服务接口参数。而通常这些XxxProperties对应的是application.properties里面的一些配置内容。
相比起直接配置服务接口参数,XxxProperties可读性更强,使用也更简单。

0.4 从Redis看【约定大于配置】

想到SpringMVC可能很多人都不熟悉,所以这边再介绍一下,Redis是怎么实现【约定大于配置】的。其实大体思路跟上面差不多:

  1. 找到有哪些不得不需要程序员配置的类
  2. 找到Redis自动配置类
  3. 看看Redis的相关自动配置XxxProperties文件

如下:(其实1和2基本上都可以在自动配置类里面体现出来)
在这里插入图片描述
当然不止这两个类,还有一些类在上面的@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })被声明,这俩是不同的客户端而已,给大家贴部分代码截图,如下:
在这里插入图片描述
在这里插入图片描述
从上面这两张代码截图图可以看出,SpringBoot默认使用Lettuce作为客户端。只有我们在项目中引入了JedisJedis客户端才会生效。

再然后就是这个RedisProperties文件,是SpringBoot提供给我们的直接配置类:
在这里插入图片描述

0.5 小结

OK,关于SpringBoot中的【约定大于配置】就介绍到这里了。那他们是怎么实现的呢?有经验的朋友,或者稍微细心的朋友估计也从上图看到了,【自动配置】主要是靠下图这些类、接口、注解实现的,后面我们就来稍微研究一下,里面的源码实现。(org.springframework.boot:spring-boot-autoconfigure包下)
在这里插入图片描述

笔记正文

一、@EnableAutoConfiguration源码解析

说实在,水平有限,解析不了很深层次。先来看看该注解声明:

/*** 启用Spring应用程序上下文的自动配置,尝试猜测和配置您可能需要的bean。* 自动配置类通常基于您的类路径和您定义的bean来应用。例如,如果您的类路径中有* tomcat-embedded.jar,那么您可能需要一个TomcatServletWebServerFactory* (除非您已经定义了自己的ServletWebServerFactory bean)。* 当使用@SpringBootApplication时,上下文的自动配置是自动启用的,因此添加这个注释没有额外的效果。* 自动配置试图尽可能地智能,并且会随着您定义更多自己的配置而后退。* 您始终可以手动排除()任何您不想应用的配置(如果您没有访问权限,请使用excludeName())。* 你也可以通过spring.autoconfigure.exclude属性排除它们。* 自动配置总是在用户定义bean注册之后应用。* 用@EnableAutoConfiguration注释的类包,通常通过@SpringBootApplication,* 具有特定的意义,通常被用作“默认”。例如,它将在扫描@Entity类时使用。通常建议将* @EnableAutoConfiguration(如果不使用@SpringBootApplication)放在根包中,* 以便可以搜索所有子包和类。* 自动配置类是常规的Spring @Configuration bean。它们是使用importcandidate和* springfactoresloader机制定位的(与这个类相关)。通常自动配置bean是@条件bean(最常使用* @ConditionalOnClass和@ConditionalOnMissingBean注释)。*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {/*** Environment property that can be used to override when auto-configuration is* enabled.*/String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";/*** Exclude specific auto-configuration classes such that they will never be applied.* @return the classes to exclude*/Class<?>[] exclude() default {};/*** Exclude specific auto-configuration class names such that they will never be* applied.* @return the class names to exclude* @since 1.3.0*/String[] excludeName() default {};}

我把官方声明注释也一起搬过来了。我稍微解读一下:

  1. @EnableAutoConfiguration 用来启用Spring的自动配置功能
  2. 它核心原理主要在于:推测,推测程序要需要的Bean。比如:我们的项目jar包中依赖了Jetty,那么推测你可能需要Jetty容器;项目依赖了redis,那就推测你需要Redis
  3. 通常自动配置bean会与@Conditional注解一起工作
  4. 自动配置的实现使用了SPI机制

OK,回到正文。@EnableAutoConfiguration中,有两个很明显的引用:

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)

所以不难看出来,自动配置的核心实现逻辑就在这两个类里面了。

1)@AutoConfigurationPackage
点进去看,显然核心实现还是在于@Import(AutoConfigurationPackages.Registrar.class)

@Import注解,可以简单的理解为,直接导入一个bean

在这里插入图片描述
再点进去看,核心主要是registerBeanDefinitions方法(注册Bean图纸)。这个方法会在哪里调用呢?大概是在Spring容器初始化之后,实例化所有Bean之前。
其实也没啥好说的了,他的作用很简单,就是向Spring容器注册了一个BeanDefinition叫做autoConfigurationPackages,这个BeanDefinition里面有一个重要属性,属性存放的是当前扫描包路径,有啥用呢?大概是为了方便第三方扫描的时候,能够方便获取。比如Mybatis自动配置类扫描的时候就用到了这个BeanDefinition。
在这里插入图片描述
2)@Import(AutoConfigurationImportSelector.class)
老规矩,看里面这个AutoConfigurationImportSelector.class。他的定义如下:

// DeferredImportSelector处理自动配置。
// 如果需要@EnableAutoConfiguration的自定义变体,这个类也可以被子类化。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

这个类的核心在于实现了DeferredImportSelector,他的类继承图如下:
在这里插入图片描述
整个方法的入口其实有点难找,需要看了源码才知道,具体在哪里会被调用呢?跟上面一样,【大概是在Spring容器初始化之后,实例化所有Bean之前】。但这并不是篇内容的核心,所以我直接贴图调用处:
在这里插入图片描述
可以看到,最终实现还是由红框内的getAutoConfigurationEntry,通过方法翻译函数名为:获取自动配置入口。(从这里开始就有点复杂了,SPI的实现就在里面)
在这里插入图片描述
步骤解析:

  1. 获取@SpringBootApplication注解上的排除路径配置
    在这里插入图片描述
  2. 使用SPI技术,获取所有候选的配置类,这应该是最核心的实现了

这里会去本包,包括所有依赖的jar包下的META-INF/spring.factories下加载EnableAutoConfigurationAutoConfiguration注解的类

  1. 去重
  2. 加载排除路径
  3. 查找需要被去除自动配置类,然后去除
  4. 过滤一遍,判断所有自动配置类上面的条件注解是否生效。对于不生效的自动配置类也没必要再去走后面的逻辑了
  5. 发布自动配置类导入事件

3)源码入口
对上面提到的两个核心类被Spring容器调用时机感兴趣的朋友,可以看看ConfigurationClassPostProcessor类。对Spring源码不熟悉或者生命周期不熟悉的朋友估计有点压力。我也有点压力…

二、SpringBoot常用条件注解源码解析

SpringBoot中的常用条件注解有:

  • ConditionalOnBean:是否存在某个某类或某个名字的Bean
  • ConditionalOnMissingBean:是否缺失某个某类或某个名字的Bean
  • ConditionalOnSingleCandidate:是否符合指定类型的Bean只有一个
  • ConditionalOnClass:是否存在某个类
  • ConditionalOnMissingClass:是否缺失某个类
  • ConditionalOnExpression:指定的表达式返回的是true还是false
  • ConditionalOnJava:判断Java版本
  • ConditionalOnWebApplication:当前应用是不是一个Web应用
  • ConditionalOnNotWebApplication:当前应用不是一个Web应用
  • ConditionalOnProperty:Environment中是否存在某个属性
  • 当然我们也可以利用@Conditional来自定义条件注解

条件注解是可以写在类上和方法上的,如果某个条件注解写在了自动配置类上,那该自动配置类会不会生效就要看当前条件能不能符合;或者条件注解写在某个@Bean修饰的方法上,那这个Bean生不生效就看当前条件符不符合。

具体原理是:

  1. Spring在解析某个自动配置类时,会先检查该自动配置类上是否有条件注解,如果有,则进一步判断该条件注解所指定的条件当前能不能满足,如果满足了则继续解析该配置类,如果不满足则不进行解析了,也就是配置类所定义的Bean都得不到解析,也就是相当于没有这些Bean了。
  2. 同理,Spring在解析某个@Bean的方法时,也会先判断方法上是否有条件注解,然后进行解析,如果不满足条件,则该Bean不会生效

2.1 自定义条件注解

SpringBoot中众多的条件注解,都是基于Spring中的@Conditional来实现的,所以这里我们演示一下,如何使用@Conditional自定义一个我们的条件注解。

先来看下@Conditional注解的定义:

/*** * 指示组件只有在所有指定条件匹配时才有资格注册。* 条件是可以在注册bean定义之前以编程方式确定的任何状态(有关详细信息,请参阅条件)。* @Conditional注释可以以以下任何一种方式使用:* 在任何直接或间接用@Component注释的类(包括@Configuration类)上作为类型级注释* 作为元注释,用于组合自定义构造型注释* 作为任何@Bean方法上的方法级注释* 如果@Configuration类被标记为@Conditional,那么与该类关联的所有@Bean方法、@Import注释和@ComponentScan注释* 将受这些条件的约束。*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {/*** All {@link Condition} classes that must {@linkplain Condition#matches match}* in order for the component to be registered.*/Class<? extends Condition>[] value();}

根据定义我们在用@Conditional注解时,需要指定一个或多个Condition的实现类,所以我们先来提供一个实现类:

public class ShenCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {Map<String, Object> component = metadata.getAnnotationAttributes("org.springframework.stereotype.Component");System.out.println("下面开始打印condition里面的内容");System.out.println(component);Object value = component.get("value");return value.toString().equals("shen");}
}

上面这个实现类很简单,就是获取被@Conditional(ShenCondition.class)注解的类上,获取它@Component上的注解值,如果值是shen那就返回true,否则bean注册不成功。接下来就是自定义一个我们自己的注解:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ShenCondition.class)
public @interface ShenConditional {
}

测试用例如下:

@ShenConditional
@Component("shen")
public class ShenWorker {public String sayHello() {return "hello";}
}@SpringBootApplication(scanBasePackages = "org.example.springboottest")
public class ShenMyApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(ShenMyApplication.class);ShenWorker shen = (ShenWorker) context.getBean(ShenWorker.class);System.out.println("------------------------");System.out.println(shen.sayHello());}
}

此时运行,控制台输出:
在这里插入图片描述
把上面的ShenWorker @Component值改成其他,如@Component("shenWorker"),运行则输出如下:
在这里插入图片描述
报错了,NoSuchBeanDefinitionException
我想到了这里大家应该多少知道@Conditional注解的作用了,接下来我们挑其中一两个SpringBoot声明的条件注解,看一下源码,剖析一下它是怎么实现的。

2.2 @ConditionalOnClass原理解析

@ConditionalOnClass注解的作用是:校验是否存在某个类。

Spring这种优秀源码,通常注释都是很完善的,我们先来看看它们是怎么定义的:

/*** @Conditional,当且仅当指定的类在类路径上时,才匹配成功** value()可以在@Configuration类上安全地指定,因为在加载类之前,* 会使用ASM解析注释元数据。当放置在@Bean方法上时,需要特别注意,* 考虑在单独的Configuration类中隔离条件,特别是如果方法的返回类型与条件的目标匹配*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {/*** 必须设置的class信息。* 因为这个注释是通过加载类字节码来解析的(classLoader.loadClass),* 当且仅当这个注解直接作用在bean上,而不是当这个注解被用作组合的元注释时,* 在这里指定最终可能不在classpath上的类是安全的(不会报错)。* 想要将此注解用作元注释,那么就只使用name属性*/Class<?>[] value() default {};/*** 必须设置的类全限定名*/String[] name() default {};
}

根据我们自定义注解经验,该注解的实现肯定是在@Conditional(OnClassCondition.class)OnClassCondition.class中。
顺便在这里介绍一下怎么用idea追踪代码吧。

1)寻找关键实现类
首先我们知道,OnClassCondition.class实现自Condition接口,主要是实现里面的matches()方法。虽然咱还不知道它是在哪里被Spring调用的,但起码他肯定是会被调用的对吧,所以,我们先跳到OnClassCondition.class文件,打开它的继承图,如下所示:
在这里插入图片描述
可以发现它继承或者实现了各种各样的接口,其中一个是Condition。那OK
我们在回到OnClassCondition.class文件,alft+7打开代码结构图,然后在右上角点击查看继承过来的属性、方法,如下所示:
在这里插入图片描述
如上图所示,绿色框内灰色的即为继承过来,但是没有重写的方法。可以看到Condition接口的matches方法,就没有被重写过,那说明父类中肯定有默认的实现类,点击一下,会跳到SpringBootCondition类中,它的默认实现方式如下:
在这里插入图片描述
可以看到,最重要的是红框内的2行代码。点击getMatchOutcome发现这是一个抽象方法,所以,又回到OnClassCondition.class文件中,查找一下看看是否实现或者重写了该方法。最后发现如下:
在这里插入图片描述
重写了,再结合之前的代码可以判断出来,这里就是OnClassCondition类的逻辑实现核心。上面的代码其实也不难,基本上通过方法名就知道啥意思了。主要原理就是:使用反射工具,根据全限定名去JVM加载类,如果能加载成功,那就证明class存在;反之则不存在。

特别注意:@ConditionalOnClass和@ConditionalOnMissingClass注解的核心实现都是这个类的这个方法

2.3 ConditionalOnBean原理解析

点击查看@ConditionalOnBean注解,会发现逻辑是在OnBeanCondition.class中,然后按照上面的思路也会发现,它跟前面的注解实现套路基本一致,逻辑也是在getMatchOutcome中,如下:
在这里插入图片描述
这个源码也很简单,通过阅读注解接口的各种字段信息就能推断出来具体过程。总的来说就是根据bean是否存在于Spring容器中来返回结果。

特别注意:@ConditionalOnBean和@ConditionalOnSingleCandidate,@ConditionalOnMissingBean注解的核心实现都是这个类的这个方法

三、SpringBoot之Mybatis自动配置源码解析

在我们的项目pom中,导入如下:

 <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency>

我们就会看到我们项目依赖中出现以groupId+artifactId命名的jar包了,如下:
在这里插入图片描述

看到这里需要说明的是,SpringBoot不总是提供所有你知道的服务的自动配置类,有时候需要厂商自己去实现。就好比这个Mybatis,就是自己实现了自动配置类,让用户尽量做到开箱即用。

对的,通常第三方在写自动配置的时候,命名风格皆是如此。而在里面的MybatisAutoConfiguration即为Mybatis的自动配置类。通过源码我们可以发现,在里面注册了多个bean,当然是有条件的:
在这里插入图片描述
上面的意思是,在Spring中如果没有SqlSessionFactory这个类型的bean,则注册当前bean;
另一个注册的bean如下:
在这里插入图片描述
最核心的一个还是,在自动配置类下面有一个这玩意:
在这里插入图片描述
这个 Bean的作用是,如果没有其他显式的注册Mapper扫描映射器,那就注册一个默认的Mapper扫描映射器,扫描路径跟SpringBoot扫描路径一致。说到这里,是不是有朋友对Mybatis如何实现把接口转换成bean这个操作感兴趣呢?大家伙可以看看按照下面这个轨迹从1-9-AB,自上而下去看看,但是需要一些Spring功底。源码入口:MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions(我有一点功底,但不算很扎实,只能勉强看个大概)
在这里插入图片描述
总的来说,它的原理大致如下:(注意,这里是指默认自动配置类情况下,如果使用了@MapperScan注解,情况略有不同)

  1. Mybatis自动配置类会向Spring容器注册一个扫描器Bean定义,根据Bean定义,初始化扫描器属性。默认扫描路径是当前SpringBoot的扫描路径
  2. Mybatis会修改扫描到Mapper接口的BeanDefinition(生成Bean的图纸),关键代码在:org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions
  3. Mybatis会修改注册成为Bean的条件,原本接口类是不能够被注册成为Bean的,但是通过修改条件让其通过了判断
  4. 具体怎么生成的Bean,主要用到了Spring提供的FactoryBean,再然后是用到了Jdk的动态代理生成代理类。感兴趣的要自己去看看Spring的一些核心概念了

四、SpringBoot之AOP自动配置类

老套路了,AOP毕竟是Spring本家产品,所以他的自动配置类肯定在本包下org.springframework.boot:spring-boot-autoconfigure
在这里插入图片描述
源码如下:

public class AopAutoConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass(Advice.class)static class AspectJAutoProxyingConfiguration {@Configuration(proxyBeanMethods = false)// 开启AOP的注解,使用JDK动态代理@EnableAspectJAutoProxy(proxyTargetClass = false)// spring.aop.proxy-target-class=false时才生效@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")static class JdkDynamicAutoProxyConfiguration {}@Configuration(proxyBeanMethods = false)// 开启AOP的注解,使用CGLIB动态代理@EnableAspectJAutoProxy(proxyTargetClass = true)// spring.aop.proxy-target-class=true时生效,或者没有配置spring.aop.proxy-target-class时默认也生效@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)static class CglibAutoProxyConfiguration {}}@Configuration(proxyBeanMethods = false)// 没有aspectj的依赖,但是又要使用cglib动态代理@ConditionalOnMissingClass("org.aspectj.weaver.Advice")@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)static class ClassProxyingConfiguration {@Beanstatic BeanFactoryPostProcessor forceAutoProxyCreatorToUseClassProxying() {return (beanFactory) -> {if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;// 注册InfrastructureAdvisorAutoProxyCreator从而开启Spring AOP// @EnableAspectJAutoProxy会注册AnnotationAwareAspectJAutoProxyCreator,也会开启Spring AOP但是同时有用解析AspectJ注解的功能AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}};}}
}

学习总结

  1. 终于理解了【约定大于配置】是什么意思
  2. 跟踪学习了Mybatis是如何实现自动配置,包括怎么让mapper接口变成一个可实例化的bean的

感谢

感谢阿里云社区的文章《探索Spring Boot中的原则:约定大于配置》
感谢百度大佬【作者:编程师兄】的文章《SpringBoot约定大于配置到底是什么意思?》

相关文章:

【微服务专题】SpringBoot自动配置简单源码解析

目录 前言阅读对象阅读导航前置知识什么是自动配置0.1 基本概念0.2 SpringBoot中的【约定大于配置】0.3 从SpringMVC看【约定大于配置】0.4 从Redis看【约定大于配置】0.5 小结 笔记正文一、EnableAutoConfiguration源码解析二、SpringBoot常用条件注解源码解析2.1 自定义条件注…...

分布式数据恢复-hbase+hive分布式存储误删除如何恢复数据?

hbasehive分布式存储数据恢复环境&#xff1a; 16台某品牌R730XD服务器节点&#xff0c;每台物理服务器节点上有数台虚拟机&#xff0c;虚拟机上配置的分布式&#xff0c;上层部署hbase数据库hive数据仓库。 hbasehive分布式存储故障&初检&#xff1a; 数据库文件被误删除…...

安卓系统修图软件(一)

平时我们会不时在朋友圈发自己的自拍照&#xff0c;或者是风景图等&#xff0c;许多小伙伴们此时会对照片进行一定的修理&#xff0c;比如添加滤镜等操作。在电脑上用ps修图比较繁琐&#xff0c;日常中大可不必用这把宰牛刀&#xff1b;而手机自带的编辑器&#xff0c;或者是QQ…...

截图转HTML代码,支持预览,前端不用浪费时间写html和css了

截图转代码 试用地址&#xff1a;https://picoapps.xyz/free-tools/screenshot-to-code 这个简单的应用可以将截图转换为HTML/Tailwind CSS代码。它使用GPT-4 Vision来生成代码&#xff0c;并使用DALL-E 3来生成类似的图像。现在你也可以输入一个URL来克隆一个现有的网站&#…...

Vite CSS Module 优雅的处理样式隔离

今天介绍的是我写的一个vite插件vite-plugin-oneof-css-module&#xff0c;该插件主要处理scss module&#xff0c;那它适用于什么场景呢&#xff1f; 1. 最大的特点就是使用scss module 可以不用写 .module.scss 了 2. 可以根据不同的文件夹或文件分别进行不同的处理&#x…...

基于Springboot+Vue选课系统

选课系统要求 (1)数据库表&#xff1a;教师信息表、学生信息表、课程表、选课表 其中&#xff0c;教师信息表、学生信息表和选课表的数据需要提前设置&#xff0c;本题主要操作课程表 (2) 技术架构&#xff1a; 后台使用springboot 前端使用vue-admin-template (3) 考试时间&…...

智能汽车十大网络安全攻击场景-《智能汽车网络安全权威指南》

引言 大家都很熟悉OWASP Top 10风险报告&#xff0c;这个报告不但总结了Web应用程序最可能、最常见、最危险的10大安全隐患&#xff0c;还包括了如何消除这些隐患的建议&#xff0c;这个“OWASP Top 10“差不多每隔三年更新一次。目前汽车网络安全攻击威胁隐患繁多&#xff0c…...

递归方法来计算二叉树的双分支节点个数

1.递归方法来计算二叉树的双分支节点个数 首先&#xff0c;你需要定义二叉树的节点结构&#xff0c;然后编写递归函数 #include <stdio.h> #include <stdlib.h>// 定义二叉树的节点结构 struct TreeNode {int value;struct TreeNode* left;struct TreeNode* righ…...

INFLOW:用于检测隐藏服务器的反向网络流水印

文章信息 论文题目&#xff1a;INFLOW: Inverse Network Flow Watermarking for Detecting Hidden Servers 期刊&#xff08;会议&#xff09;&#xff1a;IEEE INFOCOM 2018 - IEEE Conference on Computer Communications 时间&#xff1a;2018 级别&#xff1a;CCF A 文章链…...

社区物联网云服务架构设计

文章目录 1 摘要2 架构图2.1 社区物联网云服务网络拓扑图2.2 社区物联网云服务通讯流程图2.3 社区远程开锁功能流程图 3 应用场景 1 摘要 随着社区管理越来越智能化&#xff0c;社区物联网升级与改造的市场空间也越来越大。社区物联网包含楼宇对讲、门禁门锁、通道闸等等设备系…...

Linux - 文件系统 - 理解目录 - 理解 软/硬链接

前言 在上篇博客当中&#xff0c;我们对 文件系统 和 inode 做了初步了解&#xff0c;本博客将在上篇博客的基础之上&#xff0c;对于 文件系统当中的目录进行进步一阐述。 Linux - 进一步理解 文件系统 - inode - 机械硬盘-CSDN博客 目录 一个文件有一个 inode&#xff0c;…...

Springboot websocket前端无法访问到,Websocket因AOP代理 前端无法请求到

Springboot websocket前端无法访问到&#xff0c;Websocket因AOP代理 前端无法请求到 问题出现 在我后端springboot启动后&#xff0c;前端无法请求websocket请求连接到我们websocket服务器。 想要的效果 在我后端springboot启动后&#xff0c;前端可以请求到我们websocket…...

基于高质量训练数据,GPT-4 Turbo更出色更强大

11月7日消息&#xff0c;OpenAI在首届开发者大会上正式推出了GPT-4 Turbo。 与GPT-4相比&#xff0c;GPT-4 Turbo主要有6方面的提升&#xff1a; 1、扩展下文对话长度&#xff1a;GPT4最大只能支持8k的上下文长度&#xff08;约等于6000个单词&#xff09;&#xff0c;而GPT-4…...

jenkins + gitlab 自动部署(webhook)

Jenkins是一个流行的开源CI/CD工具&#xff0c;可以与Git等版本控制系统集成&#xff0c;实现自动构建、测试和部署。Webhook是一种机制&#xff0c;可以在Git仓库中设置&#xff0c;在代码提交或合并请求时触发Jenkins构建任务&#xff0c;以完成自动化部署。 实操 设备信息 …...

【数据集】全网最全的常见已公开医学影像数据集

目录 一&#xff0c;极市医学数据集汇总 1.CT 医学图像 ​编辑 2.恶性与良性皮肤癌 3.白内障数据集 4.胸部 X 光图像&#xff08;肺炎&#xff09; 5.用于图像增强的内窥镜真实合成曝光过度和曝光不足帧 6.医学家 7.乳房组织病理学图像 8.皮肤癌 MNIST&#xff1a;HA…...

图形数据库的实战应用:如何在 Neo4j 中有效管理复杂关系

关系数据库管理系统( RDBMS ) 代表了最先进的技术&#xff0c;这在一定程度上要归功于其由周边技术、工具和广泛的专业技能组成的完善的生态系统。 在这个涵盖信息技术(IT) 和运营技术(OT) 的技术革命时代&#xff0c;人们普遍认识到性能方面出现了重大挑战&#xff0c;特别是…...

Linux内核中的overlay文件系统

一、简介 Docker 内核实现容器的功能用了linux 内核中的三个特性 Namespace、Cgroup、UnionFs&#xff0c;今天我们来说一下UnionFs。 linux UnionFs 实现的是overlay 文件系统 OverlayFs 文件系统分为三层&#xff0c; lower 是只读层 Upper 是可读写 Merged 是 lower 和U…...

archery修改为不能自提自审核上线SQL

目录 背景修改代码效果参考 背景 我和同事都可以提交上线SQL&#xff0c;但是不能自己提交的SQL自己去审核通过。目前的情况是可以自提自审。 修改代码 找到/opt/archery/sql/utils/workflow_audit.py文件 ...省略...# 判断用户当前是否是可审核staticmethoddef can_revie…...

如何处理git多分支

本篇文章主要处理以下两种多分支问题 如何将自己在本地的修改上传到一个新的Git分支&#xff08;比如用于测试&#xff0c;不合并进main分支&#xff09;&#xff1f;如何在一个新的本地仓库拉取一个项目的非main分支&#xff0c;并处理他们关联关系&#xff1f; 1. 将自己在…...

Proteus仿真--基于DS1302与数码管设计的可调电子钟

本文主要介绍基于51单片机的DS1302的可调式电子钟实验&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真图如下 其中数码管显示电子钟时间信息&#xff0c;按键用于调节时间&#xff0c;时间芯片选用DS1302芯片 仿真运行视频 Proteus仿真--基于DS1302与数码管设…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...