предисловие
Прокси-режим — этоструктурныйШаблоны проектирования, обеспечивающие дополнительный доступ к целевому объекту;即通过代理对象访问目标对象。
Преимущество этого в том, что дополнительные функциональные операции могут быть расширены на основе реализации целевого объекта, то есть функция целевого объекта может быть расширена.
Здесь используется идея в программировании: не модифицируйте код или методы, которые написали другие, по своему желанию.Если вам нужно это изменить, вы можете расширить метод по доверенности.
В режиме прокси есть примерно три роли:
-
Real Subject
: реальный класс, то есть класс-посредник и класс делегата. Используется для действительно завершения функции бизнес-услуг; -
Proxy
: Прокси-класс, который реализует свой собственный запрос с функцией, соответствующей Реальному Субъекту, а объект прокси-класса на самом деле не реализует свою бизнес-функцию; -
Subject
: определяет интерфейс, который должны реализовывать роли RealSubject и Proxy.
Существует три типа режима прокси: статический прокси, динамический прокси (прокси JDK, прокси интерфейса), прокси Cglib (динамическое создание подкласса целевого объекта в памяти)
текст
статический прокси
Статический прокси-сервер должен сначала определить интерфейс, а прокси-объект и прокси-объект реализуют один и тот же интерфейс, а затем通过调用相同的方法来调用目标对象的方法
.
Видно, что прокси-класс — это не что иное, как добавление некоторых операций до и после вызова метода класса делегата. Различие в классе делегата также приводит к различию в прокси-классе.
Компании, производящей телевизоры, необходимо найти агента по местным продажам. Поэтому, когда клиенту нужно купить телевизор, он может купить его напрямую через агента.
Пример кода:
Телевизионная установка:
public class TV {
private String name;//名称
private String address;//生产地
public TV(String name, String address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "TV{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
Создадим интерфейс компании:
public interface TVCompany {
/**
* 生产电视机
* @return 电视机
*/
public TV produceTV();
}
Заводы компании производят телевизоры:
public class TVFactory implements TVCompany {
@Override
public TV produceTV() {
System.out.println("TV factory produce TV...");
return new TV("小米电视机","合肥");
}
}
Агент переходит к оформлению заказа на получение товара (статический класс агента):
public class TVProxy implements TVCompany{
private TVCompany tvCompany;
public TVProxy(){
}
@Override
public TV produceTV() {
System.out.println("TV proxy get order .... ");
System.out.println("TV proxy start produce .... ");
if(Objects.isNull(tvCompany)){
System.out.println("machine proxy find factory .... ");
tvCompany = new TVFactory();
}
return tvCompany.produceTV();
}
}
Потребители получают товары через агентов (использование агентов):
public class TVConsumer {
public static void main(String[] args) {
TVProxy tvProxy = new TVProxy();
TV tv = tvProxy.produceTV();
System.out.println(tv);
}
}
Выходной результат:
TV proxy get order ....
TV proxy start produce ....
machine proxy find factory ....
TV factory produce TV...
TV{name='小米电视机', address='合肥'}
Process finished with exit code 0
резюме:
-
преимущество:
静态代理模式在不改变目标对象的前提下,实现了对目标对象的功能扩展。
-
недостаток:
静态代理实现了目标对象的所有方法,一旦目标接口增加方法,代理对象和目标对象都要进行相应的修改,增加维护成本。
Как решить недостатки статического прокси? Ответ заключается в том, что вы можете использовать метод динамического прокси.
Динамический прокси
Динамический агент имеет следующие характеристики:
-
Динамический прокси-объект JDK не должен реализовывать интерфейс, только целевой объект должен реализовывать интерфейс.
-
Реализация динамического прокси-сервера на основе интерфейса требует использования API в JDK для его динамической сборки в памяти JVM.
Proxy对象
. -
нужно использовать
java.lang.reflect.Proxy
, и этоnewProxyInstance
метод, но метод должен получить три параметра.
Обратите внимание, что этот метод является статическим методом в классе Proxy и получает три параметра:
-
ClassLoader loader
: указывает, что текущий целевой объект использует загрузчик класса, и метод получения загрузчика является фиксированным. -
Class<?>[] interfaces
: Тип интерфейса, реализованного целевым объектом, с использованием дженериков для подтверждения типа. -
InvocationHandler h
: обработка события, при выполнении метода целевого объекта будет запущен метод обработчика события, а метод текущего выполняемого целевого объекта будет передан в качестве параметра.
В один прекрасный день компания расширила свой бизнес и продавала все больше и больше продукции, а послепродажное обслуживание также нуждалось в улучшении. Однако компания обнаружила, что первоначальный агент нуждался в переобучении, чтобы завершить весь бизнес, поэтому она нашла другого агента.
动态代理商B
.代理商B
Он обещает беспроблемно подключить весь бизнес компании, какой бы новый бизнес не добавлялся, его можно пройти без дополнительного обучения.
Пример кода:
Компания добавила техническое обслуживание:
public interface TVCompany {
/**
* 生产电视机
* @return 电视机
*/
public TV produceTV();
/**
* 维修电视机
* @param tv 电视机
* @return 电视机
*/
public TV repair(TV tv);
}
Бизнес по обслуживанию заводов также должен заниматься ими:
public class TVFactory implements TVCompany {
@Override
public TV produceTV() {
System.out.println("TV factory produce TV...");
return new TV("小米电视机","合肥");
}
@Override
public TV repair(TV tv) {
System.out.println("tv is repair finished...");
return new TV("小米电视机","合肥");
}
}
Агент БКомплексное агентство всего бизнеса компании. использоватьProxy.newProxyInstance
Метод генерирует прокси-объект, который реализуетInvocationHandler
серединаinvoke
метод, вinvoke
В методе через отражение вызывается метод прокси-класса и предоставляется метод расширения.
public class TVProxyFactory {
private Object target;
public TVProxyFactory(Object o){
this.target = o;
}
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("TV proxy find factory for tv.... ");
Object invoke = method.invoke(target, args);
return invoke;
}
});
}
}
Покупка и обслуживание этих двух предприятий B代理
Это можно сделать напрямую. Позже компания увеличит свой бизнес, и агент Б тоже может сделать то же самое.
public class TVConsumer {
public static void main(String[] args) {
TVCompany target = new TVFactory();
TVCompany tvCompany = (TVCompany) new TVProxyFactory(target).getProxy();
TV tv = tvCompany.produceTV();
tvCompany.repair(tv);
}
}
Выходной результат:
TV proxy find factory for tv....
TV factory produce TV...
TV proxy find factory for tv....
tv is repair finished...
Process finished with exit code 0
резюме:
-
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。
-
动态代理的方式中,所有的函数调用最终都会经过 invoke 函数的转发,因此我们就可以在这里做一些自己想做的操作,比如日志系统、事务、拦截器、权限控制等。
Одна из самых фатальных проблем с динамическим прокси JDK заключается в том, что он может проксировать только класс реализации, реализующий интерфейс, а класс прокси может проксировать только методы, реализованные в интерфейсе. интерфейс не делает Если это так, метод не может выполнять прокси-вызовы.
Как решить эту проблему? Мы можем использовать механизм динамического прокси CGLIB.
Cglib-прокси
И статические прокси, и прокси JDK требуют объекта для реализации интерфейса. Иногда прокси-объект представляет собой всего один объект. В этом случае можно использовать прокси Cglib.
Прокси Cglib можно назвать прокси подкласса, который создает объект подкласса в памяти для расширения функции целевого объекта.
Агент С хочет представлять не только компанию, но и продукцию нескольких заводов.
Cglib черезEnhancer
для создания прокси-классов путем реализацииMethodInterceptor
интерфейс и реализоватьintercept
метод, в котором можно добавлять методы улучшения и использовать преимущества отраженияMethod
илиMethodProxy
Наследуйте класс для вызова исходного метода.
Видеть
B代理商
Займитесь различными делами компании (интерфейс), то в это времяC代理商
Я также нашел новые возможности для бизнеса.B может представлять только продукцию компании, но я не только хочу представлять продукцию компании, но и связываться с различными фабриками, чтобы я мог получать больше товаров и зарабатывать больше денег. Поэтому используется Cglib.
Пример кода:
public class TVProxyCglib implements MethodInterceptor {
//给目标对象创建一个代理对象
public Object getProxyInstance(Class c){
//1.工具类
Enhancer enhancer = new Enhancer();
//2.设置父类
enhancer.setSuperclass(c);
//3.设置回调函数
enhancer.setCallback(this);
//4.创建子类(代理对象)
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("TVProxyFactory enhancement.....");
Object object = methodProxy.invokeSuper(o, objects);
return object;
}
}
Фабрика B нового агента
public class TVFactoryB {
public TV produceTVB() {
System.out.println("tv factory B producing tv.... ");
return new TV("华为电视机", "南京");
}
public TV repairB(TV tv) {
System.out.println("tv B is repair finished.... ");
return tv;
}
}
C代理
Вы можете работать напрямую с компанией или иметь дело с фабрикой. И может быть агентом продукции любой фабрики.
public class TVConsumer {
public static void main(String[] args) {
TVCompany tvCompany = (TVCompany) new TVProxyCglib().getProxyInstance(TVFactory.class);
TV tv = tvCompany.produceTV();
tvCompany.repair(tv);
System.out.println("==============================");
TVFactoryB tvFactoryB = (TVFactoryB) new TVProxyCglib().getProxyInstance(TVFactoryB.class);
TV tv = tvFactoryB.produceTVB();
tvFactoryB.repairB(tv);
}
}
Выходной результат:
TVProxyFactory enhancement.....
TV factory produce TV...
TVProxyFactory enhancement.....
tv is repair finished...
==============================
TVProxyFactory enhancement.....
tv factory B producing tv....
TVProxyFactory enhancement.....
tv B is repair finished....
Process finished with exit code 0
AOP использует прокси в Spring
В Spring есть две реализации АОП, JDK и Cglib, как показано ниже:
Если целевому объекту необходимо реализовать интерфейс, используется прокси-сервер JDK.
Если целевому объекту не нужно реализовывать интерфейс, используется прокси-сервер Cglib.
Суммировать
-
静态代理
: И прокси-класс, и целевой класс должны реализовать интерфейсный метод, чтобы прокси мог расширить свою функцию. -
JDK动态代理
: Прокси-класс должен реализовать интерфейс, использоватьProxy.newProxyInstance
Метод генерирует прокси-класс и реализуетInvocationHandler
серединаinvoke
способ реализации улучшений. -
Cglib动态代理
: ни один прокси-класс не реализует интерфейс, используйтеCblib
серединаEnhancer
для создания подклассов прокси-объектов и реализацииMethodInterceptor
серединаintercept
метод, в котором могут быть реализованы усовершенствования.
наконец
Я кодер, которого бьют, и я пытаюсь двигаться вперед. Если статья была вам полезна, не забудьте поставить лайк и подписаться, спасибо!