最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java項(xiàng)目依賴包選擇具體實(shí)現(xiàn)類示例介紹

 更新時(shí)間:2022年12月22日 14:40:03   作者:一灰灰  
這篇文章主要為大家介紹了java項(xiàng)目依賴包選擇具體實(shí)現(xiàn)類示例介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

最近遇到一個(gè)需求場景,開源的工具包,新增了一個(gè)高級(jí)特性,會(huì)依賴json序列化工具,來做一些特殊操作;但是,這個(gè)輔助功能并不是必須的,也就是說對(duì)于使用這個(gè)工具包的業(yè)務(wù)方而言,正常使用完全不需要json相關(guān)的功能;如果我強(qiáng)引用某個(gè)json工具,一是對(duì)于不適用高級(jí)特性的用戶而言沒有必要;二則是我引入的json工具極有可能與使用者的不一致,會(huì)增加使用者的成本

因此我希望這個(gè)工具包對(duì)外提供時(shí),并不會(huì)引入具體的json工具依賴;也就是說maven依賴中的<scope>設(shè)置為provided;具體的json序列化的實(shí)現(xiàn),則取決于調(diào)用方自身引入了什么json工具

那么可以怎么實(shí)現(xiàn)上面這個(gè)方式呢?

1. 任務(wù)說明

上面的簡單的說了一下我們需要做的事情,接下來我們重點(diǎn)盤一下,我們到底是要干什么

核心訴求相對(duì)清晰

  • 不強(qiáng)引入某個(gè)json工具
  • 若需要使用高級(jí)特性,則直接使用當(dāng)前環(huán)境中已集成的json序列化工具;若沒有提供,則拋異常,不支持

對(duì)于上面這個(gè)場景,常年使用Spring的我們估計(jì)不會(huì)陌生,Spring集成了很多的第三方開源組件,根據(jù)具體的依賴來選擇最終的實(shí)現(xiàn),比如日志,可以是logback,也可以是log4j;比如redis操作,可以是jedis,也可以是lettuce

那么Spring是怎么實(shí)現(xiàn)的呢?

2.具體實(shí)現(xiàn)

在Spring中有個(gè)注解名為ConditionalOnClass,表示當(dāng)某個(gè)類存在時(shí),才會(huì)干某些事情(如初始化bean對(duì)象)

它是怎么是實(shí)現(xiàn)的呢?(感興趣的小伙伴可以搜索一下,或者重點(diǎn)關(guān)注下 SpringBootCondition 的實(shí)現(xiàn))

這里且拋開Spring的實(shí)現(xiàn)姿勢,我們采用傳統(tǒng)的實(shí)現(xiàn)方式,直接判斷是否有加載對(duì)應(yīng)的類,來判斷有沒有引入相應(yīng)的工具包

如需要判斷是否引入了gson包,則判斷ClassLoader是否有加載com.google.gson.Gson

public static boolean exist(String name) {
    try {
        return JsonUtil.class.getClassLoader().loadClass(name) != null;
    } catch (Exception e) {
        return false;
    }
}

上面這種實(shí)現(xiàn)方式就可以達(dá)到我們的效果了;接下來我們參考下Spring的ClassUtils實(shí)現(xiàn),做一個(gè)簡單的封裝,以判斷是否存在某個(gè)類

// 這段代碼來自Spring
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Map;
/**
 * @author Spring
 */
