머리말
"Dynamic Agent"는 실제로 설계 모드의 프록시 모드에서 유래하며 프록시 모드는 프록시 객체를 사용하여 사용자 요청을 완료하고 사용자의 실제 객체에 대한 액세스를 차단합니다.
가장 간단한 예를 들기 위해, 우리는 외국 웹 사이트에 액세스하기 위해 "FQ"를 원합니다. 우리는 모든 외국 IP가 없기 때문에 요청 데이터 그램을 차단되지 않은 해외 호스트에게 보낼 수 있으며, 외국 호스트를 구성하여 응답 메시지를 수신 한 후 국내 호스트로 다시 전달할 수 있습니다.
이 예에서, 외국 호스트는 프록시 객체이며 벽에 떨어지는 호스트는 실제 물체입니다. 실제 객체에 직접 액세스 할 수는 없지만 프록시를 통해 간접적으로 액세스 할 수 있습니다.
프록시 모드의 장점 중 하나는 모든 외부 요청이 프록시 객체를 통과하고 프록시 객체는 실제 객체에 진정으로 액세스 할 수 있는지 여부를 제어 할 권리가 있다는 것입니다. 불법 요청이있는 경우 프록시 객체는 실제 객체에 실제로 문제없이 완전히 거부 할 수 있습니다.
프록시 모드의 가장 일반적인 응용 프로그램 중 하나는 스프링 프레임 워크입니다. Spring의 AOP는 측면 지향 프로그래밍을 사용하여 관련 로그 예외 및 기타 정보에서 실제 비즈니스 로직을 분리합니다. 비즈니스 로직을 요청할 때마다 프록시 객체에 해당합니다. 필요한 권한 검사 및 로그 인쇄를 수행하는 것 외에도이 프록시 객체는 실제 비즈니스 로직 처리 블록입니다.
정적 프록시
프록시 모델의 "정적 프록시"및 "동적 프록시"의 두 가지 주요 구현자가 있습니다. 이 두 가지의 필수 차이점은 이전 프록시 클래스가 프로그래머에 의한 수동 인코딩이 필요하고 후자의 프록시 클래스는 자동으로 생성된다는 것입니다. 이것이 바로 "정적 프록시"라는 개념에 대해 거의 들어 본 적이없는 이유입니다. 물론 "동적 프록시"를 이해하는 것이 자연스럽게 쉽습니다.
명확 해야하는 한 가지는 프록시 객체가 실제 객체의 모든 메소드, 즉 프록시 객체가 통화의 실제 객체와 적어도 동일한 메소드 이름을 제공해야하므로 프록시 객체는 부모 클래스의 메소드를 포함하여 실제 객체가 소유 한 모든 메소드를 정의해야한다는 것입니다.
간단한 정적 프록시 예를 살펴 보겠습니다.
문제를 설명하기 위해 Iservice 인터페이스를 정의하고 실제 클래스가 인터페이스를 상속하고 구현하여 실제 클래스에 두 가지 메소드가 있도록합니다.
그렇다면 실제 개체의 프록시를 완성하기 위해 프록시 클래스를 어떻게 정의해야합니까?
일반적으로 프록시 클래스의 본질은 실제 클래스의 모든 메소드를 정의하고 방법 내에서 다른 작업을 추가하고 마지막으로 실제 클래스의 메소드를 호출하는 것입니다.
프록시 클래스는 실제 클래스의 모든 메소드, 즉 실제 클래스의 메소드와 정확히 동일한 메소드를 프록시해야하며 실제 클래스의 메소드는 이러한 방법 내부에서 간접적으로 호출됩니다.
따라서 일반적으로 프록시 클래스는 실제 클래스의 모든 상위 메소드 서명, 즉 모든 부모 메소드를 먼저 근접하게하기 위해 실제 클래스의 모든 인터페이스와 부모 클래스를 직접 상속합니다.
다음으로 프록시는 실제 클래스에서 부모 방법이 아닙니다. 여기의 예에서, 복용 방법은 실제 클래스 자체 방법입니다. 프록시 클래스는 또한 동일한 메소드 서명이있는 메소드를 정의해야합니다.
이런 식으로 프록시 클래스가 완료 되더라도 실제 클래스의 모든 메소드는 향후 프록시 클래스를 통해 프록시 될 수 있습니다. 이와 같이:
public static void main (String [] args) {RealClass RealClass = new RealClass (); 프록시 클래스 프록시 클래스 = 새로운 프록시 클래스 (RealClass); proxyclass.sayhello (); proxyclass.doservice ();}프록시 클래스 객체로서 프록시 클래스는 실제 클래스의 모든 메소드를 프록시하고 이러한 메소드가 실행되기 전에 "중요하지 않은"정보를 인쇄 할 수 있습니다.
이것은 기본적으로 프록시 모델의 기본 구현 아이디어이지만 동적 프록시와 이러한 종류의 정적 프록시의 차이점은 동적 프록시가 우리의 메소드 정의를 하나씩 요구하지 않으며 가상 머신은 이러한 메소드를 자동으로 생성한다는 것입니다.
JDK 동적 프록시 메커니즘
동적 프록시를 정적 프록시와 구별하는 것은 Dynamic Proxy의 프록시 클래스가 런타임에 가상 머신에 의해 동적으로 생성되고 가상 머신이 제거 될 때 지우니다.
위의 정적 프록시에 사용 된 클래스를 재사용하여 JDK의 동적 프록시가 특정 클래스의 인스턴스의 모든 방법을 어떻게 근접 할 수 있는지 알아보십시오.
핸들러 처리 클래스 정의 :
기본 기능의 동적 프록시 API는 JDK를 호출하여 프록시 클래스 인스턴스를 생성합니다.
여전히 많은 코드가 관련되어 있습니다. 비트별로 분석합시다. 먼저, RealClass는 인터페이스 Iservice를 프록시 클래스로 구현하고 자체 방법을 내부적으로 정의합니다.
다음으로 인터페이스 호출 핸들러를 상속하는 처리 클래스를 정의하고 독특하게 선언 된 호출 방법을 구현합니다. 또한, 우리는 실제 객체, 즉 프록시 객체를 저장하기 위해 멤버 필드를 선언해야합니다. 왜냐하면 우리가 프록시하는 방법은 기본적으로 실제 객체의 관련 방법을 기반으로하기 때문입니다.
이 호출 방법의 역할과 다양한 공식 매개 변수의 중요성과 관련하여 프록시 소스 코드를 반영 할 때 상세한 분석을 수행합니다.
마지막으로, 처리 클래스를 정의하고 기본적으로 JDK를 기반으로 동적 프록시를 수행하십시오. 핵심 방법은 프록시 클래스의 NewProxyInstance 방법입니다. 이 방법에는 세 가지 매개 변수가 있습니다. 하나는 클래스 로더이고 두 번째는 프록시 클래스에서 구현 한 모든 인터페이스의 모음이고 세 번째는 사용자 정의 프로세서 클래스입니다.
가상 머신은 런타임에 제공하는 클래스 로더를 사용하고 지정된 모든 인터페이스 클래스를 메소드 영역에로드 한 다음 이러한 인터페이스의 메소드를 반영하고 읽고 프로세서 클래스를 결합하여 프록시 유형을 생성합니다.
마지막 문장은 약간 추상적 일 수 있습니다. "프로세서 유형을 생성하기 위해 프로세서 클래스와 결합하는 방법"방법은 무엇입니까? 이와 관련하여 가상 머신 시작 매개 변수를 지정하고 프록시 클래스의 생성 된 클래스 파일을 저장하도록합니다.
-dsun.misc.proxygenerator.savegeneratedfiles = true
우리는 타사 도구를 통해이 클래스 파일을 코 컴파일하며 많은 컨텐츠가 있습니다. 우리는 분석을 분할합니다.
우선,이 프록시 클래스의 이름은 매우 무작위입니다. 프로그램에 여러 개의 프록시 클래스가 생성되는 경우 "$ proxy + 숫자"는 클래스 이름입니다.
다음 으로이 프록시 클래스는 프록시 클래스를 상속 받고 인터페이스가 지정한 인터페이스가 서비스를 상속합니다 (이전에는 여러 인터페이스가 지정된 경우 여러 인터페이스가 여기에서 상속됩니다).
그런 다음이 생성자에게는 호출 핸들러 유형 매개 변수가 필요하며 생성자의 본문은이 invocialhandler 인스턴스를 스토리지에 대한 상위 클래스 프록시의 해당 필드로 전달해야합니다. 이것은 또한 모든 프록시 클래스가 프록시를 부모 클래스로 사용해야하는 이유 중 하나입니다. 우리는 나중에이 작은 디자인이 JDK 기반 다이나믹 프록시의 치명적인 단점으로 이어질 것이라는 것을 알게 될 것입니다.
이 콘텐츠는 또한 프록시 클래스에서 비교적 중요한 부분입니다. 가상 머신이 프록시 클래스를 정적으로 초기화하면 실행됩니다. 이 큰 코드 조각은 인터페이스에서 모든 방법을 반영하는 기능을 완료하고 모든 반사 된 방법은 메소드 유형 필드에 해당하는 저장됩니다.
또한 가상 머신은 객체의 세 가지 공통 방법, 즉 프록시 클래스가 객체에서 상속 된 실제 객체를 프록시합니다.
마지막 부분에서, 우리가 보는 것은 가상 머신이 정적 초기화 코드 블록을 기반으로 프록시해야 할 모든 방법을 반영하고 이에 대한 프록시 메소드를 생성한다는 것입니다.
이러한 방법은 많은 코드처럼 보이지만 실제로는 한 줄의 코드 일뿐입니다.이 코드는 부모 클래스 프록시에서 인스턴스화 중에 저장된 프로세서 클래스를 가져와 호출 메소드를 호출합니다.
방법의 매개 변수는 기본적으로 동일합니다. 첫 번째 매개 변수는 현재 프록시 클래스 인스턴스 (과거 에이 매개 변수를 전달하는 것이 쓸모가 없음을 증명합니다), 두 번째 매개 변수는 메소드 메소드 인스턴스이고 세 번째 매개 변수는 메소드의 공식 매개 변수 세트입니다. 그렇지 않다면, 그것은 null입니다.
이제 맞춤형 프로세서 클래스를 살펴 보겠습니다.
모든 프록시 클래스 메소드는 프로세서 클래스의 호출 메소드를 호출하고 프록시 클래스의 현재 방법을 전달합니다. 이 호출 방법은 메소드를 정상적으로 호출하거나 메소드 호출을 건너 뛰고 메소드가 실제로 호출되기 전후에 추가 작업을 수행하도록 선택할 수 있습니다.
이것이 JDK 동적 프록시의 핵심 아이디어입니다. 전체 통화 프로세스를 간단히 요약하겠습니다.
우선, 프로세서 클래스의 정의는 필수적이며 실제 객체, 즉 프록시 클래스 인스턴스와 관련되어야합니다.
다음으로, 우리는 프록시 클래스의 모든 메소드를 외부에서 호출하고, 소스 소스 코드에서 대신 프록시 클래스 메소드가 프로세서의 호출 메소드를 호출하고 메소드 서명 및 메소드 공식 매개 변수 세트를 전달한다는 것을 알고 있습니다.
마지막으로, 메소드를 일반적으로 호출 할 수 있는지 여부는 프로세서 호출 메소드 본체가 실제로 메소드 메소드를 호출하는지 여부에 따라 다릅니다.
실제로 JDK를 기반으로 구현 된 동적 프록시는 결함이 있으며 이러한 결함은 수정하기 쉽지 않으므로 CGLIB가 인기가 있습니다.
일부 결함과 결함
단일 프록시 메커니즘
위의 예를 사용할 수 없다는 것을 알 수 없습니다. 가상 머신에서 생성 된 프록시 클래스는 프록시 클래스를 상속하여 자체 프로세서 클래스 인스턴스를 공개적으로 저장합니다. 그게 무슨 뜻입니까?
Java의 단일 루트 상속은 프록시 클래스가 더 이상 다른 클래스를 상속받을 수 없다고 말하면, 프록시 클래스의 상위 클래스의 메소드는 자연스럽게 얻을 수 없을 것입니다.
이것 외에도 또 다른 작은 세부 사항이 있습니다. 당신이 그것을 알아 차 렸는지 궁금합니다. 나는 이렇게 썼다.
여기서 SayHello 방법은 인터페이스 ISSERVICE 구현이며 DOSERVICE 방법은 RealClass 자체에 속하는 메소드입니다. 그러나 우리는 프록시 클래스 에서이 방법을 보지 못하며,이 방법은이 방법이 프록시되지 않았 음을 의미합니다.
따라서 JDK의 동적 프록시 메커니즘은 단일이며 프록시 클래스의 인터페이스 컬렉션에서만 프록시 방법 만 할 수 있습니다.
비 우호적 인 반환 값
NewProxyInstance는 프록시 클래스 "$ proxy0"의 인스턴스를 반환하지만 객체 유형으로 반환되며 객체 인스턴스를 "$ proxy0"유형으로 강제로 강제 할 수는 없습니다.
이 객체 인스턴스는 실제로 "$ proxy0"유형이라는 것을 알고 있지만 컴파일 기간 동안 "$ proxy0"유형이 존재하지 않으며 컴파일러는 자연스럽게 존재하지 않는 유형으로 강제로 강제로 강제 할 수 없습니다. 따라서 일반적 으로이 프록시 클래스에서 구현 된 인터페이스 중 하나로 만 강요합니다.
RealClass rc = new RealClass (); MyHanlder Hanlder = New MyHanlder (rc); iservice obj = (iservice) proxy.newproxyInstance (rc.getClass ().
프로그램 실행 출력 :
프록시 시작 ...... 안녕하세요 월드 ... 프록시 엔딩 ......
그런 다음 질문이 다시옵니다. 프록시 클래스가 여러 인터페이스를 구현하는 경우 어떤 인터페이스 유형을 강제해야합니까? 이제 프록시 클래스가 인터페이스 A와 B를 구현한다고 가정하면 마지막 인스턴스가 A로 강제되면 자연스럽게 프록시 클래스에서 구현 된 인터페이스 B의 모든 메소드를 호출 할 수는 없으며 그 반대도 마찬가지입니다.
이것은 직접 결과로 이어집니다. 어떤 메소드가 어떤 인터페이스인지 알아야합니다. 메소드를 호출하기 전에 해당 인터페이스로 강제하면 매우 비우호적입니다.
위의 것은 JDK를 기반으로 한 동적 프록시 메커니즘에서 우아하지 않다고 생각하는 것입니다. 물론, 그 장점은 이러한 단점보다 확실히 더 큽니다. 다음 기사에서는 다양한 프레임 워크에서 널리 사용되는 CGLIB 동적 프록시 라이브러리를 소개합니다. 기본 레이어는 바이트 코드 작동 프레임 워크 ASM을 기반으로하며 더 이상 상속에 의존하지 않으며이를 구현하여 JDK의 단일 프록시의 단점을 완벽하게 해결합니다.
기사의 모든 코드, 이미지 및 파일은 내 github의 클라우드에 저장됩니다.
(https://github.com/singleyam/overview_java)
로컬로 다운로드하도록 선택할 수도 있습니다.
요약
위는이 기사의 전체 내용입니다. 이 기사의 내용에 모든 사람의 연구 나 작업에 대한 특정 참조 가치가 있기를 바랍니다. 궁금한 점이 있으면 의사 소통을 위해 메시지를 남길 수 있습니다. Wulin.com을 지원 해주셔서 감사합니다.