Mybatis筆記3-映射體系

Mybatis映射體系

Mybatis映射體系

MetaObject

MetaObject應(yīng)用

查找屬性:
忽略大小寫,支持駝峰训唱,支持子屬性

獲取屬性值:
基于點(diǎn)獲取子屬性,如user.name
基于索引獲取屬性列表值,如users[1].id
基于key獲取map值至壤,如user[name]

設(shè)置屬性
可設(shè)置子屬性值
持自動(dòng)創(chuàng)建子屬性對(duì)象(必須有無參構(gòu)造器,且不能是集合)

public class MetaObjectTest {

    static class Address {
        private String province;
        private String city;
        private String county;
        private String addressDetail;
        
        public String getProvince() { return province; }
        public void setProvince(String province) { this.province = province; }
        public String getCity() { return city; }
        public void setCity(String city) { this.city = city; }
        public String getCounty() { return county; }
        public void setCounty(String county) { this.county = county; }
        public String getAddressDetail() { return addressDetail; }
        public void setAddressDetail(String addressDetail) { this.addressDetail = addressDetail; }
    }

    private String name;
    private Address address;
    private List<Address> addressList;
    private Map<String, Object> additionParams;
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public Address getAddress() { return address; }
    public void setAddress(Address address) { this.address = address; }
    public List<Address> getAddressList() { return addressList; }
    public void setAddressList(List<Address> addressList) { this.addressList = addressList; }
    public Map<String, Object> getAdditionParams() { return additionParams; }
    public void setAdditionParams(Map<String, Object> additionParams) {
        this.additionParams = additionParams;
    }

    public static void main(String[] args) {
        Configuration configuration = new Configuration();
        MetaObjectTest metaObjectTest = new MetaObjectTest();
        // 使用MetaObject解析order對(duì)象
        MetaObject metaobject = configuration.newMetaObject(metaObjectTest);

        // 通過MetaObject設(shè)置對(duì)象屬性值
        metaObjectTest.setName("oldName");
        System.out.println(metaObjectTest.getName());
        metaobject.setValue("name", "newName");
        // 通過輸出結(jié)果枢纠,可以看到orderNo被MetaObject設(shè)置的值覆蓋
        System.out.println(metaObjectTest.getName());

        System.out.println("--->");

        // 通過MetaObject獲取對(duì)象屬性值
        System.out.println("通過MetaObject獲取對(duì)象屬性值:" + metaobject.getValue("name"));

        System.out.println("--->");

        // 通過MetaObject設(shè)置嵌套屬性的值像街,它會(huì)創(chuàng)建一個(gè)空的嵌套對(duì)象,并為其屬性賦值
        metaobject.setValue("address.addressDetail", "詳細(xì)地址");
        System.out.println("通過MetaObject自動(dòng)創(chuàng)建內(nèi)嵌對(duì)象晋渺,賦值之后獲取 : " 
                           + metaobject.getValue("address.addressDetail"));

        System.out.println("--->");

        System.out.println("通過駝峰命名法獲取屬性名稱 :" 
                           + metaobject.findProperty("address.address_detail", true));

        System.out.println("--->");

        // MetaObject無法自動(dòng)創(chuàng)建List類型的內(nèi)嵌對(duì)象镰绎,需要手動(dòng)設(shè)置,如:
        Address address = new Address();
        address.setProvince("福建省");
        List<Address> addressList = new ArrayList<>();
        addressList.add(address);
        metaobject.setValue("addressList", addressList);
        metaobject.setValue("addressList[0]", address);
        // 訪問集合對(duì)象成員的方式(也可以訪問成員的屬性值)
        System.out.println("通過MetaObject獲取List類型成員屬性 : " 
                           + metaobject.getValue("addressList[0].province"));

        System.out.println("--->");

        // MetaObject同樣無法自動(dòng)創(chuàng)建Map類型的內(nèi)嵌對(duì)象木西,需要手動(dòng)設(shè)置跟狱,如:
        metaobject.setValue("additionParams", new HashMap<>());
        // 設(shè)置map屬性的鍵值對(duì),以及訪問鍵值
        metaobject.setValue("additionParams[name]", "名稱");
        System.out.println("通過MetaObject獲取Map類型成員屬性 : " 
                           + metaobject.getValue("additionParams[name]"));
    }
}

MetaObject源碼解析

public class MetaObject {
 
    // ...
    
