一。前言

說到語言的動態性,這個是指令碼語言的一大優勢,沒有中間環節,原始碼即時執行。大家一般不會把它和java聯絡在一起,從java本身語言來看,java確實具有指令碼語言的一些特性,即可以即時編譯和執行。java相關的動態載入技術也非常的成熟,在android客戶端,可以用這種技術熱修復,動態替換有bug的相關程式碼;在服務端也有廣泛的應用,像java的外掛技術,感興趣的可以參考

https://

github。com/pf4j/pf4j

;包括我們使用的開發工具,比如idea的熱替換,幫助我們在開發過程中,修改完程式碼熱替換檔案,不用每次都重啟專案。但是java的熱替換有一些限制,比如不能修改方法的簽名,只能修改方法體裡面的內容。

二。demo

示例程式碼是簡化後的,完整的參考

https://

github。com/jsdman/dynam

ic-code

我們結合springboot框架簡單展示下動態載入程式碼技術的具體實現。動態載入程式碼的核心是類載入引擎,該類的作用是動態載入類的原始檔,編譯成class檔案並且載入到jvm中。

public

class

DynamicEngine

{

public

Class

<?>

compile

String

className

String

javaCodes

{

if

classReloadNum

containsKey

className

))

{

classReloadNum

get

className

)。

incrementAndGet

();

}

else

{

classReloadNum

put

className

new

AtomicLong

startTime

));

}

String

newClassName

=

className

+

“_”

+

classReloadNum

get

className

);

String

[]

classNameArr

=

className

split

“\\。”

);

String

[]

newClassNameArr

=

newClassName

split

“\\。”

);

String

newJavaCode

=

javaCodes

replaceAll

classNameArr

classNameArr

length

-

1

],

newClassNameArr

newClassNameArr

length

-

1

]);

JavaCompiler

compiler

=

ToolProvider

getSystemJavaCompiler

();

StandardJavaFileManager

fileManager

=

compiler

getStandardFileManager

null

null

null

);

StrSrcJavaObject

srcObject

=

new

StrSrcJavaObject

newClassName

newJavaCode

);

Iterable

<?

extends

JavaFileObject

>

fileObjects

=

Collections

singletonList

srcObject

);

String

flag

=

“-d”

String

outDir

File

classPath

try

{

classPath

=

new

File

Objects

requireNonNull

Thread

currentThread

()。

getContextClassLoader

()。

getResource

“”

))。

toURI

());

outDir

=

classPath

getAbsolutePath

()

+

File

separator

Iterable

<

String

>

options

=

Arrays

asList

flag

outDir

);

JavaCompiler

CompilationTask

task

=

compiler

getTask

null

fileManager

null

options

null

fileObjects

);

boolean

result

=

task

call

();

if

result

{

try

{

return

myClassLoader

loadClass

newClassName

);

}

catch

ClassNotFoundException

e

{

System

out

println

“載入失敗!”

);

e

printStackTrace

();

}

}

}

catch

URISyntaxException

e

{

e

printStackTrace

();

}

return

null

}

}

再定義一個介面類,我們的動態載入的類都是繼承於這個介面。

public

interface

BaseSay

{

String

say

();

}

寫個controller提供動態載入的入口和我們動態載入類的執行動作。

@RestController

public

class

IndexController

{

@Resource

private

DynamicEngine

dynamicEngine

private

BaseSay

baseSay

@PostMapping

“/loadClass”

public

String

loadClass

String

className

String

code

throws

IllegalAccessException

InstantiationException

{

Class

<

BaseSay

>

baseSayClass

=

Class

<

BaseSay

>)

dynamicEngine

compile

className

code

);

baseSay

=

baseSayClass

newInstance

();

return

“ok”

}

@GetMapping

“/say”

public

String

say

()

{

if

baseSay

==

null

{

return

“baseSay was null!”

}

return

baseSay

say

();

}

再做個html頁面去測試下我們的動態載入好使不:

一個demo展示如何應用java動態程式碼載入技術

我們點選動態載入按鈕,然後點選say:

一個demo展示如何應用java動態程式碼載入技術

say返回字串修改下,動態載入:

一個demo展示如何應用java動態程式碼載入技術

我們點選動態載入按鈕,然後點選say,可以看到返回值是我們動態定義的類了。

一個demo展示如何應用java動態程式碼載入技術

也可以在程式碼裡面加任意的其他程式碼,比如我想在執行前列印一些日誌:

一個demo展示如何應用java動態程式碼載入技術

我們點選動態載入按鈕,然後點選say,可以看到控制檯打印出我們的程式碼了

一個demo展示如何應用java動態程式碼載入技術

三。demo地址

四。應用場景

大家可以一起討論下,是否有一些場景可以應用此方式進行開發。