자바 정적 에이전트, JDK의 다이내믹 프록시와 동적 프록시 CGLIB

하나는 구현 위임 클래스를 숨길 수, 프록시는 두 가지 장점을 가지고 사용, 두 번째는 몇 가지 추가적인 처리를 할 수있는 대리자 클래스의 코드를 수정하지 않고, 클라이언트와 대리자 클래스 사이에 디커플링 달성하는 것입니다.

매우 일반적인 예를 제공합니다. , 공장 장난감을 많이 생산하지만, 우리 공장을 구입하지 구입하는 상점에 대한 모든 구매 장난감, 우리는 상관하지 않는 공장 생산 방법, 우리는 가게 그들이 원하는 장난감을 살 수 있다는 것을 알고, 우리가 그것을 멀리 줄 필요가 있다면,이 장난감은 또한 포장 선물 상자를 사용하여 저장합니다. 여기,이 공장은, 에이전트 클래스 상점 클래스이다 의뢰, 우리는 고객의 유형, 몇 가지 추가 처리 선물 상자 대리자 클래스가 수행 할 수있는 프록시 클래스입니다.

많은 시나리오가 같은 원격 RPC 호출뿐만 아니라, 구현 스프링 AOP 측면으로, 우리는 또한 자바에 절단 등의 프록시 클래스를 생성하기 위해 프록시 클래스를 통해 때와 같은 프록시 클래스를 사용할 필요가 있습니다.

연기 수업은 정적 에이전트, JDK의 다이내믹 프록시와 CGLIB의 동적 프록시로 나누어집니다, 그들은 자신의 장점과 단점이 아닌보다도, 서로 다른 목적을 가지고 서로 다른 시나리오에서 의미가있다.

자바 정적 프록시

대전 방지제는 먼저 인터페이스와 인터페이스 구현 클래스를 정의하고 인터페이스 프록시 객체를 정의하고 프록시 객체에 대한 인터페이스의 분사 인스턴스 다음 프록시 객체를 통해 실제 구현 클래스를 호출하는, 구현 과정이 매우 간단하고 이해하기 쉽다. 기관 관계의 컴파일하는 동안 정적 에이전트가 덜 적합한 프록시 클래스를 확인하고 사건을 결정하고있다, 당신은 같은 포장 상자로, 대리자 클래스를 수정하지 않고 달성하기 위해 몇 가지 추가 처리를 할 수있는 고객의 클래스와 대리자를 달성하기 위해 감 또는 그러한 것. 그러나 단점은 다음 몇 대리자 메서드 상황에만 적용, 위임 클래스 메소드 수십만이있는 경우, 상상 우리가 프록시 클래스의 프록시 방법의 무리를 작성려고하고있다, 그래서이 문제를 해결하기 위해 동적 프록시가 문제는, 동적 기관은 나중에 말했다.

인터페이스 구현 클래스와 인터페이스를 정의

인터페이스 위임 //
공용 인터페이스 SayHelloService {
    의 sayHello (문자열 사용자 이름)을 무효;
}

 

//委托实现类
공용 클래스 SayHelloServiceImpl 구현 SayHelloService {
    공공 무효의 sayHello (문자열의 userName) {
        에서 System.out.println ( "안녕하세요,"+는 userName);
    }
}

 

연기 클래스 인터페이스

 

//代理类
공용 클래스 SayHelloProxy 구현 SayHelloService {
    개인 SayHelloService sayHelloService = 새로운 SayHelloServiceImpl ();

    무효의 sayHello 공개 (문자열의 userName) {
        // 뭔가 사전에 프록시 할
        에서 System.out.println을 ( "프록시 전에 무언가를 ... ");

        // 메소드 위임 클래스를 호출
        sayHelloService.sayHello (사용자 이름);

        // 이후 프록시 뭔가를 할
        에서 System.out.println이 ( "... 프록시 후 뭔가를 할");
    }
}

 

프록시 위임 클래스를 통해 개체 액세스

 

