项目结构
gradle注意事项
// compile group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf'
compile("org.thymeleaf:thymeleaf:3.0.6.RELEASE")
compile group:'org.thymeleaf',name:'thymeleaf-spring4',version:'3.0.6.RELEASE'
compile("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.2.2")
如果引用'spring-boot-starter-thymelea'将默认使用Thymeleaf2而不是3
代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;
/**
* Created by think on 2017/7/5.
*/
@Configuration
public class ThymeleafConfigure {
@Bean
public ITemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setTemplateMode("HTML5");
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("utf-8");
templateResolver.setOrder(1);
templateResolver.setCacheable(false);
templateResolver.setApplicationContext(UnionpayApplication.getApplicationContext()); //这里是你的自己的MIANf方法入口
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
engine.addDialect(new UnionPayDialect());
return engine;
}
}
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.engine.AttributeName;
import org.thymeleaf.exceptions.TemplateProcessingException;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.standard.expression.IStandardExpression;
import org.thymeleaf.standard.expression.IStandardExpressionParser;
import org.thymeleaf.standard.expression.StandardExpressionParser;
import org.thymeleaf.util.EvaluationUtils;
import org.thymeleaf.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;
import static org.thymeleaf.util.StringUtils.trim;
import static org.thymeleaf.util.Validate.notEmpty;
import static org.thymeleaf.util.Validate.notNull;
/**
* Created by think on 2017/7/5.
*/
public final class ThymeleafFacade {
private ThymeleafFacade() {
throw new UnsupportedOperationException();
}
public static String getRawValue(final IProcessableElementTag element, final AttributeName attributeName) {
notNull(element, "element must not be null");
notNull(attributeName, "attributeName must not be empty");
final String rawValue = trim(element.getAttributeValue(attributeName));
notEmpty(rawValue, "value of '" + attributeName + "' must not be empty");
return rawValue;
}
public static String getRawValue(final IProcessableElementTag element, final String attributeName) {
notNull(element, "element must not be null");
notEmpty(attributeName, "attributeName must not be empty");
final String rawValue = trim(element.getAttributeValue(attributeName));
notEmpty(rawValue, "value of '" + attributeName + "' must not be empty");
return rawValue;
}
public static Object evaluateExpression(ITemplateContext arguments, String expression) throws TemplateProcessingException {
notNull(arguments, "arguments must not be null");
notEmpty(expression, "expression must not be empty");
final IStandardExpressionParser parser = new StandardExpressionParser();
final IStandardExpression evaluableExpression = parser.parseExpression(arguments, expression);
return evaluableExpression.execute(arguments);
}
public static List<Object> evaluateAsIterable(ITemplateContext arguments, String rawValue) throws TemplateProcessingException {
notNull(arguments, "arguments must not be null");
notEmpty(rawValue, "rawValue must not be empty");
final Object evaluatedExpression = evaluateExpression(arguments, rawValue);
return EvaluationUtils.evaluateAsList(evaluatedExpression);
}
public static List<Object> evaluateAsIterableOrRawValue(ITemplateContext arguments, String rawValue) {
notNull(arguments, "arguments must not be null");
notEmpty(rawValue, "rawValue must not be empty");
final List<Object> result = new ArrayList<Object>();
try {
result.addAll(evaluateAsIterable(arguments, rawValue));
} catch (TemplateProcessingException ex) {
result.add(rawValue);
}
return unmodifiableList(result);
}
public static List<String> evaluateAsStringsWithDelimiter(ITemplateContext arguments, String rawValue, String delimiter) {
notNull(arguments, "arguments must not be null");
notEmpty(rawValue, "rawValue must not be empty");
notEmpty(delimiter, "delimiter must not be empty");
final List<String> result = new ArrayList<String>();
final List<Object> iterates = evaluateAsIterableOrRawValue(arguments, rawValue);
for (Object o : iterates) {
result.addAll(asList(StringUtils.split(o, delimiter)));
}
return unmodifiableList(result);
}
}
import org.thymeleaf.dialect.AbstractProcessorDialect;
import org.thymeleaf.processor.IProcessor;
import org.thymeleaf.standard.StandardDialect;
import java.util.HashSet;
import java.util.Set;
/**
* Created by think on 2017/7/5.
*/
public class UnionPayDialect extends AbstractProcessorDialect {
private static final String DIALECT_NAME = "UnionPay Dialect";
public UnionPayDialect() {
// We will set this dialect the same "dialect processor" precedence as
// the Standard Dialect, so that processor executions can interleave.
super(DIALECT_NAME, "unionpay", StandardDialect.PROCESSOR_PRECEDENCE);
}
@Override
public Set<IProcessor> getProcessors(String dialectPrefix) {
final Set<IProcessor> processors = new HashSet<IProcessor>();
processors.add(new UnionpayStringProcessor(dialectPrefix));
processors.add(new UnionpayNumberProcessor(dialectPrefix));
return processors;
}
}
import org.apache.commons.lang3.StringUtils;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.engine.AttributeName;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IModelFactory;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.element.AbstractAttributeTagProcessor;
import org.thymeleaf.processor.element.IElementTagStructureHandler;
import org.thymeleaf.templatemode.TemplateMode;
import org.unbescape.html.HtmlEscape;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.List;
import static cn.gotoil.unionpay.config.thymeleaf.ThymeleafFacade.evaluateAsStringsWithDelimiter;
import static cn.gotoil.unionpay.config.thymeleaf.ThymeleafFacade.getRawValue;
/**
* Created by think on 2017/7/5.
*/
public class UnionpayNumberProcessor extends AbstractAttributeTagProcessor {
private static final String DELIMITER = ",";
private static final String ATTR_NAME = "number";
private static final int PRECEDENCE = 100;
private static final String F2Y="moneyF2y"; //金额分转元保留2位小数
public UnionpayNumberProcessor(final String dialectPrefix) {
super(
TemplateMode.HTML, // This processor will apply only to HTML mode
dialectPrefix, // Prefix to be applied to name for matching
null, // No tag name: match any tag name
false, // No prefix to be applied to tag name
ATTR_NAME, // Name of the attribute that will be matched
true, // Apply dialect prefix to attribute name
PRECEDENCE, // Precedence (inside dialect's own precedence)
true); // Remove the matched attribute afterwards
}
@Override
protected void doProcess(ITemplateContext context,
IProcessableElementTag tag, AttributeName attributeName,
String attributeValue, IElementTagStructureHandler structureHandler) {
final String rawValue = getRawValue(tag, attributeName); //获取标签内容表达式
String method=null;
String exper=null;
if(StringUtils.isNotEmpty(rawValue)){
method=rawValue.split(":")[0]; //获取类型
exper=rawValue.split(":")[1]; //获取表达式
}
//通过IStandardExpression 解析器 解析表达式获取参数
final List<String> values = evaluateAsStringsWithDelimiter(context, exper, DELIMITER);
final String elementCompleteName = tag.getElementCompleteName(); //标签名
//创建模型
final IModelFactory modelFactory = context.getModelFactory();
final IModel model = modelFactory.createModel();
//添加模型 标签
model.add(modelFactory.createOpenElementTag(elementCompleteName));
for (String value : values) {
//创建 html5标签 文本返回数据
if(F2Y.equalsIgnoreCase(method)){
try{
double n = NumberFormat.getInstance().parse(value).doubleValue();
model.add(modelFactory.createText(HtmlEscape.escapeHtml5(formatMoney(n))));
}catch (ParseException e){
model.add(modelFactory.createText(HtmlEscape.escapeHtml5("0")));
}
}
}
//添加模型 标签
model.add(modelFactory.createCloseElementTag(elementCompleteName));
//替换页面标签
structureHandler.replaceWith(model, false);
}
private String formatMoney(double value) {
DecimalFormat df = new DecimalFormat("0.00");//格式化小数,不足的补0
String v = df.format(value/100);//返回的是String类型的
return v;
}
}
import org.apache.commons.lang3.StringUtils;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.engine.AttributeName;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IModelFactory;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.element.AbstractAttributeTagProcessor;
import org.thymeleaf.processor.element.IElementTagStructureHandler;
import org.thymeleaf.templatemode.TemplateMode;
import org.unbescape.html.HtmlEscape;
import java.util.List;
import static cn.gotoil.unionpay.config.thymeleaf.ThymeleafFacade.evaluateAsStringsWithDelimiter;
import static cn.gotoil.unionpay.config.thymeleaf.ThymeleafFacade.getRawValue;
/**
* Created by think on 2017/7/5.
*/
public class UnionpayStringProcessor extends AbstractAttributeTagProcessor {
private static final String DELIMITER = ",";
private static final String ATTR_NAME = "string";
private static final int PRECEDENCE = 100;
private static final String LongTextFormat = "longTextFormat";
private static final String CARD = "card";
private static final String MOBILE = "mobile";
private static final String IDENTITY = "identity";
private static final String CSN = "csn";
public UnionpayStringProcessor(final String dialectPrefix) {
super(
TemplateMode.HTML, // This processor will apply only to HTML mode
dialectPrefix, // Prefix to be applied to name for matching
null, // No tag name: match any tag name
false, // No prefix to be applied to tag name
ATTR_NAME, // Name of the attribute that will be matched
true, // Apply dialect prefix to attribute name
PRECEDENCE, // Precedence (inside dialect's own precedence)
true); // Remove the matched attribute afterwards
}
@Override
protected void doProcess(ITemplateContext context,
IProcessableElementTag tag, AttributeName attributeName,
String attributeValue, IElementTagStructureHandler structureHandler) {
final String rawValue = getRawValue(tag, attributeName); //获取标签内容表达式
String method = null;
String exper = null;
if (StringUtils.isNotEmpty(rawValue)) {
method = rawValue.split(":")[0]; //获取类型
exper = rawValue.split(":")[1]; //获取表达式
}
//通过IStandardExpression 解析器 解析表达式获取参数
final List<String> values = evaluateAsStringsWithDelimiter(context, exper, DELIMITER);
final String elementCompleteName = tag.getElementCompleteName(); //标签名
//创建模型
final IModelFactory modelFactory = context.getModelFactory();
final IModel model = modelFactory.createModel();
//添加模型 标签
model.add(modelFactory.createOpenElementTag(elementCompleteName));
for (String value : values) {
//创建 html5标签 文本返回数据
if (CARD.equals(method)) {
model.add(modelFactory.createText(HtmlEscape.escapeHtml5(getCardNo(value))));
} else if (MOBILE.equals(method)) {
model.add(modelFactory.createText(HtmlEscape.escapeHtml5(getMobile(value))));
} else if (IDENTITY.equals(method)) {
model.add(modelFactory.createText(HtmlEscape.escapeHtml5(getIdentity(value))));
} else if (CSN.equals(method)) {
model.add(modelFactory.createText(HtmlEscape.escapeHtml5(getCsn(value))));
} else if (LongTextFormat.equals(method)) {
int precLen = StringUtils.isEmpty(tag.getAttributeValue("prec")) ? 10 : Integer.parseInt(tag.getAttributeValue("prec"));
model.add(modelFactory.createText(HtmlEscape.escapeHtml5(getLongTextFormat(value, precLen))));
}
}
//添加模型 标签
model.add(modelFactory.createCloseElementTag(elementCompleteName));
//替换页面标签
structureHandler.replaceWith(model, false);
}
private String getLongTextFormat(String value, int i) {
if (StringUtils.isEmpty(value) || value.length() < i) {
return value;
} else {
return value.substring(0, i) + "...";
}
}
protected String getCardNo(String cardNo) {
if (StringUtils.isNotBlank(cardNo) && cardNo.length() >= 9) {
return cardNo.substring(0, 4) + cardNo.substring(4, cardNo.length() - 3).replaceAll("[0-9]", "*") + cardNo.substring(cardNo.length() - 4, cardNo.length());
}
return cardNo;
}
protected static String getIdentity(String val) {
if (StringUtils.isEmpty(val) || val.length() < 9) {
return val;
} else {
return val.substring(0, 4) + val.substring(4, val.length() - 4).replaceAll("[0-9]", "*") + val.substring(val.length() - 4, val.length());
}
}
/**
* 前四后四显示
*
* @param val
* @return
*/
protected static String getMobile(String val) {
if (StringUtils.isEmpty(val) || val.length() < 9) {
return val;
} else {
return val.substring(0, 3) + val.substring(4, val.length() - 3).replaceAll("[0-9]", "*") + val.substring(val.length() - 4, val.length());
}
}
/**
* 星星显示
*
* @param val
* @return
*/
protected String getCsn(String val) {
if (StringUtils.isEmpty(val) || val.length() < 12) {
return val;
} else {
return val.substring(0, 2) + val.substring(2, val.length() - 3).replaceAll("[0-9a-zA-Z]", "*") + val.substring(val.length() - 6, val.length());
}
}
}
页面使用
<div unionpay:number="moneyF2y:${bean.orderFee}" ></div>
<div unionpay:string="longTextFormat:${bean.appName}" prec=10 th:title="${bean.appName}" ></div> //这里的prec 是指保留的长度 如果不设置也会有默认值
其他
我这里是处理我自己的业务。希望能帮到大家。