From fb70596c1d658b7e2d48bb11b7374c89f81fc78a Mon Sep 17 00:00:00 2001 From: Harrison Date: Thu, 10 Mar 2022 15:05:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20KERNEL-10354=20bytebuddy=20/=20?= =?UTF-8?q?=E5=8A=A0=E8=A7=A3=E5=AF=86=E6=80=A7=E8=83=BD=E4=BC=98=E5=8C=96?= =?UTF-8?q?=20@Harrison=20=E4=BF=AE=E5=A4=8D=20FaultToleranceAdvice=20?= =?UTF-8?q?=E7=9A=84=20Callable=20=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analyzer/DesignerAnalyzerListener.java | 8 +++ .../analyzer/advice/FaultToleranceAdvice.java | 21 +++++-- .../analyzer/BytebuddyRedefineTest.java | 56 +++++++++++++++++++ .../record/analyzer/TestCallableAdvice.java | 50 +++++++++++++++++ .../record/analyzer/TestCallableHelper.java | 15 +++++ 5 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 designer-base/src/test/java/com/fr/design/record/analyzer/TestCallableAdvice.java create mode 100644 designer-base/src/test/java/com/fr/design/record/analyzer/TestCallableHelper.java diff --git a/designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerListener.java b/designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerListener.java index 8db3a395f..dc455961a 100644 --- a/designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerListener.java +++ b/designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerListener.java @@ -6,6 +6,8 @@ import com.fr.third.net.bytebuddy.description.type.TypeDescription; import com.fr.third.net.bytebuddy.dynamic.DynamicType; import com.fr.third.net.bytebuddy.utility.JavaModule; +import java.io.File; + /** * created by Harrison on 2022/03/08 **/ @@ -14,6 +16,12 @@ public class DesignerAnalyzerListener extends AgentBuilder.Listener.Adapter { @Override public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) { FineLoggerFactory.getLogger().debug("Designer-Analyzer transform successfully:{}", typeDescription); + + try { + dynamicType.saveIn(new File("/Users/3dot141/Temp/bytebuddy")); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } } @Override 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 a8198363e..4f40ef943 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 aade4591e..837c2bd5b 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 000000000..f98cedcde --- /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 000000000..4e99bb703 --- /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"; + } +}