(인터페이스가 아닌) 추상 클래스의 프록시를 생성하기위한 java.lang.reflect.Proxy의 대안


89

문서 에 따르면 :

[ java.lang.reflect.] Proxy는 동적 프록시 클래스 및 인스턴스를 생성하기위한 정적 메서드를 제공하며 이러한 메서드에 의해 생성 된 모든 동적 프록시 클래스의 수퍼 클래스이기도합니다.

(동적 프록시 생성을 담당 하는) newProxyMethod메서드 에는 다음과 같은 서명이 있습니다.

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                             throws IllegalArgumentException

불행히도 이것은 특정 인터페이스를 구현하는 대신 특정 추상 클래스 를 확장 하는 동적 프록시를 생성하는 것을 방지 합니다. 이것은 "모든 동적 프록시의 수퍼 클래스"를 고려 하여 다른 클래스가 수퍼 클래스가되는 것을 방지 하므로 의미 가 있습니다.java.lang.reflect.Proxy

따라서 특정 추상 클래스에서 상속java.lang.reflect.Proxy 하는 동적 프록시를 생성 하여 추상 메서드에 대한 모든 호출을 호출 핸들러로 리디렉션 할 수 있는 대안이 있습니까?

예를 들어 추상 클래스가 있다고 가정합니다 Dog.

public abstract class Dog {

    public void bark() {
        System.out.println("Woof!");
    }

    public abstract void fetch();

}

다음을 수행 할 수있는 수업이 있습니까?

Dog dog = SomeOtherProxy.newProxyInstance(classLoader, Dog.class, h);

dog.fetch(); // Will be handled by the invocation handler
dog.bark();  // Will NOT be handled by the invocation handler

답변:


123

Javassist (참조 ProxyFactory) 또는 CGLIB를 사용하여 수행 할 수 있습니다 .

Javassist를 사용한 Adam의 예 :

나는 (Adam Paynter) Javassist를 사용하여이 코드를 작성했습니다.

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Dog.class);
factory.setFilter(
    new MethodFilter() {
        @Override
        public boolean isHandled(Method method) {
            return Modifier.isAbstract(method.getModifiers());
        }
    }
);

MethodHandler handler = new MethodHandler() {
    @Override
    public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
        System.out.println("Handling " + thisMethod + " via the method handler");
        return null;
    }
};

Dog dog = (Dog) factory.create(new Class<?>[0], new Object[0], handler);
dog.bark();
dog.fetch();

다음 출력을 생성합니다.

씨!
메소드 핸들러를 통해 public abstract void mock.Dog.fetch () 처리

10
+1 : 정확히 내가 필요한 것! 샘플 코드로 답변을 수정하겠습니다.
Adam Paynter

proxyFactory.setHandler()더 이상 사용되지 않습니다. 을 사용하십시오 proxy.setHandler.
AlikElzin-kilaka

@axtavt는 개체 "개"가 위 코드의 구현 또는 인터페이스입니까?
stackoverflow

-7

이러한 경우에 할 수있는 일은 추상 클래스의 기존 메서드로 호출을 리디렉션하는 프록시 처리기를 갖는 것입니다.

물론 코드를 작성해야하지만 매우 간단합니다. 프록시를 만들려면 그에게 InvocationHandler. 그런 다음 invoke(..)호출 핸들러 의 메서드 에서 메서드 유형 만 확인하면 됩니다. 그러나주의하십시오. 추상 클래스의 선언 된 유형이 아닌 핸들러에 연결된 기본 객체에 대해 메서드 유형을 확인해야합니다.

개 클래스를 예로 들어 보면 호출 핸들러의 invoke 메소드 다음과 같을 수 있습니다 (.. well ...이라는 기존의 연관된 dog 하위 클래스 포함 dog).

public void invoke(Object proxy, Method method, Object[] args) {
    if(!Modifier.isAbstract(method.getModifiers())) {
        method.invoke(dog, args); // with the correct exception handling
    } else {
        // what can we do with abstract methods ?
    }
}

하지만 궁금해하는 점이 dog있습니다. 객체에 대해 이야기했습니다 . 그러나 Dog 클래스는 추상이므로 인스턴스를 만들 수 없으므로 기존 하위 클래스가 있습니다. 또한 Proxy 소스 코드를 철저히 조사한 결과 인터페이스를 나타내지 않는 Class 객체에 대해 Proxy를 생성 할 수 없음을 (Proxy.java:362에서) 발견 할 수 있습니다.

그래서 현실 과는 별도로 하고 싶은 일이 완벽하게 가능합니다.


1
당신의 대답을 이해하려고 노력하는 동안 저를 참아주세요 ... 제 경우에는 (런타임에 생성 된) 프록시 클래스 가 하위 클래스가 되기를 원합니다 Dog(예를 들어, Poodle구현 하는 클래스를 명시 적으로 작성하지 않습니다 fetch()). 따라서 dog메서드를 호출 할 변수 가 없습니다. 헷갈 리면 죄송합니다. 좀 더 생각해 보겠습니다.
Adam Paynter

1
@Adam-일부 바이트 코드 조작 없이는 런타임에 하위 클래스를 동적으로 만들 수 없습니다 (CGLib은 이와 같은 작업을 수행한다고 생각합니다). 짧은 대답은 동적 프록시가 인터페이스를 지원하지만 추상 클래스는 지원하지 않는다는 것입니다. 두 가지가 매우 다른 개념이기 때문입니다. 정상적인 방식으로 추상 클래스를 동적으로 프록시하는 방법을 생각하는 것은 거의 불가능합니다.
Andrzej Doyle

1
@Andrzej : 내가 요청하는 것은 바이트 코드 조작이 필요하다는 것을 이해합니다 (사실 ASM을 사용하여 내 문제에 대한 해결책을 이미 작성했습니다). 또한 Java의 동적 프록시는 인터페이스 만 지원한다는 것을 이해합니다. 아마도 내 질문이 완전히 명확하지 않았을 것입니다 . 필요한 것을 수행하는 다른 클래스 (즉, 다른 클래스) 가 있는지 묻고 java.lang.reflect.Proxy있습니다.
Adam Paynter

2
글쎄, 긴 것을 짧게 만들기 위해 ... 아니오 (적어도 표준 Java 클래스에서는). 바이트 코드 조작을 사용하면 하늘이 한계입니다!
Riduidel 2010

9
질문에 대한 답이 아니기 때문에 반대표를 던졌습니다. OP는 인터페이스가 아닌 클래스를 프록시하고 싶다고 말했으며 java.lang.reflect.Proxy로는 불가능하다는 것을 알고 있습니다. 당신은 그 사실을 반복하고 다른 해결책을 제공하지 않습니다.
jcsahnwaldt 모니카 복원
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.