修改Apache Velocity源碼並重新打包velocity1.7.jar已解決動態加載自定義函數的問題(二)
上文鏈接:https://blog.csdn.net/rico_zhou/article/details/105008066
這一篇我們講述如何修改velocity源碼並打包:
首先下載源碼:http://archive.apache.org/dist/velocity/engine/1.7/,需要兩個文件一個是pom,一個是zip:
velocity-1.7.pom,velocity-1.7.zip
由於項目是ant構建,我們現在改成maven項目導入開發工具中,然後在使用ant打包,
首先將velocity-1.7.zip解壓到velocity-1.7,然後將velocity-1.7.pom複製到根目錄下並修改文件爲pom.xml,然後使用eclipse或者idea以maven項目導入velocity-1.7,等待加載完成,報錯的話百度即可解決,注意velocity項目比較老,使用的jdk也比較老。
然後我們開始修改源碼,找到類:org.apache.velocity.runtime.RuntimeInstance,定位到1000行左右這個方法附近:
/**
* instantiates and loads the directive with some basic checks
*
* @param directiveClass classname of directive to load
*/
public void loadDirective(String directiveClass)
{
try
{
Object o = ClassUtils.getNewInstance( directiveClass);
if (o instanceof Directive)
{
Directive directive = (Directive) o;
addDirective(directive);
}
else
{
String msg = directiveClass + " does not implement "
+ Directive.class.getName() + "; it cannot be loaded.";
log.error(msg);
throw new VelocityException(msg);
}
}
// The ugly threesome: ClassNotFoundException,
// IllegalAccessException, InstantiationException.
// Ignore Findbugs complaint for now.
catch (Exception e)
{
String msg = "Failed to load Directive: " + directiveClass;
log.error(msg, e);
throw new VelocityException(msg, e);
}
}
修改成如下
/**
* instantiates and loads the directive with some basic checks
*
* @param directiveClass classname of directive to load
*/
public void loadDirective(String directiveClass)
{
try
{
//Modify the source code here to add a parameter to read additional jars
String[] extraJarPathArr=configuration.getStringArray("extrauserdirective");
Object o = ClassUtils.getNewInstance( directiveClass ,extraJarPathArr);
if (o instanceof Directive)
{
Directive directive = (Directive) o;
addDirective(directive);
}
else
{
String msg = directiveClass + " does not implement "
+ Directive.class.getName() + "; it cannot be loaded.";
log.error(msg);
throw new VelocityException(msg);
}
}
// The ugly threesome: ClassNotFoundException,
// IllegalAccessException, InstantiationException.
// Ignore Findbugs complaint for now.
catch (Exception e)
{
String msg = "Failed to load Directive: " + directiveClass;
log.error(msg, e);
throw new VelocityException(msg, e);
}
}
添加並修改這個:
//Modify the source code here to add a parameter to read additional jars
String[] extraJarPathArr=configuration.getStringArray("extrauserdirective");
Object o = ClassUtils.getNewInstance( directiveClass ,extraJarPathArr);
傳遞一個參數,這個參數傳遞的是需要加載的自定義函數jar的位置,可以是目錄或者jar路徑,目錄則掃碼其下首頁jar,然後重載方法ClassUtils.getNewInstance(),
定位到類:org.apache.velocity.util.ClassUtils,大概180行,重載方法:儘量減少影響,所以方法都重載而不是覆寫
public static Object getNewInstance(String clazz)
throws ClassNotFoundException,IllegalAccessException,InstantiationException
{
return getClass(clazz).newInstance();
}
//Modify the source code here to add a parameter to read additional jars
public static Object getNewInstance(String clazz,String[] extraJarPathArr)
throws ClassNotFoundException,IllegalAccessException,InstantiationException
{
return getClass(clazz,extraJarPathArr).newInstance();
}
再次重載getClass方法:
/**
* Return the specified class. Checks the ThreadContext classloader first,
* then uses the System classloader. Should replace all calls to
* <code>Class.forName( claz )</code> (which only calls the System class
* loader) when the class might be in a different classloader (e.g. in a
* webapp).
*
* @param clazz the name of the class to instantiate
* @return the requested Class object
* @throws ClassNotFoundException
*/
public static Class getClass(String clazz) throws ClassNotFoundException
{
return getClass(clazz,null);
}
//Modify the source code here to add a parameter to read additional jars
public static Class getClass(String clazz,String[] extraJarPathArr) throws ClassNotFoundException
{
/**
* Use the Thread context classloader if possible
*/
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader != null)
{
try
{
return Class.forName(clazz, true, loader);
}
catch (ClassNotFoundException E)
{
/**
* If not found with ThreadContext loader, fall thru to
* try System classloader below (works around bug in ant).
*/
if (extraJarPathArr != null) {
List allFileList = new ArrayList();
for (int i=0;i<extraJarPathArr.length;i++) {
String path=extraJarPathArr[i];
if (path != null) {
File p = new File(path);
if (p.exists()) {
if (p.isDirectory()) {
File[] pp = p.listFiles();
List flist = filterFile(pp, ".jar");
allFileList.addAll(flist);
} else if (p.isFile() && p.getAbsolutePath().endsWith(".jar")) {
allFileList.add(p);
}
}
}
}
for (int i=0;i<allFileList.size();i++) {
File f=(File) allFileList.get(i);
URL u;
try {
u = listFileByPluginName(f);
ClassLoader classLoader = new URLClassLoader(new URL[] { u },
Thread.currentThread().getContextClassLoader());
return classLoader.loadClass(clazz);
} catch (Exception e) {
}
}
}
}
}
/**
* Thread context classloader isn't working out, so use system loader.
*/
return Class.forName(clazz);
}
再加一個工具方法:
public static List filterFile(List resultFile, String fileExtensionPointFileVm) {
List filterFileList = new ArrayList();
if (resultFile != null && resultFile.size() > 0) {
for (int i=0;i<resultFile.size();i++) {
File f=(File) resultFile.get(i);
if (f.isFile() && f.getAbsolutePath().endsWith(fileExtensionPointFileVm)) {
filterFileList.add(f);
}
}
}
return filterFileList;
}
public static List filterFile(File[] resultFile, String fileExtensionPointFileVm) {
List filterFileList = new ArrayList();
if (resultFile != null && resultFile.length > 0) {
for (int i=0;i<resultFile.length;i++) {
File f=resultFile[i];
if (f.isFile() && f.getAbsolutePath().endsWith(fileExtensionPointFileVm)) {
filterFileList.add(f);
}
}
}
return filterFileList;
}
private static URL listFileByPluginName(File jar) throws Exception {
if (!jar.exists()) {
throw new RuntimeException("Not found JAR: " + jar.getName());
}
URL url = jar.toURI().toURL();
return url;
}
注意不能有中文,不能使用泛型(比較老沒辦法,儘量不動原來打包方式)
方法也比較簡單,基本就是使用當前線程類加載器加載自定義函數類,失敗的話則使用修改的方法加載,即根據傳遞的參數去jar目錄掃描目錄下所有jar(不包括子目錄),多個目錄傳遞的參數以逗號間隔,子目錄不遍歷防止內容態度耗時,然後使用
ClassLoader classLoader = new URLClassLoader(new URL[] { u },
Thread.currentThread().getContextClassLoader());
方式加載類,如果失敗則再使用源碼中寫的系統類加載器。
修改完成後我們準備打包:
首先下載ant:http://ant.apache.org/bindownload.cgi,最新版本zip,解壓並添加環境變量ANT_HOME,打開命令行輸入ant回車有反應即可。
到根目錄下(項目根目錄),將lib中的jar包複製到bin/lib下,lib/test下jar複製到/bin/test-lib下,然後進入build目錄,打開命令執行ant,等待打包完成,報錯則解決,然後查看bin目錄下有velocity-1.7.jar,這個就是修改後的jar,可以看到大小約440k,與原有jar差不多大小。
接下來將jar引入到軟件項目中,初始化引擎時傳遞需要加載的自定義jar路徑和類名:
自定義函數類名加載:
p.setProperty("userdirective", "com.soft.support.velocity.custom.NowDate");
需要加載的額外參數:
p.setProperty("extrauserdirective", "D:/test/test");
在模板中調用NowDate這個方法就成功啦!