SpringBoot基于注解切面监听事件

Posted 一点点

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot基于注解切面监听事件相关的知识,希望对你有一定的参考价值。

创建监听器三步骤:

1、事件(event)可以封装和传递监听器中要处理的参数,如对象或字符串,并作为监听器中监听的目标。

2、监听器(listener)具体根据事件发生的业务处理模块,这里可以接收处理事件中封装的对象或字符串。

3、事件发布者(publisher)事件发生的触发者。

代码展示:

pom.xml

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.6.8</version>
        </dependency>

第一步:

定义一个事件,需要继承spring的ApplicationEvent
package top.xzhand.event;

import org.springframework.context.ApplicationEvent;

/**
 * 定义一个事件,需要继承spring的ApplicationEvent
 */
public class LogEvent extends ApplicationEvent {

    public LogEvent(Object source) {
        super(source);
    }
}

第二步:

定义切面,发布事件

@Component
@Aspect
这两个注解必须添加
package top.xzhand.event.aspect;

import com.alibaba.fastjson.JSON;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import top.xzhand.event.LogEvent;
import top.xzhand.po.RequestLog;
import org.aspectj.lang.JoinPoint;
import top.xzhand.util.LogUtil;
import java.util.Date;

/**
 * 切面发布
 */

@Component
@Aspect
public class LogAspect {
    public static final ThreadLocal<RequestLog> THREAD_LOCAL = new ThreadLocal<>();

    @Autowired
    private ApplicationContext applicationContext;

    @Pointcut("@annotation(top.xzhand.event.common.Log)")
    public void logAspect() {

    }

    @Before(value = "logAspect()")
    public void before(JoinPoint point) throws Throwable {
        RequestLog requestLog = new RequestLog();
        requestLog.setCreateAt(new Date());//开始时间
        Environment environment = applicationContext.getEnvironment();
        String appName = environment.getProperty("spring.application.name");
//		sysLog.setCreateName(createName);
        THREAD_LOCAL.set(LogUtil.getSysLog(point,requestLog));
        System.out.println("进入切面:"+JSON.toJSONString(requestLog));
    }

    @AfterReturning(returning = "rvt", pointcut = "logAspect()")
    public void afterReturning(JoinPoint point, Object rvt) throws Throwable {
        RequestLog sysLog = get();
        if (rvt != null) {
            sysLog.setResponseResult(LogUtil.getText(JSON.toJSONString(rvt)));
        } else {
            sysLog.setResponseResult(null);
        }
        publishEvent(sysLog);
        System.out.println("切面监听事件发布成功:"+JSON.toJSONString(sysLog));
    }

    private void publishEvent(RequestLog sysLog) {
        applicationContext.publishEvent(new LogEvent(sysLog));
        THREAD_LOCAL.remove();
    }

    @AfterThrowing(pointcut = "logAspect()", throwing = "e")
    public void afterThrowing(Throwable e) {
        RequestLog sysLog = get();
        publishEvent(sysLog);
    }

    private RequestLog get() {
        RequestLog sysLog = THREAD_LOCAL.get();
        if (sysLog == null) {
            return new RequestLog();
        }
        return sysLog;
    }

}

 定义切入点注解

package top.xzhand.event.common;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 方法级别 日志
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Log {

    String value() default "";
}

 相关实体类,mapper,service等忽略,可自己生成!

package top.xzhand.po;

import java.util.Date;

public class RequestLog {
    private Integer id;

    private String requestUrl;

    private String requestArgs;

    private String ipUrl;

    private String message;

    private String responseResult;

    private Date createAt;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRequestUrl() {
        return requestUrl;
    }

    public void setRequestUrl(String requestUrl) {
        this.requestUrl = requestUrl == null ? null : requestUrl.trim();
    }

    public String getRequestArgs() {
        return requestArgs;
    }

    public void setRequestArgs(String requestArgs) {
        this.requestArgs = requestArgs == null ? null : requestArgs.trim();
    }

    public String getIpUrl() {
        return ipUrl;
    }

    public void setIpUrl(String ipUrl) {
        this.ipUrl = ipUrl == null ? null : ipUrl.trim();
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message == null ? null : message.trim();
    }

    public String getResponseResult() {
        return responseResult;
    }

    public void setResponseResult(String responseResult) {
        this.responseResult = responseResult == null ? null : responseResult.trim();
    }

    public Date getCreateAt() {
        return createAt;
    }

    public void setCreateAt(Date createAt) {
        this.createAt = createAt;
    }
}

  

package top.xzhand.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import lombok.experimental.UtilityClass;
import org.aspectj.lang.JoinPoint;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import top.xzhand.event.common.Log;
import top.xzhand.po.RequestLog;

import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.Date;

