diff --git a/designer-base/src/main/java/com/fr/design/record/analyzer/advice/FaultToleranceAdvice.java b/designer-base/src/main/java/com/fr/design/record/analyzer/advice/FaultToleranceAdvice.java index a8198363e9..4f40ef9430 100644 --- a/designer-base/src/main/java/com/fr/design/record/analyzer/advice/FaultToleranceAdvice.java +++ b/designer-base/src/main/java/com/fr/design/record/analyzer/advice/FaultToleranceAdvice.java @@ -1,6 +1,8 @@ package com.fr.design.record.analyzer.advice; import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; +import com.fr.record.analyzer.advice.AdviceContext; +import com.fr.record.analyzer.advice.DefaultAdviceCallable; import com.fr.third.net.bytebuddy.asm.Advice; import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; import com.fr.tolerance.FaultTolerance; @@ -15,19 +17,30 @@ import java.util.concurrent.Callable; public class FaultToleranceAdvice implements DesignerAnalyzerAdvice { @Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class) - public static int onMethodEnter() throws Exception { - return 0; + public static boolean onMethodEnter(@Advice.Local("context") AdviceContext adviceContext) throws Exception { + + adviceContext = AdviceContext + .builder() + .onAdviceCall() + .build(); + // 如果是切面调用,则忽视当前方法 + return adviceContext.isOnAdviceCall(); } @Advice.OnMethodExit(onThrowable = Exception.class) public static void onMethodExit(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self, @Advice.Origin Method method, @Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args, - @Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result) throws Exception { + @Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result, + @Advice.Local("context")AdviceContext adviceContext) throws Exception { + // 如果是切面调用,则忽视不继续 exit + if (adviceContext != null && adviceContext.isOnAdviceCall()) { + return; + } FaultTolerance faultTolerance = method.getAnnotation(FaultTolerance.class); - Callable callable = () -> method.invoke(self, args); + Callable callable = new DefaultAdviceCallable<>(self, method, args); result = FaultToleranceFactory.getInstance().getScene(faultTolerance.scene()).getProcessor().execute(self, callable, args); } } diff --git a/designer-base/src/test/java/com/fr/design/record/analyzer/BytebuddyRedefineTest.java b/designer-base/src/test/java/com/fr/design/record/analyzer/BytebuddyRedefineTest.java index aade4591e6..837c2bd5b8 100644 --- a/designer-base/src/test/java/com/fr/design/record/analyzer/BytebuddyRedefineTest.java +++ b/designer-base/src/test/java/com/fr/design/record/analyzer/BytebuddyRedefineTest.java @@ -5,9 +5,12 @@ import com.fr.third.net.bytebuddy.agent.ByteBuddyAgent; import com.fr.third.net.bytebuddy.asm.Advice; import com.fr.third.net.bytebuddy.dynamic.loading.ClassReloadingStrategy; import com.fr.third.net.bytebuddy.matcher.ElementMatchers; +import com.fr.third.org.apache.commons.lang3.time.StopWatch; import org.junit.Assert; import org.junit.Test; +import java.util.concurrent.TimeUnit; + /** * 测试一下,通过 redefine 去处理代码时 * 相应的 advice 应该怎么写 @@ -80,4 +83,57 @@ public class BytebuddyRedefineTest { } + @Test + public void testCallable() throws Exception { + + ByteBuddyAgent.install(); + + new ByteBuddy() + .redefine(TestClass.class) + .visit(Advice.to(TestCallableAdvice.class).on(ElementMatchers.named("testPrint"))) + .make() + .load(TestClass.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); + + TestClass testClass = new TestClass(); + String print = testClass.testPrint(); + + Assert.assertEquals("[test]Callable", print); + } + + @Test + public void testCallablePerformance() throws Exception { + + // 千 + int loop = 1000; + StopWatch stopWatch = new StopWatch(); + + stopWatch.start(); + TestClass rawClass = new TestClass(); + for (int i = 0; i < loop; i++) { + rawClass.testPrint(); + } + System.out.printf("raw class run %s cost %s ms \n", loop, stopWatch.getTime(TimeUnit.MILLISECONDS)); + + + ByteBuddyAgent.install(); + + new ByteBuddy() + .redefine(TestClass.class) + .visit(Advice.to(TestCallableAdvice.class).on(ElementMatchers.named("testPrint"))) + .make() + .load(TestClass.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); + + stopWatch.reset(); + stopWatch.start(); + + TestClass retransformClass = new TestClass(); + for (int i = 0; i < loop; i++) { + retransformClass.testPrint(); + } + + System.out.printf("retransformClass class run %s cost %s ms \n", loop, stopWatch.getTime(TimeUnit.MILLISECONDS)); + + Assert.assertEquals("[test]Callable", retransformClass.testPrint()); + } + } \ No newline at end of file diff --git a/designer-base/src/test/java/com/fr/design/record/analyzer/TestCallableAdvice.java b/designer-base/src/test/java/com/fr/design/record/analyzer/TestCallableAdvice.java new file mode 100644 index 0000000000..f98cedcde9 --- /dev/null +++ b/designer-base/src/test/java/com/fr/design/record/analyzer/TestCallableAdvice.java @@ -0,0 +1,50 @@ +package com.fr.design.record.analyzer; + +import com.fr.record.analyzer.advice.AdviceCallable; +import com.fr.record.analyzer.advice.AdviceContext; +import com.fr.third.net.bytebuddy.asm.Advice; +import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; +import com.fr.tolerance.FaultTolerance; + +import java.lang.reflect.Method; +import java.util.concurrent.Callable; + +/** + * created by Harrison on 2022/03/09 + **/ +public class TestCallableAdvice { + + @Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class) + public static boolean onMethodEnter(@Advice.Local("context")AdviceContext adviceContext) { + + adviceContext = AdviceContext + .builder() + .onAdviceCall() + .build(); + // 如果是切面调用,则忽视当前方法 + return adviceContext.isOnAdviceCall(); + } + + @Advice.OnMethodExit(onThrowable = Exception.class) + public static void onMethodExit(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self, + @Advice.Origin Method method, + @Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args, + @Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result, + @Advice.Local("context") AdviceContext adviceContext) throws Exception { + + // 如果是切面调用,则忽视不继续 exit + if (adviceContext != null && adviceContext.isOnAdviceCall()) { + return; + } + + FaultTolerance faultTolerance = method.getAnnotation(FaultTolerance.class); + Callable callable = new AdviceCallable() { + @Override + public Object call() throws Exception { + return method.invoke(self, args); + } + }; + result = TestCallableHelper.test(callable); + } + +} diff --git a/designer-base/src/test/java/com/fr/design/record/analyzer/TestCallableHelper.java b/designer-base/src/test/java/com/fr/design/record/analyzer/TestCallableHelper.java new file mode 100644 index 0000000000..4e99bb7031 --- /dev/null +++ b/designer-base/src/test/java/com/fr/design/record/analyzer/TestCallableHelper.java @@ -0,0 +1,15 @@ +package com.fr.design.record.analyzer; + +import java.util.concurrent.Callable; + +/** + * created by Harrison on 2022/03/09 + **/ +public class TestCallableHelper { + + public static String test(Callable callable) throws Exception { + + callable.call(); + return "[test]Callable"; + } +}