序言

闲来无事,记录学习历程,交个朋友。欢迎讨论碰撞,产生好的解决方案!!!👏👏👏

背景

笔者曾经有过一个项目,需要一套代码部署两种业态(公司内部环境、多租户外部环境),他们的鉴权方式不一样提出的解决方案就是,java SPI + maven profile指定打包(不同环境不同的实现包)

什么是 SPI?

SPI 的全称是 Service Provider Interface,是 Java 提供的一套用于服务发现的机制。它解耦了服务的接口定义和具体实现,使得框架或核心库能够动态地加载和替换实现类。

核心思想: 面向接口编程,但具体的实现类不在主程序代码中硬编码,而是通过配置文件声明,由服务加载器动态加载。

SPI 的核心原理

SPI 机制主要涉及三个核心组件:

1、Service Interface(服务接口): 一个标准的 Java 接口,定义了服务的行为。

2、Service Provider(服务提供者): 服务接口的一个或多个具体实现类。

3、ServiceLoader(服务加载器): Java 核心库 (java.util.ServiceLoader) 中的类,它负责在运行时扫描 META-INF/services 目录下的配置文件,并加载和实例化配置文件中指定的实现类。

工作流程:

1、在项目的 META-INF/services/ 目录下,创建一个以服务接口全限定名命名的文件。

2、在该文件中,写入服务实现类的全限定名,每行一个。

3、在主程序中,通过 ServiceLoader.load(Class service) 方法加载所有配置的实现类。

4、ServiceLoader 通过迭代器模式,返回所有已配置的实现类的实例。

工程代码实例

我将用一个简单的demo,权限校验的demo来演示java spi 和 maven profile合作使用。

spi-demo(父工程)

定义一下子工程
在这里插入图片描述

<?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.gyg.iot</groupId>
    <artifactId>spi-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>biz-service</module>
        <module>auth</module>
        <module>auth-sso</module>
        <module>auth-iam</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>
    </properties>

</project>

基础权限工程 (auth)

定义了鉴权接口,作为业务服务的基础框架
在这里插入图片描述

package com.gyg.foundation;

/**
 * 认证接口 - 处理用户身份认证相关功能
 */
public interface Auth {
    
    /**
     * 用户登录认证
     * @param username 用户名
     * @param password 密码
     * @return 认证结果,包含token等信息
     */
    AuthResult authenticate(String username, String password);

}
package com.gyg.foundation;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 认证结果数据传输对象
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AuthResult {
    private UserInfo userInfo;
    private String authType;
}
package com.gyg.foundation;

import lombok.Data;

import java.util.List;

/**
 * 用户信息数据传输对象
 */
@Data
public class UserInfo {
    private Long id;
    private String username;
    private String email;
    private List<String> roles;
}
<?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>
    <parent>
        <groupId>com.gyg.iot</groupId>
        <artifactId>spi-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>auth</artifactId>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.42</version>
        </dependency>
    </dependencies>

</project>

iam权限服务

实现权限接口,定义SPI的接口实现类
在这里插入图片描述

package com.gyg.iam;

import com.gyg.foundation.Auth;
import com.gyg.foundation.AuthResult;
import com.gyg.foundation.UserInfo;

import java.util.List;

public class IamAuth implements Auth {
    @Override
    public AuthResult authenticate(String username, String password) {
        // mock 一个对象
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1L);
        userInfo.setUsername("gyg");
        userInfo.setEmail("<EMAIL>");
        userInfo.setRoles(List.of("admin"));
        return new AuthResult(userInfo, "iam");
    }
}

<?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>
    <parent>
        <groupId>com.gyg.iot</groupId>
        <artifactId>spi-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>auth-iam</artifactId>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.gyg.iot</groupId>
            <artifactId>auth</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

SSO权限校验

实现SSO权限接口,定义SPI接口实现
在这里插入图片描述

package com.gyg.sso;

import com.gyg.foundation.Auth;
import com.gyg.foundation.AuthResult;
import com.gyg.foundation.UserInfo;

import java.util.List;

public class SsoAuth implements Auth {
    @Override
    public AuthResult authenticate(String username, String password) {
        // mock 一个对象
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1L);
        userInfo.setUsername("gyg");
        userInfo.setEmail("<EMAIL>");
        userInfo.setRoles(List.of("admin"));
        return new AuthResult(userInfo, "sso");
    }
}

<?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>
    <parent>
        <groupId>com.gyg.iot</groupId>
        <artifactId>spi-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>auth-sso</artifactId>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.gyg.iot</groupId>
            <artifactId>auth</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

业务代码

调用SPI
在这里插入图片描述

package com.gyg;

import com.gyg.foundation.Auth;
import com.gyg.foundation.AuthResult;

import java.util.ServiceLoader;

public class AuthTest {

    public static void main(String[] args) {
        // 1. 使用 ServiceLoader 加载所有 Connection 接口的实现
        ServiceLoader<Auth> loader = ServiceLoader.load(Auth.class);
        for (Auth auth : loader) {
            // 2. 获取认证结果
            AuthResult authResult = auth.authenticate("gyg", "123456");
            System.out.println(authResult);
        }
    }
}

<?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>
    <parent>
        <groupId>com.gyg.iot</groupId>
        <artifactId>spi-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>biz-service</artifactId>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.gyg.iot</groupId>
            <artifactId>auth</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <profiles>
        <profile>
            <id>iam</id>
            <dependencies>
                <dependency>
                    <groupId>com.gyg.iot</groupId>
                    <artifactId>auth-iam</artifactId>
                    <version>1.0-SNAPSHOT</version>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <id>sso</id>
            <dependencies>
                <dependency>
                    <groupId>com.gyg.iot</groupId>
                    <artifactId>auth-sso</artifactId>
                    <version>1.0-SNAPSHOT</version>
                </dependency>
            </dependencies>
        </profile>
    </profiles>

</project>

代码测试

勾选profiles勾选IAM,就用了IAM的包了(记得刷新maven)
在这里插入图片描述
勾选profiles勾选SSO,就用了SSO的包了(记得刷新maven)
在这里插入图片描述

尾声

如果解决有解决你的问题,点点赞,求个关注👍👍👍
山高万仞,只登一步🤞🤞🤞,加油👏👏👏

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