JDK 동적 프록시와 CGLib의 차이점은 무엇입니까?


147

의 경우 프록시 디자인 패턴 의 차이 무엇입니까 JDK의 다이내믹 프록시 및 타사 동적 코드 생성 API의 같은 CGLIB는 ?

두 가지 접근 방식을 사용하는 것과 언제 서로를 선호해야 하는가?


3
여기에서 코드를 얻으십시오 : < gist.github.com/ksauzz/1563486 >. cglib에서 클래스 프록시와 인터페이스 프록시를 모두 만들 수 있습니다. Spring은 기본적으로 CGlib를 사용하고 AspectJ는 Java 프록시를 사용합니다. 뿐만 아니라이 읽기 : jnb.ociweb.com/jnb/jnbNov2005.html를 )
Subhadeep 레이에게

답변:


185

JDK Dynamic 프록시는 인터페이스별로 만 프록시 할 수 있습니다 (따라서 대상 클래스는 인터페이스를 구현해야하며이 인터페이스는 프록시 클래스에 의해 구현됩니다).

CGLIB (및 javassist)는 서브 클래 싱으로 프록시를 작성할 수 있습니다. 이 시나리오에서 프록시는 대상 클래스의 서브 클래스가됩니다. 인터페이스가 필요 없습니다.

따라서 Java Dynamic 프록시는 프록시 할 수 있습니다. public class Foo implements iFoo여기서 CGLIB는 프록시 할 수 있습니다.public class Foo

편집하다:

javassist와 CGLIB는 서브 클래 싱에 의해 프록시를 사용하기 때문에 이것이 최종 프레임 워크를 사용할 때 최종 메소드를 선언하거나 클래스를 최종적으로 만들 수없는 이유입니다. 이렇게하면 라이브러리가 클래스를 서브 클래스 화하고 메소드를 재정의하는 것을 막을 수 있습니다.


감사..!! 그러나 어떤 경우에는 다른 사람의 사용법을 설명하는 예제 코드 (또는 링크)를 줄 수 있다면 도움이 될 것입니다 .. !!!
KDjava

1
JDK 프록시는 실제로 Foo의 종류가 아닌 IFoo의 프록시를 중단합니다. 다소 중요한 차이점입니다. 또한 cglib 프록시는 완전한 서브 클래스입니다. 필터를 사용하여 관심있는 프록시 메소드 만 생성하고 생성 된 클래스를 직접 사용하십시오.
lscoughlin

9
또한 CGLib 서브 클래스를 생성하려면 올바른 인수로 올바른 생성자를 호출 할 수있는 수퍼 클래스에 대해 충분히 알고 있어야합니다. 생성자를 신경 쓰지 않는 인터페이스 기반 프록시와 달리. 따라서 CGLib 프록시 작업은 JDK 프록시보다 "자동"이 적습니다. 또 다른 차이점은 "스택"비용입니다. JDK 프록시는 항상 호출 당 추가 스택 프레임을 발생시키는 반면 CGLib는 추가 스택 프레임을 소비하지 않을 수 있습니다. 스택이 클수록 메모리 스레드가 더 많이 사용되므로 앱이 복잡해지면 점점 더 관련성이 높아집니다.
Ray

1
CGLIB하지 프록시 마지막 방법 할 수 있지만, 예외가 발생하지 않습니다 gist.github.com/mhewedy/7345403cfa52e6f47563f8a204ec0e80
무하마드 Hewedy

예, CGLIB는 단순히 최종 방법을 무시합니다.
yashjain12yj

56

기능상의 차이점

  • JDK 프록시를 사용하면 서브 클래 싱하는 동안 인터페이스 세트를 구현할 수 Object있습니다. 모든 인터페이스 방법은, 플러스 Object::hashCode, Object::equals그리고 Object::toString다음으로 전달됩니다 InvocationHandler. 또한 표준 라이브러리 인터페이스 java.lang.reflect.Proxy가 구현됩니다.

  • cglib를 사용하면 최종 클래스가 아닌 클래스를 서브 클래 싱하면서 인터페이스 세트를 구현할 수 있습니다. 또한, 방법은 선택적으로 재정의 될 수 있으며, 즉 모든 비 추상적 방법이 차단 될 필요는 없다. 또한 메소드를 구현하는 다른 방법이 있습니다. 또한 InvocationHandler다른 패키지 의 클래스를 제공 하지만 a와 같이 고급 인터셉터를 사용하여 수퍼 메소드를 호출 할 수도 있습니다 MethodInterceptor. 또한 cglib는와 같은 특수한 차단으로 성능을 향상시킬 수 있습니다 FixedValue. 한 번 cglib에 대한 다른 인터셉터 요약을 작성 했습니다 .

성능 차이

JDK 프록시는 하나의 가로 채기 디스패처 인으로 순진하게 구현됩니다 InvocationHandler. 이를 위해서는 항상 인라인 할 수없는 구현으로 가상 메소드 디스패치가 필요합니다. Cglib는 때때로 성능을 향상시킬 수있는 특수 바이트 코드를 생성 할 수 있습니다. 다음은 18 개의 스텁 메소드로 인터페이스를 구현하기위한 몇 가지 비교입니다.

            cglib                   JDK proxy
