欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JVM自定義類加載器在代碼擴(kuò)展性實(shí)踐分享

 更新時間:2022年06月22日 09:38:49   作者:? vivo互聯(lián)網(wǎng)技術(shù)?  ?  
這篇文章主要介紹了JVM自定義類加載器在代碼擴(kuò)展性實(shí)踐分享,一個類型從被加載到虛擬機(jī)內(nèi)存中開始,到卸載出內(nèi)存為止,它的整個生命周期將會經(jīng)歷加載、驗(yàn)證、準(zhǔn)備、解析、初始化 、使用和卸載七個階段,其中驗(yàn)證、準(zhǔn)備、解析三個部分統(tǒng)稱為連接

一、背景

名單管理系統(tǒng)是手機(jī)上各個模塊將需要管控的應(yīng)用配置到文件中,然后下發(fā)到手機(jī)上進(jìn)行應(yīng)用管控的系統(tǒng),比如各個應(yīng)用的耗電量管控;各個模塊的管控應(yīng)用文件考慮到安全問題,有自己的不同的加密方式,按照以往的經(jīng)驗(yàn),我們可以利用模板方法+工廠模式來根據(jù)模塊的類型來獲取到不同的加密方法。

代碼類層次結(jié)構(gòu)示意如下:

獲取不同加密方法的類結(jié)構(gòu)圖:

利用工廠模式和模板方法模式,在有新的加密方法時,我們可以通過添加新的handler來滿足"對修改關(guān)閉,對擴(kuò)展開放"的原則,但是這種方式不可避免的需要修改代碼和需要重新發(fā)版本和上線。那么有沒有更好的方式能夠去解決這個問題,這里就是我們今天要重點(diǎn)講的主題。

二、類加載的時機(jī)

一個類型從被加載到虛擬機(jī)內(nèi)存中開始,到卸載出內(nèi)存為止,它的整個生命周期將會經(jīng)歷加載 (Loading)、驗(yàn)證(Verification)、準(zhǔn)備(Preparation)、解析(Resolution)、初始化 (Initialization)、使用(Using)和卸載(Unloading)七個階段,其中驗(yàn)證、準(zhǔn)備、解析三個部分統(tǒng)稱為連接(Linking)。這七個階段的發(fā)生順序如圖1所示。

雖然classloader的加載過程有復(fù)雜的7步,但事實(shí)上除了加載之外的四步,其它都是由JVM虛擬機(jī)控制的,我們除了適應(yīng)它的規(guī)范進(jìn)行開發(fā)外,能夠干預(yù)的空間并不多。而加載則是我們控制classloader實(shí)現(xiàn)特殊目的最重要的手段了。也是接下來我們介紹的重點(diǎn)了。

三、加載

“加載”(Loading)階段是整個“類加載”(Class Loading)過程中的一個階段。

在加載階段,Java虛擬機(jī)需要完成以下三件事情:

  • 通過一個類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流。
  • 將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時數(shù)據(jù)結(jié)構(gòu)。
  • 在內(nèi)存中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口。

《Java虛擬機(jī)規(guī)范》對這三點(diǎn)沒有進(jìn)行特別具體的要求,從而留給虛擬機(jī)實(shí)現(xiàn)與Java應(yīng)用的靈活度都是相當(dāng)大的。例如“通過一個類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流”這條規(guī)則,它并沒有指明二 進(jìn)制字節(jié)流必須得從某個Class文件中獲取,確切地說是根本沒有指明要從哪里獲取、如何獲取。比如我們可以從ZIP壓縮包中讀取、從網(wǎng)絡(luò)中獲取、運(yùn)行時計算生成、由其他文件生成、從數(shù)據(jù)庫中讀取。也可以可以從加密文件中獲取。

