在实际项目中,我们通常会基于注解和AOP实现系统日志功能,即记载用户调用标有日志注解的方法的一些使用信息。使用注解完成该功能还是走注解三板斧流程,链接可查看2.基础加强版面试题 - 求知律己中第四节中的注解。
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;
}
实体类