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

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。