9

例如

class tester
{
    @Test
    public void testBeanUtils() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException
    {
        Stranger stranger = new Stranger();
        BeanUtils.setProperty(stranger,"name","wener");
        BeanUtils.setProperty(stranger,"xname","xwener");
        BeanUtils.setProperty(stranger,"yname","ywener");

        System.out.println(stranger);
    }
    @Data// lombok annotation generate all setter and getter
    public static class Stranger
    {
        @Accessors(chain = true)// generate chained setter
        String name;
        String xname;
        String yname;

        public Stranger setYname(String yname)// no lombok, still not work
        {
            this.yname = yname;
            return this;
        }
    }
}

我的输出:

TestValues.Stranger(name=null, xname=xwener, yname=null)

这有什么问题?链式二传手是个好东西。有什么建议吗?

编辑

再次回到这个问题。这次我无法删除Accessors chain. 现在,我用commons-lang3来实现。

// force access = true is required
Field field = FieldUtils.getField(bean.getClass(), attrName, true);
field.set(bean,value);

对于那些遇到同样问题的人。

4

3 回答 3

20

您可以使用FluentPropertyBeanIntrospector实现:

“BeanIntrospector 接口的实现,它可以检测流式 API 场景中使用的属性的写入方法。”

https://commons.apache.org/proper/commons-beanutils/apidocs/org/apache/commons/beanutils/FluentPropertyBeanIntrospector.html

PropertyUtils.addBeanIntrospector(new FluentPropertyBeanIntrospector());
BeanUtils.setProperty( this.o, "property", "value" );
于 2016-09-23T12:06:33.283 回答
9

这很简单:BeanUtils相当奇怪,Introspector它使用的是:

虽然BeanUtils.setProperty声明了一些异常,但似乎默默地忽略了要设置的属性的不存在。最终的罪魁祸首是Introspector需要二传手的空虚。

我认为它是设计破坏的,但是 YMMV。这是一个古老的类,在那个黑暗时期还没有发明流畅的接口。用于Accessors(chain=false)禁用链接。


更重要的是:使用源. 获取它并获取一个调试器(它已经在您的 IDE 中)以自己找出它(仍然可以随意询问它是否不起作用,只是再努力一点)。

于 2014-03-30T14:01:19.977 回答
2

在我的项目中,我们全面使用链式访问器,因此设置chain=false不是一种选择。我最终编写了自己的 introspector,它与 @mthielcke 推荐的类似,并且可能以相同的方式注册。

内省者

import org.apache.commons.beanutils.BeanIntrospector;
import org.apache.commons.beanutils.IntrospectionContext;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Stream;

import lombok.extern.slf4j.Slf4j;

/**
 * Allows {@link org.apache.commons.beanutils.BeanUtils#copyProperties(Object, Object)} to copy properties across beans whose
 * properties have been made <b>fluent</b> through <a href="https://projectlombok.org/">Lombok</a>
 * {@link lombok.experimental.Accessors}, {@link lombok.Setter} and {@link lombok.Getter} annotations.
 *
 * @author izilotti
 */
@Slf4j
public class LombokPropertyBeanIntrospector implements BeanIntrospector {

    /**
     * Performs introspection. This method scans the current class's methods for property write and read methods which have been
     * created by the <a href="https://projectlombok.org/">Lombok</a> annotations.
     *
     * @param context The introspection context.
     */
    @Override
    public void introspect(final IntrospectionContext context) {
        getLombokMethods(context).forEach((propertyName, methods) -> {
            if (methods[0] != null && methods[1] != null) {
                final PropertyDescriptor pd = context.getPropertyDescriptor(propertyName);
                try {
                    if (pd == null) {
                        PropertyDescriptor descriptor = new PropertyDescriptor(propertyName, methods[1], methods[0]);
                        context.addPropertyDescriptor(descriptor);
                    }
                } catch (final IntrospectionException e) {
                    log.error("Error creating PropertyDescriptor for {}. Ignoring this property.", propertyName, e);
                }
            }
        });
    }

    private Map<String, Method[]> getLombokMethods(IntrospectionContext context) {
        Map<String, Method[]> lombokPropertyMethods = new HashMap<>(); // property name, write, read
        Stream.of(context.getTargetClass().getMethods())
                .filter(this::isNotJavaBeanMethod)
                .forEach(method -> {
                    if (method.getReturnType().isAssignableFrom(context.getTargetClass()) && method.getParameterCount() == 1) {
                        log.debug("Found mutator {} with parameter {}", method.getName(), method.getParameters()[0].getName());
                        final String propertyName = propertyName(method);
                        addWriteMethod(lombokPropertyMethods, propertyName, method);
                    } else if (!method.getReturnType().equals(Void.TYPE) && method.getParameterCount() == 0) {
                        log.debug("Found accessor {} with no parameter", method.getName());
                        final String propertyName = propertyName(method);
                        addReadMethod(lombokPropertyMethods, propertyName, method);
                    }
                });
        return lombokPropertyMethods;
    }

    private void addReadMethod(Map<String, Method[]> lombokPropertyMethods, String propertyName, Method readMethod) {
        if (!lombokPropertyMethods.containsKey(propertyName)) {
            Method[] writeAndRead = new Method[2];
            lombokPropertyMethods.put(propertyName, writeAndRead);
        }
        lombokPropertyMethods.get(propertyName)[1] = readMethod;
    }

    private void addWriteMethod(Map<String, Method[]> lombokPropertyMethods, String propertyName, Method writeMethod) {
        if (!lombokPropertyMethods.containsKey(propertyName)) {
            Method[] writeAndRead = new Method[2];
            lombokPropertyMethods.put(propertyName, writeAndRead);
        }
        lombokPropertyMethods.get(propertyName)[0] = writeMethod;
    }

    private String propertyName(final Method method) {
        final String methodName = method.getName();
        return (methodName.length() > 1) ? Introspector.decapitalize(methodName) : methodName.toLowerCase(Locale.ENGLISH);
    }

    private boolean isNotJavaBeanMethod(Method method) {
        return !isGetter(method) || isSetter(method);
    }

    private boolean isGetter(Method method) {
        if (Modifier.isPublic(method.getModifiers()) && method.getParameterTypes().length == 0) {
            if (method.getName().matches("^get[A-Z].*") && !method.getReturnType().equals(Void.TYPE)) {
                return true;
            }
            return method.getName().matches("^is[A-Z].*") && method.getReturnType().equals(Boolean.TYPE);
        }
        return false;
    }

    private boolean isSetter(Method method) {
        return Modifier.isPublic(method.getModifiers())
                && method.getReturnType().equals(Void.TYPE)
                && method.getParameterTypes().length == 1
                && method.getName().matches("^set[A-Z].*");
    }

}

登记

PropertyUtils.addBeanIntrospector(new LombokPropertyBeanIntrospector());
BeanUtils.copyProperties(dest, origin);
于 2018-05-25T18:24:41.020 回答