1. Filter(过滤器)
filter是Servlet规范的内容,不属于Spring。通过filter,可以获取请求和响应信息,不能获取执行的类以及方法信息。
1.1 Filter类
package com.ruoyi.framework.filter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/**
*
*/
public class TimerFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
System.out.println("Time Filter init");
}
@Override
public void destroy() {
Filter.super.destroy();
System.out.println("time filter destroy");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
long start = System.currentTimeMillis();
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("time filter:"+(System.currentTimeMillis()-start));
System.out.println("time filter finish");
}
}
通过示例代码,可以看到,可以获取请求servletRequest和响应ServletResponse,但是并不知道经过了那个controller的那个方法处理过。
1.2 配置filter
package com.ruoyi.framework.config;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.filter.TimerFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.DispatcherType;
import java.util.HashMap;
import java.util.Map;
/**
*
*/
@Configuration
public class TimeFilterConfig {
private String urlPatterns = "/system/*";
@Bean
public FilterRegistrationBean timerFilterRegistration()
{
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new TimerFilter());
registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
registration.setName("timeFilter");
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
// Map<String, String> initParameters = new HashMap<String, String>();
// initParameters.put("excludes", excludes);
// registration.setInitParameters(initParameters);
return registration;
}
}
通过@Configuration和@Bean,可以实现Filter的注册。
此外,还可以在Filter类上添加注解来实现Filter注册。
@Component
@WebFilter(filterName ="TimeFilter",urlPatterns ="/*")
public class TimerFilter implements Filter {
......
}
2. Interceptor (拦截器)
可以获得Http原始的请求和响应信息,也拿得到执行请求的类信息和方法名(比如:com.ruoyi.web.controller.system.SysLoginController#login(HttpServletRequest, HttpServletResponse, ModelMap))。
2.1 Interceptor类实例
package com.ruoyi.framework.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
*/
@Component
public class TimeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(handler instanceof HandlerMethod)
{
HandlerMethod handlerMethod = (HandlerMethod)handler;
System.out.println("method name:" + handlerMethod.getMethod().getName());
System.out.println("class name:" + handlerMethod.getBean().getClass().getName());
}
System.out.println("handler:" + handler);
request.setAttribute("startTime",System.currentTimeMillis());
boolean res = HandlerInterceptor.super.preHandle(request, response, handler);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
Long start = (Long)request.getAttribute("startTime");
System.out.println("time interceptor 耗时:"+(System.currentTimeMillis()-start));
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
Long start = (Long)request.getAttribute("startTime");
System.out.println("time interceptor 耗时:"+(System.currentTimeMillis()-start));
}
}
从实例代码中,可以看到,可以获取请求HttpServletRequest和响应HttpServletResponse信息,还有一个参数handler,通过该参数,可以获取执行的方法信息。
2.2 interceptor注册
package com.ruoyi.framework.config;
import com.ruoyi.framework.interceptor.TimeInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
*
*/
@Configuration
public class TimeInterceptorWebConfig implements WebMvcConfigurer {
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor)
.addPathPatterns("/system/*")
.excludePathPatterns("/static/*")
.excludePathPatterns("/template/*");
}
}
通过@Configuration进行注册,并指定需要拦截的路径,以及不需要拦截的路径,一般静态资源路径不需要拦截。
3. ControllerAdvice(Controller增强)
ControllerAdvice常用的场景是作为统一的异常处理类。
3.1 ControllerAdvice实例代码
package com.ruoyi.framework.web.exception;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.authz.AuthorizationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.DemoModeException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.security.PermissionUtils;
/**
* 全局异常处理器
*
*
*/
@RestControllerAdvice
public class GlobalExceptionHandler
{
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 权限校验异常(ajax请求返回json,redirect请求跳转页面)
*/
@ExceptionHandler(AuthorizationException.class)
public Object handleAuthorizationException(AuthorizationException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
if (ServletUtils.isAjaxRequest(request))
{
return AjaxResult.error(PermissionUtils.getMsg(e.getMessage()));
}
else
{
return new ModelAndView("error/unauth");
}
}
/**
* 请求方式不支持
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
return AjaxResult.error(e.getMessage());
}
/**
* 拦截未知的运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e);
return AjaxResult.error(e.getMessage());
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(e.getMessage());
}
/**
* 业务异常
*/
@ExceptionHandler(ServiceException.class)
public Object handleServiceException(ServiceException e, HttpServletRequest request)
{
log.error(e.getMessage(), e);
if (ServletUtils.isAjaxRequest(request))
{
return AjaxResult.error(e.getMessage());
}
else
{
return new ModelAndView("error/service", "errorMessage", e.getMessage());
}
}
/**
* 自定义验证异常
*/
@ExceptionHandler(BindException.class)
public AjaxResult handleBindException(BindException e)
{
log.error(e.getMessage(), e);
String message = e.getAllErrors().get(0).getDefaultMessage();
return AjaxResult.error(message);
}
/**
* 演示模式异常
*/
@ExceptionHandler(DemoModeException.class)
public AjaxResult handleDemoModeException(DemoModeException e)
{
return AjaxResult.error("演示模式,不允许操作");
}
}
可以看到,在类上添加了@RestControllerAdvice,在方法上添加了@ExceptionHandler,并且通过参数的形式,指定了该方法处理的异常类型。
其中@RestControllerAdvice表示在方法出现异常后,返回信息采用@ResponseBody的方式将信息编码后输出到response对象的body属性,通常是json格式的数据。
3.2 自定义异常代码
package com.ruoyi.common.exception;
/**
* 业务异常
*
*
*/
public final class ServiceException extends RuntimeException
{
private static final long serialVersionUID = 1L;
/**
* 错误提示
*/
private String message;
/**
* 错误明细,内部调试错误
*
* 和 {@link CommonResult#getDetailMessage()} 一致的设计
*/
private String detailMessage;
/**
* 空构造方法,避免反序列化问题
*/
public ServiceException()
{
}
public ServiceException(String message)
{
this.message = message;
}
public String getDetailMessage()
{
return detailMessage;
}
public ServiceException setDetailMessage(String detailMessage)
{
this.detailMessage = detailMessage;
return this;
}
public String getMessage()
{
return message;
}
public ServiceException setMessage(String message)
{
this.message = message;
return this;
}
}
可以根据业务的需要,定义各种各样的异常类。
4. Aspect(切面)
切面拦截的时候,可以指定拦截哪些方法,也可以获得方法的参数和返回值。
4.1 切面示例代码
package com.ruoyi.framework.aspectj;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.annotation.EventTrack;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.system.domain.EventTrackLog;
import com.ruoyi.system.service.IEventTrackLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
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 org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
/**
*
* 埋点切面
*/
@Aspect
@Component
public class EventTrackAspect {
//日志工厂获取日志对象
static Logger logger = LoggerFactory.getLogger(EventTrackAspect.class);
/** 排除敏感属性字段 */
public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
@Autowired
private IEventTrackLogService eventTrackLogService;
//startTime存放开始时间
private ThreadLocal<Map<String, Long >> startTime = new ThreadLocal<>();
//eventTrackLog日志访问对象
private ThreadLocal<EventTrackLog> eventTrackLog = new ThreadLocal<>();
//Controller层切点
@Pointcut("@annotation(com.ruoyi.common.annotation.EventTrack)")
public void controllerAspectse() {
}
//前置通知 用于拦截Controller层记录用户的操作
@Before("controllerAspectse()")
public void before(JoinPoint pjp) {
//方法调用之前初始化
EventTrackLog eventTrackLog = this.eventTrackLog.get();
eventTrackLog = new EventTrackLog();
Map<String, Long> map = new HashMap<>();
map.put("startTime",System.currentTimeMillis());
this.startTime.set(map);
logger.info("==============前置通知开始:记录用户的操作==============");
String currentTime = DateUtils.getTime();
logger.info("请求开始时间:" + currentTime);
eventTrackLog.setStartTime(new Date());
String resultString = "";
// 是否打日志 默认打
boolean isLog = true;
try {
MethodSignature signature = (MethodSignature) pjp.getSignature();
EventTrack eventTrack = signature.getMethod().getAnnotation(EventTrack.class);
//是否开启日志打印
isLog = eventTrack.isLog();
if(isLog){
//开始打印日志
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpSession session = request.getSession();
String api = pjp.getTarget().getClass().getName() + "." + pjp.getSignature().getName();
logger.info("请求API:" + api);
eventTrackLog.setMethod(api);
String methodDescription = getControllerMethodDescription(pjp);
logger.info("方法描述:" + methodDescription);
eventTrackLog.setDescription(methodDescription);
String ipAddress = InetAddress.getLocalHost().toString().substring(InetAddress.getLocalHost().toString().lastIndexOf("/") + 1);
logger.info("请求ip:"+ ipAddress);
eventTrackLog.setIpAddress(ipAddress);
String hostName = InetAddress.getLocalHost().getHostName();
logger.info("机器名:" + hostName);
eventTrackLog.setHostName(hostName);
Enumeration<?> enu = request.getParameterNames();
String params = "{";
while (enu.hasMoreElements()) {
String paraName = (String) enu.nextElement();
List<String> list = Arrays.asList(EXCLUDE_PROPERTIES);
if(list.contains(paraName)) {
continue;
}
params += "\"" + paraName + "\":\"" + request.getParameter(paraName) + "\",";
}
String methodParams = params + "}";
String substring = methodParams.substring(0, methodParams.length() - 2);
substring = substring + "}";
logger.info("方法参数:" + substring);
eventTrackLog.setReqParams(substring);
StringBuffer url = request.getRequestURL();
logger.info("URL:" + url);
eventTrackLog.setUrl(String.valueOf(url));
}
} catch (Exception e) {
StackTraceElement stackTraceElement2 = e.getStackTrace()[2];
String reason = "异常:【"+
"类名:"+stackTraceElement2.getClassName()+";"+
"文件:"+stackTraceElement2.getFileName()+";"+"行:"+
stackTraceElement2.getLineNumber()+";"+"方法:"
+stackTraceElement2.getMethodName() + "】";
//记录本地异常日志
logger.error("==============前置通知异常:记录访问异常信息==============");
String message = e.getMessage() + "|" + reason;
logger.error("异常信息:",message);
eventTrackLog.setErrorMsg(message);
eventTrackLog.setResult("请求发生异常,异常信息:" + message);
}finally {
this.eventTrackLog.set(eventTrackLog);
}
}
@Around("controllerAspectse()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 获取方法签名
MethodSignature signature = (MethodSignature) pjp.getSignature();
EventTrack eventTrack = signature.getMethod().getAnnotation(EventTrack.class);
//是否开启日志打印
Boolean isLog = eventTrack.isLog();
EventTrackLog eventTrackLog = new EventTrackLog();
Long startTime = System.currentTimeMillis();
eventTrackLog.setStartTime(new Date());
try {
//开始打印日志
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpSession session = request.getSession();
String api = pjp.getTarget().getClass().getName() + "." + pjp.getSignature().getName();
logger.info("请求API:" + api);
eventTrackLog.setMethod(api);
String methodDescription = getControllerMethodDescription(pjp);
logger.info("方法描述:" + methodDescription);
eventTrackLog.setDescription(methodDescription);
String ipAddress = InetAddress.getLocalHost().toString().substring(InetAddress.getLocalHost().toString().lastIndexOf("/") + 1);
logger.info("请求ip:"+ ipAddress);
eventTrackLog.setIpAddress(ipAddress);
String hostName = InetAddress.getLocalHost().getHostName();
logger.info("机器名:" + hostName);
eventTrackLog.setHostName(hostName);
Enumeration<?> enu = request.getParameterNames();
String params = "{";
while (enu.hasMoreElements()) {
String paraName = (String) enu.nextElement();
List<String> list = Arrays.asList(EXCLUDE_PROPERTIES);
if(list.contains(paraName)) {
continue;
}
params += "\"" + paraName + "\":\"" + request.getParameter(paraName) + "\",";
}
String methodParams = params + "}";
String substring = methodParams.substring(0, methodParams.length() - 2);
substring = substring + "}";
logger.info("方法参数:" + substring);
eventTrackLog.setReqParams(substring);
StringBuffer url = request.getRequestURL();
logger.info("URL:" + url);
eventTrackLog.setUrl(String.valueOf(url));
}catch (Exception e) {
}
Object proceed = pjp.proceed();
String result = JSON.toJSONString(proceed);
logger.info("==============环切方法执行完成==============");
logger.info("请求结果:" + result);
try {
Long endTime = System.currentTimeMillis();
Long timeSpan = endTime - startTime;
logger.info("timeSpan = " + timeSpan);
eventTrackLog.setTimeSpend(timeSpan);
eventTrackLog.setEndTime(new Date());
// 获取当前的用户
SysUser currentUser = ShiroUtils.getSysUser();
if(currentUser != null)
{
eventTrackLog.setUserName(currentUser.getLoginName());
eventTrackLog.setDeptName(currentUser.getDept().getDeptName());
}
eventTrackLog.setSystemName(RuoYiConfig.getName());
eventTrackLog.setStatus(0l);
eventTrackLog.setNumber(1L);
eventTrackLog.setModuleTitle(eventTrack.title());
eventTrackLog.setBusinessType(eventTrack.businessType().toString());
if(ServletUtils.checkAgentIsMobile(ServletUtils.getRequest().getHeader("User-Agent")))
{
eventTrackLog.setChannel("Mobile");
}
else {
eventTrackLog.setChannel("PC");
}
eventTrackLog.setCreateBy("around");
eventTrackLog.setCreateTime(new Date());
} catch (Exception e) {
} finally {
// 添加日志信息入库
eventTrackLogService.insertEventTrackLog(eventTrackLog);
}
return proceed;
}
/**
* 拦截异常操作
*
* @param joinPoint 切点
* @param ex 异常
*/
@AfterThrowing(value = "@annotation(eventTrack)", throwing = "ex")
public void doAfterThrowing(JoinPoint joinPoint, EventTrack eventTrack, Exception ex)
{
logger.info("==============异常方法执行==============");
EventTrackLog eventTrackLog = this.eventTrackLog.get();
try {
//获取方法名
String methodName = joinPoint.getSignature().getName();
Long end = System.currentTimeMillis();
Long total = end - startTime.get().get("startTime");
logger.info("执行总耗时为:" +total);
eventTrackLog = this.eventTrackLog.get();
eventTrackLog.setTimeSpend(total);
String endTime = DateUtils.getTime();
logger.info("请求结束时间:" + endTime);
eventTrackLog.setEndTime(new Date());
// 获取当前的用户
SysUser currentUser = ShiroUtils.getSysUser();
if(currentUser != null)
{
eventTrackLog.setUserName(currentUser.getLoginName());
eventTrackLog.setDeptName(currentUser.getDept().getDeptName());
}
// eventTrackLog.setSystemName("平台管理系统");
eventTrackLog.setSystemName(RuoYiConfig.getName());
// eventTrackLog.setModuleTitle("后台管理");
eventTrackLog.setStatus(2l);
eventTrackLog.setNumber(1L);
eventTrackLog.setExceptMsg(ex.getMessage());
eventTrackLog.setModuleTitle(eventTrack.title());
eventTrackLog.setBusinessType(eventTrack.businessType().toString());
if(ServletUtils.checkAgentIsMobile(ServletUtils.getRequest().getHeader("User-Agent")))
{
eventTrackLog.setChannel("Mobile");
}
else {
eventTrackLog.setChannel("PC");
}
eventTrackLog.setCreateBy("except");
eventTrackLog.setCreateTime(new Date());
} catch (Exception e) {
StackTraceElement stackTraceElement2 = e.getStackTrace()[2];
String reason = "异常:【"+
"类名:"+stackTraceElement2.getClassName()+";"+
"文件:"+stackTraceElement2.getFileName()+";"+"行:"+
stackTraceElement2.getLineNumber()+";"+"方法:"
+stackTraceElement2.getMethodName() + "】";
//记录本地异常日志
logger.error("==============通知异常:记录访问异常信息==============");
String message = e.getMessage() + "|" + reason;
logger.error("异常信息:",message);
eventTrackLog.setExceptMsg(message);
eventTrackLog.setResult("请求发生异常!!!");
} finally {
// 添加日志信息入库
eventTrackLogService.insertEventTrackLog(eventTrackLog);
// 处理使用结束,清理变量
this.eventTrackLog.remove();
}
}
/**
*
* @param jp
*/
@AfterReturning(pointcut = "@annotation(eventTrack)", returning = "jsonResult")
public void afterMethod(JoinPoint jp, EventTrack eventTrack, Object jsonResult) {
logger.info("==============方法执行完成==============");
EventTrackLog eventTrackLog = this.eventTrackLog.get();
try {
//获取方法名
String methodName = jp.getSignature().getName();
Long end = System.currentTimeMillis();
Long total = end - startTime.get().get("startTime");
logger.info("执行总耗时为:" +total);
eventTrackLog = this.eventTrackLog.get();
eventTrackLog.setTimeSpend(total);
String endTime = DateUtils.getTime();
logger.info("请求结束时间:" + endTime);
eventTrackLog.setEndTime(new Date());
// 获取当前的用户
SysUser currentUser = ShiroUtils.getSysUser();
if(currentUser != null)
{
eventTrackLog.setUserName(currentUser.getLoginName());
eventTrackLog.setDeptName(currentUser.getDept().getDeptName());
}
// eventTrackLog.setSystemName("平台管理系统");
eventTrackLog.setSystemName(RuoYiConfig.getName());
// eventTrackLog.setModuleTitle("后台管理");
eventTrackLog.setStatus(0l);
eventTrackLog.setNumber(1L);
eventTrackLog.setModuleTitle(eventTrack.title());
eventTrackLog.setBusinessType(eventTrack.businessType().toString());
if(ServletUtils.checkAgentIsMobile(ServletUtils.getRequest().getHeader("User-Agent")))
{
eventTrackLog.setChannel("Mobile");
}
else {
eventTrackLog.setChannel("PC");
}
eventTrackLog.setCreateBy("return");
eventTrackLog.setCreateTime(new Date());
} catch (Exception e) {
StackTraceElement stackTraceElement2 = e.getStackTrace()[2];
String reason = "异常:【"+
"类名:"+stackTraceElement2.getClassName()+";"+
"文件:"+stackTraceElement2.getFileName()+";"+"行:"+
stackTraceElement2.getLineNumber()+";"+"方法:"
+stackTraceElement2.getMethodName() + "】";
//记录本地异常日志
logger.error("==============通知异常:记录访问异常信息==============");
String message = e.getMessage() + "|" + reason;
logger.error("异常信息:",message);
eventTrackLog.setExceptMsg(message);
eventTrackLog.setResult("请求发生异常!!!");
} finally {
// this.eventTrackLog.set(eventTrackLog);
// 添加日志信息入库
eventTrackLogService.insertEventTrackLog(eventTrackLog);
// 处理使用结束,清理变量
this.eventTrackLog.remove();
}
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*/
public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();//目标方法名
Object[] arguments = joinPoint.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==arguments.length){
description = method.getAnnotation(EventTrack.class).description();
break;
}
}
}
return description;
}
}
这里以一个记录日志的切面为例进行说明。切面包括前置通知,后置通知和环绕通知。这里以指定的注解来判断是否需要拦截。
这里虽然同时使用了前置通知,后置通知以及环绕通知,在实际的使用场景中,可以根据需要进行有选择的使用。
4.2 自定义注解代码
package com.ruoyi.common.annotation;
/**
*
*/
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.OperatorType;
import org.springframework.web.bind.annotation.ResponseBody;
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;
// 事件埋点
@Target({ElementType.PARAMETER, ElementType.METHOD})//作用在参数和方法上
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Documented//表明这个注解应该被 javadoc工具记录
// @ResponseBody//响应时转JSON格式
public @interface EventTrack {
/**
* 模块
*/
public String title() default "";
/**
* 功能
*/
public BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
*/
public OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
/**
* 业务描述
* @return
*/
String description() default "";
/**
* 是否打日志 默认打
*/
boolean isLog() default true;
}
以上是对Aspect、ControllerAdvice、Interceptor、Fliter的举例说明,通过这些示例,可以更具体的理解他们的差异。
1.过滤器:Filter :可以获得Http原始的请求和响应信息,但是拿不到处理该请求的方法信息(比如类名称,方法名称等);
2.拦截器:Interceptor:可以获得Http原始的请求和响应信息,也可以拿得到相应方法的信息,根据httpServeltRquest也能拿到请求参数;
3.ControllerAdvice(Controller增强):主要是用于全局的异常拦截和处理,这里的异常可以使自定义异常,也可以是JDK里面的异常;
4.切片:Aspect:主要是进行公共方法的拦截,可以拿得到方法参数的值,也可以获得方法处理后的返回值。
从技术层次来看,可以参考下图:
外层技术主要是原始的请求和响应,内层可以具体到某个具体的Controller的方法,获取的信息可以更具体和准确。