    // 以解析的屬性名稱name=comments[0].user.name為例
    public Object getValue(String name) {
        // 先通過屬性分詞器解析全屬性名稱
        PropertyTokenizer prop = new PropertyTokenizer(name);
        // 存在子屬性u(píng)ser.name
        if (prop.hasNext()) {
            // 如果存在子屬性户魏,則通過IndexedName再次創(chuàng)建MetaObject
            // IndexedName的值依次為: comments[0]驶臊、user
            MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
            if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
                return null;
            } else {
                return metaValue.getValue(prop.getChildren());
            }
        } 
        // 如果不存在子屬性,比如解析到name屬性時(shí)叼丑,不存在子屬性关翎,通過反射獲取屬性值(可參考下面BeanWrapper類源碼)
        else {
            return objectWrapper.get(prop);
        }
    }
    
    // 以解析comments[0].user.name為例
    public MetaObject metaObjectForProperty(String name) {
        // 這里會(huì)先后獲取comments[0]、user兩個(gè)屬性的值
        Object value = getValue(name);
        // 將上面通過某個(gè)屬性獲取到的對(duì)象鸠信,繼續(xù)將這些對(duì)象解析為MetaObject對(duì)象
        return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
    }
    
}
PropertyTokenizer

屬性名稱分詞器纵寝,它首先會(huì)根據(jù)"."符號(hào),將屬性名稱拆分星立,比如comments[0].user.name通過多次的屬性名稱分詞爽茴,會(huì)被劃分為comments[0]、user绰垂、name三部分室奏,再根據(jù)這三部分創(chuàng)建各自的MetaObject元對(duì)象,進(jìn)一步解析子屬性劲装,源碼如下:

public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
    private String name;
    private final String indexedName;
    // index之所以為String類型胧沫,是因?yàn)槌隽薈ollection是數(shù)值類型,還有Map類型采用String類型的key
    private String index;
    private final String children;
    
    // 假定入?yún)ullname屬性全名為:comments[0].user.name
    // 最終的解析結(jié)果為:
    // name = comments
    // indexedName = comments[0]
    // index = 0
    // children = user.name
    public PropertyTokenizer(String fullname) {
        // 首先占业,獲取第一個(gè).的偏移量
        int delim = fullname.indexOf('.');
        // 存在.的情況
        if (delim > -1) {
            // 先將name賦值為第一個(gè).號(hào)之前的全部字符绒怨,這里為comments[0]
            name = fullname.substring(0, delim);
            // childre賦值為第一個(gè).之后的全部字符串,這里為user.name
            children = fullname.substring(delim + 1);
        } else {
            // 不存在.的字符串谦疾,直接將name賦值為整個(gè)入?yún)ullname
            name = fullname;
            children = null;
        }
        // 將comments[0]賦值給indexedName
        indexedName = name;
        // 判斷name是否存在[符號(hào)南蹂,存在的話將name賦值為[]符號(hào)外的字符串,這里將name重新賦值為comments
        delim = name.indexOf('[');
        if (delim > -1) {
            // index賦值為[]內(nèi)的下標(biāo)數(shù)值念恍,如果不存在[]六剥,index為默認(rèn)值null
            index = name.substring(delim + 1, name.length() - 1);
            name = name.substring(0, delim);
        }
    }

    // ...
    
    //hasNext判斷是否存在子屬性
    public boolean hasNext() {
        return children != null;
    }
    
    // 將子屬性再次通過屬性分詞器分析
    public PropertyTokenizer next() {
        return new PropertyTokenizer(children);
    }
}
BeanWrapper

封裝目標(biāo)對(duì)象和目標(biāo)對(duì)象類元數(shù)據(jù)晚顷,可以通過它反射獲取屬性值,這個(gè)類只能獲取頂層屬性的值仗考,比如通過BeanWrapper獲取屬性名稱comments[0].user.name音同,它僅能返回comments[0]的值。

public class BeanWrapper extends BaseWrapper {
    
    private final Object object;
    private final MetaClass metaClass;
    
    // ...
    
    public Object get(PropertyTokenizer prop) {
        // 通過屬性分詞器判斷該屬性是否是一個(gè)集合秃嗜,如果屬性分詞器記錄的index屬性為null权均,表示當(dāng)前要解析的屬性不是集合
        if (prop.getIndex() != null) {
            // 繼承父類BaseWrapper的方法(詳見下方源碼)
            Object collection = resolveCollection(prop, object);
            // 繼承父類BaseWrapper的方法(詳見下方源碼)
            return getCollectionValue(prop, collection);
        } else {
            // 解析非集合類型的屬性值(詳見下方源碼)
            return getBeanProperty(prop, object);
        }
    }
    