從這里我們可以看出,只需要我們能夠獲取到加密類的.class文件,我們就可以通過類加載器獲取到對應(yīng)的加密類class對象,進(jìn)而通過反射去調(diào)用具體的加密方法。因此類加載器在.class文件的加載過程有著至關(guān)重要的地位。

四、雙親委派模型

目前Java虛擬機(jī)已經(jīng)存在三種類加載器,分別為啟動類加載器、擴(kuò)展類加載器和應(yīng)用程序類加載器;絕大多數(shù)的Java程序都會使用這三種類加載器進(jìn)行加載。

4.1 啟動類加載器

這個類由C++實(shí)現(xiàn),負(fù)責(zé)加載存放在\lib目錄,或者被-Xbootclasspath參數(shù)所指定的路徑中存放的,而且是Java虛擬機(jī)能夠識別的(按照文件名識別,如rt.jar、tools.jar,名字不符合的類庫即使放在lib目錄中也不會被加載)類庫加載到虛擬機(jī)的內(nèi)存中。啟動類加載器無法被Java程序直接引用,用戶在編寫自定義類加載器時, 如果需要把加載請求委派給引導(dǎo)類加載器去處理,那直接使用null代替即可。

4.2 擴(kuò)展類加載器

這個類加載器是在類sun.misc.Launcher$ExtClassLoader 中以Java代碼的形式實(shí)現(xiàn)的。它負(fù)責(zé)加載\lib\ext目錄中,或者被java.ext.dirs系統(tǒng)變量所指定的路徑中所有的類庫。根據(jù)“擴(kuò)展類加載器”這個名稱,就可以推斷出這是一種Java系統(tǒng)類庫的擴(kuò)展機(jī)制,JDK的開發(fā)團(tuán)隊允許用戶將具有通用性的類庫放置在ext目錄里以擴(kuò)展Java SE的功能,在JDK9之后,這種擴(kuò)展機(jī)制被模塊化帶來的天然的擴(kuò)展能力所取代。由于擴(kuò)展類加載器是由Java代碼實(shí)現(xiàn)的,開發(fā)者可以直接在程序中使用擴(kuò)展類加載器來加載Class文件。

4.3 應(yīng)用程序類加載器

這個類加載器由sun.misc.Launcher$AppClassLoader來實(shí)現(xiàn)。由于應(yīng)用程序類加載器是ClassLoader類中的getSystemClassLoader()方法的返回值,所以有些場合中也稱它為“系統(tǒng)類加載器”。它負(fù)責(zé)加載用戶類路徑(ClassPath)上所有的類庫,開發(fā)者同樣可以直接在代碼中使用這個類加載器。如果應(yīng)用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認(rèn)的類加載器。

由于現(xiàn)有的類加載器加載路徑都有特殊的要求,自己所編譯的加密類所產(chǎn)生的.class文件所存放的路徑不在三個現(xiàn)有類加載器的路徑里面,因此我們有必要自己定義類加載器。

五、自定義類加載器

除了根類加載器,所有類加載器都是ClassLoader的子類。所以我們可以通過繼承ClassLoader來實(shí)現(xiàn)自己的類加載器。

ClassLoader類有兩個關(guān)鍵的方法:

  • protected Class loadClass(String name, boolean resolve):name為類名,resove如果為true,在加載時解析該類。
  • protected Class findClass(String name) :根據(jù)指定類名來查找類。

所以,如果要實(shí)現(xiàn)自定義類,可以重寫這兩個方法來實(shí)現(xiàn)。但推薦重寫findClass方法,而不是重寫loadClass方法,重寫loadClass方法可能會破壞類加載的雙親委派模型,因?yàn)閘oadClass方法內(nèi)部會調(diào)用findClass方法。

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
 
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);
 
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

loadClass加載方法流程:

  • 判斷此類是否已經(jīng)加載;
  • 如果父加載器不為null,則使用父加載器進(jìn)行加載;反之,使用根加載器進(jìn)行加載;
  • 如果前面都沒加載成功,則使用findClass方法進(jìn)行加載。