//测试静态代理类
Public 클래스 SayHelloTest {
    공공 정적 무효 메인 (문자열 []에 args) {
        SayHelloProxy sayHelloProxy = 새로운 SayHelloProxy ();
        sayHelloProxy.sayHello ( "yanggb");
    }
}

 

자바의 동적 프록시 기술

프로그램이 실행되는 방식에서 만든 에이전트 프록시 클래스, 그것은 동적 프록시라고합니다. 동적 에이전트를 이해에서, 클래스 로딩 메커니즘의 첫 리뷰 세 가지 JVM 로딩 단계는 수행해야합니다

1 클래스의 전체 이름 또는 다른 방법의 클래스 이진 바이트 스트림을 얻었다.

2 바이트 스트림은 실행시 데이터 구조 영역 방법으로 정적 저장 구조를 나타낸다.

메모리 3. 액세스 이러한 유형의 입구 영역하는 방법으로서,이 클래스의 클래스 오브젝트의 대표를 생성한다.

동적 에이전트가 주로 많은 클래스와 같은 ZIP 패키지, 네트워크 계산을 할 수 있습니다이 단계에서, 첫 번째 단계에서 바이너리 바이트 스트림의 소스를 일어났다 및 런타임을 생성, 기타 (JSP)를 생성 파일 등을 획득 한 데이터베이스. 자바 프록시 클래스 인터페이스 클래스 프록시 에이전트 * $ 이진 바이트 스트림의 특정 형태를 생성 ProxyGenerator.generateProxyClass를 사용하고, 상기 동적 인 제로 운영 기술을 생성하도록 계산된다. 소위 동적 프록시는 인터페이스 또는 관객의 JVM에로드 된 프록시 클래스의 바이트 코드를 계산하는 방법을 찾는 것입니다. 실제 상황의 계산은 매우이 같은 JDK의 다이내믹 프록시 구현 완료에 CGLIB 타사 라이브러리와 같은 일부의 수단으로 일반적으로, 복잡 할 것입니다. JDK 다이나믹 프록시 인터페이스 및 CGLIB 동적 프록시 클래스 상속 기준 : 따라서 (즉, 대표 클래스), 일반적으로 두 가지 방법으로 일관성이있는 대상체에 의해 생성 된 프록시 클래스를 확인한다.

마찬가지로 모두 동적 프록시는, 런타임 객체 속성, 방법 봐 달성 메소드를 호출하여 범위, 메소드 명을 수정 기반 반영 말했듯. 온라인 응용 프로그램은 자주 그렇게 말 옆에 추가 공간이 상대적으로 적은 오버 헤드의 ASM 프레임 워크와 Javassist를 사용하는 것 외에도, 반영 큰 성능 오버 헤드로 반영 사용하지 않습니다.

JDK의 다이내믹 프록시

자바의 JDK 동적 프록시에서 주로 두 개의 클래스 중 하나를 java.lang.reflect.Proxy 관련, 하나는 java.lang.reflectInvocationHandler이다. 구현의 InvocationHandler 중산층 인터페이스를 JDK의 다이내믹 프록시 필요성을 사용하려면,이 인터페이스는 호출 할 수 있도록, () 메소드를 호출하기 위해 호출 될 것이다 단 하나의 방법 () 메서드를 호출, 처리 클래스의 모든 메소드에 대한 호출을 가지고 ( ) 통합 처리 로직을 추가하는 방법 (도)하는 매개 방법에 따른 방법에있어서. 중간 클래스 (클래스가 구현의 InvocationHandler)는 델리게이트 객체 참조를 갖고, () 메소드의 클래스 객체 대리자 호출에서 적절한 메소드를 호출 외부의 호출 등의 중합 방법에 의해 유지 된 델리게이트 클래스 객체 참조 () 마지막 메소드 호출을 위임 객체에 대한 호출로 변환됩니다.

