resourcebundle和classloader

jgwigjjp  于 2021-07-03  发布在  Java
关注(0)|答案(2)|浏览(451)

我想构建resourcebundles,它只包含属性文件中键/值对的一部分,因此与在单个文件中存储这些部分或节相比,可以节省大量的文件。各节以“#”开头的标题行标记,并用空行分隔。下面代码后面的示例属性文件包含两个部分:

文件选择器

选项窗格

  我试图通过在getbundle(…)方法中传递一个定制的类加载器来实现这一点,该类加载器只读取所需的部分。customclassloader可以很好地减少键定义,但是resourcebundle仍然包含属性文件的所有键。

import java.io.*;
import java.util.*;

public class ResourceReader {

  public ResourceReader() {
    Locale locale= Locale.getDefault();
    ResourceBundle i18n= ResourceBundle.getBundle("ComponentBundle", locale,
                    new CustomClassLoader("#FileChooser"));
    Enumeration<String> enu= i18n.getKeys();
    System.out.println("Keys of ResourceBundle");
    printEnumeration(enu);
  }

  public static void main(String args[]) {
    new ResourceReader();
  }

  public void printEnumeration(Enumeration<String> enu) {
    int i= 1;
    while (enu.hasMoreElements()) {
      System.out.println(i+".: "+enu.nextElement());
      i++;
    }
  }

//////////////////////////////////////////////////////////////////////////////

  public class CustomClassLoader extends ClassLoader {
    String section;

    public CustomClassLoader(String section) {
      this.section= section;
    }

    @Override
    public Class findClass(String name) throws ClassNotFoundException {
      byte[] b = loadClassFromFile(name);
//System.out.writeBytes(b); // OK.
      return defineClass(name, b, 0, b.length);
    }

    private byte[] loadClassFromFile(String fileName)  {
      InputStream inputStream = getClass().getClassLoader().getResourceAsStream(
                      fileName.replace('.', File.separatorChar) + ".properties");
      byte[] buffer;
      ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
      int nextValue = 0;
      try {
        while ( (nextValue = inputStream.read()) != -1 ) {
          byteStream.write(nextValue);
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
      buffer = extractSection(byteStream.toString(), section);
      return buffer;
    }

    private byte[] extractSection(String stream, String caption)  {
      final String LINE_SEP= System.getProperty("line.separator", "\n");
      String[] lines= stream.split(LINE_SEP);
//    Detect first and last line (exclusive) of section.
      int iEnd= 0, iStart= -1;
      for (int i=0; i<lines.length; i++) {
        lines[i]= lines[i].trim();
        if (iStart==-1) {
          if (!lines[i].equals(caption)) continue;
          iStart= i+1;
          i++;
        }
        else if (lines[i].isEmpty()) {
          iEnd= i;
          break;
        }
      }
      if (iEnd==0) iEnd= lines.length+1;
      StringBuilder sb= new StringBuilder();
      for (int i=iStart; i<iEnd; i++)
        sb.append(lines[i]+LINE_SEP);
      return sb.toString().getBytes();
    }
  }

}
//////////////////////////////////////////////////////////////////////////////

/*//文件componentbundle.properties


# FileChooser

acceptAllFileFilterText= All files (*.*)
cancelButtonText= Cancel
cancelButtonToolTipText= Cancel

# OptionPane

Cancel= Cancel
Input= Input
Message= Message
No= No
// End of ComponentBundle.properties
  • /
jtoj6r0c

jtoj6r0c1#

如果您确实想使用resourcebundle的一个子集,这可能是一种方法——尽管不是很优雅,因为需要读取和过滤整个包。

int componentFlag= ...;
final int FILE_CHOOSER= 1, OPTION_PANE= 2;

ResourceBundle i18n= ResourceBundle.getBundle("ComponentBundle", locale);
String prefix;
Set<String> set= i18n.keySet();
if ((componentFlag&FILE_CHOOSER)>0)
  prefix= "FileChooser.";
else if ((componentFlag&OPTION_PANE)>0)
  prefix= "OptionPane.";
set.stream().filter(s -> s.startsWith(prefix))
    .forEach(s -> UIManager.put(s, i18n.getString(s)));
wgx48brx

wgx48brx2#

你不会得到你的限制资源包这样,因为你已经过度骑 findClass 以类的形式返回属性文件的字节。要了解发生了什么,请添加以下代码:

public URL getResource(String name)
{
    var url = super.getResource(name);
    System.out.println("getResource "+name+" -> "+url);
    return url;
}
public Class findClass(String name) throws ClassNotFoundException {
    System.out.println("findClass "+name);
...

然后你就可以看到 ResourceBundle 会查找所有键,因为它正在加载文件url的内容-并且您的代码未被使用:

findClass ComponentBundle
getResource ComponentBundle.properties -> file:/C:/some/path/to/ComponentBundle.properties
findClass ComponentBundle_en
getResource ComponentBundle_en.properties -> null
findClass ComponentBundle_en_GB
getResource ComponentBundle_en_GB.properties -> null

如果您重写,就有可能使捆绑程序正常工作 getResource(String name) 并使其生成一个适合于密钥子集的文件,将url传回子集文件。
当您可以为所有应用程序定义一个资源包文件,或者为每个子组件定义一个资源包文件时,似乎需要做很多工作。

相关问题