所以,為了不影響類的加載過程,我們重寫findClass方法即可簡單方便的實(shí)現(xiàn)自定義類加載。

六、代碼實(shí)現(xiàn)

6.1 實(shí)現(xiàn)自定義的類加載器

public class DynamicClassLoader extends ClassLoader {
 
    private static final String CLASS_EXTENSION = "class";
 
    @Override
    public Class<?> findClass(String encryptClassInfo) {
        EncryptClassInfo info = JSON.parseObject(encryptClassInfo, EncryptClassInfo.class);
        String filePath = info.getAbsoluteFilePath();
        String systemPath = System.getProperty("java.io.tmpdir");
        String normalizeFileName = FilenameUtils.normalize(filePath, true);
        if (StringUtils.isEmpty(normalizeFileName) || !normalizeFileName.startsWith(systemPath)
                ||getApkFileExtension(normalizeFileName) == null
                || !CLASS_EXTENSION.equals(getApkFileExtension(normalizeFileName))) {
            return null;
        }
 
        String className = info.getEncryptClassName();
        byte[] classBytes = null;
        File customEncryptFile = new File(filePath);
        try {
            Path path = Paths.get(customEncryptFile.toURI());
            classBytes = Files.readAllBytes(path);
        } catch (IOException e) {
            log.info("加密錯誤", e);
        }
        if (classBytes != null) {
            return defineClass(className, classBytes, 0, classBytes.length);
        }
        return null;
    }
 
    private static String getApkFileExtension(String fileName) {
        int index = fileName.lastIndexOf(".");
        if (index != -1) {
            return fileName.substring(index + 1);
        }
        return null;
    }
}

這里主要是通過集成ClassLoader,復(fù)寫findClass方法,從加密類信息中獲取到對應(yīng)的.class文件信息,最后獲取到加密類的對象

6.2 .class文件中的encrypt()方法

public String encrypt(String rawString) {
        String keyString = "R.string.0x7f050001";
        byte[] enByte = encryptField(keyString, rawString.getBytes());
        return Base64.encode(enByte);
    }

6.3 具體的調(diào)用

public class EncryptStringHandler {
 
    private static final Map<String, Class<?>> classMameMap = new HashMap<>();
 
    @Autowired
    private VivofsFileHelper vivofsFileHelper;
 
    @Autowired
    private DynamicClassLoader dynamicClassLoader;
 
    public String encryptString(String fileId, String encryptClassName, String fileContent) {
        try {
            Class<?> clazz = obtainEncryptClass(fileId, encryptClassName);
            Object obj = clazz.newInstance();
            Method method = clazz.getMethod("encrypt", String.class);
            String encryptStr = (String) method.invoke(obj, fileContent);
            log.info("原字符串為:{},加密后的字符串為:{}", fileContent, encryptStr);
            return encryptStr;
        } catch (Exception e) {
            log.error("自定義加載器加載加密類異常", e);
            return null;
        }
    }
 
    private Class<?> obtainEncryptClass(String fileId, String encryptClassName) {
        Class<?> clazz = classMameMap.get(encryptClassName);
        if (clazz != null) {
            return clazz;
        }
        String absoluteFilePath = null;
        try {
            String domain = VivoConfigManager.getString("vivofs.host");
            String fullPath = domain + "/" + fileId;
            File classFile = vivofsFileHelper.downloadFileByUrl(fullPath);
            absoluteFilePath = classFile.getAbsolutePath();
            EncryptClassInfo encryptClassInfo = new EncryptClassInfo(encryptClassName, absoluteFilePath);
            String info = JSON.toJSONString(encryptClassInfo);
            clazz = dynamicClassLoader.findClass(info);
            //設(shè)置緩存
            Assert.notNull(clazz, "自定義類加載器加載加密類異常");
            classMameMap.put(encryptClassName, clazz);
            return clazz;
        } finally {
            if (absoluteFilePath != null) {
                FileUtils.deleteQuietly(new File(absoluteFilePath));
            }
        }
    }
}

