【问题标题】:Best approach storing and accessing Java application data存储和访问 Java 应用程序数据的最佳方法
【发布时间】:2016-09-18 16:18:39
【问题描述】:

我正在进行一个大规模的重构项目,代码有一个 5000 行的主类,它被注入到所有内容中,存储了所有内容并拥有所有公共代码。

我不是分析和设计方面的专家,但我已尽我所能将事物分离出来,我大约 80% 通过重构依赖于主类的类来使用我拥有的新类创建的。

有一些类型的数据在应用程序启动时被初始化,并且在应用程序的整个生命周期中几乎所有东西都可以访问它们。例如,有一个包含数百个参数的 Config 类。

我采用的方法是创建几个单例,其中最重要的两个是 GUIData 和 ClientData。 GUIData 包含对应用程序主机的引用,clientdata 维护对配置和其他类似类的引用。

这允许我从代码中的任何位置调用 ClientData.getInstance().getConfig().getParam("param") 但我不认为这是最好的方法。

我考虑了单独的静态类,而不是这些包含类实例但某些类确实需要构造函数的数据单例。

我已经在谷歌上搜索了一个星期,试图找到更好的方法来做到这一点,但不知何故,我总是在讨论数据库缓存的线程上结束

【问题讨论】:

  • java.util.Properties怎么样?
  • 数据存储在属性中并在启动时读取,问题是有几个线程和大量的东西要解析,还有许多方便的方法和通过 clientdata 访问的大约 10 个不同的类。如果您认为它仍然是一个很好的用例,请举例回答,我会尝试一下
  • 这听起来有点像您想使用一个注入框架,创建 bean(也许将它们分离成您需要的),然后简单地将这些 bean 注入您的应用程序类。有了这个,你甚至可以直接注入属性,所以你可以这样做而不是 ClientData.getInstance().getConfig().getParam("param"): @Inject @Named("param") String param;并且您将在该字段中获得可用的数据。
  • 老实说,这个问题有点笼统,每个人都只是建议自己喜欢的依赖注入框架,这是非常现实的风险。我建议阅读 Spring Framework 和 Java EE(两者都是有效选项)。在不了解您的项目架构和具体目标的情况下,我担心您不会得到比这更好的答案。

标签: java shared-data


【解决方案1】:

不可变(配置)实例提供“线程安全的应用程序范围的数据访问”。 Typesafe 的config(正如Brian Kent 的评论所建议的那样)正是这样做的。 请注意,这不涉及静态类或单例。静态类和单例现在可以满足您的目的, 但它们在未来可能会很麻烦。它们当然很方便,但请尽量限制它们的使用。

必须在读取和解析配置数据后进行初始化。它通常在应用程序启动时完成,在其他处理线程启动之前。初始化必须尽可能多地验证配置数据,以便快速失败并在配置数据不好时终止程序。

将大量配置数据捆绑在一起可能会创建“隐藏的通信线路”。例如。您更新一个值并且应用程序失败,因为它还需要更新其他值。将所有配置数据放在一个文件中并从那里加载它是非常好的,但是您的应用程序(具有数百个配置选项)应该将配置数据划分为应用程序不同部分使用的集合。这提高了隔离性,有助于单元测试,并且可以在将来更改应用程序而不会遇到太多令人讨厌的意外。

使用一组配置数据有两种方式:

  1. 从对象内调用单例Settings.getInstance().getConfigForThisModule()
  2. 通过构造函数或setConfig(ConfigForThisModule config)为每个使用配置数据的对象提供配置数据。

第一种方法取决于不调用Settings.getInstance().getConfigForACompletelyUnrelatedModule() 的约定,这可能是一个弱点。第二种方法更符合“依赖注入”,并且可能更适合未来。 您可以在重构时混合使用这两种方法,只要确保一致(例如,仅对应用程序所有部分中使用的配置数据使用单例方法)。

