3.项目中通用的系统日志
在实际项目中,我们通常会基于注解和AOP实现系统日志功能,即记载用户调用标有日志注解的方法的一些使用信息。使用注解完成该功能还是走注解三板斧流程,链接可查看2.基础加强版面试题 - 求知律己 - 博客园 (cnblogs.com)中第四节中的注解。
1.定义注解
定义注解其实就是创建一个注解,定义其是否被Javadoc工具编译成文档(@Document),注解可以使用的地方(@Target),注解的有效期(@Retention),注解是否可被子类继承(@Inherit)。
@Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OperateLog { //信息 String message() default ""; //模块名 String modular() default ""; //业务类型(0:其他 1:新增 2:修改 3:删除) public BusinessType businessType() default BusinessType.OTHER; //操作类型(0 成功、1失败) String operateType() default ""; //操作明细 String operateDetailsType() default ""; }
2.使用注解
使用注解其实就是将注解标注在哪些地方使用。
上述图片中,箭头指向的地方表示注解使用在方法上,其实在定义注解的代码中已经通过@Target(ElementType.Method)标识注解使用在方法上。
3.为注解注入灵魂
为注解注入灵魂,即通过反射、注解、AOP思想对我们执行的方法提供额外的逻辑操作。如本节是为方法提供日志记载功能,也就是在方法执行前,将用户调用方法执行某操作的信息持久化到数据库中,方便信息监控和分析。
import com.ku.owo.common.aspectj.annotations.OperateLog; import com.ku.owo.common.domain.SystemThreadLocal; import com.ku.owo.entity.OwoOperateLog; import com.ku.owo.entity.UserInfo; import com.ku.owo.service.IOwoOperateLogService; import com.ku.owo.utils.ip.IPUtils; import com.ku.owo.utils.RequestUtil; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Date; /** * 系统日志,切面处理类 */ @Aspect @Component public class LogAspect { @Autowired private IOwoOperateLogService operateLogService; private static Logger logger = LoggerFactory.getLogger(LogAspect.class); public LogAspect() { logger.info("************SysLogAspect********************"); } //切入点注解:execution():方法执行的切入点。 // *表示通配符表示匹配任意的返回类型。 //com.ku.owo.controller表示包路径,指定匹配该包以该包底下的子类。 //..通配符表示匹配包下或子包所有类和方法 //*通配符表示匹配任意的类名 //.通配符表示包路径分隔符 //*通配符表示匹配任意的方法名 //(..)参数列表:表示匹配任意参数类型和数量的方法。 @Pointcut("execution(* com.ku.owo.controller..*.*(..))") public void controllerAspect() { } @Before("controllerAspect()") public void doBefore(JoinPoint joinPoint) { logger.info("=====SysLogAspect前置通知开始====="); } @AfterThrowing(value = "controllerAspect()", throwing = "e") public void doAfter(JoinPoint joinPoint, Exception e) { logger.info("=====SysLogAspect异常通知开始====="); } @Around("controllerAspect()") public Object interceptorApplogicA(ProceedingJoinPoint point) throws Throwable { logger.info("=====SysLogAspect 环绕通知开始====="); long beginTime = System.currentTimeMillis(); // 执行方法 Object result = point.proceed(); // 执行时长(毫秒) long time = System.currentTimeMillis() - beginTime; // 保存日志 saveSysLog(point, time); return result; } @Before("controllerAspect()") public void doAfter(JoinPoint joinPoint) { logger.info("=====SysLogAspect后置通知开始====="); } private void saveSysLog(ProceedingJoinPoint joinPoint, long time) { //通过连接点获取签证 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod();//再通过签证调用反射的方法 OwoOperateLog owoOperateLog = new OwoOperateLog(); //获取方法上的注解 OperateLog operateLog = method.getAnnotation(OperateLog.class); try { if (operateLog != null) { // 注解上的描述 // 请求的方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); owoOperateLog.setDetails(className + "." + methodName + "()"); // 请求的参数 Object[] args = joinPoint.getArgs(); // 获取request HttpServletRequest request = RequestUtil.getRequest(); owoOperateLog.setIp(IPUtils.getIpAddr(request)); owoOperateLog.setState(0); owoOperateLog.setModular(operateLog.modular()); owoOperateLog.setMessage(operateLog.message()); owoOperateLog.setBusinessType(operateLog.businessType().ordinal()); UserInfo user = SystemThreadLocal.get(); if (null != user) { String userId = user.getUserId(); owoOperateLog.setCreateUser(userId); } owoOperateLog.setCreateTime(new Date()); operateLogService.save(owoOperateLog); } } catch (Exception e) { e.printStackTrace(); logger.error(e.getMessage()); } logger.info("owoOperateLog=======>" + owoOperateLog.toString()); } }
4.数据表、实体类
由于我们的系统日志需要持久化到数据库中,因此需要创建表来存储系统日志,实体类来完成系统日志的对象映射。
DROP TABLE IF EXISTS `owo_operate_log`; CREATE TABLE `owo_operate_log` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `ip` VARCHAR(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作主机id', `modular` VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '模块', `message` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作信息', `business_type` TINYINT(4) NULL DEFAULT NULL COMMENT '业务类型(0其它 1新增 2修改 3删除)', `state` INT(11) NULL DEFAULT NULL COMMENT '操作状态(1:失败,0成功)', `details` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '详情', `create_user` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建者', `create_time` DATETIME NULL DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`) USING BTREE ) ENGINE = INNODB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '操作日志表' ROW_FORMAT = DYNAMIC;
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("owo_operate_log") @ApiModel(value="OwoOperateLog对象", description="操作日志表") public class OwoOperateLog implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Integer id; @ApiModelProperty(value = "操作主机id") private String ip; @ApiModelProperty(value = "模块") private String modular; @ApiModelProperty(value = "操作信息") private String message; @ApiModelProperty(value = "业务类型(0其它 1新增 2修改 3删除)") private Integer businessType; @ApiModelProperty(value = "操作状态(1:失败,0成功)") private Integer state; @ApiModelProperty(value = "详情") private String details; @ApiModelProperty(value = "创建者") private String createUser; @ApiModelProperty(value = "创建时间") private Date createTime; }