【问题标题】:How to map multiple parameter names to POJO when binding spring mvc command objects绑定spring mvc命令对象时如何将多个参数名映射到POJO
【发布时间】:2016-07-03 14:41:26
【问题描述】:

我的问题实际上是这个问题as seen here... 的衍生问题,因此在继续之前检查该线程可能会有所帮助。

在我的 Spring Boot 项目中,我有两个实体 SenderRecipient 代表 Customer 并且几乎具有相同的字段,所以我让它们扩展基类 Customer

客户基类;

@MappedSuperclass
public class Customer extends AuditableEntity {

    @Column(name = "firstname")
    private String firstname;

    @Transient
    private CustomerRole role;

    public Customer(CustomerRole role) {
        this.role = role;
    }

    //other fields & corresponding getters and setters
}

发件人域对象;

@Entity
@Table(name = "senders")
public class Sender extends Customer {

    public Sender(){
        super.setRole(CustomerRole.SENDER);
    }
}

收件人域对象;

@Entity
@Table(name = "recipients")
public class Recipient extends Customer {

    public Recipient(){
        super.setRole(CustomerRole.RECIPIENT);
    }
}

注意 - SenderRecipient 除了它们的角色外完全相同。通过使Customer 基类本身成为实体,这些可以很容易地存储在单个 customers 表中,但我故意以这种方式分隔实体,因为我有义务将每个客户类型持久保存在单独的数据库中表格。

现在我在视图中有一个表单,它收集SenderRecipient 的详细信息,因此例如要收集名字,我必须以不同的方式命名表单字段,如下所示;

表单的发件人部分;

<input type="text" id="senderFirstname" name="senderFirstname" value="$!sender.firstname">

表格的收件人部分;

<input type="text" id="recipientFirstname" name="recipientFirstname" value="$!recipient.firstname">

但是客户可用的字段太多了,我正在寻找一种方法,通过this question here 中要求的注释将它们映射到 pojo。但是,there 提供的解决方案意味着我必须为两个域对象创建单独的代理并相应地注释字段,例如

public class SenderProxy {

    @ParamName("senderFirstname")
    private String firstname;

    @ParamName("senderLastname")
    private String lastname;
    //...
}

public class RecipientProxy {

    @ParamName("recipientFirstname")
    private String firstname;

    @ParamName("recipientLastname")
    private String lastname;
    //...
}

所以我很好奇并想知道,有没有办法将此代理映射到多个@ParamName,例如基类可以注释如下?;

@MappedSuperclass
public class Customer extends AuditableEntity {

    @Column(name = "firstname")
    @ParamNames({"senderFirstname", "recipientFirstname"})
    private String firstname;

    @Column(name = "lastname")
    @ParamNames({"senderLastname", "recipientLastname"})
    private String lastname;

    @Transient
    private CustomerRole role;

    public Customer(CustomerRole role) {
        this.role = role;
    }

    //other fields & corresponding getters and setters
}

然后也许找到一种方法来根据注释选择字段的值??

【问题讨论】:

  • 没有@ParamName 可以在您的实体中使用,因此不确定您使用的是什么。
  • 当然...但这就是为什么我指出要检查这些链接引用的线程,以便您了解@ParamName 注释的来源

标签: java spring-mvc spring-boot


【解决方案1】:

张杰的建议like ExtendedBeanInfo

所以我这样做

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Alias {
    String[] value();
}

public class AliasedBeanInfoFactory implements BeanInfoFactory, Ordered {
    @Override
    public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
        return supports(beanClass) ? new AliasedBeanInfo(Introspector.getBeanInfo(beanClass)) : null;
    }

    private boolean supports(Class<?> beanClass) {
        Class<?> targetClass = beanClass;
        do {
            Field[] fields = targetClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Alias.class)) {
                    return true;
                }
            }
            targetClass = targetClass.getSuperclass();

        } while (targetClass != null && targetClass != Object.class);

        return false;
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 100;
    }
}

public class AliasedBeanInfo implements BeanInfo {
    private static final Logger LOGGER = LoggerFactory.getLogger(AliasedBeanInfo.class);

    private final BeanInfo delegate;

    private final Set<PropertyDescriptor> propertyDescriptors = new TreeSet<>(new PropertyDescriptorComparator());

    AliasedBeanInfo(BeanInfo delegate) {
        this.delegate = delegate;
        this.propertyDescriptors.addAll(Arrays.asList(delegate.getPropertyDescriptors()));

        Class<?> beanClass = delegate.getBeanDescriptor().getBeanClass();
        for (Field field : findAliasedFields(beanClass)) {
            Optional<PropertyDescriptor> optional = findExistingPropertyDescriptor(field.getName(), field.getType());
            if (!optional.isPresent()) {
                LOGGER.warn("there is no PropertyDescriptor for field[{}]", field);
                continue;
            }
            Alias alias = field.getAnnotation(Alias.class);
            addAliasPropertyDescriptor(alias.value(), optional.get());
        }
    }

    private List<Field> findAliasedFields(Class<?> beanClass) {
        List<Field> fields = new ArrayList<>();
        ReflectionUtils.doWithFields(beanClass,
                fields::add,
                field -> field.isAnnotationPresent(Alias.class));
        return fields;
    }

    private Optional<PropertyDescriptor> findExistingPropertyDescriptor(String propertyName, Class<?> propertyType) {
        return propertyDescriptors
                .stream()
                .filter(pd -> pd.getName().equals(propertyName) && pd.getPropertyType().equals(propertyType))
                .findAny();
    }

    private void addAliasPropertyDescriptor(String[] values, PropertyDescriptor propertyDescriptor) {
        for (String value : values) {
            if (!value.isEmpty()) {
                try {
                    this.propertyDescriptors.add(new PropertyDescriptor(
                            value, propertyDescriptor.getReadMethod(), propertyDescriptor.getWriteMethod()));
                } catch (IntrospectionException e) {
                    LOGGER.error("add field[{}] alias[{}] property descriptor error", propertyDescriptor.getName(),
                            value, e);
                }
            }
        }
    }

    @Override
    public BeanDescriptor getBeanDescriptor() {
        return this.delegate.getBeanDescriptor();
    }

    @Override
    public EventSetDescriptor[] getEventSetDescriptors() {
        return this.delegate.getEventSetDescriptors();
    }

    @Override
    public int getDefaultEventIndex() {
        return this.delegate.getDefaultEventIndex();
    }

    @Override
    public PropertyDescriptor[] getPropertyDescriptors() {
        return this.propertyDescriptors.toArray(new PropertyDescriptor[0]);
    }

    @Override
    public int getDefaultPropertyIndex() {
        return this.delegate.getDefaultPropertyIndex();
    }

    @Override
    public MethodDescriptor[] getMethodDescriptors() {
        return this.delegate.getMethodDescriptors();
    }

    @Override
    public BeanInfo[] getAdditionalBeanInfo() {
        return this.delegate.getAdditionalBeanInfo();
    }

    @Override
    public Image getIcon(int iconKind) {
        return this.delegate.getIcon(iconKind);
    }

    static class PropertyDescriptorComparator implements Comparator<PropertyDescriptor> {

        @Override
        public int compare(PropertyDescriptor desc1, PropertyDescriptor desc2) {
            String left = desc1.getName();
            String right = desc2.getName();
            for (int i = 0; i < left.length(); i++) {
                if (right.length() == i) {
                    return 1;
                }
                int result = left.getBytes()[i] - right.getBytes()[i];
                if (result != 0) {
                    return result;
                }
            }
            return left.length() - right.length();
        }
    }
}

【讨论】:

    猜你喜欢
    • 2012-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-14
    • 1970-01-01
    相关资源
    最近更新 更多