@UtilityClass // 方法变量 静态话,类final 私有构造器
public class LogUtil {

	public RequestLog getSysLog(JoinPoint point, RequestLog sysLog) {

		HttpServletRequest request = getRequest();
		sysLog.setIpUrl(getIP(request));
		sysLog.setRequestUrl(URLUtil.getPath(request.getRequestURI()));
		sysLog.setRequestArgs(request.getQueryString());
		sysLog.setCreateAt(new Date());
		
		return sysLog;
	}

	private HttpServletRequest getRequest() {
		return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
	}
	private final String UNKNOWN = "unknown";

	public String getIP(HttpServletRequest request) {
		String ip = request.getHeader("X-Requested-For");
		if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
			ip = request.getHeader("X-Forwarded-For");
		}
		if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return StrUtil.isBlank(ip) ? null : ip.split(",")[0];
	}
	public String getText(String val) {
		return StrUtil.sub(val, 0, 65535);
	}

	/***
	 * 获取操作信息
	 * 
	 * @param point
	 * @return
	 */
	public String getControllerMethodDescription(JoinPoint point) {
		try {
			// 获取连接点目标类名
			String targetName = point.getTarget().getClass().getName();
			// 获取连接点签名的方法名
			String methodName = point.getSignature().getName();
			// 获取连接点参数
			Object[] args = point.getArgs();
			// 根据连接点类的名字获取指定类
			Class targetClass = Class.forName(targetName);
			// 获取类里面的方法
			Method[] methods = targetClass.getMethods();
			String description = "";
			for (Method method : methods) {
				if (method.getName().equals(methodName)) {
					Class[] clazzs = method.getParameterTypes();
					if (clazzs.length == args.length) {
						description = method.getAnnotation(Log.class).value();
						break;
					}
				}
			}
			return description;
		} catch (Exception e) {
			return "";
		}
	}

	/**
	 * 获取堆栈信息
	 *
	 * @param throwable
	 * @return
	 */
	public  String getStackTrace(Throwable throwable) {
		StringWriter sw = new StringWriter();
		try (PrintWriter pw = new PrintWriter(sw)) {
			throwable.printStackTrace(pw);
			return getText(sw.toString());
		}
	}
}

 

package top.xzhand.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import top.xzhand.po.RequestLog;


/**
 * 事件发布
 *
 */
@Component
public class EventPublister {
    @Autowired
    private ApplicationContext applicationContext;

    // 事件发布方法
    public void pushListener(RequestLog requsetLog) {
        applicationContext.publishEvent(new LogEvent(requsetLog));
    }

}

  

第三步:

 自定义监听器处理业务

package top.xzhand.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import top.xzhand.po.RequestLog;
import top.xzhand.service.RequestLogService;

/**
 * 自己定义的监听器需要实现ApplicationListener,
 * 同时泛型参数要加上自己要监听的事件Class名,
 * 在重写的方法onApplicationEvent中,添加自己的业务处理
 */
@Component
public class LogListerner  implements ApplicationListener<LogEvent> {
    @Autowired
    private RequestLogService requestLogService;
    @Override
    public void onApplicationEvent(LogEvent logEvent) {
        RequestLog requestLog=(RequestLog) logEvent.getSource();
        requestLogService.insertSelective(requestLog);

    }
}

 验证测试:

package top.xzhand.controller;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import top.xzhand.event.EventPublister;
import top.xzhand.event.common.Log;
import top.xzhand.po.RequestLog;
import top.xzhand.util.LogUtil;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;


/**
 *
 */
@Controller
public class TestEventListenerController {
    @Autowired
    private EventPublister publisher;


    /**
     * 非注解形式监听事件
     * @param request
     * @param arg
     */
    @RequestMapping(value = "/test/logEvent1" )
    public void testPublishEvent1(HttpServletRequest request,String arg ){
        RequestLog requestLog=new RequestLog();
        requestLog.setCreateAt(new Date());
        requestLog.setRequestUrl(request.getContextPath());
        requestLog.setIpUrl(LogUtil.getIP(request));
        requestLog.setRequestArgs(request.getQueryString());
        publisher.pushListener(requestLog);
        System.out.println(JSON.toJSONString(requestLog));
    }


    /**
     * 基于注解的切面事件发布监听
     * @param request
     * @param arg
     */
    @Log
    @RequestMapping(value = "/test/logEvent2" )
    public void testPublishEvent2(HttpServletRequest request,String arg ){

        System.out.println("切面注解监听");
    }


}

 日志成功记录

 

以上是关于SpringBoot基于注解切面监听事件的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot 中的 Aop + 自定义注解

Spring事件监听机制

Spring boot切面不生效问题

springboot 面向切面编程之使用自定义注解

springboot原理

spring切面Aspects