предисловие
Прокси-режим — этоструктурныйШаблоны проектирования, обеспечивающие дополнительный доступ к целевому объекту;即通过代理对象访问目标对象。
Преимущество этого в том, что дополнительные функциональные операции могут быть расширены на основе реализации целевого объекта, то есть функция целевого объекта может быть расширена.
Здесь используется идея в программировании: не модифицируйте код или методы, которые написали другие, по своему желанию.Если вам нужно это изменить, вы можете расширить метод по доверенности.
В режиме прокси есть примерно три роли:
-
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метод, в котором могут быть реализованы усовершенствования.
наконец
Я кодер, которого бьют, и я пытаюсь двигаться вперед. Если статья была вам полезна, не забудьте поставить лайк и подписаться, спасибо!