You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级至JSF 2.3后带属性/注入的自定义转换器失效问题

解决JSF 2.3托管转换器标签属性无法设置的问题

这个问题的核心在于JSF 2.3中@FacesConverter(managed=true)的转换器是由CDI托管的,而JSF仅会对非托管转换器(即JSF自身实例化的)调用setter方法来设置标签属性。当你启用managed=true时,转换器的生命周期交给了CDI,JSF不会再通过反射调用它的setter,导致标签中的attr属性无法被赋值;而关闭managed=true后,JSF接管实例化,setter能被调用,但CDI的依赖注入(比如BeanManager)就失效了。

下面提供两种可行的解决方案:

方案一:从UIComponent中直接获取标签属性(推荐)

保持@FacesConverter(managed=true)的配置,在转换器的转换方法中,从传入的UIComponent对象的属性集合里获取标签上的attr值,不需要依赖setter方法。修改后的转换器代码如下:

@Dependent
@FacesConverter(value = "cdijsf.ConverterWithAttr", managed = true)
public class ConverterWithAttr implements Converter<String> {
    @Inject
    private BeanManager beanManager;

    @PostConstruct
    private void init() {
        // BeanManager此时会被正常注入
    }

    @Override
    public String getAsObject(FacesContext context, UIComponent component, String value) {
        // 从组件属性中获取标签里的attr值
        String attr = (String) component.getAttributes().get("attr");
        // 在这里使用attr做逻辑处理
        return value;
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, String value) {
        // 同样可以在这里获取attr值
        String attr = (String) component.getAttributes().get("attr");
        return value;
    }
}

这种方式的优势是完全适配CDI托管的转换器生命周期,同时能动态获取标签上的属性值,不需要额外的配置修改。

方案二:将转换器改为CDI命名Bean,通过标签绑定传递属性

如果你更倾向于保留setter方法的方式,可以将转换器定义为CDI命名Bean,不再使用@FacesConverter注解,然后在页面或标签库中通过绑定的方式使用它:

1. 修改转换器代码

移除@FacesConverter,添加@Named注解,保持CDI的依赖注入和setter方法:

@Dependent
@Named("converterWithAttr")
public class ConverterWithAttr implements Converter<String> {
    @Inject
    private BeanManager beanManager;
    private String attr;

    @PostConstruct
    private void init() {
        // BeanManager正常注入
    }

    @Override
    public String getAsObject(FacesContext context, UIComponent component, String value) {
        return value;
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, String value) {
        return value;
    }

    public String getAttr() {
        return attr;
    }

    public void setAttr(String attr) {
        this.attr = attr;
    }
}

2. 修改标签库定义

将<converter>标签替换为<component>,通过绑定方式引用CDI bean,并传递属性:

<facelet-taglib version="2.3" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facelettaglibrary_2_3.xsd">
    <namespace>http://cdijsf.transdata.net/jsf</namespace>
    <tag>
        <tag-name>converterWithAttr</tag-name>
        <component>
            <component-type>javax.faces.Converter</component-type>
            <binding>#{converterWithAttr}</binding>
        </component>
        <attribute>
            <name>attr</name>
            <type>java.lang.String</type>
            <targets>javax.faces.Converter</targets>
        </attribute>
    </tag>
</facelet-taglib>

这种方式通过CDI bean绑定来实现属性传递,setter方法会被正常调用,同时依赖注入也能正常工作。

为什么会出现这个问题?

在JSF 2.2及更早版本中,转换器默认由JSF自身实例化,会自动调用setter来设置标签属性;而JSF 2.3引入了managed=true选项,允许转换器由CDI托管,此时JSF会委托CDI创建转换器实例,不再负责属性的设置——因为CDI的注入模型和JSF的属性设置模型是分离的,所以导致了setter不被调用的情况。

内容的提问来源于stack exchange,提问作者Heiko Tappe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:14:18