4.Spring Cloud (Hoxton.SR8) 实战笔记—项目中细节实现 & 约束 & 注意事项、模块难点总结、Lambda表达式

本文目录如下:

二、项目中细节实现 & 约束 & 注意事项

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 相互转换的方法

点击查看: 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使用详解

CompletableFuture 常用的异步操作有哪些?

  • runAsync: 以 Runnable函数式接口 类型为参数,没有返回结果
  • supplyAsync: 以 Supplier函数式接口 类型为参数,返回结果类型为 USupplier接口get() 方法 是有返回值的(会阻塞)

CompletableFuture 的方法,主要由几个关键单词组成的 apply / accept / runthen / both / eitherasync 组合成各方法的含义。


CompletableFuture 常用方法有哪些?

CompletableFuture使用详解

  • thenApply():把前面任务的执行结果,交给后面的 Function
  • thenCombine():合并任务,有返回值
  • runAfterBoth():两个任务都执行完成后,执行下一步操作 (Runnable类型任务)
  • allOf():当所有给定的 CompletableFuture 完成时,返回一个新的 CompletableFuture
  • whenComplete():当任务完成时,将使用结果(或 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设计和更好的性能。
  • OkHttpOkHttpSquare 开发的一款 轻量级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表达式 使用案例?

Lambda表达式 使用案例


3.2 方法引用

什么是 方法引用?

Java8 中的 方法引用 (Method Reference) 是一种快速简便地将 现有方法 转换为 Lambda 表达式 的方式。它可以使代码更加 简洁易懂减少重复性的代码,提高代码的 可读性可维护性

方法引用语法如下:

对象::实例方法名
类::静态方法名
类::实例方法名

其中,第一个参数 表示方法的接收者(如果是实例方法)。例如,对于 System.out::printlnSystem.out 是接收者,println 是方法名。如果方法是静态的,则第一个参数表示方法所在类的名字,例如 Math::sqrt

方法引用 使用案例?

方法引用 使用案例


3.3 Stream流

Java–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, XieQingZhaoText

<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 的区别

ElementNode 都是 org.dom4j 包中的类,用于处理 XML 文档中的节点。它们两者之间的区别在于:

  • Node 是一个通用的节点类型,可以表示 任何类型 的节点 (如元素文本注释等)。
  • Element 则是 Node 的一个子类,用于表示 XML 文档中的 元素节点

节点方法:

  • Node 类型 提供了通用的节点方法,如 getNodeNamegetTextgetParent 等。
  • Element 类型 除了通用方法外,还提供了一些专门处理元素节点的方法,如 getNameaddAttributegetNamespaceURI 等。

子结点:

  • NodegetChildNodes() 方法返回所有子节点的列表。
  • Elementelements() 方法返回指定名称的子元素节点列表。

总结:

  • 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() 自动缩进空标签的问题?

DOM4j 节点值为空时使用完整符号

// 默认的方法, 会自动缩进空标签
// 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());

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

页川叶川

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值