Короткий ответ
Вы можете сделать это путем создания ExprEditor и использовать его, чтобы изменить MethodCall S, которые соответствуют с начала и присоединиться объектов резьбы.
(очень) Длинный ответ (с кодом)
Прежде чем мы начнем, просто дайте мне сказать, что вы не должны быть запуганы длинный пост, большинство из них просто код и как только вы ломать вещи вниз, это довольно Легко понять!
Давайте заняты тогда ...
Представьте у вас есть следующий код: фиктивный
public class GuineaPig {
public void test() throws InterruptedException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++)
System.out.println(i);
}
});
t.start();
System.out.println("Sleeping 10 seconds");
Thread.sleep(10 * 1000);
System.out.println("Done joining thread");
t.join();
}
}
При запуске этого кода делает
new GuineaPig().test();
Вы получаете выход как (sleep system.out может отображаться в середине подсчета, так как он работает в основной нити):
Sleeping 10 seconds
0
1
2
3
4
5
6
7
8
9
Done joining thread
Наша цель состоит в том, чтобы создать код инжектора, который сделает изменение выходного сигнала на следующее:
Detected thread starting with id: 10
Sleeping 10 seconds
0
1
2
3
4
5
6
7
8
9
Done joining thread
Detected thread joining with id: 10
Мы немного ограничены, что мы можем сделать, но мы можем вводить код и получить доступ к нить ссылка. Надеюсь, этого вам будет достаточно, если мы не сможем еще немного обсудить это.
со всеми этими идеями в виде, мы создаем следующий инжектор:
ClassPool classPool = ClassPool.getDefault();
CtClass guineaPigCtClass = classPool.get(GuineaPig.class.getName());
guineaPigCtClass.instrument(new ExprEditor() {
@Override
public void edit(MethodCall m) throws CannotCompileException {
CtMethod method = null;
try {
method = m.getMethod();
} catch (NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String classname = method.getDeclaringClass().getName();
String methodName = method.getName();
if (classname.equals(Thread.class.getName())
&& methodName.equals("start")) {
m.replace("{ System.out.println(\"Detected thread starting with id: \" + ((Thread)$0).getId()); $proceed($$); } ");
} else if (classname.equals(Thread.class.getName())
&& methodName.equals("join")) {
m.replace("{ System.out.println(\"Detected thread joining with id: \" + ((Thread)$0).getId()); $proceed($$); } ");
}
}
});
guineaPigCtClass
.writeFile("<Your root directory with the class files>");
}
Так, что происходит в этом небольшом изящном куске коды? Мы используем ExprEdit для инструментария нашего класса GuineaPig (не нанося ему никакого вреда!) И перехватываем все вызовы методов.
Когда мы перехватываем вызов метода, сначала проверяем, является ли класс объявления класса Thread, если это так, это означает, что мы вызываем метод в объекте Thread. Затем переходим к проверке, является ли это одним из двух конкретных методов: start
и join
.
Когда происходит один из этих двух случаев, мы используем javassist highlevel API для замены кода.Замена легко обнаружить в коде, фактический код, предоставленный где это может быть немного сложнее, так что давайте разделить одну из этих линий, давайте возьмем, к примеру, линия, которая будет обнаруживать запуск темы:
{ System.out.println(\"Detected thread starting with id: \" + ((Thread)$0).getId()); $proceed($$); } "
- Сначала весь код внутри фигурных скобок, в противном случае Javassist не будет принимать это
- Тогда у вас есть System.out, который ссылается на $ 0. $ 0 - специальный параметр, который может использоваться в манипуляциях с javassist и представляет целевой объект вызова метода, в этом случае мы точно знаем, что это будет Thread.
- $ continue ($$) Это, наверное, самая сложная инструкция, если вы не знакомы с javassist, так как это все сахара javassist и никакой java вообще. $ continue - это то, как вы должны ссылаться на фактический вызов метода, который вы обрабатываете, и $$ ссылки на полный список аргументов, переданный вызову метода. В этом конкретном случае начало и объединение обоих будет иметь этот список пустым, тем не менее, я думаю, что лучше сохранить эту информацию.
Вы можете прочитать больше о специальных операторов в Javassist учебник, раздел 4.2 Altering a Method Body (поиск MethodCall части, извините нет якоря для этого подраздела)
Наконец, после всего этого кунг-фу мы пишем байткод нашего ctClass в папку класса (так перезаписывает существующий файл GuinePig.class), и когда мы исполняем его ... вуаля, выход теперь, что мы хотели :-)
Только окончательное предупреждение, имейте в виду, что этот инжектор довольно прост и не проверяет, был ли класс уже введен, поэтому вы можете получить несколько инъекций.
Ваш вопрос не совсем ясен .... Вы спрашиваете, как обнаружить, что новый объект Thread создается (или запускается) в коде, который вы используете с javassist, заключается в том, что это или что-то еще? Предлагаю вам отредактировать вопрос более подробно! Также предоставляя некоторый код того, что вы пытаетесь сделать. Пожалуйста, проверьте http://stackoverflow.com/help/how-to-ask, чтобы узнать немного больше о том, как задавать вопросы на SO – pabrantes
Да, это очень похоже на то, что вы поняли .. Я обновил вопрос. Пожалуйста, смотрите. – Curious