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

2023-02-01 0 438

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

为何要用Starter?现在他们就来自述呵呵,在还没有Spring-boot架构的这时候,我们采用Spring 合作开发工程项目,如果须要某两个架构,比如mybatis,他们的关键步骤通常都是:到maven库房去找须要导入的mybatis jar包,挑选出最合适的版(易发生争执)到maven库房去找mybatis-spring资源整合的jar包,挑选出最合适的版(易发生争执)在spring的applicationContext.xml文件格式中实用性dataSource和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 这样的倚赖,接着基本上不必任何人实用性就能采用这些倚赖的机能,或者说的感受到了照相狸尾豆的爽。上面他们就嘿嘿试著自己合作开发两个Starter。重新命名规范化

在采用spring-boot-starter,会发现,中有工程项目编号是 XX-spring-boot-starter,中有是spring-boot-starter-XX,那个工程项目的中文名称有什么讲求呢?从springboot非正式文件格式节录:

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

该文的约莫原意是,麻烦事我们严格遵守那个重新命名规范:

Srping非正式重新命名文件格式格式为:spring-boot-starter-{name}

非Spring非正式提议重新命名文件格式格式:{name}-spring-boot-starter

合作开发实例

上面我就以记录日志的两个组件为实例来讲述合作开发两个starter 的过程。

新建工程

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

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

Pom 导入倚赖4.0.0org.springframework.bootspring-boot-starter-parent2.5.13com.jdjd-log-spring-boot-starter1.0-SNAPSHOTjd-log-spring-boot-starterhttp://www.example.comUTF-81.81.8org.springframework.bootspring-boot-autoconfigureorg.springframework.bootspring-boot-configuration-processororg.springframework.bootspring-boot-starter-aoporg.projectlomboklombokorg.apache.maven.pluginsmaven-source-plugin2.2.1attach-sourcesjar-no-fork

这边稍微解释呵呵这几个倚赖:

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 文件格式,约莫内容是定义的实用性的元数据;效果如下截图。

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

spring-boot-starter-aop :那个就不必解释了,因为实例是记录日志,他们用到切面的机能,因此须要导入。

