本文目录如下:
二、项目中细节实现 & 约束 & 注意事项
2.1 数据类型
规范的判断字符串是否为空
判断是否为
空
:StringUtils.isEmpty(str)
判断是否非空
:StringUtils.isNotEmpty(str)
// StringUtils.isEmpty(str) 内部实现
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
字符串替换涉及的正则表达式问题: Pattern.quote()方法的使用
Pattern.quote()
是一个用于创建 正则表达式模式 的方法。它的作用是将字符串中的 特殊字符 转义,以确保这些字符在正则表达式中被视为 普通字符 而不是具有特殊含义的元字符。
str = str .replaceAll(Pattern.quote("{"), "");
str = str .replaceAll(Pattern.quote("{"), "");
str = str .replaceAll(Pattern.quote("[") + "QUOTES" + Pattern.quote("]"), "'");
字符串中插入系统换行: System.lineSeparator()
System.lineSeparator()
是一个 Java 中用于获取系统行分隔符的方法。它返回一个表示当前平台上行分隔符的字符串。
content += System.lineSeparator() +
"<a href=\"" + PkpmHost + "api/Bus/TargetFlowUrl?s_employeeid=" + msgReceiver.getReceiverId() +
"&messageId=" + msgReceiver.getMessageId() +"\">去看看</a>";
// System.lineSeparator()是一个Java中用于获取系统行分隔符的方法。它返回一个表示当前平台上行分隔符的字符串。
System.lineSeparator();
入参 ‘Num’ 字段转换为 ‘num’ 的问题
使用
@JsonProperty
对 字段 进行标注即可。
2.2 时间日期
Java 获取时间 (Date类型) 并插入数据库
// 创建时间
Calendar calendar= Calendar.getInstance();
SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// calendar.add(Calendar.HOUR_OF_DAY, 8); // 在原时间上加 8 小时
try {
Date date = dateFormat.parse(dateFormat.format(calendar.getTime()));
// ...
} catch (ParseException e) {
e.printStackTrace();
}
获取当前日期简写方法
1.方法一
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 增加时间
LocalDateTime.now().plusSeconds(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
2.方法二
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
Java 计算两个时间点之间的时间差
Date arrivetime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dc.getArriveTime());
// Duration.between() 方法计算两个时间点之间的时间差,并返回一个 Duration 对象。
Duration duration = Duration.between(arrivetime.toInstant(), Instant.now());
long days = duration.toDays();
long hours = duration.toHours() - days * 24;
String tmp = Long.toString(days);
Java 在一个日期上添加 日期 时间
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dtArriveTime = LocalDateTime.parse(dt.get(k).getArriveTime(), formatter);
//超时日期时间
LocalDateTime dtOverDay = dtArriveTime;
String d = arr[1];
String h = arr[2];
if (!d.trim().equals("")) {
dtOverDay = dtOverDay.plusDays((long) Double.parseDouble(d.trim()));
}
if (!h.trim().equals("")) {
dtOverDay = dtOverDay.plusHours((long) Double.parseDouble(h.trim()));
}
Java 对比两个时间的大小
// Date 类型
private boolean isExpired(Date expireTime) {
Date currentDateTime = new Date();
return expireTime.before(currentDateTime);
}
// LocalDateTime 类型
private boolean isExpired(LocalDateTime expireTime) {
LocalDateTime currentDateTime = LocalDateTime.now();
return expireTime.isBefore(currentDateTime);
}
Spring Boot / Spring Cloud 入参属性是 日期格式 如何处理?
- 类中的属性如下,需要添加注解
@JsonFormat
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
@JsonProperty("ArriveTime")
private String ArriveTime;
- 使用时将该属性转换为
Date
类型:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = dateFormat.parse(ArriveTime);
} catch (ParseException e) {
e.printStackTrace();
}
2.3 面向对象 & 数据格式
Java 定义出参对象?
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResultData {
private static final Logger logger = LoggerFactory.getLogger(ResultData.class);
private static final long serialVersionUID = 1L;
@JsonProperty("IsSuccess")
private String IsSuccess;
@JsonProperty("Message")
private String Message;
private Object data;
}
Java实体类 与 Map 相互转换的方法
public static void main(String[] args){
Map<String, Object> map = new HashMap();
map.put("name", "谢清照");
map.put("age", 18);
//map转java对象
Student stu = JSONObject.parseObject(JSONObject.toJSONString(map), Student .class);
// Map<String, Object> s = JSON.parseObject(JSON.toJSONString(user), new TypeReference<Map<String, Object>>() {});
Student xqz= new Student ();
user.setName("谢清照");
user.setAge(18);
//java对象转map
Map<String, Object> xqzhao = JSONObject.parseObject(JSONObject.toJSONString(xqz));
}
Java对象 和 JSON 字符串 的相互转换 (序列化Serializable)
- 使用 Jackson 库将 Java对象 序列化为 JSON 字符串:
import com.fasterxml.jackson.databind.ObjectMapper;
// 将 Java对象 序列化为 JSON 字符串
String json = new ObjectMapper().writeValueAsString(corpSendText);
- 使用 Jackson 库将 JSON 字符串 序列化为 Java对象:
// 将 JSON 字符串 序列化为 Java对象
WxAccessToken token= new ObjectMapper().readValue(jsonStr, WxAccessToken.class);
Java 通过反射机制复制对象属性
Class<?> clazz = obj1.getClass();
// Class<?> clazz = Define.class; // Define 类
Field[] fields = clazz.getDeclaredFields(); // 获取所有字段
for (Field field : fields) {
// Field.setAccessible(true): // 设置可访问私有属性
field.setAccessible(true);
if (field.getName().equals("IsDelete")) { // field.getName(): 获取 Java 属性名
System.out.println(field.get(obj1)); // field.get(obj1): 获取 Java 字段值
} else {
Object value = field.get(obj1);
if (value != null) {
// field.set(obj2, value);
clazz.getMethod(setterMethodName(field.getName())).invoke(obj2, field.get(obj1)); // 通过反射调用Java方法设置属性值
}
}
}
public static String setterMethodName(String fieldName) {
return "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1); // 将字段名首字母大写并添加 set 前缀
}
使用反射机制的场景:
- 需要获取一个 未知类对象 的所有 属性 和 值 的时候。
- 一个 对象的属性过多 但是需要获取该对象的所有 属性 和 值 的时候。
Java 创建比较器对 List 对象进行排序
// 比较器
public class SimpleEmployeeCompare implements Comparator<SimpleEmployee> {
@Override
public int compare(SimpleEmployee a, SimpleEmployee b) {
byte[] byteAFirstLetter = a.getEmployeeName().substring(0, 1).getBytes();
byte[] byteBFirstLetter = b.getEmployeeName().substring(0, 1).getBytes();
// 如果是字母与汉字比较
if ((byteAFirstLetter.length == 1 && byteBFirstLetter.length != 1)
|| (byteAFirstLetter.length != 1 && byteBFirstLetter.length == 1)) {
String aFirstLetter = "";//Utility.getCharSpellCode(a.getEmployeeName().substring(0, 1));
String bFirstLetter = "";//Utility.getCharSpellCode(b.getEmployeeName().substring(0, 1));
return aFirstLetter.toUpperCase().compareTo(bFirstLetter.toUpperCase());
}
return a.getEmployeeName().toUpperCase().compareTo(b.getEmployeeName().toUpperCase());
}
}
// ----------------------------------------------------------------------------------------------------
// 在其他方法里使用 比较器 进行 排序
Collections.sort(tmp.getListSimpleEmployee(), new SimpleEmployeeCompare());
2.4 异步编程
CompletableFuture 常用的异步操作有哪些?
runAsync
: 以 Runnable函数式接口 类型为参数,没有返回结果supplyAsync
: 以 Supplier函数式接口 类型为参数,返回结果类型为 U;Supplier接口 的 get() 方法 是有返回值的(会阻塞)
CompletableFuture 的方法,主要由几个关键单词组成的
apply / accept / run
、then / both / either
和async
组合成各方法的含义。
CompletableFuture 常用方法有哪些?
thenApply()
:把前面任务的执行结果,交给后面的 FunctionthenCombine()
:合并任务,有返回值runAfterBoth()
:两个任务都执行完成后,执行下一步操作 (Runnable类型任务)allOf()
:当所有给定的 CompletableFuture 完成时,返回一个新的 CompletableFuturewhenComplete()
:当任务完成时,将使用结果(或 null)和此阶段的异常(或 null如果没有)执行给定操作exceptionally()
:返回一个新的 CompletableFuture,当前面的 CompletableFuture 完成时,它也完成,当它异常完成时,给定函数的异常触发这个 CompletableFuture 的完成
join()
和get()
: 都是用来获取CompletableFuture异步之后的 返回值。注
: get() 需要手动 抛出异常。
Java 创建异步操作 (Async、有返回值 和 无返回值)
// 返回 Void 类型
Runnable runnable = () -> System.out.println("无返回结果异步任务");
CompletableFuture<Void> future = CompletableFuture.runAsync(runnable);
// 简写
return CompletableFuture.runAsync(() -> {
System.out.println("无返回结果异步任务");
});
// ----------------------- 有返回返回 String 类型 -----------------------
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("有返回值的异步任务");
return "Hello World";
});
// return future;
String result = future.get();
Java 创建异步函数 并 调用异步函数
public CompletableFuture<void> sendMessageSingle() {
return CompletableFuture.runAsync(() -> {
// 异步操作逻辑
});
}
public void test() {
// 直接调用
CompletableFuture<Void> futureResult = SendMessageSingle();
futureResult.join(); // 等待异步操作完成
// 创建线程调用异步函数
Executors.newSingleThreadExecutor().submit(() -> _weChatHelper.SendMessageList(sendMessageInfos));
return;
// return futureResult.join(); // 等待异步操作完成并获取结果
}
2.5 其他
Java 生成唯一标识符 UUID
在 Java 中,可以使用
UUID.randomUUID()
方法 生成一个随机的 UUID 对象,具体形式为 xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx,其中 x 为 十六进制 数字。
String uuid = UUID.randomUUID().toString();
// 为了获取 不含连接符 的字符串形式,需要调用toString()方法,并通过replace("-", "")方法将所有连接符替换为空字符串。
String uuid = UUID.randomUUID().toString().replace("-", "");
通过 HttpClient 发送 Post请求
扩展: 在 Java 中,可以使用多种方式发送 HTTP请求。以下是几种常见的方法:
1.使用 Java标准库(JDK自带)的 java.net 包:
HttpURLConnection
:缺点:API 相对较低级,使用起来不够方便;功能有限。HttpClient
:缺点: 使用起来 相对复杂;较老的API设计。
2.使用 第三方库:
Apache HttpClient
:优点:与 HttpClient 相似,但拥有更加现代化的API设计和更好的性能。OkHttp
:OkHttp 是 Square 开发的一款 轻量级HTTP客户端库,具有简单易用、性能优越等特点。
3.Spring 提供的
RestTemplate
: 缺点:不支持 异步 和 非阻塞 调用。
// 创建一个 StringEntity 对象
StringEntity content = new StringEntity("测试内容!!!", ContentType.APPLICATION_JSON);
content.setContentEncoding("UTF-8");
// 创建请求客户端
HttpClient httpClient = HttpClients.createDefault();
// 创建HttpPost请求
HttpPost httpPost = new HttpPost(url);
try {
// 设置POST参数
httpPost.setEntity(content);
// 发送POST请求并获取响应
HttpResponse response = httpClient.execute(httpPost);
// 解析响应
HttpEntity entity = response.getEntity();
System.out.println(EntityUtils.toString(entity));
} catch (IOException e) {
e.printStackTrace();
}
通过 HttpURLConnection 发送远程请求
String url = "https://qyapi.weixin.qq.com/xqzxqz/getxqz?corpid=" + _corpId + "&corpsecret=" + _secret;
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
int statusCode = connection.getResponseCode();
if (statusCode != HttpURLConnection.HTTP_OK) {
throw new IOException("AccessToken 获取失败");
}
ObjectMapper mapper = new ObjectMapper();
WxAccessToken response = mapper.readValue(connection.getInputStream(), WxAccessToken.class);
Spring Boot 处理跨域问题
- 步骤一: 添加
CorsConfig.java
文件:
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry){
//设置允许跨域的路径
registry.addMapping ("/**")
//设置允许跨域请求的域名
.allowedOrigins ("*")
//是否允许证书
.allowCredentials (true)
//设置允许的方法
.allowedMethods ("GET","POST")
//设置允许的header属性
.allowedHeaders ("*")
//允许跨域时间
.maxAge (3600);
}
}
- 步骤二: 在
Controller
上添加注解:@CrossOrigin
@CrossOrigin
@RestController
@RequestMapping("/DeptUser")
public class DeptUserController {
// ...
}
Spring Cloud 处理跨域问题
通过 Spring Cloud Gateway 网关配置,有三种配置方法:
- 普通过滤器
- 默认过滤器
- 全局过滤器
AES 实现加密函数, 供模块调用
@Component
public class AesUtil {
private final static String keyStr = "xqzhao";
public static String AesEncrypt(String data) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(keyStr.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedData = cipher.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(encryptedData);
}
public static String AesDecrypt(String data) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(keyStr.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedData = cipher.doFinal(Base64.getDecoder().decode(data));
return new String(decryptedData);
}
}
DES 实现加密函数, 供模块调用
@Component
public class DesUtil {
private final static String ALGORITHM_DES = "DES/ECB/PKCS5Padding";
// 提供原始秘钥:长度64位,8字节
private final static String keyStr = "xqzhao";
public static String decrypt(String str) throws Exception {
byte[] key = keyStr.getBytes();
byte[] data = Base64.getDecoder().decode(str);
Cipher cipher = Cipher.getInstance(ALGORITHM_DES);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "DES");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
return new String( cipher.doFinal(data), "GB2312");
}
public static String encrypt(String str) throws Exception {
SecretKeySpec key = new SecretKeySpec(keyStr.getBytes(), "DES");
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encipherByte = cipher.doFinal(str.getBytes("GB2312"));
return Base64.getEncoder().encodeToString(encipherByte);
}
}
三、Java8新特性 - Lambda表达式、方法引用、Stream流
3.1 Lambda表达式
什么是 Lambda表达式
Lambda
表达式 是 Java8 引入的新特性,它是一种 轻量级 的 匿名函数,可以像普通方法一样 传递 和 使用,并且没有名称。Lambda
表达式 的形式为:(parameters) -> expression
,其中parameters
是方法参数列表,可以为空或包含一个或多个参数,多个参数之间用逗号分隔;->
右边的expression
可以是任何有效的 Java 表达式,或者是一段代码块。
Lambda表达式 使用案例?
3.2 方法引用
什么是 方法引用?
Java8 中的
方法引用
(Method Reference) 是一种快速简便地将 现有方法 转换为Lambda
表达式 的方式。它可以使代码更加 简洁易懂,减少重复性的代码,提高代码的 可读性 和 可维护性。
方法引用语法如下:
对象::实例方法名
类::静态方法名
类::实例方法名
其中,第一个参数 表示方法的接收者(如果是实例方法)。例如,对于
System.out::println
,System.out
是接收者,println
是方法名。如果方法是静态的,则第一个参数表示方法所在类的名字,例如Math::sqrt
。
方法引用 使用案例?
3.3 Stream流
什么是 Stream流?
Stream
是 Java 8 新增的一种处理数据集合的方式,可以看作是对 集合(Collection) 对象功能 的增强。Stream 流 可以理解为是一种高级版本的 Iterator 迭代器,它提供了非常丰富强大的数据操作功能。
Stream
流 的特点包括:
- 可以进行非常复杂的数据操作,比如聚合、筛选、排序、映射等。
- 不直接修改源数据,而是生成 新的 数据集合。
Stream流 使用案例?
List<Student> students = new ArrayList<>();
// ...
// 获取所有学生的姓名
List<String> tmpList1 = students.stream().map(s -> s.getName()).collect(Collectors.toList());
// 获取所有学生的姓名和年龄
List<People> tmpList2 = students.stream().map(s -> new People(s.getName(), s.getAge)).collect(Collectors.toList());
// 筛选年龄 大于16岁 的学生
List<Student> tmpList3 = students.stream().filter(s -> s.getAge() > 16).collect(Collectors.toList());
// 列表拼接为字符串
String fileMd5List = students.stream().map(FileInfo::getFileMd5).collect(Collectors.joining(","))
Stream流 实现 group by 功能?
Map<String, Map<String, List<Get_P_Sys_SetEmployeeLimitOut>>> tmpList = _LimitTable.stream().collect(Collectors.groupingBy(Get_P_Sys_SetEmployeeLimitOut::getFunid, Collectors.groupingBy(Get_P_Sys_SetEmployeeLimitOut::getBizid)));
List<TESysBizemployeefunlimit> tmpRes = new ArrayList<>();
for (String tmpStr : tmpList.keySet()) {
for (String innerStr : tmpList.get(tmpStr).keySet()) {
TESysBizemployeefunlimit tsb = new TESysBizemployeefunlimit();
// ...
}
}
Stream流 实现 distinct 功能?
可以使用 Stream
中的 distinct()
方法对一个流中的元素进行去重。但是使用 distinct()
方法默认会对所有字段进行比较,如果需要根据对象的某一字段进行去重,需要重写该对象的 hashCode()
和 equals()
方法来实现。
例如,我们有一个 Person 类,其中包含了姓名 (name)、年龄 (age) 和性别 (gender) 这三个字段:
public class Person {
private String name;
private int age;
private char gender;
public Person(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
// 省略 getter 和 setter 方法
// 重写 hashCode 和 equals 方法,只按照姓名去重
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}
}
如果我们想针对某个人名字进行去重,可以这样使用:
List<Person> list = Arrays.asList(
new Person("Tom", 18, 'M'),
new Person("Jerry", 20, 'M'),
new Person("Tom", 21, 'M'),
new Person("Kate", 22, 'F')
);
List<Person> result = list.stream()
.distinct()
.collect(Collectors.toList());
System.out.println(result);
输出结果为:
[Person{name='Tom', age=18, gender='M'}, Person{name='Jerry', age=20, gender='M'}, Person{name='Kate', age=22, gender='F'}]
可以看到,结果中只保留了一个姓名为 “Tom
” 的对象。
3.4 并行流
tmpList.parallelStream().forEach(content -> {
System.out.println(content.getActName());
// ...
});
- 这段代码的含义是通过 并行流 (
parallelStream
) 遍历 tmpList 集合中的元素,并对每个元素执行一系列操作。- 使用 并行流 可以提高处理效率,因为不同的元素可以 并发 地进行处理。
四、WebFlow (流程业务) 模块难点总结
一个崭新的业务流程, 通过XML字符串的格式保存在数据库中。
XML文档对象处理
- 1.XML 中 text、value 的区别
在下面的语句中, name 为 属性名
, “谢清照” 为该属性的 value
, XieQingZhao 为 Text
值。
<Student name="谢清照" age=18>XieQingZhao</Student>
- 2.通过
SAXReader
把字符串解析为 XML文档对象:Document
// Document 接口表示整个 HTML 或 XML 文档。从概念上讲,它是文档树的根,并提供对文档数据的基本访问。
Document xmlDoc = null;
try {
if (StringUtils.isNotEmpty(xpdl)){
xmlDoc = DocumentHelper.parseText(xpdl);
}
} catch (DocumentException e) {
e.printStackTrace();
}
- 3.
Document
中的方法
Element element = doucument.getRootElement(); // 获取根节点
// 获取路径下的单个根节点 输出匹配到的第一个, 下面的 path 中, 此时ItemName就是根节点
Node node = document.selectSingleNode("root/body/detail/StuItem/ItemName");
// 获取路径下的所有根节点
List<Node> nodes = document.selectNodes(path);
- 4.Node 中的方法
// 获取路径下的单个根节点 输出匹配到的第一个, 下面的 path 中, 此时ItemName就是根节点
Node node = node.selectSingleNode("root/body/detail/StuItem/ItemName");
// 获取路径下的所有根节点
List<Node> nodes = node.selectNodes(path);
- 5.
Eelement
中的方法
当要获得 XML节点 中的元素时就需要把,Node
类型 的数转化为 org.dom4j.Eelement
类型,然后运用 Eelement类 中的方法。
Element element = (Element)node; // 强制转换
Short type = element.getNodeType(); // 获取该节点类型
String name = element.getNodeTypeName(); // 获取该节点名称
// 属性
Attribute isUseAttr = element.attribute("IsUse"); // 获取属性对象
String value = element.attributeValue("Name"); // 获取属性值 - Name为属性名称
element.setAttributeValue("HandleState", "1"); // 为某个属性设置值
// 文本值
String text = element.getText(); // 获取该节点的文本值
element.setText("该节点的文本值"); // 为该节点设置新的文本值
// 结点
// 获取路径下的单个根节点 输出匹配到的第一个, 下面的 path 中, 此时ItemName就是根节点
Node node = element.selectSingleNode("root/body/detail/StuItem/ItemName");
// 获取路径下的所有根节点
List<Node> nodes = element.selectNodes(path);
// 获取孩子结点
List<Element> childs = element.elements();
- 6.Element 和Node 的区别
Element 和 Node 都是 org.dom4j 包中的类,用于处理 XML 文档中的节点。它们两者之间的区别在于:
Node
是一个通用的节点类型,可以表示 任何类型 的节点 (如元素、文本、注释等)。Element
则是 Node 的一个子类,用于表示 XML 文档中的 元素节点。节点方法:
- Node 类型 提供了通用的节点方法,如 getNodeName、getText、getParent 等。
- Element 类型 除了通用方法外,还提供了一些专门处理元素节点的方法,如 getName、addAttribute、getNamespaceURI 等。
子结点:
- Node 的 getChildNodes() 方法返回所有子节点的列表。
- Element 的 elements() 方法返回指定名称的子元素节点列表。
总结:
- Element 类型 适用于处理 XML 文档中的 元素节点,它提供了一些专门处理元素节点的方法和属性访问方式。
- Node 类型 则更具通用性,可以用于表示任何类型的节点,但处理起来 相对复杂。
XML文件结构示例
<?xml version="1.0" encoding="utf-8"?>
<WorkFlowDefine>
<!---流程基本信息-->
<WorkFlowBaseInfo>
<!---版本-->
<WorkFlowId>GUID</WorkFlowId>
<WorkFlowName>流程定义名称</WorkFlowName>
<OrderNO>1</OrderNO>
<!---描述-->
<Description></Description>
<RecID></RecID>
</WorkFlowBaseInfo>
<!---流程事件-->
<WorkFlowEvent></WorkFlowEvent>
<!---流程过程-->
<WorkFlowProcess>
<!--节点-->
<Activities>
<Activity Id="1" Name="谢清照" ActivityType="Start" xPos="16" yPos="819">
<!--关联样式-->
<AppInfo TableName="X_QZ_loveforever" TableStyleID="487c53a5d678" TableStyleName="默认样式" />
<!--节点接受人employeeid通过|隔开-->
<Performer>
<!--1流程发起人,2用户,3部门-->
<PerformerType>2</PerformerType>
<!--(2)employeeid多个用,隔开;(3)deptid多个用,隔开;(4)roleid多个用,隔开-->
<PerformerContent></PerformerContent>
<!--处理人中文名-->
<PerformerContentName>超级管理员</PerformerContentName>
</Performer>
</Activity>
</Activities>
</WorkFlowProcess>
</WorkFlowDefine>
解决 Dom4j 使用 asXML() 自动缩进空标签的问题?
// 默认的方法, 会自动缩进空标签
// instance.setXpdl(wfProcess.getXmlXPDL().asXML());
// 改进的方法
OutputFormat format = OutputFormat.createPrettyPrint(); // 创建输出流
// 设置为false 就是<name/> 设置为true 则是<name></name>
format.setExpandEmptyElements(true); // 设置空标签不缩写
StringWriter stringWriter = new StringWriter();
XMLWriter writer = new XMLWriter(stringWriter, format);
try {
writer.write(wfProcess.getXmlXPDL());
writer.close();
}catch (Exception e) {
e.printStackTrace();
}
instance.setXpdl(stringWriter.toString());