자바 에이전트 디자인 모델(Proxy)의 네 가지 구체 적 인 실현:정적 에이전트 와 동적 에이전트

더 읽 기
면접 문제:자바 의 프 록 시 디자인 모델(Proxy Design Pattern)은 모두 몇 가지 실현 방식 이 있 습 니까?이 문 제 는 공 을 기 씨 가'희 향 두 희 자 는 몇 가지 표기 법 이 있 나 요?'
 
프 록 시 모드 란 클 라 이언 트(Client)가 실제 대상(아래 그림 오른쪽 아래 RealSubject)을 직접 호출 하지 않 고 프 록 시(Proxy)를 호출 하여 실제 대상 을 간접 적 으로 호출 하 는 것 을 말한다.
대리 모델 의 사용 장 소 는 일반적으로 클 라 이언 트 가 실제 대상 을 직접 방문 하고 싶 지 않 거나 실제 대상 을 방문 하 는 데 기술적 인 장애 가 존재 하기 때문에 대리 대상 을 교량 으로 하여 간접 방문 을 완성 한다.
 
구현 방식 1:정적 에이전트
인터페이스 IDeveloper 를 개발 합 니 다.이 인 터 페 이 스 는 writeCode,코드 를 작성 하 는 방법 을 포함 합 니 다.
public interface IDeveloper {

     public void writeCode();

}

이 인 터 페 이 스 를 실현 하기 위해 Developer 클래스 를 만 듭 니 다.
public class Developer implements IDeveloper{
    private String name;
    public Developer(String name){
        this.name = name;
    }
    @Override
    public void writeCode() {
        System.out.println("Developer " + name + " writes code");
    }
}

테스트 코드:Jerry 라 는 개발 자 인 스 턴 스 를 만 들 고 코드 를 쓰 세 요!
public class DeveloperTest {
    public static void main(String[] args) {
        IDeveloper jerry = new Developer("Jerry");
        jerry.writeCode();
    }
}

지금 문제 가 생 겼 다.Jerry 의 프로젝트 매니저 는 Jerry 가 어떤 문서 도 지 키 지 않 고 코드 만 쓰 는 것 에 불만 을 가지 고 있 습 니 다.어느 날 제 리 가 휴가 를 갔다 고 가정 하면 다른 프로그래머 들 이 제 리 의 일 을 대신 해 낯 선 코드 에 물음 표를 던 졌 다.팀 전체의 토론 을 통 해 모든 개발 자가 코드 를 쓸 때 문 서 를 동시에 업데이트 해 야 한다 고 결정 했다.
모든 프로그래머 에 게 개발 할 때 문 서 를 기록 하도록 강요 하고 코드 를 쓰 는 동작 자체 에 영향 을 주지 않 기 위해 우 리 는 원래 의 Developer 류 를 수정 하지 않 고 새로운 종 류 를 만 들 었 습 니 다.마찬가지 로 IDeveloper 인 터 페 이 스 를 실현 합 니 다.이 새로운 종류의 Developer Proxy 내부 에서 원본 IDeveloper 인 스 턴 스 를 가리 키 는 구성원 변 수 를 유지 합 니 다.
public class DeveloperProxy implements IDeveloper{
    private IDeveloper developer;
    public DeveloperProxy(IDeveloper developer){
        this.developer = developer;
    }
    @Override
    public void writeCode() {
        System.out.println("Write documentation...");
        this.developer.writeCode();
    }
}

이 프 록 시 클래스 가 실 현 된 writeCode 방법 에 실제 프로그래머 writeCode 방법 을 호출 하기 전에 문 서 를 쓰 는 호출 을 추가 하면 프로그래머 가 코드 를 쓸 때 문서 업데이트 가 수반 되 는 것 을 확보 할 수 있다.
테스트 코드:
 
