javassist 사용 안내

11039 단어 javassist쓰다
자바 바이트 코드 는.class 파일 에 바 이 너 리 형식 으로 저장 되 어 있 으 며,모든.class 파일 은 자바 류 나 인 터 페 이 스 를 포함 하고 있 습 니 다.자바 assist 는 자바 바이트 코드 를 처리 하 는 라 이브 러 리 입 니 다.그것 은 이미 컴 파일 된 클래스 에 새로운 방법 을 추가 하거나 기 존의 방법 을 수정 할 수 있 으 며 바이트 코드 에 대해 깊이 이해 할 필요 가 없다.또한 새로운 클래스 대상 을 만 들 고 완전히 수 동 으로 만 들 수 있 습 니 다.
1.Javassist 를 사용 하여 class 파일 만 들 기
우선 jar 가방 을 도입 해 야 합 니 다:

<dependency>
 <groupId>org.javassist</groupId>
 <artifactId>javassist</artifactId>
 <version>3.25.0-GA</version>
</dependency>
생 성 대상 의 클래스 작성:

package com.rickiyang.learn.javassist;

import javassist.*;

/**
 * @author rickiyang
 * @date 2019-08-06
 * @Desc
 */
public class CreatePerson {

 /**
 *     Person   
 *
 * @throws Exception
 */
 public static void createPseson() throws Exception {
 ClassPool pool = ClassPool.getDefault();

 // 1.       
 CtClass cc = pool.makeClass("com.rickiyang.learn.javassist.Person");

 // 2.        private String name;
 //     name
 CtField param = new CtField(pool.get("java.lang.String"), "name", cc);
 //       private
 param.setModifiers(Modifier.PRIVATE);
 //      "xiaoming"
 cc.addField(param, CtField.Initializer.constant("xiaoming"));

 // 3.    getter、setter   
 cc.addMethod(CtNewMethod.setter("setName", param));
 cc.addMethod(CtNewMethod.getter("getName", param));

 // 4.          
 CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
 cons.setBody("{name = \"xiaohong\";}");
 cc.addConstructor(cons);

 // 5.          
 cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, cc);
 // $0=this / $1,$2,$3...       
 cons.setBody("{$0.name = $1;}");
 cc.addConstructor(cons);

 // 6.       printName  ,   ,    ,  name 
 CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, cc);
 ctMethod.setModifiers(Modifier.PUBLIC);
 ctMethod.setBody("{System.out.println(name);}");
 cc.addMethod(ctMethod);

 //               .class  
 cc.writeFile("/Users/yangyue/workspace/springboot-learn/java-agent/src/main/java/");
 }

 public static void main(String[] args) {
 try {
  createPseson();
 } catch (Exception e) {
  e.printStackTrace();
 }
 }
}
위의 main 함 수 를 실행 하면 지정 한 디 렉 터 리 에 Person.class 파일 을 생 성 합 니 다.

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.rickiyang.learn.javassist;

public class Person {
 private String name = "xiaoming";

 public void setName(String var1) {
 this.name = var1;
 }

 public String getName() {
 return this.name;
 }

 public Person() {
 this.name = "xiaohong";
 }

 public Person(String var1) {
 this.name = var1;
 }

