Harrison
3 years ago
14 changed files with 643 additions and 0 deletions
@ -0,0 +1,22 @@
|
||||
package com.fr.design.record.analyzer; |
||||
|
||||
import com.fr.record.analyzer.AnalyzerConfiguration; |
||||
import com.fr.record.analyzer.AnalyzerUnit; |
||||
import com.fr.record.analyzer.configuration.AnalyzerAssemblyFactory; |
||||
import com.fr.record.analyzer.configuration.impl.RedefineAssemblyFactory; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/03/08 |
||||
**/ |
||||
public class DesignerAnalyzer { |
||||
|
||||
private static final AnalyzerUnit ANALYZER = new AnalyzerUnit(); |
||||
|
||||
public static synchronized void init(AnalyzerAssemblyFactory factory, AnalyzerConfiguration... configurations) { |
||||
|
||||
AnalyzerAssemblyFactory redefineFactory = factory.prepare(RedefineAssemblyFactory.getInstance()); |
||||
|
||||
ANALYZER.init(redefineFactory, configurations); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,121 @@
|
||||
package com.fr.design.record.analyzer; |
||||
|
||||
import com.fr.base.OptimizeUtil; |
||||
import com.fr.concurrent.NamedThreadFactory; |
||||
import com.fr.design.record.analyzer.advice.DBMonitorAdvice; |
||||
import com.fr.design.record.analyzer.advice.FaultToleranceAdvice; |
||||
import com.fr.design.record.analyzer.advice.FocusAdvice; |
||||
import com.fr.design.record.analyzer.advice.MonitorAdvice; |
||||
import com.fr.design.record.analyzer.advice.PerformancePointAdvice; |
||||
import com.fr.intelli.metrics.Compute; |
||||
import com.fr.intelli.record.Focus; |
||||
import com.fr.intelli.record.PerformancePoint; |
||||
import com.fr.module.Activator; |
||||
import com.fr.module.extension.Prepare; |
||||
import com.fr.record.analyzer.AnalyzerConfiguration; |
||||
import com.fr.record.analyzer.AnalyzerKey; |
||||
import com.fr.record.analyzer.DBMetrics; |
||||
import com.fr.record.analyzer.FineAdviceTransformer; |
||||
import com.fr.record.analyzer.FineAnalyzer; |
||||
import com.fr.record.analyzer.advice.AnalyzerAdviceConfiguration; |
||||
import com.fr.record.analyzer.advice.AnalyzerAdviceKey; |
||||
import com.fr.record.analyzer.configuration.AnalyzerAssemblyFactory; |
||||
import com.fr.record.analyzer.configuration.FineAnalyzerAssemblyFactory; |
||||
import com.fr.stable.collections.CollectionUtils; |
||||
import com.fr.third.net.bytebuddy.matcher.ElementMatchers; |
||||
import com.fr.tolerance.FaultTolerance; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.util.List; |
||||
import java.util.concurrent.ExecutorService; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/03/04 |
||||
**/ |
||||
public class DesignerAnalyzerActivator extends Activator implements Prepare { |
||||
|
||||
@Override |
||||
public void start() { |
||||
|
||||
OptimizeUtil.open(() -> { |
||||
ExecutorService es = newSingleThreadExecutor(new NamedThreadFactory("designer-analyzer", true)); |
||||
|
||||
try { |
||||
AnalyzerAssemblyFactory basicFactory = createBasicFactory(); |
||||
|
||||
// 兼容逻辑
|
||||
List<AnalyzerConfiguration> backwardsConfigurations = findMutableBackwards(AnalyzerKey.KEY); |
||||
if (!CollectionUtils.isEmpty(backwardsConfigurations)) { |
||||
es.submit(() -> { |
||||
FineAnalyzer.init(basicFactory, backwardsConfigurations.toArray(new AnalyzerConfiguration[0])); |
||||
}); |
||||
} |
||||
|
||||
// 加入 retransform 部分的逻辑
|
||||
List<AnalyzerAdviceConfiguration> adviceConfigurations = findMutable(AnalyzerAdviceKey.KEY); |
||||
|
||||
if (!CollectionUtils.isEmpty(adviceConfigurations)) { |
||||
AnalyzerConfiguration[] configurations = convertConfigurations(adviceConfigurations); |
||||
es.submit(() -> { |
||||
DesignerAnalyzer.init(basicFactory, configurations); |
||||
}); |
||||
} |
||||
|
||||
} finally { |
||||
es.shutdown(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@NotNull |
||||
private AnalyzerConfiguration[] convertConfigurations(List<AnalyzerAdviceConfiguration> list) { |
||||
|
||||
return list.stream() |
||||
.map((e) -> AnalyzerConfiguration.create(e.getMatcher(), new FineAdviceTransformer(e.getMatcher(), e.getAdviceClass()))) |
||||
.toArray(AnalyzerConfiguration[]::new); |
||||
} |
||||
|
||||
@Override |
||||
public void stop() { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void prepare() { |
||||
|
||||
addMutable(AnalyzerAdviceKey.KEY, AnalyzerAdviceConfiguration.create( |
||||
ElementMatchers.isAnnotatedWith(Focus.class), |
||||
FocusAdvice.class |
||||
)); |
||||
|
||||
addMutable(AnalyzerAdviceKey.KEY, AnalyzerAdviceConfiguration.create( |
||||
ElementMatchers.isAnnotatedWith(Compute.class), |
||||
MonitorAdvice.class |
||||
)); |
||||
|
||||
addMutable(AnalyzerAdviceKey.KEY, AnalyzerAdviceConfiguration.create( |
||||
ElementMatchers.isAnnotatedWith(DBMetrics.class), |
||||
DBMonitorAdvice.class |
||||
)); |
||||
|
||||
addMutable(AnalyzerAdviceKey.KEY, AnalyzerAdviceConfiguration.create( |
||||
ElementMatchers.isAnnotatedWith(PerformancePoint.class), |
||||
PerformancePointAdvice.class |
||||
)); |
||||
|
||||
addMutable(AnalyzerAdviceKey.KEY, AnalyzerAdviceConfiguration.create( |
||||
ElementMatchers.isAnnotatedWith(FaultTolerance.class), |
||||
FaultToleranceAdvice.class |
||||
)); |
||||
|
||||
} |
||||
|
||||
|
||||
private AnalyzerAssemblyFactory createBasicFactory() { |
||||
|
||||
AnalyzerAssemblyFactory factory = findSingleton(AnalyzerAssemblyFactory.class); |
||||
FineAnalyzerAssemblyFactory basicFactory = new FineAnalyzerAssemblyFactory(); |
||||
basicFactory.prepare(factory); |
||||
return basicFactory; |
||||
} |
||||
} |
@ -0,0 +1,12 @@
|
||||
package com.fr.design.record.analyzer; |
||||
|
||||
import com.fr.record.analyzer.advice.AnalyzerAdvice; |
||||
|
||||
/** |
||||
* 仅作为标志 |
||||
* 没有方法 |
||||
* |
||||
* created by Harrison on 2022/03/04 |
||||
**/ |
||||
public interface DesignerAnalyzerAdvice extends AnalyzerAdvice { |
||||
} |
@ -0,0 +1,29 @@
|
||||
package com.fr.design.record.analyzer.advice; |
||||
|
||||
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; |
||||
import com.fr.general.data.DataModel; |
||||
import com.fr.measure.DBMeterFactory; |
||||
import com.fr.measure.metric.DBMetric; |
||||
import com.fr.third.net.bytebuddy.asm.Advice; |
||||
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/03/07 |
||||
**/ |
||||
public class DBMonitorAdvice implements DesignerAnalyzerAdvice { |
||||
|
||||
@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.Thrown(typing = Assigner.Typing.DYNAMIC) Exception e) { |
||||
|
||||
if (args.length > 1 && args[1] instanceof DataModel) { |
||||
DBMetric meter = ((DataModel) args[1]).getMetric(); |
||||
DBMeterFactory.getMeter().record(meter); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,35 @@
|
||||
package com.fr.design.record.analyzer.advice; |
||||
|
||||
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; |
||||
import com.fr.third.net.bytebuddy.asm.Advice; |
||||
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||
import com.fr.tolerance.FaultTolerance; |
||||
import com.fr.tolerance.FaultToleranceFactory; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.concurrent.Callable; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/03/07 |
||||
**/ |
||||
public class FaultToleranceAdvice implements DesignerAnalyzerAdvice { |
||||
|
||||
@Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class) |
||||
public static int onMethodEnter(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self, |
||||
@Advice.Origin Method method, |
||||
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args) throws Exception { |
||||
return 0; |
||||
} |
||||
|
||||
@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 { |
||||
|
||||
|
||||
FaultTolerance faultTolerance = method.getAnnotation(FaultTolerance.class); |
||||
Callable<Object> callable = () -> method.invoke(self, args); |
||||
result = FaultToleranceFactory.getInstance().getScene(faultTolerance.scene()).getProcessor().execute(self, callable, args); |
||||
} |
||||
} |
@ -0,0 +1,32 @@
|
||||
package com.fr.design.record.analyzer.advice; |
||||
|
||||
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; |
||||
import com.fr.intelli.record.Focus; |
||||
import com.fr.intelli.record.FocusPoint; |
||||
import com.fr.intelli.record.FocusPolicy; |
||||
import com.fr.log.counter.DefaultLimitedMetric; |
||||
import com.fr.third.net.bytebuddy.asm.Advice; |
||||
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/03/07 |
||||
**/ |
||||
public class FocusAdvice implements DesignerAnalyzerAdvice { |
||||
|
||||
private static final String FOCUS_POINT_ID_PREFIX = "function_"; |
||||
|
||||
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||
public static void onMethodExit(@Advice.Origin Method method, |
||||
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] arguments, |
||||
@Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result) throws Exception { |
||||
|
||||
if (FocusPolicy.IGNORE == result) { |
||||
return; |
||||
} |
||||
Focus focus = method.getAnnotation(Focus.class); |
||||
String id = FOCUS_POINT_ID_PREFIX + focus.id(); |
||||
DefaultLimitedMetric.INSTANCE.submit(FocusPoint.create(id, focus.text(), focus.source()), id); |
||||
} |
||||
} |
@ -0,0 +1,159 @@
|
||||
package com.fr.design.record.analyzer.advice; |
||||
|
||||
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; |
||||
import com.fr.general.GeneralUtils; |
||||
import com.fr.intelli.metrics.Compute; |
||||
import com.fr.intelli.metrics.SupervisoryConfig; |
||||
import com.fr.intelli.record.Measurable; |
||||
import com.fr.intelli.record.MeasureObject; |
||||
import com.fr.intelli.record.MeasureUnit; |
||||
import com.fr.intelli.record.MetricRegistry; |
||||
import com.fr.measure.DBMeterFactory; |
||||
import com.fr.stable.ArrayUtils; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.stable.web.Session; |
||||
import com.fr.stable.web.SessionProvider; |
||||
import com.fr.third.net.bytebuddy.asm.Advice; |
||||
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||
import com.fr.web.core.SessionPoolManager; |
||||
import com.fr.web.session.SessionLocalManager; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.Method; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.UUID; |
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/03/07 |
||||
**/ |
||||
public class MonitorAdvice implements DesignerAnalyzerAdvice { |
||||
|
||||
private static final Pattern P = Pattern.compile("-?\\d+"); |
||||
private static final int MIN_ERROR_CODE = 10000000; |
||||
|
||||
@Advice.OnMethodEnter |
||||
public static void onMethodEnter(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self, |
||||
@Advice.Origin Method method, |
||||
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args, |
||||
@Advice.Local("startTime") Long startTime, |
||||
@Advice.Local("registeredSession") Boolean registeredSession) { |
||||
|
||||
startTime = (System.currentTimeMillis()); |
||||
registeredSession = (findSessionAnnotation(method, args)); |
||||
} |
||||
|
||||
@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.Thrown(typing = Assigner.Typing.DYNAMIC) Exception e, |
||||
@Advice.Local("startTime") Long startTime, |
||||
@Advice.Local("registeredSession") Boolean registeredSession) throws Exception { |
||||
|
||||
String error = StringUtils.EMPTY; |
||||
|
||||
try { |
||||
|
||||
if (e != null) { |
||||
try { |
||||
error = getErrorContent(e); |
||||
} catch (Exception ignore) { |
||||
} |
||||
} |
||||
} finally { |
||||
try { |
||||
if (self instanceof Measurable) { |
||||
long consume = System.currentTimeMillis() - startTime; |
||||
Compute once = method.getAnnotation(Compute.class); |
||||
Measurable measurable = (Measurable) self; |
||||
MeasureObject measureObject = MeasureObject.create(); |
||||
recordMemory(once, measurable, measureObject); |
||||
recordSQL(once, measureObject); |
||||
measureObject.consume(consume); |
||||
measureObject.error(error); |
||||
String id = UUID.randomUUID().toString(); |
||||
List<Object> newArgs = new ArrayList<Object>(Arrays.asList(args)); |
||||
newArgs.add(id); |
||||
recordSQLDetail(id); |
||||
Object message; |
||||
try { |
||||
message = measurable.durableEntity(measureObject, newArgs.toArray()); |
||||
} catch (Throwable ignore) { |
||||
//埋点生成失败,降级逻辑
|
||||
message = measurable.fallBackEntity(); |
||||
} |
||||
if (message != null) { |
||||
SessionProvider provider = SessionLocalManager.getSession(); |
||||
if (provider == null) { |
||||
MetricRegistry.getMetric().submit(message); |
||||
} else { |
||||
MetricRegistry.getMetric().submitAccumulativeData(provider.getSessionID(), message); |
||||
} |
||||
} |
||||
} |
||||
} catch (Exception ignore) { |
||||
//埋点信息入库失败应该不能影响业务流程
|
||||
} finally { |
||||
if (registeredSession) { |
||||
// 如果上面记录了,这里就要释放
|
||||
SessionLocalManager.releaseSession(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private static String getErrorContent(Exception e) { |
||||
int errorCode = GeneralUtils.objectToNumber( |
||||
extractCodeFromString(e.getMessage()) |
||||
).intValue(); |
||||
// 提取字符串中的第一个数字,最小的错误码为10000000
|
||||
return e.getClass().getName() + ":" + (errorCode >= MIN_ERROR_CODE ? errorCode : StringUtils.EMPTY); |
||||
} |
||||
|
||||
private static String extractCodeFromString(String errorMsg) { |
||||
Matcher m = P.matcher(errorMsg); |
||||
if (m.find()) { |
||||
return m.group(); |
||||
} |
||||
return StringUtils.EMPTY; |
||||
} |
||||
|
||||
private static void recordSQLDetail(String uuid) { |
||||
DBMeterFactory.getMeter().submit(uuid); |
||||
} |
||||
|
||||
private static void recordSQL(Compute once, MeasureObject measureObject) { |
||||
if (SupervisoryConfig.getInstance().isEnableMeasureSql() && once.computeSql()) { |
||||
measureObject.sqlTime(SessionLocalManager.getSqlTime()); |
||||
measureObject.sql(SessionLocalManager.getSql()); |
||||
} |
||||
} |
||||
|
||||
private static void recordMemory(Compute once, Measurable measurable, MeasureObject measureObject) { |
||||
if (SupervisoryConfig.getInstance().isEnableMeasureMemory() && once.computeMemory()) { |
||||
MeasureUnit unit = measurable.measureUnit(); |
||||
measureObject.memory(unit.measureMemory()); |
||||
} |
||||
} |
||||
|
||||
private static boolean findSessionAnnotation(Method method, Object[] args) { |
||||
Annotation[][] all = method.getParameterAnnotations(); |
||||
int len = ArrayUtils.getLength(args); |
||||
for (int i = 0; i < len; i++) { |
||||
Annotation[] current = all[i]; |
||||
for (Annotation annotation : current) { |
||||
if (annotation.annotationType().equals(Session.class)) { |
||||
SessionLocalManager.setSession( |
||||
SessionPoolManager.getSessionIDInfor(GeneralUtils.objectToString(args[i]), SessionProvider.class)); |
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
} |
@ -0,0 +1,54 @@
|
||||
package com.fr.design.record.analyzer.advice; |
||||
|
||||
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; |
||||
import com.fr.intelli.record.ConsumePoint; |
||||
import com.fr.intelli.record.MetricRegistry; |
||||
import com.fr.intelli.record.PerformancePoint; |
||||
import com.fr.intelli.record.PerformancePointRecord; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.third.net.bytebuddy.asm.Advice; |
||||
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/03/07 |
||||
**/ |
||||
public class PerformancePointAdvice implements DesignerAnalyzerAdvice { |
||||
|
||||
@Advice.OnMethodEnter |
||||
public static void onMethodEnter(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self, |
||||
@Advice.Origin Method method, |
||||
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args, |
||||
@Advice.Local("startTime") Long startTime) { |
||||
|
||||
startTime = (System.currentTimeMillis()); |
||||
} |
||||
|
||||
@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.Thrown(typing = Assigner.Typing.DYNAMIC) Exception e, |
||||
@Advice.Local("startTime") Long startTime) { |
||||
|
||||
PerformancePoint point = method.getAnnotation(PerformancePoint.class); |
||||
String id = point.id(); |
||||
long endTime = System.currentTimeMillis(); |
||||
long consume = endTime - startTime; |
||||
if (self instanceof PerformancePointRecord) { |
||||
PerformancePointRecord measurable = (PerformancePointRecord) self; |
||||
List<Object> newArgs = new ArrayList<Object>(Arrays.asList(args)); |
||||
ConsumePoint consumePoint = ConsumePoint.create(id, startTime, endTime, consume, point.source()); |
||||
MetricRegistry.getMetric().submit(measurable.recordPoint(consumePoint, newArgs.toArray())); |
||||
} else { |
||||
if (StringUtils.isNotEmpty(id)) { |
||||
MetricRegistry.getMetric().submit(ConsumePoint.create(id, consume, point.source())); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,81 @@
|
||||
package com.fr.design.record.analyzer; |
||||
|
||||
import com.fr.third.net.bytebuddy.ByteBuddy; |
||||
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 org.junit.Test; |
||||
|
||||
/** |
||||
* 测试一下,通过 redefine 去处理代码时 |
||||
* 相应的 advice 应该怎么写 |
||||
*/ |
||||
public class BytebuddyRedefineTest { |
||||
|
||||
/** |
||||
* 测试一下是否可以直接抛出异常 |
||||
*/ |
||||
@Test |
||||
public void testThrowException() { |
||||
|
||||
try { |
||||
ByteBuddyAgent.install(); |
||||
|
||||
new ByteBuddy() |
||||
.redefine(TestClass.class) |
||||
.visit(Advice.to(TestThrowExceptionAdvice.class).on(ElementMatchers.named("testPrint"))) |
||||
.make() |
||||
.load(TestClass.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); |
||||
|
||||
TestClass testClass = new TestClass(); |
||||
testClass.testPrint(); |
||||
} catch (Throwable throwable) { |
||||
|
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 测试是否可以直接传值 |
||||
*/ |
||||
@Test |
||||
public void testTransferValue() { |
||||
|
||||
ByteBuddyAgent.install(); |
||||
|
||||
new ByteBuddy() |
||||
.redefine(TestClass.class) |
||||
.visit(Advice.to(TestTransferValueAdvice.class).on(ElementMatchers.named("testPrint"))) |
||||
.make() |
||||
.load(TestClass.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); |
||||
|
||||
TestClass testClass = new TestClass(); |
||||
String print = testClass.testPrint(); |
||||
|
||||
System.out.println(TestTransferValueAdvice.intField); |
||||
System.out.println(TestTransferValueAdvice.stringField); |
||||
System.out.println(TestTransferValueAdvice.objectField); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 测试是否可以改变返回值 |
||||
*/ |
||||
@Test |
||||
public void testModifyReturn() { |
||||
|
||||
ByteBuddyAgent.install(); |
||||
|
||||
new ByteBuddy() |
||||
.redefine(TestClass.class) |
||||
.visit(Advice.to(TestModifyReturnAdvice.class).on(ElementMatchers.named("testPrint"))) |
||||
.make() |
||||
.load(TestClass.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); |
||||
|
||||
TestClass testClass = new TestClass(); |
||||
String print = testClass.testPrint(); |
||||
|
||||
System.out.println(print); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,11 @@
|
||||
package com.fr.design.record.analyzer; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/03/04 |
||||
**/ |
||||
public class TestClass { |
||||
|
||||
public String testPrint() { |
||||
return ""; |
||||
} |
||||
} |
@ -0,0 +1,29 @@
|
||||
package com.fr.design.record.analyzer; |
||||
|
||||
import com.fr.third.net.bytebuddy.asm.Advice; |
||||
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/03/07 |
||||
**/ |
||||
public class TestModifyReturnAdvice { |
||||
|
||||
@Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class) |
||||
public static int onMethodEnter() throws Exception { |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
@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 { |
||||
|
||||
result = "[test]Modify Return Value"; |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,19 @@
|
||||
package com.fr.design.record.analyzer; |
||||
|
||||
import com.fr.third.net.bytebuddy.asm.Advice; |
||||
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/03/07 |
||||
**/ |
||||
public class TestThrowExceptionAdvice { |
||||
|
||||
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||
public static void onMethodExit(@Advice.Origin Method method, |
||||
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] arguments, |
||||
@Advice.Thrown(typing = Assigner.Typing.DYNAMIC) Exception e) throws Exception { |
||||
throw new RuntimeException("[test] throw exception in advice"); |
||||
} |
||||
} |
@ -0,0 +1,37 @@
|
||||
package com.fr.design.record.analyzer; |
||||
|
||||
import com.fr.third.net.bytebuddy.asm.Advice; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/03/07 |
||||
**/ |
||||
public class TestTransferValueAdvice { |
||||
|
||||
public static int intField; |
||||
|
||||
public static String stringField; |
||||
|
||||
public static Object objectField; |
||||
|
||||
@Advice.OnMethodEnter |
||||
public static void onMethodEnter(@Advice.Local("int") int intField, |
||||
@Advice.Local("string") String stringField, |
||||
@Advice.Local("Object") Object objectField) { |
||||
|
||||
intField = 10; |
||||
stringField = "[test]stringField"; |
||||
objectField = "[test]objectField"; |
||||
} |
||||
|
||||
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||
public static void onMethodExit(@Advice.Local("int") int intField, |
||||
@Advice.Local("string") String stringField, |
||||
@Advice.Local("Object") Object objectField) throws Exception { |
||||
|
||||
TestTransferValueAdvice.intField = intField; |
||||
TestTransferValueAdvice.stringField = stringField; |
||||
TestTransferValueAdvice.objectField = objectField; |
||||
|
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue