JDK Attach API 简介
从JDK1.6开始可以使用Attach API连接到目标JVM上并让目标JVM加载一个Java Agent。 Attach API的包名称为com.sun.tools.attach。如下图3-1所示主要包含2个类:VirtualMachine和VirtualMachineDescriptor。
图3-1 Attach API 官方文档
VirtualMachine代表一个Java虚拟机,也就是监控的目标虚拟机,而VirtualMachineDescriptor用来描述虚拟机信息,配合VirtualMachine类完成各种功能。
主要的功能实现在VirtualMachine以及子类中,其它类起到辅助作用。下面的代码使用Attach API连接到进程pid为72695的JVM进程上,然后读取目标JVM的系统参数并输出到终端,最后调用detach与目标JVM断开连接。
上面代码输出目标JVM的系统属性参数,其结果如下所示。
上面代码第9行处,可以直观的理解在调用attach方法之后,就获得了一个目标JVM的VirtualMachine对象,调用VirtualMachine对象的方法(第12行处调用getSystemProperties方法)就可以完成对目标JVM的操作。除了获取目标 JVM 系统参数的方法之外,VirtualMachine还有如下方法,如下所示。
利用 JDK 17 的 Stream API 实现高效数据处理
在 Java 开发领域,随着 JDK 的不断演进,Stream API 已然成为处理集合数据的强大工具,尤其是在 JDK 17 中,它为我们带来了更便捷、高效的数据处理方式。
Stream API 提供了一种函数式编程风格来操作集合数据,它允许我们以声明式的方式处理数据,而非传统的命令式迭代。简单来说,我们可以用简洁的代码表达复杂的数据转换、过滤和聚合操作。
首先,假设我们有一个包含多个整数的 List:
import java.util.List;
public class StreamExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(5);
numbers.add(3);
numbers.add(8);
numbers.add(2);
// 使用 Stream API 过滤出偶数
List<Integer> evenNumbers = numbers.stream()
.filter(num -> num % 2 == 0)
.toList();
System.out.println(evenNumbers);
}
}
在上述代码中,我们通过 stream() 方法将 List 转换为流,接着使用 filter 操作,仅保留满足条件(是偶数)的元素,最后通过 toList 方法将流转换回 List。整个过程清晰简洁,无需繁琐的循环迭代。
在 JDK 17 中,Stream API 有一些令人瞩目的改进。例如,对 toList 方法的支持更加直接,像上述示例中,不再需要额外导入 Collectors 类来使用 collect(Collectors.toList()),简化了代码结构。
另外,在处理并行流时,性能也有一些优化。假设我们有一个大规模的数据集,想要快速计算所有元素的总和:
import java.util.List;
public class ParallelStreamExample {
public static void main(String[] args) {
List<Integer> largeNumbers = new ArrayList<>();
// 假设这里填充了大量整数
int sum = largeNumbers.parallelStream()
.mapToInt(Integer::intValue)
.sum();
System.out.println(\”总和为: \” + sum);
}
}
通过 parallelStream,JDK 17 能更智能地利用多核处理器,在大数据集场景下显著提升计算效率。
在电商系统中,我们常常需要对订单数据进行多维度的分析。假设有一个 Order 类,包含订单号、客户 ID、订单金额、订单日期等属性,存储在一个 List<Order> 中。
private String orderId;
private int customerId;
private double amount;
localDate orderDate;
// 构造函数、getter 和 setter 省略
}
现在要找出某个特定客户在过去一个月内的订单总金额:
import java.util.ArrayList;
import java.util.List;
public class EcommerceExample {
public static void main(String[] args) {
List<Order> orders = new ArrayList<>();
// 假设这里填充了一些订单数据
int targetCustomerId = 123;
LocalDate oneMonthAgo = LocalDate.now().minusMonths(1);
double totalAmount = orders.stream()
.filter(order -> order.getCustomerId() == targetCustomerId && order.getOrderDate().isAfter(oneMonthAgo))
.mapToDouble(Order::getAmount)
.sum();
System.out.println(\”该客户过去一个月订单总金额: \” + totalAmount);
}
}
在这段代码中,首先通过 filter 筛选出目标客户且在指定时间范围内的订单,然后使用 mapToDouble 提取订单金额并通过 sum 方法计算总和,一步到位地实现了复杂的数据查询需求。
在企业管理系统里,有一个 Employee 类,包含员工 ID、绩效评分、部门等属性,存储在 List<Employee> 中。
private int employeeId;
private double performanceScore;
private String department;
// 构造函数、getter 和 setter 省略
}
要统计每个部门的平均绩效评分:
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class PerformanceExample {
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
// 假设这里填充了员工数据
Map<String, Double> departmentAverageScore = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment, Collectors.averagingDouble(Employee::getPerformanceScore)));
departmentAverageScore.forEach((department, averageScore) -> System.out.println(department + \” 平均绩效评分: \” + averageScore));
}
}
这里利用 collect 方法结合 groupingBy 与 averagingDouble,先按照部门分组,再计算每组的平均绩效评分,最后以清晰的格式输出结果,高效完成了复杂的统计任务。
在社交平台应用中,有一个 Post 类,包含帖子 ID、发布用户 ID、发布时间、内容、点赞数等属性,存储在 List<Post> 中。假设要获取过去一周内点赞数超过 50 的热门帖子:
import java.util.ArrayList;
import java.util.List;
class Post {
private int postId;
private int userId;
private LocalDateTime postTime;
private String content;
private int likeCount;
// 构造函数、getter 和 setter 省略
}
public class SocialMediaExample {
public static void main(String[] args) {
List<Post> posts = new ArrayList<>();
// 假设这里填充了一些帖子数据
LocalDateTime oneWeekAgo = LocalDateTime.now().minusWeeks(1);
List<Post> popularPosts = posts.stream()
.filter(post -> post.getLikeCount() > 50 && post.getPostTime().isAfter(oneWeekAgo))
.toList();
popularPosts.forEach(post -> System.out.println(\”帖子 ID: \” + post.getPostId() + \”, 内容: \” + post.getContent()));
}
}
通过 filter 依据点赞数和时间条件筛选出热门帖子,让用户能快速聚焦关注度高的内容,提升用户体验。
在物流系统里,有一个 Shipment 类,包含运单号、发货地、收货地、重量、运输成本等属性,存储在 List<Shipment> 中。若要统计不同发货地到某一固定收货地的平均运输成本:
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class Shipment {
private String shipmentId;
private String origin;
private String destination;
private double weight;
private double cost;
// 构造函数、getter 和 setter 省略
}
public class LogisticsExample {
public static void main(String[] args) {
List<Shipment> shipments = new ArrayList<>();
// 假设这里填充了一些运单数据
String targetDestination = \”北京\”;
Map<String, Double> averageCostByOrigin = shipments.stream()
.filter(shipment -> shipment.getDestination().equals(targetDestination))
.collect(Collectors.groupingBy(Shipment::getOrigin, Collectors.averagingDouble(Shipment::getCost)));
averageCostByOrigin.forEach((origin, cost) -> System.out.println(origin + \” 到 \” + targetDestination + \” 的平均运输成本: \” + cost));
}
}
利用 groupingBy 和 averagingDouble 配合 filter,精准统计出各地到特定收货地的成本情况,助力物流企业优化成本与规划路线。
总之,JDK 17 的 Stream API 为 Java 开发者赋予了强大的数据处理能力,通过这些复杂业务逻辑案例可见一斑。掌握它能让我们在面对各种刁钻的数据操作任务时,编写出更加简洁、高效且易于维护的代码,提升整个项目的开发效率与质量。
Spring Framework对Java API的实践
本篇我们来继续讨论Spring Framework对JDK API的一个实践情况。这里会分布从Java1.0到Java 8这个版本的API的实践。
首先我们看<Java5这一部分,这里其实我主要罗列了Java 1.3以后的一个实现或者是包括1.2的实现。第一个部分是关于Java的反射,那么Java反射是从Java1.2也就是Java2这个版本进行引入的。其次就是Java Beans,那么反射以及Java Beans在JDK以及在Spring里面的运用是非常广泛的。那么我们知道Spring 1.0的版本就必须依赖于Java的1.3,动态代理就在里面,那么动态代理简单说就是Proxy或者是InvocationHandler这么一个API。
接下来是Java5这一部分,这里运用了大量的新型的API,包括了我们常说的并发框架JUC、格式化Formatter,还有Java管理扩展以及Instrumentation、XML处理,包括DOM比如说Simple API for XML就是简称SAX。
从Java6里面主要是做了一些版本的升级,包括JDBC4.0的一个升级以及JAXB, 这个是Java API对XML的绑定,再来就是关于可插拔API的处理,这个部分简单来说是另外一个单词,当然如果是安卓开发的话可能会说另外一个名词叫做APT就是Annotaion Processing API,那么这个API其实帮助我们去在编译时去处理一些注解的方式。那么接下来就是Common Annotations,就是所谓的通用型的注解称为JSR 250。这里我们可以注意到一个特点,从Java 6开始,我们会逐一的把JSR给引入进来,那这个JSR是什么意思呢?JSR其实是Java Specification Request的缩写,也就是Java的规范请求,那么这里有很多的编列的序号,比如说250、199这么个序列。199就是Java Compiler API,就是所谓的编译器的一个API,意思是在Java里面可以实现编译器。换言之,我们说从Java6开始Javac这个进程其实就是Java API来进行实现的,比如说通过Java来编译Java。再比如说JSR 223就是说Scripting In JVM,这是什么意思呢?就是说从Java 6开始支持脚本语言在JVM上面部署,最早的版本就是支持JavaScript在JVM上面的一个实施,这是Java6的一个情况。
然后是Java7,但是Java7主要一些特性也不是太明显。第一部分特性主要说是NIO 2.0,提供了一些比如说像文件或者路径这么一个抽象,包括异步的NIO这么个支持,还有包括Fork/Join框架,它是在JUC上面做一些补充,包括提供一个复合Join的模型帮助我们来实现一些复杂性的并行计算。再来一个就是JSR 292,Invokedynamic 字节码,这个字节码是做什么用呢?通常动态语言是用于类似于Grovvy这样的动态语言的支持。
另外一方面Java8里面也提供了新的支持,包括像Stream API,叫JSR 335这么一个API。Stream大家用的可能比较多,因为它是几个框架的一个扩展,比如Map/Reduce一些复杂的操作。与此同时在JUC的基础上,比如说Java7提供了Fork/Join,Java8开始提供CompletableFuture的支持。再下来就是关于Annotation on Java Types,就是所谓的JSR 308,这个地方我们的感知不适特别的明显,这里我就不多说了。Java8还有个很显著的特点就是Date and Time API,这个属于时间和日期的API,这个API有啥用呢?这个主要是解决过去在Java里面时间是可变的情况,而Date and Time API就是一个不变的API的实现,在Spring里面也会有很多的部署。还有我们前面说过的可重复的API JSR 337,这里就是@Repeatable,有个注解叫做@Repeatable的一个注解。接下来是JavaScript运行时 JSR223,其实这个和我们前面说的Scripting in JVM多多少绍是有重叠的。
再来看Java9,这个在到目前为止的Spring Framework里面是没有支持的,这里我们不多说了,感兴趣的同学可以自行了解一下。
以上是Java标准的API的一个基本实现,接下来我们会逐一的真对每个API在Spring Framework的基础上面来做一个对比。
首先我们来看下Spring Framework对<Java 5 API的实践,示意图如下:
然后是Java 5 API的实践,示意图如下:
接下来是Java 6 API的实践,示意图如下:
另外一方面对于Java 7 API的实践,示意图如下:
然后是对于Java 8 API的实践,示意图如下:
以上是Spring对于Java EE API的基本实践,接下来我们再来看一下Spring Framework对于Java EE API的实践。
首先我们来看一下Spring Framework对于Java EE Web技术的实践,示意图如下:
接下来是对于Java EE 数据存储相关的实践,示意图如下:
然后是对于Java EE Bean相关的实践,示意图如下:
本篇主要是讨论Spring 对于Java的API的支持,从这里我们可以看出我们要想了解Spring的核心原理必须要夯实Java的基础。
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。