Spring Orika Bean Copy 屬性丟失問題說明

一、背景說明
考慮到bean複製性能問題,在SSM框架中使用引入了orkia 實現bean複製。上線一段時間後,發現部分線上機器出現bean複製後屬性丟失問題,重啓後正常

二、問題詳細說明
在線上機器使用orika 進行bean複製時,在bean屬性類型、名稱相同情況下無法賦值的情況,屬性值全爲null,其中最特別的僅出現在部分bean上,大部分bean複製完全正常,並且出現問題的bean不固定(每次重啓都會變),tomcat容器重啓恢復正常。

三、問題嘗試解決
1、在問題發生後,tomcat容器重啓後恢復正常,首先想到的是在 orkia 進行bean 複製時依賴的jar包衝突導致的(浪費了大量時間),簡略說明下,在orkia-core 1.4.5 包下需要依賴的包中如下所示:

<dependency>
	<groupId>org.javassist</groupId>
	<artifactId>javassist</artifactId>
</dependency>
<dependency>
	<groupId>com.thoughtworks.paranamer</groupId>
	<artifactId>paranamer</artifactId>
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
	<groupId>com.googlecode.concurrentlinkedhashmap</groupId>
	<artifactId>concurrentlinkedhashmap-lru</artifactId>
</dependency>
<dependency>
	<groupId>com.carrotsearch</groupId>
	<artifactId>java-sizeof</artifactId>
</dependency>

其中最容易出現jar包衝突的是javassist 和 slf4j-api 這兩個包,通過jar包排除統一版本後,在夜深人靜時上線,一切正常。


過一段時間後白天上線又出現同樣的問題,中間耗費了大量的時間,經過各種日誌排查監控,通過排除法確定,發生問題的機器是在服務器發佈上線重啓時,接到了用戶的請求,由於上線操作員是個急性子剛將線上機器從軟負載上摘掉就進行了機器重啓,軟負載緩存未過期,導致線上用戶請求打到重啓機器上。

2、結合1步排查結果,繼續進行問題定位,組中發現在orkia文檔中寫到一句話,具體地址內容如下所示:
http://orika-mapper.github.io/orika-docs/faq.html

得到結果在 spring 中使用orkia 時需要將 ConfigurableMapper 定義成單例模式,結合着這個提示對我們代碼進行排查,發現我們代碼如下所示:

@Component
public class BeanMapper implements InitializingBean {
    private MapperFacade mapper;
    @Override
    public void afterPropertiesSet() throws Exception {
        if(mapper == null){
            MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
            mapper = mapperFactory.getMapperFacade();
        }
    }
	public  <S, D> D map(S source, Class<D> destinationClass) {
        return mapper.map(source, destinationClass);
    }
}

進一步結合orkia 源碼進行問題查找,發現MapperFacadeImpl 中會將賦值的bean信息統一存到到 MappingContext 中的typeCache存到內部屬性中,其數據結構OpenIntObjectHashMap 類型。

進一步猜測得知由於此時MapperFacade 是懶加載方式,可能是在bean存儲到MappingContext 之前,接到到了用戶的請求,先一步將需要複製的bean存儲到了MappingContext中,在 MappingContext中存儲的bean 信息爲null,最終導致問題的產生,這裏僅是結合和orkia源碼進行猜測,是否正確有待考察(在本地tomcat環境無法重現此問題)。

四、解決方案
將MapperFacade 改爲預加載方式,具體修改後代碼如下所示:

@Component
public class BeanMapper {
 
    private final static MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
     
    private final static MapperFacade mapper = mapperFactory.getMapperFacade();
	
    public  <S, D> D map(S source, Class<D> destinationClass) {
        return mapper.map(source, destinationClass);
    }
}

通過以上方式最終解決問題。問題關鍵點如下所示:
1、MapperFacade 需要配置爲單例
2、MapperFacade需要配置爲預加載

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章