Введение в АОП
Обзор AOP
AOP - это ориентированное на аспектное программирование, которое ориентировано на (切面
) аспектное программирование. Объяснение в Википедии:Aspect
это новый模块化机制
, используется для описания разрозненных объектов, классов или функций横切关注点
. Отделение сквозных задач от задач является основной концепцией аспектно-ориентированного программирования.分离关注点
Создавайте код, решающий конкретную проблему предметной области, из业务逻辑
Независимый код бизнес-логики не обязательно должен содержать вызывающий код для конкретных проблемных областей, таких как ведение журнала некоторых общих модулей, код безопасности.
Код по разделам отделен, более упорядочен и понятен, дублирование кода извлекается отдельно для обслуживания и использования при необходимости, кодовые модули унифицированного публичного вызова, такой класс является базовым модулем, простым в обслуживании и расширении унифицированного обновления.
АОП должен предоставить механизм для достижения бизнес-раздела внедрения, раздела привязки, определяемого бизнес-логикой в точке входа (pointcut). Для всех микроконтроллеров, таких как сервисные уровни SpringBoot, требуется некоторая процедура для печати журнала в HTTP-запрос, если печать выполняется каждый раз, когда контроллер, код будет избыточным, если они инкапсулируют общий код, но также требуется класс контроллера для каждый вызов, поэтому АОП выглядит в самый раз, на этот раз ввести журнал АОП на логике http, связанной с унифицированным управлением для написания кода, не нужно вызывать уровень контроллера, нужно только создать раздел и контроллер через точку входа в привязка, следующий пример будет упомянут.
Терминология, связанная с АОП
-
切面(Aspect)
: относится к модуляризации задач, пересекающих несколько объектов, управление транзакциями является хорошим примером сквозных задач в приложениях J2EE. В Spring AOP аспекты реализуются либо через обычные классы (базовые методы шаблонов), либо с помощью аннотаций.@Aspect
Обычный класс для реализации. -
连接点(Joint point)
: относится к точке во время выполнения программы, такой как выполнение метода или обработка исключения. В Spring AOP точка соединения часто представляет一个方法执行
. -
通知(Advice)
: Относится к действию, выполняемому аспектом в конкретной точке подключения. Существуют различные типы уведомлений, в том числе"around"
,"before"
и"after"
Уведомление. Многие фреймворки АОП, включая Spring, моделируют рекомендации как перехватчики и поддерживают цепочку перехватчиков вокруг точек соединения. -
切入点(Pointcut)
: относится к утверждению, которое соответствует точке соединения. Advice связан с выражением pointcut и выполняется в любой точке соединения, соответствующей pointcut (например, при выполнении метода с определенным именем). Ядром АОП является切入点表达式匹配连接点的思想
. Spring использует по умолчаниюAspectJ切入点表达式语言
-
引入(Introduction)
: представляет объявление дополнительного метода или свойства типа. Spring AOP позволяет вводить новые интерфейсы для любого рекомендуемого объекта (и соответствующей реализации). Например, вы можете использовать импорт, чтобы реализовать bean-компонент.IsModified
интерфейс, упрощающий механизм кэширования. (В сообществе AspectJ импорт также известен как межтиповое объявление) -
目标对象(Target object)
: относится к объекту, уведомленному одним или несколькими аспектами. Также относится к уведомляемому объекту ("advised object"
), из-за того, что Spring AOP запускается через прокси, это часто является целевым объектом代理对象
. -
AOP 代理(AOP proxy)
: относится к объекту, созданному через структуру АОП для реализации контракта аспекта (выполнения методов уведомления и т. д.). В среде Spring AOP-прокси — этоJDK动态代理
илиCGLIB代理
. -
织入(Weaving)
: соединяет аспекты с другими типами приложений или объектами для создания уведомляемого объекта. Это можно сделать во время компиляции (например, с помощью компилятора AspectJ), во время загрузки или во время выполнения. Spring AOP, как и другие чистые среды Java AOP, обычно находятся в运行时
Полное плетение.
Термины, связанные с рекомендациями по АОП
-
前置通知(Before advice)
: Уведомления выполняются перед точкой соединения. Но это уведомление не может предотвратить процесс выполнения точки соединения (если только он не бросает исключение) -
后置返回通知(After returning advice)
: Совет выполняется после нормального завершения точки соединения (например, если метод возвращается без создания исключения). -
后置异常通知(After throwing advice)
: Совет, который выполняется, когда метод завершает работу, вызывая исключение. -
后置(最终)通知(After(finally) advice)
: Уведомление, выполняемое при выходе из точки соединения (нормально или ненормально). -
环绕通知(Around advice)
: уведомления вокруг точки соединения, такие как вызовы методов. Это один из самых мощных типов уведомлений. Рекомендации по объемному звучанию можно давать до и после вызовов методов для завершения пользовательского поведения. Он также отвечает за выбор, следует ли продолжить выполнение точки соединения или упростить выполнение уведомленного метода, возвращая собственное возвращаемое значение или вызывая исключение.
Интеграция АОП в SpringBoot
1) Импорт Pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<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.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.14</version>
</dependency>
</dependencies>
Среди них представлены основные зависимости, связанные с aop, для поддержки аспектного программирования:org.aspectj.aspectjweaver
иorg.aspectj.aspectjrt
зависимость.aspectjweaver
это тканый пакет Aspectj,aspectjrt
это пакет времени выполнения для аспекта.
2) Файл конфигурации
server:
context-path: /demo/v1
port: 9000
3) класс контроллера
package com.example.andya.demo.controller;
import org.springframework.web.bind.annotation.*;
/**
* @author Andya
* @create 2020-04-12 10:36
*/
@RestController
@RequestMapping("/aopTest")
public class AopController {
@RequestMapping(value = "/sayHi/{name}", method = RequestMethod.GET)
public String sayHi(@PathVariable(value = "name") String name) {
return "hi, " + name;
}
}
4) слой на основе AOP
package com.example.andya.demo.aop;
import com.alibaba.fastjson.JSON;
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.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* @author Andya
* @create 2020-04-12 10:39
*/
@Aspect
@Component
public class WebLogAspect {
private Logger LOG = LoggerFactory.getLogger(WebLogAspect.class);
ThreadLocal<Long> startTime = new ThreadLocal<>();
/**
* 定义切入点,以controller下所有包的请求为切入点
*/
@Pointcut("execution(public * com.example.andya.demo.controller..*.*(..))*")
public void webLog(){
}
/**
*前置通知:在切入点之前执行的通知
* @param joinPoint
* @throws Throwable
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
startTime.set(System.currentTimeMillis());
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
//打印请求相关参数
LOG.info("========================================== Start ==========================================");
LOG.info("URL:" + request.getRequestURL().toString());
LOG.info("HTTP_METHOD:" + request.getMethod());
//header第一种格式展示
Enumeration<String> enumeration = request.getHeaderNames();
Map<String, String> headerMap = new HashMap<>();
while (enumeration.hasMoreElements()) {
String headerName = enumeration.nextElement();
headerMap.put(headerName, request.getHeader(headerName));
}
String headerJsonStr = JSON.toJSONString(headerMap);
if (headerJsonStr.length() > 0) {
LOG.info("HTTP_HEADERS INFO IS: {}", headerJsonStr);
}
//header第二种格式展示
LOG.info("HTTP_HEADERS: ");
Enumeration<?> enumeration1 = request.getHeaderNames();
while (enumeration1.hasMoreElements()) {
String key = (String) enumeration1.nextElement();
String value = request.getHeader(key);
LOG.info(" {}: {}", key, value);
}
LOG.info("IP:" + request.getRemoteAddr());
LOG.info("CLASS_METHOD:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
try {
LOG.info("REQUEST BODY : [{}]", JSON.toJSONString(joinPoint.getArgs()[0]));
// LOG.info("ARGS:{}", Arrays.toString(joinPoint.getArgs()));
} catch (Exception e) {
LOG.error("REQUEST BODY PARSE ERROR!");
}
HttpSession session = (HttpSession) servletRequestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
LOG.info("SESSION ID:" + session.getId());
}
// /**
// * 后置通知
// * @param ret
// * @throws Throwable
// */
// @AfterReturning(returning = "ret", pointcut = "webLog()")
// public void doAfterReturning(Object ret) throws Throwable {
// // 处理完请求,返回内容
// LOG.info("RESPONSE : " + ret);
// LOG.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
//
// }
/**
* 后置最终通知
* @throws Throwable
*/
@After("webLog()")
public void doAfter() throws Throwable {
LOG.info("=========================================== End ===========================================");
// 每个请求之间空一行
LOG.info("");
}
/**
* 环绕通知
* 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("webLog()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
String resultStr = JSON.toJSONString(result);
// 打印出参
LOG.info("RESPONSE ARGS : {}", resultStr);
// 执行耗时
LOG.info("TIME-CONSUMING : {} ms", System.currentTimeMillis() - startTime);
return result;
}
}
в:
-
@Aspect
Аннотация указывает, что класс является аспектным классом,@Component
Аннотация заключается в добавлении класса аспекта в контейнер Ioc. -
@Pointcut
Определите точки для всех функций во всем пакете контроллера. -
joinPoint.getArgs()
Получить информацию о параметрах целевого метода. -
joinPoint.getSignature()
Получить подпись уведомления и передатьjoinPoint.getSignature().getDeclaringTypeName()
Получать代理类
название,joinPoint.getSignature().getName()
Получать代理方法
название.
5) Проведите класс
package com.example.andya.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
6) URL-адрес посещения
http://127.0.0.1:9000/demo/v1/aopTest/sayHi/andya
7) Результат выполнения
... ...
... ...
... ...
2020-04-12 15:21:31.737 INFO 18548 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2020-04-12 15:21:31.782 INFO 18548 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 9000 (http)
2020-04-12 15:21:31.786 INFO 18548 --- [ main] com.example.andya.demo.DemoApplication : Started DemoApplication in 2.928 seconds (JVM running for 4.26)
2020-04-12 15:21:41.084 INFO 18548 --- [nio-9000-exec-2] o.a.c.c.C.[.[localhost].[/demo/v1] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2020-04-12 15:21:41.085 INFO 18548 --- [nio-9000-exec-2] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2020-04-12 15:21:41.102 INFO 18548 --- [nio-9000-exec-2] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 17 ms
2020-04-12 15:21:41.126 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : ========================================== Start ==========================================
2020-04-12 15:21:41.126 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : URL:http://127.0.0.1:9000/demo/v1/aopTest/sayHi/andya
2020-04-12 15:21:41.126 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : HTTP_METHOD:GET
2020-04-12 15:21:41.155 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : HTTP_HEADERS INFO IS: {"accept-language":"zh-CN","cookie":"JSESSIONID=1014BD34FFE9D2660CB47B282C63FA7D","host":"127.0.0.1:9000","connection":"Keep-Alive","accept-encoding":"gzip, deflate","accept":"text/html, application/xhtml+xml, image/jxr, */*","user-agent":"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"}
2020-04-12 15:21:41.156 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : HTTP_HEADERS:
2020-04-12 15:21:41.156 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : accept: text/html, application/xhtml+xml, image/jxr, */*
2020-04-12 15:21:41.156 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : accept-language: zh-CN
2020-04-12 15:21:41.156 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : user-agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
2020-04-12 15:21:41.156 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : accept-encoding: gzip, deflate
2020-04-12 15:21:41.156 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : host: 127.0.0.1:9000
2020-04-12 15:21:41.156 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : connection: Keep-Alive
2020-04-12 15:21:41.156 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : cookie: JSESSIONID=1014BD34FFE9D2660CB47B282C63FA7D
2020-04-12 15:21:41.156 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : IP:127.0.0.1
2020-04-12 15:21:41.157 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : CLASS_METHOD:com.example.andya.demo.controller.AopController.sayHi
2020-04-12 15:21:41.157 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : REQUEST BODY : ["andya"]
2020-04-12 15:21:41.160 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : SESSION ID:0357B2624C73C5F79BC977AD628DB45F
2020-04-12 15:21:41.162 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : RESPONSE ARGS : "hi, andya"
2020-04-12 15:21:41.163 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : TIME-CONSUMING : 37 ms
2020-04-12 15:21:41.163 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect : =========================================== End ===========================================
2020-04-12 15:21:41.163 INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect :
Справочная литература «Технология SPING Insider, углубленный анализ весенних архитектурных и дизайнерских принципов» Обратитесь к официальному сайтуОфициальный сайт Spring AOP