Существует определенная корреляция между отражением и динамическим прокси, но просто сказать, что динамический прокси реализуется механизмом отражения, недостаточно всесторонне и неточно.Динамический прокси — это функциональное поведение, и существует множество способов его реализации. Чтобы понять приведенное выше предложение, см. ниже.
1. Отражение
Механизм отражения — это базовая функция, предоставляемая языком Java, которая дает программе время выполнения.самоанализ(самоанализ, официальный термин) способность. С помощью отражения мы можем напрямую манипулировать классами или объектами, например получать определение класса объекта, получать свойства и методы, объявленные классом, вызывать методы или создавать объекты и даже изменять определение класса во время выполнения.
1. Получите объект класса
Есть три способа получить объект класса:
- Через forName() -> Пример: Class.forName("PeopleImpl")
- Через getClass() -> Пример: new PeopleImpl().getClass()
- Получить .class напрямую -> Пример: PeopleImpl.class
2. Общие методы классов
- getName(): получить полный метод класса;
- getSuperclass(): получить суперкласс класса;
- newInstance(): создать экземпляр объекта;
- getFields(): получить все свойства текущего класса и общедоступную модификацию родительского класса;
- getDeclaredFields(): получить все объявленные свойства текущего класса (за исключением родительского класса);
- getMethod(): получить все методы публичной модификации текущего класса и родительского класса;
- getDeclaredMethods(): получить все объявленные методы текущего класса (за исключением родительского класса);
Больше способов:IC computer.API go.to/blog/class-…
3. Вызов метода класса
Чтобы вызвать метод в классе путем отражения, его нужно реализовать через ключевой метод "invoke()". Существует три типа вызовов методов:
- вызов статического метода
- обычный вызов метода
- вызов частного метода
Далее будут продемонстрированы коды реализации различных вызовов и общие части кода различных вызовов, а именно:
// 此段代码为公共代码
interface People {
int parentAge = 18;
public void sayHi(String name);
}
class PeopleImpl implements People {
private String privSex = "男";
public String race = "汉族";
@Override
public void sayHi(String name) {
System.out.println("hello," + name);
}
private void prvSayHi() {
System.out.println("prvSayHi~");
}
public static void getSex() {
System.out.println("18岁");
}
}
3.1 Вызовы статических методов
// 核心代码(省略了抛出异常的声明)
public static void main(String[] args) {
Class myClass = Class.forName("example.PeopleImpl");
// 调用静态(static)方法
Method getSex = myClass.getMethod("getSex");
getSex.invoke(myClass);
}
Вызов статических методов относительно прост.Используйте getMethod(xx) для получения соответствующего метода и непосредственно используйте invoke(xx).
3.2 Обычный вызов метода
Для обычных вызовов нестатических методов вам необходимо сначала получить пример класса, а затем получить его с помощью метода newInstance(). Основной код выглядит следующим образом:
Class myClass = Class.forName("example.PeopleImpl");
Object object = myClass.newInstance();
Method method = myClass.getMethod("sayHi",String.class);
method.invoke(object,"老王");
getMethod Get, вы можете объявить тип передаваемых параметров.
3.3 Вызов приватных методов
Чтобы вызвать закрытый метод, вы должны использовать "getDeclaredMethod(xx)", чтобы получить все методы этого класса. Код выглядит следующим образом:
Class myClass = Class.forName("example.PeopleImpl");
Object object = myClass.newInstance();
Method privSayHi = myClass.getDeclaredMethod("privSayHi");
privSayHi.setAccessible(true); // 修改访问限制
privSayHi.invoke(object);
В дополнение к "getDeclaredMethod(xx)" видно, что ключом к вызову закрытого метода является установка атрибута setAccessible(true) и изменение ограничений доступа, чтобы вызов можно было выполнить после установки.
4. Резюме
1. Основными методами отражения являются newInstance() для получения экземпляров класса, getMethod(..) для получения методов, invoke(..) для вызова методов и setAccessible для изменения ограничений доступа к закрытым переменным/методам.
2. Разница между наличием «Объявленных» при получении свойств/методов заключается в том, что метод или свойство с модификацией «Объявленное» могут получать все методы или свойства этого класса (от частного к общедоступному), но не могут получать какую-либо информацию о родительском классе; Необъявленные измененные методы или свойства могут получать только общедоступные измененные методы или свойства и могут получать информацию о родительском классе, такую как getMethod(..) и getDeclaredMethod(..).
2. Динамический прокси
Динамический прокси — это механизм, упрощающий динамическое создание прокси и динамическую обработку вызовов методов прокси во время выполнения.Многие сценарии выполняются с использованием аналогичных механизмов, таких как перенос вызовов RPC и аспектно-ориентированное программирование (АОП).
Существует множество способов реализации динамического прокси, например, динамический прокси, предоставляемый самим JDK, в основном использует упомянутый выше механизм отражения. Существуют и другие реализации, такие как использование легендарного высокопроизводительного механизма манипулирования байт-кодом, такого как ASM, cglib (на основе ASM) и т. д.
Проблемы, которые решают динамические прокси?
Во-первых, этопрокси-механизм. Если вы знакомы с шаблоном прокси в шаблоне проектирования, мы будем знать, что прокси можно рассматривать как обертку для вызывающей цели, так что наш вызов целевого кода происходит не напрямую, а через прокси. Через прокси вызывающая сторона и исполнитель могут общаться друг с другом.разъединение. Например, выполнение вызовов RPC через прокси-сервер может обеспечить более удобный интерфейс. Его также можно использовать как глобальный перехватчик через прокси.
1. Динамический прокси-сервер JDK Proxy
JDK Proxy реализуется путем реализации интерфейса InvocationHandler, код выглядит следующим образом:
interface Animal {
void eat();
}
class Dog implements Animal {
@Override
public void eat() {
System.out.println("The dog is eating");
}
}
class Cat implements Animal {
@Override
public void eat() {
System.out.println("The cat is eating");
}
}
// JDK 代理类
class AnimalProxy implements InvocationHandler {
private Object target; // 代理对象
public Object getInstance(Object target) {
this.target = target;
// 取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用前");
Object result = method.invoke(target, args); // 方法调用
System.out.println("调用后");
return result;
}
}
public static void main(String[] args) {
// JDK 动态代理调用
AnimalProxy proxy = new AnimalProxy();
Animal dogProxy = (Animal) proxy.getInstance(new Dog());
dogProxy.eat();
}
С помощью приведенного выше кода мы реализовали динамический прокси для печати простого сообщения до и после всех запросов.
Уведомление:JDK Proxy может проксировать только классы, которые реализуют интерфейсы (даже расширения, наследующие классы, не могут быть проксированы).
Почему JDK Proxy может использовать только прокси-классы, реализующие интерфейсы?
Эта проблема начинается с исходного кода метода реализации newProxyInstance динамического прокси:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// 省略其他代码
Посмотрите на описание первых двух параметров исходного кода:
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class to implement
- загрузчик: это загрузчик классов, то есть target.getClass().getClassLoader()
- interfaces: Список реализаций интерфейса прокси-класса интерфейса.
Таким образом, источник этой проблемы кроется в исходном дизайне JDK Proxy. Если вы хотите настаивать на динамическом прокси, класс реализации без интерфейса сообщит об ошибке:
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to xxx
2. Динамический прокси Cglib
Механизм динамического прокси JDK может проксировать только класс, который реализует интерфейс. Cglib реализует прокси для класса. Его принцип заключается в создании подкласса для указанного целевого класса и переопределении метода для достижения улучшения, но из-за наследования, поэтому вы не может проксировать для окончательно модифицированных классов.
На Cglib может напрямую ссылаться Maven, адрес версии Maven:MV внутри репозитория.com/artifact/success…
В этой статье используется последняя версия Cglib 3.2.9 и добавлена следующая ссылка на pom.xml:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.9</version>
</dependency>
Реализация кода Cglib выглядит следующим образом:
class Panda {
public void eat() {
System.out.println("The panda is eating");
}
}
class CglibProxy implements MethodInterceptor {
private Object target; // 代理对象
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
// 设置父类为实例类
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("调用前");
Object result = methodProxy.invokeSuper(o, objects); // 执行方法调用
System.out.println("调用后");
return result;
}
}
public static void main(String[] args) {
// CGLIB 动态代理调用
CglibProxy proxy = new CglibProxy();
Panda panda = (Panda)proxy.getInstance(new Panda());
panda.eat();
}
Вызов cglib реализует метод перехвата интерфейса MethodInterceptor и вызывает invokeSuper для выполнения динамического проксирования, которое может напрямую выполнять динамическое проксирование обычных классов.
3. Прокси-сервер JDK против Cglib
Преимущества прокси-сервера JDK:
- Минимизация зависимостей, сокращение зависимостей означает упрощение разработки и обслуживания, а поддержка самого JDK более надежна;
- Плавно обновите версию JDK, и библиотеку классов байт-кода обычно необходимо обновить, чтобы гарантировать, что ее можно будет использовать в новой версии;
Преимущества фреймворка Cglib:
- Общие классы можно вызывать без реализации интерфейсов;
- высокая производительность;
Суммировать:Следует отметить, что производительность не обязательно является единственным соображением при нашем выборе.Надежность, ремонтопригодность, программная нагрузка и т. д. часто являются более важными соображениями.В конце концов, порог для стандартных библиотек классов и программирования с отражением намного ниже. код также более управляем, что также можно увидеть, если мы сравним инвестиции в разработку динамических прокси различных проектов с открытым исходным кодом.
Весь пример кода в этой статье:GitHub.com/VIP stone/Срочно…
4. Справочные документы
Основные технологии Java 36 лекций:t.cn/EwUJvWA
Отражение Java и динамический прокси:woo woo woo.cn blog on.com/Han Ganglin/…
Обратите внимание на публичный аккаунт автора:
Если эта статья оказалась для вас полезной, пригласите меня на чашечку кофе.