在SpringBoot项目中实现切面执行链功能
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();
@Override
public 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
// refactor
Annotation 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 {
@Bean
public List<AspectHandler> apiAspectHandlers() {
return Arrays.asList(new HttpHeaderAspectHandler(),new SecurityAspectHandler());
}
}
5.定义在Http请求头中添加自定义信息切面 HttpHeaderAspectHandler
@Slf4j
@AspectHandlerOrder(Ordered.HIGHEST_PRECEDENCE)
public class HttpHeaderAspectHandler implements AspectHandler {
@Override
public 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;
}
@Override
public 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) {
}