手把手带你开发starter,点对点带你讲解原理()

  本篇文章为你整理了手把手带你开发starter,点对点带你讲解原理()的详细内容,包含有startpoint 手把手带你开发starter,点对点带你讲解原理,希望能帮助你了解 手把手带你开发starter,点对点带你讲解原理。

   在2012 年 10 月,一个叫 Mike Youngstrom 的人在 Spring Jira 中创建了一个功能请求,要求在 Spring Framework 中支持无容器 Web 应用程序体系结构,提出了在主容器引导 Spring 容器内配置 Web 容器服务;这件事情对 SpringBoot 的诞生应该说是起到了一定的推动作用。

  所以SpringBoot 设计的目标就是简化繁琐配置,快速建立Spring 应用。

  
(___ _ __ _ __ _ _ __ __ _ _) ___ ___ _

   \___ \ _ \ __ _ \ / _` _ / _ \ / _ \ __

   ____) _) (_ _) (_) (_) _

   _____/ .__/_ __ _\__, ____/ \___/ \___/ \__

   __/

   _ ___/

  
现在我们就来回忆一下,在还没有Spring-boot框架的时候,我们使用Spring 开发项目,如果需要某一个框架,例如mybatis,我们的步骤一般都是:

  
假如所有工作都到位,一般可以一气呵成;但很多时候都会花一堆时间解决jar 冲突,配置项缺失,导致怎么都启动不起来等等,各种问题。

  所以在2012 年 10 月,一个叫 Mike Youngstrom 的人在 Spring Jira 中创建了一个功能请求,要求在 Spring Framework 中支持无容器 Web 应用程序体系结构,提出了在主容器引导 Spring 容器内配置 Web 容器服务;这件事情对 SpringBoot 的诞生应该说是起到了一定的推动作用。

  所以SpringBoot 设计的目标就是简化繁琐配置,快速建立Spring 应用。

  然后在开发Spring-boot 应用的是时候, 经常可以看到我们的pom 文件中引入了spring-boot-starter-web、spring-boot-starter-data-redis、mybatis-spring-boot-starter 这样的依赖,然后几乎不用任何配置就可以使用这些依赖的功能,真正的感受到了开箱即用的爽。

  
2. 命名规范

  在使用spring-boot-starter,会发现,有的项目名称是 XX-spring-boot-starter,有的是spring-boot-starter-XX,这个项目的名称有什么讲究呢?从springboot官方文档摘录:

  这段话的大概意思就是,麻烦大家遵守这个命名规范:

  Srping官方命名格式为:spring-boot-starter-{name}

  非Spring官方建议命名格式:{name}-spring-boot-starter

  3. 开发示例

  下面我就以记录日志的一个组件为示例来讲述开发一个starter 的过程。

  3.1 新建工程

  首先新建一个maven 工程,名称定义为jd-log-spring-boot-starter

  3.2 Pom 引入依赖

  

 ?xml version="1.0" encoding="UTF-8"? 

 

   project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"

   modelVersion 4.0.0 /modelVersion

   parent

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-parent /artifactId

   version 2.5.13 /version

   relativePath/ !-- lookup parent from repository --

   /parent

   groupId com.jd /groupId

   artifactId jd-log-spring-boot-starter /artifactId

   version 1.0-SNAPSHOT /version

   name jd-log-spring-boot-starter /name

   url http://www.example.com /url

   properties

   project.build.sourceEncoding UTF-8 /project.build.sourceEncoding

   maven.compiler.source 1.8 /maven.compiler.source

   maven.compiler.target 1.8 /maven.compiler.target

   /properties

  
groupId org.springframework.boot /groupId

   artifactId spring-boot-autoconfigure /artifactId

   /dependency

   !-- 在编译时会自动收集配置类的条件,写到一个META-INF/spring-autoconfigure-metadata.json中--

   dependency

   groupId org.springframework.boot /groupId

   artifactId spring-boot-configuration-processor /artifactId

   /dependency

   !--记录日志会用到切面,所以需要引入--

   dependency

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-aop /artifactId

   /dependency

   dependency

   groupId org.projectlombok /groupId

   artifactId lombok /artifactId

   /dependency

   /dependencies

   build

   plugins

   plugin

   groupId org.apache.maven.plugins /groupId

   artifactId maven-source-plugin /artifactId

   version 2.2.1 /version

   executions

   execution

   id attach-sources /id

   goals

   goal jar-no-fork /goal

   /goals

   /execution

   /executions

   /plugin

   /plugins

   /build

   /project

  

 

  这边稍微解释一下这几个依赖:

  spring-boot-autoconfigure :提供自动化装配功能,是为了Spring Boot 应用在各个模块提供自动化配置的作用;即加入对应 pom,就会有对应配置其作用;所以我们想要自动装配功能,就需要引入这个依赖。

  spring-boot-configuration-processor:将自定义的配置类生成配置元数据,所以在引用自定义STARTER的工程的YML文件中,给自定义配置初始化时,会有属性名的提示;确保在使用@ConfigurationProperties注解时,可以优雅的读取配置信息,引入该依赖后,IDEA不会出现“spring boot configuration annotation processor not configured”的错误;编译之后会在META-INF 下生成一个spring-configuration-metadata.json 文件,大概内容就是定义的配置的元数据;效果如下截图。

  spring-boot-starter-aop :这个就不用解释了,因为示例是记录日志,我们用到切面的功能,所以需要引入。

  3.3 定义属性配置

  

/**

 

  * @author kongxiangdong2

  * @Title: LogProperties

  * @ProjectName jd-log-spring-boot-starter

  * @Description: TODO

  * @date 2022/9/110:04

  @ConfigurationProperties(prefix = "jd")

  @Data

  public class LogProperties {

  
 

 

  @ConfigurationProperties:该注解和@Value 注解作用类似,用于获取配置文件中属性定义并绑定到Java Bean 或者属性中;换句话来说就是将配置文件中的配置封装到JAVA 实体对象,方便使用和管理。

  这边我们定义两个属性,一个是是否开启日志的开关,一个是标识平台的名称。

  3.4 定义自动配置类

  

/**

 

  * @author kongxiangdong2

  * @Title: JdLogAutoConfiguration

  * @ProjectName jd-log-spring-boot-starter

  * @Description: TODO

  * @date 2022/9/110:06

  @Configuration

  @ComponentScan("com.jd")

  @ConditionalOnProperty(prefix = "jd",name = "enable",havingValue = "true",matchIfMissing = false)

  @EnableConfigurationProperties({LogProperties.class})

  public class JdLogAutoConfiguration {

  
 

 

  这个类最关键了,它是整个starter 最重要的类,它就是将配置自动装载进spring-boot的;具体是怎么实现的,下面在讲解原理的时候会再详细说说,这里先完成示例。

  @Configuration :这个就是声明这个类是一个配置类

  @ConditionalOnProperty:作用是可以指定prefix.name 配置文件中的属性值来判定configuration是否被注入到Spring,就拿上面代码的来说,会根据配置文件中是否配置jd.enable 来判断是否需要加载JdLogAutoConfiguration 类,如果配置文件中不存在或者配置的是等于false 都不会进行加载,如果配置成true 则会加载;指定了havingValue,要把配置项的值与havingValue对比,一致则加载Bean;配置文件缺少配置,但配置了matchIfMissing = true,加载Bean,否则不加载。

  在这里稍微扩展一下经常使用的Condition

  
Property Conditions环境变量条件注解(含配置文件)

  prefix 前缀name 名称havingValue 用于匹配配置项值matchIfMissing 没找指定配置项时的默认值

  
@EnableConfigurationProperties使@ConfigurationProperties 注解的类生效。

  3.5 配置EnableAutoConfiguration

  在resources/META-INF/ 目录新建spring.factories 文件,配置内容如下;

  

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.jd.JdLogAutoConfiguration

 

  

 

  好了,至此自定义Starter 大体框架已经好了,下面就是我们记录日志的功能。

  3.6 业务功能实现

  首先我们先定义一个注解Jdlog

  

/**

 

  * @author kongxiangdong2

  * @Title: Jdlog

  * @ProjectName jd-log-spring-boot-starter

  * @Description: TODO

  * @date 2022/9/110:04

  @Target(ElementType.METHOD)

  @Retention(RetentionPolicy.RUNTIME)

  public @interface Jdlog {

  

 

  定义切面执行逻辑,这边就简单的打印一下配置文件的属性值+目标执行方法+耗时。

  

import com.jd.annotation.Jdlog;

 

  import com.jd.config.LogProperties;

  import lombok.AllArgsConstructor;

  import lombok.extern.slf4j.Slf4j;

  import org.aspectj.lang.ProceedingJoinPoint;

  import org.aspectj.lang.annotation.Around;

  import org.aspectj.lang.annotation.Aspect;

  import org.aspectj.lang.annotation.Pointcut;

  import org.springframework.stereotype.Component;

  
@Around("pointCut() @annotation(jdlog)")

  public Object around(ProceedingJoinPoint thisJoinPoint, Jdlog jdlog){

  
log.info("{} -- method:{} run :{} ms",logProperties.getPlatform(), taskName,

  (System.currentTimeMillis() - time));

  return result;

  
整体项目结构就是这样子

  好了,现在就可以打包编译安装

  3.7 测试使用

  然后就可以在其他项目中引入使用了;下面以一个简单的spring-boot web 项目做个测试,在pom 中引入下面的依赖配置。

  

 dependency 

 

   groupId com.jd /groupId

   artifactId jd-log-spring-boot-starter /artifactId

   version 1.0-SNAPSHOT /version

   /dependency

  

 

  增加一个http 访问的方法,标注上@Jdlog 注解

  application.yaml 文件中配置

  

jd:

 

  enable: true

  platform: "测试项目"

  

 

  启动测试,访问地址http://localhost:8080/test/method1,控制台打印如下:

  咋样,自定义的Starter是不是特别的简单啊,快动手试试吧!

  上面我们讲的都是怎么去开发一个starter,但是到底为什么要这样,spring-boot 是如何去实现的?是不是还不知道?那下面我们就来说说;

  4. 原理讲解

  我们上面已经看到一个starter,只需要引入到pom 文件中,再配置一下(其实都可以不配置)jd.enable=true,就可以直接使用记录日志的功能了,Spring-boot 是怎么做到的?

  在开始的时候说过,Spring-boot 的好处就是可以自动装配。那下面我就来说说自动装配的原理。

  相比于传统Spring 应用,我们搭建一个SpringBoot 应用,我们只需要引入一个注解(前提:引入springBoot y依赖)@SpringBootApplication,就可以直接运行;所以我们就从这个注解开始入手,看看这个注解到底做了写什么?

  SpringBootApplication 注解

  点开@SpringBootApplication注解可以看到包含了@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解。

  前面的四个注解就不用过多叙述了,是定义注解最基本的,关键在于后面的三个注解:@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan,其实也就是说在启动类上如果不使用@SpringBootApplication 这个复合注解,直接使用者三个注解一样可以达到相同的效果。

  @SpringBootConfiguration 注解:我们再次点进去看这个注解,其实它就是一个@Configuration 注解。

  @ComponentScan 注解

  @ComponentScan 注解:配置包扫描定义的扫描路径,把符合扫描规则的类装配到spring容器

  @EnableAutoConfiguration 注解

  @EnableAutoConfiguration 打开自动装配(自动配置着重来看该注解)

  
标记当前类为配置类

  加上这个注解就是为了让当前类作为一个配置类交由 Spring 的 IOC 容器进行管理,因为前面我们说了,SpringBoot 本质上还是 Spring,所以原属于 Spring 的注解 @Configuration 在 SpringBoot 中也可以直接应用

  
配置包扫描定义的扫描路径,把符合扫描规则的类装配到spring容器

  用于定义 Spring 的扫描路径,等价于在 xml 文件中配置 context:component-scan,假如不配置扫描路径,那么 Spring 就会默认扫描当前类所在的包及其子包中的所有标注了 @Component,@Service,@Controller 等注解的类。

  
我们再次点击@EnableAutoConfiguration进入查看,它是一个由 @AutoConfigurationPackage 和 @Import 注解组成的复合注解;

  首先我们先来看@Import 这个注解,这个是比较关键的一个注解;

  在说这个注解之前我们先举个例子,假如我们有一个类Demo,它是一个不在启动配置类目录之下的,也就意味着它不会被扫描到,Spring 也无法感知到它的存在,那么如果需要能将它被扫描到,是不是我们可以通过加@Import 注解来导入Demo 类,类似如下代码

  

@Configuration

 

  @Import(Demo.class)

  public class MyConfiguration {

  

 

  所以,我们可以知道@Import 注解其实就是为了去导入一个类。所以这里@Import({AutoConfigurationImportSelector.class}) 就是为了导入AutoConfigurationImportSelector 类,那我们继续来看这个类,AutoConfigurationImportSelector实现的是DeferredImportSelector接口,这是一个延迟导入的类;再细看会有一个方法比较显眼,根据注解元数据来选择导入组件,当注解元数据空,直接返回一个空数组;否则就调用getAutoConfigurationEntry ,方法中会使用AutoConfigurationEntry的getConfigurations(),configurations是一个List String ,那么我们看下AutoConfigurationEntry是怎么生成的。

  进入到getAutoConfigurationEntry 方法中可以看到主要是getCandidateConfigurations 来获取候选的 Bean,并将其存为一个集合;后续的方法都是在去重,校验等一系列的操作。

  我们继续往getCandidateConfigurations 方法里看,最终通过SpringFactoriesLoader.loadFactoryNames来获取最终的configurations,并且可以通过断言发现会使用到META-INF/spring.factories文件,那么我们再进入SpringFactoriesLoader.loadFactoryNames()中来看下最终的实现。

  SpringFactoriesLoader.loadFactoryNames()方法会读取META-INF/spring.factories文件下的内容到Map中,再结合传入的factoryType=EnableAutoConfiguration.class,因此会拿到 org.springframework.boot.autoconfigure.EnableAutoConfiguration为key对应的各个XXAutoConfiguration的值,然后springboot在结合各个starter中的代码完成对于XXAutoConfiguration中的Bean的加载动作。

  这边再扩展一下这个内容,通过 SpringFactoriesLoader 来读取配置文件 spring.factories 中的配置文件的这种方式是一种 SPI 的思想。

  @AutoConfigurationPackage 注解

  进入这个注解看,其实它就是导入了Registrar 这个类

  再进入这个类查看,它其实是一个内部类,看代码的大概意思就是读取到我们在最外层的 @SpringBootApplication 注解中配置的扫描路径(没有配置则默认当前包下),然后把扫描路径下面的Bean注册到容器中;

  好了,现在我们大概来理一下整个自动装配的流程:

  启动类中通过使用@SpringBootApplication实现自动装配的功能;

  
在注解@EnableAutoConfiguration中又有两个注解,@AutoConfigurationPackage,@EnableAutoConfiguration。

  
@EnableAutoConfiguration通过@Import({AutoConfigurationImportSelector.class})实现对于Pom引入的start中的XXAutoConfiguration的加载;

  
@AutoConfigurationImportSelector类中通过SpringFactoriesLoader读取 META-INF/spring.factories中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的各个XXAutoConfiguration的值,然后springboot在结合各个start中的代码完成对于XXAutoConfiguration中的Bean的加载动作;

  到这里是不是已经可以很了然对我们之前开发starter中的定义了啊,赶紧试试吧

  以上就是手把手带你开发starter,点对点带你讲解原理()的详细内容,想要了解更多 手把手带你开发starter,点对点带你讲解原理的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: