拦截器
拦截器的作用:以低耦合度,解决共有的问题。 可以拦截浏览器访问过来的部分或全部请求,在这些请求的开始或者结束部分插入代码,从而可以批量的解决多个请求共有的业务。
示例
- 定义拦截器interceptor实现HandlerInterceptor接口。在controller 下新建interceptor包。建AlphaInterceptor.java。
- HandlerInterceptor 有三个实现方法。preHandle,postHandle,afterCompletion。
- 代码
@Component public class AlphaInterceptor implements HandlerInterceptor { private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class); //在controller之前执行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { logger.debug("preHandle:"+handler.toString()); return true; //返回false 不执行controller了就 } //在controller之后执行,调模板引擎之前 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { logger.debug("postHandle:"+handler.toString()); } //在TemplateEngine模板引擎之后执行 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { logger.debug("afterCompletion:"+handler.toString()); } }
- 对AlphaInterceptor拦截器进行配置。指定拦截、排除的路径 Config包下 WebMvcConfig
通常写配置类是声明第三方的类,作为Bean。拦截器配置器要求实现 WebMvcConfigurer 接口。
/** * 通常写配置类是声明第三方的类,作为Bean * 拦截器配置器要求实现 WebMvcConfigurer 接口 * */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private AlphaInterceptor alphaInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(alphaInterceptor) //拦截一切请求 .excludePathPatterns("/*/*.css","/*/*.js","/*/*.png","/*/*.jpg","/*/*.jpeg") //排除访问静态资源,所有目录下的cs文件 .addPathPatterns("/register","/login"); //拦截指定路径及其下的页面 } }
拦截器应用
每一次请求都需要完成以下这种操作。因此写拦截器完成这部分工作
首先要Controller实现HandelInterceptor接口
-
在请求开始之前查询登录用户
//获取ticket,在處理所有請求之前,contrller之前 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //无法直接使用@CookieValue()注解了,但是可以从request中获取Cookie。比较麻烦,因此进行封装在CookieUtil中。 //从Cookie中获取凭证 String ticket =CookieUtil.getValue(request,"ticket"); if(ticket !=null){ //登录了,通过ticket查询 到UserID(在LoginTicket中) LoginTicket loginTicket =userService.findLoginTicket(ticket); //查看凭证是否有效?? if(loginTicket !=null && loginTicket.getStatus()==0 && loginTicket.getExpired().after(new Date())){ //根据凭证查询用户。 User user = userService.findUserById(loginTicket.getUserId()) ; //在本次请求中持有用户 (要考虑多线程的情况) //考虑线程的隔离,ThreadLocal。 HostHolder封装使用 hostHolder.setUser(user); } } return true; }
CookieUtilpublic class CookieUtil { public static String getValue(HttpServletRequest request, String name){ if(request==null || name ==null){ throw new IllegalArgumentException("参数为空"); } Cookie[] cookies =request.getCookies(); if (cookies !=null){ for (Cookie cookie :cookies){ if (cookie.getName().equals(name)){ return cookie.getValue(); } } } return null; } }
HostHolder
线程的隔离 有一个工具类 ThreadLocal。 通过获取当前线程做一个map,每次取得时候从map中根据当前线程取。通过这种方式实现线程隔离。其get方法如下。public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
因此我们针对User对ThreadLocal进行封装。HostHolder
/** * 主要目的是起到一个容器的作用 * 持有用户信息,用于代替session对象 */ @Component public class HostHolder { private ThreadLocal<User> users = new ThreadLocal<User>();
public void setUser (User user){ users.set(user); }
public User getUser(){ return users.get(); } //清理 public void clear(){ users.remove(); } } -
在本次请求中持有用户数据(存一下)
//在模板之前,将信息加入model中,用以显示 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { User user =hostHolder.getUser(); if(user != null && modelAndView!=null){ modelAndView.addObject("loginUser",user); } }
-
在请求结束时清理用户数据
//在模板使用完后,进行清理 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { hostHolder.clear(); }
对LoginTicketIntercepter拦截器进行配置。 WebMvcConfig
/** * 通常写配置类是声明第三方的类,作为Bean * 拦截器配置器要求实现 WebMvcConfigurer 接口 * */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private AlphaInterceptor alphaInterceptor; @Autowired private LoginTicketIntercepter loginTicketIntercepter; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(alphaInterceptor) //拦截一切请求 .excludePathPatterns("/*/*.css","/*/*.js","/*/*.png","/*/*.jpg","/*/*.jpeg") //排除访问静态资源,所有目录下的cs文件 .addPathPatterns("/register","/login"); //拦截指定路径及其下的页面 registry.addInterceptor(loginTicketIntercepter) .excludePathPatterns("/*/*.css","/*/*.js","/*/*.png","/*/*.jpg","/*/*.jpeg"); } }
在对应模板上进行修改
改写index的header部分。
<!-- 功能 --> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav mr-auto"> <li class="nav-item ml-3 btn-group-vertical"> <a class="nav-link" th:href="@{/index}">首页</a> </li> <li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser!=null}"> <a class="nav-link position-relative" href="site/letter.html">消息<span class="badge badge-danger">12</span></a> </li> <li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser==null}"> <a class="nav-link" th:href="@{/register}" >注册</a> </li> <li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser==null}"> <a class="nav-link" th:href="@{/login}" >登录</a> </li> <li class="nav-item ml-3 btn-group-vertical dropdown" th:if="${loginUser!=null}"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <img th:src="@{${loginUser.headerUrl}}" class="rounded-circle" style="30px;"/> </a> <div class="dropdown-menu" aria-labelledby="navbarDropdown"> <a class="dropdown-item text-center" href="site/profile.html">个人主页</a> <a class="dropdown-item text-center" href="site/setting.html">账号设置</a> <a class="dropdown-item text-center" th:href="@{/logout}">退出登录</a> <div class="dropdown-divider"></div> <span class="dropdown-item text-center text-secondary" th:utext="${loginUser.username}">nowcoder</span> </div> </li>