AOP进阶
适用于定时任务,或者业务系统全局报错日志查看工具
1.引入依赖和相关坐标这里需要用到AOPSPring和mybatis-plus以及其他依赖
以cloud项目为例
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
<version>3.5.7</version>
</dependency>
2.定义注解类简化开发使用
import com.vt.api.business.enum_data.BaseEnum;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
/**
* @fallbackMessage 传入值描述
* @service 默认传入要清库的service(!!!!谨慎使用,传入后会清空整个库!!!!)
*/
public @interface Fallback {
String fallbackMessage() default "";
BaseEnum type()default BaseEnum.TB;
Class<?> service() default Object.class;
}
3.定义枚举类规范报错与同步类型
import lombok.Getter;
@Getter
public enum BaseEnum {
//这里可以根据自己需求自定义
TB("同步"),//同步
SB("上报"),//上报
SC("数据生成");//数据生成
// 获取枚举值的方法
private final String value;
BaseEnum(String value) {
this.value = value;
}
// 重写 toString 方法,返回对应的值
@Override
public String toString() {
return String.valueOf(this.value);
}
}
4.定义切面类
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.vt.api.business.entity_extend.ExtendProcessResult;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
@Slf4j
public class FallbackAspect {
public FallbackAspect(ProcessorUtil processorUtil, ApplicationContext applicationContext) {
this.processorUtil = processorUtil;
this.applicationContext = applicationContext;
}
private final ProcessorUtil processorUtil;
private final ApplicationContext applicationContext;
@Around("@annotation(fallback)")
public Object around(ProceedingJoinPoint joinPoint, Fallback fallback) throws Throwable {
String msg = fallback.fallbackMessage();
String type = String.valueOf(fallback.type().getValue());
try {
Object result = joinPoint.proceed();
if (result instanceof ExtendProcessResult) {
ExtendProcessResult processResult = (ExtendProcessResult) result;
Integer size = processResult.getSize();
if (size != null) {
boolean isNotObjectClass = !Object.class.equals(fallback.service());
processorUtil.saveProvinceDataSuccess(msg, size, type);
if (isNotObjectClass) {
executeServiceRemoveMethod(fallback.service());
}
}
}
return result;
} catch (Exception e) {
e.printStackTrace();
log.error("--------------{}失败---------------------", msg, e);
// 获取异常堆栈信息
StackTraceElement[] stackTrace = e.getStackTrace();
String exceptionMessage = e.getMessage();
String where = "";
for (StackTraceElement element : stackTrace) {
String className = element.getClassName();
if (className.startsWith("com.vt.api")) {
String fileName = element.getFileName();
String methodName = element.getMethodName();
int lineNumber = element.getLineNumber();
where = "(异常出现在) " + fileName + "(在方法)" + methodName + "(位于) " + lineNumber + "(行)";
if (element.getLineNumber() != -1) {
break;
}
}
}
if (stackTrace.length > 0) {
StackTraceElement element = stackTrace[0];
String fileName = element.getFileName();
String methodName = element.getMethodName();
int lineNumber = element.getLineNumber();
where += "\n(底层异常出现在) " + fileName + "(底层在方法)" + methodName + "(位于) " + lineNumber + "(行)" + "(异常出现的直接原因)" + exceptionMessage;
}
log.error("错误信息{}", where);
processorUtil.saveError(msg, where,type);
return "失败";
}
}
private void executeServiceRemoveMethod(Class<?> serviceClass) {
Object serviceBean = applicationContext.getBean(serviceClass);
try {
QueryWrapper<Object> queryWrapper = new QueryWrapper<>();
Method remove = serviceClass.getMethod("remove", Wrapper.class);
boolean result = (boolean) remove.invoke(serviceBean, queryWrapper);
log.info("{}: remove 方法执行成功", serviceClass.getSimpleName());
} catch (Exception e) {
log.error("{}: remove 方法执行失败", serviceClass.getSimpleName(), e);
}
}
}
5.定义工具类用于保存错误日志
import cn.hutool.core.util.ObjectUtil;
import com.vt.api.business.entity.EnterpriseUploadData;
import com.vt.api.business.service.EnterpriseUploadDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
@Component
public class ProcessorUtil {
@Autowired
private EnterpriseUploadDataService enterpriseUploadDataService;
@Transactional(rollbackFor = Exception.class)
public void saveProvinceDataSuccess(String msg, int size, String type) {
if (ObjectUtil.isNotEmpty(size)) {
EnterpriseUploadData data = new EnterpriseUploadData();
data.setCnName("接口:" + msg+"成功"+"("+type+")");
data.setCreatedTime(LocalDateTime.now());
data.setType(type);
data.setSuccess(size);
data.setTenant(tenantId);
enterpriseUploadDataService.save(data);
}
}
public void saveError(String msg, String errMsg,String type) {
if (ObjectUtil.isNotEmpty(errMsg)) {
EnterpriseUploadData data = new EnterpriseUploadData();
data.setCnName("接口:" + msg+"失败"+"("+type+")");
data.setCreatedTime(LocalDateTime.now());
data.setReason(errMsg);
data.setType(type);
data.setTenant(tenantId);
enterpriseUploadDataService.save(data);
}
}
}
6.日志实体类//CustomTenantEntity为公共字段
import com.baomidou.mybatisplus.annotation.TableName;
import com.vt.framework.db_mybatis_plus_tenant.entity.CustomTenantEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "日志信息")
@TableName("t_enterprise_upload_data")
/**
* 该类必须继承删除标识
*/
public class EnterpriseUploadData extends CustomTenantEntity {
private Integer success;
private String error;
private String reason;
private String cnName;
private String type;
}
7.Mapper//这里集成BaseMapper即可
import com.vt.api.business.entity.EnterpriseUploadData;
import com.vt.framework.db_mybatis_plus_base.mapper.CustomBaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EnterpriseUploadDataMapper extends CustomBaseMapper<EnterpriseUploadData> {
}
8.Service//集成BaseService
import com.vt.api.business.entity.EnterpriseUploadData;
import com.vt.framework.db_mybatis_plus_base.service.CustomBaseService;
public interface EnterpriseUploadDataService extends CustomBaseService<EnterpriseUploadData> {
}
9.IMPL实现类集成Base实现类
import com.vt.api.business.entity.EnterpriseUploadData;
import com.vt.api.business.mapper.EnterpriseUploadDataMapper;
import com.vt.api.business.service.EnterpriseUploadDataService;
import com.vt.framework.db_mybatis_plus_base.service.impl.CustomBaseServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class EnterpriseUploadDataServiceImpl extends CustomBaseServiceImpl<EnterpriseUploadDataMapper, EnterpriseUploadData> implements EnterpriseUploadDataService {
}
表结构以Mysql为例//删除状态到租户标识为公共字段可以忽略
CREATE TABLE `t_enterprise_upload_data` (
`id` varchar(36) NOT NULL COMMENT '主键',
`delete_status` int DEFAULT NULL COMMENT '删除状态',
`revision` int DEFAULT NULL COMMENT '乐观锁',
`created_by` varchar(36) DEFAULT NULL COMMENT '创建人',
`created_org_by` varchar(32) DEFAULT NULL COMMENT '创建人所属机构',
`created_time` datetime DEFAULT NULL COMMENT '创建时间',
`updated_by` varchar(36) DEFAULT NULL COMMENT '修改人',
`updated_time` datetime DEFAULT NULL COMMENT '修改时间',
`tenant` varchar(32) DEFAULT NULL COMMENT '租户标识',
`success` varchar(255) DEFAULT NULL COMMENT '成功条数',
`error` varchar(255) DEFAULT NULL COMMENT '失败条数',
`reason` longtext COMMENT '失败原因(代码报错)',
`cn_name` varchar(255) DEFAULT NULL COMMENT '中文名称(定时任务名称)',
`type` varchar(255) DEFAULT NULL COMMENT '0数据同步1数据上报2数据生成',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='业务库接口上传日志';
基本使用案例注意在synchronizationCameraInformation方法中不要try cath异常要抛出给上层
@Override
@Fallback(fallbackMessage = "摄像头信息同步")
public ProcessResult process(TaskContext taskContext) {
String msg = "摄像头信息同步";
ExtendProcessResult processResult = new ExtendProcessResult();
processResult.setSuccess(true);
processResult.setMsg(msg + "成功");
log.info("--------------{}开始---------------------", msg);
this.synchronizationCameraInformation(processResult, msg);
log.info("--------------{}结束---------------------", msg);
return processResult;
}
在这里ExtendProcessResult类主要是为了存放信息,方便传递,利于获取
import lombok.*;
import tech.powerjob.worker.core.processor.ProcessResult;
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class ExtendProcessResult extends ProcessResult {
private Integer size;
}