creation    804.000     (1.899)     973.650     (1.624)
invocation    0.002     (0.000)       0.005     (0.000)

시간은 표준 편차 (중괄호)와 함께 나노초로 표시됩니다. Byte Buddy의 자습서 에서 Byte Buddy가 cglib의보다 현대적인 대안 인 벤치 마크에 대한 자세한 내용을 확인할 수 있습니다 . 또한 cglib는 더 이상 개발 중이 아닙니다.


2
스프링 문서가 cglib보다 JDK 프록시를 선호하는 이유는 무엇입니까? docs.spring.io/spring/docs/2.5.x/reference/…
P4ndaman

2
Cglib는 외부 의존성이며 현재 지원되지 않습니다. 타사 소프트웨어에 의존하는 것은 항상 도박이므로 가능한 한 적은 수의 사람들이 소프트웨어에 의존 할 때 최고입니다.
Rafael Winterhalter

블로그에서 "그러나 InvocationHandler # invoke 메소드와 함께 제공되는 프록시 객체에서 메소드를 호출 할 때는주의해야합니다.이 메소드에 대한 모든 호출은 동일한 InvocationHandler와 함께 전달되므로 무한 루프가 발생할 수 있습니다. " 무슨 소리 야?
Koray Tugay

프록시 객체에서 메소드를 호출하면 모든 호출이 호출 핸들러를 통해 라우팅됩니다. 호출 핸들러 호출이 오브젝트에 대한 호출을 위임하면 언급 된 재귀가 발생합니다.
Rafael Winterhalter

안녕하세요 Rafael, 귀하의 답변과 관련이없는 메시지 입니다. 5 년 전의 수정 사항에 대해 핑을 보냅니다 . 로 CGLIB는 분명히 아직 2019에서 커밋을 가지고 있으며, 어떤 표시되지 않습니다 체포 개발 의 추가 정보에를, 나는 한 명세서 제거 태그 발췌에서합니다. 언급과 관련된 것이 있으면 태그 설명 / 발췌를 자유롭게 개선하십시오.
Cœur

28

동적 프록시 : JDK Reflection API를 사용하여 런타임시 인터페이스의 동적 구현 .

예 : Spring은 다음과 같이 트랜잭션에 동적 프록시를 사용합니다.

여기에 이미지 설명을 입력하십시오

생성 된 프록시는 Bean의 맨 위에옵니다. Bean에 다국적 동작을 추가합니다. 여기서 프록시는 JDK Reflection API를 사용하여 런타임에 동적으로 생성됩니다.

응용 프로그램이 중지되면 프록시가 삭제되고 파일 시스템에 인터페이스와 Bean 만 있습니다.


위의 예에서 인터페이스가 있습니다. 그러나 대부분의 인터페이스 구현은 최선이 아닙니다. 따라서 bean은 인터페이스를 구현하지 않습니다.이 경우 상속을 사용합니다.

여기에 이미지 설명을 입력하십시오

이러한 프록시를 생성하기 위해 Spring은 CGLib 이라는 타사 라이브러리를 사용합니다 .

CGLIB ( C ODE G eneration 리브 rary)는 상단에 구축 ASM 이 주로 프록시 연장 빈을 생성하고 프록시 방법 콩 동작을 추가 사용된다.

JDK 동적 프록시 및 CGLib의 예

봄 심판


5

Spring 문서에서 :

Spring AOP는 JDK 동적 프록시 또는 CGLIB를 사용하여 지정된 대상 오브젝트에 대한 프록시를 작성합니다. (선택할 때마다 JDK 동적 프록시가 선호됩니다).

프록시 할 대상 객체가 하나 이상의 인터페이스를 구현하는 경우 JDK 동적 프록시가 사용됩니다. 대상 유형으로 구현 된 모든 인터페이스가 프록시됩니다. 대상 객체가 인터페이스를 구현하지 않으면 CGLIB 프록시가 작성됩니다.

CGLIB 프록 싱을 강제로 사용하려면 (예를 들어, 인터페이스로 구현 된 메소드뿐만 아니라 대상 오브젝트에 대해 정의 된 모든 메소드를 프록 싱하기 위해) 그렇게 할 수 있습니다. 그러나 고려해야 할 몇 가지 문제가 있습니다.

재정의 될 수 없으므로 최종 방법은 권장되지 않습니다.

클래스 경로에 CGLIB 2 바이너리가 필요하지만 JDK와 함께 동적 프록시를 사용할 수 있습니다. Spring은 CGLIB가 필요하고 CGLIB 라이브러리 클래스가 클래스 경로에 없을 때 자동으로 경고합니다.

프록시 된 객체의 생성자가 두 번 호출됩니다. 이는 각 프록시 오브젝트마다 서브 클래스가 생성되는 CGLIB 프록시 모델의 자연스러운 결과입니다. 프록시 된 각 인스턴스에 대해 실제 프록시 된 오브젝트와 권고를 구현하는 서브 클래스의 두 개의 오브젝트가 작성됩니다. JDK 프록시를 사용할 때는이 동작이 나타나지 않습니다. 일반적으로 할당 유형 만 생성되고 생성자에 실제 논리가 구현되지 않으므로 프록시 유형의 생성자를 두 번 호출하는 것은 문제가되지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.