最近升級團隊的代碼生成工具,此工具是velocity實現的。java
以前習慣使用UTF-8編碼,如今團隊使用GBK。apache
因此遇到一種場景,模板文件使用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是解決亂碼問題的關鍵app
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,字面意思理解 "模板文件的編碼,用於格式化輸入流";this
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)方法!spa
接下來談下Velocity和VelocityEngine的區別設計
Velocity和VelocityEngine均可以用來讀取模板文件和輸出文件,宏觀上講,Velocity是單例模式,VelocityEngine是多例模式!code
從細節上,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; } }