    private Object getBeanProperty(PropertyTokenizer prop, Object object) {
        try {
            // 通過MetaClass獲取屬性值對(duì)應(yīng)的MethodInvoker,內(nèi)部封裝了JDK反射的method對(duì)象(詳見下方源碼)
            // 注意這里是通過屬性分詞器的name屬性獲取屬性值
            // 當(dāng)嘗試通過BeanWrapper獲取屬性值锅锨,且傳入的屬性為user.name時(shí)叽赊,這里的prop.getName()將會(huì)獲取到user
            //(參看上面PropertyTokenizer如何解析屬性名稱)
            // 因此其實(shí)獲取到的時(shí)user屬性值,無法獲取user對(duì)象的name屬性值
            Invoker method = metaClass.getGetInvoker(prop.getName());
            try {
                // 通過反射獲取屬性值
                return method.invoke(object, NO_ARGUMENTS);
            } catch (Throwable t) {
                throw ExceptionUtil.unwrapThrowable(t);
            }
        } catch (RuntimeException e) {
            throw e;
        } catch (Throwable t) {
            // ...
        }
    }
    
}
MethodInvoker

封裝了類的Setter和Getter必搞,通過它類反射獲取屬性值或者設(shè)置屬性值

public class MethodInvoker implements Invoker {
    
    private final Class<?> type;
    private final Method method;
    
    public MethodInvoker(Method method) {
        this.method = method;
        // 兼容Getter和Setter方法必指,Setter根據(jù)入?yún)㈩愋蜎Q定,Getter根據(jù)出參類型決定
        if (method.getParameterTypes().length == 1) {
            type = method.getParameterTypes()[0];
        } else {
            type = method.getReturnType();
        }
    }
    
    // 反射調(diào)用Getter或者Setter方法
    public Object invoke(Object target, Object[] args) 
            throws IllegalAccessException, InvocationTargetException {
        return method.invoke(target, args);
    }
    
    // 返回當(dāng)前操作的屬性類型
    public Class<?> getType() {
        return type;
    }
}
BaseWrapper

為接口ObjectWrapper提供的封裝了處理集合公共方法的抽象類

public abstract class BaseWrapper implements ObjectWrapper {
    
    protected static final Object[] NO_ARGUMENTS = new Object[0];
    protected final MetaObject metaObject;
    
    protected BaseWrapper(MetaObject metaObject) {
        this.metaObject = metaObject;
    }
    
    protected Object resolveCollection(PropertyTokenizer prop, Object object) {
        // 屬性名稱為空直接返回BeanWrapper封裝的目標(biāo)對(duì)象Object
        if ("".equals(prop.getName())) {
            return object;
        } else {
            // 若解析的屬性是comments[0],將會(huì)返回comments屬性名對(duì)應(yīng)的對(duì)象值
            return metaObject.getValue(prop.getName());
        }
    }
    
