gpt4 book ai didi

java - 分析一个已经加载的类

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:32:27 25 4
gpt4 key购买 nike

我正在为一项服务开发一个插件。为了使其正常运行,它需要一些服务不提供的数据。

插件有严格的加载/卸载规范。裸插件如下所示:

public class Plugin extends JavaPlugin 
{
@Override
public void onEnable() {} //Plugin enters here. Comparable to main(String[] args)

@Override
public void onDisable() {} //Plugin exits here, when service shuts down.
}

有一个名为 org.service.aClass 的包。它里面有一个方法。一个方法看起来像这样:

public boolean aMethod(boolean bool) {
return bool;
}

一个过于简单的场景,但它有效。 每当调用 aMethod 时,我的插件需要知道 bool 的值。这对我的程序来说绝对是至关重要的;我没有其他方法可以获得该值。

我会建议使用 aMethod,但由于我的插件是在服务之后加载的,所以这将不起作用。据我所知,加载时间编织也不适合这里,因为是在之后加载的。

尽管它不起作用,但这是我正在使用的方面,以防它有任何用处:

public aspect LogAspect {

pointcut publicMethodExecuted(): execution(* org.service.aClass.aMethod(..));

after(): publicMethodExecuted(){
cat(String.format("Entered method: %s.",
thisJoinPoint.getSignature()));

List<Object> arguments = Arrays.asList(thisJoinPoint.getArgs());
List<Object> argTypes = new ArrayList<Object>();
for (Object o: arguments) {
argTypes.add(o.getClass().toString());

}

cat(String.format("With argument types: %s and values: %s.",
argTypes, arguments));


cat(String.format("Exited method: %s.", thisJoinPoint.getSignature()));
}

public void cat(Object dog) {
System.out.println("[TEST] " + dog);
}

}

我现在打开了 AspectJ:In Action 一书,在所有加载时编织示例中,它都提到程序必须以 -javaagent 标志启动。由于我的 proram 是一个插件,所以不可能发生这种情况。

我还研究了 ASM。我找到了一个关于构建分析器的精彩教程(基本上是我想做的)here .

问题是它在启动时再次使用 -javaagent 标志,以及公共(public)静态预维护,所以它不合适,因为我只有 onEnable 和 onDisable。

然后我发现了Java的Attach API .从它的外观来看,这将允许我在加载类后附加我的代理程序,分析器。看起来很完美,但经过半小时的搜索,我找不到一个我能理解的很好的例子。

有人能帮忙吗?这是一个二合一的问题:AspectJ 可以用于此吗?如果是这样,怎么办?此外,如果它不能,有人可以指出正确的方向,将 Attach API 与 ASM 分析器一起使用吗?

提前致谢!

最佳答案

我做到了!

我为概要分析器创建了一个运行时附件 here .基本上就是这样,premain 重命名为“agentmain”。

我创建了一个 Util 类,其中包含附加程序以及其他有用的功能。附加器通过使用代理创建一个 jar 来工作,并附有声明它可以分析的 list 。 Util 类如下所示:

    public class Util {

/**
* Gets the current JVM PID
* @return
* Returns the PID
* @throws Exception
*/

public static String getPidFromRuntimeMBean() {
String jvm = ManagementFactory.getRuntimeMXBean().getName();
String pid = jvm.substring(0, jvm.indexOf('@'));
return pid;
}

/**
* Attaches given agent classes to JVM
*
* @param agentClasses
* A Class<?>[] of classes to be included in agent
* @param JVMPid
* The PID of the JVM to attach to
*/

public static void attachAgentToJVM(Class<?>[] agentClasses, String JVMPid) {

try {


File jarFile = File.createTempFile("agent", ".jar");
jarFile.deleteOnExit();

Manifest manifest = new Manifest();
Attributes mainAttributes = manifest.getMainAttributes();
mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
mainAttributes.put(new Attributes.Name("Agent-Class"),
Agent.class.getName());
mainAttributes.put(new Attributes.Name("Can-Retransform-Classes"),
"true");
mainAttributes.put(new Attributes.Name("Can-Redefine-Classes"), "true");

JarOutputStream jos = new JarOutputStream(new FileOutputStream(
jarFile), manifest);


for(Class<?> clazz: agentClasses) {
JarEntry agent = new JarEntry(clazz.getName().replace('.',
'/')
+ ".class");
jos.putNextEntry(agent);

jos.write(getBytesFromIS(clazz.getClassLoader()
.getResourceAsStream(
clazz.getName().replace('.', '/') + ".class")));
jos.closeEntry();
}

jos.close();
VirtualMachine vm = VirtualMachine.attach(JVMPid);
vm.loadAgent(jarFile.getAbsolutePath());
vm.detach();
} catch (Exception e) {
e.printStackTrace();
}

}

/**
* Gets bytes from InputStream
*
* @param stream
* The InputStream
* @return
* Returns a byte[] representation of given stream
*/

public static byte[] getBytesFromIS(InputStream stream) {

ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
int nRead;
byte[] data = new byte[16384];

while ((nRead = stream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}

buffer.flush();
} catch (Exception e) {
System.err.println("Failed to convert IS to byte[]!");
e.printStackTrace();
}

return buffer.toByteArray();

}

/**
* Gets bytes from class
*
* @param clazz
* The class
* @return
* Returns a byte[] representation of given class
*/

public static byte[] getBytesFromClass(Class<?> clazz) {
return getBytesFromIS(clazz.getClassLoader().getResourceAsStream( clazz.getName().replace('.', '/') + ".class"));
}

}

为了清晰起见,我包含了 JavaDoc 注释。

使用它的一个例子是:

Util.attachAgentToJVM(new Class<?>[] { Agent.class, Util.class,
Profile.class, ProfileClassAdapter.class,
ProfileMethodAdapter.class }, Util.getPidFromRuntimeMBean());

请记住,附加器希望 Agent.class 成为主要代理。您可以轻松更改此设置。 Class[] 的其余部分是要包含在临时 agent.jar 中的类

如果您的 IDE 提示“UnsatisfiedLinkError”,那是因为为此所需的 attach.(dll|so) 仅随 JDK 一起提供。只需将其复制粘贴到您的 %JAVA_PATH%/jre/lib 中即可。此外,添加对 JDK 的 tools.jar 的引用,因为它包含所有 com.sun 导入。

编辑: 我有一个可用的 github 示例,任何人都可能认为这很有用。它的here .

关于java - 分析一个已经加载的类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12238694/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com