1. 程式人生 > >多層巢狀物件獲取底層物件屬性

多層巢狀物件獲取底層物件屬性

最近在做一個需求,對業務層的查詢引數及返回值做校驗,檢視其是否是合法的值,使用AOP做。後來發現業務方法的返回值有多重巢狀,有Map,List,Set,Page,自定義包裝類等等,且包裝層還巢狀層,如Map< ?,List>,Page< List >,僅僅是通過反射獲取這些包裝之下實際Model就很花費精力,就想能不能將這些邏輯抽象出來,寫個特定的工具類,能很方便的剝離這些外層,獲取底層物件的指定屬性。

每一個包裝類其獲取下一層包裝的方法不盡相同,他們沒有什麼共同之處,因此提取各自的封裝邏輯是必不可少,那麼每一個包裝類至少需要各自的提取方法,如果想抽成通用的形式,則基本是使用方法的重寫來實現;但是如果用重寫則意味著會有很多的子類,且每多一種包裝就需要額外新增一個提取類,工具體系會很臃腫,不好。因此想到了使用列舉,藉助列舉的例項能重寫外部類的方法的特性,將每個包裝類的下層提取邏輯定義在不同的列舉例項裡。先看具體程式碼:

/**
 * 巢狀物件屬性驗證列舉
 *
 * @create 2017-12-18 9:20
 */
public enum NestedObjectFieldCheckingProcessor {

    LIST_RESULT(ListResult.class) {
        @Override
        public void process(Object object, Object expectedFieldVal) {
            doProcessingInternal(((ListResult)object).getContent(), expectedFieldVal);
        }
    },

    PAGE_RESULT(PageResult.class) {
        @Override
public void process(Object object, Object expectedFieldVal) { doProcessingInternal(((PageResult)object).getContent(), expectedFieldVal); } }, RESULT_MODEL(ResultModel.class) { @Override public void process(Object object, Object expectedFieldVal) { doProcessingInternal(((ResultModel)object).getContent(), expectedFieldVal); } }, POJO_RESULT(PojoResult.class) { @Override
public void process(Object object, Object expectedFieldVal) { doProcessingInternal(((PojoResult)object).getContent(), expectedFieldVal); } }, PAGE(Page.class) { @Override public void process(Object object, Object expectedFieldVal) { doProcessingInternal(((Page)object).getResult(), expectedFieldVal); } }, MAP(Map.class) { @Override public void process(Object object, Object expectedFieldVal) { Map<?, ?> map = (Map)object; if (CollectionUtils.isEmpty(map)) { return; } COLLECTION.process(map.keySet(), expectedFieldVal); COLLECTION.process(map.values(), expectedFieldVal); } }, COLLECTION(Collection.class) { @Override public void process(Object object, Object expectedFieldVal) { if (CollectionUtils.isEmpty((Collection)object)) { return; } for (Object element : (Collection)object) { doProcessingInternal(element, expectedFieldVal); } } }, PLAIN_OBJECT(Object.class) { @Override public void process(Object object, Object expectedFieldVal) { Class<?> clazz = object.getClass(); Field field = FIELD_MAPPINGS.get(clazz); if (field == null) { field = ReflectionUtils.findField(clazz, CAMPUS_ID); field.setAccessible(true); FIELD_MAPPINGS.put(clazz, field != null ? field : NON_CAMPUS_ID_FIELD); } else if (field == NON_CAMPUS_ID_FIELD) { return; } Object actualFieldValue = ReflectionUtils.getField(field, object); Assert.isTrue(expectedFieldVal.equals(actualFieldValue), clazz.getSimpleName()); } }; private Class<?> clazz; private static final String CAMPUS_ID = "campusId"; private static final Map<Class<?>, Field> FIELD_MAPPINGS = new ConcurrentHashMap<Class<?>, Field>(); private static final Field NON_CAMPUS_ID_FIELD = ReflectionUtils.findField(NestedObjectFieldCheckingProcessor.class, CAMPUS_ID); NestedObjectFieldCheckingProcessor(Class<?> clazz) { this.clazz = clazz; } /** * @param clazz * @return */ public static NestedObjectFieldCheckingProcessor valueOf(Class<?> clazz) { for (NestedObjectFieldCheckingProcessor processorEnum : NestedObjectFieldCheckingProcessor.values()) { if (processorEnum.clazz.isAssignableFrom(clazz)) { return processorEnum; } } return null; } /** * @param value * @param expectedFieldVal */ protected void doProcessingInternal(Object value, Object expectedFieldVal) { if (value != null) { valueOf(value.getClass()).process(value, expectedFieldVal); } } /** * @param object * @param expectedFieldVal */ public abstract void process(Object object, Object expectedFieldVal);

使用方法:

ListResult<User> userlist = new ListResult<User>(new ArrayList<User>);
NestedObjectFieldCheckingProcessor processor = NestedObjectFieldCheckingProcessor.valueOf(userlist.getClass());
processor.process(userlist,10);

在列舉外殼類裡定義了一個抽象方法process(…),這個方法就是業務處理邏輯,可以定義不同的邏輯,沒什麼限制。

每一個包裝類對應的列舉裡重寫了此抽象方法,在方法內先提取其下一層的包裝物件(若是多層包裝),然後下層物件傳遞給doProcessingInternal(…)方法,在doProcessingInternal(…)方法內再次獲取引數物件對應的列舉,再呼叫其process(…)方法。

每一個包裝類的process(…)方法主要是提取其下層物件或包裝物件,然後在用下層物件去尋找適配的列舉處理器,在呼叫其process(…)方法。

遞迴式的處理,每一個包裝類對應的列舉例項僅僅負責提取下一層的包裝物件,而不負責實際的業務處理。借鑑職責鏈設計模式,每個元素僅對待處理物件做職責範圍內的處理,然後將其傳遞給下一個元素

當然這個工具類有幾點需要注意:

  1. Enum的values()方法返回的是列舉例項列表,順序由上而下,所以我們定義包裝處理列舉時,要考慮其處理範圍,範圍越小的位置越靠前,處理範圍越大的越靠後,否則可能會出現意想不到的問題。
  2. 列舉例項的最後一個是Object對應的例項,它攔截所有的實際Model,也就是說到了這步包裝層應該都已經剝離了,它才是真正負責處理實際業務的,其他的實包裝類的處理列舉,而它是實際Model的處理列舉。
  3. 列舉裡可以重寫外部類的方法,和類方法重寫機制一樣。