2012-08-02 5 views
4

동적 프록시를 만들고 싶습니다. 동적 프록시는 메소드를 다른 구현에 위임 할 수 있습니다 (각 메소드 호출은 잠재적으로 다른 객체를 선택합니다). 그리고 나는 프록시 된 메소드가 다른 프록시 된 메소드를 호출 할 때 객체 선택 메커니즘이 다시 적용되는 것처럼 다형성 효과를 얻고 싶습니다.다형성 위임을 사용하여 동적 프록시를 만드는 방법은 무엇입니까?

interface IService { 
    void a(); 
    void b(); 
} 

class HappyService implements IService { 
    public void a() { 
    System.out.println("Happy a"); 
    b(); 
    } 

    public void b() { 
    System.out.println("Happy b"); 
    } 
} 

class SadService implements IService { 
    public void a() { 
    System.out.println("Sad a"); 
    b(); 
    } 

    public void b() { 
    System.out.println("Sad b"); 
    } 
} 

지금, 난 항상 방법 b()의 호출을위한 방법 a()SadService의 호출에 HappyService를 선택 IService에 대한 프록시를 만들려면 :

좋아, 충분히 혼란, 여기에 예입니다. 여기에 처음에는 내 마음에 오는 것입니다 :

InvocationHandler h = new InvocationHandler() { 
    @Override 
    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { 
    Object impl; 
    if (method.getName().equals("a")) { 
     impl = new HappyService(); 
    } else if (method.getName().equals("b")) { 
     impl = new SadService(); 
    } else { 
     throw new IllegalArgumentException("Unsupported method: " + method.getName()); 
    } 
    return method.invoke(impl, args); 
    } 
}; 
IService service = (IService)Proxy.newProxyInstance(IService.class.getClassLoader(), new Class[]{ IService.class }, h); 
service.a(); 

이 인쇄 :

Happy a 
Happy b 

그래, a()의 내부 b()의 호출은 동적 프록시에 대해 아무것도 모르기 때문에 그건.

그래서 목표를 달성하는 가장 좋은 방법은 무엇입니까? 내 원하는 출력은 다음과 같습니다 아마 a()HappyService 만 방법을 전달하고 다른 모든 방법 원래 프록시 리디렉션 또 다른 프록시로 호출 핸들러 안에 내 new HappyService()을 대체 할 수

Happy a 
Sad b 

. 그러나 더 나은/더 쉬운 해결책이있을 수 있습니까?

+0

어, 나는 지난 단락에서 아이디어를 시도했지만 지금은 그것을 할 수 없다는 것을 알았습니다. 그래서 나는 완전히 붙어있다. – vadipp

+1

동일한 문제가 발생하여 [javassist] (http://www.javassist.org)를 사용하여 java.lang.reflect.Proxy를 사용하지 않고 프록시를 작성했습니다. 예를 들어 https://github.com/hertzsprung/autumn/blob/master/src/main/java/uk/co/datumedge/autumn/Singletoniser.java를보십시오. – hertzsprung

답변

3

프록시는 호출자가 "실제"구현 대신 프록시에 대한 참조를 가지고있을 때만 객체 간 호출을 가로 채기 위해 사용할 수 있습니다. 여기에 a()의 구현은 b()을 직접 호출하므로 물론 this이라고 부릅니다. 당신이 원하는 것은 프록시에 의해 달성 될 수 없습니다.

그러나 AspectJcompile-time weaving (aspectj-maven-plugin과 함께 사용 가능) 또는 load-time weaving을 사용하여 AOP를 사용하여 수행 할 수 있습니다. 대략 구현의 a()b() 메서드의 calling sites에 pointcuts가있는 aspect을 만들고 실행을 조언합니다. 원래 호출을 다른 것으로 대체하는 것이 좋습니다. 테스트되지 않은 코드는하지만 다음과 같습니다

public aspect IServiceAspect { 
    private IService happy = new HappyService(); // Assuming these are stateless services 
    private IService sad = new SadService();  // that can be shared. 

    // The pointcut is inside an IService implementation, and is a call to the a() method 
    pointcut aCall(): within(IService+) && call(* IService.a()); 

    pointcut bCall(): within(IService+) && call(* IService.b()); 

    around(): aCall() && !within(HappyService) { // To avoid infinite recursion 
     return happy.a(); 
    } 

    around(): bCall() && !within(SadService) { 
     return sad.b(); 
    } 
} 

당신은 다음 IService로 직접 HappyService 또는 SadService 구현을 제공 할 수있는, 모두가 수정되었습니다. 예를 들어, IService의 모든 메소드와 일치하는 단일 pointcut을 작성하고, 조언에서 thisJoinPoint을 사용하여 메소드 이름에 따라 동적으로 라우팅을 수행 할 수도 있습니다.

양상은 annotations을 사용하여 선언 할 수도 있습니다.

+0

답장을 보내 주셔서 감사합니다, 프랭크! 프로젝트에 AspectJ를 도입 할 수 없다는 점에 유감 스럽다.이 문제를 해결할 수있는 aspect 도구를 알고 있습니까? 그러나 Java 이외의 새로운 구문을 도입하지는 않습니까? – vadipp

+1

주석을 사용하는 AspectJ 구문이 있지만 그 자체가 AspectJ 구문의 비트를 포함하고있다. 그러나 나는 asp 나 javassist를 사용하는 일을 거의하지 않으면 서 AspectJ 나 그와 동등한 방법으로 피할 수있는 것을 보지 못한다. –

+0

주석이 받아 들여질 것 같습니다! 고마워, 나는 그것을 시험해 볼 것이다. – vadipp