改为调用其他方法
Invoke other method instead
我有两种方法,其中一种带有注释,比方说:
@ReplacingMethod(bar)
public void foo() { ... }
public void bar { ... }
是否可以在调用 foo 时调用 bar 而不是 foo,而不跳转到 foo 的主体?我对此做了一些研究,但无法通过反射设置 return 值。有什么建议吗?
您可以使用 Aspect Oriented Programming, e.g. with Spring AOP 实现此目的。我不认为你可以在没有 AOP 的情况下更改纯 Java 中的方法实现。
让我举例说明如何使用 Spring AOP 实现您的要求。首先,定义你的注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReplacingMethod {
String value();
}
然后定义一个 方面 来实际替换方法:
@Aspect // aspect is a module encapsulating your replacing functionality
public class ReplacingAspect {
// pointcut gives an expression selecting the "joint points" to be intercepted
@Pointcut("@annotation(example.annotation.ReplacingMethod)")
public void methodToBeReplaced() { }
// advice defining the code executed at joint points selected by given pointcut;
// in our case @Around is executed instead of the method call selected by pointcut methodToBeReplaced()
@Around("methodToBeReplaced()")
public void replaceMethodCall(ProceedingJoinPoint pjp) throws Throwable {
// get reference to the method to be replaced
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
// extract the name of the method to be called from ReplacingMethod annotation
ReplacingMethod replacingMethodAnnotation = method.getAnnotation(ReplacingMethod.class);
String methodToCallName = replacingMethodAnnotation.value();
// use reflection to call the method
Method methodToCall = pjp.getTarget().getClass().getMethod(methodToCallName);
methodToCall.invoke(pjp.getTarget());
}
}
现在,假设您在 class TestClass
应用了 @ReplacingMethod
注释,
public class TestClass {
@ReplacingMethod("bar")
public void foo() { System.out.println("foo"); }
public void bar() { System.out.println("bar"); }
}
最后遗漏的部分是创建启用 AOP 的 TestClass
实例并应用 ReplacingAspect
:
public class Main {
public static void main(String... args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class); // create Spring context that enables AOP under the hood
TestClass testObject = context.getBean(TestClass.class); // we get reference to TestClass instance from context; calling on a plain new instance wouldn't work
testObject.foo(); // prints "bar" !
}
@EnableAspectJAutoProxy // enables AOP support
@Configuration
public static class TestConfiguration {
@Bean public TestClass testClass() { return new TestClass(); }
@Bean public ReplacingAspect aspect() { return new ReplacingAspect(); } // enables our ReplacingAspect
}
}
您可以在 GitHub 查看整个工作示例。
反射不能改变 class 的模式而不是它的行为。它只能调用(可能隐藏的)特征。
如果您想用另一个方法调用替换方法调用,请尝试使用字节代码库作为 asm or javassist。这些工具允许您更改 class 定义和行为(即使在运行时有一些限制)。
使用 AOP 的方法更简单,但不够灵活,而且 class路径占用空间更大。
我有两种方法,其中一种带有注释,比方说:
@ReplacingMethod(bar)
public void foo() { ... }
public void bar { ... }
是否可以在调用 foo 时调用 bar 而不是 foo,而不跳转到 foo 的主体?我对此做了一些研究,但无法通过反射设置 return 值。有什么建议吗?
您可以使用 Aspect Oriented Programming, e.g. with Spring AOP 实现此目的。我不认为你可以在没有 AOP 的情况下更改纯 Java 中的方法实现。
让我举例说明如何使用 Spring AOP 实现您的要求。首先,定义你的注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReplacingMethod {
String value();
}
然后定义一个 方面 来实际替换方法:
@Aspect // aspect is a module encapsulating your replacing functionality
public class ReplacingAspect {
// pointcut gives an expression selecting the "joint points" to be intercepted
@Pointcut("@annotation(example.annotation.ReplacingMethod)")
public void methodToBeReplaced() { }
// advice defining the code executed at joint points selected by given pointcut;
// in our case @Around is executed instead of the method call selected by pointcut methodToBeReplaced()
@Around("methodToBeReplaced()")
public void replaceMethodCall(ProceedingJoinPoint pjp) throws Throwable {
// get reference to the method to be replaced
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
// extract the name of the method to be called from ReplacingMethod annotation
ReplacingMethod replacingMethodAnnotation = method.getAnnotation(ReplacingMethod.class);
String methodToCallName = replacingMethodAnnotation.value();
// use reflection to call the method
Method methodToCall = pjp.getTarget().getClass().getMethod(methodToCallName);
methodToCall.invoke(pjp.getTarget());
}
}
现在,假设您在 class TestClass
应用了 @ReplacingMethod
注释,
public class TestClass {
@ReplacingMethod("bar")
public void foo() { System.out.println("foo"); }
public void bar() { System.out.println("bar"); }
}
最后遗漏的部分是创建启用 AOP 的 TestClass
实例并应用 ReplacingAspect
:
public class Main {
public static void main(String... args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class); // create Spring context that enables AOP under the hood
TestClass testObject = context.getBean(TestClass.class); // we get reference to TestClass instance from context; calling on a plain new instance wouldn't work
testObject.foo(); // prints "bar" !
}
@EnableAspectJAutoProxy // enables AOP support
@Configuration
public static class TestConfiguration {
@Bean public TestClass testClass() { return new TestClass(); }
@Bean public ReplacingAspect aspect() { return new ReplacingAspect(); } // enables our ReplacingAspect
}
}
您可以在 GitHub 查看整个工作示例。
反射不能改变 class 的模式而不是它的行为。它只能调用(可能隐藏的)特征。
如果您想用另一个方法调用替换方法调用,请尝试使用字节代码库作为 asm or javassist。这些工具允许您更改 class 定义和行为(即使在运行时有一些限制)。
使用 AOP 的方法更简单,但不够灵活,而且 class路径占用空间更大。