사실, 중층 및 델리게이트 클래스는 관계 미들 클래스는 프록시 클래스가 클래스 위임 대리인 클래스이다 정적 에이전트 관계를 구성한다. 그런 다음 프록시 클래스 미들 클래스는 이러한 관계에서, 중층 대리자 클래스 프록시 클래스 프록시 클래스이다 정적 에이전트 관계를 형성한다. 즉, 동적 기관 관계는 JDK 다이나믹 프록시 원칙 개의 고정 제 조성물의 관계이다.

설명 매개 변수의 InvocationHandler의 인터페이스 (의견) 소스를 정의

 

의 InvocationHandler의 인터페이스 {공개
    / **
    * 프로세스를 호출
    * @param 프록시 에이전트 클래스 객체
    * 특정 메소드 프록시 클래스 호출 식별 파라미터 : methon
    파라미터 파라미터 : 인수 프록시 클래스 방법
    * /
    공용 객체 호출 (개체 프록시 방법을 방법, 개체 [] 인수)이 발생하는 Throwable를;
}

 

인터페이스 구현 클래스와 인터페이스를 정의

 

// Delegate 클래스 인터페이스
의 HelloService {인터페이스 공공
    의 sayHello (문자열 사용자 이름)를 무효;

    무효 sayByeBye (문자열의 userName);
}

 

 

//委托类
공용 클래스 HelloServiceImpl 구현의 HelloService {
    공공 무효의 sayHello (문자열의 userName) {
        에서 System.out.println ( "안녕하세요,"+는 userName);
    }

    공공 무효 sayByeBye (문자열의 userName) {
        에서 System.out.println ( "바이 바이"+는 userName);
    }
}

 

중간 인터페이스의 클래스를 정의의 InvocationHandler

 

// 중산층
공용 클래스 HelloInvocationHandler 구현의 InvocationHandler {
    / **
    * 중간 클래스에서 정적 프록시 관계를 구성하는 것이다 클래스 객체에 대한 참조 보유 의뢰
    * /
    개인 개체 (OBJ)를;

    / **
    *이 인수 생성자 객체에 전달 위임 클래스
    *
    * @param OBJ 위임 클래스 객체
    * /
    공공 HelloInvocationHandler (개체 OBJ) {
        this.obj = (OBJ);
    }

    / **
    * 프록시 클래스를 동적으로 생성 된 객체의 Proxy.newProxyInstance
    *
    * @return 프록시 클래스 인스턴스 반환
    * /
    공공 객체 'newProxyInstance () {
        Proxy.newProxyInstance를 반환 (
                // 지정된 프록시 객체 클래스 로더
                obj.getClass (). getClassLoader를은 (),
                // 프록시 객체는 동시에 여러 인터페이스를 지정할 수있는 인터페이스를 구현해야
                obj.getClass ()의 getInterfaces () ,.
                실제 핸들러 // 메서드 호출이 프록시 객체 메소드 호출이 여기까지 전달되는
                )이;
    }

    / **
    * @param 프록시 프록시 객체
    * @param 방법 프록시 메소드
    파라미터 메소드 파라미터 : 인수
    * /
    @Override
    공용 객체 호출 (개체 프록시, 방법에있어서, 객체는 [] 인수) Throwable의 {발생
        // 전에있는 invoke 수행을 뭔가
        에서 System.out.println이 ( "... 인보 전에 뭔가를 할");
        // 인보 실행
        객체 Method.invoke에게 결과 = (OBJ, 인수);
        // 인보 후 뭔가 할
        (에서 System.out.println을 " 호출 후 무언가를 ... ");

        결과를 반환;
    }
}

 

중산층으로 대리자 클래스에 액세스 할 수

 

//测试动态代理类
Public 클래스 HelloTest {
    공공 정적 무효 메인 (문자열 []에 args) {
        HelloInvocationHandler helloInvocationHandler = 새로운 HelloInvocationHandler (새 HelloServiceImpl ());
        의 HelloService의 HelloService = (의 HelloService) helloInvocationHandler.newProxyInstance ();
        helloService.sayHello ( "yanggb");
        helloService.sayByeBye ( "yanggb");
    }
}

 

