当一个 service 执行的时间超过 3s,打印 error 级别的日志,当执行时间为 2s 至 3s,打印 warn 级别的日志,2s 以下打印 info 级别日志
    在 foodie-dev 的 pom 文件添加 Spring AOP 的依赖

    1. <dependency>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-aop</artifactId>
    4. </dependency>

    AOP 通知

    1. 前置通知:在方法调用之前执行
    2. 后置通知:在方法正常调用之后执行
    3. 环绕通知:在方法调用之前和之后,都分别可以执行的通知
    4. 异常通知:如果在方法调用过程中发生异常,则通知
    5. 最终通知:在方法调用之后执行

    后置通知和最终通知区别:方法调用异常,后置通知不执行,最终通知执行,最终通知类似于 try-catch 中的 finally
    在 foodie-dev-api 模块新建 com.imooc.aspect 包并创建 ServiceLogAspect 类

    package com.imooc.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    /**
     * Created by 92578 on 2020/8/22 11:19
     **/
    @Aspect
    @Component
    public class ServiceLogAspect {
    
        public static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);
    
        /**
         * 切面表达式:
         * execution 代表所要执行的表达式主体
         * 第一处 * 代表方法返回类型,* 代表所有类型
         * 第二处 包名代表 aop 监控的类所在的包
         * 第三处 .. 代表该包以及其子包下的所有类方法
         * 第四处 * 代表类名,* 代表所有类
         * 第五处 *(..) * 代表类中的方法名,(..) 表示方法中的任何参数
         * @param joinPoint
         * @return
         * @throws Throwable
         */
        @Around("execution(* com.imooc.service.impl..*.*(..))")
        public Object recordTimeLog(ProceedingJoinPoint joinPoint) throws Throwable {
            logger.info("====== 开始执行 {}.{} ======",
                    joinPoint.getTarget().getClass(),
                    joinPoint.getSignature().getName());
    
            // 记录开始时间
            long begin = System.currentTimeMillis();
    
            // 执行目标 service
            Object result = joinPoint.proceed();
    
            // 记录结束时间
            long end = System.currentTimeMillis();
            long takeTime = end - begin;
    
            if (takeTime > 3000) {
                logger.error("====== 执行结束,耗时:{} 毫秒 ======", takeTime);
            } else if (takeTime > 2000) {
                logger.warn("====== 执行结束,耗时:{} 毫秒 ======", takeTime);
            } else {
                logger.info("====== 执行结束,耗时:{} 毫秒 ======", takeTime);
            }
    
            return result;
        }
    }
    

    启动项目,打开浏览器,访问 http://localhost:8080/foodie-shop/register.html
    输入用户名“i”,查看控制台,发现已经打印相关日志
    image.png
    在 foodie-dev-service 模块中的 UserServiceImpl 类添加线程睡眠代码,模拟超过 3s 后的场景

    package com.imooc.service.impl;
    
    import com.imooc.enums.Sex;
    import com.imooc.mapper.UsersMapper;
    import com.imooc.pojo.Users;
    import com.imooc.pojo.bo.UserBO;
    import com.imooc.service.UserService;
    import com.imooc.utils.DateUtil;
    import com.imooc.utils.MD5Utils;
    import org.n3r.idworker.Sid;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    import tk.mybatis.mapper.entity.Example;
    
    import java.util.Date;
    
    /**
     * @author 92578
     * @since 1.0
     */
    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        private UsersMapper usersMapper;
    
        @Autowired
        private Sid sid;
    
        private static final String USER_FACE = "http://dp.gtimg.cn/discuzpic/0/discuz_x5_gamebbs_qq_com_forum_201306_19_1256219xc797y90heepdbh.jpg/0";
    
        /**
         * 判断用户名是否存在
         *
         * @param username 用户名
         * @return 是否存在
         */
        @Transactional(propagation = Propagation.SUPPORTS)
        @Override
        public boolean queryUsernameIsExist(String username) {
            Example userExample = new Example(Users.class);
            Example.Criteria userCriteria = userExample.createCriteria();
    
            userCriteria.andEqualTo("username", username);
    
            Users result = usersMapper.selectOneByExample(userExample);
    
            return result == null ? false : true;
        }
    
        /**
         * 创建用户
         *
         * @param userBO
         * @return
         */
        @Transactional(propagation = Propagation.REQUIRED)
        @Override
        public Users createUser(UserBO userBO) {
            try {
                Thread.sleep(3500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String userId = sid.nextShort();
            Users user = new Users();
            user.setId(userId);
            user.setUsername(userBO.getUsername());
            try {
                user.setPassword(MD5Utils.getMD5Str(userBO.getPassword()));
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 默认用户昵称同用户名
            user.setNickname(userBO.getUsername());
            // 默认头像
            user.setFace(USER_FACE);
            // 默认生日
            user.setBirthday(DateUtil.stringToDate("1900-01-01"));
            // 默认性别为 保密
            user.setSex(Sex.secret.type);
            user.setCreatedTime(new Date());
            user.setUpdatedTime(new Date());
    
            usersMapper.insert(user);
    
            return user;
        }
    
        @Transactional(propagation = Propagation.SUPPORTS)
        @Override
        public Users queryUserForLogin(String username, String password) {
            try {
                Thread.sleep(2500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            Example userExample = new Example(Users.class);
            Example.Criteria userCriteria = userExample.createCriteria();
    
            userCriteria.andEqualTo("username", username);
            userCriteria.andEqualTo("password", password);
    
            Users result = usersMapper.selectOneByExample(userExample);
    
            return result;
        }
    }
    

    重启项目,打开浏览器,访问 http://localhost:8080/foodie-shop/register.html
    注册用户名“imooc123”,密码“123123”,点击“注册”,此时查看控制台,发现打印 error 级别日志
    image.png
    清空浏览器 cookie,访问 http://localhost:8080/foodie-shop/login.html 进行登录
    输入用户名“imooc”,密码“123123”,点击“登录”,此时查看控制台,发现打印 warn 级别日志
    image.png