java - 当我想拦截一个方法时,我只能使用接口子类吗?
问题描述
我正在尝试使用 byte buddy 来拦截方法,但发现它仅在您子类型接口时才起作用。
下面的代码片段演示了这一点,您可以看到“hello”被传递到 3 种类型的相同“message”方法中,但只拦截了 Interface 子类型。
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.attribute.AnnotationRetention;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Morph;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.This;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
class Sandbox {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
testIt(StaticClass.class).message("hello");
testIt(AbstractStaticClass.class).message("hello");
testIt(Interface.class).message("hello");
}
private static <T> T testIt(Class<T> aClass) throws IllegalAccessException, InstantiationException {
return new ByteBuddy().with(AnnotationRetention.ENABLED)
.subclass(aClass)
.method(
ElementMatchers.isAnnotatedWith(AnAnnotation.class)
).intercept(
MethodDelegation.withDefaultConfiguration()
.withBinders(Morph.Binder.install(Invoker.class))
.to(new Interceptor()))
.make()
.load(Sandbox.class.getClassLoader())
.getLoaded()
.newInstance();
}
public interface Invoker {
Object invoke(Object[] args);
}
public static class Interceptor {
@RuntimeType
public Object intercept(@This Object proxy, @Origin Method method, @Morph(defaultMethod = true) Invoker invoker, @AllArguments Object[] args) {
return invoker.invoke(new Object[]{"there"});
}
}
public static class StaticClass {
@AnAnnotation
String message(String message) {
System.out.println(getClass().getName() + ": message: " + message);
return message;
}
}
public static abstract class AbstractStaticClass {
@AnAnnotation
String message(String message) {
System.out.println(getClass().getName() + ": message: " + message);
return message;
}
}
public interface Interface {
@AnAnnotation
default String message(String message) {
System.out.println(getClass().getName() + ": message: " + message);
return message;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
private @interface AnAnnotation {}
}
我还发现,如果我将类更改为相互继承,如下所示,那么对于 AbstractStaticClass 和 StaticClass,该方法根本不会执行。
public static class StaticClass extends AbstractStaticClass { ... }
public static abstract class AbstractStaticClass implements Interface { ... }
public interface Interface { ... }
如何拦截类的方法以及接口的方法?
谢谢
解决方案
您正在定义仅在包中可见的包私有方法。您还使用了默认的类注入策略,它将创建一个新的类加载器来加载您注入的类。在运行时,两个包只有在它们具有相同的名称和类加载器时才相等,类似于类。
实际上,Byte Buddy 会为您覆盖这些方法,因为类创建不考虑目标类加载器。getDeclaredMethod
您可以通过调用生成的类并调用它们来验证这一点。但是,即使它们具有相同的签名,Java 也不会实际调度此类外部包方法。要在同一个类加载器中定义类,可以使用ClassLoadingStrategy.Default.INJECTION
. 但是请注意,不鼓励这种策略,因为它依赖于不安全的 API。在 Java 9+ 中,您可以ClassLoadingStrategy.UsingLookup
改用什么是安全的。
当然,您也可以将您的方法更改为公开或受保护的。
推荐阅读
- powershell - Anyway to split files to diff folder?
- c# - WPF,经常图片刷新不闪烁
- java - 错误:包不存在 JUnit 命令行 - 没有 IDE/包管理器
- google-kubernetes-engine - 子域的 gke-security-groups
- matlab - 我将如何创建一个查找表,其中包含用于增加/减少给定数量的图像亮度的传递函数
- spring-boot - Keycloak门面服务
- c# - 会话未结束时停止页面刷新 ASP.NET MVC C#
- java - 在将 pdf 文件复制到另一个文件时添加注释
- angular - Angular 库如何使用 .tgz 文件打包依赖项
- jquery - 多个选择字段将ajax数据作为字符串传递