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

news2024/9/30 1:31:06

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

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

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

相关文章

解锁潜力:创建支持Actions接口调用的高级GPTs

如何创建带有Actions接口调用的GPTs 在本篇博客中&#xff0c;我们将介绍如何创建一个带有Actions接口调用的GPTs &#xff0c;以及如何进行配置和使用。我们将以 https://chat.openai.com/g/g-GMrQhe7ka-gptssearch 为例&#xff0c;演示整个过程。 Ps: 数据来源&#xff1a…

如何在3dMax中使用Python返回场景内所有对象的列表?

如何在3dMax中使用Python返回场景内所有对象的列表&#xff1f; 3dMax支持开发基于Python的工具和扩展&#xff0c;因此可以对其进行自定义并将其集成到现代数字内容创建管道中。为此&#xff0c;3dMax集成了Python 3.9解释器&#xff0c;并通过pymxs API公开了3dMax的丰富功能…

2023年山东省安全员B证证模拟考试题库及山东省安全员B证理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年山东省安全员B证证模拟考试题库及山东省安全员B证理论考试试题是由安全生产模拟考试一点通提供&#xff0c;山东省安全员B证证模拟考试题库是根据山东省安全员B证最新版教材&#xff0c;山东省安全员B证大纲整理…

金融企业为啥不选择云服务器还是考虑服务器托管

尽管云主机在近年来的发展中取得了巨大的成功&#xff0c;但在金融行业中&#xff0c;一些客户仍然倾向于将服务器托管到数据中心&#xff0c;而不是使用云主机。以下是一些金融客户选择将服务器托管到数据中心的原因&#xff1a; 数据安全性&#xff1a;金融行业对数据的安全性…

中国毫米波雷达产业分析1——毫米波雷达行业概述

一、毫米波雷达简介 &#xff08;一&#xff09;产品定义 雷达是英文Radar的音译&#xff0c;源于Radio Detection and Ranging的缩写&#xff0c;原意是“无线电探测和测距”&#xff0c;即用无线电方法发现目标并测定它们在空间的位置。毫米波雷达是指一种工作在毫米波频段的…

hook io异常注入

文中code https://gitee.com/bbjg001/darcy_common/tree/master/io_hook 需求引入 最近工作需要&#xff0c;需要验证一下我们的服务在硬盘故障下的鲁棒性。 从同事大佬哪里了解到hook技术&#xff0c;可以通过LD_PRELOAD这个环境变量拦截依赖库的调用链&#xff0c;将对标准…

从0开始学习JavaScript--JavaScript中的对象

JavaScript中的对象是一种重要的数据结构&#xff0c;它不仅是语言的基石&#xff0c;还提供了丰富的功能和灵活性。本文将深入研究JavaScript对象的创建、属性访问、方法定义&#xff0c;以及实际应用中的技巧&#xff0c;通过丰富的示例代码&#xff0c;帮助读者更全面地了解…

pycharm统计代码运行时间

方法1&#xff1a;写代码实现 import……&#xff08;自己会用到的包&#xff09; import time start time.perf_counter() #开始计时#代码开始了 …… …… …… end time.perf_counter() #结束计时 runtime end - start print(f"输出代码运行时间{runtime}")…

七天.NET 8操作SQLite入门到实战 - 第二天 在 Windows 上配置 SQLite环境

前言 SQLite的一个重要的特性是零配置的、无需服务器&#xff0c;这意味着不需要复杂的安装或管理。它跟微软的Access差不多&#xff0c;只是一个.db格式的文件。但是与Access不同的是&#xff0c;它不需要安装任何软件&#xff0c;非常轻巧。 七天.NET 8操作SQLite入门到实战…

RabbitMQ消息队列快速入门

RabbitMQ消息队列快速入门 初始MQ MQ全称为Message Queue&#xff0c;即消息队列&#xff0c;是在消息的传输过程中保存消息的容器。它是典型的生产者-消费者模型。 生产者不断向消息队列中生产消息&#xff0c;消费者不断的从队列中获取消息。消息的生产和消费都是异步的&am…

多项式求和

题目描述 给定程序中 fun 函数的功能是&#xff1a;求出以下分数序列的前 n 项之和&#xff0c;并通过函数值返回 main 函数。 输入格式 输入参数。 输出格式 计算公式返回的结果。 输入输出样例 输入1 5 输出1 8.391667 python解&#xff1a; def fun(n):a1b2s0for…

PyTorch中并行训练的几种方式

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

Linux下使用宏定义判断系统架构和系统类型

文章目录 查看编译器当前支持的宏定义查找指定的宏不同架构不同系统 附录-编译器内部常用的一些宏定义宏定义实际应用使用宏定义判断系统架构使用宏定义判断系统类型 一般情况下在linux下做C/C方面的开发不需要太关注系统架构&#xff0c;当然如果涉及到不同架构下的适配问题&a…

『亚马逊云科技产品测评』活动征文|基于Lightsail 使用 html + css 实现圣诞树

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 前言 又快要到今年的圣诞节了&#xff0c;去年看好多小伙伴分享自己的圣…

完美解决:yum -y install nginx 报出 没有可用软件包 nginx。错误:无须任何处理

目录 一、问题&#xff1a; 二、原因&#xff1a; 三、解决方法&#xff1a; 一、问题&#xff1a; [rootlocalhost ~]# yum -y install nginx 已加载插件&#xff1a;fastestmirror Loading mirror speeds from cached hostfile * base: mirrors.bfsu.edu.cn * extras: m…

windows电脑连接Android和iPhone真机调试

windows电脑连接Android和iPhone真机调试 目前用的是Hbuilder X编辑器&#xff0c;在正常情况下&#xff0c;Android手机需要在 "设置 ----> 更多设置 ----->关于手机 ------> 版本号&#xff08;手指点击5-7下即可打开开发者模式&#xff09;"(我的是vivo的…

环境配置|GitHub——如何在github上搭建自己写的网站

下面简单地总结了从本地的网页文件到在github服务器上展示出来即可以通过网络端打开的过程&#xff1a; &#xff08;以下可能会出现一些难点&#xff0c;照着做就可以了&#xff0c;由于笔者是小白&#xff0c;也不清楚具体原理是什么&#xff0c;希望有一天成为大神的时候能轻…

【漏洞复现】IP-guard WebServer 存在远程命令执行漏洞

漏洞描述 IP-guard是由溢信科技股份有限公司开发的一款终端安全管理软件,旨在帮助企业保护终端设备安全、数据安全、管理网络使用和简化IT系统管理。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危…

MySQL InnoDB 引擎底层解析(三)

6.3.3. InnoDB 的内存结构总结 InnoDB 的内存结构和磁盘存储结构图总结如下&#xff1a; 其中的 Insert/Change Buffer 主要是用于对二级索引的写入优化&#xff0c;Undo 空间则是 undo 日志一般放在系统表空间&#xff0c;但是通过参数配置后&#xff0c;也可以用独立表空 间…

linux 系统调用流程分析

x86 1.系统调用 系统调用是用户空间程序与内核交互的主要机制。系统调用与普通函数调用不同&#xff0c;因为它调用的是内核里的代码。使用系统调用时&#xff0c;需要特殊指令以使处理器权限转换到内核态。另外&#xff0c;被调用的内核代码由系统调用号来标识&#xff0c;而…