zoukankan      html  css  js  c++  java
  • Velocity VelocityEngine 支持多种loader 乱码问题

    最近升级团队的代码生成工具,此工具是velocity实现的。

    之前习惯使用UTF-8编码,现在团队使用GBK。

    所以遇到一种场景,模板文件使用UTF-8(习惯了所有任性),输出文件使用GBK(项目需要)。

    
    
    Properties props = new Properties(); 
    props.setProperty(Velocity.ENCODING_DEFAULT, "GBK");//全局编码,如果以下编码不设置它就生效
    props.setProperty(Velocity.INPUT_ENCODING, "UTF-8");//输入流的编码,其实是打酱油!非模板文件编码
    props.setProperty(Velocity.OUTPUT_ENCODING, "GBK");//输入流编码,很关键!
    props.setProperty(VelocityEngine.RESOURCE_LOADER,"file");//模板文件加载方式
    VelocityEngine engine = new VelocityEngine(props);

    下面这段code是解决乱码问题的关键

    Template  template = engine.getTemplate("a.vm","UTF-8");//模板文件的编码,很关键!
    VelocityContext context = new VelocityContext();
    context.put("a","a");
    FileWriter fileWriter=new FileWriter("test.java");
    template.merge(context, fileWriter);

    分析:

    String org.apache.velocity.runtime.RuntimeConstants.INPUT_ENCODING = "input.encoding"
     
    
    The character encoding for the templates. Used by the parser in processing the input streams.

    这是velocity中  INPUT_ENCODING 的doc,字面意思理解 "模板文件的编码,用于格式化输入流";

    Template org.apache.velocity.app.VelocityEngine.getTemplate(String name, String encoding) throws ResourceNotFoundException, ParseErrorException
     
    
    Returns a Template from the Velocity resource management system.
    
    Parameters:
    name The file name of the desired template.
    encoding The character encoding to use for the template.

    这是getTemplate(String name, String encoding)的doc,字面意思理解"encoding 参数是设置模板文件的编码"
    经过我的多次实践,如果设置了 INPUT_ENCODING 为 UTF-8,直接使用getTemplate(String name)方法,结果就是输出的文件 编码没有问题,但是模板文件的中文到输出文件中就成乱码了!
    换成getTemplate(String name, String encoding),传入UTF-8编码,一切正常!

    这是一个深深的坑!之前翻遍各大博客及官方doc都无法定位这个问题的原因!因为大家一致使用getTemplate(String name)方法!

    接下来谈下Velocity和VelocityEngine的区别

    Velocity和VelocityEngine都可以用来读取模板文件和输出文件,宏观上讲,Velocity是单例模式,VelocityEngine是多例模式!

    从细节上,Velocity因为是单例的设计模式,所以init方法只能执行一次,这就意味着你在整个应用程序生命周期中Velocity的配置是无法修改的!


    再说下Velocity的几种Loader,主要有ClasspathResourceLoader FileResourceLoader (默认),一些不常用的 JarResourceLoader  DataSourceResourceLoader 及 WebappResourceLoader URLResourceLoader 

    下边再说下另外一个大坑

         /**
         * Key used to retrieve the names of the resource loaders to be used. In a properties file they may appear as the following:
         *
         * <p>resource.loader = file,classpath</p>
         */
        String RESOURCE_LOADER = "resource.loader";

    从字面意思理解,这里可以设置file/classpath!真实的情况是,当你设置为 classpath 时,Velocity会切换成file,因为classpath是无效设置(这个让人很费解),只有设置为 class 并且设置 class.resource.loader.class 为 org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader,才会使用ClasspathResourceLoader !

    综上所述,如果想实现同时支持file和class两种loader,必须使用VelocityEngine!在文件系统中无法读取到模板文件时自动切换为classPathLoader!代码如下

    import java.util.Properties;
    
    import org.apache.velocity.Template;
    import org.apache.velocity.VelocityContext;
    import org.apache.velocity.app.Velocity;
    import org.apache.velocity.app.VelocityEngine;
    import org.apache.velocity.exception.ResourceNotFoundException;
    
    
    public abstract class BaseCode {
        {
            initVelocity("file");
        }
        
        private void initVelocity(String loader){
            props = new Properties(); 
            props.setProperty(Velocity.ENCODING_DEFAULT, "GBK");
            props.setProperty(Velocity.INPUT_ENCODING, "GBK");
            props.setProperty(Velocity.OUTPUT_ENCODING, "GBK");
            props.setProperty(VelocityEngine.RESOURCE_LOADER,loader);
            if(loader.equals("class")){
                props.setProperty("class.resource.loader.class","org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
            }
            engine = new VelocityEngine(props);
        }
        
        private String templetPath;
        private Template template;
        protected static VelocityEngine engine;  
        
        protected static Properties props ;
        
        public String getTempletPath() {
            return templetPath;
        }
    
        public void setTempletPath(String templetPath) {
            this.templetPath = templetPath;
        }
        
        protected  Template getTemplate(){
            if(this.template==null){
                try
                { 
                   template = engine.getTemplate(this.getTempletPath(),"GBK");
                }
                catch(ResourceNotFoundException e ){
                    initVelocity("class");
                    getTemplate();
                }
            }
            return template;
                
        }
        
        protected VelocityContext getVelocityContext(){
            VelocityContext context = new VelocityContext();
            context.put( "nameUtil", NameUtil.get() );
            return context;
        }
    
    }
  • 相关阅读:
    C#解决界面不响应
    C#调用SendMessage 用法
    C#开机自动启动程序代码
    C# WinForm使用乐器数字接口
    以下C#程序的输出结果是( )。
    C# 关键字extern用法
    C#循环测试题
    C#播放wav文件
    Matches正则使用提取内容
    C#测试题若干,都是基础阿
  • 原文地址:https://www.cnblogs.com/zhanghaoh/p/4503946.html
Copyright © 2011-2022 走看看