public abstract class ClassUtils {
    private static final Map<String, Class<?>> primitiveTypeNameMap = new HashMap(32);
    private static final Map<String, Class<?>> commonClassCache = new HashMap(64);
    private ClassUtils() {
    }
    public static boolean isPresent(String className) {
        try {
            forName(className, getDefaultClassLoader());
            return true;
        } catch (IllegalAccessError var3) {
            throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + className + "]: " + var3.getMessage(), var3);
        } catch (Throwable var4) {
            return false;
        }
    }
    public static boolean isPresent(String className, ClassLoader classLoader) {
        try {
            forName(className, classLoader);
            return true;
        } catch (IllegalAccessError var3) {
            throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + className + "]: " + var3.getMessage(), var3);
        } catch (Throwable var4) {
            return false;
        }
    }
    public static Class<?> forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError {
        Class<?> clazz = resolvePrimitiveClassName(name);
        if (clazz == null) {
            clazz = (Class) commonClassCache.get(name);
        }
        if (clazz != null) {
            return clazz;
        } else {
            Class elementClass;
            String elementName;
            if (name.endsWith("[]")) {
                elementName = name.substring(0, name.length() - "[]".length());
                elementClass = forName(elementName, classLoader);
                return Array.newInstance(elementClass, 0).getClass();
            } else if (name.startsWith("[L") && name.endsWith(";")) {
                elementName = name.substring("[L".length(), name.length() - 1);
                elementClass = forName(elementName, classLoader);
                return Array.newInstance(elementClass, 0).getClass();
            } else if (name.startsWith("[")) {
                elementName = name.substring("[".length());
                elementClass = forName(elementName, classLoader);
                return Array.newInstance(elementClass, 0).getClass();
            } else {
                ClassLoader clToUse = classLoader;
                if (classLoader == null) {
                    clToUse = getDefaultClassLoader();
                }
                try {
                    return Class.forName(name, false, clToUse);
                } catch (ClassNotFoundException var9) {
                    int lastDotIndex = name.lastIndexOf(46);
                    if (lastDotIndex != -1) {
                        String innerClassName = name.substring(0, lastDotIndex) + '$' + name.substring(lastDotIndex + 1);
                        try {
                            return Class.forName(innerClassName, false, clToUse);
                        } catch (ClassNotFoundException var8) {
                        }
                    }
                    throw var9;
                }
            }
        }
    }
    public static Class<?> resolvePrimitiveClassName(String name) {
        Class<?> result = null;
        if (name != null && name.length() <= 8) {
            result = (Class) primitiveTypeNameMap.get(name);
        }
        return result;
    }
    public static ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        } catch (Throwable var3) {
        }
        if (cl == null) {
            cl = ClassUtils.class.getClassLoader();
            if (cl == null) {
                try {
                    cl = ClassLoader.getSystemClassLoader();
                } catch (Throwable var2) {
                }
            }
        }
        return cl;
    }
}

工具類存在之后,我們實(shí)現(xiàn)一個(gè)簡單的json工具類,根據(jù)已有的json包來選擇具體的實(shí)現(xiàn)

public class JsonUtil {
    private static JsonApi jsonApi;
    private static void initJsonApi() {
        if (jsonApi == null) {
            synchronized (JsonUtil.class) {
                if (jsonApi == null) {
                    if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", JsonUtil.class.getClassLoader())) {
                        jsonApi = new JacksonImpl();
                    } else if (ClassUtils.isPresent("com.google.gson.Gson", JsonUtil.class.getClassLoader())) {
                        jsonApi = new GsonImpl();
                    } else if (ClassUtils.isPresent("com.alibaba.fastjson.JSONObject", JsonUtil.class.getClassLoader())) {
                        jsonApi = new JacksonImpl();
                    } else {
                        throw new UnsupportedOperationException("no json framework to deserialize string! please import jackson|gson|fastjson");
                    }
                }
            }
        }
    }
    /**
     * json轉(zhuǎn)實(shí)體類,會(huì)根據(jù)當(dāng)前已有的json框架來執(zhí)行反序列化
     *
     * @param str
     * @param t
     * @param <T>
     * @return
     */
    public static <T> T toObj(String str, Class<T> t) {
        initJsonApi();
        return jsonApi.toObj(str, t);
    }
    public static <T> String toStr(T t) {
        initJsonApi();
        return jsonApi.toStr(t);
    }
}

上面的實(shí)現(xiàn)中,根據(jù)已有的json序列化工具,選擇具體的實(shí)現(xiàn)類,我們定義了一個(gè)JsonApi接口,然后分別gson,jackson,fastjson給出默認(rèn)的實(shí)現(xiàn)類

public interface JsonApi {
    <T> T toObj(String str, Class<T> clz);
    <T> String toStr(T t);
}
public class FastjsonImpl implements JsonApi {
    public <T> T toObj(String str, Class<T> clz) {
        return JSONObject.parseObject(str, clz);
    }
    public <T> String toStr(T t) {
        return JSONObject.toJSONString(t);
    }
}
public class GsonImpl implements JsonApi {
    private static final Gson gson = new Gson();
    public <T> T toObj(String str, Class<T> t) {
        return gson.fromJson(str, t);
    }
    public <T> String toStr(T t) {
        return gson.toJson(t);
    }
}
public class JacksonImpl implements JsonApi{
    private static final ObjectMapper jsonMapper = new ObjectMapper();
    public <T> T toObj(String str, Class<T> clz) {
        try {
            return jsonMapper.readValue(str, clz);
        } catch (Exception e) {
            throw new UnsupportedOperationException(e);
        }
    }
    public <T> String toStr(T t) {
        try {
            return jsonMapper.writeValueAsString(t);
        } catch (Exception e) {
            throw new UnsupportedOperationException(e);
        }
    }
}

最后的問題來了,如果調(diào)用方并沒有使用上面三個(gè)序列化工具,而是使用其他的呢,可以支持么?

既然我們定義了一個(gè)JsonApi,那么是不是可以由用戶自己來實(shí)現(xiàn)接口,然后自動(dòng)選擇它呢?

