BeanUtils不适用于链设置器

例如

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); 

对于那些有同样问题的人。

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

尽管BeanUtils.setProperty声明了一些exception,但它似乎默默地忽略了要设置的属性的不存在。 最终的罪魁祸首是Introspector ,它只需要设置者的空虚。

我称之为设计破坏,但是YMMV。 它是一个古老的类,在那些黑暗时代还没有发明流畅的界面。 使用Accessors(chain=false)禁用链接。


更重要的是: 使用来源 。 得到它并得到一个调试器(它已经在您的IDE中)自己找到它(仍然可以随意询问它是否不起作用,只是尝试一下)。

您可以使用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" ); 

在我的项目中,我们使用链式访问器,因此设置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 fluent through Lombok * {@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 Lombok 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 getLombokMethods(IntrospectionContext context) { Map 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 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 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[AZ].*") && !method.getReturnType().equals(Void.TYPE)) { return true; } return method.getName().matches("^is[AZ].*") && 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[AZ].*"); } } 

注册

 PropertyUtils.addBeanIntrospector(new LombokPropertyBeanIntrospector()); BeanUtils.copyProperties(dest, origin); 
Interesting Posts