简单上手SpringBean的整个装配过程

2024-05-16 08:36

本文主要是介绍简单上手SpringBean的整个装配过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

你好,这里是codetrend专栏“Spring6全攻略”。

典型的企业级应用程序并非仅由单个对象(在Spring术语中称为bean)组成。即使是最简单的应用程序,也会包含一些协同工作的对象,共同呈现出终端用户眼中连贯一致的应用程序形态。

以下mermaid流程图简单展示了Spring工作过程。

产生
业务类POJO
Spring容器ApplicationContext
配置元数据Configuration Metadata
可执行的系统/应用程序

业务类与配置元数据相结合,使得在Spring容器ApplicationContext被创建并初始化后,得到的是一个完全配置好且可执行的系统或应用程序。

下文将从定义一系列独立的bean定义出发,进而构建出一个对象间相互协作以达成目标的完全成型的应用程序。

配置元数据 Configuration Metadata

Spring IoC 容器通过消费一种形式的配置元数据。这些配置元数据代表了您作为应用程序开发者告诉 Spring 容器如何实例化、配置和组装应用程序中的对象。

配置元数据方式如下:

  • 基于 XML 格式配置
  • 基于 Groovy 格式配置
  • 基于Java类和注解进行配置

虽然配置的形式不一样,但是配置内容和api基本一样的。

以下是基于 XML 格式、Groovy 格式和 Java 类与注解的方式来配置 Spring IoC 容器的示例:

  • 基于 XML 格式配置:
<!-- applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userService" class="com.example.UserService"><property name="userDao" ref="userDao"/></bean><bean id="userDao" class="com.example.UserDao"/></beans>
  • 基于 Groovy 格式配置:
// applicationContext.groovy
beans {userService(com.example.UserService) {userDao = ref('userDao')}userDao(com.example.UserDao)
}
  • 基于 Java 类和注解进行配置:
// AppConfig.java
@Configuration
public class AppConfig {@Beanpublic UserService userService() {UserService userService = new UserService();userService.setUserDao(userDao());return userService;}@Beanpublic UserDao userDao() {return new UserDao();}
}

以上示例分别展示了使用 XML、Groovy 和 Java 类与注解的方式来配置 Spring IoC 容器。无论使用哪种配置方式,都可以定义和组装应用程序中的对象,并且相应的 API 在实现上基本一致。

这三种配置方式各有优劣,开发者可以根据项目需求和个人喜好选择合适的方式。

Ioc容器使用初体验

Ioc容器在Spring6框架中也就是各种BeanFactory的实现类来创建和管理对象。

ClassPathXmlApplicationContext就是通过读取xml配置初始化bean的一种方法。

启动类的代码如下:

/*** 宠物测试app* @author nine* @since 1.0*/
public class PetApp {public static void main(String[] args) {// 创建一个类路径下的XML应用上下文,并指定配置文件ApplicationContext context = new ClassPathXmlApplicationContext("s104/services.xml", "s104/daos.xml");// 从上下文中获取名为"petStore"的bean,其类型为PetStoreServiceImpl,其中petStoreAlias是别名PetStoreService petStoreService = context.getBean("petStoreAlias", PetStoreServiceImpl.class);// 调用获取的bean的buyPet方法petStoreService.buyPet(new Pet("Tom", "Cat",1));}
}

bean的配置如下:

<!-- daos.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="accountDao"class="io.yulin.learn.spring.s104.AccountDao"></bean><bean id="itemDao" class="io.yulin.learn.spring.s104.ItemDao"></bean>
</beans><!-- services.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!-- services --><bean id="petStore" class="io.yulin.learn.spring.s104.PetStoreServiceImpl" ><property name="accountDao" ref="accountDao"/><property name="itemDao" ref="itemDao"/></bean><!--命名别名--><alias name="petStore" alias="petStoreAlias" />
</beans>

对应bean如下。其中这些bean也就是简单的业务bean。

@Slf4j
@Data
public class PetStoreServiceImpl implements PetStoreService {private  AccountDao accountDao;private  ItemDao itemDao;@Overridepublic boolean buyPet(Pet pet) {log.info("buy pet: {}", pet);accountDao.store(pet);itemDao.minus(pet);return true;}
}@Slf4j
public class ItemDao {public void minus(Pet pet) {log.info("minus pet num: {}", pet.getNum());}
}@Slf4j
public class AccountDao {public void store(Pet pet) {log.info("增加收入: {}", pet);}
}

这个过程与开发者编写工具类一样,没有任何注解、导入依赖这些配置。习惯使用springboot的开发者可能对此表示有点不习惯。

但是xml元数据配置+BeanFactory一起,就组合成了一个单独的app。运行起来就和springboot无异。

讲上述代码修改为基于Java类和注解进行配置的代码如下。

public class PetAppJavaConfig {public static void main(String[] args) {// 创建一个基于 Java Config 的应用上下文ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);// 从上下文中获取名为"petStoreService"的bean,其类型为PetStoreServicePetStoreService petStoreService = context.getBean(PetStoreService.class);// 调用获取的bean的buyPet方法petStoreService.buyPet(new Pet("Tom", "Cat", 1));}
}
@Configuration
class AppConfig {@Beanpublic PetStoreService petStoreService(AccountDao accountDao, ItemDao itemDao) {PetStoreServiceImpl petStoreService = new PetStoreServiceImpl();petStoreService.setAccountDao(accountDao);petStoreService.setItemDao(itemDao);return petStoreService;}@Beanpublic AccountDao accountDao() {return new AccountDao();}@Beanpublic ItemDao itemDao() {return new ItemDao();}
}

可以看出代码简洁明了不少,代码输出结果都是一致的。

11:38:11.646 [main] INFO io.yulin.learn.spring.s104.PetStoreServiceImpl -- buy pet: Pet(name=Tom, type=Cat, num=1)
11:38:11.653 [main] INFO io.yulin.learn.spring.s104.AccountDao -- 增加收入: Pet(name=Tom, type=Cat, num=1)
11:38:11.653 [main] INFO io.yulin.learn.spring.s104.ItemDao -- minus pet num: 1

完整项目源码信息查看可以在gitee或者github上搜索r0ad查看。(外链审核太严格~木办法)

配置Bean初体验

Spring IoC容器管理一个或多个bean。这些bean是根据您提供给容器的配置元数据创建的(例如,以XML <bean/> 定义的形式)。

在容器内部,bean 定义被表示为 BeanDefinition 对象,其中包含(除其他信息外)以下元数据:

  • 一个包限定的类名:通常是所定义的 bean 的实际实现类。
  • Bean 行为配置元素,用于说明 bean 在容器中应如何运行(作用域、生命周期回调等)。
  • 引用其他 bean,这些 bean 是该 bean 执行工作所需的。这些引用也称为协作者或依赖项。
  • 其他配置设置用于设置新创建对象中的值,例如,管理连接池的 bean 中的池大小限制或要使用的连接数。

/*** 说明备案definition的例子** @author nine* @since 1.0*/
public class BeanDefinitionProcessDemo {public static void main(String[] args) {// ️GenericApplicationContext 是一个【干净】的容器GenericApplicationContext context = new GenericApplicationContext();// 用原始方法注册三个 beancontext.registerBean("bean1", Bean1.class);// 初始化容器// 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例context.refresh();Bean1 bean = context.getBean(Bean1.class);bean.print();// 销毁容器context.close();}
}@Slf4j
class Bean1 {public void print() {log.info("I am bean1");}
}

通过这个代码可以发现,Bean1在调用registerBean接口后从一个普通的pojo类变成了一个bean。

org.springframework.context.support.GenericApplicationContext#registerBean为了方便使用有很多重载方法。

通过源码可以发现,普通类通过ClassDerivedBeanDefinition的构造函数转换为BeanDefinition。也就是该class通过setBeanClass成为BeanDefinition的属性beanClass

后续通过一些列操作,自定义、名字处理、注册容器等等添加了其他的属性信息或者进行二次处理。

具体源码如下。

public <T> void registerBean(@Nullable String beanName, Class<T> beanClass, @Nullable Supplier<T> supplier, BeanDefinitionCustomizer... customizers) {// 创建一个 ClassDerivedBeanDefinition 对象,用于封装 Bean 的定义信息ClassDerivedBeanDefinition beanDefinition = new ClassDerivedBeanDefinition(beanClass);// 如果存在 supplier,则设置到 BeanDefinition 中if (supplier != null) {beanDefinition.setInstanceSupplier(supplier);}// 对 BeanDefinition 进行定制处理for (BeanDefinitionCustomizer customizer : customizers) {customizer.customize(beanDefinition);}// 如果指定了 beanName,则使用指定的名称,否则使用 beanClass 的名称String nameToUse = (beanName != null ? beanName : beanClass.getName());// 将封装好的 BeanDefinition 注册到容器中registerBeanDefinition(nameToUse, beanDefinition);
}

bean的生命周期回调

Spring6 中 Bean 的生命周期可以通过 InitializingBean 和 DisposableBean 接口、@PostConstruct 和 @PreDestroy 注解以及配置文件中的 init-method 和 destroy-method 方法来管理。

把上面手动注入的bean的demo修改,增加实现 Bean 的初始化和销毁回调:

public class BeanDefinitionProcessDemo {public static void main(String[] args) {// GenericApplicationContext 是一个干净的容器GenericApplicationContext context = new GenericApplicationContext();// 用原始方法注册 bean1,并指定初始化和销毁方法context.registerBean("bean1", Bean1.class, Bean1::new, beanDefinition -> {beanDefinition.setInitMethodName("init");beanDefinition.setDestroyMethodName("destroy");});// 初始化容器context.refresh();Bean1 bean = context.getBean(Bean1.class);bean.print();// 销毁容器context.close();}
}@Slf4j
class Bean1 {public void print() {log.info("I am bean1");}public void init() {log.info("Bean1 is being initialized");}public void destroy() {log.info("Bean1 is being destroyed");}
}

通过输出可以发现,bean1的初始化和销毁回调被调用了。

15:14:23.631 [main] INFO io.yulin.learn.spring.s104.Bean1 -- Bean1 is being initialized
15:14:23.669 [main] INFO io.yulin.learn.spring.s104.Bean1 -- I am bean1
15:14:23.670 [main] INFO io.yulin.learn.spring.s104.Bean1 -- Bean1 is being destroyed

把整个过程改为更熟悉的基于注解驱动开发的方式,代码如下。

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** 通过注解方式配置Bean* @author nine* @since 1.0*/
public class BeanDefinitionProcessAnnotationDemo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(BeanAnnotation1.class);context.refresh();BeanAnnotation1 bean = context.getBean(BeanAnnotation1.class);bean.print();context.close();}
}@Slf4j
class BeanAnnotation1 {public void print() {log.info("I am BeanAnnotation1");}@PostConstructpublic void init() {log.info("BeanAnnotation1 is being initialized");}@PreDestroypublic void destroy() {log.info("BeanAnnotation1 is being destroyed");}
}

输出结果基本一致的。

通过这个转换过程可以更能清晰的发现,Spring如何从基于xml配置、Java配置、注解配置的转换。也能更加深刻体会到Spring的强大兼容性。

实例化Bean

需要使用bean就必须实例化这个类,最简单的方式就是new 一个对象。

但是在Spring6框架中提供了更多的配置来实现实例化bean。

如果使用基于XML的配置元数据,可以在<bean/>元素的class属性中指定要实例化的对象的类型(或类)。

这个class属性(在BeanDefinition实例上内部是一个Class属性)通常是必需的。

使用Class属性的两种方式之一:

  • 通常情况下,为了指定要构造的bean类,在容器本身通过调用其构造函数反射性地直接创建bean的情况下,类似于使用new运算符的Java代码。
  • 在较不常见的情况下,为了指定包含静态工厂方法的实际类,容器调用该类上的静态工厂方法来创建bean。从调用静态工厂方法返回的对象类型可以是相同的类,也可以是完全不同的类。

构造器实例化bean

当通过构造函数方式创建一个bean时,所有普通类都可以被Spring使用并与之兼容。

也就是说,正在开发的类不需要实现任何特定的接口或以特定方式编码。只需指定bean类即可。

然而,根据为该特定bean使用的IoC类型,可能需要一个默认(空)构造函数。

Spring IoC容器可以管理几乎任何希望它管理的类。它不局限于管理真正的JavaBeans。

大多数Spring用户更喜欢具有仅默认(无参数)构造函数。

Spring容器还可以管理非bean的类。例如,如果需要使用不符合JavaBean规范的传统连接池,Spring也可以进行管理。

下面通过一个例子说明基于构造器实例化bean的例子。

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** SpringBean创建demo** @author nine* @since 1.0*/
public class SpringBeanCreateDemo {public static void main(String[] args) {// 创建一个基于 Java Config 的应用上下文ApplicationContext context = new AnnotationConfigApplicationContext(AppCreateConfig.class);// 从上下文中获取名bean,其类型为PetStoreServiceMyClass bean = context.getBean(MyClass.class);// 调用获取的bean的方法bean.hello("jack");}
}@Configuration
@Slf4j
class AppCreateConfig {@Beanpublic MyClass exampleBean() {return new MyClass("exampleConstructorArg");}
}@Slf4j
class MyClass {public MyClass(String constructorArg) {log.info(constructorArg);}public void hello(String name) {log.info("hello " + name);}
}

输出结果如下。可以看到MyClass类被正确初始化和被IoC容器管理。

09:44:57.211 [main] INFO io.yulin.learn.spring.s104.MyClass -- exampleConstructorArg
09:44:57.262 [main] INFO io.yulin.learn.spring.s104.MyClass -- hello jack

静态工厂方法实例化bean

在定义使用静态工厂方法创建的bean时,使用class属性指定包含静态工厂方法的类,并使用名为factory-method的属性指定工厂方法本身的名称。

应该能够调用这个方法(带有可选参数,如后面所述),并返回一个活动对象,随后将其视为通过构造函数创建的对象。

这样一个bean定义的用途之一是在遗留代码中调用静态工厂。

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** SpringBean创建demo** @author nine* @since 1.0*/
public class SpringBeanFactoryMethodCreateDemo {public static void main(String[] args) {// 创建一个基于 Java Config 的应用上下文ApplicationContext context = new AnnotationConfigApplicationContext(AppFactoryConfig.class);// 从上下文中获取名bean,其类型为PetStoreServiceAppFactoryConfig.MyBean bean = context.getBean(AppFactoryConfig.MyBean.class);// 调用获取的bean的方法bean.hello();}
}@Slf4j
@Configuration
class AppFactoryConfig {@Beanpublic MyBean myBean() {// 调用带有可选参数的静态工厂方法创建 beanreturn MyBeanFactory.createBean("Tom");}static class MyBean {private String name;public MyBean(String name) {this.name = name;}public void hello() {log.info("Hello, " + name);}}static class MyBeanFactory {public static MyBean createBean(String parameter) {return new MyBean(parameter);}}
}

通过实例工厂方法实例化bean

类似于通过静态工厂方法进行实例化,使用实例工厂方法进行实例化会调用容器中现有 bean 的非静态方法来创建一个新的 bean。

要使用这种机制,将class属性留空,在factory-bean属性中指定当前(或父级或祖先)容器中包含要被调用以创建对象的实例方法的 bean 的名称。

使用factory-method属性设置工厂方法本身的名称。

这个例子是基于java config来实现的。

可以发现MyBean通过现有名为MyBeanFactory的bean来创建的。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AppBeanCreateConfig {@Beanpublic MyBeanFactory myBeanFactory() {return new MyBeanFactory();}@Beanpublic MyBean myBean(MyBeanFactory myBeanFactory) {// 调用实例工厂方法创建 beanreturn myBeanFactory.createBean("optionalParameter");}static class MyBean {private String name;public MyBean(String name) {this.name = name;}public void hello() {System.out.println("Hello, " + name);}}static class MyBeanFactory {public MyBean createBean(String parameter) {return new MyBean(parameter);}}
}

确定Bean的运行时类型

确定Spring框架中一个特定bean的运行时类型确实需要考虑到多种复杂情况。

在bean元数据定义中指定的类只是一个初始类引用,可能与声明的工厂方法结合,或者是一个可能导致bean具有不同运行时类型的FactoryBean类,或者在实例级工厂方法的情况下根本没有设置(这是通过指定的工厂-bean 名称来解析的)。

此外,AOP代理可能会用基于接口的代理包装一个bean实例,只暴露目标bean的实际类型(仅暴露其实现的接口)。

查找特定bean的实际运行时类型的推荐方法是使用BeanFactory.getType调用指定的bean名称。

这考虑了上述所有情况,并返回BeanFactory.getBean调用将为相同的bean名称返回的对象类型。

以上面的通过实例工厂方法实例化bean为例说明使用BeanFactory.getType

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;@Slf4j
public class BeanRunTimeTypeTest {@Testpublic void test() {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppBeanCreateConfig.class);// 获取 BeanFactory 实例ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();// 使用 BeanFactory.getType 方法获取特定 bean 的运行时类型Class<?> beanType = beanFactory.getType("myBean");log.info("The runtime type of 'myBean' is: " + beanType.getName());// 使用 BeanFactory.getBean 方法获取特定 bean 的实例对象AppBeanCreateConfig.MyBean myBeanInstance = (AppBeanCreateConfig.MyBean) beanFactory.getBean("myBean");myBeanInstance.hello();// 关闭应用上下文context.close();}
}

输出结果如下。可以看到实例工厂方法没有设置class,但是运行时类型为MyBean。

10:51:25.111 [main] INFO io.yulin.learn.spring.s104.BeanRunTimeTypeTest -- The runtime type of 'myBean' is: io.yulin.learn.spring.s104.AppBeanCreateConfig$MyBean
Hello, optionalParameter

BeanFactory.getType()一些常见用途:

  • 类型检查:通过调用 getType() 方法,可以获取特定 bean 的实际类型,并根据这些类型信息执行相应的操作。这对于在运行时进行类型检查和验证非常有用。
  • 动态处理:在某些情况下,您可能需要根据 bean 的类型来动态地决定如何处理该 bean。通过 getType() 方法可以获取 bean 的类型信息,并根据需要执行相应的处理逻辑。
  • 条件化配置:在 Spring 应用程序中,有时根据 bean 的类型来进行条件化的配置会很有用。通过 getType() 方法可以获取 bean 的类型,从而根据不同的类型执行不同的配置。
  • 自定义逻辑:某些情况下,可能需要根据 bean 的类型来编写特定的业务逻辑。通过 getType() 方法可以获取 bean 的准确类型信息,并在代码中编写相应的逻辑。

关于作者

来自全栈程序员nine的探索与实践,持续迭代中。

欢迎关注或者点个小红心~

这篇关于简单上手SpringBean的整个装配过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/994406

相关文章

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

SpringBoot 整合 Grizzly的过程

《SpringBoot整合Grizzly的过程》Grizzly是一个高性能的、异步的、非阻塞的HTTP服务器框架,它可以与SpringBoot一起提供比传统的Tomcat或Jet... 目录为什么选择 Grizzly?Spring Boot + Grizzly 整合的优势添加依赖自定义 Grizzly 作为

mysql-8.0.30压缩包版安装和配置MySQL环境过程

《mysql-8.0.30压缩包版安装和配置MySQL环境过程》该文章介绍了如何在Windows系统中下载、安装和配置MySQL数据库,包括下载地址、解压文件、创建和配置my.ini文件、设置环境变量... 目录压缩包安装配置下载配置环境变量下载和初始化总结压缩包安装配置下载下载地址:https://d

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

使用PyQt5编写一个简单的取色器

《使用PyQt5编写一个简单的取色器》:本文主要介绍PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16进制颜色编码,一款跟随鼠标刷新图像的RGB和16... 目录取色器1取色器2PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16

springboot整合gateway的详细过程

《springboot整合gateway的详细过程》本文介绍了如何配置和使用SpringCloudGateway构建一个API网关,通过实例代码介绍了springboot整合gateway的过程,需要... 目录1. 添加依赖2. 配置网关路由3. 启用Eureka客户端(可选)4. 创建主应用类5. 自定

四种简单方法 轻松进入电脑主板 BIOS 或 UEFI 固件设置

《四种简单方法轻松进入电脑主板BIOS或UEFI固件设置》设置BIOS/UEFI是计算机维护和管理中的一项重要任务,它允许用户配置计算机的启动选项、硬件设置和其他关键参数,该怎么进入呢?下面... 随着计算机技术的发展,大多数主流 PC 和笔记本已经从传统 BIOS 转向了 UEFI 固件。很多时候,我们也

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

最新版IDEA配置 Tomcat的详细过程

《最新版IDEA配置Tomcat的详细过程》本文介绍如何在IDEA中配置Tomcat服务器,并创建Web项目,首先检查Tomcat是否安装完成,然后在IDEA中创建Web项目并添加Web结构,接着,... 目录配置tomcat第一步,先给项目添加Web结构查看端口号配置tomcat    先检查自己的to

SpringBoot集成SOL链的详细过程

《SpringBoot集成SOL链的详细过程》Solanaj是一个用于与Solana区块链交互的Java库,它为Java开发者提供了一套功能丰富的API,使得在Java环境中可以轻松构建与Solana... 目录一、什么是solanaj?二、Pom依赖三、主要类3.1 RpcClient3.2 Public