当前位置: 首页 > news >正文

如何找人帮我做网站推广百度爱采购优化排名软件

如何找人帮我做网站推广,百度爱采购优化排名软件,简易小程序制作,在微信上做彩票网站吗文章目录 前言正文一、项目结构介绍二、核心类2.1 核心注解2.1.1 CLog 日志注解2.1.2 ProcessorBean 处理器bean 2.2 切面类2.3 自定义线程池2.4 工具类2.4.1 管理者工具类 2.5 测试2.5.1 订单创建处理器2.5.2 订单管理者2.5.3 订单控制器2.5.4 测试报文2.5.5 测试结果 附录1、…

文章目录

  • 前言
  • 正文
    • 一、项目结构介绍
    • 二、核心类
    • 2.1 核心注解
      • 2.1.1 CLog 日志注解
      • 2.1.2 ProcessorBean 处理器bean
    • 2.2 切面类
    • 2.3 自定义线程池
    • 2.4 工具类
      • 2.4.1 管理者工具类
    • 2.5 测试
      • 2.5.1 订单创建处理器
      • 2.5.2 订单管理者
      • 2.5.3 订单控制器
      • 2.5.4 测试报文
      • 2.5.5 测试结果
  • 附录
    • 1、其他相关文章

前言

关于操作日志记录,在一个项目中是必要的。
本文基于 java8 和 SpringBoot 2.7 来实现此功能。

之前写过一个简单的接口报文日志打印的,和本文的起始思路相同,都是使用切面。但是本文功能更为强大,也更复杂。文章见本文附录《SpringBoot自定义starter之接口日志输出》。

本文代码仓库:https://gitee.com/fengsoshuai/custom-log2.git

正文

本文知识点如下:
自定义注解,SpringBoot使用切面,全局异常处理器,ThreadLocal的使用,MDC传递日志ID,登录拦截器,日志拦截器,自定义线程池,SPEL表达式解析,模版方法设计模式等。

一、项目结构介绍

在这里插入图片描述
其中 org.feng.clog 是核心代码区域。org.feng.test 是用于测试功能写的。

二、核心类

在这里插入图片描述

在项目启动时,会把AbstractProcessorTemplate 的子类放入Spring容器。同时会执行注册处理器的方法,其定义如下:

package org.feng.clog;import lombok.extern.slf4j.Slf4j;
import org.feng.clog.annotation.ProcessorBean;
import org.feng.clog.utils.SpringBeanUtils;import javax.annotation.PostConstruct;/*** 处理器模板** @author feng*/
@Slf4j
public abstract class AbstractProcessorTemplate<T, R> implements Processor<T, R> {protected void init(ProcessorContext<T> context) {}protected void after(ProcessorContext<T> context, R result) {}public R start(ProcessorContext<T> context) {init(context);// 直接调用handle会导致aop失效// R result = handle(context);AbstractProcessorTemplate<T, R> template = SpringBeanUtils.getByClass(this.getClass());R result = template.handle(context);after(context, result);return result;}@PostConstructprivate void registerProcessor() {if (this.getClass().isAnnotationPresent(ProcessorBean.class)) {ProcessorBean processorBean = this.getClass().getDeclaredAnnotation(ProcessorBean.class);log.info("ProcessorBean Register, action is {}, processor is {}", processorBean.action(), this.getClass().getName());ProcessorFactory.register(processorBean.action(), this);}}
}

2.1 核心注解

2.1.1 CLog 日志注解

package org.feng.clog.annotation;import org.feng.clog.enums.ActionTypeEnum;
import org.feng.clog.enums.ModuleEnum;import java.lang.annotation.*;/*** 日志注解</br>* <pre>* <ul>使用示例:* <li>@CLog(template = "这是简单模版,无参数",actionType = ActionTypeEnum.UPDATE,actionIdEl = "{#userReq.id}",moduleEl = "1")</li>* <li>@CLog(template = "带参数模版,学生名称:{#userReq.name},班级名称:{#userReq.classReq.name}",actionTypeStr = "这是操作",actionIdEl = "{#userReq.id}")</li>* <li>@CLog(template = "带参数计算模版,{#userReq.classReq.number > 20?'大班':'小班'}",actionTypeStr = "这是操作",actionIdEl = "{#userReq.id}")</li>* <li>@CLog(template = "复杂模版,{#userReq.classReq.number > 20?'大班':('这是名称:').concat(#userReq.name).concat(',这是年龄:').concat(#userReq.age)}",actionTypeStr = "这是操作",actionIdEl = "{#userReq.id}")</li>* <li>@CLog(template = "自定义表达式处理,{SfObjectUtil.isEmpty(#userReq.id)?'id为0或者为空':'id不为0或者为空'}",actionTypeStr = "这是操作",actionIdEl = "{#userReq.id}")</li>* <li>@CLog(template = "自定义处理,{logDesc}",actionTypeStr = "这是操作",actionIdEl = "{id}")</li>* </ul>* </pre>** @author feng*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CLog {/*** 日志模版*/String template();/*** 模块*/ModuleEnum module() default ModuleEnum.DEFAULT;/*** 所属模块名*/String moduleStr() default "";/*** 所属模块名</br>* 变量/表达式获取*/String moduleEl() default "";/*** 操作类型*/ActionTypeEnum actionType() default ActionTypeEnum.DEFAULT;/*** 操作类型,优先级高于枚举;不为空时强制读取此值*/String actionTypeStr() default "";/*** 操作类型</br>* 变量/表达式获取*/String actionTypeEl() default "";/*** 业务操作唯一值</br>* 变量/表达式获取*/String actionIdEl() default "";/*** 业务操作唯一值,多值*/String actionIds() default "";/*** 扩展字段*/String ext() default "";
}

