e.g。
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);
对于那些遇到同样问题的人。
答案 0 :(得分:9)
您可以使用 FluentPropertyBeanIntrospector 实施:
“BeanIntrospector接口的一个实现,它可以检测在流畅的API场景中使用的属性的写入方法。”
PropertyUtils.addBeanIntrospector(new FluentPropertyBeanIntrospector());
BeanUtils.setProperty( this.o, "property", "value" );
答案 1 :(得分:5)
这很简单:BeanUtils
相当奇怪,所使用的Introspector
也是如此:
虽然BeanUtils.setProperty
声明了一些例外,但silently ignore似乎没有要设置的属性。最终的罪魁祸首是Introspector
,它只是requires二传手的空虚。
我称之为设计破坏,但是YMMV。它是一个古老的类,在那些黑暗时代还没有发明流畅的界面。使用Accessors(chain=false)
禁用链接。
更重要的是:Use the source。得到它并获得一个调试器(它已经在您的IDE中)自己找到它(仍然可以随意询问它是否不起作用,只是尝试一下)。
答案 2 :(得分:0)
在我的项目中,我们全面使用链式访问器,因此设置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);