상기 시험 동적 프록시 클래스에서, 우리는 프록시 클래스 예제를 얻기 위해 프록시 클래스의 newProxyInstance () 메서드를 호출합니다. 프록시 클래스가 구현하는 인터페이스 우리가 지정하고, 지정된 메소드 호출 호출 프로세서에 배포됩니다. 'newProxyInstance ()을 통해 실시 프록시 클래스를 얻는 첫 번째 방법은, 다음 프록시 클래스의 방법 프록시 클래스 프록시 클래스 호출 전화 실시 예에있어서 () 메소드를 호출하여 (구현의 InvocationHandler 인터페이스 클래스) 중간 클래스를 호출 할 수 의해 호출 () 메소드에서 우리는 대응 방법 위임 클래스를 호출 한 다음 자신의 처리 로직을 추가 할 수 있습니다.

JDK 동적 프록시 가장 큰 특징은 동적 프록시 클래스와 대리인이 동일한 인터페이스를 구현 생성됩니다. JDK 다이나믹 프록시 실제로 또한 자신의 로직 중 일부를 추가 할 수있는 호출의 내부 런타임 동적 알려진 객체는 메소드 호출 인 반사 (Proxy.newProxyInstance), 시간을 통해 달성된다.

CGLIB의 동적 프록시

JDK의 다이내믹 프록시 의존 인터페이스, 우리는 다른 동적 에이전트 기술을 사용할 필요가 인터페이스 만 같이하지 않는 경우 : CGLIB의 동적 프록시를. 먼저 CGLIB의 동적 프록시는 타사 달성하기위한 프레임 워크, Maven 프로젝트에 CGLIB 패키지를 도입 할 필요가있다 :

<의존성>
    <의 groupId> CGLIB </의 groupId>
    <artifactId를> CGLIB </ artifactId를>
    <버전> 2.2 </ 버전>
</ 의존성>

CGLIB 프록시는 원칙은 비즈니스 에이전트를 달성하기위한 방법의 수수료 및 재정의 하나에 지정된 클래스의 서브 클래스를 생성하는 클래스를 달성하기위한 프록시입니다. 에이전트 클래스 객체는 증강 클래스에 의해 생성된다. CGLIB 생성 모드 동적 프록시 클래스입니다 :

대상 클래스의 모든 비 최종 공개 방법을 찾기 1. (마지막 방법은 다시 쓸 수 없습니다).

바이트 코드에 이러한 방법 2. 정의.

해당 클래스 개체 프록시에 바이트 코드를 변환 3. 객체의 프록시 클래스 인스턴스는 반사에 의해 얻어진다.

4.의 MethodInterceptor 인터페이스, 프록시 클래스에 대한 요청을 처리하는 모든 메소드를 구현합니다.

간단한 클래스를 정의

 

// Delegate 클래스, 클래스 단순히
공공 클래스 HelloClass는 {
    공공 무효의 sayHello (문자열 사용자 이름) {
        에서 System.out.println ( "안녕하세요,"+ 사용자 이름);
    }

    공공 무효 sayByeBye (문자열의 userName) {
        에서 System.out.println ( "바이 바이"+는 userName);
    }
}

 

구현 클래스 인터셉터 클래스의 MethodInterceptor를 정의

 

