Java中处理千万级数据的最佳实践:性能优化指南

在今天的数字化时代,处理大规模数据已经成为许多Java应用程序的核心任务。无论您是构建数据分析工具、实现实时监控系统,还是处理大规模日志文件,性能优化都是确保应用程序能够高效运行的关键因素。本指南将介绍一系列最佳实践,帮助您在处理千万级数据时提高Java应用程序的性能。

引言

数据规模的挑战

在当今数字化时代,数据规模迅速增长。处理千万级甚至更大规模的数据集已成为常态。这些大数据集可能包含来自传感器、社交媒体、日志文件等各种来源的信息,对于企业和科研机构来说都具有重要价值。

为什么性能优化很重要

处理大规模数据时,性能问题可能导致应用程序变得缓慢或不稳定。用户体验下降,系统响应时间延长,甚至可能导致服务中断。因此,性能优化是确保应用程序能够处理大规模数据并保持高效运行的关键因素。

选择合适的数据结构

Java提供了丰富的数据结构,选择合适的数据结构对性能至关重要。

数组 vs. 列表 vs. 集合

  • 数组是最基本的数据结构之一,具有快速的随机访问能力。如果数据集的大小是已知且不变的,数组可以是一个高效的选择。然而,数组的大小是固定的,不能动态增长,这限制了其在某些场景的适用性。
  • 列表(如ArrayList)是可变大小的数据结构,适用于大部分情况。它通过动态增长内部数组的方式来处理数据。但要注意在大规模数据集上频繁添加和删除元素可能会导致性能下降,因为需要重新分配和复制数组。
  • 集合(如HashSetTreeSet)提供了快速的查找操作。选择合适的集合类型取决于您的需求。例如,HashSet对于快速查找唯一值非常有用,而TreeSet可以保持元素的有序性。

使用哈希表和树结构

  • 哈希表(如HashMap)对于快速查找和插入操作非常高效。它通过将键映射到桶中的索引来实现快速查找。
  • 树结构(如TreeMap)可以保持有序性,适用于需要有序遍历数据的情况。它基于二叉搜索树实现,因此查找操作的复杂度较低。

自定义数据结构的考虑

根据应用程序的特性,有时自定义数据结构可以提供更好的性能。例如,如果您需要高效存储大量的布尔值数据,可以考虑使用位集合(BitSet),它可以显著减小内存消耗。

