在我们的项目中,我们目前转向Java 8,现在希望使用内部XML库而不是其他库。
为此,我们删除了我们来自类路径的xalan.jar。

尝试呈现XML文件时,这会导致ClassNotFoundExceptions。
在XSL样式表中,我们可以转换XML文件,我们还可以调用外部Java方法,如以下示例所示:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:fo="http://www.w3.org/1999/XSL/Format"
   xmlns:java="http://xml.apache.org/xalan/java">

...

<xsl:template name="example">
  <fo:block>
    <xsl:value-of select="java:my.package.name.JavaClass.testMethod('test')"/>
  </fo:block>
</xsl:template>
...


但是,总是在应调用外部方法testMethod()时,该过程会因ClassNotFoundException而停止。
我调试了com.sun.org.apache.xalan.internal.xsltc.compiler.FunctionCall类中的代码,发现XSLTC试图实例化my.package.name.JavaClass以便调用testMethod()。没关系但...

尝试在以下com.sun.org.apache.xalan.internal.utils.ObjectFactory代码中实例化/加载该类:

/**
     * Find a Class using the same class loader for the ObjectFactory by default
     * or boot class loader when Security Manager is in place
     */
    public static Class<?> findProviderClass(String className, boolean doFallback)
        throws ClassNotFoundException, ConfigurationError
    {
        return findProviderClass (className,
                findClassLoader (), doFallback);
    }


这进一步调用findClassLoader()以获取正确的类加载器以加载我的JavaClass

/**
     * Figure out which ClassLoader to use.  For JDK 1.2 and later use
     * the context ClassLoader.
     */
    public static ClassLoader findClassLoader()
    {
        if (System.getSecurityManager()!=null) {
            //this will ensure bootclassloader is used
            return null;
        }

        // Figure out which ClassLoader to use for loading the provider
        // class.  If there is a Context ClassLoader then use it.
        ClassLoader context = SecuritySupport.getContextClassLoader();
        ClassLoader system = SecuritySupport.getSystemClassLoader();

        ClassLoader chain = system;
        while (true) {
            if (context == chain) {
...loop to get correct class loader
        }


现在,在我们的项目中,我们有了一个“问题”,即已经有一个SecurityManager实现,这意味着上面的该实现只返回null而不是上下文类加载器。

顺便说一句:我也希望在这里获得上下文类加载器,但是稍后还要进行其他检查,以查看SecurityManager是否允许加载类,为什么它在这里只返回null?

这再次意味着尝试使用引导类加载器加载我的类,该引导类加载器不知道我的类,因此引发异常。删除SecurityManager会返回上下文类加载器,该类加载器能够加载我的类,并且XML可以成功转换。

因此-如果您仍然在这里睡着了-是否可以在XSL样式表中仍然使用我的外部Java方法?有没有办法在这种情况下加载我的课程?
我考虑了以下可能的解决方案,但它们都不完全使我满意:


修补ObjectFactory,删除或扩展对SecurityManager的检查,然后将其放置在我的应用程序的启动类路径上,然后将其放在原始应用程序之前加载(肮脏的hack)
删除SecurityManager(目前或多或少是虚拟实现,但这不是永久解决方案)
将Java方法编写为XSL中的扩展函数(不确定该方法是否适用于我们拥有的所有方法,并且将非常耗时且容易出错;而且我不确定这是否能够真正解决问题)
使用反射通过以下方式在XSL中创建我的JavaClass的实例:





但是,最后一种方法也不起作用,因为该类也随AppClassLoader一起加载,并且在解析过程中也可能不可用。但是也许有类似的解决方案?

提前致谢。

最好的祝福,
詹斯

最佳答案

做这样的事情:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:fo="http://www.w3.org/1999/XSL/Format"
   xmlns:myHelper="http://xml.apache.org/xalan/java/my.package.name.JavaClass">

...

<xsl:template name="example">
  <fo:block>
    <xsl:value-of select="myHelper:testMethod('test')"/>
  </fo:block>
</xsl:template>


在样式表声明中如何指定名称空间(在本例中为myHelper)取决于您用于处理XSL的内容,上面的示例适用于Xalan。

10-07 19:32
查看更多