Retrofit의 동적 에이전트

4718 단어
모두 알고 있다Retrofit는 동적 에이전트를 통해 에이전트 대상을 생성하여 네트워크 요청의 발기자로 한다.
오늘 동적 에이전트가 어떻게 작동하는지 한번 봅시다.아니면 어떻게 인터페이스처럼 보이는 대상에게 추상적인 방법을 사용하게 합니까?
코드부터 볼게요.
public static void main(String[] args) {
  Factory factory = new Factory();
  Bird bird = factory.create(Bird.class);
  bird.fly();
}

interface Bird {
  void fly();
}


이 코드는 하나의 Factory 실례 호출create 방법을 통해 하나의 인터페이스에 전송된 class 대상을 하나의 인터페이스의 실례로 되돌려주고 인터페이스에 있는 방법fly()을 호출할 수 있다.
우리의 정적 코드 중 이 Bird 인터페이스를 실현한 클래스는 하나도 없다. (전체 코드는 아래를 볼 수 있다.)그렇다면 이 상대는 도대체 어디서 온 걸까?
전체 코드는 다음과 같다.
public class DynamicProxy {

    interface Bird {
        void fly();
    }

    public static void main(String[] args) {
        Factory factory = new Factory();
        Bird bird = factory.create(Bird.class);
        bird.fly();
    }

    static class Factory implements InvocationHandler {

        public  T create(Class target) {
            return (T) Proxy.newProxyInstance(target.getClassLoader(),
                    new Class[]{target},
                    this);
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("flying...");
            return null;
        }
    }
}

호출bird.fly()할 때 출력 결과flying...는 분명히 코드에 처음에 말한 것처럼 Bird 인터페이스를 실현한 하위 클래스가 존재하지 않았고 bird는 실제 호출fly() 방법을 사용했다.유일한 가능성은 bird이 인터페이스의 실례(또는 인터페이스를 실현하는 하위 클래스의 대상)이다.이곳은 보기에 좀 이상한 것 같다.
물론 프로그래밍에는 마법이 없다. 여기에는 Java의 동적 에이전트만 이용했고 Proxy.newProxyInstance() 방법을 통해 지정된 인터페이스를 실현한 하위 클래스를 생성하고 이 동적 생성 클래스의 실례 대상을 되돌려주었다.
이 하위 클래스는 인터페이스에서 방법을 호출할 때 사실 InvocationHandlerinvoke() 방법을 호출한다.이 방법에는 대응하는 매개 변수의 리셋이 있어 이 매개 변수에 따라 적당한 차단/강화 등의 조작을 할 수 있다.
주의해야 할 점은 JDK가 제공한 동적 에이전트이다. 동적 생성의 하위 클래스는 Proxy류에서 계승된 것이고 Java는 다중 계승을 지원하지 않기 때문에 분명하다.동적 에이전트를 통해 되돌아오는 대상은 반드시 인터페이스 형식으로 수신된다. 확장된 것은 인터페이스와 인터페이스를 실현하는 하위 클래스만 있고 인터페이스를 실현하지 못한 일부 클래스는 확장할 수 없다.cglib 확장 클래스 지원)
이것들을 알면 Retrofit의 동적 대리가 대체로 어떤 논리인지 알 수 있다.
다음 모조Retrofit는 방법에 대한 주석을 통해 네트워크 요청을 모의해 봅시다.
인터페이스에서 방법에 대한 설명을 통해 요청한 매개 변수를 확인합니다.그리고 프록시 대상을 만들고 방법에서 온전한 Url,https://github.com/search?q=java네트워크를 요청하고 응답 헤더의 Status 필드를 출력합니다.
public interface Bird {

    @Protocol()
    @Method()
    @Path("search")
    @Query("q=java")
    @Url("github.com")
    String fly();
}


public class Test {

    public static void main(String[] args) {
        Factory factory = new Factory();
        Bird bird = factory.create(Bird.class);
        String status = bird.fly();

        System.out.println(status);//   Status: 200 OK
    }
}

부분 코드
public class Factory implements InvocationHandler {
    OkHttpClient httpClient = new OkHttpClient();

    public  T create(Class target) {
        return (T) Proxy.newProxyInstance(target.getClassLoader(),
                new Class[]{target},
                this);
    }

    public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
        String get = method.getAnnotation(Method.class).method();
        String protocol = method.getAnnotation(Protocol.class).value();
        String url = method.getAnnotation(Url.class).value();
        String path = method.getAnnotation(Path.class).value();
        String query = method.getAnnotation(Query.class).value();

        String entire_url = protocol + "://" + url + "/" + path + "?" + query;


        System.out.println(entire_url);
        Request build = new Request.Builder().url(entire_url).get().build();

        return httpClient.newCall(build).execute().headers().get("Status");
    }
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Method {
    String method() default "GET";
}


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Url {
    String value();
}


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Protocol {
    String value() default "https";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Path {
    String value();
}



@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Query {
    String value() default "";
}

좋은 웹페이지 즐겨찾기