【 微服务SpringCloud | 模块拆分 】

**摘要:**本文围绕微服务项目拆分构建展开:基于 Spring Cloud Alibaba 搭建项目结构,按职责拆分多子模块,保障微服务正常运行。

创建项目结构

首先,我们需要创建微服务项目的整体结构。在原有项目的根目录下,我们创建一个新的微服务父项目 ai-code-mother-microservice

然后依次创建各个子模块:

复制代码
ai-code-mother-microservice/
├── pom.xml                          # 父项目 POM
├── ai-code-common/              # 公共模块
├── ai-code-model/               # 实体模型模块
├── ai-code-client/              # 服务接口模块
├── ai-code-user/                # 用户服务
├── ai-code-app/                 # 应用服务
├── ai-code-ai/                  # AI 代码生成
└── ai-code-screenshot/          # 网页截图服务

明确依赖版本

微服务开发中,由于涉及的依赖较多,一定要注意各个依赖版本的兼容性!

根据 Spring Cloud Alibaba 的版本说明,选择以下版本进行开发:

技术组件 推荐版本
Java 21
Spring Boot 3.5.3
Spring Cloud 2023.0.1
Spring Cloud Alibaba 20<23.0.1.0>
Nacos Server 2.3.2
Dubbo 3.3.0
Higress 2.1.6

父项目 POM 配置

父项目的 POM 文件是整个微服务项目的核心配置文件,它定义了全局依赖版本号、引入全局依赖、配置打包插件等:

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
​
    <groupId>com.yupi</groupId>
    <artifactId>yu-ai-code-mother-microservice</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>yu-ai-code-common</module>
        <module>yu-ai-code-model</module>
        <module>yu-ai-code-client</module>
        <module>yu-ai-code-user</module>
        <module>yu-ai-code-app</module>
        <module>yu-ai-code-ai</module>
        <module>yu-ai-code-screenshot</module>
    </modules>
​
    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-boot.version>3.5.3</spring-boot.version>
        <spring-cloud.version>2023.0.1</spring-cloud.version>
        <spring-cloud-alibaba.version>2023.0.1.0</spring-cloud-alibaba.version>
    </properties>
​
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.38</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.38</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
​
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.14.0</version>
                <configuration>
                    <parameters>true</parameters>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

需要注意一个重要的配置:必须配置 Maven 编译插件,并且设置编译时保留参数名信息(-parameters 标志),否则 Spring 无法通过反射获取请求参数名,会导致有些 GET 请求参数失效。

比如这个接口:

java 复制代码
@GetMapping("/get")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<User> getUserById(long id) {
    ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
    User user = userService.getById(id);
    ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR);
    return ResultUtils.success(user);
}

如果没有正确配置编译插件,接口文档就无法识别参数名称,影响开发调试。

通用模块构建

ai-code-common 公共模块

公共模块包含所有模块公用的代码,一般不包含业务逻辑、不使用复杂的依赖。

这个模块的依赖配置如下:

java 复制代码
<dependencies>
    <!-- 腾讯云 COS 对象存储 -->
    <dependency>
        <groupId>com.qcloud</groupId>
        <artifactId>cos_api</artifactId>
        <version>5.6.227</version>
    </dependency>

    <!-- MyBatis Flex 代码生成 -->
    <dependency>
        <groupId>com.mybatis-flex</groupId>
        <artifactId>mybatis-flex-codegen</artifactId>
        <version>1.11.0</version>
    </dependency>

    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
</dependencies>

接下来进行代码迁移,也就是把代码从原单体项目复制到新微服务项目的对应位置。这里要确保迁移的包路径和之前完全一致,减少代码修改量。

迁移内容

  • common/ 公共请求响应类(BaseResponse、ResultUtils等)

  • constant/ 常量

  • exception/ 异常处理

  • generator/ 代码生成器

  • utils/ 工具类(除 WebScreenshotUtils 外)

  • config/ 配置类(JsonConfig、CorsConfig、CosClientConfig)

  • manager/ 通用能力(CosManager)

  • annotation/ 注解(AuthCheck)

注意事项

  • ratelimit/ 限流模块涉及到引用 UserService,有一定的业务特性,因此不放到 common 中(可以单独定义成模块)

  • constant/ 常量也可以选择拆分到对应的业务模块中,比如 UserConstant 放到用户模块、AppConstant 放到应用模块

  • 引入 CosManager 后,必须要填写 COS 配置才能启动项目,但有些模块是不需要 CosManager 的。因此添加条件注解,没配置就不加载:

java 复制代码
@Configuration
@ConfigurationProperties(prefix = "cos.client")
@ConditionalOnProperty(
        prefix = "cos.client",
        name = {"host", "secretId", "secretKey", "region", "bucket"}
)
@Data
public class CosClientConfig {
}
​
@Component
@ConditionalOnBean(COSClient.class)
@Slf4j
public class CosManager {
}

这样确保了只有在配置了 COS 相关参数时才会加载相关的 Bean,避免了启动时的配置错误。

ai-code-model 实体模型模块

实体模型模块的依赖配置很简单,只需要依赖公共模块:

XML 复制代码
<dependencies>
    <dependency>
        <groupId>com.yupi</groupId>
        <artifactId>yu-ai-code-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

然后迁移整个 model 包:

  • model/entity/ 实体类(User、App、ChatHistory)

  • model/dto/ 数据传输对象

  • model/vo/ 视图对象

  • model/enums/ 枚举类

由于这个模块依赖其他模块,需要通过父项目进行统一构建,这样可以确保依赖关系正确、版本一致、构建顺序合理。

ai-code-client 服务接口模块

这个模块定义了需要被其他服务内部调用的接口。

这里沿用了基于 Feign 实现的命名,client 的含义是可以和实际服务交互的客户端,也可以重命名为 api 模块。

依赖配置如下:

XML 复制代码
<dependencies>
    <dependency>
        <groupId>com.yupi</groupId>
        <artifactId>yu-ai-code-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>com.yupi</groupId>
        <artifactId>yu-ai-code-model</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

为了和对外提供的接口区分,这个模块内的接口统一取名为 innerService,放到 innerservice 包下。

InnerUserService 定义了用户相关的内部接口:

java 复制代码
User getLoginUser(HttpServletRequest request);

List<User> listByIds(Collection<? extends Serializable> ids);

User getById(Serializable id);

UserVO getUserVO(User user);

InnerScreenshotService 定义了截图相关的内部接口:

java 复制代码
String generateAndUploadScreenshot(String webUrl);

其中,用户服务的 getLoginUser 方法比较特殊,由于 HttpServletRequest 对象不好在网络中传递,因此采用静态方法,避免跨服务调用:

java 复制代码
public interface InnerUserService {

    List<User> listByIds(Collection<? extends Serializable> ids);

    User getById(Serializable id);

    UserVO getUserVO(User user);

    // 静态方法,避免跨服务调用
    static User getLoginUser(HttpServletRequest request) {
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        return currentUser;
    }
}

注意,不能使用默认方法,否则 Dubbo 会尝试序列化,导致报错。

业务服务构建

ai-code-user 用户服务

修改用户服务的依赖配置,包括 Session 管理、数据访问等功能:

XML 复制代码
<dependencies>
    <!-- Spring Session + Redis -->
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>

    <!-- 必须引入,才能共享 Session -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
      <exclusions>
        <exclusion>
          <artifactId>lettuce-core</artifactId>
          <groupId>io.lettuce</groupId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
    </dependency>

    <!-- 数据访问 -->
    <dependency>
        <groupId>com.mybatis-flex</groupId>
        <artifactId>mybatis-flex-spring-boot3-starter</artifactId>
        <version>1.11.0</version>
    </dependency>

    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>com.yupi</groupId>
        <artifactId>yu-ai-code-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <dependency>
        <groupId>com.yupi</groupId>
        <artifactId>yu-ai-code-model</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

💡 注意,必须引入 spring-boot-starter-data-redis 依赖,才能在微服务环境中共享 Session,并且这里采用了 jedis 连接池替代默认的 lettuce 连接池,让连接池管理更稳定。

编写配置文件 application.yml,包含应用基本信息、Session 配置、数据库配置、Redis 配置等:

bash 复制代码
spring:
  application:
    name: yu-ai-code-user
  # session
  session:
    store-type: redis
    # 30 days expire
    timeout: 2592000
  # mysql
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/yu_ai_code_mother
    username: root
    password: 123456
  # redis
  data:
    redis:
      host: localhost
      port: 6379
      database: 0
      password:
  profiles:
    active: local
server:
  port: 8124
  servlet:
    context-path: /api
    # 30 days expire
    session:
      cookie:
        max-age: 2592000
# springdoc-openapi
springdoc:
  group-configs:
    - group: 'default'
      packages-to-scan: com.yupi.yuaicodemother.controller
# knife4j
knife4j:
  enable: true
  setting:
    language: zh_cn

接下来迁移代码,包括:

  • aop/AuthInterceptor.java 权限拦截器

  • controller/UserController.java 用户控制器

  • service/UserService.java 和 service/impl/UserServiceImpl.java 用户服务及实现类

  • mapper/UserMapper.java 和对应的 XML 文件(注意 XML 文件中引入的 Mapper 路径)

创建主启动类,代码如下:

java 复制代码
@SpringBootApplication
@MapperScan("com.yupi.yuaicodeuser.mapper")
@ComponentScan("com.yupi")
@EnableDubbo
public class YuAiCodeUserApplication {
    public static void main(String[] args) {
        SpringApplication.run(YuAiCodeUserApplication.class, args);
    }
}

