Сегодня случайно мне захотелось взглянуть на динамический прокси JDK, потому что я немного знал о нем раньше и просто хотел протестировать его использование. Я быстро написал эти интерфейсы и классы:
Класс интерфейса: UserService.java
Скопируйте код кода следующим образом:
пакет com.yixi.proxy;
общедоступный интерфейс UserService {
общественное int save();
публичное обновление void (int id);
}
Класс реализации: UserServiceImpl.java
Скопируйте код кода следующим образом:
пакет com.yixi.proxy;
публичный класс UserServiceImpl реализует UserService {
@Override
общественный int save() {
System.out.println("Сохранить пользователя...");
возврат 1;
}
@Override
общественное недействительное обновление (int id) {
System.out.println("обновить пользователя" + id);
}
}
Затем я с тревогой написал нужный мне InvokeHandler: функция этого очень проста: записывать время начала и время окончания выполнения метода.
TimeInvoctionHandler.java
Скопируйте код кода следующим образом:
пакет com.yixi.proxy;
импортировать java.lang.reflect.IndictionHandler;
импортировать java.lang.reflect.Method;
публичный класс TimeInvocateHandler реализует IndictionHandler {
@Override
Открытый вызов объекта (прокси-сервер объекта, метод метода, аргументы Object[])
бросает Throwable {
System.out.println("startTime: " +System.currentTimeMillis());
Объект obj = метод.invoke(прокси, args);
System.out.println("endTime: " +System.currentTimeMillis());
вернуть объект;
}
}
После того, как все приготовления сделаны, самое время приступить к написанию тестов!
Тест.java
Скопируйте код кода следующим образом:
пакет com.yixi.proxy;
импортировать java.lang.reflect.Proxy;
тест публичного класса {
public static void main(String[] args) {9 TimeIndictionHandler timeHandler = new TimeInvocateHandler();
UserService u = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler);
у.обновление(2);
у.сохранить();
}
}
Он работал успешно, но не выдавал никакого лица. В результате возникло исключение, заполнившее экран:
Скопируйте код кода следующим образом:
Время начала: 1352877835040
Время начала: 1352877835040
Время начала: 1352877835040
Исключение в потоке «основной» java.lang.reflect.UndeclaredThrowableException
в $Proxy0.update (неизвестный источник)
на com.yixi.proxy.Test.main(Test.java:11)
Вызвано: java.lang.reflect.IndictionTargetException.
в sun.reflect.NativeMethodAccessorImpl.invoke0 (собственный метод)
в sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
в sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
в java.lang.reflect.Method.invoke(Method.java:597)
в com.yixi.proxy.TimeInvocateHandler.invoke(TimeInvocationHandler.java:12)
... еще 2
Исключение com.yixi.proxy.TimeInvocateHandler.invoke(TimeInvocateHandler.java:12) ясно указывает на проблему в строке 12 TimeInvocationHandle: то есть
Скопируйте код кода следующим образом:
Открытый вызов объекта (прокси-сервер объекта, метод метода, аргументы Object[])
бросает Throwable {
System.out.println("startTime: " +System.currentTimeMillis());
Объект obj = метод.invoke(прокси, args);
System.out.println("endTime: " +System.currentTimeMillis());
вернуть объект;
}
В этом методе нет ничего плохого! Поскольку метод, похоже, предоставляет все параметры, необходимые для метода.invoke(Object, Object[]), мы будем использовать его как нечто само собой разумеющееся. Если вы действительно так думаете, то вы обманули JDK. ловушка. Давайте сначала посмотрим, как правильно это написать. Если некоторые ученики не в настроении читать следующее, по крайней мере, дайте мне правильное решение:
Измените TimeInvocationHandler.java.
Скопируйте код кода следующим образом:
пакет com.yixi.proxy;
импортировать java.lang.reflect.IndictionHandler;
импортировать java.lang.reflect.Method;
публичный класс TimeInvocateHandler реализует IndictionHandler {
частный объект о;
общественный TimeInvocationHandler (Объект o) {
это.о = о;
}
@Override
Открытый вызов объекта (прокси-сервер объекта, метод метода, аргументы Object[])
бросает Throwable {
System.out.println("startTime: " +System.currentTimeMillis());
Объект obj = метод.invoke(o, args);
System.out.println("endTime: " +System.currentTimeMillis());
вернуть объект;
}
}
Изменить Test.java
Скопируйте код кода следующим образом:
пакет com.yixi.proxy;
импортировать java.lang.reflect.Proxy;
тест публичного класса {
public static void main(String[] args) {
TimeInvocateHandler timeHandler = новый TimeIndictionHandler (новый UserServiceImpl());
UserService u = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler);
у.обновление(2);
у.сохранить();
}
}
Теперь вот правильный вывод:
Скопируйте код кода следующим образом:
Время начала: 1352879531334
обновить пользователя 2
время окончания: 1352879531334
Время начала: 1352879531334
сохранение пользователя....
время окончания: 1352879531335
Если вам нужно меньше кода, вы можете напрямую написать анонимный класс:
пакет com.yixi.proxy;
импортировать java.lang.reflect.IndictionHandler;
импортировать java.lang.reflect.Method;
импортировать java.lang.reflect.Proxy;
тест публичного класса {
public static void main(String[] args) {
окончательный UserServiceImpl usi = новый UserServiceImpl ();
UserService u = (UserService) Proxy.newProxyInstance(
usi.getClass().getClassLoader(),
usi.getClass().getInterfaces(),
новый InvokeHandler() {
@Override
Открытый вызов объекта (прокси-сервер объекта, метод метода, аргументы Object[])
бросает Throwable {
System.out.println("startTime: " +System.currentTimeMillis());
Объект obj = метод.invoke(usi, args);
System.out.println("endTime: " +System.currentTimeMillis());
вернуть объект;
}
});
у.обновление(2);
у.сохранить();
}
}
Поскольку первый параметр в методе.invoke(target,args); является целевым объектом, почему методу Invoke объекта InvokeHandler нужен параметр прокси-сервера объекта? Давайте посмотрим вниз!
Что касается самого важного метода вызова (по моему личному мнению), давайте посмотрим, что говорит JDK:
Скопируйте код кода следующим образом:
вызывать
Вызов объекта (прокси объекта,
метод метод,
Объект[] аргументы)
выдает вызовы методов Throwable handles на экземпляре прокси и возвращает результат. Этот метод вызывается в обработчике вызова, когда метод вызывается в экземпляре прокси, с которым он связан.
параметр:
proxy - экземпляр прокси, на котором вызывается метод
метод — экземпляр метода, соответствующий методу интерфейса, вызванному в экземпляре прокси. Объявляющим классом объекта метода будет интерфейс, в котором объявлен метод, который может быть суперинтерфейсом прокси-интерфейса, от которого прокси-класс наследует метод.
args — массив объектов, содержащий значения аргументов, передаваемых в вызов метода экземпляра прокси, или значение null, если метод интерфейса не принимает аргументов. Параметры базовых типов заключаются в экземпляры соответствующего базового класса-оболочки (например, java.lang.Integer или java.lang.Boolean).
proxy — экземпляр прокси, на котором вызывается метод? Что означает это предложение? актерское мастерство? метод является прокси-методом? Тогда разве мой метод выполнения прокси не должен быть Object obj = метод.invoke(proxy, args);? Я тогда не обернулся. Я пошел в дискуссионную группу и зашел в Google, но не нашел вдохновения и подумал, что лучше посмотреть исходный код и, может быть, я смогу что-нибудь увидеть!
Откройте исходный код класса Proxy и узнайте, что такое конструктор:
Скопируйте код кода следующим образом:
protectedInvoctionHandler h;
защищенный прокси (InvoctionHandler h) {
this.h = час;
}
Используйте InvocationHandler в качестве параметра конструктора Proxy.... Тогда для чего он использует InvocationHandler? Есть ли какая-либо связь с методом вызова() в InvoctionHandler?
Моя первая мысль заключается в том, что Proxy внутренне вызовет следующий оператор:
Скопируйте код кода следующим образом:
h.invoke(this,methodName,args);
Потому что вам нужно вызвать метод вызова, чтобы выполнить соответствующий метод.
Давайте сначала посмотрим на это
Здесь вы найдете кое-что, что имеет смысл: когда u.update(2), àProxy вызывает handler.invoke(proxyClass,update,2) à, что означает proxyClass.update(2);
Когда u.save(); Proxy вызовет handler.invoke(proxyClass,save,null) à, то есть proxyClass.save();
Когда Test.java изменится на это:
Скопируйте код кода следующим образом:
тест публичного класса {
public static void main(String[] args) {
окончательный UserServiceImpl usi = новый UserServiceImpl ();
UserService u = (UserService) Proxy.newProxyInstance(
usi.getClass().getClassLoader(),
usi.getClass().getInterfaces(),
новый InvokeHandler() {
@Override
Открытый вызов объекта (прокси-сервер объекта, метод метода, аргументы Object[])
бросает Throwable {
вернуть ноль;
}
});
у.обновление(2);
у.сохранить();
}
}
Обратите внимание, что метод анонимного класса в это время возвращает значение null. Если вы его запустите, вы обнаружите:
Скопируйте код кода следующим образом:
Исключение в потоке «основной» java.lang.NullPointerException
в $Proxy0.save (неизвестный источник)
на com.yixi.proxy.Test.main(Test.java:17)
В строке 17 есть нулевой указатель, то есть метод u.save() здесь имеет нулевой элемент. Может быть, u пуст? Этого не должно быть. Если u имеет значение null, то u.update(2) сообщит об исключении нулевого указателя. Когда я закомментирую строку 17, исключение исчезнет, указывая, что u.update() может выполняться нормально. Так почему же это?
Фактически, именно поэтому метод вызова возвращает значение null:
Обратите внимание на два метода в классе UserService:
Скопируйте код кода следующим образом:
общедоступный интерфейс UserService {
общественный int save();
публичное обновление void (int id);
}
Метод Save() возвращает тип int, а метод update возвращает тип void, согласно приведенному выше предположению, handler.invoke() реализует proxyClass.update(2); и метод return в методе вызова соответствует возврату; значение прокси-метода,
Поэтому, когда метод вызова возвращает значение null, метод обновления агента получает возвращаемое значение null, и изначально он возвращает void, поэтому об исключении не сообщается, и сохранение агента должно возвращать значение типа int. То, что мы здесь возвращаем, по-прежнему имеет значение null. и JVM не может преобразовать значение null. Он преобразуется в тип int, поэтому сообщается об исключении. Это объяснение можно объяснить ясно, а также оно может относительно подтвердить предыдущее предположение.
Кажется, что первый параметр proxy в методе вызова InvoctionHandler позволяет классу Proxy передавать ссылку на прокси-объект proxyClass при вызове метода со ссылкой на собственный объект InvoctionHandler для завершения дела, которое необходимо завершить proxyClass. .
Литературный талант – это нехорошо! Возможности ограничены! Надеюсь, вы, ребята, меня поправите...