1.定义切面执行链顶级接口 AspectHandler
/*** 切面执行链**/
public interface AspectHandler {/*** 设置排除项* @param excludes*/default void setExcludes(List<String> excludes) {}/*** 获取排除项* @return*/default List<String> getExcludes() {return new ArrayList<>();}/*** 前置处理* @param pjp* @return* @throws Exception*/boolean execute(ProceedingJoinPoint pjp) throws Exception;/*** 后置处理* @param pjp* @param response* @param exception*/default void afterCompletion(ProceedingJoinPoint pjp, Object response, Exception exception) {}
}
2.定义切面处理顺序注解 AspectHandlerOrder
/*** 处理器排序*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AspectHandlerOrder {/*** The order value.* <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.** @see Ordered#getOrder()*/int value() default Ordered.LOWEST_PRECEDENCE;
}
3.web层统一处理切面类 AbstractWebAspect
/*** web层的统一切面,需要在使用时进行统一配置,设置切点*/
public abstract class AbstractWebAspect implements InitializingBean {private static final Logger logger = LoggerFactory.getLogger(AbstractWebAspect.class);private final static String SESSION_KEY = "sessionId";@Autowired(required = false)@Qualifier("apiAspectHandlers")private List<AspectHandler> aspectHandlers;private AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic void afterPropertiesSet() throws Exception {if (!CollectionUtils.isEmpty(aspectHandlers)) {Collections.sort(aspectHandlers, (o1, o2) -> {AspectHandlerOrder o1HandlerOrder = o1.getClass().getAnnotation(AspectHandlerOrder.class);AspectHandlerOrder o2HandlerOrder = o2.getClass().getAnnotation(AspectHandlerOrder.class);int o1OrderValue = o1HandlerOrder == null ? Ordered.LOWEST_PRECEDENCE : o1HandlerOrder.value();int o2OrderValue = o2HandlerOrder == null ? Ordered.LOWEST_PRECEDENCE : o2HandlerOrder.value();return o1OrderValue == o2OrderValue ? 0 : o1OrderValue > o2OrderValue ? 1 : -1;});}}public List<AspectHandler> getAspectHandlers() {return aspectHandlers;}private List<Class<? extends AspectHandler>> asList(Class<? extends AspectHandler>[] classes) {if (classes != null && classes.length > 0) {List<Class<? extends AspectHandler>> list = new ArrayList<>();for (Class<? extends AspectHandler> aClass : classes) {list.add(aClass);}return list;}return new ArrayList<>();}protected Object doAroundMethod(ProceedingJoinPoint pjp, List<AspectHandler> aspectHandlers) throws Exception {StopWatch stopWatch = new StopWatch();stopWatch.start();Exception exception = null;Object response = null;ArrayList<AspectHandler> executedAspectHandlers = new ArrayList<>();String methodName = pjp.toShortString();try {Object[] args = pjp.getArgs();logger.info("Aspect request={},args={}", methodName, Arrays.toString(args));// whether declare with AspectHandlerExclude annotation// if true pass declare excludes, default empty pass all// refactorAnnotation clazzAnnotation = getClazzAnnotation(pjp, AspectHandlerExclude.class);Annotation methodAnnotation = getMethodAnnotation(pjp, AspectHandlerExclude.class);boolean result = true;Class<? extends AspectHandler>[] methodExcludes = methodAnnotation == null ? new Class[]{} : ((AspectHandlerExclude) methodAnnotation).excludes().length == 0 ? aspectHandlers.stream().map(a -> a.getClass()).collect(Collectors.toList()).toArray(new Class[]{}) : ((AspectHandlerExclude) methodAnnotation).excludes();Class<? extends AspectHandler>[] clazzExcludes = clazzAnnotation == null ? new Class[]{} : ((AspectHandlerExclude) clazzAnnotation).excludes().length == 0 ? aspectHandlers.stream().map(a -> a.getClass()).collect(Collectors.toList()).toArray(new Class[]{}) : ((AspectHandlerExclude) clazzAnnotation).excludes();if (!CollectionUtils.isEmpty(aspectHandlers)) {aspectHandlers = new ArrayList<>(aspectHandlers);if (clazzExcludes.length > 0) {List<Class<? extends AspectHandler>> classes = asList(clazzExcludes);aspectHandlers.removeIf(h -> classes.contains(h.getClass()));}if (methodExcludes.length > 0) {List<Class<? extends AspectHandler>> classes = asList(methodExcludes);aspectHandlers.removeIf(h -> classes.contains(h.getClass()));}for (AspectHandler aspectHandler : aspectHandlers) {executedAspectHandlers.add(aspectHandler);result = aspectHandler.execute(pjp);if (!result) {break;}}}if (result) {response = pjp.proceed();if (response != null && response instanceof ApiResponse) {ApiResponse re = (ApiResponse) response;String sessionId = MDC.get(SESSION_KEY);re.setReqId(sessionId);return re;}return response;}} catch (Throwable throwable) {exception = (Exception) throwable;logger.error("execute:[{}],throw exception:[{}],message:[{}]", methodName, exception == null ? "" : exception.getClass().getName(), exception == null ? "" : exception.getMessage());throw exception;} finally {if (executedAspectHandlers.size() > 0) {for (int i = executedAspectHandlers.size() - 1; i >= 0; i--) {AspectHandler ah = executedAspectHandlers.get(i);try {ah.afterCompletion(pjp, response, exception);} catch (Exception e) {logger.error("AspectHandler afterCompletion execute error", e);}}}stopWatch.stop();long elapseTime = stopWatch.getTotalTimeMillis();logger.info("Aspect elapseTime:[{}],methodName:[{}]", elapseTime, methodName);}return response;}private void populateReqId(Object response) {}protected Object doAroundMethod(ProceedingJoinPoint pjp) throws Exception {return doAroundMethod(pjp, this.aspectHandlers);}private boolean matchExclude(AspectHandler aspectHandler, Class<? extends AspectHandler>[] excludes) {for (Class<? extends AspectHandler> exclude : excludes) {if (aspectHandler.getClass().equals(exclude)) {return true;}}HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String url = request.getRequestURI();List<String> excludeUrls = aspectHandler.getExcludes();if (!CollectionUtils.isEmpty(excludeUrls)) {for (String excludeUrl : excludeUrls) {if (antPathMatcher.match(excludeUrl, url)) {return true;}}}return false;}/*** get the annotation declare with method<br/>* if declare return the annotation,otherwise return null** @param pjp* @param annotation the annotation which find* @return*/private Annotation getMethodAnnotation(ProceedingJoinPoint pjp, Class<? extends Annotation> annotation) {Signature signature = pjp.getSignature();if (signature instanceof MethodSignature) {MethodSignature methodSignature = (MethodSignature) signature;return methodSignature.getMethod().getAnnotation(annotation);}return null;}/*** get the annotation declare with class<br/>* if declare return the annotation,otherwise return null** @param pjp* @param annotation the annotation which find* @return*/private Annotation getClazzAnnotation(ProceedingJoinPoint pjp, Class<? extends Annotation> annotation) {return pjp.getTarget().getClass().getAnnotation(annotation);}
}
4.切面配置类 AspectConfiguration
@Configuration
public class AspectConfiguration {@Beanpublic List<AspectHandler> apiAspectHandlers() {return Arrays.asList(new HttpHeaderAspectHandler(),new SecurityAspectHandler());}
}
5.定义在Http请求头中添加自定义信息切面 HttpHeaderAspectHandler
@Slf4j
@AspectHandlerOrder(Ordered.HIGHEST_PRECEDENCE)
public class HttpHeaderAspectHandler implements AspectHandler {@Overridepublic boolean execute(ProceedingJoinPoint proceedingJoinPoint) {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();Enumeration<String> headerNames = request.getHeaderNames();HttpHeader httpHeader = new HttpHeader();while (headerNames.hasMoreElements()) {String s = headerNames.nextElement();String header = request.getHeader(s);Field field = ReflectionUtils.findField(HttpHeader.class, s);if (field != null) {field.setAccessible(true);ReflectionUtils.setField(field, httpHeader, header);}}//没有按要求返回的情况,赋值默认值if(ObjectUtils.isEmpty(AppSourceEnum.self(httpHeader.getApp_source()))){httpHeader.setApp_source(AppSourceEnum.DEFAULT.getCode().toString());}WebContext.setHttpHeader(httpHeader);return true;}@Overridepublic void afterCompletion(ProceedingJoinPoint pjp, Object response, Exception exception) {AspectHandler.super.afterCompletion(pjp, response, exception);WebContext.removeHttpHeader();if (!(response instanceof ApiResponse)) {Optional.ofNullable(response).ifPresent((r) -> {log.error("controller return wrong type:" + response.getClass().getSimpleName());});return;}}
}
6.定义跳过切面注解 AspectHandlerExclude
/*** web controller中的方法加上该注解则会跳过切面逻辑**/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AspectHandlerExclude {/*** 如果excludes为空则跳过所有handler如果不为空则跳过指定的handler** @return*/Class<? extends AspectHandler>[] excludes() default {};
}
7.在controller方法上添加跳过切面配置
@AspectHandlerExclude(excludes = { SecurityAspectHandler.class })
public ApiResponse<?> findPageList(searchRequestDTO searchRequestDTO) {}