介绍
当我们有个固定的模板,但是里面某些值不一样,可以使用该工具类将里面的变量进行替换!
使用案例
对象传递 (支持取子类或父类)
User user = new User();
user.setName("依桐");
user.setAge(21);
User.Detail detail = new User.Detail();
user.setDetail(detail);
User.Detail.Ability ability = new User.Detail.Ability();
ability.setHobbyList(new ArrayList<String>() {{
add("Java");
}});
detail.setAbility(ability);
String contentByObj = "我是${user.name} ,今年${user.age}岁, 我的爱好:${user.detail.ability.hobbyList}";
String result = PlaceholderHelper.resolve(contentByObj, user);
//输出内容:我是依桐 ,今年21岁, 我的爱好:[Java]
System.out.println(result);
Map传递(支持取多维Map/对象)
User user = new User();
user.setName("依桐 - 对象");
user.setAge(21);
User.Detail detail = new User.Detail();
detail.setPhone("123456789");
user.setDetail(detail);
User.Detail.Ability ability = new User.Detail.Ability();
ability.setHobbyList(new ArrayList<String>() {{
add("Java");
}});
detail.setAbility(ability);
Map<String, Object> valueMap = new HashMap<>();
valueMap.put("name", "依桐");
valueMap.put("age", 21);
valueMap.put("user", user);
String contentByMap = "我是${name} ,今年${age}岁, 我的爱好:${user.detail.ability.hobbyList}";
String result = PlaceholderHelper.resolve(contentByMap, valueMap);
//输出内容:我是依桐 ,今年21岁, 我的爱好:[Java]
System.out.println(result);
PlaceholderHelper 占位符解析器工具代码
/**
* 占位符解析器
*
* @author www.jufb.cn
*/
public class PlaceholderHelper {
/**
* 默认前缀占位符
*/
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
/**
* 默认后缀占位符
*/
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
/**
* 占位符前缀长度
*/
public static final int PREFIX_LENGTH = DEFAULT_PLACEHOLDER_PREFIX.length();
/**
* 占位符后缀长度
*/
public static final int SUFFIX_LENGTH = DEFAULT_PLACEHOLDER_SUFFIX.length();
/**
* 根据map解析占位符
*
* @param content 内容
* @param valueMap map
* @return 解析后的内容
*/
public static String resolve(String content, Map<String, Object> valueMap) {
return resolveByRule(content, placeholder -> {
String[] fieldNames = placeholder.split("\\.");
//如果是多级占位符 如:user.name 那么可能是map中的key,也可能是对象中的属性 优先取map中的key 如果没有则取对象中的属性
if (fieldNames.length > 1) {
//先取map中的key
Object value = valueMap.get(placeholder);
if (null != value) {
return String.valueOf(value);
}
//获取根对象 如:user.name 那么先取user对象
Object obj = valueMap.get(fieldNames[0]);
return String.valueOf(getFieldValue(fieldNames, obj));
}
return String.valueOf(valueMap.get(placeholder));
});
}
/**
* 根据对象解析占位符
*
* @param content 内容
* @param obj 对象
* @return 解析后的内容
*/
public static String resolve(String content, Object obj) {
if (null == content || null == obj) {
return content;
}
return resolveByRule(content, placeholder -> {
String[] fieldNames = placeholder.split("\\.");
//获取根对象 如:user.name 那么先取user对象
return String.valueOf(getFieldValue(fieldNames, obj));
});
}
/**
* 获取对象中字段的值
*
* @param fieldNames 字段名数组
* @param obj 对象
* @return 属性值
*/
private static Object getFieldValue(String[] fieldNames, Object obj) {
//适配直接根据对象中的属性获取值 如:name 实际上是取user.name 但是必须从根对象开始取
if (fieldNames.length == 1) {
return getFieldValue(obj, fieldNames[0]);
}
//取对象中的属性 如:user.name 那么先取user对象,然后取name属性
for (int i = 1; i < fieldNames.length; i++) {
//获取属性值
Object fieldValue = getFieldValue(obj, fieldNames[i]);
//如果属性值为空,则返回null字符串
if (null == fieldValue) {
return "null";
}
//如果属性值不为空,则将属性值赋值给objClass
obj = fieldValue;
}
return obj;
}
/**
* 获取对象中字段的值
*
* @param obj 对象
* @param fieldName 字段名
* @return 属性值
*/
public static Object getFieldValue(Object obj, String fieldName) {
if (null == obj || null == fieldName) {
return null;
}
//多级Map嵌套的情况 如:userMap.detailMap.name 那么先取userMap,然后取detailMap,最后取name属性
if (obj instanceof Map) {
return ((Map<?, ?>) obj).get(fieldName);
}
//获取字段
Field field = getField(obj.getClass(), fieldName);
//获取字段的值
return getFieldValue(field, obj);
}
/**
* 用于获取对象中字段的值
*
* @param field 字段
* @param obj 对象
* @return 属性值
*/
public static Object getFieldValue(Field field, Object obj) {
if (null == field) {
return null;
}
//将字段设置为可访问 就是将private修饰符的字段也能访问
makeAccessible(field);
try {
return field.get(obj);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/**
* 从类中获取字段
*
* @param clazz 类
* @param fieldName 字段名
* @return
*/
public static Field getField(Class<?> clazz, String fieldName) {
if (null == clazz || null == fieldName || fieldName.trim().isEmpty()) {
return null;
}
// 获取所有属性
while (clazz != Object.class && clazz != null) {
for (Field declaredField : clazz.getDeclaredFields()) {
if (fieldName.equals(declaredField.getName())) {
return declaredField;
}
}
// 获取父类 递归查找
clazz = clazz.getSuperclass();
}
return null;
}
/**
* 根据规则解析占位符
*
* @param content 内容
* @param rule 规则 回调函数
* @return 解析后的内容
*/
public static String resolveByRule(String content, Function<String, String> rule) {
// 获取第一个占位符的位置开始
int start = content.indexOf(DEFAULT_PLACEHOLDER_PREFIX);
if (start == -1) {
return content;
}
// 用于替换占位符的内容
StringBuilder result = new StringBuilder(content);
while (start != -1) {
//从初始占位符开始查找结束符
int end = result.indexOf(DEFAULT_PLACEHOLDER_SUFFIX, start);
//获取占位符属性值,如${id}, 即获取id
String placeholder = result.substring(start + PREFIX_LENGTH, end);
//替换整个占位符内容,即将${id}值替换为替换规则回调中的内容
String replaceContent = placeholder.trim().isEmpty() ? "" : rule.apply(placeholder);
result.replace(start, end + SUFFIX_LENGTH, replaceContent);
//从替换后的内容中向后继续查找占位符
start = result.indexOf(DEFAULT_PLACEHOLDER_PREFIX, start + replaceContent.length());
}
return result.toString();
}
/**
* 设置字段为可见
*
* @param field 字段
*/
public static void makeAccessible(Field field) {
if ((!Modifier.isPublic(field.getModifiers()) ||
!Modifier.isPublic(field.getDeclaringClass().getModifiers()) ||
Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
field.setAccessible(true);
}
}
}
6 条评论
2025年10月新盘 做第一批吃螃蟹的人coinsrore.com
新车新盘 嘎嘎稳 嘎嘎靠谱coinsrore.com
新车首发,新的一年,只带想赚米的人coinsrore.com
新盘 上车集合 留下 我要发发 立马进裙coinsrore.com
做了几十年的项目 我总结了最好的一个盘(纯干货)coinsrore.com
新车上路,只带前10个人coinsrore.com
新盘首开 新盘首开 征召客户!!!coinsrore.com
新项目准备上线,寻找志同道合 的合作伙伴coinsrore.com
新车即将上线 真正的项目,期待你的参与coinsrore.com
新盘新项目,不再等待,现在就是最佳上车机会!coinsrore.com
新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com
作者的布局谋篇匠心独运,让读者在阅读中享受到了思维的乐趣。
在畅想未来时需警惕乌托邦式理想化。
不错不错,我喜欢看 www.jiwenlaw.com
怎么收藏这篇文章?
想想你的文章写的特别好