写在前面
本文看下如何通过bytebuddy的advice切面技术来动态修改方法入参值。
1:程序
首先定义premain:
package com.dahuyou.change.method.param;
//import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import java.lang.instrument.Instrumentation;
public class MyPreMain {
public static void premain(String agentArgs, Instrumentation instrumentation) {
System.out.println("MyPreMain.premain agentArgs is: " + agentArgs);
AgentBuilder agentBuilder = new AgentBuilder.Default();
AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
builder = builder.visit(
Advice.to(MyByteBuddyAdvice.class)
.on(ElementMatchers.named("newSelectorManager")));
/*
AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
builder = builder.visit(
Advice.to(MyByteBuddyAdvice.class)
.on(ElementMatchers.isMethod()
.and(ElementMatchers.any())
.and(ElementMatchers.not(ElementMatchers.nameStartsWith("main")))));
*/
return builder;
};
agentBuilder = agentBuilder.type(ElementMatchers.nameStartsWith("com.dahuyou.change.method.param")).transform(transformer).asDecorator();
//监听
AgentBuilder.Listener listener = new AgentBuilder.Listener() {
@Override
public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {
}
@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) {
System.out.println("onTransformation:" + typeDescription);
}
@Override
public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) {
}
@Override
public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) {
}
@Override
public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {
}
};
// 将bytebuddy的插桩逻辑安装到instrument
agentBuilder.with(listener).installOn(instrumentation);
}
/**
* 有public static void premain(String agentArgs, Instrumentation instrumentation)这个方法,本方法不会被调用
* @param agentArgs
*/
public static void premain(String agentArgs) {}
}
重点是代码Advice.to(MyByteBuddyAdvice.class)
指定了切面类MyByteBuddyAdvice,在方法进入和方法退出时分别调用对应注解的方法,源码如下:
package com.dahuyou.change.method.param;
import net.bytebuddy.asm.Advice;
public class MyByteBuddyAdvice {
@Advice.OnMethodEnter()
public static void enter(@Advice.Argument(value=2, readOnly=false) int param2,
@Advice.Origin("#t") String className,
@Advice.Origin("#m") String methodName) {
// System.out.println("MyByteBuddyAdvice.enter className " + className + ", methodName is: " + methodName);
System.out.println("MyByteBuddyAdvice.enter param2 " + param2);
param2 = 9090;
}
@Advice.OnMethodExit()
public static void exit(@Advice.Origin("#t") String className, @Advice.Origin("#m") String methodName) {
// System.out.println("MyByteBuddyAdvice.exit className " + className + ", methodName is: " + methodName);
}
}
注解@Advice.OnMethodEnter()
标注的方法在方法执行前调用,注解@Advice.OnMethodExit()
标注的方法在方法退出前调用。@Advice.Argument(value=2, readOnly=false) int param2
注解用于获取方法的第三个参数(要保证有第三个参数,并且类型是int,如果不是,我也没验证会发生什么,但肯定程序就跑不了了)
,#t
,#m
分别获取类名称和方法名称。
在enter方法中修改第三个参数的值param2 = 9090
。
测试类:
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package com.dahuyou.change.method.param;
public class ServerConnector {
public static void main(String[] args) {
ServerConnector serverConnector = new ServerConnector();
serverConnector.newSelectorManager(null, null, 9);
}
protected void newSelectorManager(Object executor, Object scheduler, int selectors) {
showSelector(selectors);
}
private void showSelector(int selectors) {
System.out.println("selector is: " + selectors);
}
}
打包:
配置Javaagent:
运行:
写在后面
参考文章列表
ByteBuddy(五)—拦截方法参数、方法返回和实例变量 。