Боевая серия Spring Boot (3) Аспектно-ориентированное программирование АОП

Spring Boot задняя часть MySQL GitHub

АОП — это независимая от языка идея программирования,парадигма программирования. В бизнес-логике проекта общие модули разделены и обрабатываются горизонтально и часто используются в бизнесе, таком как ведение журнала, контроль разрешений и обработка исключений.

Быстрая навигация

Парадигмы программирования в основном делятся на следующие категории:

  • АОП (аспектно-ориентированное программирование) Аспектно-ориентированное программирование
  • ООП (объектно-ориентированное программирование) объектно-ориентированное программирование
  • POP (процедурно-ориентированное программирование)
  • FP (Functional Programming) — функциональное программирование.

Внедрение зависимостей aop

Следующий пример основан наБоевая серия Spring Boot (2) Работа Jpa с хранилищем данных MySQL глава 2-1Исходный код доступен на Github

корневой каталог проектаpom.xmlдобавить зависимостиspring-boot-starter-aop

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

аннотация аоп

  • @Aspect: Аспект, который состоит из совета и точки, эта аннотация представлена ​​как аспект в классе.
  • @Joinpoint: точка соединения, класс или метод, перехваченный АОП, вводится и используется в предварительном уведомлении.@JoinpointПолучите имя класса, метод и параметры запроса.
  • Advice: Несколько типов уведомлений
    • @Before: предварительное уведомление, в точку@Pointcutпредварительное уведомление
    • @After: Опубликовать уведомление, в pointcut@PointcutПоследующие уведомления либо успешны, либо ненормальны.
    • @AfterReturning: Уведомление после возврата, после того как метод выполнит возврат, возвращенные данные могут быть обработаны.
    • @Around: Surround-совет, выполняемый до и после вызова метода.
    • @AfterThrowing: выдается уведомление об исключении, метод уведомления будет выполнен, если программа выйдет из строя и возникнет исключение.
  • @Pointcut: точка входа, с чего начать. Например, начиная с пакета или класса под пакетом и т. д.

Реализовать разделение журнала

содержаниеaspectпод НовыйhttpAspect.javaКласс, после получения запроса, сначала запишите соответствующую информацию журнала параметров запроса, распечатайте информацию ответа после успешного выполнения запроса и распечатайте информацию журнала ошибок для ошибки обработки запроса.

httpAspect.java

package com.angelo.aspect;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
public class HttpAspect {
    // 打印日志模块
    private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);

    // 下面会一一介绍... 

Добавить точку

Определите, где находится точка входа, и инкапсулируйте общедоступный метод для повторного использования.

httpAspect.java

    /**
     *  定义一个公共的方法,实现服用
     *  拦截UserController下面的所有方法
     *  拦截UserController下面的userList方法里的任何参数(..表示拦截任何参数)写法:@Before("execution(public * com.angelo.controller.UserController.userList(..))")
     */
    @Pointcut("execution(public * com.angelo.controller.UserController.*(..))")
    public void log() {
    }

предварительное уведомление

Часть бизнес-логики перед методом перехвата для получения некоторой информации о запросе, который используетсяGsonОбработка объекта для вывода json

httpAspect.java

@Before("log()")
public void doBefore(JoinPoint joinPoint) {
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();

    Map params = new HashMap();
    params.put("url", request.getRequestURL()); // 获取请求的url
    params.put("method", request.getMethod()); // 获取请求的方式
    params.put("ip", request.getRemoteAddr()); // 获取请求的ip地址
    params.put("className", joinPoint.getSignature().getDeclaringTypeName()); // 获取类名
    params.put("classMethod", joinPoint.getSignature().getName()); // 获取类方法
    params.put("args", joinPoint.getArgs()); // 请求参数

    // 输出格式化后的json字符串
    Gson gson = new GsonBuilder().setPrettyPrinting().create();

    logger.info("REQUEST: {}", gson.toJson(params));
}

опубликовать уведомление

Кусок бизнес-логики после метода перехвата

httpAspect.java

@After("log()")
public void doAfter() {
    logger.info("doAfter");
}

Объемное уведомление

Совет по окружению — это логическая операция до и после метода, которая может изменить возвращаемое значение целевого метода.org.aspectj.lang.ProceedingJoinPointТип, обратите внимание, что здесь вызывается метод цели выполненияproceed()Получите значение и верните его, иначе это вызовет исключение нулевого указателя. Возвраты ошибок также могут быть перехвачены внутри циклического уведомления.

httpAspect.java

@Around("log()")
public Object doAround(ProceedingJoinPoint point) {
    try {
        Object o =  point.proceed();
        System.out.println("方法环绕proceed,结果是 :" + o);
        logger.info("doAround1");

        return o;
    } catch (Throwable e) {
        // e.printStackTrace();
        logger.info("doAround2");

        return null;
    }
}

Сообщите, когда вы вернетесь

В уведомлении о возврате после завершения pointcut уведомление об исключении не будет выдано в это время, если только бизнес-логика уведомления после возврата не сообщит об ошибке.

httpAspect.java

    /**
     * 获取响应返回值
     * @param object
     */
    @AfterReturning(returning = "object", pointcut = "log()")
    public void doAfterReturning(Object object) {
        // logger.info("RESPONSE: {}", object); 会打印出一个对象,想打印出具体内容需要在定义模型处加上toString()

        logger.info("RESPONSE: {}", object.toString());
    }

уведомление об исключении

Уведомление после создания исключения и уведомление после возврата в это время@AfterReturningне будет выполняться.

httpAspect.java

@AfterThrowing(pointcut = "log()")
public void doAfterThrowing() {
    logger.error("doAfterThrowing: {}", " 异常情况!");
}

Кусок псевдокода для чтения порядка выполнения

try {
    // @Before 执行前通知

    // 执行目标方法

    // @Around 执行环绕通知 成功走finall,失败走catch
} finally {
    // @After 执行后置通知

    // @AfterReturning 执行返回后通知
} catch(e) {
    // @AfterThrowing 抛出异常通知
}

Тест на два случая нормального и ненормального

Перед тестированиемcontroller/UserController.javaдокументuserListдобавлен методexceptionпараметр

    /**
     * 查询用户列表
     * @return
     */
    @RequestMapping(value = "/user/list/{exception}")
    public List<User> userList(@PathVariable("exception") Boolean exception) {
        if (exception) {
            throw new Error("测试抛出异常!");
        }

        return userRepository.findAll();
    }
  • нормальный тест

curl 127.0.0.1:8080/user/list/false

Нормальное возвращаемое значение выглядит следующим образом:

  • Тест на исключения

curl 127.0.0.1:8080/user/list/true

Возвращаемые значения исключения следующие:

图片描述

В двух приведенных выше тестовых примерах мы видим, что объемное уведомление может выполняться как в нормальных, так и в нештатных ситуациях.

Посмотреть полный пример этой статьи на Github, глава 3-1.

Автор: Мэй Джун
Ссылка на сайт:Ууууу. ИМО OC.com/article/259…
Источник: МООК
Github: Боевая серия Spring Boot