我正在开发一个服务插件。为了使其起作用,它需要一些服务不提供的数据。
插件具有严格的加载/卸载规范。裸插件看起来像这样:
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的包。里面有一个方法。 aMethod看起来像这样:
public boolean aMethod(boolean bool) {
return bool;
}
过于简化的场景,但它确实有效。 每当调用aMethod时,我的插件都需要知道bool的值。这对我的课程来说绝对至关重要;我没有办法获得这个价值。
我会建议aMethod,但由于我的插件是在服务之后加载的,所以这不起作用。根据我的理解,加载时间编织在这里也不合适,因为它是在AFTER之后加载的。
尽管它不起作用,但这是我正在使用的方面,以防它可能有用:
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标志,以及公共静态premain,因此它不适合,因为我只有onEnable和onDisable。
然后我发现了Java Attach API。从外观上看,它允许我在加载一个类之后附加我的代理,分析器。这看起来很完美,但经过半个小时的搜索,我无法找到一个我能理解的好例子。
有人可以帮忙吗?这是一个二合一的问题:AspectJ可以用于此吗?如果是这样,怎么样?此外,如果它不能,有人能指出我正确的方向使用Attach API与ASM探查器?
提前致谢!
答案 0 :(得分:2)
我做到了!
我为概述文件here创建了一个运行时附加程序。它的基本原理是,将premain重命名为'agentmain'。
我制作了一个带有attacher的Util类以及其他有用的函数。附件通过使用代理创建一个jar来工作,清单表明它可以分析。 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());
请记住,attacher希望Agent.class成为主要代理。你可以轻松改变这一点。 Class []的其余部分是要包含在临时agent.jar
中的类如果你的IDE抱怨“UnsatisfiedLinkError”,那是因为只需要JDK就可以附加。(dll | so)。只需将其粘贴到%JAVA_PATH%/ jre / lib中即可。另外,添加对JDK的tools.jar的引用,因为它包含所有com.sun导入。
编辑:我有一个正在运行的github示例,任何人都可能认为这是有用的。它的here。