要进一步改进使用配置数据的设计,请牢记以下(可能的)未来功能要求:更新配置文件时,会重新加载配置数据并在应用程序中使用。大多数日志框架都设法支持此功能要求,而不会影响多线程应用程序的性能。除其他外,它需要您的应用程序的以下内容:

  • 如果新的配置数据不好,程序不会终止,而是会记录一个错误,旧的配置数据仍在使用中。您的初始化过程将需要处理“重新启动时加载”和“重新加载”两种情况。主要需要注意的是,您的初始化过程需要可重用,并且不应影响应用程序的其他(运行)部分(再次隔离)。
  • 长期存在的对象可能不会保留配置数据的本地副本或对ConfigForThisModule 实例的引用,而应定期调用Settings.getInstance()...(或其他可以返回更新实例的方法)。李>
  • 用新配置替换旧配置可能不会导致错误。从技术上讲,替换配置就像用Settings.getInstance()... 返回的新配置实例更新AtomicReference 一样简单。但这也是测试配置数据集隔离的地方:在一个模块中使用旧集并在另一个模块中同时使用新集应该没有问题。

配置数据可以看作是一种“全局状态”。考虑到这一点,在以下两个问题中讨论了关于做什么和避免什么的进一步设计点(部分公然复制到这个答案):

【讨论】:

    【解决方案2】:

    您可以使用 Java Properties 类 util,基本上它是一个 HashTable 参考:https://docs.oracle.com/javase/7/docs/api/java/util/Properties.html

    您创建一个文件 fileName.properties 并将您的数据存储在键值对中,例如:

    username=your name
    port=8080
    

    然后将其加载到 Properties 对象中并获取如下数据:

    Properties prop = new Properties();
    load the file...
    String userName = prop.getProperty("username")
    String port = prop.getProperty("port")// you can parse it to int if needed
    

    我的建议是为每种类型的配置创建一个属性文件,例如:

    1. clientData.properties
    2. appConfig.properties

    你可以按照这个简单的教程 http://www.mkyong.com/java/java-properties-file-examples/

    【讨论】:

      【解决方案3】:

      如果您只需要 String 属性值,您甚至不需要一个类 - 已经为您提供了一个全局工具:System.getProperties()

      您需要做的就是在启动时首先加载属性值:

      System.setProperty("myKey", "myValue"); // see below how load properties from a file
      

      然后在代码中的任何位置阅读它:

      String myValue = System.getProperty("myKey");
      

      String myValue = System.getProperty("myKey", "my desired default");
      

      如果您的容器不支持开箱即用的属性加载,请从如下所示的外部文件加载属性:

      key1=value
      key2=some other value
      etc...
      

      您可以使用此代码:

      Files.lines(Paths.get("path/to/file"))
        .filter(line -> !line.startsWith("#") || !line.contains("=")) // ignore comment/blank
        .map(line -> line.split("=", 2)) // split into key/value
        .forEach(split -> System.setProperty(split[0], split[1])); // load as property
      

      【讨论】:

        【解决方案4】:

        抱歉,问题有点含糊,您是要存储配置或程序其他部分使用的缓存对象吗?

        由于您有 100 多个参数,因此首先将配置拆分为可管理的块

        1) 将您的配置参数拆分为与简单属性文件 1:1 对应的逻辑块 - 这需要一些时间

        2) 这些属性文件必须外部化,以便您可以随时更改它们,确保您通过环境变量将基本位置传递给程序

        3) 编写一个实用程序类(单例),包装Apache commons configuration 来保存您的配置。 (从基本位置读取 *.properties 并将属性合并到一个配置对象中)这必须在启动任何线程之前完成。

        4) 使用 config.getXXXX() 方法参考代码中的配置参数

        Apache commons 配置还可以在文件系统上的属性文件更改时重新加载配置。

        完成后,使用像 Spring 或 Guice 这样的 DI 容器来缓存配置的对象。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-12-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-02-20
          相关资源
          最近更新 更多