import java.util.BitSet;public class BitSetExample {public static void main(String[] args) {int dataSize = 10000000;BitSet bitSet = new BitSet(dataSize);// 设置某些位为truebitSet.set(1);bitSet.set(100);bitSet.set(1000);// 检查位的状态boolean isSet = bitSet.get(100);  // 返回trueboolean isNotSet = bitSet.get(500); // 返回false}
}

在上述示例中,BitSet被用于高效地存储大量布尔值数据。

内存管理和优化

内存管理是性能优化的关键部分。不正确的内存使用可能导致内存泄漏和性能下降。

内存泄漏的检测和解决

使用工具如Java虚拟机自带的内存分析器(VisualVM)来检测潜在的内存泄漏问题。内存泄漏通常发生在对象被引用后没有被正确释放的情况下。确保及时解决这些问题,以释放未使用的内存。

public class MemoryLeakExample {private static List<Object> memoryLeakList = new ArrayList<>();public static void main(String[] args) {for (int i = 0; i < 10000000; i++) {Object obj = new Object();memoryLeakList.add(obj);}}
}

在上述示例中,未正确释放memoryLeakList中的对象可能导致内存泄漏。

使用对象池

对象池是一种重用对象的机制,它可以减少对象的频繁创建和销毁,从而提高性能。通过重复使用对象,

可以避免频繁的垃圾回收操作。常见的对象池库包括Apache Commons Pool和Google Guava。

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;public class ObjectPoolExample {public static void main(String[] args) {GenericObjectPoolConfig<MyObject> config = new GenericObjectPoolConfig<>();config.setMaxTotal(100); // 池中最多存放的对象数量GenericObjectPool<MyObject> objectPool = new GenericObjectPool<>(new MyObjectFactory(), config);// 从对象池中获取对象MyObject obj = objectPool.borrowObject();// 使用对象// 将对象归还给对象池objectPool.returnObject(obj);}
}

在上述示例中,我们使用Apache Commons Pool创建了一个对象池,可以重复使用MyObject对象。

减少对象创建

对象的创建和销毁操作通常是性能的瓶颈之一。尽量减少不必要的对象创建,可以通过对象池、缓存等方式来实现。如果一个对象是一次性的,并且在短时间内被多次创建和销毁,考虑将其重用以减少开销。

public class ObjectCreationExample {public static void main(String[] args) {for (int i = 0; i < 10000000; i++) {// 避免在循环内创建对象String str = "Object " + i;// 使用str}}
}

在上述示例中,避免在循环内部创建大量的String对象可以提高性能。

使用弱引用和软引用

Java提供了弱引用(WeakReference)和软引用(SoftReference)来管理对象的生命周期。它们可以用于缓存和缓存清理,从而更好地管理内存。弱引用的对象在下一次垃圾回收时会被释放,而软引用的对象则会在内存不足时才被释放。

import java.lang.ref.WeakReference;public class ReferenceExample {public static void main(String[] args) {Object obj = new Object();WeakReference<Object> weakReference = new WeakReference<>(obj);// 在需要时,可以通过弱引用获取对象Object retrievedObj = weakReference.get();// 如果对象被回收,则retrievedObj将为null}
}

在上述示例中,使用弱引用可以更灵活地管理对象的生命周期。

多线程并发处理

多线程可以有效地利用多核处理器,提高数据处理速度,但也需要谨慎处理以避免竞态条件和死锁。

并发编程基础

了解多线程编程的基本原理和概念,包括线程的创建、同步和互斥。确保您的代码在多线程环境下是线程安全的。

public class ThreadSafetyExample {private static int counter = 0;public static synchronized void increment() {counter++;}public static void main(String[] args) {Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000000; i++) {increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000000; i++) {increment();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Counter: " + counter);}
}

在上述示例中,我们使用synchronized关键字确保increment方法的原子性,避免了竞态条件。

使用线程池

线程池是管理线程的最佳方式之一。它可以管理线程的生命周期,提供线程的复用和管理,从而减少线程创建销毁的开销。Java提供了java.util.concurrent.Executor框架来帮助您轻松创建和管理线程池。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {int numberOfThreads = 4;ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);for (int i = 0; i < 10; i++) {executorService.submit(() -> {// 执行任务});}// 关闭线程池executorService.shutdown();}
}

在上述示例中,我们使用线程池管理并发任务的执行。

避免共享数据的竞态条件

在多线程环境中,多个线程可能会同时访问和修改共享数据,导致竞态条件。使用合适的锁机制(如Synchronized关键字和java.util.concurrent包中的锁)来避免这种情况。并发集合(如ConcurrentHashMap)也提供了一种线程安全的方式来处理共享数据。

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();concurrentMap.put("key1", 1);concurrentMap.put("key2", 2);// 使用并发集合进行安全的操作int value = concurrentMap.get("key1");System.out.println("Value: " + value);}
}

在上述示例中,我们使用ConcurrentHashMap来安全地操作共享数据。

使用并发集合

Java提供了各种并发集合,如ConcurrentHashMapConcurrentLinkedQueue,可以安全地在多线程环境下使用。这些集合实现了内部同步,因此可以避免手动加锁。

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;public class ConcurrentCollectionExample {public static voidmain(String[] args) {Queue<String> concurrentQueue = new ConcurrentLinkedQueue<>();concurrentQueue.offer("Item 1");concurrentQueue.offer("Item 2");// 使用并发队列进行安全的操作String item = concurrentQueue.poll();System.out.println("Item: " + item);}
}

在上述示例中,我们使用ConcurrentLinkedQueue来安全地操作队列。

锁的选择和性能影响

了解锁的种类,包括悲观锁和乐观锁,并选择合适的锁对性能的影响。例如,悲观锁(如ReentrantLock)提供了强大的互斥保护,但可能会导致线程争用,影响性能。乐观锁(如Atomic类)通过版本控制来避免争用,适用于某些高并发场景。

import java.util.concurrent.atomic.AtomicInteger;public class AtomicExample {private static AtomicInteger counter = new AtomicInteger(0);public static void main(String[] args) {counter.incrementAndGet();int value = counter.get();System.out.println("Counter: " + value);}
}

在上述示例中,我们使用AtomicInteger来实现无锁的原子操作。

数据分区和分片

将数据分为多个分区或分片可以有效提高数据处理性能。

数据分区的概念

数据分区是将数据集划分为多个较小部分的过程。每个分区都是相对独立的,可以被独立处理。数据分区的目标是将工作负载均衡地分布到不同的处理单元上,从而提高并行处理性能。

分布式计算和MapReduce

在分布式环境中使用MapReduce等技术来处理大规模数据集。MapReduce模型将数据分为多个块,然后对每个块执行Map和Reduce操作,以实现并行处理。

分片处理技术

使用分片技术将数据集分割成多个片段(或分片),每个分片可以在独立的线程或处理单元上处理。这种方式可提高数据处理的并行性,从而加速整体处理速度。分片处理适用于需要逐一处理大量数据记录的任务。

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class DataShardingExample {public static void main(String[] args) {List<DataChunk> dataChunks = loadDataChunks();ExecutorService executorService = Executors.newFixedThreadPool(4);for (DataChunk chunk : dataChunks) {executorService.submit(() -> {// 处理数据分片});}// 关闭线程池executorService.shutdown();}private static List<DataChunk> loadDataChunks() {// 加载数据分片return null;}private static class DataChunk {// 数据分片的定义}
}

在上述示例中,我们使用分片处理技术来并行处理数据分片。

索引和查询优化

如果您的应用程序涉及数据库或搜索操作,优化索引和查询是至关重要的。

数据库索引的作用

数据库索引是一种数据结构,用于加速查询操作。它们允许数据库引擎更快地查找符合特定条件的数据行。索引通常是根据表中的一个或多个列创建的。

内存中索引 vs. 磁盘索引

将索引保留在内存中可以显著提高查询性能。因为内存操作通常比磁盘访问快得多,所以将索引数据加载到内存中可以加速查询操作。此外,使用合适的数据结构来表示索引也很重要。

查询优化技巧

优化SQL查询以减少查询时间是数据库性能优化的核心。以下是一些查询优化的技巧:

  • 选择合适的索引:根据查询的条件选择适当的索引,避免全表扫描。
  • 避免不必要的连接:尽量减少查询中的连接操作。
  • 使用合适的数据类型:选择适当的数据类型来存储数据,避免数据类型转换。
  • 查询分页:如果应用程序需要分页查询结果,使用LIMIT和OFFSET来限制结果集的大小。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class QueryOptimizationExample {public static void main(String[] args) {String url = "jdbc:mysql://localhost:3306/mydatabase";String username = "user";String password = "password";try (Connection connection = DriverManager.getConnection(url, username, password)) {String sql = "SELECT * FROM mytable WHERE column1 = ? LIMIT ? OFFSET ?";try (PreparedStatement statement = connection.prepareStatement(sql)) {statement.setString(1, "value");statement.setInt(2, 10);statement.setInt(3, 0);try (ResultSet resultSet = statement.executeQuery()) {while (resultSet.next()) {// 处理查询结果}}}} catch (SQLException e) {e.printStackTrace();}}
}

在上述示例中,我们使用了查询优化技巧,包括使用索引、限制结果集大小等。

I/O操作优化

如果应用程序需要进行大量的I/O操作,如文件读写,优化这些操作也是关键。

文件读写性能优化

文件读写操作通常涉及到磁盘访问,因此可以成为性能瓶颈。以下是一些文件读写性能优化的技巧:

  • 使用缓冲:缓冲可以减少频繁的磁盘访问,提高I/O性能。Java提供了BufferedReaderBufferedWriter等类来帮助您实现缓冲。

异步I/O:如果应用程序支持异步操作,可以考虑使用异步I/O来提高并发性。

  • 文件通道:Java的NIO包提供了文件通道(FileChannel)来进行高效的文件读写操作。
  • 内存映射文件:通过内存映射文件(Memory-Mapped Files)可以将文件映射到内存中,以加速读写操作。
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;public class FileIOOptimizationExample {public static void main(String[] args) throws IOException {String filePath = "data.txt";try (RandomAccessFile file = new RandomAccessFile(filePath, "rw");FileChannel channel = file.getChannel()) {MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, file.length());// 使用内存映射文件进行读写操作buffer.force(); // 强制刷新到磁盘}}
}

在上述示例中,我们使用内存映射文件来进行高效的文件读写操作。

数据编码和序列化

数据的编码方式和序列化方式对性能有重要影响。选择合适的编码和序列化方式可以减少数据传输的开销。

数据编码方式

数据的编码方式决定了数据在传输和存储时所占用的空间。例如,对于文本数据,UTF-8编码通常是一个不错的选择,因为它可以在节省空间的同时支持多种字符。

序列化和反序列化的性能考虑

序列化是将对象转换为字节流的过程,而反序列化是将字节流还原为对象的过程。不同的序列化框架和格式对性能有不同的影响。在选择序列化方法时,需要考虑性能因素。一些常见的Java序列化框架包括Java序列化、JSON、Protocol Buffers和Avro。

使用二进制协议

在网络通信中,使用二进制协议可以减少数据传输的开销,提高性能。与文本协议相比,二进制协议通常更紧凑且更快速。

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;public class BinaryProtocolExample {public static void main(String[] args) throws IOException {// 创建一个二进制数据流ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);// 写入数据dataOutputStream.writeInt(42);dataOutputStream.writeDouble(3.14);// 获取二进制数据byte[] binaryData = byteArrayOutputStream.toByteArray();// 处理二进制数据// ...dataOutputStream.close();}
}

在上述示例中,我们使用二进制数据流来编码数据,以提高数据传输的效率。

编程技巧和工具

一些编程技巧和工具可以帮助您更轻松地进行性能优化。

编程最佳实践

遵循Java编程的最佳实践,如避免过度的方法调用、减少不必要的异常处理、使用final关键字来优化不可变对象等。这些最佳实践有助于改善代码的可维护性和性能。

使用性能分析工具

性能分析工具可以帮助您识别应用程序中的性能瓶颈。一些常用的性能分析工具包括VisualVM、YourKit和Java Flight Recorder。通过使用这些工具,您可以查看方法调用栈、内存使用情况和线程活动,以找出性能问题的根本原因。

配置和部署优化

优化应用程序的配置和部署设置对性能也具有重要影响。在部署应用程序时,可以考虑以下一些优化策略:

  • 调整JVM参数:根据应用程序的需求,调整Java虚拟机的参数,如堆大小、垃圾回收策略和线程池大小。
  • 服务器硬件升级:如果应用程序需要更多的计算资源,可以考虑升级服务器硬件,如CPU、内存和存储设备。
  • 负载均衡:如果应用程序需要处理大量请求,可以考虑使用负载均衡器来均匀分配流量到多个服务器实例上。

监控和调试

设置监控系统来实时监视应用程序的性能,以便快速定位和解决问题。监控系统可以捕获关键性能指标,如响应时间、吞吐量和错误率。当性能问题发生时,监控数据可以帮助您快速定位问题的根本原因,并采取相应的措施来解决它们。

最佳实践示例

通过实际案例分析,展示性能优化的成果,说明如何将这些最佳实践应用到实际项目中。以下是一些可能的最佳实践示例:

  • 数据库查询优化:通过优化数据库查询语句和索引设计,将查询时间从数秒缩短到数毫秒,提高了应用程序的响应速度。
  • 并发处理优化:通过引入线程池和缓存机制,将并发请求的处理时间从几分钟降低到几秒,提高了系统的吞吐量。
  • 内存管理和垃圾回收:通过修复内存泄漏问题,减少了应用程序的内存占用,提高了稳定性和可伸缩性。

结论

处理千万级数据是一项挑战性的任务,但通过选择合适的数据结构、内存管理、多线程并发处理、数据分区、索引和查询优化、I/O操作优化、数据编码和序列化以及一些编程技巧和工具,您可以显著提高Java应用程序的性能。本指南提供了一系列最佳实践和示例,帮助您优化应用程序,确保它在处理大规模数据时表现出色。通过不断学习和优化,您可以打造出高性能的Java应用程序,满足用户和业务需求。祝您的编程之路顺利!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/94114.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TinyWebServer学习笔记-互斥锁、信号量、条件变量

为什么要使用锁、信号量、条件变量&#xff1f; 网站不可能是单线程的&#xff0c;否则网站的性能和响应都会收到严重的影响。因此&#xff0c;这个项目一定是运行在多线程条件下的。而在多线程条件下&#xff0c;对共享资源的互斥访问就极其重要。 为什么要将资源封装成类&a…

IDEA中的神仙插件——Smart Input (自动切换输入法)

推荐专栏&#xff1a;开发环境配置攻略 致力于记录学习过程中各种软件的安装及环境配置操作&#xff0c;并提供详细的步骤说明和丰富的配图。涵盖了 Java、Python、IntelliJ IDEA、Tomcat、MySQL 等常见开发工具和服务器组件的配置&#xff0c;为初学者提供一个实用、全面的配置…

Pandas进阶修炼120题-第五期(一些补充,101-120题)

目录 往期内容&#xff1a;第一期&#xff1a;Pandas基础&#xff08;1-20题&#xff09;第二期&#xff1a;Pandas数据处理&#xff08;21-50题&#xff09;第三期&#xff1a;Pandas金融数据处理&#xff08;51-80题&#xff09;第四期&#xff1a;当Pandas遇上NumPy&#xf…

Vue2 第一次学习

本章为超级浓缩版,文章过于短,方便复习使用哦~ 文章目录 1. 简单引入 vue.js2. 指令2.1 事件绑定指令 v-on (简写 )2.2 内容渲染指令2.3 双向绑定指令 v-model2.4 属性绑定指令 v-bind (简写 : )2.5 条件渲染指令2.6 循环指令 v-for 3. vue 其他知识3.1 侦听器 watch3.2 计算属…

WOL唤醒配置(以太网、PHY、MAC)

目录 wol 以太网 MAC PHY RMII 通信配置 总结 wol Wake-on-LAN简称WOL&#xff0c;WOL&#xff08;网络唤醒&#xff09; 是一种标准网络协议&#xff0c;它的功效在于让已经进入休眠状态或关机状态的计算机&#xff0c;透过局域网&#xff08;多半为以太网&#xff…

lv8 嵌入式开发-网络编程开发 01什么是互联网

目录 1 计算机网络的定义与分类 1.1 按照网络的作用范围进行分类 1.2 按照网络的使用者进行分类 2 网络的网络 2.1 名词解释 2.2 边缘与核心 3 互联网基础结构发展的三个阶段 3.1 第一阶段&#xff1a;1969 – 1990 3.2 第二阶段&#xff1a;1985 – 1993 3.3 第三阶…

HTML5高级部分

目录 一、拖拽API1.1 拖拽元素1.2 监听事件1.3 dataTransfer传递数据 二、媒体API2.1 常用监听事件2.2 常用API 三、画布API3.1 canvas 标签3.2 创建canvas对象3.3 常用API 四、地理API4.1 方法 一、拖拽API 1.1 拖拽元素 页面中设置了draggable"true"的元素可以进…

【Java-LangChain:使用 ChatGPT API 搭建系统-11】用 ChatGPT API 构建系统 总结篇

第十一章&#xff0c;用 ChatGPT API 构建系统 总结篇 本课程详细介绍了 LLM 工作原理&#xff0c;包括分词器&#xff08;tokenizer&#xff09;的细节、评估用户输入的质量和安全性的方法、使用思维链作为 Prompt、通过链式 Prompt 分割任务以及返回用户前检查输出等。 本课…

linux MySQL高阶语句

linux MySQL高阶语句 1、MySQL高级语言1.1order by排序1.2group by分组1.3limit前几行1.4as别名1.5通配符1.6子查询1.7in1.8not in1.9exists 2、视图2.1视图概念2.2功能2.3应用场景2.4视图和表的区别和联系2.5创建视图 3、null值3.1null值3.2null值与空值的区别3.3验证null和空…

僵尸进程的产生原因和解决方法

僵尸进程的产生原因 当一个进程&#xff08;通常是父进程&#xff09;创建了一个子进程&#xff0c;但是在子进程终止后&#xff0c;父进程没有及时处理子进程的终止状态&#xff0c;就会导致僵尸进程的产生。这个时候&#xff0c;子进程虽然已经终止&#xff0c;但是其进程表…

Autowired和Resource的关系

相同点对于下面的代码来说&#xff0c;如果是Spring容器的话&#xff0c;两个注解的功能基本是等价的&#xff0c;他们都可以将bean注入到对应的field中 不同点但是请注意&#xff0c;这里说的是基本相同&#xff0c;说明还是有一些不同点的&#xff1a; byName和byType匹配顺…

IDEA的Maven换源

前言 IDEA是个好东西&#xff0c;但是使用maven项目时可能会让人很难受&#xff0c;要么是非常慢&#xff0c;要么直接下载不了。所以我们需要给IDEA自带maven换源&#xff0c;保证我们的下载速度。 具体操作 打开IDEA安装路径&#xff0c;然后打开下面的文件夹 plugins\m…

Armv8/9-A cpu在安全/非安全世界切换时,是否需要对共享内存进行cache维护操作?

安全之安全(security)博客目录导读 问题&#xff1a;当Armv8/9-A cpu在安全世界和非安全世界之间切换时&#xff0c;是否需要对这两个世界的共享内存进行缓存维护操作? 答案&#xff1a; 不需要。 1&#xff09;运行在非安全世界的软件只能对内存进行非安全访问&#xff0c…

第10章 MySQL(一)

10.1 谈谈MySQL的架构 难度:★★ 重点:★ 白话解析 要想彻底的理解MySQL,它的架构一定要先弄清楚,当Java程序员通过JDBC或者Mybatis去执行一条SQL的时候,到底经历了什么。下边先看一幅图: 户端:Java程序员通过JDBC或者Mybatis去拿MySQL的驱动程序,实际上就是拿客户端。…

java基础之构造器

构造器 学习java对于构造器应该很熟悉&#xff0c;但是有些人会认为构造器不是必要的&#xff0c;这就是对于构造器没有深入的了解。 每一个java类中都必须至少有一个显式或隐式的构造器&#xff0c;很多时候看到类中并没有定义构造器&#xff0c;有人会认为构造器不是必须的&a…

GNN PyG~torch_geometric 学习理解

目录 1. PyG Introduction 2. PyG Installation 2.1 PyG 安装常见错误及原因 2.2 PyG 具体安装步骤 3. torch_geometric packages torch_geometric.data.Data Dataset 与 DataLoader Dropout、BatchNorm 3. torch_geometric: 理解edge_index 3.1 理解 mini-batch edg…

【Java】SpringMVC ResponseBodyAdvice详解

目录 1. ResponseBodyAdvice 2. supports方法 3. beforeBodyWrite方法 4. 实践 1. ResponseBodyAdvice Spring MVC的ResponseBodyAdvice是Spring 4.1版本中引入的一个接口&#xff0c;它允许在Controller控制器中ResponseBody修饰的方法或ResponseEntity执行之后&#xff…

【2023年11月第四版教材】第17章《干系人管理》(合集篇)

第17章《干系人管理》&#xff08;合集篇&#xff09; 1 章节内容2 管理基础3 管理过程3.1 管理的过程★★★ &#xff08;22上44&#xff09;3.2 管理ITTO汇总★★★ 4 过程1-识别干系人4.1 数据收集★★★4.3数据分析4.4 权力利益方格4.5 数据表现&#xff1a;干系人映射分析…

记录UNIAPP打包苹果iOS·APP

用到生成的四个文件:1-1.CSR证书文件、2-2.CER证书文件、3-3.PP文件【证书Profiles文件】、4-4.P12文件【证书私钥】 1. 生成CSR证书文件: 2. 操作苹果后台:Sign In - Applehttps://developer.apple.com/account/resources/certificates/list

使用CrawlSpider爬取全站数据。

CrawpSpider和Spider的区别 CrawlSpider使用基于规则的方式来定义如何跟踪链接和提取数据。它支持定义规则来自动跟踪链接&#xff0c;并可以根据链接的特征来确定如何爬取和提取数据。CrawlSpider可以对多个页面进行同样的操作&#xff0c;所以可以爬取全站的数据。CrawlSpid…