これを説明するのは少し難しいです。私はクラスAを持っています:
_public class A {
private Integer a1;
private Integer a2;
// getters and setters.
}
_
私のクラスAを返す静的クラスBがあります:
_public static class B {
public static A getCurrentA() {
return a;
}
}
_
クラスAのすべての使用法を見つける必要がありますBによって返されます。したがって、クラスCがc.setA(B.getCurrentA())
を呼び出し、さらにc.getA().getA2();
の呼び出しがあるとしましょう。これらすべてを見つけたいと思いますが、.
実際のシナリオでは、B.getCurrentA()
を呼び出す217の異なるクラスがあります。 Eclipseのすべての呼び出しを手動で追跡して、どのメソッドが呼び出されているかを確認することはできません。
Eclipse呼び出し階層ビューには、B.getCurrentA()
へのすべての呼び出しのみが表示されます。
どうすればこれを達成できますか?
[〜#〜]編集[〜#〜]
クリス・ヘイズは私がやりたいことを理解しました。システム全体を壊さずにいくつかの本当に悪いレガシーコードをリファクタリングするには、最初にHibernateのプロジェクションを使用していくつかのクエリを微調整する必要があります(システム内のすべてのマップされたエンティティは熱心にロードされ、多くのエンティティが関連しているため、一部のクエリには時間がかかりますすべてをフェッチする時間)。しかし、最初に、どこかでNullPointerExceptionが発生しないように、使用されているプロパティを見つける必要があります...
これが私が手動でしなければならないことの例です:
見つかった最初のメソッドを開きます。たとえば、次のメソッドであるとします。
_public class CController {
C c = new C();
CFacade facade = new CFacade();
List<C> Cs = new ArrayList<C>();
public void getAllCs() {
c.setA(B.getCurrentA()); // found it!
facade.search(c);
}
}
_
CFacadeクラスで検索メソッドを開きます。
_public class CFacade {
CBusinessObject cBo = new CBusinessObject();
public List<C> search(C c) {
// doing stuff...
cBo.verifyA(c);
cBo.search(c); // yes, the system is that complicated
}
}
_
CBusinessObjectクラスのverifyAメソッドを開き、フィールドa2が使用されていることを確認します。
_public class CBusinessObject {
public void verifyA(c) {
if (Integer.valueOf(1).equals(c.getA().getA2())) {
// do stuff
else {
// something else
}
}
}
_
次の216試合について、手順2〜4を繰り返します...そうです。
助けてください。
ソースコードの変更/リファクタリングを行う場合は、すべての使用法を手動で見つけて、コードの変更を適用する必要があります。
とにかく、私には2つの異なるアプローチがあります
静的検索Eclipseで_Text Search
_を実行するだけで、getA2()
の出現を見つけることができます。 Callerメソッド(ここではCBusinessObject.verifyA())に直接移動しますが、getA2()が発生するたびに、異なるクラスからのものである可能性があります。
実行時検索_Java instrumentation API
_を使用して、必要なメソッドの実行時にバイトコードを変更し、呼び出し元のクラスを見つけて_Java agent
_として実行します-既存のコードベースに触れることなく発信者を識別できるため、非常に便利です特にソースコードにアクセスできない場合。
ここでは、実装方法について説明します
ステップ1-インストルメンテーションを開始するためのエージェントメインクラスを作成する
_public class BasicAgent {
public static void premain(String agentArguments, Instrumentation instrumentation){
System.out.println("Simple Agent");
FindUsageTransformer transformer = new FindUsageTransformer ();
instrumentation.addTransformer(transformer,true);
}
}
_
ステップ2-ClassFileTransformer実装を記述し、メソッドをキャプチャします
_public class FindUsageTransformer implements ClassFileTransformer{
Class clazz = null;
public byte[] transform(ClassLoader loader,String className,Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if(className.equals("A")){
doClass(className, classBeingRedefined, classfileBuffer);
}
return classfileBuffer;
}
private byte[] doClass(String name, Class clazz, byte[] b) {
ClassPool pool = ClassPool.getDefault();
CtClass cl = null;
try {
cl = pool.makeClass(new Java.io.ByteArrayInputStream(b));
CtMethod method = cl.getDeclaredMethod("getA2");
// here you have lot of options to explore
method.insertBefore("System.out.println(Thread.currentThread().getStackTrace()[0].getClassName()+ Thread.currentThread().getStackTrace()[0].getMethodName());");
b = cl.toBytecode();
} catch (Exception e) {
System.err.println("Could not instrument " + name
+ ", exception : " + e.getMessage());
} finally {
if (cl != null) {
cl.detach();
}
}
return b;
}
_
ステップ3-エージェントクラスのjarファイルを作成します(premainクラスでマニフェストファイルを設定し、javaassit jarを追加する必要があります)ビルドファイルのスニペットが表示されます-手動でも実行できます
_<jar destfile="build/jar/BasicAgent.jar" basedir="build/classes">
<manifest>
<attribute name="Manifest-Version" value="1.0"/>
<attribute name="Premain-Class" value="com.sk.agent.basic.BasicAgent"/>
<attribute name="Boot-Class-Path" value="../lib/javassist.jar"/>
</manifest>
</jar>
_
ステップ4-Javaエージェント-その前にVMエージェントをロードするための引数
_ -`javaagent:D:\softwares\AgentProject\AgentLib\build\jar\BasicAgent.jar`
_
前提条件:クラスパスに_javassist.jar
_が必要です。
IDEを使用している場合は、この問題を見つけるのが簡単です。
Eclipse IDEには、最も可能性の高いCall Hierarchyモジュールの1つが存在します。検索して実行するメソッド宣言に、マウスを置くだけですCtrl + Alt + H
これにより、分析するメソッドを使用しているメソッドの階層全体がわかります。
また、Call Hierarchyモジュールは、メソッドが呼び出しているメソッドを見つけることができるモードを提供します。
methodgetCurrentA
へのすべての参照をスキャンするのではなく、ClassA
へのすべての参照をスキャンします。
これにより、そのクラスがプログラム内で使用されているすべての場所が表示され、手作業でそのリストを調べてスキャンし、何か凝ったことをしようとするよりも、見つかった各結果に基づいて行動する必要があるかどうかを判断する方が簡単です。
私はあなたの質問を正しく理解したと思います。 _grep -Irns
_関数を使用して呼び出しを見つけることができると思います。 getA().getA2()
にはgrep
を使用できます。これにより、関数が呼び出された場所から行番号とともに行が返されます。
IntelliJ IDEAで、c.getA().getA2();
の使用法を検索する場合は、_A.a2
_を右クリックして、[使用法の検索]を選択します。同様に、_A.a1
_およびB.getCurrentA()
についても同様です。未使用のフィールドとメソッドは、IDEAでは異なる色で表示されます。 IntelliJはEclipseよりもリファクタリング能力が高いと聞きましたが、Eclipseは同じことをしますが、わずかに異なります。
また、grep、find、およびsedを使用して、Aと同じパッケージにあるファイル、またはAをインポートするファイルで適切なメソッドを検索したり、名前で綴ったりすることができます。
Call Usageを見つける最も簡単な方法は、Eclipseで参照を使用することですが、面白い方法があります。
IntelliJはあなたの問題を解決できると思います。 「データフローの分析」機能があり、探していることを実行していると思います。
これが私のサンプルコードです:
public class Main {
private static A a = new A(); //nevermind the way it is initialized
public static A getA(){
return a;
}
public void method(){
A myA = getA();
Integer a1 = myA.getA1(); //this line is found
Integer a2 = myA.getA2(); //this line is found
}
public void anotherMethod(){
A myA = new A();
Integer a1 = myA.getA1(); //this line is NOT found
Integer a2 = myA.getA2(); //this line is NOT found
}
}
「ここからデータフローを分析する」(カーソルをreturn a;
行に置く)を実行すると、次のようになります。
IntelliJのソリューションのみを提供して申し訳ありません(IntelliJ-13 Ultimate Editionでテスト済み)