通過上述代碼的實(shí)現(xiàn),我們可以通過在管理平臺添加編譯好的.class文件,最后通過自定義的類加載器和反射調(diào)用方法,來實(shí)現(xiàn)具體方法的調(diào)用,避免了我們需要修改代碼和重新發(fā)版來適應(yīng)不斷新增加密方法的問題。

七、問題

上面的代碼在本地測試時,沒有出現(xiàn)任何異常,但是部署到測試服務(wù)器以后出現(xiàn)了JSON解析異常,看上去貌似是json字符串的格式不對。

json解析邏輯主要存在于DynamicClassLoader#findClass方法入口處的將字符串轉(zhuǎn)換為對象邏輯,為什么這里會報錯,我們在入口處打印了入?yún)ⅰ?/p>

發(fā)現(xiàn)這里除了我們需要的正確的入?yún)?第一個入?yún)⑿畔⒋蛴?外,還多了一個Base64的全路徑名cn.hutool.core.codec.Base64。出現(xiàn)這種情況,說明由于我們重寫了ClassLoader的findClass方法,而Base64加載的時候會調(diào)用原始的ClassLoader類的loadClass方法去加載,并且里面調(diào)用了findClass方法,由于findClass已經(jīng)被重寫,所以就會報上面的json解析錯誤。

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
 
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);
 
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

但是這里期望的是除了用于加密的.class文件用自定義類加載器進(jìn)行以外,不希望其他的類用自定義類加載器加載,通過對ClassLoader#loadClass方法分析,那么我們就希望能否通過其父類加載器加載到Base64這個三方類。因?yàn)閱宇惣虞d器Bootstrap Class Loader肯定不能加載到Base64,所以我們需要顯示的設(shè)置父類加載器,但是這個父類加載器究竟設(shè)置為哪一個類加載器,那么就需要我們了解Tomcat類加載器結(jié)構(gòu)。

為什么Tomcat需要在JVM基礎(chǔ)之上做一套類加載結(jié)構(gòu),主要是為了解決如下問題:

  • 部署在同一個服務(wù)器上的兩個web應(yīng)用程序所使用的Java類庫可以實(shí)現(xiàn)相互隔離;
  • 部署在同一個服務(wù)器上的兩個web應(yīng)用程序所使用的Java類庫可以實(shí)現(xiàn)共享;
  • 服務(wù)器需要盡可能保證自身安全,服務(wù)器所使用的類庫應(yīng)該與應(yīng)用程序的類庫相互獨(dú)立;
  • 支持JSP應(yīng)用的Web服務(wù)器,大對數(shù)需要支持HotSwap功能。

