Spring Boot 2.X (8): Spring AOP реализует простой аспект ведения журнала.

Spring Boot

AOP

1. Что такое АОП?

Полное название АОП — Aspect Oriented Programming, что переводится как аспектно-ориентированное программирование — это технология, которая реализует унифицированное сопровождение сквозных поведений вне основной бизнес-логики посредством прекомпиляции и динамических агентов времени выполнения. АОП является дополнением и расширением объектно-ориентированного программирования (ООП). Использование АОП может изолировать каждую часть бизнес-логики, чтобы уменьшить связь между модулями, и инкапсулировать эти общие поведения, влияющие на несколько классов, в повторно используемый модуль, чтобы улучшить возможность повторного использования программы и улучшить эффективность разработки. работоспособность и ремонтопригодность системы.

2. Зачем использовать АОП?

При разработке реального веб-проекта нам часто требуется реализовать ведение журнала, статистику производительности, контроль безопасности, обработку транзакций, обработку исключений и другие функции на всех уровнях. Если мы напишем эту часть кода независимо для каждого класса на каждом уровне, со временем код станет сложно поддерживать, поэтому мы отделяем эти функции от кода бизнес-логики и объединяем их вместе для обслуживания, и мы можем гибко выбирать, где используйте эти коды.

3. Основные концепции АОП

имя существительное концепция понимать
Совет Код, который будет выполняться после перехвата точки подключения.Уведомления делятся на пять категорий: предварительные, пост-, исключительные, окончательные и объемные уведомления. Функции, которые мы хотим реализовать, такие как ведение журнала, статистика производительности, элементы управления безопасностью, обработка транзакций, обработка исключений и т. д., объясняют, что делать, когда
Совместная точка Перехваченные точки, такие как перехваченные методы, доступ к членам класса и выполнение блоков обработчика исключений, также могут сами вкладываться в другие точки соединения. Spring позволяет использовать локальные уведомления, методы, связанные с входами и выходами (включая генерацию исключений)
Точечная резка Определение перехватывающих точек соединения Укажите, каким способом оповещать, и объясните, где это сделать
Аспект Определение класса аспекта, которое содержит определение точки (Pointcut) и совет (Advice) Аспект представляет собой комбинацию совета и точки.
Целевой объект Объект, выбранный pointcut, то есть объект, который необходимо уведомить; поскольку Spring AOP реализован через режим прокси, объект всегда является проксируемым объектом сама бизнес-логика
Ткачество Процесс применения аспекта к целевому объекту для создания прокси-объекта АОП. Плетение может быть выполнено во время компиляции, во время загрузки класса и во время выполнения, в то время как Spring принимает его во время выполнения. Pointcuts определяют, какие точки соединения будут уведомлены
Введение Методы и поля можно динамически добавлять в классы во время выполнения, а Spring позволяет вводить новые интерфейсы для всех целевых объектов. Введение - это введение новых улучшений интерфейса на основе интерфейса/класса.
Прокси АОП (прокси АОП) Spring AOP может использовать динамический прокси-сервер JDK или прокси-сервер CGLIB, первый основан на интерфейсе, второй основан на классе. Применение аспектов к целевым объектам через прокси

Spring AOP

1. Введение

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

2. Связанное понимание

аннотация инструкция
@Aspect Определите класс Java как класс аспекта
@Pointcut Определите pointcut, который может быть регулярным выражением, таким как все функции в пакете в следующем примере, или аннотацией и т. д.
@Before Вырезать содержимое в начале pointcut
@After Вырезать содержимое в конце pointcut
@AfterReturning Обработка логики после возврата содержимого pointcut
@Around Вырезать содержимое до и после pointcut и контролировать, когда выполняется содержимое самого pointcut
@AfterThrowing Используется для обработки логики обработки при возникновении исключения из раздела содержимого.
@Order(100) Порядок выполнения АОП-аспектов: чем меньше значение @Before, тем выполняется первым, и чем больше значение @After и @AfterReturning, тем первым выполняется.

Среди них @Before, @After, @AfterReturning, @Around и @AfterThrowing — это советы.

Обработка веб-журнала с помощью AOP

1. Создайте проект

2. Добавьте зависимости

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- 热部署模块 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional> <!-- 这个需要为 true 热部署才有效 -->
		</dependency>
		<!-- Spring AOP -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
	</dependencies>

3. Аннотации веб-журнала

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ControllerWebLog {
	 String name();//所调用接口的名称
     boolean intoDb() default false;//标识该条操作日志是否需要持久化存储
}

4. Реализуйте логику аспектов

@Aspect
@Component
@Order(100)
public class WebLogAspect {

