如何解决用作JSF值的Java记录
我正在使用Java 15和JSF 2.3以及PrimeFaces 8开发一个简单的JSF Web应用程序(主要用于学习),并且正在使用一个简单的Java记录对实体进行建模。该应用程序不使用任何数据库。
我的问题是这样的xhtml页面中是否可以使用java记录作为值
<p:column headerText="Id">
<h:outputText value="#{car.randomId}" />
</p:column>
因为我收到以下错误
javax.el.PropertyNotFoundException: The class 'com.company.Car' does not have the property 'id'.
。
我尝试放入car.year()
,但是没有用。
记录的定义是这样
public record Car (String randomId,String randomBrand,int randomYear,String randomColor,int randomPrice,boolean randomSoldState) {
}
在pom.xml中,我正在使用以下api
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>8.0.0</version>
<scope>provided</scope>
</dependency>
谢谢您的帮助!
解决方法
从技术上讲,问题不在JSF中,而在EL中。异常的程序包名称已经暗示了这一点:javax.el.PropertyNotFoundException
。使用的EL版本尚无法识别Java记录。 Jakarta EE 8与Java 8联系在一起,但是Java Records功能是在Java 14中引入的。从理论上讲,它最早只能在与Java 14相关的Jakarta EE版本相关的EL版本中得到本机支持。因为Java记录只能用作“预览功能”(因此默认情况下根本没有启用)。
回到您的具体问题,使用#{car.randomId()}
中的方法表达式语法确实在WildFly 21上对我有用。在任何情况下,始终可以使用自定义ELResolver
自定义EL解析。这是一个启动示例,它基于Class#isRecord()
检查记录并收集通过Class#getRecordComponents()
可用的字段作为属性:
public class RecordELResolver extends ELResolver {
private static final Map<Class<?>,Map<String,PropertyDescriptor>> RECORD_PROPERTY_DESCRIPTOR_CACHE = new ConcurrentHashMap<>();
private static boolean isRecord(Object base) {
return base != null && base.getClass().isRecord();
}
private static Map<String,PropertyDescriptor> getRecordPropertyDescriptors(Object base) {
return RECORD_PROPERTY_DESCRIPTOR_CACHE
.computeIfAbsent(base.getClass(),clazz -> Arrays
.stream(clazz.getRecordComponents())
.collect(Collectors
.toMap(RecordComponent::getName,recordComponent -> {
try {
return new PropertyDescriptor(recordComponent.getName(),recordComponent.getAccessor(),null);
}
catch (IntrospectionException e) {
throw new IllegalStateException(e);
}
})));
}
private static PropertyDescriptor getRecordPropertyDescriptor(Object base,Object property) {
PropertyDescriptor descriptor = getRecordPropertyDescriptors(base).get(property);
if (descriptor == null) {
throw new PropertyNotFoundException("The record '" + base.getClass().getName() + "' does not have the field '" + property + "'.");
}
return descriptor;
}
@Override
public Object getValue(ELContext context,Object base,Object property) {
if (!isRecord(base) || property == null) {
return null;
}
PropertyDescriptor descriptor = getRecordPropertyDescriptor(base,property);
try {
Object value = descriptor.getReadMethod().invoke(base);
context.setPropertyResolved(base,property);
return value;
}
catch (Exception e) {
throw new ELException(e);
}
}
@Override
public Class<?> getType(ELContext context,property);
context.setPropertyResolved(true);
return descriptor.getPropertyType();
}
@Override
public Class<?> getCommonPropertyType(ELContext context,Object base) {
if (!isRecord(base)) {
return null;
}
return String.class;
}
@Override
public boolean isReadOnly(ELContext context,Object property) {
if (!isRecord(base)) {
return false;
}
getRecordPropertyDescriptor(base,property); // Forces PropertyNotFoundException if necessary.
context.setPropertyResolved(true);
return true;
}
@Override
public void setValue(ELContext context,Object property,Object value) {
if (!isRecord(base)) {
return;
}
throw new PropertyNotWritableException("Java Records are immutable");
}
@Override
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context,Object base) {
if (!isRecord(base)) {
return null;
}
Map rawDescriptors = getRecordPropertyDescriptors(base);
return rawDescriptors.values().iterator();
}
}
要使其正常工作,请在faces-config.xml
中进行如下注册:
<application>
<el-resolver>com.example.RecordELResolver</el-resolver>
</application>
实际工作是通过getValue()
方法完成的。它基本上找到java.lang.reflect.Method
代表Java记录的访问者并调用它。
也就是说,Java记录不适合替代功能强大的JavaBean,主要是因为Java记录是不可变的。因此,它们不能用作真实(JPA)实体,因为它们应该是可变的。 Java记录最多可用作DTO。
,在深入研究堆栈跟踪并检查了用于读取属性的代码之后,目前看来,从java记录读取属性的操作似乎尚未实现。
也许是将来的版本。
请注意,一旦找到有关此主题的相关信息,我将立即更新答案。
更新8.11.2020 不幸的是,我对记录的范围有误解。
在实际应用中,我的实体将是JPA实体。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。