정적 에이전트 방식 의 장점
1.이해 하기 쉽 고 실현 하기 쉽다
2.대리 류 와 실제 류 의 관 계 는 컴 파일 기간 에 정태 적 으로 결정 되 고 다음 에 소개 할 동적 대리 와 비교 하면 실행 할 때 추가 비용 이 없습니다.
정적 에이전트 의 단점
모든 실제 클래스 는 새로운 프 록 시 클래스 를 만들어 야 합 니 다.아니면 상기 문서 업 데 이 트 를 예 로 들 어 사장 이 테스트 엔지니어 에 게 새로운 요 구 를 제기 했다 고 가정 하고 테스트 엔지니어 가 bug 를 측정 할 때마다 해당 하 는 테스트 문 서 를 신속하게 업데이트 해 야 한다.그러면 정적 에이전트 방식 으로 테스트 엔지니어 의 실현 클래스 인 ITester 도 대응 하 는 ITester Proxy 클래스 를 만들어 야 합 니 다.
public interface ITester {
    public void doTesting();
}
Original tester implementation class:
public class Tester implements ITester {
    private String name;
    public Tester(String name){
        this.name = name;
    }
    @Override
    public void doTesting() {
        System.out.println("Tester " + name + " is testing code");
    }
}
public class TesterProxy implements ITester{
    private ITester tester;
    public TesterProxy(ITester tester){
        this.tester = tester;
    }
    @Override
    public void doTesting() {
        System.out.println("Tester is preparing test documentation...");
        tester.doTesting();
    }
}

정적 코드 방식 이라는 단점 때문에 자바 의 동적 에이전트 실현 방식 이 탄생 했다.
Java 동적 에이전트 구현 방식 1:InvocationHandler
Invocation Handler 의 원 리 는 제 가 전문 적 으로 글 을 써 서 소개 한 적 이 있 습 니 다.자바 동적 대리 의 Invocation Handler 의 가장 간단 한 입문 과정 입 니 다.
Invocation Handler 를 통 해 저 는 Enginner Proxy 대리 류 로 Developer 와 Tester 의 행 위 를 동시에 대리 할 수 있 습 니 다.
public class EnginnerProxy implements InvocationHandler {
    Object obj;
    public Object bind(Object obj)
    {
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
        .getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable
    {
        System.out.println("Enginner writes document");
        Object res = method.invoke(obj, args);
        return res;
    }
}

실제 클래스 의 writeCode 와 doTesting 방법 은 동적 에이전트 에서 반사 적 인 방식 으로 실 행 됩 니 다.
테스트 출력:
 
Invocation Handler 를 통 해 동적 에이전트 의 한계점 을 실현 합 니 다.
제품 매니저 류(ProductOwner)가 인 터 페 이 스 를 실현 하지 못 했다 고 가정 합 니 다.
public class ProductOwner {
    private String name;
    public ProductOwner(String name){
        this.name = name;
    }
    public void defineBackLog(){
        System.out.println("PO: " + name + " defines Backlog.");
    }
}

우 리 는 여전히 Enginner Proxy 프 록 시 클래스 를 사용 하여 그것 을 대리 합 니 다.컴 파일 할 때 오류 가 발생 하지 않 습 니 다.운행 할 때 무슨 일이 발생 합 니까?
ProductOwner po = new ProductOwner("Ross");

ProductOwner poProxy = (ProductOwner) new EnginnerProxy().bind(po);

poProxy.defineBackLog();

운행 시보 가 틀리다.따라서 한계 성 은 대 리 된 클래스 가 어떠한 인터페이스 도 실현 되 지 않 으 면 Invocation Handler 동적 대 리 를 통 해 행 위 를 대리 할 수 없다 는 것 이다.
 
자바 동적 에이전트 구현 방식 2:CGLIB
CGLIB 는 자바 바이트 코드 생 성 라 이브 러 리 로 자바 바이트 코드 를 만 들 고 수정 하기 쉬 운 API 를 제공 합 니 다.이 소스 라 이브 러 리 에 대한 자세 한 내용 은 CGLIB 가 github 에 있 는 창고 로 이동 하 십시오.https://github.com/cglib/cglib
저 희 는 지금 CGLIB 로 대리 하기 전에 InvocationHandler 가 성공 적 으로 대리 하지 못 한 ProductOwner 류 를 사용 하려 고 합 니 다.
현재 나 는 CGLIB API 를 사용 하여 프 록 시 클래스 를 만 드 는 것 으로 바 꾸 었 다.
public class EnginnerCGLibProxy {
    Object obj;
    public Object bind(final Object target)
    {
        this.obj = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy proxy) throws Throwable
            {
                System.out.println("Enginner 2 writes document");
                Object res = method.invoke(target, args);
                return res;
            }
        }
        );
        return enhancer.create();
    }
}

