3

我有一个使用适当的 java.text.spi.DateFormatProvider 文件将 DateFormatProvider 的实现打包在一个 jar 中,但是不会使用 DateFormatProvider(实际上LocaleServiceProviderPool.getPool(DateFormatProvider.class).hasEntries()在下面的测试程序中是错误的。

DateFormatProvider 的实现:

package dateformatproviders;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.spi.DateFormatProvider;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;

public class DateFormatProviderImpl extends DateFormatProvider
{
  private static final CompareLocales COMPARE_LOCALES = new CompareLocales();
  private Locale[] availableLocales;
  private static final String VARIANT = "variant_xxx";
  private final String formatString;
  private final Object availableLocalesLock = new Object();

  /**
   * for each available locale create a variant using the given variantString.
   * Return the list of all these locales
   *
   * @param variantString must not be null
   * @return never null nor empty
   */
  public static Locale[] installVariant(String variantString)
  {
    Locale[] availableLocales = Locale.getAvailableLocales();
    Locale[] result = new Locale[availableLocales.length];
    int pos = 0;
    for (Locale locale : availableLocales)
    {
      Locale newLocale = createLocale(locale, variantString);
      result[pos] = newLocale;
      ++pos;
    }
    return result;
  }

  public static Locale createLocale(Locale originalDefaultLocale, String variant)
  {
    Locale modifiedDefaultLocale = new Locale(originalDefaultLocale.getLanguage(), originalDefaultLocale.getCountry(), variant);
    return modifiedDefaultLocale;
  }

  public DateFormatProviderImpl()
  {
    formatString = "HH:mm:ss 't' MM/dd/yy";
  }

  private DateFormat createDateFormat()
  {
    return new SimpleDateFormat(formatString);
  }

  @Override
  public final DateFormat getTimeInstance(int style, Locale locale)
  {
    checkArguments(locale, style);
    return createDateFormat();
  }

  @Override
  public final DateFormat getDateInstance(int style, Locale locale)
  {
    checkArguments(locale, style);
    return createDateFormat();
  }

  @Override
  public final DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale)
  {
    checkArguments(locale, dateStyle, timeStyle);
    return createDateFormat();
  }

  @Override
  public final Locale[] getAvailableLocales()
  {
    synchronized (availableLocalesLock)
    {
      if (null == availableLocales)
      {
        Locale[] sourceLocales = installVariant(VARIANT);
        availableLocales = Arrays.copyOf(sourceLocales, sourceLocales.length);
        Arrays.sort(availableLocales, COMPARE_LOCALES);
      }
    }
    return availableLocales;
  }

  public final String getVariant()
  {
    return VARIANT;
  }

  private void checkArguments(Locale locale, int... styles)
  {
    for (int style : styles)
    {
      switch (style)
      {
        case DateFormat.SHORT:
        case DateFormat.MEDIUM:

        case DateFormat.LONG:
        case DateFormat.FULL:
          break;
        default:
          throw new IllegalArgumentException("style:" + style + " must be one of{" + DateFormat.SHORT + ',' + DateFormat.MEDIUM + ','
                  + DateFormat.LONG + ',' + DateFormat.FULL + '}');
      }
    }
    checkLocale(locale);
  }

  private void checkLocale(Locale locale)
  {
    if (null == locale)
    {
      throw new IllegalArgumentException("locale must not be null");
    }
    if (Arrays.binarySearch(availableLocales, locale, COMPARE_LOCALES) < 0)
    {
      throw new IllegalArgumentException("locale not supported:" + locale);
    }
  }

  private static class CompareLocales implements Comparator<Locale>
  {
    @Override
    public int compare(Locale lhs, Locale rhs)
    {
      if (null == lhs)
      {
        if (null == rhs)
        {
          return 0;
        }
        return -1;
      }
      if (null == rhs)
      {
        return 1;
      }
      String lhsLang = lhs.getLanguage();
      String rhsLang = rhs.getLanguage();
      int langCompare = lhsLang.compareTo(rhsLang);
      if (0 == langCompare)
      {
        String lhsCountry = lhs.getCountry();
        String rhsCountry = rhs.getCountry();
        int countryCompare = lhsCountry.compareTo(rhsCountry);
        if (0 == countryCompare)
        {
          String lhsVariant = lhs.getVariant();
          String rhsVariant = rhs.getVariant();
          return lhsVariant.compareTo(rhsVariant);
        }
        return countryCompare;
      }
      return langCompare;
    }
  }
}

META-INF/services/java.text.spi.DateFormatProvider:

dateformatproviders.DateFormatProviderImpl

测试程序:

package datetest;

import dateformatproviders.DateFormatProviderImpl;
import java.text.DateFormat;
import java.text.spi.DateFormatProvider;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import sun.util.LocaleServiceProviderPool;

public class Main
{
  /**
   * install the given dateFormatProvider for the default locale, using the default variant. <br/>
   * Changes the default locale
   *
   * @param dateFormatProvider
   */
  public static void install(DateFormatProviderImpl dateFormatProvider)
  {
    Locale originalDefaultLocale = Locale.getDefault();
    Locale modifiedDefaultLocale = DateFormatProviderImpl.createLocale(originalDefaultLocale, dateFormatProvider.getVariant());
    Locale.setDefault(modifiedDefaultLocale);
  }

  public static void main(String[] args) throws InterruptedException
  {
    install(new DateFormatProviderImpl());
    LocaleServiceProviderPool pool = LocaleServiceProviderPool.getPool(DateFormatProvider.class);
    boolean hasEntries = pool.hasProviders(); // is false
    while (true)
    {
      System.out.printf("%s\n", DateFormat.getDateTimeInstance().format(new Date()));
      TimeUnit.SECONDS.sleep(2);
    }

  }
}

在包含 DateTest.jar 的目录和包含 DateFormatProvider.jar 的文件夹 lib 中执行:

java -cp DateTest.jar:lib/DateFormatProvider.jar dateTest.Main

产生默认的格式化日期

当包含 DateFormatProvider 及其 java.text.spi.DateFormatProvider 文件的 jar 文件被复制到 jre/lib/ext 时,测试程序仍然可以工作。

4

1 回答 1

1

根据关于可选包的官方文档(这是 JVM 扩展的新名称),有两种方法可以将 JAR 文件用作可选包:

  • 通过放置在 Java 2 运行时环境或 JDK 目录结构中的特殊位置 - 在这种情况下,它是一个已安装的可选包

  • 通过从 applet 或应用程序的 JAR 文件的清单中以指定方式引用 - 在这种情况下,它是一个下载可选包

仅将 JAR 放在类路径上不足以将其作为可选包加载。但是,当您将 JAR 文件放在jre/lib/ext您使用第一种方式的目录中时。

该文档还包含此注释:

安装和下载可选包之间的另一个区别是,只有捆绑在 JAR 文件中的小程序和应用程序才能使用下载可选包。未捆绑在 JAR 文件中的 Applet 和应用程序没有可供参考下载可选包的清单。

另请注意,已弃用已安装的可选软件包:

已弃用:已弃用对已安装可选包的支持,并且可能会在将来的版本中删除。

于 2016-01-08T15:13:04.517 回答