定义属性实用性* @author kongxiangdong2* @Title: LogProperties* @ProjectName jd-log-spring-boot-starter* @Description: TODO* @date 2022/9/110:04@ConfigurationProperties(prefix = “jd”)@Datapublic class LogProperties {* 是否开启日志private boolean enable;* 平台:不同服务项目采用的区分,默认取 spring.application.name@Value(“${spring.application.name:#{null}}”)private String platform;

JAVA bean 或者属性中;换句话来说是将实用性文件格式中的实用性封装到JAVA 实体对象,方便采用和管理。

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

定义自动实用性类* @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

注解

类型

说明

@ConditionalOnClass

Class Conditions类条件注解

当前classpath下有指定类才加载

@ConditionalOnMissingClass

Class Conditions类条件注解

当前classpath下无指定类才加载

@ConditionalOnBean

Bean ConditionsBean条件注解

当期罐子内有指定bean才加载

@ConditionalOnMissingBean

Bean ConditionsBean条件注解

当期罐子内无指定bean才加载

@ConditionalOnProperty

Property Conditions环境变量条件注解(含实用性文件格式)

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

@ConditionalOnResource

ResourceConditions 资源条件注解

有指定资源才加载

@ConditionalOnWebApplication

Web Application Conditionsweb条件注解

是web才加载

@ConditionalOnNotWebApplication

Web Application Conditionsweb条件注解

不是web才加载

@ConditionalOnExpression

SpEL Expression Conditions

符合SpEL 表达式才加载

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

实用性EnableAutoConfiguration

在resources/META-INF/ 目录新建spring.factories 文件格式,实用性内容如下;

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

好了,至此自定义Starter 大体架构已经好了,上面是他们记录日志的机能。

业务机能实现

首先他们先定义两个注解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;* @author kongxiangdong2* @Title: LogAspectjProcess* @ProjectName jd-log-spring-boot-starter* @Description: TODO* @date 2022/9/111:12@Aspect@Component@Slf4j@AllArgsConstructorpublic class LogAspectjProcess {LogProperties logProperties;* 定义切点@Pointcut(“@annotation(com.jd.annotation.Jdlog)”)public void pointCut(){}* 环绕通知* @param thisJoinPoint* @param jdlog* @return@Around(“pointCut() && @annotation(jdlog)”)public Object around(ProceedingJoinPoint thisJoinPoint, Jdlog jdlog){//执行方法中文名称String taskName = thisJoinPoint.getSignature().toString().substring(thisJoinPoint.getSignature().toString().indexOf(” “),thisJoinPoint.getSignature().toString().indexOf(“(“));taskName = taskName.trim();long time = System.currentTimeMillis();Object result = null;try {result = thisJoinPoint.proceed();} catch (Throwable throwable) {throwable.printStackTrace();log.info(“{} — method:{} run :{} ms”,logProperties.getPlatform(), taskName,(System.currentTimeMillis() – time));return result;

整体工程项目结构是这样子

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

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

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

测试采用

接着就能在其他工程项目中导入采用了;上面以两个简单的spring-boot web 工程项目做个测试,在pom 中导入上面的倚赖实用性。

com.jdjd-log-spring-boot-starter1.0-SNAPSHOT

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

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

application.yaml 文件格式中实用性

jd:enable: trueplatform: “测试工程项目”

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

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

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

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

代码实例地址:https://coding.jd.com/kongxiangdong2/jd-log-spring-boot-starter.git

基本原理传授

他们上面已经看见两个starter,只须要导入到pom 文件格式中,再实用性呵呵(其实都能不实用性)jd.enable=true,就能直接采用记录日志的机能了,Spring-boot 是怎么做到的?

在开始的这时候说过,Spring-boot 的好处是能自动装配。那上面我就来说说自动装配的基本原理。

相比于传统Spring 应用领域,他们搭建两个SpringBoot 应用领域,他们只须要导入两个注解(前提:导入springBoot y倚赖)@SpringBootApplication,就能直接运行;因此他们就从那个注解开始入手,看看那个注解到底做了写什么?

SpringBootApplication 注解

点开@SpringBootApplication注解能看见包含了@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解。

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

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

@SpringBootConfiguration 注解:他们再次点进去看那个注解,其实它是两个@Configuration 注解。

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

@ComponentScan 注解

@ComponentScan 注解:实用性包扫描定义的扫描路径,把符合扫描规则的类装配到spring罐子

@EnableAutoConfiguration 注解

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

注解

作用

解释

@SpringBootConfiguration

标记当前类为实用性类

加上那个注解是为了让当前类作为两个实用性类交由 Spring 的 IOC 罐子进行管理,因为前面他们说了,SpringBoot 本质上还是 Spring,因此原属于 Spring 的注解 @Configuration 在 SpringBoot 中也能直接应用领域

@ComponentScan

实用性包扫描定义的扫描路径,把符合扫描规则的类装配到spring罐子

用于定义 Spring 的扫描路径,等价于在 xml 文件格式中实用性 ,倘若不实用性扫描路径,那么 Spring 就会默认扫描当前类所在的包及其子包中的所有标注了 @Component,@Service,@Controller 等注解的类。

@EnableAutoConfiguration

打开自动装配

上面着重传授

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

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

首先他们嘿嘿看@Import 那个注解,那个是比较关键的两个注解;

在说那个注解之前他们先举个例子,倘若他们有两个类Demo,它是两个不在开启实用性类目录之下的,也就意味着它不会被扫描到,Spring 也无法感知到它的存在,那么如果须要能将它被扫描到,是不是他们能通过加@Import 注解来导入Demo 类,类似如下代码

@Configuration@Import(Demo.class)public class MyConfiguration {

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

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

进入到getAutoConfigurationEntry 方法中

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

他们继续往getCandidateConfigspring.factories文件格式,那么他们再进入SpringFactoriesLoader.loadFactoryNames()中来看下最终的实现。

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

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

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

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

这边再扩展呵呵那个内容,通过 SpringFactoriesLoader 来读取实用性文件格式 spring.factories 中的实用性文件格式的这种方式是一种 SPI 的思想。

@AutoConfigurationPackage 注解

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

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

再进入那个类查看,它其实是两个内部类,看代码的约莫原意是读取到他们在最外层的 @SpringBootApplication 注解中实用性的扫描路径(没有实用性则默认当前包下),接着把扫描路径上面的Bean注册到罐子中;

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

总结

好了,现在他们约莫来理呵呵整个自动装配的流程:

开启类中通过采用@SpringBootApplication实现自动装配的机能;实际注解@SpringBootApplication是借助注解@EnableAutoConfiguration的机能。在注解@EnableAutoConfiguration中又有两个注解,@AutoConfigurationPackage,@EnableAutoConfiguration。通过@AutoConfigurationPackage实现对于当前工程项目中Bean的进行加载;@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中的定义了啊,赶紧试试吧

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务