初識模板框架【Velocity】

因最近項目中涉及到javabean 2 XML的轉換,比較多,但是又不涉及具體的業務邏輯,請求系統與目標系統間進行數據透傳,兩個系統間的報文字段都不一致,需要進行映射轉換,所以考慮再三,xml2bean選擇了xpath實現,然後再把bean轉換爲對應的xml,此過程考慮幾個不同的模板引擎:FreeMarker、Thymeleaf、Enjoy、Velocity,對比了幾個的優缺點之後,選擇了Velocity,比較輕巧,效率高,性能還不錯,最重要是滿足當前的項目需要。

【xpath】

package com.xx.xxx.api.xml.xpath;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.dom4j.Document;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;


/**
 * xml path 映射
 * @author zenghaom
 *
 */
public class XmlPathUtil {
	
	
	/**
	 * xml字符串轉換Object
	 * @param xml
	 * @param clz 轉換對象class
	 * @return
	 * @throws Exception
	 */
	public static <T> T convert(String xml,Class<T> clz) throws Exception{
		InputStream in = null;
		try {
			in = IOUtils.toInputStream(xml);
			return convert(in,clz);
		}finally {
			IOUtils.closeQuietly(in);
		}
	}
	
	/**
	 * xml字符串轉換Object
	 * @param xml xml字符串
	 * @param clz 轉換對象class
	 * @param ns 命名空間
	 * @param alisa 別名
	 * @return
	 * @throws Exception
	 */
	public static <T> T convert(String xml,Class<T> clz,String ns,String alisa) throws Exception{
		InputStream in = null;
		try {
			in = IOUtils.toInputStream(xml);
			return convert(in,clz,ns,alisa);
		}finally {
			IOUtils.closeQuietly(in);
		}
	}
	
	/**
	 * inputStream xml轉Object
	 * @param in
	 * @param clz
	 * @return
	 * @throws Exception
	 */
	public static <T> T convert(InputStream in,Class<T> clz) throws Exception {
			Document doc = new SAXReader().read(in);
			return convert(doc,clz);
	}
	
	/**
	 * inputStream xml轉Object
	 * @param in
	 * @param clz
	 * @param ns 命名空間
	 * @param alisa 別名
	 * @return
	 * @throws Exception
	 */
	
	public static <T> T convert(InputStream in,Class<T> clz,String ns,String alisa) throws Exception {
		SAXReader saxReader = new SAXReader();
		Document doc = saxReader.read(in);
		Map<String,String> map = new HashMap<>();  
	    map.put(alisa,ns); 
	    saxReader.getDocumentFactory().setXPathNamespaceURIs(map);
		return convert(doc,clz);
	}
	
	
	
	/**
	 * document 轉 Object
	 * @param doc
	 * @param clz
	 * @return
	 * @throws Exception 
	 */
	public static <T> T convert(Document doc,Class<T> clz) throws Exception {
		T t = clz.newInstance();
		getValue(doc, t, clz,"");
		return t;
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static void getValue(Document doc,Object obj,Class clz,String parentPath) throws Exception{
		
		Map<Field,XmlField> xmlType = XmlFieldBeanFactory.getForClass(clz);
		
		for(Field field : xmlType.keySet()) {
			XmlField xmlField = xmlType.get(field);
			Object value = field.getType().newInstance();
			BeanUtils.setProperty(obj, field.getName(), value);
			if(!xmlField.haveSon()) {
				if(isCollection(field.getType())) {
					Collection cl = (Collection) value;
					List<Node> nodes = doc.selectNodes(parentPath+xmlField.path());
					if(CollectionUtils.isNotEmpty(nodes)) {
						for (Node node : nodes) {
							cl.add(node.getText());
						}
					}
					
				}else {
					if(xmlField.isText()) {
						Node node = doc.selectSingleNode(parentPath+xmlField.path());
						if(node!=null) {
							BeanUtils.setProperty(obj,field.getName() , node.getText());
						}
					}else {
						BeanUtils.setProperty(obj,field.getName() , doc.valueOf(parentPath+xmlField.path()));
					}
				}
			}else {
				if(isCollection(field.getType())){
					Collection cl = (Collection) value;
					List<Node> nodes = doc.selectNodes(parentPath+xmlField.path());
					if(CollectionUtils.isNotEmpty(nodes)) {
						for (Node node : nodes) {
							 Type genericType = field.getGenericType();                   
							  if(genericType == null) 
								  return;                    // 如果是泛型參數的類型                    
							  if(genericType instanceof ParameterizedType){                        
								  ParameterizedType pt = (ParameterizedType) genericType;//得到泛型裏的class類型對象                     
								  Class<?> accountPrincipalApproveClazz = (Class<?>)pt.getActualTypeArguments()[0];
								  Object v = accountPrincipalApproveClazz.newInstance();
								  cl.add(v);
								  getValue(node.getDocument(), v,accountPrincipalApproveClazz,node.getUniquePath());
							  }
						}
					}
				}else {
					  Node node = doc.selectSingleNode(xmlField.path());
					  if(node != null) {
						  Object v = field.getType().newInstance();
						  getValue(doc, v,field.getType(),node.getUniquePath());
					  }
					 
				}
			}
		}

	}
	
	
	 private static boolean isCollection(Class<?> type) {
		    return Collection.class.isAssignableFrom(type);
	 }
	

}

package com.xx.xxx.api.xml.xpath;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
 * 緩存解析過的xmlField
 * @author zenghaom
 *
 */
public class XmlFieldBeanFactory {
	

