解压缩到一个单独的应用程序中jar

在开发过程中,系统中的一部分很少更改,需要大量资源,需要运行大量的先决条件,并且/或需要大量的时间来部署。你通常做什么?将该部分解压缩到一个单独的应用程序中,并在一个或多个服务器上运行它,让开发人员连接到这些服务。例如:搜索引擎界面,CPU密集型操作,第三方系统等。在某种程度上,你使你的系统模块化。

然而,这种模块化有一个缺点-它增加了复杂性和故障点。如果模块之间的通信中断,如何跟踪和调试问题(它们可能由两个(或多个)系统中的任何一个系统引起),如何保持应用程序公共接口的多个版本保持同步(是否需要多个版本来共存?),如何在一个原子步骤中处理两个应用程序,等等。

很多时候,您不需要引入这种复杂性,但是在开发过程中仍然需要灵活性。

我有时使用的一个选项是将子系统作为嵌入式模块和独立模块。为此,您创建了一个瘦层(从体系结构的角度将其称为层;就代码而言,它将由2个类和一个接口组成),它处理两种类型的部署。有一个接口定义了由模块执行的操作,还有两个实现--一个是RESTful,调用单独部署的应用程序的RESTful服务,一个是简单地包装执行实际操作的类(甚至是类本身),然后出现在应用程序的类路径上。

举个例子。你有一个ExtractionService它必须对输入执行一些文本分析和概念提取。这需要大量的CPU和内存,因此每个开发人员都不可能在本地运行它。你做

  • ExtractionService接口,定义提取操作。
  • RestfulExtractionService,它使用端点URL进行配置,并调用RESTful服务来包装实际的提取实现。
  • ClasspathExtractionService它要么是实际的提取实现,要么是它的简单包装器。

您是如何根据项目及其依赖关系来组织这些项目的?您有一个单独的带有提取实现的JAR打包项目,您有一个WAR打包的项目(依赖于JAR),它将实现封装在RESTfulAPI中,并且您的主应用程序也依赖于JAR。

您如何切换实现,取决于您是在开发还是在生产模式中?这取决于您使用的框架。有了春天,你就可以拥有@Resource("${service.implementation}") ExtractionService service,在哪里service.impl

为什么您需要这个,难道它不增加开销吗?为什么我不将模块化保留在生产中呢?您将能够构建并部署一个单块应用程序(只有类路径依赖项)到生产中,而不必担心第2段中提到的问题。你保持你的架构简单,这是一件好事。同时,您在开发过程中获得了很大的灵活性,不必重新部署系统的重要部分。所有这些都是通过几个类和一个配置开关来实现的。当然,它并不总是适用的,但如果您面临类似的情况,请考虑将其作为一种选择

我们需要使多个基于网络的项目具有许多共享功能。对此,某种插件系统将是一个很好的选择(作为复制粘贴材料的替代)。有些框架(如grails)可以选择制作web插件,但大多数框架没有,所以需要实现一些定制的插件。

首先,让我们定义所需的功能。“插件”:

https://movie.douban.com/doulist/146770780/

  • 应该简单地通过maven/ivy导入
  • 如果使用依赖项注入容器,则应将所有类(自动或通过单行配置)注册到依赖项注入容器中。
  • 应该是垂直的,即包含所有文件,从javascript、css和模板到控制器,再到服务层类。
  • 不应要求从一个项目复制粘贴到另一个项目的复杂配置。
  • 应该允许轻松开发和调试而不需要重新部署。

Java类被放入JAR文件并添加到lib目录中,因此添加到类路径中,因此这是简单的部分。但是我们需要将Web资源提取到相应的位置,在那里它们可以被代码的其他部分使用。有三种常用的方法:构建时提取、运行时提取和从类路径加载运行时。

最后一种方法需要一个控制器(或servlet),它从类路径(相应的JAR)加载资源,缓存它们,并为它们服务。这有几个明显的缺点,其中之一是在一个罐子中,它们在开发过程中很难被替换。使用类路径资源也很棘手,因为您事先不知道文件的名称。

另外两种方法非常相似。例如,grails使用构建时提取插件是一个zip文件,包含所有所需的资源,并在构建项目时将它们提取到相应的位置。这很好,但是它需要更多的配置(在我们的例子中是Maven),这也可能需要从一个项目复制到另一个项目。

https://movie.douban.com/subject/26943069/discussion/637088305/

所以我们选择了运行时提取方法。它在启动时发生--当加载应用程序时,某种类型的启动监听器(在本例中是带有@PostConstruct的Spring组件)遍历lib文件夹中的所有JAR文件,并从特定文件夹(例如“web”)中提取文件。因此,JAR文件的结构如下所示:

com
   company
      pkg
         Foo.class
         Bar.class
web
   plugin-name
       css
           main.css
       js
          foo.js
          bar.js
       images
          logo.png
       views
          foo.jsp
          bar.jsp

最终的结果是,在应用程序启动后,您可以从应用程序获得所有所需的Web资源,因此可以将它们包含在主应用程序的页面(视图)中。

进行提取的代码相当简单(zip部分使用zip4j)。这可以是servlet上下文侦听器,而不是Springbean--没有任何区别。

https://www.imdb.com/list/ls558003370/

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
 * Component that locates modules (in the form of jar files) and extracts their web elements, if any, on startup
 *
 * @author Bozhidar
 */
@Component
public class ModuleExtractor {
     
    private static final Logger logger = LoggerFactory.getLogger(ModuleExtractor.class);
     
    @Inject
    private ServletContext ctx;
     
    @SuppressWarnings("unchecked")
    @PostConstruct
    public void init() {
        File lib = new File(ctx.getRealPath("/WEB-INF/lib"));
        File[] jars = lib.listFiles();
        String targetPath = ctx.getRealPath("/");
        String viewPath = "/WEB-INF/views"; //that can be made configurable
        for (File jar : jars) {
            try {
                ZipFile file = new ZipFile(jar);
                for (FileHeader header : (List<FileHeader>) file.getFileHeaders()) {
                    if (header.getFileName().startsWith("web/") && !fileExists(header)) {
                        // extract views in WEB-INF (inaccessible to the outside world)
                        // all other files are extracted in the root of the application
                        if (header.getFileName().contains("/views/")) {
                            file.extractFile(header, targetPath + viewPath);
                        } else {
                            file.extractFile(header, targetPath);
                        }
                    }
                }
            } catch (ZipException ex) {
                logger.warn("Error opening jar file and looking for a web-module in: " + jar, ex);
            }
        }
    }
 
    private boolean fileExists(FileHeader header) {
        return new File(ctx.getRealPath(header.getFileName())).exists();
    }
}

因此,为了制作一个插件,您只需使用JAR打包生成一个Maven项目,并将其作为依赖项添加到您的主项目中,其他的一切都会得到处理。您可能需要注册ModuleExtractor如果没有启用类路径扫描bean(或者您选择让它成为侦听器),那么就这样了。

注意:这个解决方案的目标不是要成为一个解决所有问题的功能齐全的插件系统。它不支持版本控制、子模块等等。这就是为什么标题是“简单”的原因。但是你可以用它做很多事情,而且它的复杂度很低。

注2:Servlet3.0有一种本机它不允许动态更改资产。如果您不需要更改它们,也不需要保存和刷新,那么这可能是更好的选择。

posted @ 2021-11-18 01:04  javd9w  阅读(92)  评论(0)    收藏  举报