文章目录
AOP概述
AOP表示面向切面编程(Aspect-Oriented Programming)。
首先AOP不是一项技术,它是一种编程思想。
过滤器、拦截器、切面都可以理解为AOP的实现方式。
过滤器、拦截器、切面区别
过滤器 | 拦截器 | Aspect | |
---|---|---|---|
关注的点 | 所有web请求 | 部分web请求 | 偏向于业务层面的拦截 |
实现原理 | 函数回调 | JAVA反射机制(动态代理) | 动态代理 |
Servlet提供的支持 | Filter接口 | 无 | 无 |
Spring提供的支持 | 无 | HandlerInterceptorAdapter类、HandlerInterceptor接口 | @Aspect |
可能需要实现的方法 | void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) | boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) | Object around(ProceedingJoinPoint joinPoint) |
过滤器 Filter
简单来讲就是用来过滤东西的,比如:统一设置编码,统一设置所有请求头信息。
实现: 实现 javax.Servlet.Filter
接口就可以创建一个过滤器。
使用过滤器统一请求耗时
/**
* 统一记录请求耗时
*/
@Slf4j
@WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/*")
public class ApiAccessFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
long start = System.currentTimeMillis();
filterChain.doFilter(servletRequest, servletResponse);
long duration = System.currentTimeMillis() - start;
if (duration > 200) {
log.info("Api Access uri: {}, method: {}, client: {}, duration: {}ms , time is too long",
request.getRequestURI(), request.getMethod(), getIP(request), System.currentTimeMillis() - start);
}else{
log.info("Api Access uri: {}, method: {}, client: {}, duration: {}ms",
request.getRequestURI(), request.getMethod(), getIP(request), System.currentTimeMillis() - start);
}
}
private String getIP(HttpServletRequest request) {
if (request == null) {
return "0.0.0.0";
}
String Xip = request.getHeader("X-Real-IP");
String XFor = request.getHeader("X-Forwarded-For");
String UNKNOWN_IP = "unknown";
if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) {
//多次反向代理后会有多个ip值,第一个ip才是真实ip
int index = XFor.indexOf(",");
if (index != -1) {
return XFor.substring(0, index);
} else {
return XFor;
}
}
XFor = Xip;
if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) {
return XFor;
}
if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
XFor = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
XFor = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
XFor = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
XFor = request.getRemoteAddr();
}
return XFor;
}
}
拦截器 Interceptor
在执行方法之前,进行拦截,然后在之前或之后加入一些操作。
使用场景:
- 日志记录: 记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
- 权限检查: 如登录检测,进入处理器检测检测是否登录。
- 性能监控: 通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。
- 通用行为: 读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,只要是多个处理器都需要的即可使用拦截器实现。
实现拦截器: 实现 HandlerInterceptor
接口就可以创建一个拦截器。
HandlerInterceptor
接口的源码如下:
public interface HandlerInterceptor {
// 在请求处理之前进行调用(Controller方法调用之前)
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
// 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后),如果异常发生,则该方法不会被调用
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
// 在整个请求结束之后被调用,也就是在 DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
使用拦截器判断是否登录
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotCheckToken {
}
@Component
public class AuthenticationIntercept extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
NotCheckToken methodAnnotation = method.getMethodAnnotation(NotCheckToken.class);
if (Objects.nonNull(methodAnnotation)) {
return true;
}
}
String userId = request.getHeader("userId");
if (StringUtils.isBlank(userId)) {
throw new Exception("未登录");
}
request.setAttribute("userId", userId);
return super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
}
拦截器注册:
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
@Autowired
private AuthenticationIntercept authenticationIntercept;
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration registration = registry.addInterceptor(authenticationIntercept);
// 拦截所有请求
registration.addPathPatterns("/**");
// 添加不拦截路径
registration.excludePathPatterns("/login", "/error", "/logout","/login.html");
}
}
切面 AspectJ
https://blog.csdn.net/const_/article/details/93502628