Freemarker库JDK 8准备好了吗?

时间:2017-08-21 16:02:36

标签: freemarker

将项目迁移到JDK8时,会遇到Freemarker问题。 如果Freemarker声明
< #if response.matchedNames ??>
< #list response.matchedNames as matchedName>

在街区内
< #escape x as x?xml>
下面的Java异常将从freemarker-2.3.19.jar或更高版本中抛出。

引起:freemarker.template.TemplateModelException:?size不支持:freemarker.ext.beans.SimpleMethodModel

这些语句在JDK7中没有任何问题。调试应用程序后,可以找到一些相关信息和原因如下。 该物业" matchedNames"一个Java对象"响应"是一种数组类型。在JDK7中,属性" matchedNames"被处理为ArrayModel。但是在JDK8中,它被处理为SimpleMethodModel。

下面还提供了一些可能有助于调查的信息。 BeanInfo内省显示数组属性的属性类型是JDK中的java.beans.PropertyDescriptor。但是,在JDK8中,数组类型被解释为java.beans.IndexedPropertyDescriptor。 Freemarker似乎没有在JDK8中正确处理这个并且抛出"?大小不受支持"对于数组类型属性。我下载了几个更新版本的Freemarker jar(2.3.24 - 2.3.26),问题是一样的。

Freemarker与JDK8不兼容吗?有人可以帮忙吗?

非常感谢, 雷蒙德

============================================== <登记/> 添加Java类和freemarker模板文件的失败方案如下所示。

超级班:

public class SearchByNameRes {
    protected String[] matchedNames;
    public String[] getMatchedNames() {
        return matchedNames;
    }
    public void setMatchedNames(String[] names) {
        matchedNames = names;
    }
}

子类:

public class SearchByAddressRes extends SearchByNameRes {
    private String[] addresses;
    public String[] getMatchedAddresses() {
        return addresses;
    }
    public void setMatchedAddresses(String[] addrs) {
        addresses = addrs;
    }
}

模板response.ftl:

<Response>
<#escape x as x?xml>
    <#if response.matchedNames??>
        <#list response.matchedNames as matchedName>
        </#list>
    </#if>
</#escape>
</Response>

响应是子类SearchByAddressRes的对象实例。在这个简单的例子中,第3行和第4行可能抛出异常&#34; freemarker.template.TemplateModelException:?size不支持:freemarker.ext.beans.SimpleMethodModel&#34;。

=============================================== ======
完成的测试代码(由wsimport生成)如下所示。 String []类型的属性可以替换为其他Java类。

package test.jdk8.freemarker;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "response", propOrder = {
    "matchedNames",
    "matchedAddresses"
})
public class Response {

    @XmlElement(nillable = true)
    protected String[] matchedNames;
    @XmlElement(nillable = true)
    protected String[] matchedAddresses;

    public String[] getMatchedNames() {
        if (this.matchedNames == null) {
            return new String[0];
        }
        String[] retVal = new String[this.matchedNames.length] ;
        System.arraycopy(this.matchedNames, 0, retVal, 0, this.matchedNames.length);
        return (retVal);
    }

    public String getMatchedNames(int idx) {
        if (this.matchedNames == null) {
            throw new IndexOutOfBoundsException();
        }
        return this.matchedNames[idx];
    }

    public int getMatchedNamesLength() {
        if (this.matchedNames == null) {
            return  0;
        }
        return this.matchedNames.length;
    }

    public void setMatchedNames(String[] values) {
        int len = values.length;
        this.matchedNames = ((String[]) new String[len] );
        for (int i = 0; (i<len); i ++) {
            this.matchedNames[i] = values[i];
        }
    }

    public String setMatchedNames(int idx, String value) {
        return this.matchedNames[idx] = value;
    }

    public String[] getMatchedAddresses() {
        if (this.matchedAddresses == null) {
            return new String[0];
        }
        String[] retVal = new String[this.matchedAddresses.length] ;
        System.arraycopy(this.matchedAddresses, 0, retVal, 0, this.matchedAddresses.length);
        return (retVal);
    }

    public String getMatchedAddresses(int idx) {
        if (this.matchedAddresses == null) {
            throw new IndexOutOfBoundsException();
        }
        return this.matchedAddresses[idx];
    }

    public int getMatchedAddressesLength() {
        if (this.matchedAddresses == null) {
            return  0;
        }
        return this.matchedAddresses.length;
    }

    public void setMatchedAddresses(String[] values) {
        int len = values.length;
        this.matchedAddresses = ((String[]) new String[len] );
        for (int i = 0; (i<len); i ++) {
            this.matchedAddresses[i] = values[i];
        }
    }

    public String setMatchedAddresses(int idx, String value) {
        return this.matchedAddresses[idx] = value;
    }
}

1 个答案:

答案 0 :(得分:1)

显然,{8}中java.beans.Introspector的行为已发生变化,如果对于JavaBeans属性(在您的情况下为marchedNames),则&#34;正常&#34;属性读取器(String[] getMatchedNames())和索引属性读取器(String getMatchedNames(int))。在这种情况下,在Java 7中,Introspector仅报告&#34;正常&#34;属性读者,在Java 8中它报告&#34; normal&#34;和索引属性读者。不幸的是,当报告两者时,FreeMarker更喜欢最后一个,因此该属性变得不可列出。

怎么做......其中一个:

  • 当FreeMarker 2.3.27出局时,将incompatibleImprovements设置为2.3.27或更高以解决此问题。请注意,如果您未使用ObjectWrapper的默认Configuration,则需要设置正在使用的incompatibleImprovements实例的ObjectWrapper设置,而不是(只有Configuration

  • 尝试告诉代码生成器不生成索引属性方法......但我不知道wsimport中的此类选项。

  • 使用response.getMatchedNames()代替response.matchedNames,至少最高2.3.26,作为解决方法。