// HelloInterceptor 방법 및 콜백 전화 인터셉트
공용 클래스 HelloInterceptor을의 MethodInterceptor {유단
    / **
    * CGLIB 향상된 클래스 객체가 객체 클래스 프록시 클래스 인핸서에 의해 생성이
    * 향상제이다 CGLIB 바이트 코드 인핸서 쉽게 할 수 클래스 확장
    * /
    ) (개인 증강 증강 증강 새로운 새 =을;

    / **
    *
    * @param 프록시 객체 (OBJ)
    * @param 방법 프록시 프로세스
    파라미터 파라미터 : 인수 방법
    프록시 객체 * @param 프록시 CGLIB 방법은
    * 객체를 생성하는 @return CGLIB 방법 개체 MethodProxy을 이용하는 대신에 사용될 직접적인 실행 방법보다 메소드 호출 JDK 효율 개선
    * /
    공용 객체 차단 (객체 (OBJ), 방법에있어서, 오브젝트 [] args를 MethodProxy 프록시) Throwable의 {슬로우
        에서 System.out.println ( "있어서 전에 호출");
        개체 proxy.invokeSuper = O (OBJ, 인수)
        에서 System.out.println ( "메소드 호출 이후");
        리턴 O;
    }

    / **
    * 동적 프록시 프록시 객체를 생성
    * @param C 클래스 명
    * /
    공용 객체 newProxyInstance (클래스 C <?>) {
        // 생성 된 프록시 객체의 상위 클래스를 설정 보강의 종류
        enhancer.setSuperclass (c);
        // 현재 오브젝트 인터페이스의 MethodInterceptor 달성하는데 필요한 활성제로서 논리 개체 정의
        enhancer.setCallback를 (이 본)
        // 매개 변수없이 기본 생성자 인수없이 프록시 클래스 생성자를 제공하는 필수 구성 요소가 가진 대상체를 만들 메소드
        리턴 enhancer.create ();
    }
}

 

차단 액세스 클래스 위임 클래스

 

//测试类
Public 클래스 HelloTest {
    공공 정적 무효 메인 (문자열 []에 args) {
        HelloInterceptor helloInterceptor = 새로운 HelloInterceptor ();
        HelloClass sayHelloClass = (HelloClass) helloInterceptor.newProxyInstance (HelloClass.class);
        sayHelloClass.sayHello ( "yanggb");
        sayHelloClass.sayByeBye ( "yanggb");
    }
}

 

프록시 클래스에 대한 필요성은, 동적 비 최종 다루 방법을 생성하는 단순한 서브 클래스 후크 콜백 바인딩 맞춤 인터셉터있다. 더 빠른 성능의 JDK 동적 프록시에서보다. 우리가 부모 프록시 클래스와 목표 클래스에 전달할 것을 주목할 필요가있다. 의 JDK의 동적 프록시는 달리, 우리는 대상 개체를 사용하여 프록시를 만들 수 없습니다. CGLIB 대상 개체는 생성 할 수 있습니다. 예에서, 인수없이 기본 생성자는 대상 개체를 만드는 데 사용됩니다.

개요

쉽게 1. 정적 프록시는 이해하는 프록시 클래스 및 프록시 클래스는 다음의 동일한 인터페이스를 구현하는 프록시 클래스의 실제 구현 클래스를 호출 할 수 및 컴파일하는 동안 정적 에이전트 관계가 확인되었다. 동적 프록시 관계는 동작 중에 결정된다. 정적 프록시 작은 프록시 클래스에 적합한 간단하고 사건을 결정하고, 동적 프록시는 더 큰 유연성을 우리에게 제공한다.

프록시 클래스 객체를 호출하는 프로그램에 이용되는 프록시 클래스 실제로 JVM에 의해 생성 2.JDK 다이나믹 프록시가, JVM이 객체 클래스 및 메소드의 이름을 달성하기 위해 기업에 따라 전달 동적 프록시 클래스의 클래스 파일과 단어를 만들 실행 엔진 바이트, 다음 방법에 의해 에이전트 클래스 오브젝트를 호출한다. 우리는 호출 작업이 될 수 있습니다 후, 프록시 클래스를 미리 지정 할 필요가있다.

3. 정적 및 동적 프록시 에이전트는 인터페이스를 기반으로, 간단하게 구현 클래스의 측면에서 제공하는 인터페이스를 제공하지 않는 사람들을 위해, 당신은 단지 CGLIB 동적 프록시를 선택할 수 있습니다.

1 내접되어 차분 JDK 다이나믹 프록시와 프록시 동적 CGLIB

1.JDK의 동적 프록시를 달성하기 위해 자바 반영하고 있으며, 우리는이 방법을 사용하여 프록시 객체를 생성하기 위해 비즈니스 클래스에 대한 인터페이스를 구현해야합니다.

