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 {

    @Bean
    public 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 {
    // 订单id
    private 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 {

    @Bean
    public 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;
    }
    @Bean
    public 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 {
    @Autowired
    private AlipayAPI alipayAPI;

    @Test
    void 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

golang学习笔记——接口和继承比较1

继承 Go 语言的设计之初&#xff0c;就不打算支持面向对象的编程特性&#xff0c;因此 Go 不支持面向对象的三大特性之一——继承。但是 Go 可以通过组合的思想去实现 “继承”。继承是面向对象的三大特性之一&#xff0c;继承是从已有的类中派生出新的类&#xff0c;新的类能…

基数排序详解(LSD方法+MSD方法+思路+图解+代码)

文章目录 基数排序一、基数排序概念1.LSD排序法&#xff08;最低位优先法&#xff09;2.MSD排序法&#xff08;最高位优先法&#xff09; 基数排序 一、基数排序 概念 基数排序是一种非比较型整数排序算法 将整数按位数切割成不同的数字&#xff0c;然后按每个位数分别比较 …

函数有返回类型,但函数体未返回类型,程序崩溃问题记录

问题 使用类指针调用函数时&#xff0c;程序崩溃。 问题定位&#xff1a; name new nameSetting;name->setName("helloworld");qDebug().noquote() << name->getName();原因 class nameSetting { public:nameSetting();QString setName(const QStri…

短视频配音软件有哪些?这些常用的短视频配音软件

短视频行业近年来发展得很快&#xff0c;几乎闯入了我们每个现代人的生活&#xff0c;它以其独有的特点和乐趣&#xff0c;也收获了大批短视频爱好者&#xff0c;配音是短视频创作过程中不可或缺的环节&#xff0c;今天&#xff0c;我们就来聊聊短视频配音及好用的配音软件。 短…

关闭bitlocker加密

windows11的笔记本电脑买回来发现分驱都处于bitlocker状态&#xff0c;上网上搜索都是说进入控制面板的安全项进行关闭&#xff0c;包括去搜索栏搜索“管理 BitLocker”&#xff0c;对搜索出来的项打开&#xff0c;经过试验&#xff0c;它们进入的是同一个位置&#xff0c;只有…

详解Python安装requests库的实例代码

文章目录 前言基本用法基本的get请求带参数的GET请求解析json关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠道 前…

【20年扬大真题】编写对数组求逆的递归算法

【20年扬大真题】 编写对数组求逆的递归算法 void swap(int* a, int* b) {int tmp *b;*b *a;*a tmp; } void Ni(int arr[],int left,int right) {if (left > right) {return;}swap(&arr[left], &arr[right]);Ni(arr, left 1, right - 1); } int main() {int ar…

全网最全jmeter接口测试/接口自动化测试看这篇文章就够了:跨线程组传递jmeter变量及cookie的处理

setUp线程组 setUp thread group&#xff1a; 一种特殊类型的线程组&#xff0c;用于在执行常规线程组之前执行一些必要的操作。 在 setup线程组下提到的线程行为与普通线程组完全相同。不同的是执行顺序--- 它会在普通线程组执行之前被触发&#xff1b; 应用场景举例&#xf…

Oracle数据库笔记(一)

1.概述 Oracle版本 19c 在线迁移、自适应扫描、自适应数据共享11g 企业管理器、自动化诊断工具、自动化性能管理 Oracle特点 可用性强可扩展性强数据安全性强稳定性强 常见数据库 小 Access中 SQL Server、MySQL大 Oracle、DB2 2.数据、数据库、数据库管理系统、数据库系…

基于知识问答的上下文学习中的代码风格11.20

基于知识问答的上下文学习中的代码风格 摘要1 引言2 相关工作3 方法3.1 概述3.2 元函数设计3.3 推理 4 实验4.1 实验设置4.2 实施细节4.3 主要结果 摘要 现有的基于知识的问题分类方法通常依赖于复杂的训练技术和模型框架&#xff0c;在实际应用中存在诸多局限性。最近&#x…

redis运维(十二)

一 位图 ① 概念 1、说明&#xff1a;位图还是在操作字符串2、位图玩字符串在内存中存储的二进制3、ASCII字符通过映射转化为二进制4、操作的是字符串value ② ASCII字符铺垫 1、控制ASCII字符 2、ASCII可显示字符 ③ SETBIT 细节&#xff1a; setbit 命令的返回值是之…

算法分析与设计课后练习22

设W(5,7,10,12,15,18,20)和M35&#xff0c;使用过程SUMOFSUB找出W种使得和数等于M的全部子集并画出所生成的部分状态空间树

打破传统束缚,释放服务潜能:本地生活服务商聚合系统引领行业新风向!

本地生活服务商聚合系统是一种集合多平台、多项目的创新型服务系统&#xff0c;它打破了传统服务商系统的一对一限制&#xff0c;为创业者和运营商带来了诸多优势。小多将深入探讨本地生活服务商聚合系统的优势。 随着互联网的快速发展&#xff0c;本地生活服务也迎来了蓬勃的发…

Java实现围棋算法

围棋是一种源自中国的棋类游戏&#xff0c;也是世界上最古老、最复杂的棋类游戏之一。该游戏由黑白两方交替放置棋子在棋盘上进行&#xff0c;目的是将自己的棋子占据更多的空间&#xff0c;并将对手的棋子围死或吃掉&#xff0c;最终获得胜利。围棋不仅是一种游戏&#xff0c;…

审计dvwa高难度命令执行漏洞的代码,编写实例说明如下函数的用法

审计dvwa高难度命令执行漏洞的代码 &#xff0c;编写实例说明如下函数的用法 代码&#xff1a; <?phpif( isset( $_POST[ Submit ] ) ) {// Get input$target trim($_REQUEST[ ip ]);// Set blacklist$substitutions array(& > ,; > ,| > ,- > ,$ …

PACS系统源码,WORKLIST数字化工作流程,影像数字化存储,电子报告书写、胶片打印

PACS系统源码 可与医院HIS、LIS无缝连接 PACS系统以实现医学影像数字化存储、诊断为核心任务&#xff0c;从医学影像设备&#xff08;如CT、CR、DR、MR、DSA、RF等&#xff09;获取影像&#xff0c;集中存储、综合管理医学影像及病人相关信息&#xff0c;建立数字化工作流程。 …

Linux 环境配置小白入门

Linux从 全栈开发centOS 7 到 运维 一 Linux 入门概述1.1 操作系统1.2 Linux 简介1.3 Linux 系统组成1.4 Linux 发行版1.5 Linux 应用领域1.6 Linux vs Windows 二 虚拟机2.1 虚拟机介绍2.2 VMware WorkStation 安装2.3 VMware WorkStation 配置检查2.3 安装 CentOS 72.3.1 安装…

PIL如何批量给图片添加文字水印?

PIL如何批量给图片添加文字水印&#xff1f; 1 简单引入2 关于PIL3 本文涉及的PIL的几个类4 实现原理5 实现过程5.1 原始图片5.2 导入相关模块5.3 初始化数据5.4 水印字体设置5.5 打开原始图片并新建存储对象5.6 计算图片和水印的大小5.7 选择性设置水印文字5.8 绘制文字并设置…

ChatGPT/GPT4科研实践应用与AI绘图技术及论文高效写作

2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车…

高防CDN如何预防攻击?

现在网络攻击事件越来越多&#xff0c;而且愈发凶猛&#xff0c;为了保障互联网业务能稳定正常的运行&#xff0c;市场上出现了很多高防产品&#xff0c;例如高防服务器、高防IP、高防CDN等等。其中究竟高防CDN怎么防攻击&#xff0c;能防哪些攻击&#xff1f;高防CDN如何实现防…