Spring 加载时织入(Load-Time Weaving)
简介
本文将带你了解 Spring 加载时织入(Load-Time Weaving)是如何工作的,以便在运行时应用 Hibernate 字节码增强机制。
一般来说,字节码增强机制是在使用 Maven 或 Gradle 插件构建项目时应用的。
Domain Model
假设有以下 Attachment
实体,如下:
@Entity
@Table(name = "attachment")
public class Attachment {
@Id
private Long id;
private String name;
@Enumerated
@Column(name = "media_type")
private MediaType mediaType;
@Lob
@Column(columnDefinition="BLOB")
@Basic(fetch = FetchType.LAZY)
private byte[] content;
// get、set 和其他方法省略
}
content
属性使用的是 FetchType.LAZY
抓取策略,但在 POJO 实体上无法懒加载实体属性,因此需要 Hibernate 字节码增强机制来实现这一目标。
Hibernate 字节码增强机制
Hibernate 字节码增强机制允许我们更改 JPA 实体的字节码,这样就可以拦截 getter 和 setter 方法调用,从而达到以下目的
- 懒加载属性
- 记录实体的修改
如上所述,字节码增强机制是通过 Maven 或 Gradle 插件配置的,该插件会在项目构建时增强实体类。
不过,这并不是可以使用的唯一方法,还可以在项目启动时在运行时对 JPA 实体类进行检测。
Spring Data JPA 加载时织入
要在 Spring 应用时启用运行时字节码增强机制,必须在 Spring 配置中添加 @EnableLoadTimeWeaving
注解:
@EnableLoadTimeWeaving
public class BytecodeEnhancementConfiguration {
...
}
现在,需要通过 javaagent
参数向 JVM 提供 Spring Instrumentation
库。如果使用的是 Windows,参数如下:
-javaagent:%M2_REPOSITORY%\org\springframework\spring-instrument\%SPRING_VERSION%\spring-instrument-%SPRING_VERSION%.jar
测试
加载 Attachment
实体并访问 content
属性:
Attachment book = attachmentRepository.findById(1L).orElseThrow(null);
LOGGER.debug("Fetched book: {}", book.getName());
assertArrayEquals(readBytes(bookFilePath), book.getContent());
可以看到,第一个 SQL 查询并没有获取 content
列,而是在第二个 SQL 查询中按需获取:
SELECT
a.id,
a.media_type,
a.name
FROM
attachment a
WHERE
a.id = 1
-- Fetched book: High-Performance Java Persistence
SELECT
a.content
FROM
attachment a
WHERE
a.id = 1
总结
如果我们想在无法访问项目构建的情况下增强 JPA 实体类,那么就可以使用 Spring 加载时织入机制。
@EnableLoadTimeWeaving
注解告诉 Spring 注册一个 LoadTimeWeaver
Bean,该 Bean 将提供给 JPA EntityManagerFactory
以增强加载的 JPA 实体,从而使我们有机会启用字节码增强的延迟加载或脏数据跟踪机制。
Ref:https://vladmihalcea.com/spring-load-time-weaving/