現(xiàn)在的問題就是如何找到用戶自定義的接口實(shí)現(xiàn)了

3. 擴(kuò)展機(jī)制

對(duì)于SPI機(jī)制比較熟悉的小伙伴可能非常清楚,可以通過在配置目錄META-INF/services/下新增接口文件,內(nèi)容為實(shí)現(xiàn)類的全路徑名稱,然后通過 ServiceLoader.load(JsonApi.class) 的方式來獲取所有實(shí)現(xiàn)類

除了SPI的實(shí)現(xiàn)方式之外,另外一個(gè)策略則是上面提到的Spring的實(shí)現(xiàn)原理,借助字節(jié)碼來處理(詳情原理后面專文說明)

當(dāng)然也有更容易想到的策略,掃描包路徑下的class文件,遍歷判斷是否為實(shí)現(xiàn)類(額外注意jar包內(nèi)的實(shí)現(xiàn)類場景)

接下來以SPI的方式來介紹下擴(kuò)展實(shí)現(xiàn)方式,首先初始化JsonApi的方式改一下,優(yōu)先使用用戶自定義實(shí)現(xiàn)

private static void initJsonApi() {
    if (jsonApi == null) {
        synchronized (JsonUtil.class) {
            if (jsonApi == null) {
                ServiceLoader<JsonApi> loader = ServiceLoader.load(JsonApi.class);
                for (JsonApi value : loader) {
                    jsonApi = value;
                    return;
                }
                if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", JsonUtil.class.getClassLoader())) {
                    jsonApi = new JacksonImpl();
                } else if (ClassUtils.isPresent("com.google.gson.Gson", JsonUtil.class.getClassLoader())) {
                    jsonApi = new GsonImpl();
                } else if (ClassUtils.isPresent("com.alibaba.fastjson.JSONObject", JsonUtil.class.getClassLoader())) {
                    jsonApi = new JacksonImpl();
                } else{
                    throw new UnsupportedOperationException("no json framework to deserialize string! please import jackson|gson|fastjson");
                }
            }
        }
    }
}

對(duì)于使用者而言,首先是實(shí)現(xiàn)接口

package com.github.hui.quick.plugin.test;
import com.github.hui.quick.plugin.qrcode.util.json.JsonApi;
public class DemoJsonImpl implements JsonApi {
    @Override
    public <T> T toObj(String str, Class<T> clz) {
        // ...
    }
    @Override
    public <T> String toStr(T t) {
        // ...
    }
}

接著就是實(shí)現(xiàn)定義, resources/META-INF/services/ 目錄下,新建文件名為 com.github.hui.quick.plugin.qrcode.util.json.JsonApi

內(nèi)容如下

com.github.hui.quick.plugin.test.DemoJsonImpl

然后完工~

小結(jié)

主要介紹一個(gè)小的知識(shí)點(diǎn),如何根據(jù)應(yīng)用已有的jar包來選擇具體的實(shí)現(xiàn)類的方式;本文介紹的方案是通過ClassLoader來嘗試加載對(duì)應(yīng)的類,若能正常加載,則認(rèn)為有;否則認(rèn)為沒有;這種實(shí)現(xiàn)方式雖然非常簡單,但是請注意,它是有缺陷的,至于缺陷是啥...

除此之外,也可以考慮通過字節(jié)碼的方式來判斷是否有某個(gè)類,或者獲取某個(gè)接口的實(shí)現(xiàn);文中最后拋出了一個(gè)問題,如何獲取接口的所有實(shí)現(xiàn)類

常見的方式有下面三類(具體介紹了SPI的實(shí)現(xiàn)姿勢,其他的兩種感興趣的可以搜索一下)

  • SPI定義方式
  • 掃描包路徑
  • 字節(jié)碼方式(如Spring,如Tomcat的@HandlesTypes)

以上就是java項(xiàng)目依賴包選擇具體實(shí)現(xiàn)類示例介紹的詳細(xì)內(nèi)容,更多關(guān)于java項(xiàng)目依賴包實(shí)現(xiàn)類的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

嫩江县| 灌南县| 香格里拉县| 林周县| 长汀县| 册亨县| 曲水县| 巴东县| 扎赉特旗| 即墨市| 抚州市| 南岸区| 石台县| 泉州市| 四川省| 屏东县| 台中县| 甘孜县| 梨树县| 当涂县| 宁陕县| 东海县| 巴彦淖尔市| 六安市| 东安县| 南溪县| 巫溪县| 赫章县| 东兴市| 神农架林区| 鲁甸县| 绿春县| 丰台区| 桃园市| 宣汉县| 河南省| 广昌县| 蕲春县| 潮安县| 龙门县| 康平县|