2011-08-18 6 views
2

약 1 일 동안 문제를 해결하려고 시도했지만 아무데도 나올 수 없습니다. 문제 :실행을 지연시키고, 모든 호출을 그룹화하고, java로 실행하기 위해 어떻게 메소드 호출을 가로 챌 수 있습니까?

자바 클래스 인 ExternalClass에 30 개의 메소드가 있습니다. ExternalClassFacade 인터페이스도 있습니다.

public class ExternalClass { 
    public method1() {...} 
    public method2() {...} 
    ... 
    ... 
    public metod30(...) {...} 
} 

이 클래스는 외부 라이브러리이므로 코드를 수정할 수 없습니다. 클래스는 잘 작동하지만 정의되지 않은 timespan에서 30 개의 메소드를 여러 번 호출하고 실행을 지연하고 모든 것을 한 번에 (직렬 또는 병렬로 상관하지 않음) 몇 차례 호출해야하는 상황이 있습니다.

예를 들어, 10 분이 넘으면 1 ~ 30 번 방법은 임의로 500 번 호출되며, 호출하는 순간에는 아무 것도하지 않기를 원하지만 10 분 후에 원래 500 번 호출을 모두 호출하려고합니다 라는.

대부분의 메서드는 내가 메서드를 호출 할 때 기억해야 할 매개 변수가 필요합니다.

나는이 메소드를 호출 할 때 또는 원래 메소드로 호출을 연결하여 지연되도록하는 특별한 메소드를 호출 할 수 있도록이 클래스를 확장/랩/합성하는 방법을 찾고있다. 바로 그 순간이 올 때까지.

나는 클래스를 확장하고 모든 방법을 무시하고 통화에 대한 정보를 개최 (30) 구조체와 같은 수업 관리에 대한 생각,하지만이 필요 :

  • 30 무시
  • (30 개) 목록
  • 30 클래스

많은 코드가 있지만별로 똑똑하지 않습니다.

이 작업을 수행하는 더 좋은 방법을 찾고 있는데, 원래 호출을 포착하고 포인터를 원래 메서드 호출로 유지하려고 생각했지만이 코드는 자바이므로 가능한 것은 아닙니다.

+0

Iv'e는 내가 처분 할 수있는 ExternalClass 인터페이스가 있음을 설명하는 질문을 편집했습니다. – eitama

답변

9

참으로 흥미로운 문제입니다. 첫 번째 질문 : ExternalClass이 일부 인터페이스를 구현합니까? 만약 그렇다면, 그것은하지 않는 경우 그러나, 당신은 하나를 만들 수 있습니다, 물건을 많이 단순화 :

interface ExternalClassFacade { 
    method1(); 
    method2(); 
      //... 
    method30(); 
} 
걱정하지 마세요

, 당신이 그것을 구현할 필요가 없습니다! ExternalClass에서 모든 메소드 서명을 복사하면됩니다. java.lang.Proxy을 알고 계십니까? 당신처럼 이러한 문제에 멋진 도구 :이 마법과 모호한 코드 ExternalClassFacade를 구현하고 ExternalClass과 같은 방법을 실행할 수 있도록 만든 것을 볼 수 있듯이

ExternalClass ext = //obtain target ExternalClass somehow 
ExternalClassFacade extFacade = (ExternalClassFacade) Proxy.newProxyInstance(
    ExternalClass.class.getClassLoader(), 
    new Class<?>[]{ExternalClassFacade.class}, 
    new BatchInvocationHandler(ext)); 
extFacade.method1(); 

. - 당신이 같은 인수 ExternalClass에 같은 이름의 메서드 호출을 전달합니다 ExternalClassFacade에 메서드를 호출 할 때이 코드 자체는 유용한 아무것도하지 않는

public class BatchInvocationHandler implements InvocationHandler { 

    private final ExternalClass ext; 

    public BatchInvocationHandler(ExternalClass ext) { 
     this.ext = ext; 
    } 

    @Override 
    public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable { 
     return MethodUtils.invokeMethod(ext, method.getName(), args); 
    } 

} 

: 여기에 누락 된 퍼즐입니다. 그래서 우리는 아직 아무것도 얻지 못했습니다. BTW 나는 반향 코드를 단순화하기 위해 Apache Commons LangMethodUtils을 사용하고 있습니다. CLASSPATH에 이미이 라이브러리가있을 가능성이 있습니다. 그렇지 않은 경우 추가 코드가 몇 줄 밖에 없습니다.

지금이 개선 된 버전을보고 :

private static class BatchInvocationHandler implements InvocationHandler { 

    private final ExternalClass ext; 

    private Queue<Callable<Object>> delayedInvocations = new ConcurrentLinkedQueue<Callable<Object>>(); 

    public BatchInvocationHandler(ExternalClass ext) { 
     this.ext = ext; 
    } 

    @Override 
    public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable { 
     delayedInvocations.add(new Callable<Object>() { 
      @Override 
      public Object call() throws Exception { 
       return MethodUtils.invokeMethod(ext, method.getName(), args); 
      } 
     }); 
     return null; 
    } 

} 

이제 우리는 어딘가에 얻고있다 : 대신의 방법 우리가 Callable 내부의 전화를 포장하고 delayedInvocations 큐에 추가를 호출. 물론 실제 메소드를 호출하지 않으므로 반환 값은 단지 자리 표시 자일뿐입니다. ExternalClass 메서드의 반환 유형이 void이 아닌 경우 매우주의해야합니다.

나는 지금 당신이 빛을 볼 것 같아요. 필요한 모든 것은 Callable을 대기열에 모으고 일괄 적으로 실행하는 스레드를 만드는 것입니다. 다양한 방법으로 할 수 있지만 기본적인 빌딩 블록이 있습니다. 또한 맵과 같은 데이터 구조를 선택하거나 큐가 아닌 집합을 선택할 수도 있습니다. 예를 들어 어떤 이유로 든 이름으로 그룹화 방법을 상상할 수 있습니다.


물론 AspectJ/Spring AOP를 사용할 수 있다면 전체 프록시 인프라 코드를 피할 수 있습니다. 그러나 기본 아이디어는 API만이 더 즐거울 것이라는 점만 같을 것입니다.

+1

+1 나는 [Command Pattern] (http://en.wikipedia.org/wiki/Command_pattern)과 같은 것을 제안하려고했는데, 이것은 당신의 대답이되는 것입니다. –

+0

대단한 답변입니다. java.lang 패키지에도 불구하고 최근에야 발견 된'Proxy' 만. java.util.concurrent 클래스를 사용하면 클래스가 더욱 향상됩니다. 한 게시물에서 여러 가지를 배울 수 있다면 항상 좋습니다. –

+0

+1 좋은 답변 Tomasz! – helios

0

애스펙트를 사용하여 모든 호출을 가로 채어 외부 lib 메소드를 실행하고 스레드를 일시 정지하고 스레드 ID를 동기화 된 Set에 기록 할 수 있습니다. 그 세트는 다른 스레드가 본 싱글 톤에 있습니다.

실행할 비즈니스 규칙이 실행되면 Singleton watcher가 Set을 반복하고 각 스레드에 프로세스를 계속 처리하도록 알리십시오. 애스펙트는 계속 진행되어 원래 요청 된 외부 메소드를 각각 실행합니다.

2

AspectJ를 사용하면 클래스에 introduce an interface을 입력 한 다음 인터페이스에 코딩 할 수 있습니다. 그 다음에는 원하는 인터페이스 뒤에 원하는 동작을 자유롭게 추가 할 수 있습니다. 또는, AspectJ를 사용하여 찾고있는 수집/실행 동작을 짜기 만하면된다.

Cglib 또는 Javassist 또한 동적으로 서브 클래 싱하여 클래스를 프록시 처리하여 더 깔끔하게 처리 할 수 ​​있습니다 (최종이 아니라고 가정).

다양한 옵션이 있습니다. 그것들은 나에게 일어난 3 개의 제 3의 것입니다. 이러한 접근법의 장점은 나중에 쉽게 수집하고 실행할 수있는 객체 형식의 메소드 호출을 표현할 수 있다는 것입니다.

관련 문제