테스트 코드:
ProductOwner ross = new ProductOwner("Ross");

ProductOwner rossProxy = (ProductOwner) new EnginnerCGLibProxy().bind(ross);

rossProxy.defineBackLog();

비록 ProductOwner 는 어떠한 코드 도 실현 하지 못 했 지만,그것 도 성공 적 으로 대리 되 었 다.
 
CGLIB 로 자바 동적 에이전트 의 한계 실현
CGLIB 가 프 록 시 클래스 를 만 드 는 원 리 를 알 게 된다 면 그 한계 도 한눈 에 알 수 있다.우 리 는 지금 ProductOwner 류 에 final 장식 부 를 더 해서 계승 할 수 없 도록 실험 을 하고 있 습 니 다.
 
테스트 코드 를 다시 실행 하 였 습 니 다.이번 에는 잘못 보 고 했 습 니 다:Cannot subclass final class XXXX.
그래서 CGLIB 를 통 해 성공 적 으로 만 든 동적 프 록 시 는 실제 프 록 시 클래스 의 하위 클래스 입 니 다.프 록 시 클래스 가 final 로 표시 되면 CGLIB 를 통 해 동적 프 록 시 를 만 들 수 없습니다.
자바 동적 에이전트 구현 방식 3:컴 파일 기간 을 통 해 제공 하 는 API 동적 생 성 에이전트 클래스
만약 에 우리 가 final 이자 인 터 페 이 스 를 실현 하지 못 한 ProductOwner 류 에 동적 코드 를 만들어 야 한다 고 가정 합 니 다.Invocation Handler 와 CGLIB 를 제외 하고 우 리 는 마지막 방법 이 있다.
프 록 시 클래스 의 소스 코드 를 문자열 로 직접 맞 춘 다음 에 이 문자열 을 기반 으로 JDK 의 Compiler(컴 파일 기간)API 를 호출 하여 새로운 자바 파일 을 동적 으로 만 든 다음 에 이 자바 파일 을 동적 으로 컴 파일 하면 새로운 프 록 시 클래스 를 얻 을 수 있 습 니 다.
 
테스트 성공:
 
나 는 코드 류 의 소스 코드 를 맞 추고 프 록 시 류 의 자바 파일 을 동적 으로 만 들 었 습 니 다.Eclipse 에서 코드 로 만 든 자바 파일 을 열 수 있 습 니 다.
 
 
다음 그림 은 ProductPwnerSCProxy.java 파일 을 동적 으로 만 드 는 방법 입 니 다.
 
다음 그림 은 자바 Compiler API 로 이전 동적 으로 만 든 자바 파일 을 동적 으로 컴 파일 하여.class 파일 을 만 드 는 방법 입 니 다.
 
다음 그림 은 컴 파일 된.class 파일 을 클래스 로 불 러 오 는 방법 입 니 다.
 
이 글 에서 소개 한 네 가지 프 록 시 모드(Proxy Design Pattern)를 사용 해 보 시 려 면 제 github 창 고 를 참고 하 십시오.모든 코드 가 위 에 있 습 니 다.읽 어 주 셔 서 감사합니다.
https://github.com/i042416/JavaTwoPlusTwoEquals5/tree/master/src/proxy
 
더 많은 Jerry 의 오리지널 기술 글 을 얻 으 려 면 대중 번호 인'왕 자 희'를 주목 하거나 다음 QR 코드 를 스 캔 하 십시오.
 
 

좋은 웹페이지 즐겨찾기