트래픽 클래스를 서브 클래스에 의해 구현 ASM 2.CGLIB의 동적 프록시 기반 프레임 워크.

동적 프록시의 3.JDK의 장점은 의존성을 최소화 단순화 개발 및 유지 보수에 의미 의존도를 줄이기 위해, 그리고 자신의 JDK의 지원을하고 있습니다. 또한 JDK 버전 업그레이드 코드는 간단하다 원활하게 수행 할 수 있습니다.

우리가 걱정하는 것처럼 CGLIB 프레임 워크의 장점을 바탕으로 4. 비 침습적 프록시 클래스를 달성하기 위해 인터페이스를 구현 할 필요 없다, 우리는 단순히 다른 클래스의 작업 부하를 증가하지 않는, 운영, 성능은 상대적으로 높다.

2 개 얼굴 질문 : 프록시의 여러 구현을 설명하고, 장점과 단점을 말

행동은 정적 및 동적 프록시 에이전트들로 분할 될 수 있고, 제제는 동적 JDK 다이나믹 프록시 CGLIB 다이나믹 프록시로 분할된다.

정적 프록시 : 프록시 객체와 실제 객체가 프록시 개체의 동일한 인터페이스, 포인트를 상속는 외부 노출이 실제 개체라는 프록시 객체가 정말 그래서, 실제 개체의 인스턴스입니다. 장점은 잘하여 안전성을 향상, 외부 노출의 비즈니스 로직의 실제 개체를 보호 할 수 있습니다. 단점은 서로 다른 인터페이스는 다른 프록시 클래스 구현을 가지고, 그것은 중복 될 것입니다.

동적 에이전트 JDK : JDK 동적 프록시의 InvocationHandler 만 호출 () 메소드는 에이전트의 구현을 완료 할 수있을 것입니다 재 작성, 인터페이스를 구현해야, 반사 형 Proxyxx.class의 사용은 프록시 프록시 클래스의 바이트 코드를 생성하고 객체를 생성합니다. JDK의 동적 프록시 에이전트 인터페이스 유일한 이유는 왜 프록시 클래스 자체는 프록시 클래스를 상속하지만, 자바는 다중 상속을 허용하지 않기 때문에,하지만 여러 인터페이스를 할 수 있습니다. 장점은 정적 프록시 중복 기관 구현 클래스 문제를 해결하는 것입니다. 단점에는 인터페이스가 존재하지 않는 경우는 예외를 발생한다, JDK 동적 프록시 기반 인터페이스 설계가 구현된다는 것이다.

CGLIB의 동적 프록시 다음은 인터페이스 디자인을 기반으로 동적 프록시 제한을 JDK하지만, 경우 인터페이스 아니므로, JDK의 방법으로 해결할 수 없습니다. 바이트 코드 기술의 맨 아래에 CGLIB는 원리는 클래스의 바이트 코드 기술의 서브 클래스를 만드는 것입니다, 그리고 요격에 메서드 호출을 차단 기술은 모든 서브 클래스에서 상위 클래스의 방법은 크로스 직물 논리 흐름 동적 프록시를 완료합니다. DETAILED 구현 콜백 클래스 증강 방법에 의해 구현되는 인터페이스의 MethodInterceptor 차단 및 대체 () 메소드를 달성하는 것이다. 단 하나의 객체가 자주 만드는 데 사용되지 않기 때문에 그러나 프록시 객체를 생성 CGLIB 시간은 CGLIB가 더 적절한 오브젝트 그래서 예를 들어, 훨씬 더 JDK 이상 소요됩니다. 그렇지 않으면 JDK의 동적 프록시는 더 적절한 방법을 사용하여. CGLIB 동적 최종 접근 사용 서브 클래스를 생성하는 방법도 있기 때문에, 프록시 가능하지 않다. 장점은 인터페이스 에이전트는 동적 일 수 있음을, 그리고 바이트 코드 향상 기술 및 성능을 사용한다. 단점은 기술이 상대적으로 더 어려워 이해하기 때문이다.

추천

출처www.linuxidc.com/Linux/2019-11/161505.htm