>  기사  >  Java  >  Java의 프록시 패턴에 대한 자세한 설명 및 예제 코드

Java의 프록시 패턴에 대한 자세한 설명 및 예제 코드

高洛峰
高洛峰원래의
2017-02-07 13:08:031369검색

Java 프록시 모드에 대한 자세한 설명

서문:

어떤 경우에는 클라이언트가 객체를 직접 참조하는 것을 원하지 않거나 참조할 수 없는 경우가 있습니다. 간접 참조를 달성하기 위한 "프록시" 3개. 프록시 객체는 클라이언트와 대상 객체 사이에서 중개 역할을 할 수 있으며, 클라이언트가 볼 수 없는 콘텐츠와 서비스를 제거하거나 클라이언트가 필요로 하는 추가적인 서비스를 프록시 객체를 통해 추가할 수 있다.

간단히 말하면 프록시 모드는 프록시 객체를 통해 실제 객체에 접근하는 모드로, 데코레이션 모드와 같이 객체에 몇 가지 기능을 추가할 수 있습니다.

정적 프록시

정적 프록시라고 불리는 것은 프로그램이 실행되기 전에 이미 프록시 클래스가 존재한다는 의미입니다. 즉, 코드를 작성할 때 이미 코드를 작성했다는 의미입니다. 프록시 클래스 및 동적 프록시 프록시 클래스는 프로그램이 실행될 때 자동으로 생성됩니다.

설명이 너무 추상적입니다. 코드만 보면 무슨 일이 일어나고 있는지 알 수 있습니다.

main

public class Main {
 
  public static void main(String[] args) {
    Water water = new Water();
    WaterProxy waterProxy = new WaterProxy(water);
    waterProxy.drink();
  }
 
}

인터페이스

//代理类与被代理类共同实现的接口
public interface Drink {
  void drink();
}

프록시 클래스

//被代理的类
public class Water implements Drink {
 
  @Override
  public void drink() {
    System.out.println("drink water");
  }
 
}

프록시 클래스

//代理类
//与被代理类实现同一个接口
public class DrinkProxy implements Drink {
   
  private Drink drinkImpl;
   
  //通过构造函数传入Water对象
  public DrinkProxy(Drink drinkImpl) {
    this.drinkImpl = drinkImpl;
  }
   
  @Override
  public void drink() {
    //在执行被代理对象的方法前做一些事情
    System.out.println("before drink");
    //执行被代理对象的方法
    drinkImpl.drink();
    //在执行被代理对象的方法后做一些事
    System.out.println("after drink");
  }
 
}

실행 결과

before drink
drink water
after drink

동적 프록시

가끔 프록시만 변경하고 싶을 때가 있습니다. 클래스로 표현되지만 프록시 객체는 실제 객체의 메소드 실행 전후에 동일한 작업을 수행하는 이른바 철갑 프록시 클래스와 간소화된 프록시 클래스입니다. 정적 프록시를 사용하면 동일한 인터페이스를 구현하는 클래스만 프록시할 수 있습니다. 임의의 클래스를 프록시하려면 중복 프록시 클래스를 많이 작성해야 합니다. 현재 동적 프록시를 사용할 수 있습니다. Java는 동적 프록시 구현을 위한 편리한 도구 세트를 제공합니다.

프록시 객체를 동적으로 생성할 수 있는 java.lang.reflect.Proxy 클래스의 메서드

/**
   *返回实现了指定接口的对象,调用代理对象的方法会调用
   *InvocationHandler的invoke方法
   *
   * @param  loader 获取代理类所使用的类加载器
   * @param  interfaces 代理类所要实现的接口
   * @param  h 实现了InvocationHandler接口的对象
   * @return 代理对象
   */
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)

InvocationHandler 인터페이스

/**
 *每个代理类都有一个关联的InvocationHandler
 *当代理对象执行一个方法的时候会直接执行invoke方法
 */
public interface InvocationHandler {
 
  /**
   * @param  调用该方法的代理对象
   * @param  method 代理对象所调用的方法
   * @param  args 调用的方法的参数
   * @return 调用的方法的返回值
   */
  public Object invoke(Object proxy, Method method, Object[] args)
}

설명은 항상 추상적이므로 실제 예제를 보면 이해하기 쉽습니다.

InvocationHandler 인터페이스 구현 클래스

public class CommonInvocationHandler implements InvocationHandler {
   
  //被代理的对象
  private Object proxied;
   
  public CommonInvocationHandler(Object proxied) {
    this.proxied = proxied;
  }
   
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //在调用被代理对象的方法前做一些事情
    System.out.println("before doing something");
    //调用被代理对象的方法
    Object result = method.invoke(proxied, args);
    //在调用被代理对象的方法后做一些事情
    System.out.println("after doing something");;
    return result;
  }
 
}

메인

public class Main {
 
  public static void main(String[] args) {
    //被代理的对象
    Water water = new Water();
    //动态获取代理对象
    Drink waterProxy =
        (Drink) Proxy.newProxyInstance(water.getClass().getClassLoader(),
            water.getClass().getInterfaces(),
            new CommonInvocationHandler(water));
    //通过代理对象调用方法
    waterProxy.drink();
  }
 
}

출력결과

before doing something
drink water
after doing something

특정 프록시 객체가 필요하지는 않지만 해당 인터페이스(인터페이스를 구현하지 않는 클래스는 cglib를 사용하여 동적 프록시를 구현할 수 있음)가 있어야 프록시 객체를 동적으로 얻을 수 있습니다. 예를 들어, 최근 인기 있는 Retrofit은 동적 프록시를 사용하여 선언된 인터페이스를 통해 직접 네트워크 요청을 합니다.

간단한 개조 시뮬레이션

POST 주석

//Post请求注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
 String value() default "";
}

쿼리 주석

//Post请求注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
 String value() default "";
}

서비스 인터페이스

public interface Service {
  //用POST注解声明请求的方式和相对路径
  @POST("/login")
  //@Query注解声明请求的参数名
  void login(@Query("username")String username,
      @Query("password")String password);
}

메인

public class Main {
 
  public static void main(String[] args) {
    // 动态获取Service接口的代理
    Service service = (Service) Proxy.newProxyInstance(Service.class.getClassLoader(),
        new Class[] { Service.class }, new InvocationHandler() {
 
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 通过注解获取请求的相对路径
            String retativePath = ((POST) method.getAnnotations()[0]).value();
            System.out.println("relative path: " + retativePath);
            // 获取参数的注解
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            // 通过参数的注解获取请求参数
            for (int i = 0; i < parameterAnnotations.length; i++) {
              if (parameterAnnotations[i].length != 0) {
                for (int j = 0; j < parameterAnnotations[i].length; j++) {
                  Query query = (Query) parameterAnnotations[i][j];
                  System.out.println(query.value() + ": " + args[i].toString());
                }
              }
            }
            return null;
          }
        });
    // 调用代理对象的方法
    service.login("hello", "world");
  }
 
}

읽어주셔서 감사합니다. 도움이 되기를 바랍니다. 이 사이트를 지원해 주셔서 감사합니다!

Java의 프록시 모드에 대한 자세한 설명과 샘플 코드 관련 기사를 보려면 PHP 중국어 웹사이트를 주목하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.