Есть чувства, есть галантерейные товары, поиск в WeChat【Третий принц Ао Бин] Подпишитесь на этого программиста, у которого есть кое-что.
эта статьяGitHub github.com/JavaFamilyВключено, и есть полные тестовые площадки, материалы и мой цикл статей для интервью с производителями первой линии.
Шаблоны проектирования поделились с вами многими общими шаблонами. Заинтересованные партнеры могут просмотреть их еще раз, чтобы закрепить свое понимание.
На этот раз я хочу поделиться с вами тремя типами создания в шаблоне проектирования.прокси-режим, агентская модель может не часто использоваться в бизнес-сценариях, но интервьюер часто задает вопрос?
Не могли бы вы рассказать мне о прокси-режиме АОП в Spring? В чем разница между прокси-режимом jdk и прокси-режимом cglib?
Учащиеся, у которых есть ясность и неясность, могут продолжать смотреть вниз, и это принесет пользу.
Ближе к делу, давайте начнем поэтапно анализировать модель агентства.
Определение и цель
Во-первых, режим прокси можно разделить на различные типы.
- Удаленный прокси: делегирование работы удаленному объекту (другому серверу или другому процессу) для завершения. Обычно используется в веб-сервисах. Там же наш вызов RPC тоже можно понимать как удаленный прокси.
- Агент защиты: в этом режиме в основном выполняются проверки безопасности/разрешений. (маленький контакт)
- Кеширование прокси: Это понятно, что для ускорения вызова через хранилище.Например, метод @Cacheable в Sping кэширует результаты, полученные по определенным параметрам.При следующем вызове метода с теми же параметрами данные возвращается непосредственно из кэша.
- Виртуальный агент: Этот тип агента в основном добавляет функции к методу, такие как запись некоторых показателей производительности и т. д., или задержка инициализации.
Вышеизложенное — это просто концепция, которую мы понимаем, а затем давайте посмотрим, из каких частей состоит режим прокси.
- Тема (общий интерфейс): существующий интерфейс, используемый клиентом.
- RealSubject: класс реального объекта
- ProxySubject: класс прокси
Как видно из рисунка, весь интерфейс по-прежнему очень простой, то есть реальный объект и прокси-объект.
Цель: Предоставить фактический прокси-объект для лучшего контроля над фактическим объектом. Приведенное выше определение взято из книги «Красота шаблонов проектирования».
реализация примера кода
Чтобы облегчить понимание, давайте рассмотрим пример. Я не знаю, сталкивались ли вы с процессом передачи небольших заметок в средней или старшей школе. Если однокласснику А есть о чем поговорить с одноклассником С (например, встреча после в школу, чтобы играть в игры), но поскольку сейчас во время урока, я не могу сказать это вслух, между одноклассником A и одноклассником C сидит ученик B, так что теперь одноклассник A может сначала найти одноклассника B и дать ему обратите внимание и попросите его рассказать об этом однокласснику С, но играть все же можно, играть, это может решить только сам настоящий одноклассник С.
Следовательно, агентский режим можно понимать так: одноклассник B является агентом одноклассника C. Если одноклассник A хочет найти одноклассника C, можно найти только одноклассника B, и одноклассник C будет передан однокласснику C через одноклассника B, и выполнение результат одноклассника C будет возвращен однокласснику A.
После разговора о примере давайте взглянем на реализацию кода.
public interface Subject {
// 共同的接口
void doSomething();
}
Определите общий интерфейс (то есть то, что все должны делать, пожалуйста: вместе играть в игры после школы)
public class RealSubject implements Subject {
// 真实对象
@Override
public void doSomething() {
System.out.println("放学去打游戏");
}
}
Построить реальный объект, одноклассник C в примере
public class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
public ProxySubject() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
this.realSubject = (RealSubject) this.getClass().getClassLoader().loadClass("com.ao.bing.demo.proxyPattern.RealSubject").newInstance();
}
@Override
public void doSomething() {
realSubject.doSomething();
}
public static void main(String[] args) {
try {
// 第一种方式
new ProxySubject().doSomething();
// 打印结果: 放学去打游戏
} catch (Exception e) {
// 异常情况,代理失败,
// 传纸条的被老师抓了。或者同学C不在座位上了 等异常情况
}
// 第二种方式
new ProxySubject(new RealSubject()).doSomething();
// 打印结果: 放学去打游戏
}
}
Создайте прокси-объект, то есть одноклассник B, тогда вы увидите, что одноклассник A на самом деле не контактировал с одноклассником C. Через прокси одноклассника B к однокласснику C вы можете узнать, может ли одноклассник C играть с ним в игры после школы.
В методе Main есть два способа вызова реального объекта
Первый: используйте форму загрузчика классов для загрузки реального объекта столбца, чтобы нас не заботило, когда нужен реальный объект реального столбца.
Второй: передать сериализованный объект в виде передачи по значению. (понимается как шаблон декоратора)
Здесь мы должны различать: режим прокси должен предоставлять точно такой же интерфейс, а режим декоратора — улучшать интерфейс.
Статический прокси, динамический прокси и анализ прокси cglib
статический прокси
Реализация приведенного выше примера на самом деле представляет собой статический прокси, и вы можете видеть, что все относительно просто. Но и его недостатки очевидны
Статические прокси требуют создания прокси-класса для каждого объекта, что увеличивает затраты на обслуживание и затраты на разработку.Для решения этой проблемы выходят динамические прокси.Не создавайте прокси-класс для каждого класса, которому нужны прокси.
Динамический прокси
Динамические прокси-серверы разумно избегают статических прокси-серверов, не создавая заранее классы прокси для классов, которые будут проксироваться. Скорее, он создается во время выполнения с помощью механизмов отражения.
При написании динамического прокси необходимо понимать две вещи: прокси можно понимать как планировщик, а интерфейс расширенной службы InvocationHandler можно понимать как прокси. Так что я лично понимаю, что динамический прокси на самом деле является своего рода мониторингом поведения.
Пример конкретной реализации кода: богомол ловит цикад, а за движениями цикады следит богомол. О многоуровневом прокси-режиме удобно поговорить позже.
public interface BaseService {
void mainService();
}
public class Cicada implements BaseService {
@Override
public void mainService() {
System.out.println("主要业务,以蝉为例,当蝉出现业务调用时,螳螂监听到");
}
}
Создавайте общие интерфейсы, а также настоящие объектные цикады
public class PrayingMantis implements InvocationHandler {
private BaseService baseService;
// 这里采用的是构建传参数,可以用反射,举的第一个例子有样式代码
public PrayingMantis(BaseService baseService) {
this.baseService = baseService;
}
// 螳螂主要业务,也就是监听对象
@Override
public Object invoke(Object listener, Method method, Object[] args) throws Throwable {
method.invoke(baseService,args);
secondaryMain();
return null;
}
// 这里理解增强业务,即我们可以在实现InvocationHandler里面添加其他的业务,比如日志等等。
private void secondaryMain(){
System.out.println("螳螂捕蝉 - 次要业务");
}
}
Создайте класс богомола, который слушает действия класса цикады.
public class BeanFactory {
public static BaseService newInstanc(Class classFile) {
// 1. 创建蝉,真实类对象
BaseService trueCicada = new Cicada();
// 2.创建代理类 螳螂
InvocationHandler prayingMantis = new PrayingMantis(trueCicada);
// 3.向Jvm索要代理对象 其实就是监听的对象,
Class classArray[] = {BaseService.class};
BaseService baseService = (BaseService) Proxy.newProxyInstance(classFile.getClassLoader(), classArray, prayingMantis);
return baseService;
}
// 测试Demo
public static void main(String[] args) {
BaseService baseService = newInstanc(Cicada.class);
baseService.mainService();
// 测试结果 :主要业务
// 螳螂捕蝉 - 次要业务
}
}
Из результатов видно, что когда вызывается основной бизнес Cicada, Mantis может отслеживать бизнес Cicada и обрабатывать другую бизнес-логику, поэтому AOP в Spring может обрабатывать аспекты журнала и так далее.
Характер прокси:
Я думаю, что на самом деле это своего рода мониторинг поведения, мониторинг поведения прокси-объекта ($proxy InvocationHandler).
Состав режима прокси:
- Интерфейс: объявляет поведение, которое необходимо отслеживать
- Класс реализации прокси (InvocationHandler): вторичный бизнес, вторичный бизнес и выполнение основной бизнес-привязки
- Прокси-объект (объект прослушивания)
Cglib динамический прокси
Динамический прокси cglib на самом деле очень похож на динамический прокси jdk, и все это делается путем реализации интерфейса прокси.
Конкретный код выглядит следующим образом:
public class PrayingMantis implements MethodInterceptor {
private Cicada cicada;// 代理对象
public Cicada getInstance(Cicada cicada) {
this.cicada = cicada;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.cicada.getClass());
enhancer.setCallback(this);
return (Cicada) enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object object = methodProxy.invokeSuper(o, objects);
secondaryMain();
return object;
}
private void secondaryMain() {
System.out.println("螳螂捕蝉 - 次要业务");
}
public static void main(String[] args) {
PrayingMantis prayingMantis = new PrayingMantis();
Cicada instance = prayingMantis.getInstance(new Cicada());
instance.mainService();
// 结果:主要业务
// 螳螂捕蝉 - 次要业务
}
Поскольку цикады все одинаковые, я не буду размещать их здесь по отдельности.
Внимательные студенты обнаружили, что Cglib не нужно реализовывать через интерфейс, он вызывается путем реализации подкласса.
Объект Enhancer устанавливает прокси-объект как подкласс прокси-класса для реализации динамического проксирования. Поскольку принят метод наследования, прокси-класс не может быть финализирован, иначе будет сообщено об ошибке.
Конечный класс: класс не может быть унаследован, а внутренние методы и переменные становятся конечными типами.
Различия между JDK и Cglib:
Динамический прокси jdk использует механизм отражения для создания анонимного класса, реализующего интерфейс прокси, и вызывает InvokeHandler для его обработки перед вызовом конкретного метода.
Динамический прокси-сервер cglib использует пакет с открытым исходным кодом ASM, загружает файл класса класса прокси-объекта и создает подкласс, изменяя его байт-код для его обработки.
ASM: среда обработки байт-кода Java. Его можно использовать для динамического создания классов или для улучшения функциональности существующих классов. ASM может напрямую генерировать бинарные файлы классов или динамически изменять поведение классов перед их загрузкой в виртуальную машину Java. Классы Java хранятся в строго отформатированных файлах .class, метаданных которых достаточно для разрешения всех элементов класса: имени класса, методов, свойств и байт-кода Java (инструкций). После того, как ASM прочитает информацию из файлов классов, он может изменить поведение класса, проанализировать информацию о классе и даже создать новые классы в соответствии с требованиями пользователя. -- Приведенное выше объяснение ASM исходит от Цзяньшу.
Многоуровневый динамический прокси
После прочтения динамического прокси выше, мне интересно, есть ли у вас идеи по реализации многоуровневого динамического прокси.
Возьмем в качестве примера богомола, ловящего цикад, и добавим иволгу позади, чтобы реализовать многоуровневый динамический режим агента.
public class Cardinal implements InvocationHandler {
// 监听代理代理对象
private Object proxyOne;
public Cardinal(Object proxyOne) {
this.proxyOne = proxyOne;
}
// 螳螂主要业务,也就是监听对象
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(proxyOne, args);
secondaryMain();
return null;
}
private void secondaryMain() {
System.out.println("黄雀吃螳螂 - 次要业务");
}
}
Создайте прокси-объект иволги, затем, как его реальный объект, он станет богомолом.Когда вызывается объект богомола, иволга может стоять твердо и в то же время выполнять соответствующую бизнес-логику.
public class BeanFactory {
public static BaseService newInstanc(Class classFile) {
// 1. 创建蝉,真实类对象
BaseService trueCicada = new Cicada();
// 2.创建代理类 螳螂
InvocationHandler prayingMantis = new PrayingMantis(trueCicada);
// 3.向Jvm索要代理对象 其实就是坚挺的对象
Class classArray[] = {BaseService.class};
BaseService baseService = (BaseService) Proxy.newProxyInstance(classFile.getClassLoader(), classArray, prayingMantis);
// 4.创建代理实现类 黄雀 二级代理
InvocationHandler cardinal = new Cardinal(baseService);
BaseService secondBaseService = (BaseService) Proxy.newProxyInstance(classFile.getClassLoader(), classArray, cardinal);
// 假设要实现三级,四级代理,则在黄雀类上再加一层代理即可实现。
// 省略其他的更多级代理对象
return secondBaseService;
}
// 测试demo
public static void main(String[] args) {
BaseService baseService = BeanFactory.newInstanc(Cicada.class);
baseService.mainService();
// 结果:主要业务
// 螳螂捕蝉 - 次要业务
// 黄雀吃螳螂 - 次要业务
}
}
По этому коду в основном реализуется многоуровневый прокси-процесс. Богомол следит за движениями цикад, а иволга следит за движениями богомолов.
Точно так же, если вы хотите реализовать трехуровневый прокси, четырехуровневый прокси не представляет сложности, просто добавьте прокси-объект поверх каждого слоя.
Суть динамического прокси по-прежнему можно понимать как отделение «второстепенного бизнеса» от «основного бизнеса», что позволяет разработчикам больше сосредоточиться на основном бизнесе, повышая эффективность разработки и затраты на обслуживание.
Суммировать
Лично я считаю, что режим прокси относительно редко встречается в бизнес-коде, особенно динамический прокси практически никогда не встречается. Но прокси-режим также является режимом, который мы должны понимать, потому что хорошее изучение прокси-режима помогает нам читать исходный код, устранять некоторые более глубокие проблемы или сталкиваться с некоторыми проблемами бизнес-сценариев, он также может иметь большое улучшение, сами шаблоны проектирования. создаются для решения проблем.
После понимания динамического прокси для нас самоочевидно понять принцип АОП.
Подробные шаблоны проектирования здесь, я дам вам краткое изложение некоторых необычных шаблонов позже.
Я Ао Бин,Чем больше вы знаете, тем больше вы не знаете, спасибо за ваши таланты:как,собиратьиКомментарий, увидимся в следующий раз!
Статья постоянно обновляется, вы можете искать в WeChat "Третий принц Ао Бин"Прочтите это в первый раз, ответьте [материал] Подготовленные мной материалы интервью и шаблоны резюме крупных заводов первой линии, эта статьяGitHub github.com/JavaFamilyОн был включен, и есть полные тестовые сайты для интервью с крупными заводами.Добро пожаловать в Star.