	private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);

	private ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<Map<String, Object>>();

	/**
	 * 横切点
	 */
	@Pointcut("execution(public * cn.zwqh.springboot.controller..*.*(..))")
	public void webLog() {
	}
	/**
	 * 接收请求,并记录数据
	 * @param joinPoint
	 * @param controllerWebLog
	 */
	@Before(value = "webLog()&& @annotation(controllerWebLog)")
	public void doBefore(JoinPoint joinPoint, ControllerWebLog controllerWebLog) {
		// 接收到请求
		RequestAttributes ra = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes sra = (ServletRequestAttributes) ra;
		HttpServletRequest request = sra.getRequest();
		// 记录请求内容,threadInfo存储所有内容
		Map<String, Object> threadInfo = new HashMap<>();
		logger.info("URL : " + request.getRequestURL());
		threadInfo.put("url", request.getRequestURL());
		logger.info("URI : " + request.getRequestURI());
		threadInfo.put("uri", request.getRequestURI());
		logger.info("HTTP_METHOD : " + request.getMethod());
		threadInfo.put("httpMethod", request.getMethod());
		logger.info("REMOTE_ADDR : " + request.getRemoteAddr());
		threadInfo.put("ip", request.getRemoteAddr());
		logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "."
				+ joinPoint.getSignature().getName());
		threadInfo.put("classMethod",
				joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
		logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
		threadInfo.put("args", Arrays.toString(joinPoint.getArgs()));
		logger.info("USER_AGENT"+request.getHeader("User-Agent"));
		threadInfo.put("userAgent", request.getHeader("User-Agent"));
		logger.info("执行方法:" + controllerWebLog.name());
		threadInfo.put("methodName", controllerWebLog.name());
		threadLocal.set(threadInfo);
	}
	/**
	 * 执行成功后处理
	 * @param controllerWebLog
	 * @param ret
	 * @throws Throwable
	 */
	@AfterReturning(value = "webLog()&& @annotation(controllerWebLog)", returning = "ret")
	public void doAfterReturning(ControllerWebLog controllerWebLog, Object ret) throws Throwable {
		Map<String, Object> threadInfo = threadLocal.get();
		threadInfo.put("result", ret);
		if (controllerWebLog.intoDb()) {
			//插入数据库操作
			//insertResult(threadInfo);
		}
		// 处理完请求,返回内容
		logger.info("RESPONSE : " + ret);
	}
	/**
	 * 获取执行时间
	 * @param proceedingJoinPoint
	 * @return
	 * @throws Throwable
	 */
	@Around(value = "webLog()")
	public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		long startTime = System.currentTimeMillis();
		Object ob = proceedingJoinPoint.proceed();
		Map<String, Object> threadInfo = threadLocal.get();
		Long takeTime = System.currentTimeMillis() - startTime;
		threadInfo.put("takeTime", takeTime);
		logger.info("耗时:" + takeTime);
		threadLocal.set(threadInfo);
		return ob;
	}
	/**
	 * 异常处理
	 * @param throwable
	 */
	@AfterThrowing(value = "webLog()", throwing = "throwable")
	public void doAfterThrowing(Throwable throwable) {
		RequestAttributes ra = RequestContextHolder.getRequestAttributes();

		ServletRequestAttributes sra = (ServletRequestAttributes) ra;

		HttpServletRequest request = sra.getRequest();
		// 异常信息
		logger.error("{}接口调用异常,异常信息{}", request.getRequestURI(), throwable);
	}

}

5. Тестовый интерфейс

@RestController
@RequestMapping("/user")
public class UserController {

	@GetMapping("/getOne")
	@ControllerWebLog(name = "查询", intoDb = true)
	public String getOne(Long id, String name) {

		return "1234";
	}
}

6. Запустите тест

Запрос браузера:http://127.0.0.1:8080/user/getOne?id=1&name=zwqhВы можете увидеть вывод фонового журнала:

резюме

Ведение журнала — это всего лишь простой пример, а применение Spring AOP делает всю систему более упорядоченной и мощной в других сценариях. Это помогает нам уменьшить связь между модулями, улучшить возможность повторного использования программы, повысить эффективность разработки и улучшить работоспособность и ремонтопригодность системы.

образец кода

github

Облако кода

Если не указано иное, авторские права на эту статью принадлежатутренний туманВсе, пожалуйста, укажите источник при перепечатании.

Оригинальное название: Spring Boot 2.x (восемь): Spring AOP достичь простого раздела журнала

Оригинальный адрес: https://www.zwqh.top/article/info/14

Если статья была вам полезна, отсканируйте код и подпишитесь на мой официальный аккаунт, статья постоянно обновляется...