ApplicationEvent 监听事件实现异步保存日志

Posted 洛阳泰山

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ApplicationEvent 监听事件实现异步保存日志相关的知识,希望对你有一定的参考价值。

下面以spring boot项目为例

pom文件所需的依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.0</version>
        <relativePath/>
    </parent>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

SpringUtil工具类 


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

@Component
public class SpringUtil implements ApplicationContextAware {
    private static final Logger log = LoggerFactory.getLogger(SpringUtil.class);
    private static ConfigurableApplicationContext context;

    public SpringUtil() {
    }

    public static DefaultListableBeanFactory getBeanFactory() {
        return (DefaultListableBeanFactory)context.getBeanFactory();
    }

    public static <T> T getBean(Class<T> clazz) {
        return clazz == null ? null : context.getBean(clazz);
    }

    public static <T> T getBean(String beanId) {
        return beanId == null ? null : (T) context.getBean(beanId);
    }

    public static <T> T getBean(String beanName, Class<T> clazz) {
        if (null != beanName && !"".equals(beanName.trim())) {
            return clazz == null ? null : context.getBean(beanName, clazz);
        } else {
            return null;
        }
    }

    public static ApplicationContext getContext() {
        return context == null ? null : context;
    }

    public static void publishEvent(ApplicationEvent event) {
        if (context != null) {
            try {
                context.publishEvent(event);
            } catch (Exception var2) {
                log.error(var2.getMessage());
            }

        }
    }

    public static void registerBeanDefinition(String beanName, BeanDefinition definition) {
        getBeanFactory().registerBeanDefinition(beanName, definition);
    }

    public void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
        SpringUtil.context = (ConfigurableApplicationContext)context;
    }
}

登录事件

LoginLogEvent类继承ApplicationEvent

import com.tarzan.cms.module.admin.model.log.LoginLog;
import org.springframework.context.ApplicationEvent;

/**
 * 登录日志事件
 *
 * @author tarzan
 * @version 1.0
 * @date 2021年07月19日 09:41:28
 */
public class LoginLogEvent extends ApplicationEvent {

    public LoginLogEvent(LoginLog source) {
        super(source);
    }
}

登录事件监听器 


import com.tarzan.cms.common.event.LoginLogEvent;
import com.tarzan.cms.module.admin.model.log.LoginLog;
import com.tarzan.cms.module.admin.service.log.LoginLogService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * 登录日志事件监听
 *
 * @author tarzan
 * @version 1.0
 * @date 2021年04月19日 09:42:36
 */
@Slf4j
@AllArgsConstructor
@Component
public class LoginLogListener {

    private LoginLogService loginLogService;

    @Async
    @Order
    @EventListener(LoginLogEvent.class)
    public void saveLoginLog(LoginLogEvent event) {
        LoginLog loginLog = (LoginLog)event.getSource();
        loginLogService.saveOrUpdate(loginLog);
    }
}

 登录时候,调用

SpringUtil.publishEvent(new LoginLogEvent(loginLog)) ,监听器监听到事件,开始保存日志

  /**
     * 提交登录
     *
     * @param request
     * @param username
     * @param password
     * @param verification
     * @param rememberMe
     * @return
     */
    @PostMapping("/login")
    @ResponseBody
    public ResponseVo login(HttpServletRequest request, String username, String password, String verification,
                            @RequestParam(value = "rememberMe", defaultValue = "0") Integer rememberMe) {
        //判断验证码
        if (!CaptchaUtil.ver(verification, request)) {
            // 清除session中的验证码
            CaptchaUtil.clear(request);
            return ResultUtil.error("验证码错误!");
        }
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            token.setRememberMe(1 == rememberMe);
            Subject subject = SecurityUtils.getSubject();
            subject.login(token);
        } catch (LockedAccountException e) {
            token.clear();
            return ResultUtil.error("用户已经被锁定不能登录,请联系管理员!");
        } catch (AuthenticationException e) {
            token.clear();
            return ResultUtil.error("用户名或者密码错误!");
        }
        //更新最后登录时间
        User user=(User) SecurityUtils.getSubject().getPrincipal();
        userService.updateLastLoginTime(user);
        //异步保存登录日志
        String userType= request.getHeader(CoreConst.USER_TYPE_HEADER_KEY)==null?UserEnum.WEB.getName():request.getHeader(CoreConst.USER_TYPE_HEADER_KEY);
        saveLoginLog(userType,user);
        return ResultUtil.success("登录成功!");
    }


    /**
     * 保存日志
     *
     * @param userInfo
     * @return
     */
    private void saveLoginLog(String userType,User userInfo) {
        Date now = new Date();
        LoginLog loginLog = new LoginLog();
        loginLog.setCreateTime(now);
        loginLog.setStartTime(now);
        loginLog.setSourceIp(IpUtil.getIpAddr(WebUtil.getRequest()));
        if (userType.equals(UserEnum.WEB.getName())) {
            loginLog.setSource("PC登录");
        } else if (userType.equals(UserEnum.APP.getName())) {
            loginLog.setSource("APP登录");
        }
        loginLog.setName(userInfo.getNickname());
        loginLog.setPhone(userInfo.getPhone());
        loginLog.setLoginName(userInfo.getUsername());
        SpringUtil.publishEvent(new LoginLogEvent(loginLog));
    }

以上是关于ApplicationEvent 监听事件实现异步保存日志的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot 发布ApplicationEventPublisher和监听ApplicationEvent事件

Java:SpringBoot实现ApplicationEvent事件的监听和发布

ApplicationEvent 与ApplicationListener 异步化执行测试

ApplicationEvent事件处理

Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法

如何实现Application event,观察者模式