修改Apache Velocity源码并重新打包velocity1.7.jar已解决动态加载自定义函数的问题(二)

修改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.pomvelocity-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这个方法就成功啦!

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值