"안녕, 아미고."

"안녕, 리시."

"오늘 저는 새롭고 매우 흥미로운 주제인 동적 프록시에 대해 설명하겠습니다." .

"Java에는 특정 클래스의 기능을 변경하는 여러 가지 방법이 있습니다…"

"첫 번째 방법은 상속입니다."

"클래스의 동작을 변경하는 가장 쉬운 방법은 원래(기본) 클래스를 상속하는 새 클래스를 만들고 해당 메서드를 재정의하는 것입니다. 그런 다음 원래 클래스를 사용하는 대신 파생 클래스를 사용합니다. 예를 들면 다음과 같습니다."

Reader reader = new UserCustomReader();

"두 번째 방법은 래퍼 클래스를 사용하는 것입니다."

" BufferedReader 는 이러한 유형의 클래스의 예입니다. 첫째, Reader를 상속 합니다. 즉, Reader 대신 사용할 수 있습니다. 둘째, BufferedReader 객체의 생성자로 전달되어야 하는 원래 Reader 객체에 대한 모든 호출을 리디렉션합니다. . 예를 들어:"

Reader readerOriginal = new UserCustomReader();
Reader reader = new BufferedReader(readerOriginal);

"세 번째 방법은 동적 프록시(Proxy)를 만드는 것입니다."

"Java(java.lang.reflect.Proxy)에는 별도의 클래스를 만들지 않고도 프로그램 실행 중에(동적으로) 개체를 구성할 수 있는 특수 클래스가 있습니다 ."

"이 작업은 매우 쉽습니다."

Reader reader = (Reader)Proxy.newProxyInstance();

"그것은 이미 나에게 새로운 것입니다!"

"하지만 물론 메서드가 없는 개체는 필요하지 않습니다. 메서드가 있는 개체가 필요하고 원하는 작업을 수행하려면 개체가 필요합니다. Java는 모든 메서드 호출을 가로챌 수 있는 InvocationHandler라는 특수 인터페이스를 사용 합니다 . 프록시 개체와 연결되어 있습니다 . 프록시 개체는 인터페이스를 통해서만 만들 수 있습니다."

" Invoke – 주요 작업이 단순히 일부 메서드를 호출하는 것인 메서드 또는 클래스의 표준 이름입니다."

" 핸들러 – 일부 이벤트를 처리하는 클래스의 표준 이름입니다. 예를 들어 마우스 클릭을 처리하는 클래스는 MouseClickHandler 등으로 불립니다."

"InvocationHandler 인터페이스에는 프록시 개체에 대한 모든 호출이 전달되는 단일 호출 메서드가 있습니다 . 예를 들면 다음과 같습니다."

암호
Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler());
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  System.out.println("yes!");
  return null;
 }
}

" reader . close () 메서드를 호출하면 invoke 메서드가 호출되고 화면에 'yes!'가 표시됩니다."

"그래서 CustomInvocationHandler 클래스를 선언하고 InvocationHandler 인터페이스와 해당 invoke 메서드를 구현했습니다. invoke 메서드가 호출되면 'yes!'가 표시됩니다. 그런 다음 CustomInvocationHandler 객체를 생성하고 생성 시 newProxyInstance 메서드 에 전달했습니다. 프록시 개체입니다."

"네, 모두 맞습니다."

"이것은 매우 강력한 도구입니다. 일반적으로 이러한 프록시는 다른 컴퓨터에서 실제로 실행되는 프로그램의 개체를 시뮬레이트하거나 액세스를 제어하기 위해 생성됩니다  ."

"이 방법에서 현재 사용자의 권한을 확인하고, 오류를 처리하고, 오류를 기록하는 등의 작업을 수행할 수 있습니다."

"다음은 invoke 메소드가 원래 객체의 메소드를 호출하는 예입니다."

암호
Reader original = new UserCustomReader();

Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler(original));
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
 private Reader readerOriginal;

 CustomInvocationHandler(Reader readerOriginal)
 {
  this.readerOriginal = readerOriginal;
 }

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  if (method.getName().equals("close"))
  {
   System.out.println("Reader closed!");
  }

  // This calls the readerOriginal object's close method.
  // The method's name and a description of its parameters are stored in the method variable.
  return method.invoke(readerOriginal, args);
 }
}

"이 예에는 두 가지 특별한 기능이 있습니다."

"먼저 «원본» Reader 개체가 생성자에 전달되고 이에 대한 참조가 CustomInvocationHandler 내부에 저장됩니다 . "

"두 번째로, invoke 메서드에서 이 동일한 메서드를 다시 호출하지만 이번에는 «원본» 개체에서 호출합니다."

"아. 즉, 이 마지막 줄은 동일한 메서드를 호출하지만 원래 개체에 대해 다음과 같이 호출합니다."

return method.invoke(readerOriginal, args);

"네."

"매우 명백하다고 말할 수는 없지만 여전히 이해할 수 있습니다. 또는 그렇게 보입니다."

"좋습니다. 그리고 한 가지 더. newProxyInstance 메서드에서 프록시 개체를 생성하려면 관리 정보를 조금 더 전달해야 합니다. 하지만 우리는 엄청난 프록시 개체를 생성하지 않기 때문에 이 정보는 원래 클래스 자체에서 쉽게 얻을 수 있습니다. "

"여기 또 다른 예가 있습니다."

암호
Reader original = new UserCustomReader();

ClassLoader classLoader = original.getClass().getClassLoader();
Class<?>[] interfaces = original.getClass().getInterfaces();
CustomInvocationHandler invocationHandler = new CustomInvocationHandler(original);

Reader reader = (Reader)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
class CustomInvocationHandler implements InvocationHandler
{
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  return null;
 }
}

"아. ClassLoader와 인터페이스 목록. 이것은 Reflection에서 가져온 것 아닌가요?"

"네."

"그렇군요. 음, 필요하다면 원시적이고 매우 간단한 프록시 개체를 만들 수 있을 것 같습니다."

"그럼 디에고에게 가서 확인해봐."