From b37d8dbe2b809b9cb783ee15bb586e5597bd29fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E5=BA=9C=E5=BC=BA?= <8569561+fuqiangma@user.noreply.gitee.com> Date: Sun, 30 Apr 2023 22:37:52 +0800 Subject: [PATCH] =?UTF-8?q?log=20aop=20SpringBoot=E4=BD=BF=E7=94=A8AOP?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=97=A5=E5=BF=97=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logrizhi/.gitignore | 33 ++++ logrizhi/README.md | 11 ++ logrizhi/pom.xml | 82 +++++++++ .../demo/DemoRequestScopeApplication.java | 13 ++ .../com/example/demo/aop/LoggingAspect.java | 101 ++++++++++ .../demo/constants/GloablConstants.java | 10 + .../demo/controller/TestController.java | 30 +++ .../example/demo/request/CustomerType.java | 15 ++ .../example/demo/request/OrderDiscount.java | 15 ++ .../example/demo/request/OrderRequest.java | 26 +++ .../example/demo/service/AopLogService.java | 16 ++ .../src/main/resources/application.properties | 1 + logrizhi/src/main/resources/application.yml | 7 + logrizhi/src/main/resources/logback.xml | 19 ++ .../resources/rules/customer-discount.drl | 38 ++++ .../com/example/demo/testscope/TestMain.java | 172 ++++++++++++++++++ 16 files changed, 589 insertions(+) create mode 100644 logrizhi/.gitignore create mode 100644 logrizhi/README.md create mode 100644 logrizhi/pom.xml create mode 100644 logrizhi/src/main/java/com/example/demo/DemoRequestScopeApplication.java create mode 100644 logrizhi/src/main/java/com/example/demo/aop/LoggingAspect.java create mode 100644 logrizhi/src/main/java/com/example/demo/constants/GloablConstants.java create mode 100644 logrizhi/src/main/java/com/example/demo/controller/TestController.java create mode 100644 logrizhi/src/main/java/com/example/demo/request/CustomerType.java create mode 100644 logrizhi/src/main/java/com/example/demo/request/OrderDiscount.java create mode 100644 logrizhi/src/main/java/com/example/demo/request/OrderRequest.java create mode 100644 logrizhi/src/main/java/com/example/demo/service/AopLogService.java create mode 100644 logrizhi/src/main/resources/application.properties create mode 100644 logrizhi/src/main/resources/application.yml create mode 100644 logrizhi/src/main/resources/logback.xml create mode 100644 logrizhi/src/main/resources/rules/customer-discount.drl create mode 100644 logrizhi/src/test/java/com/example/demo/testscope/TestMain.java diff --git a/logrizhi/.gitignore b/logrizhi/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/logrizhi/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/logrizhi/README.md b/logrizhi/README.md new file mode 100644 index 0000000..c912aec --- /dev/null +++ b/logrizhi/README.md @@ -0,0 +1,11 @@ +# demo-request-scope + +《基于ThreadLocal实现一个上下文管理组件》技术博客对应源码 + +https://juejin.cn/post/7153287656624324638 + +本文基于`ThreadLocal`原理,实现了一个上下文状态管理组件`Scope`,通过开启一个自定义的`Scope`,在`Scope`范围内,可以通过`Scope`各个方法读写数据; + +通过自定义线程池实现上下文状态数据的线程间传递; + +提出了一种基于`Filter`和`Scope`的`Request`粒度的上下文管理方案。 diff --git a/logrizhi/pom.xml b/logrizhi/pom.xml new file mode 100644 index 0000000..acad05f --- /dev/null +++ b/logrizhi/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.4 + + + com.example + demo-request-scope + 0.0.1-SNAPSHOT + demo-request-scope + Demo project for Spring Boot + + 8 + + + + + + + org.drools + drools-core + 7.59.0.Final + + + org.drools + drools-compiler + 7.59.0.Final + + + org.drools + drools-decisiontables + 7.59.0.Final + + + + com.google.guava + guava + 30.1-jre + + + + org.springframework.boot + spring-boot-starter-aop + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.projectlombok + lombok + true + + + junit + junit + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/logrizhi/src/main/java/com/example/demo/DemoRequestScopeApplication.java b/logrizhi/src/main/java/com/example/demo/DemoRequestScopeApplication.java new file mode 100644 index 0000000..4c0d58d --- /dev/null +++ b/logrizhi/src/main/java/com/example/demo/DemoRequestScopeApplication.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoRequestScopeApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoRequestScopeApplication.class, args); + } + +} diff --git a/logrizhi/src/main/java/com/example/demo/aop/LoggingAspect.java b/logrizhi/src/main/java/com/example/demo/aop/LoggingAspect.java new file mode 100644 index 0000000..e355051 --- /dev/null +++ b/logrizhi/src/main/java/com/example/demo/aop/LoggingAspect.java @@ -0,0 +1,101 @@ +package com.example.demo.aop; + +import com.example.demo.constants.GloablConstants; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.Profiles; +import org.springframework.stereotype.Component; + +import java.util.Arrays; + +@Aspect +@Component +public class LoggingAspect { + + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + private final Environment env; + + public LoggingAspect(Environment env) { + this.env = env; + } + + /** + * 匹配spring框架的repositories、service、rest端点的切面 + */ + @Pointcut("within(@org.springframework.stereotype.Repository *)" + + " || within(@org.springframework.stereotype.Service *)" + + " || within(@org.springframework.web.bind.annotation.RestController *)") + public void springBeanPointcut() { + // 方法为空,因为这只是一个切入点,实现在通知中。 + } + + /** + * 匹配我们自己项目的repositories、service、rest端点的切面 + */ + @Pointcut("within(com.example.demo.config..*)" + + " || within(com.example.demo.service..*)" + + " || within(com.example.demo.controller.OrderDiscountController..*)") + public void applicationPackagePointcut() { + // 方法为空,因为这只是一个切入点,实现在通知中。 + } + + /** + * 记录方法抛出异常的通知 + * + * @param joinPoint join point for advice + * @param e exception + */ + @AfterThrowing(pointcut = "applicationPackagePointcut() && springBeanPointcut()", throwing = "e") + public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { + + // 判断环境,dev、test or prod + if (env.acceptsProfiles(Profiles.of(GloablConstants.SPRING_PROFILE_DEVELOPMENT, GloablConstants.SPRING_PROFILE_TEST))) { + log.error("Exception in {}.{}() with cause = '{}' and exception = '{}'", joinPoint.getSignature().getDeclaringTypeName(), + joinPoint.getSignature().getName(), e.getCause() != null ? e.getCause() : "NULL", e.getMessage(), e); + + } else { + log.error("Exception in {}.{}() with cause = {}", joinPoint.getSignature().getDeclaringTypeName(), + joinPoint.getSignature().getName(), e.getCause() != null ? e.getCause() : "NULL"); + } + + } + + /** + * 在方法进入和退出时记录日志的通知 + * + * @param joinPoint join point for advice + * @return result + * @throws Throwable throws IllegalArgumentException + */ + @Around("applicationPackagePointcut() && springBeanPointcut()") + public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { + + if (log.isDebugEnabled()) { + log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(), + joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs())); + } + try { + Object result = joinPoint.proceed(); + if (log.isDebugEnabled()) { + log.debug("Exit: {}.{}() with result = {}", joinPoint.getSignature().getDeclaringTypeName(), + joinPoint.getSignature().getName(), result); + } + return result; + } catch (IllegalArgumentException e) { + log.error("Illegal argument: {} in {}.{}()", Arrays.toString(joinPoint.getArgs()), + joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); + + throw e; + } + + } + +} \ No newline at end of file diff --git a/logrizhi/src/main/java/com/example/demo/constants/GloablConstants.java b/logrizhi/src/main/java/com/example/demo/constants/GloablConstants.java new file mode 100644 index 0000000..91602c3 --- /dev/null +++ b/logrizhi/src/main/java/com/example/demo/constants/GloablConstants.java @@ -0,0 +1,10 @@ +package com.example.demo.constants; + + +public interface GloablConstants { + + String SPRING_PROFILE_DEVELOPMENT = "dev"; + String SPRING_PROFILE_TEST = "test"; + + +} diff --git a/logrizhi/src/main/java/com/example/demo/controller/TestController.java b/logrizhi/src/main/java/com/example/demo/controller/TestController.java new file mode 100644 index 0000000..218e92e --- /dev/null +++ b/logrizhi/src/main/java/com/example/demo/controller/TestController.java @@ -0,0 +1,30 @@ +package com.example.demo.controller; + +import com.example.demo.service.AopLogService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 超简洁!SpringBoot使用AOP统一日志管理 + */ +@RestController +@RequestMapping("/api") +@Slf4j +public class TestController { + + private final AopLogService aopLogService; + + public TestController(AopLogService aopLogService) { + this.aopLogService = aopLogService; + } + + @GetMapping("/test/{id}") + public ResponseEntity test(@PathVariable("id") Integer id) { + return ResponseEntity.ok().body(aopLogService.test(id)); + } + +} diff --git a/logrizhi/src/main/java/com/example/demo/request/CustomerType.java b/logrizhi/src/main/java/com/example/demo/request/CustomerType.java new file mode 100644 index 0000000..2ddf07b --- /dev/null +++ b/logrizhi/src/main/java/com/example/demo/request/CustomerType.java @@ -0,0 +1,15 @@ +package com.example.demo.request; + +/** + * @className: CustomerType + * @author: woniu + * @date: 2023/4/25 + **/ +public enum CustomerType { + LOYAL, NEW, DISSATISFIED; + + public String getValue() { + return this.toString(); + } + +} diff --git a/logrizhi/src/main/java/com/example/demo/request/OrderDiscount.java b/logrizhi/src/main/java/com/example/demo/request/OrderDiscount.java new file mode 100644 index 0000000..969660d --- /dev/null +++ b/logrizhi/src/main/java/com/example/demo/request/OrderDiscount.java @@ -0,0 +1,15 @@ +package com.example.demo.request; + + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class OrderDiscount { + + /** + * 折扣 + */ + private Integer discount = 0; +} diff --git a/logrizhi/src/main/java/com/example/demo/request/OrderRequest.java b/logrizhi/src/main/java/com/example/demo/request/OrderRequest.java new file mode 100644 index 0000000..bcf76b8 --- /dev/null +++ b/logrizhi/src/main/java/com/example/demo/request/OrderRequest.java @@ -0,0 +1,26 @@ +package com.example.demo.request; + + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class OrderRequest { + /** + * 客户号 + */ + private String customerNumber; + /** + * 年龄 + */ + private Integer age; + /** + * 订单金额 + */ + private Integer amount; + /** + * 客户类型 + */ + private CustomerType customerType; +} \ No newline at end of file diff --git a/logrizhi/src/main/java/com/example/demo/service/AopLogService.java b/logrizhi/src/main/java/com/example/demo/service/AopLogService.java new file mode 100644 index 0000000..6cec3e8 --- /dev/null +++ b/logrizhi/src/main/java/com/example/demo/service/AopLogService.java @@ -0,0 +1,16 @@ +package com.example.demo.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * AOP统一日志管理测试服务 + */ +@Service +@Slf4j +public class AopLogService { + + public String test(Integer id) { + return "传入的参数是:" + id; + } +} diff --git a/logrizhi/src/main/resources/application.properties b/logrizhi/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/logrizhi/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/logrizhi/src/main/resources/application.yml b/logrizhi/src/main/resources/application.yml new file mode 100644 index 0000000..c91b837 --- /dev/null +++ b/logrizhi/src/main/resources/application.yml @@ -0,0 +1,7 @@ +server: + port: 8888 + +# 环境:dev-开发 test-测试 prod-生产 +spring: + profiles: + active: dev \ No newline at end of file diff --git a/logrizhi/src/main/resources/logback.xml b/logrizhi/src/main/resources/logback.xml new file mode 100644 index 0000000..1e071a1 --- /dev/null +++ b/logrizhi/src/main/resources/logback.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/logrizhi/src/main/resources/rules/customer-discount.drl b/logrizhi/src/main/resources/rules/customer-discount.drl new file mode 100644 index 0000000..6a9469f --- /dev/null +++ b/logrizhi/src/main/resources/rules/customer-discount.drl @@ -0,0 +1,38 @@ +package rules + +import com.example.demo.request.OrderRequest; +import com.example.demo.request.CustomerType; + +global com.example.demo.request.OrderDiscount orderDiscount; + +dialect "mvel" + +// 规则1: 根据年龄判断 +rule "Age based discount" +when +// 当客户年龄在20岁以下或者50岁以上 +OrderRequest(age < 20 || age > 50) +then +// 则添加10%的折扣 +System.out.println("==========Adding 10% discount for Kids/ senior customer============="); +orderDiscount.setDiscount(orderDiscount.getDiscount() + 10); +end + +// 规则2: 根据客户类型的规则 +rule "Customer type based discount - Loyal customer" +when +// 当客户类型是LOYAL +OrderRequest(customerType.getValue == "LOYAL") +then +// 则增加5%的折扣 +System.out.println("==========Adding 5% discount for LOYAL customer============="); +orderDiscount.setDiscount(orderDiscount.getDiscount() + 5); +end + +rule "Customer type based discount - others" +when +OrderRequest(customerType.getValue != "LOYAL") +then +System.out.println("==========Adding 3% discount for NEW or DISSATISFIED customer============="); +orderDiscount.setDiscount(orderDiscount.getDiscount() + 3); +end diff --git a/logrizhi/src/test/java/com/example/demo/testscope/TestMain.java b/logrizhi/src/test/java/com/example/demo/testscope/TestMain.java new file mode 100644 index 0000000..d068e13 --- /dev/null +++ b/logrizhi/src/test/java/com/example/demo/testscope/TestMain.java @@ -0,0 +1,172 @@ +package com.example.demo.testscope; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.junit.jupiter.api.Test; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class TestMain { + +//工作中常用的一些Stream流操作,多年实践总结 + + /** + * 演示map的用途:一对一转换 + */ + @Test + public void stringToIntMap() { + List ids = Arrays.asList("205", "105", "308", "469", "627", "193", "111"); + // 使用流操作 + List results = ids.stream() + .map(id -> { + User user = new User(Integer.valueOf(id)); + return user; + }) + .collect(Collectors.toList()); + System.out.println(results); + } + + + /** + * 演示map的用途:一对多转换 + */ + @Test + public void stringToIntFlatmap() { + List sentences = Arrays.asList("hello world", "Jia Gou Wu Dao"); + // 使用流操作 + List results = sentences.stream() + .flatMap(sentence -> Arrays.stream(sentence.split(" "))) + .collect(Collectors.toList()); + System.out.println(results); + } + + + + + + /** + *filter、sorted、distinct、limit + */ + @Test + public void testGetTargetUsers() { + List ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193"); + // 使用流操作 + List results = ids.stream() + .filter(s -> s.length() > 2) + .distinct() + .map(Integer::valueOf) + .sorted(Comparator.comparingInt(o -> o)) + .limit(3) + .map(id -> new User(id)) + .collect(Collectors.toList()); + System.out.println(results); + } + + + + + /** + * 简单结果终止方法 + */ + @Test + public void testSimpleStopOptions() { + List ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193"); + // 统计stream操作后剩余的元素个数 + System.out.println(ids.stream().filter(s -> s.length() > 2).count()); + // 判断是否有元素值等于205 + System.out.println(ids.stream().filter(s -> s.length() > 2).anyMatch("205"::equals)); + // findFirst操作 + ids.stream().filter(s -> s.length() > 2) + .findFirst() + .ifPresent(s -> System.out.println("findFirst:" + s)); + } + + + + + + /** + * 一旦一个Stream被执行了终止操作之后,后续便不可以再读这个流执行其他的操作了,否则会报错 + */ + @Test + public void testHandleStreamAfterClosed() { + List ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193"); + Stream stream = ids.stream().filter(s -> s.length() > 2); + // 统计stream操作后剩余的元素个数 + System.out.println(stream.count()); + System.out.println("-----下面会报错-----"); + // 判断是否有元素值等于205 + try { + System.out.println(stream.anyMatch("205"::equals)); + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println("-----上面会报错-----"); + } + + + /** + * 生成集合 + */ + @Test + public void testCollectStopOptions() { + List ids = Arrays.asList(new User(17), new User(22), new User(23)); + // collect成list + List collectList = ids.stream().filter(dept -> dept.getId() > 20) + .collect(Collectors.toList()); + System.out.println("collectList:" + collectList); + // collect成Set + Set collectSet = ids.stream().filter(dept -> dept.getId() > 20) + .collect(Collectors.toSet()); + System.out.println("collectSet:" + collectSet); + // collect成HashMap,key为id,value为Dept对象 + Map collectMap = ids.stream().filter(dept -> dept.getId() > 20) + .collect(Collectors.toMap(com.example.demo.testscope.User::getId, dept -> dept)); + System.out.println("collectMap:" + collectMap); + } + + + + + /** + * 生成拼接字符串 + */ + @Test + public void testCollectJoinStrings() { + List ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193"); + String joinResult = ids.stream().collect(Collectors.joining(",")); + System.out.println("拼接后:" + joinResult); + } + + + + + + /** + * 数学运算 + */ + @Test + public void testNumberCalculate() { + List ids = Arrays.asList(10, 20, 30, 40, 50); + // 计算平均值 + Double average = ids.stream().collect(Collectors.averagingInt(value -> value)); + System.out.println("平均值:" + average); + // 数据统计信息 + IntSummaryStatistics summary = ids.stream().collect(Collectors.summarizingInt(value -> value)); + System.out.println("数据统计信息: " + summary); + } + + + + +} + + +@Data +@AllArgsConstructor +class User { + private int id; + +} \ No newline at end of file