春季启动1.4.x和自定义CharsetProvider

问题描述:

我想注册一个自定义的CharsetProvider能够使用X-Gsm7Bit编码。我使用Logica的供应商https://github.com/OpenSmpp/opensmpp/tree/master/charset/src/main/java/org/smpp/charset。 要注册这个新的字符集提供商,我使用内容org.smpp.charset.Gsm7BitCharsetProviderMETA-INF/services/java.nio.charsets.spi.CharsetProvider文件。春季启动1.4.x和自定义CharsetProvider

我无法使它工作。 测试应用程序的来源是这里https://github.com/asmsoft/provider

我得到java.util.ServiceConfigurationError: java.nio.charset.spi.CharsetProvider: Provider org.smpp.charset.Gsm7BitCharsetProvider not found当我开始它作为脂肪罐子

mvn clean 
mvn package 
java -jar target/provider-1.0-SNAPSHOT.jar 

如果我mvn spring-boot:run启动它,我得到java.io.UnsupportedEncodingException: X-Gsm7Bit

而且一切正常时,我用我的IDE启动应用程序。

目前我解决了我的问题,如下所示:我已经把jar提供的自定义字符集转换为JAVA_HOME/jre/lib/ext,并且一切按预期工作,字符集正在注册。

我对这个解决方案不满意,我想请求您的帮助。

+0

嵌套jar从'lib'移动到'BOOT-INF/lib'应该没有什么区别,因为两者只对Boot的类加载器而不是JVM的app类加载器可见。您能否提供一个能够再现问题的小样本,并说明如何以及何时尝试使用自定义提供程序? –

+0

@AndyWilkinson,感谢您的回复,我已经更改了问题的描述并添加了一个链接到测试应用程序。 –

我想你在JDK中遇到了一个错误。该javadoc for CharsetProvider说:

字符集提供商通过当前线程的上下文类加载抬头

然而,code that looks up the providers讲述了一个不同的故事:

ClassLoader cl = ClassLoader.getSystemClassLoader(); 
ServiceLoader<CharsetProvider> sl = 
    ServiceLoader.load(CharsetProvider.class, cl); 

正如你所看到的,它使用系统类加载器而不是线程的上下文类加载器。

它可以在您的IDE中使用,因为您的应用程序的类及其依赖项都可用于系统类加载器。当你将应用程序打包到一个胖的jar包中时,它会失败,因为你的应用程序的类和它们的依赖关系可以被Spring Boot的类加载器使用,该加载器是系统类加载器的子类。在任何使用其他类加载器的环境中,您都会遇到同样的问题,例如传统的战争部署到servlet容器。

要避免此问题,您需要使提供程序可用于系统类加载器。正如你发现的那样,把它放在JAVA_HOME/jre/lib/ext是实现这一目标的一种方法。其他选项包括使用阴影的jar而不是fat jar,或者后处理fat jar以将提供者的类直接添加到jar的根目录,而不是嵌套的jar。

+0

感谢您的回复,是的,我知道关于类加载器的问题,我只是想知道是否有任何方法可以解决它适合使用特别是弹簧启动启动器和胖jar没有复制提供程序jar,因为它需要更多的部署和支持努力。 –

由于Andy Wilkinson mentioned,你可能打一个bug,即这一项(2002年创建):https://bugs.openjdk.java.net/browse/JDK-4619777

我不知道是否能解决你的问题,但你可以直接使用CharsetProvider得到Charset,东西线:

Charset charset = new CharsetProvider().charsetForName("..."); 

你甚至可以创建一个实用的方法来获得具有手动回退一CharsetCharsetProvider S:

Charset getCharset(String charsetName) { 
    if (Charset.isSupported(charsetName)) { 
     return Charset.forName(charsetName); 
    } 

    Charset charset = new org.smpp.charset.Gsm7BitCharsetProvider().charsetForName(charsetName); 
    if (charset != null) { return charset; } 
    charset = new com.beetstra.jutf7.CharsetProvider().charsetForName(charsetName); 
    if (charset != null) { return charset; } 
    // more fallbacks here ... 
    return null; 
} 

尽管这只是解决所提到的JDK bug的一种解决方法,因为这正是通过注册CharsetProvider s自动执行的逻辑。