1 Что такое лицом к лицу
Такая идея программирования, которая динамически разрезает код на заданный метод и позицию класса во время выполнения, является аспектно-ориентированным программированием.
АОП — одна из ключевых функций, предоставляемых Spring. АОП — это аспектно-ориентированное программирование, которое является эффективным дополнением к ООП-программированию. Используя технологию АОП, некоторые работы по программированию, связанные с системой, могут быть извлечены независимо, реализованы независимо, а затем включены в систему через аспекты. Это позволяет избежать смешивания большого количества системной логики в коде бизнес-логики, такой как управление разрешениями, управление транзакциями, ведение журналов и т. д. Эти систематические задачи программирования могут быть реализованы с помощью независимого кодирования, а затем включены в систему с помощью технологии АОП. Таким образом достигается эффект разделения различных проблем.
2 термина АОП
2.1 Совет
Работа, которую должен выполнять аспект, называется советом. Уведомления определяют, какие аспекты и когда они полезны.
Существует 5 типов уведомлений, для которых может быть полезен аспект Spring:
- Перед уведомлением (Before): вызовите функцию уведомления до вызова целевого метода;
- Уведомление о публикации (после): Уведомление о вызове после завершения целевого метода, и не важно, каков результат метода в это время;
- После возврата: вызовите уведомление после успешного выполнения целевого метода;
- Уведомление об исключении (после создания): уведомление о вызове после того, как целевой метод выдает исключение;
- Совет вокруг (Around): Совет оборачивает рекомендуемый метод, выполняя настраиваемые действия до и после вызова рекомендованного метода.
2.2 Точка соединения
Наше приложение может иметь тысячи случайных уведомлений. Эти времена называются для точки подключения. Точка соединения — это точка, в которую аспект может быть вставлен во время выполнения приложения. Эта точка может быть при вызове метода, при возникновении исключения или даже при изменении поля. Код аспекта может использовать эти точки для вставки в обычный поток приложения и добавления нового поведения.
2.3 Точечная резка (Poincut)
Pointcut определяет, где врезаться. Pointcut определяется для соответствия одной или нескольким точкам соединения, в которые должен быть вплетен совет. Эти точки обычно указываются с использованием явных имен классов и методов или с использованием определений регулярных выражений для сопоставления имен классов и методов.
2.4 Аспект
Аспекты представляют собой комбинацию советов и точечных сокращений. Вместе советы и подсказки определяют, что собой представляет тот или иной аспект — что это такое, когда и где он выполняет свою функцию.
2.5 Введение
Introduce позволяет нам добавлять новые методы или свойства в существующий класс.
2.6 Ткачество
Плетение — это процесс применения аспектов к целевым объектам и создания новых прокси-объектов. Аспекты вплетаются в целевой объект в указанных точках соединения.
- Время компиляции: Аспекты вплетаются во время компиляции целевого класса. Этот способ требует специального компилятора. Компиляторы AspectJ сплетают аспекты таким образом.
- Время загрузки класса: аспекты вплетаются, когда целевой класс загружается в JVM. Для этого подхода требуется специальный загрузчик классов (ClassLoader), который может улучшить байт-код целевого класса до того, как он будет введен в приложение. Таким образом, технология load-timeweaving (LTW) в AspectJ 5 поддерживает аспекты переплетения.
- Время выполнения: Аспекты переплетаются в какой-то момент во время выполнения приложения. Как правило, при создании аспекта контейнер АОП динамически создает прокси-объект для целевого объекта. Таким образом, Spring AOP вплетен в аспекты.
3 Spring поддерживает аспекты
Spring предоставляет 4 типа поддержки АОП:
- классический Spring AOP на основе прокси;
- Чистый аспект POJO;
- аспекты, управляемые аннотациями @AspectJ;
- Внедряемые аспекты AspectJ (применимы ко всем версиям Spring).
Первые три — это все варианты реализации Spring AOP, Spring AOP построен на основе динамического прокси, поэтому поддержка Spring для AOP ограничена перехватом методов.
4 Знайте точку среза
В Spring AOP pointcut определяются с использованием языка выражений pointcut AspectJ.
Сначала определите интерфейс как pointcut:
public interface Performance {
void perform();
}
Предположим, мы хотим написать подпрограмму, запускаемую методом Perform() класса Performance. Знать. Следующее выражение задает вызов, который инициирует уведомление при выполнении метода Perform().
execution(* com.wtj.springlearn.aop.Performance.perform(..))
Индикатор выполнения () выбирает метод выполнения () производительности. Выражение метода начинается со знака «*», указывающего на то, что ему не важен тип возвращаемого значения метода. Затем указываются полное имя класса и имя метода. Для списка параметров метода используйте две точки (..), чтобы указать, что pointcut выбирает любой метод Perform() независимо от входных параметров метода.
Если нам нужно установить pointcuts в соответствии с пакетом com.wtj.springlearn.aop, мы можем использовать inside() для ограничения соответствия.
execution(* com.wtj.springlearn.aop.Performance.perform(..)) && within(com.wtj.springlearn.aop.*)
Указывает, когда вызывается метод любого класса в пакете com.wtj.springlearn.aop.
Используйте оператор «&&», чтобы соединить индикаторы исполнения () и внутри () вместе, чтобы сформировать отношение И (pointcut должен соответствовать всем индикаторам). Точно так же мы можем использовать оператор «||» для определения отношения или (или) и оператор «!» для обозначения операции «не» (не).
Поскольку «&» имеет особое значение в XML, мы можем использовать and вместо «&&» при описании точечных сокращений в XML-конфигурации Spring. Аналогично, or и not можно использовать вместо «||» и «!» соответственно.
Вы также можете использовать идентификатор компонента для идентификации компонента. bean() принимает идентификатор компонента или имя компонента в качестве аргумента, чтобы ограничить pointcuts для соответствия только определенным компонентам.
execution(* com.wtj.springlearn.aop.Performance.perform(..)) && bean('book')
Это означает, что уведомление выполняется при выполнении метода execute, но оно ограничено компонентом, идентификатор которого равен book.
5 Создание аспектов с аннотациями
В этой статье в основном представлен метод определения аспекта метода аннотации.
Аннотируется @Aspect, указывающим, что аудитория является не только POJO, но и аспектом. Методы класса представляют конкретное поведение аспекта.
Spring предоставляет пять аннотаций для определения времени уведомления:
Сначала создайте срез:
@Aspect
public class Audience {
//表演前 手机静音
@Before("execution(* com.wtj.springlearn.aop.Performance.perform(..))")
public void silenceCellPhone(){
System.out.println("silence Cell Phone");
}
//表演成功-clap
@AfterReturning("execution(** com.wtj.springlearn.aop.Performance.perform(..))")
public void clap(){
System.out.println("clap clap clap");
}
//表演失败-退款
@AfterThrowing("execution(** com.wtj.springlearn.aop.Performance.perform(..))")
public void refund(){
System.out.println("refund refund refund");
}
}
Класс реализации производительности:
@Component
public class PerformanceImpl implements Performance {
public void perform() {
System.out.println("the perform is good");
}
}
Наконец, вам нужно включить функцию автоматического прокси., настроенный через JavaConfig, используя@EnableAspectJAutoProxy
Вкладка включена.
@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class AudienceConfig {
@Bean
public Audience audience(){
return new Audience();
}
}
Наконец, для проверки можно использовать простой тестовый пример.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AudienceConfig.class)
public class PerformanceTest {
@Autowired
private Performance performance;
@Test
public void perTest(){
performance.perform();
}
}
распечатать результат:
silence Cell Phone
the perform is good
clap clap clap
5.1 @PointCut объявляет pointcut
Вы обнаружите, что в методе вышеупомянутого аспекта объявление pointcut такое же, в этом случае вы можете использовать@Pointcut
Аннотации для определения точечных разрезов.
@Pointcut("execution(* com.wtj.springlearn.aop.Performance.perform(..))")
public void per(){};
//表演前 手机静音
@Before("per()")
public void silenceCellPhone(){
System.out.println("silence Cell Phone");
}
Сам метод per() не важен, этот метод является просто идентификатором для присоединения аннотации @PointCut.
5.2 Окружающие уведомления
Объемные уведомления — самый мощный тип уведомлений. Это позволяет логике, которую вы пишете, полностью обернуть целевой метод. На самом деле это похоже на написание как предварительного уведомления, так и пост-уведомления в одном методе консультации.
Перепишите аспект Аудитория, чтобы использовать циклический совет вместо нескольких различных предварительных советов и пост-советов.
@Around("per()")
public void watch(ProceedingJoinPoint point) throws Throwable {
try{
System.out.println("silence Cell Phone");
point.proceed();
System.out.println("clap clap clap");
}catch (Exception e){
System.out.println("refund refund refund");
}
}
Вероятно, первое, на что следует обратить внимание, это то, что он принимает ProceedingJoinPoint в качестве параметра. Этот объект необходим, поскольку вы используете его для вызова метода уведомления в уведомлении. В методе уведомления можно сделать все что угодно.Когда он хочет передать управление методу уведомления, ему нужно вызвать метод continue() ProceedingJoinPoint.
Если вы не вызовете метод continue(), то ваш совет фактически заблокирует вызов уведомленного метода. Точно так же вы можете вызывать его несколько раз.
5.3 Передача параметров в уведомление
Срезы, которые мы создали выше, все простые, без каких-либо параметров. Так могут ли аспекты получать доступ и использовать параметры, переданные в рекомендуемый метод?
Новые методы в производительности:
void perform(String name);
Класс реализации:
public void perform(String name) {
System.out.println("下面请 "+name+" 开始他的表演");
}
Изменение точек касания и плоскостей касания в Audience
@Pointcut("execution(* com.wtj.springlearn.aop.Performance.perform(String)) && args(name)")
public void per(String name){};
@Around("per(name)")
public void toWatch(ProceedingJoinPoint point,String name) throws Throwable {
try{
point.proceed();
System.out.println(name +" 上场啦");
System.out.println(name +" 演出结束");
}catch (Exception e){
System.out.println("refund refund refund");
}
}
выражениеargs(name)
Квалификатор, указывающий, что параметр типа String, переданный методу execute(String name), также будет передан в уведомление, а имя параметра совпадает с именем параметра в pointcut.perform(String)
Указывает тип входящего параметра.
затем в@Around
Pointcut и имя параметра указываются в аннотации, что завершает передачу параметра.
Наконец, измените тестовый пример, и все готово.
@Test
public void perTest(){
performance.perform("渣渣辉");
}
распечатать:
下面请 渣渣辉 开始他的表演
渣渣辉 上场啦
渣渣辉 演出结束
5.4 Введение новых методов путем аннотирования @DeclareParents
Если мы хотим добавить новый метод в класс, что мы обычно делаем? Самый простой способ — добавить этот метод к этому целевому классу, но если исходный целевой класс очень сложен, он пострадает. А иногда у нас нет исходного кода целевого класса, что нам делать в это время?
Мы можем создать класс для метода, который необходимо добавить, а затем создать прокси-класс, который проксирует как класс, так и целевой класс. представлен диаграммой
Когда вызывается метод введенного интерфейса, прокси делегирует вызов какому-то другому объекту, реализующему новый интерфейс.
Снова в приведенном выше примере, допустим, нам нужно заставить исполнителя прыгать.
Создайте новый интерфейс Jump и класс реализации:
public interface Jump {
void duJump();
}
public class JumpImpl implements Jump {
public void duJump() {
System.out.println("do Jump");
}
}
Затем мы проксируем два класса:
@Aspect
public class JumpIntroducer {
@DeclareParents(value = "com.wtj.springlearn.aop.Performance+",defaultImpl = JumpImpl.class)
public static Jump jump;
}
Аннотация @DeclareParents состоит из трех частей:
- Атрибут value указывает, какой тип bean-компонента импортировать интерфейс. В этом случае все типы, реализующие Performance. (Знак плюс после тега указывает на все подтипы производительности, а не на саму производительность. тело. )
- Атрибут defaultImpl указывает класс, обеспечивающий реализацию импортированных функций.
- Статическое свойство, аннотированное аннотацией @DeclareParents, указывает, что соединение должно быть введено. рот.
Объявление JumpIntroducer через конфигурацию
@ComponentScan
@Configuration
@EnableAspectJAutoProxy
public class JumpConfig {
@Bean
public JumpIntroducer jumpIntroducer(){
return new JumpIntroducer();
}
}
Или вы также можете добавить класс JumpIntroducer@Component
Аннотация, вы не можете объявить bean-компонент.
Наконец, проверьте с тестовыми примерами:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = JumpConfig.class)
public class PerformanceTest {
@Autowired
private Performance performance;
@Test
public void perTest(){
//类型转换
Jump jump = (Jump) performance;
jump.duJump();
}
}
распечатать результат:
do Jump