ai-code-AI 代码生成服务

方案设计阶段提到过,综合考虑开发复杂度、以及 Dubbo 的序列化机制不支持 Reactor 的 Flux 类型,AI 模块不能单独作为服务调用,而是以依赖的方式引入。

编写依赖配置,包括 LangChain4j 相关的依赖:

XML 复制代码
<dependencies>
    <!-- LangChain4j -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
        <version>1.1.0-beta7</version>
    </dependency>
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-reactor</artifactId>
        <version>1.1.0-beta7</version>
    </dependency>
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-community-redis-spring-boot-starter</artifactId>
        <version>1.1.0-beta7</version>
    </dependency>
    <dependency>
        <groupId>com.yupi</groupId>
        <artifactId>yu-ai-code-model</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>com.yupi</groupId>
        <artifactId>yu-ai-code-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

然后迁移代码,内容包括:

  • dev.langchain4j/ LangChain4j 源码修改

  • ai/guardrail/ AI 护轨相关代码

  • ai/model/ AI 模型相关代码

  • ai/tools/ AI 工具相关代码

  • ai/ 所有 AI Service(不迁移工厂类)

  • config/ 和 AI 模型相关的配置

  • resources/prompt/ 提示词文件

考虑到可观测性是独立学习的章节,不迁移可观测性的 monitor 包,因此要修改 StreamingChatModelConfig 和 ReasoningStreamingChatModelConfig 类,移除监听器。

ai-code-app 应用服务

应用服务是最复杂的服务,它的依赖配置包括分布式限流、缓存、Session、数据访问等:

XML 复制代码
<dependencies>
    <!-- Redisson for distributed rate limiting -->
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.50.0</version>
    </dependency>

    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
    </dependency>

    <!-- Spring Session + Redis -->
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>

    <!-- 必须引入,才能共享 Session -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
      <exclusions>
        <exclusion>
          <artifactId>lettuce-core</artifactId>
          <groupId>io.lettuce</groupId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
    </dependency>

    <!-- 数据访问 -->
    <dependency>
        <groupId>com.mybatis-flex</groupId>
        <artifactId>mybatis-flex-spring-boot3-starter</artifactId>
        <version>1.11.0</version>
    </dependency>

    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>com.yupi</groupId>
        <artifactId>yu-ai-code-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <dependency>
        <groupId>com.yupi</groupId>
        <artifactId>yu-ai-code-model</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <dependency>
        <groupId>com.yupi</groupId>
        <artifactId>yu-ai-code-client</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <dependency>
        <groupId>com.yupi</groupId>
        <artifactId>yu-ai-code-ai</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

修改配置文件,包含应用信息、Session 配置、数据库配置、Redis 配置、AI 模型配置等:

bash 复制代码
spring:
  application:
    name: yu-ai-code-app
  # session
  session:
    store-type: redis
    # 30 days expire
    timeout: 2592000
  # mysql
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/yu_ai_code_mother
    username: root
    password: 123456
  # redis
  data:
    redis:
      host: localhost
      port: 6379
      database: 0
      password:
      ttl: 3600
server:
  port: 8125
  servlet:
    context-path: /api
    # 30 days expire
    session:
      cookie:
        max-age: 2592000
# AI
langchain4j:
  open-ai:
    chat-model:
      base-url: https://api.deepseek.com
      api-key: <Your API Key>
      model-name: deepseek-chat
      max-tokens: 8192
      log-requests: true
      log-responses: true
    streaming-chat-model:
      base-url: https://api.deepseek.com
      api-key: <Your API Key>
      model-name: deepseek-chat
      max-tokens: 8192
      log-requests: true
      log-responses: true
    # 推理 AI 模型配置(用于复杂的推理任务)
    reasoning-streaming-chat-model:
      base-url: https://api.deepseek.com
      api-key: <Your API Key>
      model-name: deepseek-reasoner
      max-tokens: 32768
      temperature: 0.1
      log-requests: true
      log-responses: true
    # 智能路由 AI 模型配置
    routing-chat-model:
      base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api-key: <Your API Key>
      model-name: qwen-turbo
      max-tokens: 100
      log-requests: true
      log-responses: true
# springdoc-openapi
springdoc:
  group-configs:
    - group: 'default'
      packages-to-scan: com.yupi.yuaicodemother.controller
# knife4j
knife4j:
  enable: true
  setting:
    language: zh_cn

接下来迁移代码:

  • ai/ AI 服务工厂类(AiCodeGeneratorServiceFactory.java、AiCodeGenTypeRoutingServiceFactory.java)

  • config/ 缓存配置(RedisCacheManagerConfig.java)

  • controller/ 控制器层(AppController.java、ChatHistoryController.java、StaticResourceController.java)

  • core/ 代码生成核心代码

  • mapper/ 数据访问层(AppMapper.java、ChatHistoryMapper.java)

  • ratelimit/ 限流模块

  • service/ 业务服务层(AppService.java、ChatHistoryService.java、ProjectDownloadService.java 及其实现类)

  • resources/mapper/ MyBatis 映射文件

