SpringBoot 自动装配原理 - 支付宝支付封装starter

SpringBoot 自动装配

  • SpringBoot 自动装配原理
    • 详细介绍
    • 自定义 Spring Boot Starter
      • 1.读取配置文件
      • 2.注册 AlipayClient bean
      • 3.核心代码编写
      • 4.注册 AlipayAPI bean
      • 5.编写 META-INF/spring.factories 文件
      • 6.项目结构
      • 测试
        • 1.创建一个测试项目,引入自定义 starter 依赖
        • 2.配置文件编写
        • 3.编写测试代码

SpringBoot 自动装配原理

Spring Boot的自动装配是通过@EnableAutoConfiguration注解来实现的,该注解包含了一系列的自动装配配置类,这些配置类会根据项目的依赖和配置,自动地配置应用程序上下文中的Bean。

SpringBoot 应用的启动类上都有一个 @SpringBootApplication 注解,该注解包含 @EnableAutoConfiguration注解。

@EnableAutoConfiguration注解包含两个重要注解:

  1. @AutoConfigurationPackage
    • 该注解是用于标记主配置类(通常是Spring Boot应用程序的入口类),以指示在进行自动配置时应该扫描的基本包。它会将该类所在的包及其子包纳入自动配置的扫描范围。
  2. @Import({AutoConfigurationImportSelector.class})
    • 该注解用于导入一个配置选择器,即AutoConfigurationImportSelector类。
    • AutoConfigurationImportSelector是Spring Boot自动配置的核心,它负责从类路径下的META-INF/spring.factories文件中加载自动配置类的候选列表,并根据条件选择合适的自动配置类导入到Spring容器中。
    • 通过@Import注解将AutoConfigurationImportSelector引入到主配置类中,以启用自动配置的机制。

装配流程如下:

  1. 主配置类上的@EnableAutoConfiguration触发自动配置的启用。
  2. @EnableAutoConfiguration包含@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})
  3. @AutoConfigurationPackage标记了要扫描的基本包。
  4. @Import({AutoConfigurationImportSelector.class})导入了AutoConfigurationImportSelector,启动自动配置的核心。
  5. AutoConfigurationImportSelector根据条件加载META-INF/spring.factories文件中的自动配置类候选列表。
  6. 过滤掉不符合条件的自动配置类,移除重复的自动配置类,获取需要排除的自动配置类。
  7. 最终,将符合条件的自动配置类导入到Spring容器中。

详细介绍

AutoConfigurationImportSelector 实现了 DeferredImportSelector接口,用于延迟导入配置类的选择器。它允许在运行时决定要导入的配置类。通常,它用于实现一些自定义逻辑,以便根据运行时条件来选择性地导入配置。

在这里插入图片描述

DeferredImportSelector 定义了一个方法:

String[] selectImports(AnnotationMetadata importingClassMetadata);