    // 從集合中獲取某個(gè)成員
    protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
        // 若屬性值為Map類型恕洲,PropertyTokenizer解析得到的index為key名稱
        if (collection instanceof Map) {
            return ((Map) collection).get(prop.getIndex());
        } 
        // 從List塔橡、數(shù)組類型取值
        else {
            int i = Integer.parseInt(prop.getIndex());
            if (collection instanceof List) {
                return ((List) collection).get(i);
            } else if (collection instanceof Object[]) {
                return ((Object[]) collection)[i];
            } else if (collection instanceof char[]) {
                return ((char[]) collection)[i];
            } else if (collection instanceof boolean[]) {
                return ((boolean[]) collection)[i];
            } else if (collection instanceof byte[]) {
                return ((byte[]) collection)[i];
            } else if (collection instanceof double[]) {
                return ((double[]) collection)[i];
            } else if (collection instanceof float[]) {
                return ((float[]) collection)[i];
            } else if (collection instanceof int[]) {
                return ((int[]) collection)[i];
            } else if (collection instanceof long[]) {
                return ((long[]) collection)[i];
            } else if (collection instanceof short[]) {
                return ((short[]) collection)[i];
            } else {
                // ...
            }
        }
    }
    
    // 為集合屬性設(shè)置值
    protected void setCollectionValue(PropertyTokenizer prop, Object collection, Object value) {
        // Map類型,以key為index值霜第,value為入?yún)alue設(shè)置一個(gè)Map.Entry
        if (collection instanceof Map) {
            ((Map) collection).put(prop.getIndex(), value);
        } 
        // 為L(zhǎng)ist葛家、數(shù)組類型賦值
        else {
            int i = Integer.parseInt(prop.getIndex());
            if (collection instanceof List) {
                ((List) collection).set(i, value);
            } else if (collection instanceof Object[]) {
                ((Object[]) collection)[i] = value;
            } else if (collection instanceof char[]) {
                ((char[]) collection)[i] = (Character) value;
            } else if (collection instanceof boolean[]) {
                ((boolean[]) collection)[i] = (Boolean) value;
            } else if (collection instanceof byte[]) {
                ((byte[]) collection)[i] = (Byte) value;
            } else if (collection instanceof double[]) {
                ((double[]) collection)[i] = (Double) value;
            } else if (collection instanceof float[]) {
                ((float[]) collection)[i] = (Float) value;
            } else if (collection instanceof int[]) {
                ((int[]) collection)[i] = (Integer) value;
            } else if (collection instanceof long[]) {
                ((long[]) collection)[i] = (Long) value;
            } else if (collection instanceof short[]) {
                ((short[]) collection)[i] = (Short) value;
            } else {
                // ...
            }
        }
    }
}

Reflector

Reflector可以解析一個(gè)類的所有屬性,記錄所有的Setter和Getter方法

public class Reflector {
    
    // 解析的類
    private final Class<?> type;
    private final String[] readablePropertyNames;
    private final String[] writeablePropertyNames;
    // 通過類的Method解析
    // 封裝所有g(shù)et開頭的方法泌类,key為去除get前綴癞谒,首字母小寫的方法名稱,value為封裝Method的MethodInvoker
    // 通過類的Field解析
    // 封裝所有屬性的賦值方法刃榨,key為屬性名稱弹砚,value為屬性的賦值方法SetFieldInvoker
    private final Map<String, Invoker> setMethods = new HashMap<String, Invoker>();
    // 封裝所有is、get開頭的方法枢希,key為去除is桌吃、get前綴,首字母小寫的方法名稱晴玖,value為封裝Method的MethodInvoker
    // 通過類的Field解析
    // 封裝所有屬性的賦值方法读存,key為屬性名稱,value為屬性的賦值方法GetFieldInvoker
    private final Map<String, Invoker> getMethods = new HashMap<String, Invoker>();
    // 封裝所有set開頭呕屎,被認(rèn)定為Setter方法的屬性類型,以及所有Field的類型
    // 如果是List<T>返回泛型類型敬察,如果是T[]同樣返回泛型
    private final Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();
    // 封裝所有is秀睛、get開頭,被認(rèn)定為Getter方法的屬性類型莲祸,以及所有Filed的類型
    // 如果是List<T>返回泛型類型蹂安,如果是T[]同樣返回泛型
    private final Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();
    // 解析得到的無參構(gòu)造器
    private Constructor<?> defaultConstructor;
    // 保存所有可以取值椭迎、設(shè)置的屬性名稱的并集,key為屬性名稱全大寫字符串田盈,value為屬性名稱
    private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>();
    
    public Reflector(Class<?> clazz) {
        type = clazz;
        // 獲取無參構(gòu)造器
        addDefaultConstructor(clazz);
        // 添加所有合法的Getter
        addGetMethods(clazz);
        // 添加所有合法的Setter
        addSetMethods(clazz);
        // 通過類的Field添加另外一批Getter和Setter方法
        addFields(clazz);
        // 使用數(shù)組保存上述通過Method和Filed解析得到的可以獲取值的屬性名稱
        readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
        // 使用數(shù)組保存上述通過Method和Filed解析得到的可以設(shè)置值的屬性名稱
        writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
        // 將所有可以賦值畜号、可以取值的屬性名稱,轉(zhuǎn)換為大寫字符串保存到一個(gè)Map中
        for (String propName : readablePropertyNames) {
            caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }
        for (String propName : writeablePropertyNames) {
            caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }
    }
    
    // 獲取默認(rèn)無參構(gòu)造器
    private void addDefaultConstructor(Class<?> clazz) {
        // 獲取當(dāng)前類型定義的全部構(gòu)造器
        Constructor<?>[] consts = clazz.getDeclaredConstructors();
        // 遍歷所有構(gòu)造器允瞧,找到入?yún)?的構(gòu)造器简软,并將其設(shè)置為可訪問的
        for (Constructor<?> constructor : consts) {
            if (constructor.getParameterTypes().length == 0) {
                if (canAccessPrivateMethods()) {
                    try {
                        constructor.setAccessible(true);
                    } catch (Exception e) {
                    }
                }
                if (constructor.isAccessible()) {
                    this.defaultConstructor = constructor;
                }
            }
        }
    }
    
    private void addGetMethods(Class<?> cls) {
        // 保存解析得到同名屬性,卻有多個(gè)方法的沖突Map
        Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
        // 獲取cls(包括父類述暂、接口定義的)上所有的方法
        Method[] methods = getClassMethods(cls);
        for (Method method : methods) {
            // Getter方法應(yīng)該沒有入?yún)⒈陨挥涗浻袇?shù)的方法
            if (method.getParameterTypes().length > 0) {
                continue;
            }
            
            // 如果方法名稱是get或者is為前綴,并且前綴之后還有字符畦韭,將后面的字符串作為屬性名稱
            String name = method.getName();
            if ((name.startsWith("get") && name.length() > 3)
                || (name.startsWith("is") && name.length() > 2)) {
                // 刪除方法名稱前綴get疼蛾、is、set艺配,并將剩下的第一個(gè)字符變?yōu)樾?                // 如:getUsername => username
                name = PropertyNamer.methodToProperty(name);
                // 保存得到的method察郁,映射關(guān)系為 屬性名 -> List<Method> ,避免同一個(gè)name獲得多個(gè)方法
                addMethodConflict(conflictingGetters, name, method);
            }
        }
        // 為每個(gè)屬性選定唯一一個(gè)Getter方法(方法詳情略)
        // 規(guī)則如下:
        // 遍歷每個(gè)方法的返回類型转唉,
        // 1. 存在返回類型相同的多個(gè)方法
        //      1.1 如果不是boolean類型屬性皮钠,存在多個(gè)方法返回類型一致,拋出異常
        //      1.2 如果是boolean類型屬性酝掩,以最后一個(gè)is開頭的方法名稱作為該屬性的Getter方法
        // 2. 如果多個(gè)Getter方法返回類型不一致鳞芙,以最小的子類作為Getter方法
        // 3. 解析得到的屬性名稱不能是$開頭、serialVersionUID期虾、class這三種形式的名稱原朝,不添加Getter方法
        // 4. 將得到的Getter方法,封裝為MethodInvoker對(duì)象
        resolveGetterConflicts(conflictingGetters);
    }
    
    // ...
    
    private void addSetMethods(Class<?> cls) {
        Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
        // 獲取cls(包括父類镶苞、接口定義的)上所有的方法
        Method[] methods = getClassMethods(cls);
        // 遍歷所有以set開頭喳坠,并且set之后還有字符的方法名稱,且方法只有一個(gè)入?yún)?        for (Method method : methods) {
            String name = method.getName();
            if (name.startsWith("set") && name.length() > 3) {
                if (method.getParameterTypes().length == 1) {
                    // 刪除方法名稱前綴get茂蚓、is壕鹉、set,并將剩下的第一個(gè)字符變?yōu)樾?                    // 如:getUsername => username
                    name = PropertyNamer.methodToProperty(name);
                    // 保存得到的method聋涨,映射關(guān)系為 屬性名 -> List<Method> 晾浴,避免同一個(gè)name獲得多個(gè)方法
                    addMethodConflict(conflictingSetters, name, method);
                }
            }
        }
        // 為每個(gè)屬性選定唯一一個(gè)Setter方法(方法詳情略)
        // 規(guī)則如下:
        // 遍歷屬性名稱的所有方法
        // 1. 若通過屬性名稱查詢Getter方法的返回類型和當(dāng)前方法的入?yún)㈩愋鸵恢拢x定該方法作為Setter方法
        // 2. 若通過屬性名稱查詢Getter方法的返回類型和當(dāng)前方法的入?yún)㈩愋筒灰恢码拱祝x取入?yún)㈩愋褪亲钭宇惖姆椒?        resolveSetterConflicts(conflictingSetters);
    }
    
    // ...
    
    // 添加當(dāng)前類屬性的訪問和賦值方法
    private void addFields(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (canAccessPrivateMethods()) {
                try {
                    field.setAccessible(true);
                } catch (Exception e) {
                }
            }
            if (field.isAccessible()) {
                if (!setMethods.containsKey(field.getName())) {
                    int modifiers = field.getModifiers();
                    // 屬性的set方法不能同時(shí)被static和final修飾
                    if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
                        // 在全局屬性setMethods集合中脊凰,添加為該屬性賦值的SetFieldInvoker對(duì)象
                        addSetField(field);
                    }
                }
                // 在全局屬性getMethods集合中,添加為該屬性賦值的GetFieldInvoker對(duì)象
                if (!getMethods.containsKey(field.getName())) {
                    addGetField(field);
                }
            }
        }
        // 進(jìn)一步解析父類的屬性
        if (clazz.getSuperclass() != null) {
            addFields(clazz.getSuperclass());
        }
    }
    
    // ...
    
    /*
     * TODO 這個(gè)方法在類中多次使用茂腥,為什么不采取一個(gè)集合保存解析得到的Method[]狸涌?切省??帕胆?朝捆??
     *
     * 這個(gè)方法會(huì)返回當(dāng)前類懒豹,當(dāng)前類繼承的父類芙盘,當(dāng)前類實(shí)現(xiàn)的接口中所有的方法,
     * 之所以不簡(jiǎn)單的采用Class.getMethods()歼捐,是因?yàn)橐蚕氆@取到private訪問權(quán)限的方法
     */
    private Method[] getClassMethods(Class<?> cls) {
        Map<String, Method> uniqueMethods = new HashMap<String, Method>();
        Class<?> currentClass = cls;
        while (currentClass != null && currentClass != Object.class) {
            // 根據(jù)一定規(guī)則將方法的明明標(biāo)記作為key何陆,淘汰所有JVM自己生成的橋接方法(橋接方法用于處理泛型方法)
            // 如果存在方法明明標(biāo)記一致的情況,考慮存在重載的方法豹储,以最后一個(gè)方法為準(zhǔn)
            addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
            // 同時(shí)也去獲取接口的方法贷盲,因?yàn)楫?dāng)前cls入?yún)⒖赡苁莻€(gè)抽象類
            Class<?>[] interfaces = currentClass.getInterfaces();
            for (Class<?> anInterface : interfaces) {
                addUniqueMethods(uniqueMethods, anInterface.getMethods());
            }
            // 繼續(xù)獲取父類的方法
            currentClass = currentClass.getSuperclass();
        }
        Collection<Method> methods = uniqueMethods.values();
        return methods.toArray(new Method[methods.size()]);
    }
    
    // ... 
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市剥扣,隨后出現(xiàn)的幾起案子巩剖,更是在濱河造成了極大的恐慌,老刑警劉巖钠怯,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件佳魔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡晦炊,警方通過查閱死者的電腦和手機(jī)鞠鲜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來断国,“玉大人贤姆,你說我怎么就攤上這事∥瘸模” “怎么了霞捡?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)薄疚。 經(jīng)常有香客問我碧信,道長(zhǎng),這世上最難降的妖魔是什么街夭? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任砰碴,我火速辦了婚禮,結(jié)果婚禮上板丽,老公的妹妹穿的比我還像新娘衣式。我一直安慰自己,他們只是感情好檐什,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布碴卧。 她就那樣靜靜地躺著,像睡著了一般乃正。 火紅的嫁衣襯著肌膚如雪住册。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天瓮具,我揣著相機(jī)與錄音荧飞,去河邊找鬼。 笑死名党,一個(gè)胖子當(dāng)著我的面吹牛叹阔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播传睹,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼耳幢,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了欧啤?” 一聲冷哼從身側(cè)響起睛藻,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎邢隧,沒想到半個(gè)月后店印,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡倒慧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年按摘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纫谅。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡炫贤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出系宜,到底是詐尸還是另有隱情照激,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布盹牧,位于F島的核電站俩垃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏汰寓。R本人自食惡果不足惜口柳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望有滑。 院中可真熱鬧跃闹,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至找默,卻和暖如春艇劫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惩激。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工店煞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人风钻。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓顷蟀,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親骡技。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鸣个,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容