新建主启动类:

java 复制代码
@SpringBootApplication(exclude = {RedisEmbeddingStoreAutoConfiguration.class})
@MapperScan("com.yupi.yuaicodemother.mapper")
@EnableCaching
public class YuAiCodeAppApplication {
    public static void main(String[] args) {
        SpringApplication.run(YuAiCodeAppApplication.class, args);
    }
}

修改 AppServiceImpl 的代码,移除可观测性配置:

java 复制代码
// 5. 通过校验后,添加用户消息到对话历史
chatHistoryService.addChatMessage(appId, message, ChatHistoryMessageTypeEnum.USER.getValue(), loginUser.getId());
// 6. 调用 AI 生成代码(流式)
Flux<String> codeStream = aiCodeGeneratorFacade.generateAndSaveCodeStream(message, codeGenTypeEnum, appId);
// 7. 收集 AI 响应内容并在完成后记录到对话历史
return streamHandlerExecutor.doExecute(codeStream, chatHistoryService, appId, loginUser, codeGenTypeEnum);

重点来了,在代码迁移过程中,需要修改调用了其他服务的代码(比如截图服务)。可以先使用 @Lazy 注解代替实际引入,后续会通过 Dubbo 进行服务调用:

java 复制代码
@Resource
@Lazy
private InnerScreenshotService screenshotService;

类似的,修改所有调用了 UserService 的代码,替换为调用 InnerUserService 的实例方法或静态方法:

java 复制代码
@Resource
@Lazy
private InnerUserService userService;

// 调用实例方法
User user = userService.getById(userId);
UserVO userVO = userService.getUserVO(user);

// 调用静态方法
User loginUser = InnerUserService.getLoginUser(request);

总结

  1. 模块边界要清晰,按功能职责拆分
  • 严格区分通用层(common/model/client)和业务层(user/app/ai/screenshot):通用层只放全模块共享的工具、实体、接口,不包含业务逻辑;业务层按 "用户管理、应用功能、AI 能力、截图服务" 等单一职责拆分,避免模块功能混杂。
  1. 依赖管理要统一且兼容
  • 父项目通过dependencyManagement统一管理 Spring Boot、Spring Cloud Alibaba 等核心依赖的版本,避免子模块版本冲突;

  • 子模块只引入自身需要的依赖(比如 AI 模块只加 LangChain4j,截图模块只加 Selenium),轻量依赖减少冗余。

  1. 跨服务调用的提前规划
  • client模块定义内部调用接口(InnerService),统一跨服务交互契约;

  • 避免跨服务传递复杂对象(比如HttpServletRequest),通过静态方法 + 本地处理(如getLoginUser)或简化参数的方式规避序列化问题。

  1. 配置与启动的兼容性
  • 通用模块中涉及第三方服务(如 COS)的配置,通过@ConditionalOnProperty/@ConditionalOnBean做条件加载,避免无配置时模块启动失败;

  • 共享资源(如 Session)通过 Redis 统一存储,确保微服务间状态一致。

  1. 代码迁移的成本控制
  • 迁移时保持包路径与原单体项目一致,减少代码修改量;

  • 拆分时剥离非核心逻辑(如可观测性),优先保证基础功能正常运行,后续再扩展。

  1. 服务调用的过渡处理
  • 跨服务依赖先通过@Lazy注解占位,避免模块启动时的循环依赖;后续再通过 Dubbo 实现实际的远程调用,分步完成拆分落地。
相关推荐
guslegend2 小时前
SpringBoot 全局异常处理
spring boot
独断万古他化2 小时前
【二分算法 深度解析】二段性思维与经典题型全通关
java·算法
摇滚侠2 小时前
尚硅谷 Nginx 教程(亿级流量 Nginx 架构设计),基本使用,笔记 6-42
java·笔记·nginx
SenChien2 小时前
Java大模型应用开发day06-天机ai-学习笔记
java·spring boot·笔记·学习·大模型应用开发·springai
早川9192 小时前
9种常用排序算法总结
数据结构·算法·排序算法
卷毛迷你猪2 小时前
小肥柴慢慢手写数据结构(C篇)(2.1.1 动态数组(ArrayList))
c语言·数据结构
小北方城市网2 小时前
SpringBoot 安全认证实战(Spring Security + JWT):打造无状态安全接口体系
数据库·spring boot·后端·安全·spring·mybatis·restful
大只鹅2 小时前
Stream使用
java·开发语言
青衫码上行2 小时前
maven依赖管理和生命周期
java·学习·maven