【java spi原理】搭配maven profile使用
序言
闲来无事,记录学习历程,交个朋友。欢迎讨论碰撞,产生好的解决方案!!!👏👏👏
背景
笔者曾经有过一个项目,需要一套代码部署两种业态(公司内部环境、多租户外部环境),他们的鉴权方式不一样提出的解决方案就是,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)

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

所有评论(0)