Skip to content

Latest commit

 

History

History
139 lines (120 loc) · 4.74 KB

File metadata and controls

139 lines (120 loc) · 4.74 KB

Proxy

В java для проксирования используется два класса:
java.lang.reflect.Proxy
java.lang.reflect.InvocationHandler

Создание прокси объекта:

Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)
loader - класс лоадер, для объявления прокси-класса
interfaces – список интерфейсов, которые прокси класс должен реализовывать
handler - обработчик для отправки вызовов метода

Создание обработчика вызовов:

InvocationHandler handler = (proxy, method, args) -> {
    if(method.getName().equals("getName")){
        return ((String)method.invoke(user, args)).toUpperCase();
    }
    return method.invoke(user, args);
};

В обработчике всего один метод: Object invoke(Object proxy, Method method, Object[] args)
proxy - прокси, на котором вызов метода происходит
method - класс метода (из рефлексии), основано на интерфейсе, который реализует прокси
args - массив аргументов, которые были переданы в вызов метода

Для выполнения дефолтных методов, есть специальный метод invokeDefault, пример использования:

// arrange
interface A {
    default T m(A a) {
        return t1; 
    }
}
interface B {
    default T m(A a) {
        return t2;
    }
}
interface C extends A {}

// proxy for A
// Код будет вызывать дефолтный метод в классе A::m
Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { A.class },
        (o, m, params) -> {
    if (m.isDefault()) {
        // if it's a default method, invoke it
        return InvocationHandler.invokeDefault(o, m, params);
    }
});

// Если прокси реализует оба интерфейса и A и B, то можно выбирать какой именно дефолтный метод мы будем вызывать
// далее вызывается дефолтный метод из B::m
Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { A.class, B.class },
        (o, m, params) -> {
    if (m.getName().equals("m")) {
        // invoke B::m instead of A::m
        Method bMethod = B.class.getMethod(m.getName(), m.getParameterTypes());
        return InvocationHandler.invokeDefault(o, bMethod, params);
    }
});

// Если проксируется C, то можно вызвать дефолтный метод из A
// далее прокси C и вызывается метод A::m
C c = (C) Proxy.newProxyInstance(loader, new Class<?>[] { C.class },
        (o, m, params) -> {
    if (m.getName().equals("m")) {
        // IllegalArgumentException thrown as {@code A::m} is not a method                  
        // inherited from its proxy interface C
        Method aMethod = A.class.getMethod(m.getName(), m.getParameterTypes());
        return InvocationHandler.invokeDefault(o, aMethod, params);
    }
});
c.m(...);

Пример кода:

package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxyDemo {
    interface If {
        void originalMethod(String s);
    }
    static class Original implements If {
        public void originalMethod(String s) {
            System.out.println(s);
        }
    }
    static class Handler implements InvocationHandler {
        private final If original;
        public Handler(If original) {
            this.original = original;
        }
        public Object invoke(Object proxy, Method method, Object[] args)
                throws IllegalAccessException, IllegalArgumentException,
                InvocationTargetException {
            System.out.println("BEFORE");
            method.invoke(original, args);
            System.out.println("AFTER");
            return null;
        }
    }
    public static void main(String[] args){
        Original original = new Original();
        Handler handler = new Handler(original);
        If f = (If) Proxy.newProxyInstance(If.class.getClassLoader(),
                new Class[] { If.class },
                handler);
        f.originalMethod("Hallo");
    }
}

Результат работы программы:

BEFORE
Hallo
AFTER

Info: