AOP记录操作日志【SpringBoot+MyBatis-Plus+MySQL】

1. 实现AOP记录面向用户的日志

① 添加依赖:在pom.xml中添加以下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- 整合mysql -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- aop依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.1</version>
    </dependency>

</dependencies>

② 修改配置文件:在application.properties中添加一下配置:

# 应用服务 WEB 访问端口
server.port=8080
#mysql数据库连接信息配置
#mysql驱动
spring.datasource.driverClassName=com.mysql.jdbc.Driver
#数据库连接信息
spring.datasource.url=jdbc:mysql://localhost:3306/log?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
#数据库用户名
spring.datasource.username=root
#数据库密码
spring.datasource.password=123456
spring.aop.auto=true
# mybatis-plus.configuration.map-underscore-to-camel-case=true

③ 自定义注解:在启动类的同级包下边新建一个config包,在这个报下边新建new一个名为Log的Annotation文件,文件内容如下:
在这里插入图片描述

元注解:用来对注解进行注解的注解类
@Target: 描述注解的使用范围,取值有ElementType.TYPE(类、接口、枚举类)、FIELD(成员变量)、METHOD(成员方法)等等;
@Retention:描述注解保留的时间范围,取值有三种:RetentionPolicy.SOURCE(源文件保留)、CLASS(编译期保留,默认值)、RUNTIME(运行期保留,可以通过反射获取注解信息);
@Documented:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。
@Inherited:使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解)。

④ 准备数据库表以及实体类SysLog
在这里插入图片描述

@Data
//省去代码中大量的get()、 set()、 toString()等方法;
@TableName("sys_log")
//指定实体类和数据库表的映射关系。当实体类的类名在转成小写后和数据库表名相同时,可以不指定该注解。
public class SysLog implements Serializable {
    @TableId(type = IdType.AUTO)
    //指定实体类的某个属性为对应的主键
    private Long id;

    @TableField("user_id")
    //如果我们开启了驼峰命名,就不用再指定value属性了
    private Long userId;

    @TableField("user_action")
    private String userAction;

    @TableField("create_time")
    private Timestamp createTime;
}

⑤ 创建SysLogMapper.class文件

@Mapper
public interface SysLogMapper extends BaseMapper<SysLog> {
}

⑥ 创捷Service日志接口ISysLogServcie.class 以及实现类SysLogServiceImpl.class

public interface ISysLogServcie extends IService<SysLog> {
    /**
     * 插入日志
     * @param entity
     * @return
     */
    int insertLog(SysLog entity);
}
@Service
public class SysLogServiceImpl extends ServiceImpl<SysLogMapper,SysLog> implements ISysLogServcie {
    @Autowired
    private SysLogMapper sysLogMapper;

    @Override
    public int insertLog(SysLog entity) {
        return sysLogMapper.insert(entity);
    }
}

⑦ AOP的切面和切点:将该类放在config包下,命名为LogAsPect.java,内容如下:

@Aspect//这个注解表示将当前类视为一个切面类
@Component//将当前类交由Spring管理
public class LogAspect {
    private final static Logger log= org.slf4j.LoggerFactory.getLogger(LogAspect.class);

    @Autowired
    private ISysLogServcie sysLogServcie;

    @Pointcut("@annotation(com.dianxin.operationlog.config.Log)")
    //切点表达式,定义我们的匹配规则,上边我们使用@Pointcut("@annotation(com.dianxin.operationlog.config.Log)")表示匹配带有我们自定义注解的方法。
    public void pointcut(){}

    @Around("pointcut()")
    //@Around:环绕通知,可以在目标方法执行前后执行一些操作,以及目标方法抛出异常时执行的操作。
    public Object around(ProceedingJoinPoint point){
        Object result=null;
        long beginTime= System.currentTimeMillis();

        try {
            log.info("我在目标方法之前执行");
            result = point.proceed();
            long endTime = System.currentTimeMillis();
            insertLog(point,endTime-beginTime);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    private void insertLog(ProceedingJoinPoint point , long time){
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        SysLog sys_Log = new SysLog();

        Log userAction = method.getAnnotation(Log.class);
        if(userAction != null){
            //注解上的描述
            sys_Log.setUserAction(userAction.value());
        }

        String className = point.getTarget().getClass().getName();//类名
        String methodName = signature.getName();//方法名
        String args = Arrays.toString(point.getArgs());//请求的方法参数值

        Long userid=1L;//应该从session中获取当前的登陆人id
        sys_Log.setUserId(userid);
        sys_Log.setCreateTime(new Timestamp(new Date().getTime()));

        log.info("当前登陆人:{},类名:{},方法名:{},参数:{},执行时间:{}",userid,className,methodName,args,time);
        sysLogServcie.insertLog(sys_Log);
    }

⑧ 测试控制器:在controller包下新建一个HomeController.java,内容如下:

@Controller
public class HomeController {
    private final static Logger log = LoggerFactory.getLogger(HomeController.class);

    @Autowired
    private ISysLogServcie logServcie;

    @RequestMapping("/aop")
    @ResponseBody
    @Log("测试aoplog")
    public Object aop(String name,String nick){
        Map<String,Object> map=new HashMap<>();
        log.info("我被执行了!");
        map.put("res","ok");
        return map;
    }
}

浏览器访问localhost:8080/aop?name=xfr&nick=eran,看到数据库成功插入一下数据:
在这里插入图片描述

2. 实现AOP记录面向开发者的日志

使用该方式的应用场景,在项目中出现了bug,想要知道前台的请求是否进入了我们控制器中,以及参数的获取情况。原理跟上边是一样的,只是切点的匹配规则变了而已,而且不用将日志记录到数据库。
LogAsPect.java

@Aspect
@Component
public class LogAspect {
    ...
    @Pointcut("execution(public * com.dianxin.operationlog.controller..*.*(..))")
    //表示匹配com.dianxin.operationlog.controller包及其子包下的所有公有方法。
    public void pointcutController(){}

    @Before("pointcutController()")
    //目标方法执行之前执行以下方法体的内容
    public void around2(JoinPoint point){
        String methodName = point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName();
        String params = Arrays.toString(point.getArgs());
        log.info("get in {} params :{}",methodName,params);
    }
}

HomeController.java

@RequestMapping("/testaop3")
@ResponseBody
public Object testAop3(String name,String nick){
    Map<String,Object> map = new HashMap<>();
    map.put("res","ok");
    return map;
}
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值