2.1.2 ProcessorBean 处理器bean

package org.feng.clog.annotation;import org.feng.clog.enums.ActionTypeEnum;import java.lang.annotation.*;/*** 处理器bean** @author feng*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ProcessorBean {ActionTypeEnum action();
}

2.2 切面类

package org.feng.clog.aspect;import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.feng.clog.LogId;
import org.feng.clog.LogRecordContext;
import org.feng.clog.annotation.CLog;
import org.feng.clog.config.LogCustomerConfig;
import org.feng.clog.enums.ActionTypeEnum;
import org.feng.clog.enums.ModuleEnum;
import org.feng.clog.utils.SpELParserUtils;
import org.feng.clog.utils.StringUtil;
import org.feng.clog.utils.UserUtil;
import org.feng.clog.vo.UserVo;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** 日志切面** @author feng*/
@Aspect
@Component
@Slf4j
public class LogAspect {private static final Pattern BRACES_PATTERN = Pattern.compile("\\{.*?}");@Resource(name = "logThreadPoolTaskExecutor")private Executor  executor;@Pointcut("@annotation(org.feng.clog.annotation.CLog)")private void pointCut() {}@AfterReturning(value = "pointCut()")public void after(JoinPoint joinPoint) {try {addLog(joinPoint);} finally {LogRecordContext.clean();}}public void addLog(JoinPoint joinPoint) {String logId = LogId.get();UserVo userVo = UserUtil.get();Map<String, String> logRecordMap = LogRecordContext.get();executor.execute(() -> {try {// 传递logId到异步线程LogId.put(logId);// 获取方法+入参MethodSignature signature = (MethodSignature) joinPoint.getSignature();Object[] args = joinPoint.getArgs();// 获取注解CLog cLog = signature.getMethod().getDeclaredAnnotation(CLog.class);// 获取模版中的参数(如果存在参数),并拼接List<String> templateParameters = getTemplateParameters(cLog.template());buildTemplateData(templateParameters, signature, args, logRecordMap);String template = cLog.template();for (String templateParameter : templateParameters) {template = template.replace(templateParameter, logRecordMap.get(templateParameter));}// 获取moduleString module = getModule(cLog, signature, args, logRecordMap);// 获取actionTypeString actionType = getActionType(cLog, signature, args, logRecordMap);// 获取actionIdList<String> actionIds = getActionId(cLog, signature, args, logRecordMap);// 获取扩展字段JSONObject ext = getExt(cLog, signature, args, logRecordMap);if (StringUtil.isNotBlank(template)) {for (String actionId : actionIds) {log.info("记录日志,user={}, template={}, module={}, actionType={}, actionId={}, ext={}", userVo, template, module, actionType, actionId, ext);// todo 日志落库}} else {log.info("设置日志数据失败:不满足注解条件");}} catch (Exception e) {log.warn("设置日志异常:", e);}});}private List<String> getTemplateParameters(String template) {List<String> parameters = new ArrayList<>();Matcher matcher = BRACES_PATTERN.matcher(template);while (matcher.find()) {parameters.add(matcher.group());}return parameters;}private void buildTemplateData(List<String> parameters, MethodSignature signature, Object[] args, Map<String, String> map) {for (String el : parameters) {// 如果EL表达式为空,则直接下一个if (!StringUtil.isNotBlank(el)) {continue;}String spEl = el;// 兼容自定义数据spEl = getEl(spEl);if (map.containsKey(spEl)) {map.put("{" + spEl + "}", map.get(spEl));continue;}// 自定义类处理spEl = parseCustomerMethodEl(spEl);// El执行if (spEl.contains("#")) {String value = SpELParserUtils.parse(signature.getMethod(), args, spEl, String.class);map.put(el, value);} else {map.put(el, "");}}}private String getModule(CLog cLog, MethodSignature signature, Object[] args, Map<String, String> map) {// 设置了module枚举时,优先获取枚举对应的描述if (!ModuleEnum.DEFAULT.equals(cLog.module())) {return cLog.module().getDesc();}// 设置了moduleStr时if (StringUtil.isNotBlank(cLog.moduleStr())) {return cLog.moduleStr();}// 设置了moduleEl时if (StringUtil.isNotBlank(cLog.moduleEl())) {try {String el = cLog.moduleEl();el = getEl(el);// 处理自定义的elif (map.containsKey(el)) {return map.get(el);}// 处理自定义方法elel = parseCustomerMethodEl(el);// 执行elreturn SpELParserUtils.parse(signature.getMethod(), args, el, String.class);} catch (Exception e) {log.error("日志切面获取module错误", e);}}return null;}private String getActionType(CLog cLog, MethodSignature signature, Object[] args, Map<String, String> map) {// 设置了actionType枚举时,优先获取枚举对应的描述if (!ActionTypeEnum.DEFAULT.equals(cLog.actionType())) {return cLog.actionType().getDesc();}// 设置了actionTypeStr时if (StringUtil.isNotBlank(cLog.actionTypeStr())) {return cLog.actionTypeStr();}// 设置了actionTypeEl时if (StringUtil.isNotBlank(cLog.actionTypeEl())) {String el = cLog.actionTypeEl();el = getEl(el);// 处理自定义的elif (map.containsKey(el)) {return map.get(el);}// 处理自定义方法elel = parseCustomerMethodEl(el);// 执行elreturn SpELParserUtils.parse(signature.getMethod(), args, el, String.class);}return null;}private List<String> getActionId(CLog cLog, MethodSignature signature, Object[] args, Map<String, String> map) {// 设置了actionIdEl时if (StringUtil.isNotBlank(cLog.actionIdEl())) {if (map.containsKey(cLog.actionIdEl())) {return Collections.singletonList(map.get(cLog.actionIdEl()));}String el = cLog.actionIdEl();el = getEl(el);// 处理自定义elif (map.containsKey(el)) {return Collections.singletonList(map.get(el));}// 执行elreturn Collections.singletonList(SpELParserUtils.parse(signature.getMethod(), args, el, String.class));}// 设置了actionIds时if (StringUtil.isNotBlank(cLog.actionIds())) {String el = getEl(cLog.actionIds());if (map.containsKey(el)) {return Arrays.asList(map.get(el).split(","));}}return Collections.singletonList(System.currentTimeMillis() * 10 + new Random().nextInt(10000) + "");}private JSONObject getExt(CLog cLog, MethodSignature signature, Object[] args, Map<String, String> map) {// 如果EL表达式为空,则直接结束if (!StringUtil.isNotBlank(cLog.ext())) {return null;}String spEl = cLog.ext();//兼容自定义数据spEl = getEl(spEl);if (map.containsKey(spEl)) {String value = map.get(spEl);if (StringUtil.isNotBlank(value)) {try {return JSONObject.parseObject(value);} catch (Exception e) {log.info("JSON转换失败:{},{}", value, e.getMessage());return null;}}return null;}// 自定义类处理spEl = parseCustomerMethodEl(spEl);// El执行if (spEl.contains("#")) {String value = SpELParserUtils.parse(signature.getMethod(), args, spEl, String.class);if (StringUtil.isNotBlank(value)) {try {return JSONObject.parseObject(value);} catch (Exception e) {log.info("JSON转换失败:{},{}", value, e.getMessage());return null;}}return null;}return null;}private String parseCustomerMethodEl(String el) {for (String key : LogCustomerConfig.getCustomerMethod().keySet()) {if (el.contains(key)) {String className = key.split("\\.")[0];el = el.replace(className, "T(" + LogCustomerConfig.getCustomerMethod().get(key) + ")");}}return el;}private String getEl(String str) {str = str.replaceAll("\\{", "");str = str.replaceAll("}", "");return str;}}

2.3 自定义线程池

package org.feng.clog.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;/*** 线程池配置** @author feng*/
@Configuration
@EnableAsync
public class ThreadPoolConfig {@Bean(name = "logThreadPoolTaskExecutor")public Executor initLogCpuExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(Runtime.getRuntime().availableProcessors() + 1);executor.setMaxPoolSize(150);executor.setQueueCapacity(50);executor.setKeepAliveSeconds(60);executor.setThreadNamePrefix("log-thread-pool-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();executor.setTaskDecorator(runnable -> runnable);return executor;}
}

2.4 工具类

2.4.1 管理者工具类

package org.feng.clog.utils;import org.feng.clog.AbstractProcessorTemplate;
import org.feng.clog.ProcessorContext;
import org.feng.clog.ProcessorFactory;/*** 管理工具** @author feng*/
public class ManagerUtil {public static <R, T> R handle(ProcessorContext<T> context) {AbstractProcessorTemplate<T, R> processor = ProcessorFactory.getProcessor(context.getAction());if (processor == null) {throw new RuntimeException("未找到 " + context.getAction() + "对应的处理器");}return processor.start(context);}
}

2.5 测试

2.5.1 订单创建处理器

package org.feng.test;import lombok.extern.slf4j.Slf4j;
import org.feng.clog.AbstractProcessorTemplate;
import org.feng.clog.LogRecordContext;
import org.feng.clog.ProcessorContext;
import org.feng.clog.annotation.CLog;
import org.feng.clog.annotation.ProcessorBean;
import org.feng.clog.enums.ActionTypeEnum;
import org.feng.clog.enums.ModuleEnum;
import org.feng.clog.utils.StringUtil;
import org.springframework.stereotype.Service;/*** 创建订单处理器** @author feng*/
@Slf4j
@Service
@ProcessorBean(action = ActionTypeEnum.ORDER_CREATE)
public class OrderCreateProcessor extends AbstractProcessorTemplate<OrderCreateReq, Boolean> {@Overrideprotected void init(ProcessorContext<OrderCreateReq> context) {preHandleReq(context.getData());}@Override@CLog(template = "测试日志记录,{testK1}", module = ModuleEnum.ORDER, actionType = ActionTypeEnum.ORDER_CREATE,actionIdEl = "{#context.data.orderNum}", ext = "{JacksonUtil.toJSONString(#context.data)}")public Boolean handle(ProcessorContext<OrderCreateReq> context) {LogRecordContext.put("testK1", "3wewd2");OrderCreateReq orderCreateReq = context.getData();log.info("处理--创建订单{}", orderCreateReq.getOrderNum());return true;}@Overrideprotected void after(ProcessorContext<OrderCreateReq> context, Boolean result) {// todo 后置操作}private void preHandleReq(OrderCreateReq req) {// todo 参数校验// 例如校验参数if (StringUtil.isBlank(req.getOrderNum())) {throw new IllegalArgumentException("订单号不能为空");}}
}

2.5.2 订单管理者

package org.feng.test;import org.feng.clog.ProcessorContext;
import org.feng.clog.enums.ActionTypeEnum;
import org.feng.clog.utils.ManagerUtil;
import org.springframework.stereotype.Component;/*** 订单管理** @author feng*/
@Component
public class OrderManager {/*** 创建订单*/public Boolean createOrder(OrderCreateReq req) {ProcessorContext<OrderCreateReq> processorContext = new ProcessorContext<>();processorContext.setAction(ActionTypeEnum.ORDER_CREATE);processorContext.setData(req);return ManagerUtil.handle(processorContext);}
}

2.5.3 订单控制器

package org.feng.test;import org.feng.clog.utils.ResultUtil;
import org.feng.clog.vo.ResultVo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** 控制器** @author feng*/
@RestController
@RequestMapping("order")
public class OrderController {@Resourceprivate OrderManager orderManager;// @WithoutLogin@PostMapping("/test1")public ResultVo<String> test1(@RequestBody OrderCreateReq req) {// 创建Boolean started = orderManager.createOrder(req);return ResultUtil.success("success " + started);}
}

2.5.4 测试报文

{"orderNum": "1001","type": 1,"senderName": "","likes": ["1", "2", "3"]
}

2.5.5 测试结果

控制台日志输出:

2024-02-28 11:48:40.102  INFO  92309 --- [log-thread-pool-1] org.feng.clog.aspect.LogAspect.lambda$addLog$0(LogAspect.java:95) : [logId=d3b0dc267ce64dfa8a987e8eb6aad4ba] 记录日志,user=UserVo(id=1001, username=feng123, phone=18143431243, email=null), template=测试日志记录,3wewd2, module=订单, actionType=订单创建, actionId=1001, ext={"senderName":"","orderNum":"1001","type":1,"likes":["1","2","3"]}

可以看到,日志中记录了logId,以及日志注解对应的信息。

附录

1、其他相关文章

  • SpringBoot自定义starter之接口日志输出
  • SpringBoot使用线程池之ThreadPoolTaskExecutor和ThreadPoolExecutor
http://www.dinnco.com/news/50208.html

相关文章:

  • 自己做电影网站需要什么看书网站排名
  • 杭州发布官网天津优化网络公司的建议
  • 合肥做拼拼团网站的公司百度竞价专员
  • 鞋图相册网站怎么做泉州全网营销优化
  • 组建网站需多少钱青岛关键词排名哪家好
  • 专业模板网站制作服务关键词网络推广企业
  • 租赁服务器的网站seo搜索优化公司排名
  • 做衣服哪个网站好短期培训班学什么好
  • 兰州企业网站建设哪家好网络电商推广方案
  • 郑州做品牌网站的公司个人博客搭建
  • wordpress怎样设置友情链接株洲seo优化
  • wordpress 文章页当前栏目链接seo项目培训
  • 河北涿州建设局网站seo公司后付费
  • 滕州个人兼职做网站52种新颖的促销方式
  • 做二手房怎找房源网站seo推广知识
  • 东莞网站建设公司辉煌大厦互联网营销怎么赚钱
  • 信誉好的顺德网站建设郑州网络推广报价
  • 视觉中国网站百度竞价是什么
  • 服务器租用网站模板日本网站源码
  • 设计师自己做网站百度seo优化怎么做
  • 餐饮公司网站建设的特点关键词挖掘查询工具
  • 常州网约车营运证怎么办理企业网站优化软件
  • 网站建站报价单免费网站申请注册
  • 企业网站源码带支付媒体发稿网
  • wordpress区块链快讯模板谷歌seo服务公司
  • 川畅科技搜搜 网站设计上海网站建设优化
  • wordpress首页加速seo优化6个实用技巧
  • 网站内容图片怎么做的百度seo刷排名软件
  • 新网站做优化要准备什么小程序免费制作平台
  • php做的网站代码软文通