	private final static ConcurrentMap<Class<?>, Map<Field,XmlField>> xmlTypeMap = new ConcurrentHashMap<Class<?>,Map<Field,XmlField>>();
	
	public static Map<Field,XmlField> getForClass(Class<?> type){
		Map<Field,XmlField> xmlType = xmlTypeMap.get(type);
		if(xmlType == null) {
			xmlType = getXmlType(type);
			xmlTypeMap.put(type, xmlType);
		}
		return xmlType;
	}
	
	private static Map<Field,XmlField> getXmlType(Class<?> type){
		
		Map<Field,XmlField> xmlType = new HashMap<>();
		while (type != null) {//當父類爲null的時候說明到達了最上層的父類(Object類).
		      for (Field field : type .getDeclaredFields()) {  
		    	  if(field.isAnnotationPresent(XmlField.class)){//判斷成員變量是否有註解
		    		  xmlType.put(field, field.getAnnotation(XmlField.class));
		    	  }
		    	  
		    	 }
		      type = type.getSuperclass(); //得到父類,然後賦給自己
		}

		return xmlType;
	}
	
}

package com.xx.xxx.api.xml.xpath;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * 用於註解字段對應的xpath,進行解析xml
 * @author zenghaom
 *
 */

@Retention(RUNTIME)
@Target(FIELD)
public @interface XmlField {
	/**
	 * xml對應的xPath路徑
	 * @return
	 */
	String path();
	/**
	 * 是否嵌套對象
	 * @return
	 */
	boolean haveSon() default false;
	
	/**
	 * 取屬性值
	 * @return
	 */
	boolean isText() default true;
}

【Velocity】

package com.xx.xxx.api.xml.velocity;

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.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * <pre>
 * <b>JavaBean convert to xml  by VelocityEngine</b>
 * <b>Description:</b>
 *
 * <b>Author:</b> Mike•Tran
 * <b>Date:</b> 2018年10月10日  17:52
 * <b>Copyright:</b> Copyright 2018 xxx-xxxx.com.cn. All rights reserved.
 * <b>Changelog:</b>
 *   Ver   Date                    Author                Detail
 *   ----------------------------------------------------------------------
 *   1.0   2018年10月10日  17:52   Mike  new file.
 *
 * </pre>
 */
public class VelocityXmlUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(VelocityXmlUtil.class);
    private static final String DEFAULT_ENCODING = "UTF-8";
    private static Properties p = new Properties();
    static {
        p.setProperty(Velocity.RESOURCE_LOADER, "class");
        p.setProperty(Velocity.ENCODING_DEFAULT, "UTF-8");
        p.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
        p.setProperty(Velocity.OUTPUT_ENCODING, "UTF-8");
        p.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        p.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.runtime.log.NullLogChute");//關閉日誌
    }
    /**
     *  pojo轉換成xml 默認編碼UTF-8
     *
     * @author Mike
     * @date 2018/10/11 18:03
     * @param map
     * @param templateFilePathAndName 資源文件路徑:比如模板放在resources/template/example.vm,則basePath="template/example.vm"
     * @return   java.lang.String
     */
    public static String convertBeanToXml(Map<String, Object> map, String templateFilePathAndName) {
        return convertBeanToXml(map, templateFilePathAndName, DEFAULT_ENCODING);
    }
    /**
     *  pojo轉換成xml  自定義編碼
     *
     * @author Mike
     * @date 2018/10/11 18:03
     * @param map
     * @param templateFilePathAndName
     * @return   java.lang.String
     */
    public static String convertBeanToXml(Map<String, Object> map, String templateFilePathAndName, String encoding) {

        try {
            //Velocity 引擎初始化(singleton)
            Velocity.init(p);
            //實例化一個VelocityContext
            VelocityContext velocityContext = new VelocityContext(map);

            //實例化一個StringWriter
            StringWriter stringWriter = new StringWriter();

            Template template = Velocity.getTemplate(templateFilePathAndName, encoding);
            template.merge(velocityContext, stringWriter);
            stringWriter.flush();
            stringWriter.close();
            return stringWriter.toString();
        } catch (Exception e) {
            LOGGER.error("VelocityXmlUtil_convertBeanToXml_Exception:{}", e.getMessage());
        }
        return "";
    }
}

  • Velocity依賴
	<!--velocity start-->
	  <dependency>
		  <groupId>org.apache.velocity</groupId>
		  <artifactId>velocity</artifactId>
		  <version>1.7</version>
	  </dependency>
	  <dependency>
		  <groupId>org.apache.velocity</groupId>
		  <artifactId>velocity-tools</artifactId>
		  <version>2.0</version>
	  </dependency>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章