AOP 检测所有 Class 转换为 Hibernate.unproxy()
AOP To Detect All Class Casts For Hibernate.unproxy()
我希望能够编写一个方面来检测我何时在我的 org.mypackage
classes 中投射某些东西。
package org.mypackage;
class Foo {
public static void main(String[] args) {
Bar casted = (Bar) args[0]; // want to detect this casting action!
}
}
如何编写切入点来表达转换操作,不仅针对 Foo
class,而且针对 org.mypackage
中的任何 class?
背景:因此 Hibernate 5 + Spring Data JPA 需要使用继承来转换实体:
if (isInstanceOfMyEntity(someEntity)) {
// formerly, this was sufficient:
// MyEntity myEntity = (MyEntity) someEntity;
// now, this is required *everywhere* it is casted:
MyEntity myEntity = (MyEntity) Hibernate.unproxy(someEntity);
...
}
...在大型代码库中,考虑起来很可怕,因为有很多地方可能会中断。因此,如果可以编写一个 aspect/pointcut 来至少检测到它,那么我们至少可以记录它并确定我们的测试中需要解决问题的地方。
@Vlad-Mihalcea 在这个问题 How to convert a Hibernate proxy to a real entity object 中推荐了这种技术。
对于 Spring AOP 和 AspectJ,答案是:你不能拦截转换。没有像这样细粒度的切入点。
背景:这也没有意义,因为一些强制转换甚至不存在于字节码中,因为编译器将它们优化掉了。其他的在字节码中用一个checkcast
表示。看这个例子:
package de.scrum_master.Whosebug.q58984334;
public class Dummy {
public void foo() {
int i = (int) 42L;
System.out.println(i);
}
public void bar() {
Base base = new Sub();
Sub sub = (Sub) base;
System.out.println(sub);
}
static class Base {}
static class Sub extends Base {}
}
如果你通过javap -c Dummy.class
反汇编它,你会得到:
Compiled from "Dummy.java"
public class de.scrum_master.Whosebug.q58984334.Dummy {
public de.scrum_master.Whosebug.q58984334.Dummy();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public void foo();
Code:
0: bipush 42
2: istore_1
3: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
6: iload_1
7: invokevirtual #21 // Method java/io/PrintStream.println:(I)V
10: return
public void bar();
Code:
0: new #30 // class de/scrum_master/Whosebug/q58984334/Dummy$Sub
3: dup
4: invokespecial #32 // Method de/scrum_master/Whosebug/q58984334/Dummy$Sub."<init>":()V
7: astore_1
8: aload_1
9: checkcast #30 // class de/scrum_master/Whosebug/q58984334/Dummy$Sub
12: astore_2
13: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
16: aload_2
17: invokevirtual #33 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
20: return
}
看到了吗?从 long
到 int
的转换甚至不存在,即使在源代码中编译是必要的。那么你将如何拦截它呢?很抱歉这次深入探讨,实际答案在第一段中。
更新:
鉴于您无法确定转换是否存在于字节码中这一事实,您可以做的是编写一个简单的测试来扫描您的源代码并在每个 Maven 或 Gradle build.
或者,如果你想变得更复杂,你可以编写一个编译器插件,它会检查表示已解析源代码的 AST(抽象语法树),然后在你发现一些你认为不正确的内容时发出编译器警告需要举报
使用 AspectJ(不是 Spring AOP)你可以拦截对 Hibernate.unproxy()
的调用,但是你需要检测的是这些调用的缺失,所以你不能写一个甚至不存在的东西的切入点。
我希望能够编写一个方面来检测我何时在我的 org.mypackage
classes 中投射某些东西。
package org.mypackage;
class Foo {
public static void main(String[] args) {
Bar casted = (Bar) args[0]; // want to detect this casting action!
}
}
如何编写切入点来表达转换操作,不仅针对 Foo
class,而且针对 org.mypackage
中的任何 class?
背景:因此 Hibernate 5 + Spring Data JPA 需要使用继承来转换实体:
if (isInstanceOfMyEntity(someEntity)) {
// formerly, this was sufficient:
// MyEntity myEntity = (MyEntity) someEntity;
// now, this is required *everywhere* it is casted:
MyEntity myEntity = (MyEntity) Hibernate.unproxy(someEntity);
...
}
...在大型代码库中,考虑起来很可怕,因为有很多地方可能会中断。因此,如果可以编写一个 aspect/pointcut 来至少检测到它,那么我们至少可以记录它并确定我们的测试中需要解决问题的地方。
@Vlad-Mihalcea 在这个问题 How to convert a Hibernate proxy to a real entity object 中推荐了这种技术。
对于 Spring AOP 和 AspectJ,答案是:你不能拦截转换。没有像这样细粒度的切入点。
背景:这也没有意义,因为一些强制转换甚至不存在于字节码中,因为编译器将它们优化掉了。其他的在字节码中用一个checkcast
表示。看这个例子:
package de.scrum_master.Whosebug.q58984334;
public class Dummy {
public void foo() {
int i = (int) 42L;
System.out.println(i);
}
public void bar() {
Base base = new Sub();
Sub sub = (Sub) base;
System.out.println(sub);
}
static class Base {}
static class Sub extends Base {}
}
如果你通过javap -c Dummy.class
反汇编它,你会得到:
Compiled from "Dummy.java"
public class de.scrum_master.Whosebug.q58984334.Dummy {
public de.scrum_master.Whosebug.q58984334.Dummy();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public void foo();
Code:
0: bipush 42
2: istore_1
3: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
6: iload_1
7: invokevirtual #21 // Method java/io/PrintStream.println:(I)V
10: return
public void bar();
Code:
0: new #30 // class de/scrum_master/Whosebug/q58984334/Dummy$Sub
3: dup
4: invokespecial #32 // Method de/scrum_master/Whosebug/q58984334/Dummy$Sub."<init>":()V
7: astore_1
8: aload_1
9: checkcast #30 // class de/scrum_master/Whosebug/q58984334/Dummy$Sub
12: astore_2
13: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
16: aload_2
17: invokevirtual #33 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
20: return
}
看到了吗?从 long
到 int
的转换甚至不存在,即使在源代码中编译是必要的。那么你将如何拦截它呢?很抱歉这次深入探讨,实际答案在第一段中。
更新:
鉴于您无法确定转换是否存在于字节码中这一事实,您可以做的是编写一个简单的测试来扫描您的源代码并在每个 Maven 或 Gradle build.
或者,如果你想变得更复杂,你可以编写一个编译器插件,它会检查表示已解析源代码的 AST(抽象语法树),然后在你发现一些你认为不正确的内容时发出编译器警告需要举报
使用 AspectJ(不是 Spring AOP)你可以拦截对
Hibernate.unproxy()
的调用,但是你需要检测的是这些调用的缺失,所以你不能写一个甚至不存在的东西的切入点。