就由於加了Lombok的@Accessors(chain = true),bean拷貝工具類不幹活了

前言

此次新建了一個工程,由於 Lombok 用得很習慣,但之前的話,通常只用了@Data@AllArgsConstructor@EqualsAndHashCode等常規註解;那這個Accessors(chain = true)註解是幹嗎的呢?java

用了這個註解後,生成的set方法是這樣的:git

#加了Accessors(chain = true)   
public Devolution setCenterId(Long centerId) {
        this.centerId = centerId;
        return this;
}

注意,正常狀況下,方法應該是下面這樣的:github

#沒加Accessors(chain = true)   
public void setCenterId(Long centerId) {
        this.centerId = centerId;
}

爲何要用這個方法?主要是方便級聯操做。基於這個考慮就加了。spring

加了後,出現了什麼問題?工具

咱們以前有個bean拷貝的工具類,用於在 po 和 vo 間拷貝屬性。測試

import org.springframework.cglib.beans.BeanCopier;
    public static void copyProperties(Object source,Object target){
        BeanCopier copier = getBeanCopier(source.getClass(), target.getClass());
        copier.copy(source, target, null);  
    }

結果,同事反映說,當target的類型,加了 Accessors(chain = true)時, 這個工具類不能用了!this

跟蹤問題

我原本覺得改改spring源碼就能夠了,結果發現org.springframework.cglib.beans.BeanCopier 源碼打不開,換了個spring 4的版本,也不行。看到包裏面,是待了cglib的,因而本地找了個cglib的包,發現是帶source的,因而解壓後導入工程,嗯,還不錯,能夠用!google

工程代碼在:lua

https://gitee.com/ckl111/cglib-lombok-test調試

我這裏先說問題緣由:

我找到了一個測試用例,大概以下:

public void testSimple() {
        BeanCopier copier = BeanCopier.create(MA.class, MA.class, false);
        MA bean1 = new MA();
        bean1.setIntP(42);
        MA bean2 = new MA();
        copier.copy(bean1, bean2, null);
        assertTrue(bean2.getIntP() == 42);
    }

而後本身改造了一下,加了個類:

@Data
@Accessors(chain = true)
class MaWithLombok {
    private Long id;
    private String name;
    private String privateName;
    private int intP;        
    private long longP;        
    private boolean booleanP;
    private char charP;
    private byte byteP;
    private short shortP;
    private float floatP;
    private double doubleP;
    private String stringP;
    public  String publicField;

}
這裏是測試用例:
public void testSimpleLombok() {
  BeanCopier copier = BeanCopier.create(MA.class, MaWithLombok.class, false);
  MA bean1 = new MA();
  bean1.setIntP(42);
  MaWithLombok bean2 = new MaWithLombok();
  copier.copy(bean1, bean2, null);
  assertTrue(bean2.getIntP() == 42);
}

接下來,就是調試了,在不打斷點直接run時,會拋下面異常:

java.lang.NullPointerException
    at net.sf.cglib.core.ReflectUtils.getMethodInfo(ReflectUtils.java:424)
    at net.sf.cglib.beans.BeanCopier$Generator.generateClass(BeanCopier.java:133)
    at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
    at net.sf.cglib.beans.BeanCopier$Generator.create(BeanCopier.java:90)
    at net.sf.cglib.beans.BeanCopier.create(BeanCopier.java:50)
    at net.sf.cglib.beans.TestBeanCopier.testSimpleLombok(TestBeanCopier.java:38)

打斷點時,發現:

參數member爲null,ok,把堆棧退一層(鼠標點到上一層frame)

而後尋找setter的來源:

PropertyDescriptor[] setters = ReflectUtils.getBeanGetters(target);

單步調試,會找到這個地方:

這裏是進到了jdk的類,這裏
java.beans.Introspector#getBeanInfo() 
private BeanInfo getBeanInfo() throws IntrospectionException {

        // the evaluation order here is import, as we evaluate the
        // event sets and locate PropertyChangeListeners before we
        // look for properties.
        BeanDescriptor bd = getTargetBeanDescriptor();
        MethodDescriptor mds[] = getTargetMethodInfo();
        EventSetDescriptor esds[] = getTargetEventInfo();
        PropertyDescriptor pds[] = getTargetPropertyInfo();//在這裏,獲取目標類的屬性描述符列表

        int defaultEvent = getTargetDefaultEventIndex();
        int defaultProperty = getTargetDefaultPropertyIndex();

        return new GenericBeanInfo(bd, esds, defaultEvent, pds,
                        defaultProperty, mds, explicitBeanInfo);

    }

咱們進入該方法,下圖就能告訴你爲何(java/beans/Introspector.java:520):

緣由總結

好了,通過上面的問題,你們能發現,由於咱們註解的緣由,致使setXXX方法的返回值不爲void,因此使用

java.beans.Introspector#getTargetPropertyInfo來獲取 PropertyDescriptor的時候,出現了問題。

問題解決

問題發現了,要怎麼解決呢,也簡單,我google了一下,哈哈哈。

參考:https://github.com/cglib/cglib/issues/108

使用下面這個工具方法便可:

org.springframework.beans.BeanUtils.copyProperties(source, target);

個人測試工程在,若是你們須要調試 cglib源碼,也能夠看看,裏面有不少功能的test用例:

https://gitee.com/ckl111/cglib-lombok-test

相關文章
相關標籤/搜索