场景: 项目中常常会有一些message , 如邮件, 短信, UI的提示信息, 多数情况,写在代码中,或者配置文件xxx.properties, @value 或者读取xxx.properties ,这两种方案都...
1.好处,统一管理
2.动态管理,如配置了appolo , 配置中心
3. 配置国际化, 如中文 ,英文
方式0 (spring 自带工具PropertyPlaceholderHelper, 可以换用 @{ 作为前缀,解决@value 不可解析的问题 )
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.SystemPropertyUtils;
import java.util.Properties;
public class MsgUtil {
public static void main(String[] args) {
PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX, SystemPropertyUtils.PLACEHOLDER_SUFFIX);
String offline_ivt_email_notify_template="${userName} ,您好!<br>你已经通过 <strong> 【${companyName}】.</strong><strong> [${job}] </strong> 职位的线上面试";
Properties properties = new Properties() {{
put("userName", "userName");
put("companyName", "companyName");
put("job", "job");
}};
String content= propertyPlaceholderHelper .replacePlaceholders(offline_ivt_email_notify_template, properties);
System.err.println(content);
}
}
方式1:( 消息模板占位符使用与详解)
消息模板
在消息相关的系统开发过程中,我们通常会对用户提供消息模板的配置,如短信模板,通讯软件消息模板,邮件消息模板,站内信消息模板等,用户配置好模板后,可调用消息系统的api,传递消息参数,消息系统根据模板内容对占位符进行替换,发送消息。
-
模板样例
尊敬的:${userName} 您好,您有${integralCount}积分未使用,现有${activityName}活动,大礼包为您准备好了!戳${activityUrl}参加吧!
freemarker
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency>
案例一:
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.Version;
@Test public void test() { try { String templateName ="ActivityMessage"; Template template = new Template(templateName, getTemplate(), new Configuration(Configuration.VERSION_2_3_30)); // Template template1 = new Template(templateName, getTemplate(), new Configuration(new Version("2.3.30"))); StringWriter result = new StringWriter(); template.process(getParams(),result); System.out.println(result.toString()); } catch (Exception e) { e.printStackTrace(); } } public Map<String, Object> getParams() { Map<String, Object> params =new HashMap<>(8); params.put("userName","Sea"); params.put("integralCount",100.3456); params.put("activityName","新用户活动"); params.put("activityUrl","jd.com"); return params; } // 三目运算 ${ (type == ‘SPOT’) ?string(‘热点词’,’热点搜索’)} public String getTemplate() { // return "尊敬的:${userName} 您好,您有${integralCount?string(\"#.##\")}积分未使用,现有${activityName}活动,大礼包为您准备好了!戳${activityUrl}参加吧!"; //if userName==Sea print("my boss" ) 条件语句 <#if userName == \"Sea\">, my boss </#if>" return "尊敬的:${userName} <#if userName == \"Sea\">, my boss </#if>" + " 您好,您有${integralCount}积分未使用,现有${activityName}活动,大礼包为您准备好了!戳${activityUrl}参加吧!"; }
案例二:
@Test public void notifyMsgTemplate() throws Exception{ // 替换 ${name?replace("\n","好好")} // 三目表达式 ${ (sex=='1')? string('先生','女士')} // 取得list的长度:${fields?size} // 字符串分割 ${(reservationTime?split(','))?size} // if 条件语句 <#if userName == \"Sea\">, my boss </#if>" // 遍历list <#list (reservationTime?split(',')) as k> ${k} ---- </#list> String messageTemplate="尊敬的: ${inviteeName} ${ (sex=='1')? string('先生','女士')}"+ " 你已经通过 ${company} : ${job} 职位的初面," + // "诚挚的邀请你 在如下时间 ${reservationTime} ${((reservationTime?split(','))?size>0)?string('之一','')} 到公司完成面试 , \n" + "诚挚的邀请你 在如下时间 ${reservationTime} <#if ((reservationTime?split(','))?size>0) > 之一 </#if> 到公司完成面试 , \n" + "诚挚的邀请你 在如下时间 <#list (reservationTime?split(',')) as k> ${k} ---- </#list> <#if ((reservationTime?split(','))?size>0) > 之一 </#if> 到公司完成面试 , \n" + "诚挚的邀请你 在如下时间 ${reservationTime?replace(',',' 或者 ')} <#if ((reservationTime?split(','))?size>0) > 之一 </#if> 到公司完成面试 , \n" + "请在[Sea-APP]上确认前往公司面试的时间,以便人事安排时间, 感谢! 智帮惠-职聘APP下载链接: https://www.sea.com/sea-app.apk"; JSONObject params = new JSONObject() {{ put("inviteeName", "lily"); put("sex", "0"); put("job", "高级技师"); put("company", "Sea conpany"); put("reservationTime", "4月6号 9:00,4月7号 9:00"); }}; Template template = new Template("xx", messageTemplate, new Configuration(Configuration.VERSION_2_3_30)); StringWriter result = new StringWriter(); template.process(params,result); System.out.println(result.toString()); }
方式2: commons-text
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.10.0</version>
</dependency>
@Test public void void() { StringSubstitutor strSubstitutor =new StringSubstitutor(getParams()); String messageContent =strSubstitutor.replace(getTemplate()); System.out.println(messageContent); } public Map<String, Object> getParams() { Map<String, Object> params =new HashMap<>(8); params.put("userName","fly"); params.put("integralCount",100.3456); params.put("activityName","新用户活动"); params.put("activityUrl","jd.com"); return params; } public String getTemplate() { return "尊敬的:${userName} 您好,您有${integralCount}积分未使用,现有${activityName}活动,大礼包为您准备好了!戳${activityUrl}参加吧!"; }
在模板类的展位符的场景下,通常使用相同名称参数替换的方式,我们可以采用freemarker或者commons-text,相对来讲commons-text使用起来代码更加简洁。
方式3:(更加适合国际化)
使用:
一.依赖:
1 2 3 4 | <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> |
二.配置文件xxx.properties位置:
默认名字为 messages.properties
内容 :eg
//event.validation.content= Notification \n{0} exception was detected, caused by :\n\n{1} \n\nSincerely \XXX Team //event.validation.subject=ERROR: Validation error found for booking <{0}>
三.配置类:
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.stereotype.Component; import java.util.Locale; @Component @Slf4j public class MesgHelper { private static MessageSource messageSource; public static MessageSource getMessageSource() { return messageSource; } @Autowired public void setMessageSource(MessageSource msgSource) { MesgHelper.messageSource = msgSource; } /** * Convert single message according to locale. * * @param mesgKey * @param values * @param locale * @return */ public static String getMessage(String mesgKey,Object[] values,Locale locale) { Locale locale1 = locale!=null?locale:Locale.US; String mesgText = null; if(mesgKey != null && locale1 != null) { mesgText = messageSource.getMessage(mesgKey, values, locale1); } return mesgText; } }
四.使用:
//注入 @Autowired MesgHelper mesgHelper private static Runnable validationNotify(String bookingNo,String mesgKeys,String body) { return ()->{ log.debug("Current thread is {}",Thread.currentThread()); log.info("@@!!@@ NOT pass the null validation checking , below data not allow null: {}", mesgKeys); LinkedHashMap msgBody = JSONObject.parseObject(body,new TypeReference<LinkedHashMap<String, Object>>(){} ,Feature.OrderedField); String mailBody = JSONObject.toJSONString(msgBody, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue); //参数变量 用{0} {2} 表示 //event.validation.content= Notification \n{0} exception was detected, caused by :\n\n{1} \n\nSincerely \XXX Team //event.validation.subject=ERROR: Validation error found for booking <{0}> //参数变量 Object[] paras = {bookingNo}; String subject = mesgHelper.getMessage("event.validation.subject", paras, null); //参数变量 Object[] params = { mesgKeys, mailBody}; String content = mesgHelper.getMessage("event.validation.content", params, null); MailUtils.sendMail(auoExOp,from,subject,content,to); }; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?