AutoConfigurationImportSelector 对这个方法的实现

    public String[] selectImports(AnnotationMetadata annotationMetadata) {// isEnabled(annotationMetadata): 用于判断是否启用了自动配置if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {// * getAutoConfigurationEntry(annotationMetadata) 获取自动配置的条目,其中包含了要导入的配置类的信息。AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}

getAutoConfigurationEntry()

    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {AnnotationAttributes attributes = this.getAttributes(annotationMetadata);// * 获取候选的自动配置类的全限定类名列表List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);// 移除重复的自动配置类configurations = this.removeDuplicates(configurations);// 获取需要排除的自动配置类的全限定类名列表Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);// 检查是否有重复排除的自动配置类,如果有则抛出异常this.checkExcludedClasses(configurations, exclusions);// 移除需要排除的自动配置类configurations.removeAll(exclusions);// 获取配置类的过滤器,并过滤掉不符合条件的自动配置类configurations = this.getConfigurationClassFilter().filter(configurations);// 触发自动配置导入事件this.fireAutoConfigurationImportEvents(configurations, exclusions);// 返回一个AutoConfigurationEntry对象,包含了最终要导入的自动配置类的信息。return new AutoConfigurationEntry(configurations, exclusions);}}

getCandidateConfigurations() :获取候选配置

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {/*使用SpringFactoriesLoader加载META-INF/spring.factories文件中的配置。this.getSpringFactoriesLoaderFactoryClass()返回工厂类的类名,通常是org.springframework.boot.autoconfigure.EnableAutoConfiguration。这里加载的是自动配置的候选类的全限定类名。相当于根据 key 获取 value*/List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);// 使用Assert来确保最终得到的自动配置类列表不为空,如果为空,则抛出异常。Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");return configurations;}

image-20231121185632216

loadFactoryNames() :

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoader == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();// * 调用loadSpringFactories方法加载META-INF/spring.factories文件中的配置。return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

image-20231121183914769

查看 Spring Boot 自动装配源码可以看到上面的代码就是加载 META-INF/spring.factories 中键org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值

在这里插入图片描述

自定义 Spring Boot Starter

以支付宝沙箱支付为例

新建一个项目,启动类和配置文件都删掉,创建META-INF/spring.factories

image-20231121191042109

1.读取配置文件

@Data
@ConfigurationProperties(prefix = "alipay")
public class PayProperties {private String appId;private String appPrivateKey;private String alipayPublicKey;private String notifyUrl;private String gateway;
}

2.注册 AlipayClient bean

@Configuration
@EnableConfigurationProperties(PayProperties.class)
public class AutoConfiguration {@Beanpublic AlipayClient getAlipayClient(PayProperties payProperties){AlipayClient alipayClient = new DefaultAlipayClient(payProperties.getGateway(),payProperties.getAppId(),payProperties.getAppPrivateKey(),AlipayConstants.FORMAT_JSON,AlipayConstants.CHARSET_UTF8,payProperties.getAlipayPublicKey(),AlipayConstants.SIGN_TYPE_RSA2);return alipayClient;}
}

3.核心代码编写

AlipayAPI

@AllArgsConstructor // 生成全部参数的构造函数
public class AlipayAPI {private String notifyUrl;private AlipayClient alipayClient;public String pay(Order order){AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();// 支付宝页面跳转地址request.setReturnUrl(notifyUrl);// 异步通知的地址request.setNotifyUrl(notifyUrl);Map<String,String> map = new HashMap<>();map.put("out_trade_no",order.getOrderId());map.put("total_amount",order.getPrice());map.put("subject",order.getSubject());map.put("body",order.getBody());map.put("product_code","FAST_INSTANT_TRADE_PAY");// 设置业务参数request.setBizContent(JSONObject.toJSONString(map));// 发起支付请求// 发起支付请求AlipayTradePagePayResponse response = null;try {response = alipayClient.pageExecute(request);} catch (AlipayApiException e) {throw new RuntimeException(e);}// 获取响应结果if (response.isSuccess()) {System.out.println("调用成功");System.out.println("支付宝支付链接:" + response.getBody());return response.getBody();} else {System.out.println("调用失败");System.out.println("错误信息:" + response.getMsg());return "支付失败";}}
}

Order

@Data
public class Order {// 订单idprivate String orderId;// 价格private String price;// 商品名称private String subject;// 商品描述private String body;// 支付场景/*** FAST_INSTANT_TRADE_PAY(即时到账):适用于即时交易场景,买家付款后,卖家立即收到款项。* QUICK_MSECURITY_PAY(手机网页支付):适用于手机网页支付场景。* FACE_TO_FACE_PAYMENT(当面付):适用于线下面对面付款场景,比如扫码支付。* APP支付(APP支付场景):适用于在APP内的支付场景。* WAP支付(手机网站支付场景):适用于手机网站支付场景。* PRE_AUTH(预授权):适用于预先授权场景,买家授权预先冻结资金,商家在完成业务后调用支付宝解冻资金*/private String code;
}

4.注册 AlipayAPI bean

@Configuration
@EnableConfigurationProperties(PayProperties.class)
public class AutoConfiguration {@Beanpublic AlipayClient getAlipayClient(PayProperties payProperties){AlipayClient alipayClient = new DefaultAlipayClient(payProperties.getGateway(),payProperties.getAppId(),payProperties.getAppPrivateKey(),AlipayConstants.FORMAT_JSON,AlipayConstants.CHARSET_UTF8,payProperties.getAlipayPublicKey(),AlipayConstants.SIGN_TYPE_RSA2);return alipayClient;}@Beanpublic AlipayAPI getAlipayApi(PayProperties payProperties,AlipayClient alipayClient){return new AlipayAPI(payProperties.getNotifyUrl(),alipayClient);}
}

5.编写 META-INF/spring.factories 文件

Spring Boot 自动装配会加载这个config.AutoConfiguration 类,在这个类中注册的bean也会注入到 Spring 容器中

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.hzy.alipaystarter.config.AutoConfiguration

6.项目结构

config- AutoConfiguration 自动装配配置类- PayProperties 配置文件读取类
core - api- AlipayAPI - dtos- Order 

测试

1.创建一个测试项目,引入自定义 starter 依赖
        <dependency><groupId>com.hzy</groupId><artifactId>alipay-starter</artifactId><version>0.0.1-SNAPSHOT</version></dependency>
2.配置文件编写
alipay:appId: appPrivateKey: alipayPublicKey: notifyUrl: gateway: https://openapi-sandbox.dl.alipaydev.com/gateway.do
3.编写测试代码
@SpringBootTest
class TestApplicationTests {@Autowiredprivate AlipayAPI alipayAPI;@Testvoid pay(){Order order = new Order();order.setOrderId(String.valueOf(System.currentTimeMillis()));order.setSubject("xiaomi 12");order.setPrice("456.89");order.setBody("8 + 256");order.setCode("FAST_INSTANT_TRADE_PAY");// 一行代码实现支付宝支付String pay = alipayAPI.pay(order);System.out.println(pay);}
}

image-20231121203521803

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/157033.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3+elementPlus登录向后端服务器发起数据请求Ajax

后端的url登录接口 先修改main.js文件 // 导入Ajax 前后端数据传输 import axios from "axios"; const app createApp(App) //vue3.0使用app.config.globalProperties.$http app.config.globalProperties.$http axios app.mount(#app); login.vue 页面显示部分…

数据结构之栈的讲解

&#x1f495;" 春宵一刻值千金&#xff0c;花有清香月有阴。 "&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;leetcode刷题之哈希表的应用(1) 1.栈的概念 栈是一种只允许在一端&#xff08;栈顶&#xff09;进行数据操作的数据结构&#xff0c;具…

如何把A3 pdf 文章打印成A4

1. 用Adobe Acrobat 打开pdf 2 打印 选择海报 进行调整即可如下图,见下面红色的部分。

说一下类的生命周期

&#x1f47d;System.out.println(“&#x1f44b;&#x1f3fc;嗨&#xff0c;大家好&#xff0c;我是代码不会敲的小符&#xff0c;双非大四&#xff0c;Java实习中…”); &#x1f4da;System.out.println(“&#x1f388;如果文章中有错误的地方&#xff0c;恳请大家指正&a…

MySQL MHA高可用配置及故障切换

一、MHA相关概念 1&#xff0e;什么是 MHA MHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中&#xff0c;MHA能做到0-30秒内自动完成故障切换操作。 …

二百零七、Flume——Flume实时采集5分钟频率的Kafka数据直接写入ODS层表的HDFS文件路径下

一、目的 在离线数仓中&#xff0c;需要用Flume去采集Kafka中的数据&#xff0c;然后写入HDFS中。 由于每种数据类型的频率、数据大小、数据规模不同&#xff0c;因此每种数据的采集需要不同的Flume配置文件。玩了几天Flume&#xff0c;感觉Flume的使用难点就是配置文件 二、…

信号的处理时机(内核态,用户态,如何/为什么相互转换,内核空间,cpu寄存器),信号的处理流程详细介绍+抽象图解

目录 信号的处理时机 引入 思考 -- 什么时候才能算合适的时候呢? 用户态转为内核态 引入 内核地址空间 引入 思考 -- 进程为什么能切换成内核态呢? 虚拟地址空间 注意点 原理 (总结一下) 为什么如何进入内核态 引入 介绍 底层原理(int 80) cpu的寄存器 用…

nodejs express vue uniapp新闻发布系统源码

开发技术&#xff1a; node.js&#xff0c;mysql5.7&#xff0c;vscode&#xff0c;HBuilder nodejs express vue uniapp 功能介绍&#xff1a; 用户端&#xff1a; 登录注册 首页显示搜索新闻&#xff0c;新闻分类&#xff0c;新闻列表 点击新闻进入新闻详情&#xff0…

计算机网络——网络可靠性及网络出口配置

1. 前言&#xff1a; 学习目标&#xff1a; 1.了解链路聚合的作用 2. 了解ACL的工作原理 3. 了解NAT的工作原理和配置 2. 网络可靠性方案 网络可靠性是指网络在面对各种异常情况或故障时&#xff0c;能够维持正常运行和提供服务的能力。这包括防止网络中断、减小数据丢失的可能…

spring boot零配置

spring boot是如何选择tomcat还是Jett作为底层服务器的呢&#xff1f; springboot通过ServletWebServerApplicationContext的onRefresh()方法&#xff0c;会创建web服务 protected void onRefresh() {super.onRefresh();try {// 创建web服务createWebServer();}catch (Throwab…

PyTorch 之 Dataset 类入门学习

PyTorch 之 Dataset 类入门学习 Dataset 类简介 PyTorch 中的 Dataset 类是一个抽象类&#xff0c;用来表示数据集。通过继承 Dataset 类可以进行自定义数据集的格式、大小和其它属性&#xff0c;供后续使用&#xff1b; 可以看到官方封装好的数据集也是直接或间接的继承自 …

6.Gin 路由详解 - GET POST 请求以及参数获取示例

6.Gin 路由详解 - GET POST 请求以及参数获取示例 GET POST 请求以及参数获取示例 Get 请求&#xff1a;获取 Quary 参数 // 获取query参数示例&#xff1a;GET /user?uid20&namejack&page1 r.GET("/user", func(c *gin.Context) {// 获取参数// Query获取参…

[机缘参悟-119] :一个IT人的反思:反者道之动;弱者,道之用 VS 恒者恒强,弱者恒弱的马太效应

目录 前言&#xff1a; 一、道家的核心思想 二、恒者恒强&#xff0c;弱者恒弱的马太效应 三、马太效应与道家思想的统一 3.1 大多数的理解 3.2 个人的理解 四、矛盾的对立统一 前言&#xff1a; 马太效应和强弱互转的道家思想&#xff0c;都反应了自然规律和社会规律&…

SVN 修改版本库地址url路径

一、win11用户 1. win11系统右链菜单比较优秀&#xff0c;如果菜单中选择“TortoiseSVN”找不到“重新定位”&#xff0c;如下图所示&#xff0c;则需要添加右键菜单&#xff1a; 2.添加右键菜单&#xff1a;选择“TortoiseSVN”&#xff0c;点击设置&#xff0c;如下图所示&a…

Vue 项目实战——如何在页面中展示 PDF 文件以及 PDFObject 插件实战

文章目录 &#x1f4cb;前言&#x1f3af;使用 HTML 标签&#x1f9e9; embed 标签&#x1f9e9; object标签&#x1f9e9; iframe标签&#x1f9e9;完整代码 &#x1f3af;使用 PDFObject 插件&#x1f9e9;为什么使用 PDFObject 插件&#xff08;AI翻译&#xff09;&#x1f…

【微服务】SaaS云智慧工地管理平台源码

智慧工地系统是一种利用人工智能和物联网技术来监测和管理建筑工地的系统。它可以通过感知设备、数据处理和分析、智能控制等技术手段&#xff0c;实现对工地施工、设备状态、人员安全等方面的实时监控和管理。 一、智慧工地让工程施工智能化 1、内容全面&#xff0c;多维度数…

华为云IoT与OpenHarmony深度协同,加速设备上鸿即上云【云驻共创】

本次专题论坛探讨了华为云IoT与Open Harmony的深度协同、边缘屏蔽硬件差异、实现智慧隧道全方位智能化管理&#xff0c;以及华为云与Open Harmony生态的合作。同时也介绍了华为云物联网卡平台、HTTP2协议以及华为物联网在交通领域的应用。 一&#xff0e;华为云IoT与Open Harm…

数学几百年重大错误:将两异函数误为同一函数

黄小宁 因各实数都可是数轴上点的坐标所以数集A可形象化为数轴上的点集A&#xff0c;从而使x∈R变换为实数yxδ的几何意义可是&#xff1a;一维空间“管道”g内R轴上的质点x∈R(x是点的坐标)运动到新的位置yxδ还在管道g内&#xff08;设各点只作位置改变而没别的改变即变位前…

Vue学习

1。 搭框架 依赖等 创建vue项目 vue create 项目名称 vue create [options] <app-name>使用vite npm init vitelatest <app-name>-- --template vue 目录调整1 apiutilsvenderimages、styles 配置文件 jsconfig.json 配置之后路径可以直接使用 / {"comp…

mysql 查询

-- 多表查询select * from tb_dept,tb_emp; 内来链接 -- 内连接 -- A 查询员工的姓名 &#xff0c; 及所属的部门名称 &#xff08;隐式内连接实现&#xff09;select tb_emp.name,tb_dept.name from tb_emp,tb_dept where tb_emp.idtb_emp.id;-- 推荐使用select a.name,b.n…