AOP
введение
Spring рожден для развязки, из которых АОП (аспектно-ориентированное программирование) — очень сильный ход. АОП (аспектно-ориентированное программирование) относится к парадигме программирования, основанной на языковой конструкции, называемой аспектом, которая представляет собой новый модульный механизм для описания объектов, рассредоточенных в , сквозных задачах в классе или методе.
Понятия, связанные с АОП
- JoinPoint: программа выполняется до определенной позиции (например: до вызова метода, после вызова метод выдает исключение), класс или фрагмент программного кода имеет определенные точки с граничными свойствами. точка подключения.
- Pointcut (PointCut): если точка соединения эквивалентна записи в данных, то pointcut эквивалентна условию запроса, а pointcut может соответствовать нескольким точкам соединения. Механизм синтаксического анализа правил SpringAop отвечает за синтаксический анализ условий запроса, установленных pointcut, и поиск соответствующей точки подключения.
- Расширение (Совет): Расширение — это фрагмент программного кода, вплетенный в точку подключения целевого класса.Расширенные интерфейсы, предоставляемые Spring, представляют собой все ориентации, такие как BeforeAdvice, AfterReturningAdvice, ThrowsAdvice и т. д. , многие материалы будут усиливать перевод как "уведомление", что явно не является значением этого слова.
Примечание: Advice переведено как "уведомление" во многих отечественных письменных материалах, но этот перевод не может выразить его суть. Существует небольшое количество материалов для чтения, которые переводят слово как "расширенный". Этот перевод является более точной интерпретацией. добавляет в исходную бизнес-логику сквозную функцию интереса, которая представляет собой усовершенствование исходной бизнес-логики. Это усовершенствование может быть до усовершенствования, после усовершенствования, после возврата, усовершенствование при возникновении исключения и окружение улучшение.
- Введение: Введение — это специальное усовершенствование, которое добавляет в класс некоторые свойства и методы, так что даже если бизнес-класс изначально не реализует определенный интерфейс, реализация интерфейса может быть динамически добавлена в бизнес-класс с помощью функции введения Логика , пусть бизнес-класс называется классом реализации этого интерфейса
- Сплетение: Сплетение — это процесс добавления улучшений в определенные точки соединения целевого класса.АОП состоит из трех методов сплетения: 1. Сплетение во время компиляции: требуется специальный период компиляции Java (например, ajc Aspect); 2. Загрузка- переплетение во времени: требует использования специального загрузчика классов для улучшения класса при загрузке класса. 3 Плетение во время выполнения: создает прокси для целевого класса для реализации расширения во время выполнения. Spring использует динамический прокси для реализации переплетения во время выполнения, в то время как AspectJ использует переплетение во время компиляции и во время загрузки.
- Аспект: Аспект состоит из точек пересечения и улучшений (введений), которые включают определение сквозных функций интереса, а также определение точек соединения.
принцип
АОП использует динамические прокси.прокси-режимЭто один из самых классических шаблонов среди 23 шаблонов проектирования, предложенных GoF. Прокси — это структурный шаблон объектов. Он предоставляет объектный прокси для объекта, а прокси управляет ссылкой на исходный объект. Короче говоря, прокси объект может выполнять больше обязанностей, чем исходный объект.Когда вам нужно добавить сквозное внимание к исходному объекту, вы можете использовать прокси-объект исходного объекта. Когда мы открываем документ Word серии Office, если в документе есть иллюстрация, когда документ только что загружен, иллюстрация в документе является просто заполнителем для виртуального фрейма.Когда реальный пользователь переходит на определенную страницу для просмотра изображение, Это изображение на самом деле загружается, что на самом деле является использованием режима прокси. Вместо реального виртуального ящика это виртуальный прокси. Метод загрузки Hibernate также возвращает виртуальный прокси-объект. Когда реальному пользователю необходимо получить доступ к свойства объекта, он отправит базу данных в базу данных.Выполните оператор SQL.
По начальному этапу создания агентов агенты можно разделить на два типа:
- Статический прокси: Исходный код создается программистом или автоматически генерируется специальным инструментом, а затем компилируется.Перед запуском программы файл .class класса прокси уже существует.
- Динамический прокси: динамически создается путем запуска механизма отражения во время работы программы. а такжеДинамический прокси CGLib, который является прокси для реализации класса.
выполнить
JDK-прокси
статический прокси
Ниже используется пример поиска боевика для прохождения теста, чтобы продемонстрировать использование режима прокси.
package com.test.test2.interfaces;
/**
* @Author: Young
* @QQ: 403353323
* @Date: 2019/4/25 10:35
*/
public interface Candidate {
public void answerTheQuestion();
}
package com.test.test2;
import com.test.test2.interfaces.Candidate;
/**
* @Author: Young
* @QQ: 403353323
* @Date: 2019/4/25 10:36
*/
public class LazyStudent implements Candidate {
private String name;
public LazyStudent(String name) {
this.name = name;
}
@Override
public void answerTheQuestion() {
// 懒学生只能写出自己名字不会答题
System.out.println("姓名" + name);
}
}
package com.test.test2;
import com.test.test2.interfaces.Candidate;
/**
* @Author: Young
* @QQ: 403353323
* @Date: 2019/4/25 10:40
*/
public class Gunman implements Candidate {
private Candidate target; //被代理的对象
public Gunman(Candidate target) {
this.target = target;
}
@Override
public void answerTheQuestion() {
// 抢手要写上代考学生的名字
target.answerTheQuestion();
// 抢手帮助懒学生答题并交卷
System.out.println("奋笔疾书书写正确答案");
System.out.println("交卷");
}
}
@Test
public void testGunman(){
Candidate c = new Gunman(new LazyStudent("王小二"));
c.answerTheQuestion();
}
Наблюдая за прокси, вы можете обнаружить, что каждый класс прокси может обслуживать только один интерфейс. Таким образом, при разработке программы неизбежно будет сгенерировано много прокси. Более того, все прокси имеют одинаковые операции, кроме вызывающего метода. это должно быть Повторение кода, лучший способ решить эту проблему - выполнить все прокси-функции через прокси-класс, тогда вы должны использовать динамический прокси.
Динамический прокси
Начиная с JDK 1.3, Java предоставляет динамическую технологию.Байт-код динамического прокси-класса динамически генерируется механизмом отражения Java во время работы программы, без необходимости программистам вручную писать исходный код. Класс динамического прокси не только упрощает работу по программированию, но и улучшает масштабируемость программной системы, поскольку механизм отражения может генерировать любой тип динамического прокси.
java.lang.reflect
Класс Proxy и интерфейс InvocationHandler в пакете предоставляют возможность создавать динамические прокси.
Динамический прокси JDK содержит интерфейс и класс:
- Интерфейс обработчика вызовов
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
Описание параметра:
параметр | иллюстрировать |
---|---|
Object proxy | относится к проксируемому объекту |
Method method | способ вызова |
Object[] args | Параметры в вызове метода |
Думайте о подклассе интерфейса InvocationHandler как о последнем классе действия прокси, заменяющем ProxySubject.
- Класс прокси:
- Класс Proxy — это операционный класс, специализирующийся на проксировании. С помощью этого класса можно динамически генерировать классы реализации для одного или нескольких интерфейсов. Этот класс предоставляет следующие методы работы.
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
* @param h the invocation handler to dispatch method invocations to
параметр | иллюстрировать |
---|---|
ClassLoader | загрузчик классов |
Class<?>[] interfaces | получить все интерфейсы |
InvocationHandler h | Получить экземпляр подкласса интерфейса InvocationHandler |
Ps: класс в проксиnewProxyInstance()
Для этого метода требуется экземпляр класса ClassLoader, а ClassLoader фактически соответствует загрузчику класса. В Java есть три основных загрузчика классов:
- Bootstrap ClassLoader: этот загрузчик портов написан на C++ и не используется в общей разработке.
- Extension ClassLoader: загрузчик, используемый для расширения классов, обычно соответствующий классам в каталоге jre\lib\ext;
- APPClassLoader: (по умолчанию) загружает классы, указанные в пути к классам, который является наиболее часто используемым загрузчиком.
BookFacade.java
public interface BookFacade {
public void addBook();
}
BookFacadeImpl.java
import net.battier.dao.BookFacade;
public class BookFacadeImpl implements BookFacade {
@Override
public void addBook() {
System.out.println("增加图书方法。。。");
}
}
BookFacadeProxy.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理代理类
*/
public class BookFacadeProxy implements InvocationHandler {
private Object target;
/**
* 绑定委托对象并返回一个代理类
* @param target
* @return
*/
public Object bind(Object target) {
this.target = target;
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this); //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
}
@Override
/**
* 调用方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result=null;
System.out.println("事物开始");
//执行方法
result=method.invoke(target, args);
System.out.println("事物结束");
return result;
}
}
TestProxy.java
import net.battier.dao.BookFacade;
import net.battier.dao.impl.BookFacadeImpl;
import net.battier.proxy.BookFacadeProxy;
public class TestProxy {
public static void main(String[] args) {
BookFacadeProxy proxy = new BookFacadeProxy();
BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());
bookProxy.addBook();
}
}
Примечание. Одним из ограничений использования динамического прокси-сервера Java является то, что проксируемый класс должен реализовывать интерфейс (то есть могут проксироваться только методы, определенные в интерфейсе, реализованном классом), хотя программирование, ориентированное на интерфейс, всегда хорошо. Java-программа Однако реальность часто бывает неудовлетворительной Как сгенерировать прокси-класс для прокси-класса, который не реализует интерфейс?наследоватьНаследование — наиболее классический способ расширения возможностей существующего кода. Прокси CGLib делают именно это.
Динамический прокси CGLib
CGLib использует очень низкоуровневую технологию генерации байт-кода для создания прокси-классов путем создания подклассов для класса, что компенсирует недостаток динамических прокси-серверов Java.Поэтому и динамические прокси-серверы, и CGLib в Spring являются важным средством создания прокси-серверов. Класс, реализующий интерфейс, использует динамический прокси для создания для него класса прокси, а класс, который не реализует интерфейс, использует CGLib для создания для него класса прокси посредством наследования. Однако, поскольку используется наследование, окончательно измененный класс не может быть делегирован.
список параметров перехвата:
параметр | иллюстрировать |
---|---|
Object obj | прокси-объект |
Metho method | Указывает способ перехвата |
Object[] args | Массив представляет список параметров, и базовый тип данных должен передаваться в его типе упаковки, таком как int-->Integer, long-Long, double-->Double |
MethodProxy methodProxy | Представляет прокси для метода, а метод invokeSuper представляет вызов метода прокси-объекта. |
package com.test.test2.proxy;
/**
* @Author: Young
* @QQ: 403353323
* @Date: 2019/5/3 21:32
*/
public class BookFacadeImpl1 {
public void addBook(){
System.out.println("增加图书的普通方法");
}
}
package com.test.test2.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* @Author: Young
* @QQ: 403353323
* @Date: 2019/5/3 21:34
*/
/**
* CGLib创建动态代理
*/
public class BookFacadeProxy 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 obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("开始事务");
methodProxy.invokeSuper(obj, args);
System.out.println("事务结束");
return null;
}
}
public class TestCglib {
public static void main(String[] args) {
BookFacadeCglib cglib=new BookFacadeCglib();
BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());
bookCglib.addBook();
}
}
Ссылаться на
Принцип реализации АОП
Резюме и анализ знаний о собеседовании по Java
динамический прокси Java (JDK и cglib)
Cglib и его основное использование