為此,tomcat擴(kuò)展出了Common類加載器(CommonClassLoader)、Catalina類加載器(CatalinaClassLoader)、Shared類加載器(SharedClassLoader)和WebApp類加載器(WebAppClassLoader),他們分別加載/commons/、/server/、/shared/*和/WebApp/WEB-INF/*中的Java類庫的邏輯。

通過分析,我們知道WebAppClassLoader類加載器可以加載到/WEB-INF/*目錄下的依賴包,而我們所依賴的類cn.hutool.core.codec.Base64所在的包hutool-all-4.6.10-sources.jar就存在于/WEB-INF/*目錄下面,并且我們自定義類加載器所在的包 vivo-namelist-platform-service-1.0.6.jar也在/WEB-INF/*下,所以自定義類加載器DynamicClassLoader也是WebAppClassLoader加載的。

我們可以寫一個測試類測試一下:

@Slf4j
@Component
public class Test implements ApplicationListener<ContextRefreshedEvent> {
 
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        log.info("classLoader DynamicClassLoader:" + DynamicClassLoader.class.getClassLoader().toString());
    }
}

測試結(jié)果:

所以我們可以設(shè)置自定義類加載器DynamicClassLoader的父加載器為加載其本身的類加載器:

public DynamicClassLoader() {
        super(DynamicClassLoader.class.getClassLoader());
}

我們再次執(zhí)行文件的加解密操作時,已經(jīng)沒有發(fā)現(xiàn)報錯,并且通過添加日志,我們可以看到加載類cn.hutool.core.codec.Base64對應(yīng)的類加載器確實(shí)為加載DynamicClassLoader對應(yīng)的類加載器WebAppClassLoader。

public String encrypt(String rawString) {
        log.info("classLoader Base64:{}", Base64.class.getClassLoader().toString());
        String keyString = "R.string.0x7f050001";
        byte[] enByte = encryptField(keyString, rawString.getBytes());
        return Base64.encode(enByte);
    }

現(xiàn)在再來思考一下,為什么在IDEA運(yùn)行環(huán)境下不需要設(shè)置自定義類加載器的父類加載器就可以加載到cn.hutool.core.codec.Base64。

在IDEA運(yùn)行環(huán)境下添加如下打印信息:

public String encrypt(String rawString) {
        System.out.println("類加載器詳情...");
        System.out.println("classLoader EncryptStrategyHandler:" + EncryptStrategyHandlerH.class.getClassLoader().toString());
        System.out.println("classLoader EncryptStrategyHandler:" + EncryptStrategyHandlerH.class.getClassLoader().getParent().toString());
        String classPath = System.getProperty("java.class.path");
        System.out.println("classPath:" + classPath);
        System.out.println("classLoader Base64:" + Base64.class.getClassLoader().toString());
        String keyString = "R.string.0x7f050001";
        byte[] enByte = encryptField(keyString, rawString.getBytes());
        return Base64.encode(enByte);
    }

發(fā)現(xiàn)加載.class文件的類加載器為自定義類加載器DynamicClassLoader,并且.class加載器的父類加載器為應(yīng)用類加載器AppClassLoader,加載cn.hutool.core.codec.Base64的類加載器也是AppClassLoader。

具體的加載流程如下:

1)先由自定義類加載器委托給AppClassLoader;

2)AppClassLoader委托給父類加載器ExtClassLoader;

3)ExtClassLoader再委托給BootStrapClassLoader,但是BootClassLoader無法加載到,于是ExtClassLoader自己進(jìn)行加載,也無法加載到;

4)再由AppClassLoader進(jìn)行加載;

AppClassLoader會調(diào)用其父類UrlClassLoader的findClass方法進(jìn)行加載;

5)最終從用戶類路徑j(luò)ava.class.path中加載到cn.hutool.core.codec.Base64。

由此,我們發(fā)現(xiàn)在IDEA環(huán)境下面,自定義的加密類.class文件中依賴的三方cn.hutool.core.codec.Base64是可以通過AppClassLoader進(jìn)行加載的。

而在linux環(huán)境下面,經(jīng)過遠(yuǎn)程調(diào)試,發(fā)現(xiàn)初始時加載cn.hutool.core.codec.Base64的類加載器為DynamicClassLoader。然后委托給父類加載器AppClassLoader進(jìn)行加載,根據(jù)雙親委派原理,后續(xù)會交由AppClassLoader自己進(jìn)行處理。但是在用戶路徑下仍然沒有找到類cn.hutool.core.codec.Base64,最終交由DynamicClassLoader進(jìn)行加載,最終出現(xiàn)了最開始的JSON解析錯誤。

八、總結(jié)

由于類加載階段沒有嚴(yán)格限制如何獲取一個類的二進(jìn)制字節(jié)流,因此給我們提供一個通過自定義類加載器來動態(tài)加載.class文件實(shí)現(xiàn)代碼可擴(kuò)展性的可能。通過靈活自定義classloader,也可以在其他領(lǐng)域發(fā)揮重要作用,例如實(shí)現(xiàn)代碼加密來避免核心代碼泄漏、解決不同服務(wù)依賴同一個包的不同版本所引起的沖突問題以及實(shí)現(xiàn)程序熱部署來避免調(diào)試時頻繁重啟應(yīng)用。

到此這篇關(guān)于JVM自定義類加載器在代碼擴(kuò)展性實(shí)踐分享的文章就介紹到這了,更多相關(guān)JVM加載器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring?Cloud?Gateway編碼實(shí)現(xiàn)任意地址跳轉(zhuǎn)的示例

    Spring?Cloud?Gateway編碼實(shí)現(xiàn)任意地址跳轉(zhuǎn)的示例

    本文主要介紹了Spring?Cloud?Gateway編碼實(shí)現(xiàn)任意地址跳轉(zhuǎn)的示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • 命令行中 javac、java、javap 的使用小結(jié)

    命令行中 javac、java、javap 的使用小結(jié)

    使用 java 命令運(yùn)行一個.class文件,需要使用該類的全限定類名,同時需要在當(dāng)前路徑下有該類的包層次文件夾,這篇文章主要介紹了命令行中 javac、java、javap 的使用小結(jié),需要的朋友可以參考下
    2023-07-07
  • Java多線程實(shí)現(xiàn)Callable接口

    Java多線程實(shí)現(xiàn)Callable接口

    本文給大家分享的是使用Java多線程來實(shí)現(xiàn)callable接口的方法,以及使用方法,另外還有一個網(wǎng)友的實(shí)例,希望能夠?qū)Υ蠹艺莆認(rèn)ava多線程有所幫助。
    2016-06-06
  • java實(shí)現(xiàn)簡單五子棋小游戲(2)

    java實(shí)現(xiàn)簡單五子棋小游戲(2)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡單五子棋小游戲的第二部分,添加游戲結(jié)束條件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • springboot實(shí)現(xiàn)圖片大小壓縮功能

    springboot實(shí)現(xiàn)圖片大小壓縮功能

    這篇文章主要為大家詳細(xì)介紹了springboot實(shí)現(xiàn)圖片大小壓縮功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • 一篇文章帶你入門java算術(shù)運(yùn)算符(加減乘除余,字符連接)

    一篇文章帶你入門java算術(shù)運(yùn)算符(加減乘除余,字符連接)

    這篇文章主要介紹了Java基本數(shù)據(jù)類型和運(yùn)算符,結(jié)合實(shí)例形式詳細(xì)分析了java基本數(shù)據(jù)類型、數(shù)據(jù)類型轉(zhuǎn)換、算術(shù)運(yùn)算符、邏輯運(yùn)算符等相關(guān)原理與操作技巧,需要的朋友可以參考下
    2021-08-08
  • Mybatis之映射實(shí)體類中不區(qū)分大小寫的解決

    Mybatis之映射實(shí)體類中不區(qū)分大小寫的解決

    這篇文章主要介紹了Mybatis之映射實(shí)體類中不區(qū)分大小寫的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 在Java中避免NullPointerException的解決方案

    在Java中避免NullPointerException的解決方案

    這篇文章主要介紹了在Java中避免NullPointerException的解決方案,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • spring 中事務(wù)注解@Transactional與trycatch的使用

    spring 中事務(wù)注解@Transactional與trycatch的使用

    這篇文章主要介紹了spring 中事務(wù)注解@Transactional與trycatch的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • java判斷integer是否為空的詳細(xì)過程

    java判斷integer是否為空的詳細(xì)過程

    在java編寫過程中,我們會使用到各種各樣的表達(dá)式,在使用表達(dá)式的過程中,有哪些安全問題需要我們注意的呢?對java判斷integer是否為空相關(guān)知識感興趣的朋友一起來看看吧
    2023-02-02

最新評論