创建和导入自定义 Spring 库,同时分离共享依赖项
Creating and importing a custom Spring library whilst separating shared dependencies
我希望创建一个 Spring 库项目以在内部团队中共享。
在非常基本的概念层面上,库会将消息事件发送到队列,我的计划是在一个团队中跨多个 Spring 将此标准化,引导微服务以相同的方式发送消息。
我在库项目中的 pom 看起来像这样
<artifactId>my-library</artifactId>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
etc...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.16.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.2.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
我在图书馆项目中有一个看起来像这样的服务
public class EventService {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public void sendAuditEvent(AuditMessage auditMessage){
Set<ConstraintViolation<AuditMessage>> violations = validator.validate(auditMessage);
if(!isEmpty(violations)){
log.error("Unable to send audit message");
violations.stream().forEach( v-> log.error(v.getMessage()));
}
log.info("Found {} violations", violations.size());
// etc blah blah
return;
}
}
当我将库导入另一个项目时,我的想法是我可以自动装配 EventService。通过将它添加到 pom 然后
@ComponentScan({"my.library.package.eventlibrary.service"})
如何防止 spring 版本锁定?如果库今天使用 spring 2.1.5.RELEASE
而导入库的项目使用不同的版本,我不会以潜在的 maven 冲突结束吗?
也可以说导入库的项目使用较低版本的休眠 api 并且库有 6.0.16.Final
。我如何防止项目使用在库类路径中找到的较新的项目?
为了进一步澄清我的问题,有没有一种方法可以将库中的依赖项与使用它的项目分开。
Pre Java 9. 您可以在声明对模块的依赖项时使用 maven 排除 spring 依赖项,Hibernate 也是如此。但是你不能告诉你的模块在 WAR 中使用不同的休眠版本。
如果你想解决这个问题,你可以将你的库开发为独立的微服务,以 REST 或 Websocket 的形式公开接口,如果你想要全双工通信或其他 JMS 等等......
Post Java 9 您可以使用 java 模块化来为您的 jar 模块定义确切的依赖关系。检查拼图项目 https://www.baeldung.com/project-jigsaw-java-modularity。
在您的情况下,为了拥有同一库的不同版本(休眠)。您将需要两个单独的 class 装载机。为此,您需要使用此处阅读的分层 http://openjdk.java.net/projects/jigsaw/spec/sotms/#layers
这里是许多示例的源代码,包括使用图层的示例。关注他们:https://github.com/accso/java9-jigsaw-examples/tree/master/jigsaw-examples
您可以尝试排除您的库可以带给将使用它的项目的所有传递依赖项。
为此,您应该将 dependencyManagement 部分中的 spring-boot-starter-parent
替换为 spring-boot-dependencies
,并对库需要的所有依赖项使用 provided
范围与项目一起工作并且将被项目完全使用的,将与库一起工作。
例如,您图书馆的 pom.xml 可以是这样的:
<!-- ... -->
<groupId>com.example</groupId>
<artifactId>library</artifactId>
<version>0.1.0</version>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<spring-boot.version>2.1.5.RELEASE</spring-boot.version>
</properties>
<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>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- ... -->
然后您将能够在不同的项目中使用您的库,例如使用旧的 Spring Boot:
<!-- ... -->
<groupId>com.example</groupId>
<artifactId>old-project</artifactId>
<version>0.13.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.19.RELEASE</version>
<relativePath/>
</parent>
<!-- ... -->
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>library</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!-- ... -->
因此该项目将使用其 spring-boot-starter-web
中的 hibernate-validator:5.3.6.Final
。
重要说明 - 您的库代码应该 'compatible' 与此版本的 Spring Boot.换句话说,您应该使用您感兴趣的 Spring Boot 的不同版本来测试您的库。
以我的project为例。
可能不是您想要的,但您可以将您的库作为 spring-boot-starter auto configuration 模块分发(当然,如果客户端是 spring 启动应用程序)。
通过这种方式,您可以灵活地控制依赖项,并让您的客户更自由地使用该库。
在您的特定情况下,如果您需要向队列发送消息,您肯定需要在类路径中有相应的 类。使用自动配置,您可以根据 Class Conditions or Been Conditions 跟踪您的客户端在运行时是否具有正确的配置。如果出现问题(提供有意义的错误消息),您也可以使上下文加载失败。
Spring 还提供了跟踪机制,用于跟踪如果缺少特定 class/library 可能发生的情况。
我希望创建一个 Spring 库项目以在内部团队中共享。
在非常基本的概念层面上,库会将消息事件发送到队列,我的计划是在一个团队中跨多个 Spring 将此标准化,引导微服务以相同的方式发送消息。
我在库项目中的 pom 看起来像这样
<artifactId>my-library</artifactId>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
etc...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.16.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.2.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
我在图书馆项目中有一个看起来像这样的服务
public class EventService {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public void sendAuditEvent(AuditMessage auditMessage){
Set<ConstraintViolation<AuditMessage>> violations = validator.validate(auditMessage);
if(!isEmpty(violations)){
log.error("Unable to send audit message");
violations.stream().forEach( v-> log.error(v.getMessage()));
}
log.info("Found {} violations", violations.size());
// etc blah blah
return;
}
}
当我将库导入另一个项目时,我的想法是我可以自动装配 EventService。通过将它添加到 pom 然后
@ComponentScan({"my.library.package.eventlibrary.service"})
如何防止 spring 版本锁定?如果库今天使用 spring 2.1.5.RELEASE
而导入库的项目使用不同的版本,我不会以潜在的 maven 冲突结束吗?
也可以说导入库的项目使用较低版本的休眠 api 并且库有 6.0.16.Final
。我如何防止项目使用在库类路径中找到的较新的项目?
为了进一步澄清我的问题,有没有一种方法可以将库中的依赖项与使用它的项目分开。
Pre Java 9. 您可以在声明对模块的依赖项时使用 maven 排除 spring 依赖项,Hibernate 也是如此。但是你不能告诉你的模块在 WAR 中使用不同的休眠版本。
如果你想解决这个问题,你可以将你的库开发为独立的微服务,以 REST 或 Websocket 的形式公开接口,如果你想要全双工通信或其他 JMS 等等......
Post Java 9 您可以使用 java 模块化来为您的 jar 模块定义确切的依赖关系。检查拼图项目 https://www.baeldung.com/project-jigsaw-java-modularity。
在您的情况下,为了拥有同一库的不同版本(休眠)。您将需要两个单独的 class 装载机。为此,您需要使用此处阅读的分层 http://openjdk.java.net/projects/jigsaw/spec/sotms/#layers
这里是许多示例的源代码,包括使用图层的示例。关注他们:https://github.com/accso/java9-jigsaw-examples/tree/master/jigsaw-examples
您可以尝试排除您的库可以带给将使用它的项目的所有传递依赖项。
为此,您应该将 dependencyManagement 部分中的 spring-boot-starter-parent
替换为 spring-boot-dependencies
,并对库需要的所有依赖项使用 provided
范围与项目一起工作并且将被项目完全使用的,将与库一起工作。
例如,您图书馆的 pom.xml 可以是这样的:
<!-- ... -->
<groupId>com.example</groupId>
<artifactId>library</artifactId>
<version>0.1.0</version>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<spring-boot.version>2.1.5.RELEASE</spring-boot.version>
</properties>
<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>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- ... -->
然后您将能够在不同的项目中使用您的库,例如使用旧的 Spring Boot:
<!-- ... -->
<groupId>com.example</groupId>
<artifactId>old-project</artifactId>
<version>0.13.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.19.RELEASE</version>
<relativePath/>
</parent>
<!-- ... -->
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>library</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!-- ... -->
因此该项目将使用其 spring-boot-starter-web
中的 hibernate-validator:5.3.6.Final
。
重要说明 - 您的库代码应该 'compatible' 与此版本的 Spring Boot.换句话说,您应该使用您感兴趣的 Spring Boot 的不同版本来测试您的库。
以我的project为例。
可能不是您想要的,但您可以将您的库作为 spring-boot-starter auto configuration 模块分发(当然,如果客户端是 spring 启动应用程序)。
通过这种方式,您可以灵活地控制依赖项,并让您的客户更自由地使用该库。
在您的特定情况下,如果您需要向队列发送消息,您肯定需要在类路径中有相应的 类。使用自动配置,您可以根据 Class Conditions or Been Conditions 跟踪您的客户端在运行时是否具有正确的配置。如果出现问题(提供有意义的错误消息),您也可以使上下文加载失败。
Spring 还提供了跟踪机制,用于跟踪如果缺少特定 class/library 可能发生的情况。