 public void printName() {
 System.out.println(this.name);
 }
}
우리 가 예 상 했 던 것 과 같다.
자바 ssist 에서 클래스 Javaassit.CtClass 는 class 파일 을 표시 합 니 다.GtClass(컴 파일 시 클래스)대상 은 class 파일 을 처리 할 수 있 습 니 다.ClassPool대상 의 용기 입 니 다.이것 은 읽 기 클래스 파일 에 따라CtClass 대상 을 구성 하고 나중에 사용 할 수 있 도록CtClass 대상 을 저장 합 니 다.
주의해 야 할 것 은 ClassPool 은 메모리 에서 생 성 된 모든 CtClass 를 유지 합 니 다.CtClass 의 수량 이 너무 많 을 때 대량의 메모 리 를 차지 합 니 다.API 에서 제 시 된 해결 방안 은 의식 적 으로 호출CtClassCtClass방법 으로 메모 리 를 방출 합 니 다.
ClassPool 이 주목 해 야 할 방법:
  • getDefault:기본 ClassPool 로 돌아 가 는 것 은 단일 모드 입 니 다.보통 이 방법 을 통 해 우리 의 ClassPool 을 만 듭 니 다.
  • appendClassPath,insertClassPath:클래스 검색 경로 의 끝 위치 에 ClassPath 를 추가 하거나 시작 위치 에 삽입 합 니 다.보통 이 방법 을 통 해 여러 종류의 로 더 환경 에서 클래스 를 찾 을 수 없 는 어색 함 을 해결 하기 위해 추가 검색 경 로 를 기록 합 니 다.
  • toClass:수 정 된 CtClass 를 현재 스 레 드 의 컨 텍스트 클래스 로 불 러 옵 니 다.CtClass 의 toClass 방법 은 이 방법 을 호출 하여 이 루어 집 니 다.주의해 야 할 것 은 이 방법 을 호출 하면 불 러 온 class 를 계속 수정 할 수 없습니다.
  • get,getCtClass:클래스 경로 이름 에 따라 이 클래스 의 CtClass 대상 을 가 져 와 후속 편집 에 사용 합 니 다.
  • CtClass 가 주목 해 야 할 방법:
  • freeze:한 종 류 를 동결 하여 수정 할 수 없 도록 합 니 다.
  • isFrozen:한 종류 가 동결 되 었 는 지 판단 합 니 다.
  • prune:불필요 한 속성 을 삭제 하여 메모리 사용량 을 줄 입 니 다.이 방법 을 호출 한 후,많은 방법 들 이 정상적으로 사용 할 수 없 으 니,신중하게 사용 하 세 요.
  • defrost:하나의 종 류 를 해동 하여 수정 할 수 있 도록 합 니 다.만약 에 하나의 클래스 가 defrost 에 의 해 호출 될 것 이라는 것 을 미리 알 았 다 면 prune 방법 을 사용 하 는 것 을 금지 합 니 다.
  • detach:이 class 를 ClassPool 에서 삭제 합 니 다.
  • writeFile:CtClass 에 따라.class 파일 생 성;
  • toClass:클래스 로 더 를 통 해 이 CtClass 를 불 러 옵 니 다.
  • 위 에서 우 리 는 CtMethod 클래스 를 사용 하 는 새로운 방법 을 만 들 었 습 니 다.CtMthod 대표 클래스 의 한 방법 은 CtClass 에서 제공 하 는 API 를 통 해 가 져 오 거나 CtNewMethod 를 통 해 새로 만 들 수 있 으 며,CtMethod 대상 을 통 해 방법 을 수정 할 수 있 습 니 다.
    CtMethod 의 중요 한 방법:
  • insert Before:방법의 시작 위치 에 코드 를 삽입 합 니 다.
  • insterAfter:방법의 모든 return 문장 앞 에 코드 를 삽입 하여 문장 이 실 행 될 수 있 도록 합 니 다.exception 을 만 나 지 않 는 한.
  • insert At:지정 한 위치 에 코드 를 삽입 합 니 다.
  • setBody:방법의 내용 을 기록 할 코드 로 설정 합 니 다.방법 이 abstract 에 의 해 수식 되 었 을 때 이 수식 자 는 제거 되 었 습 니 다.
  • make:새로운 방법 을 만 듭 니 다.
  • 위 코드 에 있 는 것 을 알 수 있 습 니 다:setBody()때 우 리 는 기 호 를 사 용 했 습 니 다.
    //$0=this/$1,$2,$3...대표 적 인 방법 매개 변수
    cons.setBody("{$0.name = $1;}");
    구체 적 으로 많은 기 호 를 사용 할 수 있 지만 서로 다른 기 호 는 서로 다른 장면 에서 서로 다른 의 미 를 가지 기 때문에 여기 서 군말 하지 않 고 자바 ssist 의 설명 문 서 를 볼 수 있 습 니 다.http://www.javassist.org/tutorial/tutorial2.html
    2.생 성 된 클래스 대상 호출
    (1).반사 적 으로 호출
    위의 사례 는 클래스 대상 을 만 들 고 이 대상 이 컴 파일 된 후의 class 파일 을 출력 하 는 것 입 니 다.그럼 우리 가 생 성 된 클래스 의 속성 이나 방법 을 호출 하려 면 어떻게 해 야 합 니까?javassist 도 해당 하 는 api 를 제공 합 니 다.클래스 대상 을 만 드 는 코드 는 첫 번 째 세그먼트 와 마찬가지 로 마지막 으로 파일 에 기록 한 코드 를 다음 과 같이 바 꿉 니 다.
    
    //        ,     
    Object person = cc.toClass().newInstance();
    //    
    Method setName = person.getClass().getMethod("setName", String.class);
    setName.invoke(person, "cunhua");
    //    
    Method execute = person.getClass().getMethod("printName");
    execute.invoke(person);
    그리고 main 방법 을 실행 하면 printName 방법 을 호출 하 는 것 을 볼 수 있 습 니 다.
    (2).class 파일 을 읽 는 방식 으로 호출 합 니 다.
    
    ClassPool pool = ClassPool.getDefault();
    //      
    pool.appendClassPath("/Users/yangyue/workspace/springboot-learn/java-agent/src/main/java/");
    CtClass ctClass = pool.get("com.rickiyang.learn.javassist.Person");
    Object person = ctClass.toClass().newInstance();
    // ......                
    (3).인 터 페 이 스 를 통과 하 는 방식
    위의 두 가 지 는 모두 반사 적 인 방식 으로 호출 된 것 이다.문 제 는 우리 의 공사 에 이런 대상 이 없 기 때문에 반사 하 는 방식 이 비교적 번 거 롭 고 비용 도 많이 든다.그러면 만약 에 당신 의 대상 이 일부 방법 으로 집합 할 수 있다 면 이런 유형 에 인터페이스 류 를 만 드 는 것 을 고려 할 수 있 습 니 다.이렇게 하면 new Instance()에서 우 리 는 인터페이스 로 강하 게 전환 할 수 있 고 반 사 된 세트 를 생략 할 수 있다.
    위 에 있 는 Person 클래스 를 가지 고 PersonI 인터페이스 클래스 를 새로 만 듭 니 다.
    
    package com.rickiyang.learn.javassist;
    
    /**
     * @author rickiyang
     * @date 2019-08-07
     * @Desc
     */
    public interface PersonI {
    
     void setName(String name);
    
     String getName();
    
     void printName();
    
    }
    실현 부분의 코드 는 다음 과 같다.
    
    ClassPool pool = ClassPool.getDefault();
    pool.appendClassPath("/Users/yangyue/workspace/springboot-learn/java-agent/src/main/java/");
    
    //     
    CtClass codeClassI = pool.get("com.rickiyang.learn.javassist.PersonI");
    //         
    CtClass ctClass = pool.get("com.rickiyang.learn.javassist.Person");
    //        ,   PersonI   
    ctClass.setInterfaces(new CtClass[]{codeClassI});
    
    //              
    PersonI person = (PersonI)ctClass.toClass().newInstance();
    System.out.println(person.getName());
    person.setName("xiaolv");
    person.printName();
    사용 하기 편 해 요.
    3.기 존의 클래스 대상 수정
    앞에서 말 했 듯 이 클래스 대상 을 추가 합 니 다.이 사용 장면 은 아직 만난 적 이 없 기 때문에 일반적으로 만 날 수 있 는 사용 장면 은 기 존의 유형 을 수정 해 야 한다.예 를 들 어 흔히 볼 수 있 는 로그 절단면,권한 절단면.우 리 는 javassist 를 이용 하여 이 기능 을 실현 한다.
    다음 과 같은 대상 이 있 습 니 다.
    
    package com.rickiyang.learn.javassist;
    
    /**
     * @author rickiyang
     * @date 2019-08-07
     * @Desc
     */
    public class PersonService {
    
     public void getPerson(){
     System.out.println("get Person");
     }
    
     public void personFly(){
     System.out.println("oh my god,I can fly");
     }
    }
    
    그리고 그 를 수정 했다.
    
    package com.rickiyang.learn.javassist;
    
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtMethod;
    import javassist.Modifier;
    
    import java.lang.reflect.Method;
    
    /**
     * @author rickiyang
     * @date 2019-08-07
     * @Desc
     */
    public class UpdatePerson {
    
     public static void update() throws Exception {
     ClassPool pool = ClassPool.getDefault();
     CtClass cc = pool.get("com.rickiyang.learn.javassist.PersonService");
    
     CtMethod personFly = cc.getDeclaredMethod("personFly");
     personFly.insertBefore("System.out.println(\"         \");");
     personFly.insertAfter("System.out.println(\"    。。。。\");");
    
    
     //      
     CtMethod ctMethod = new CtMethod(CtClass.voidType, "joinFriend", new CtClass[]{}, cc);
     ctMethod.setModifiers(Modifier.PUBLIC);
     ctMethod.setBody("{System.out.println(\"i want to be your friend\");}");
     cc.addMethod(ctMethod);
    
     Object person = cc.toClass().newInstance();
     //    personFly   
     Method personFlyMethod = person.getClass().getMethod("personFly");
     personFlyMethod.invoke(person);
     //   joinFriend   
     Method execute = person.getClass().getMethod("joinFriend");
     execute.invoke(person);
     }
    
     public static void main(String[] args) {
     try {
      update();
     } catch (Exception e) {
      e.printStackTrace();
     }
     }
    }
    
    detach()방법 전후 에 인쇄 로 그 를 추가 하 였 습 니 다.그리고 하나의 방법personFly을 추가 했다.main 함 수 를 실행 하면 이미 추 가 된 것 을 발견 할 수 있 습 니 다.
    또한 주의해 야 할 것 은 위의joinFriendinsertBefore() 의 문장 입 니 다.만약 당신 이 단행문 이 라면 쌍 따옴표 를 직접 사용 할 수 있 지만,여러 줄 의 문장 이 있 는 경우,여러 줄 의 문장 을{}로 묶 어야 합 니 다.javassist 는 하나의 문장 만 받 아들 이거 나 큰 괄호 로 묶 은 문장 블록 만 받 아들 입 니 다.
    이상 은 javassist 사용 안내서 의 상세 한 내용 입 니 다.javassist 사용 에 관 한 자 료 는 저희 의 다른 관련 글 을 주목 해 주 십시오!

    좋은 웹페이지 즐겨찾기