spingmvc的执行步骤
用户发送请求到dispatcherservlet,
前端控制器到handermaping去找对应的handler,
然后返回一个执行链,是handler和响应的拦截器,
执行链经过适配器,做一些处理,
到达对应的handler,
经过handler来处理后,返回modelandview,
前端控制器会先对视图对象进行解析,解析完成后,
前端控制器会对视图进行渲染,
渲染完成之后,将视图返回给用户。
首先,我们发现以上步骤中包含了handlermaping,handlerAdpdater(适配器),和handler,那么springmvc执行的时候,这些都应该作为对象存在的,这就是我们springmvc配置的由来
1.我们配置扫描包,将controller全都交给spring管理
2.然后我们开启注解的自动,来注册handlermaping和handlerAdpdater
3.因为我们要解析视图,所以要有视图解析器的配置。
4.我们看到经过handlerMaping后,返回的是一个handler和拦截器的执行链,那么重点来了,我们的拦截器开始配置了。
步骤是:我们首先用一个类实现HandlerAdpter,实现里面的方法,有前置拦截(在handler之前),后置拦截,最终拦截(视图渲染完成之后)
public
class
LoginInterceptor
implements
HandlerInterceptor {
@Autowired
private
UserServiceImpl
userService;
@Override
public
boolean
preHandle(
HttpServletRequest
request,
HttpServletResponse
response,
Object
handler)
throws Exception {
//在Handler执行之前处理
//判断用户是否登录
//从cookie中取token
String
token =
CookieUtils.
getCookieValue(request,
"TT_TOKEN");
/*if(StringUtils.isBlank(token)){
//跳转到登录页面,把用户请求的url作为参数传递给登录页面。
response.sendRedirect(userService.SSO_BASE_URL + userService.SSO_PAGE_LOGIN
+ "?redirect=" + request.getRequestURL());
//返回false
return false;
}*/
//根据token换取用户信息,调用sso系统的接口。
TbUser
user =
userService.
getUserByToken(token);
//取不到用户信息
if (
null == user) {
//跳转到登录页面,把用户请求的url作为参数传递给登录页面。
response.
sendRedirect(
userService.
SSO_BASE_URL +
userService.
SSO_PAGE_LOGIN
+
"?redirect=" +
request.
getRequestURL());
//返回false
return
false;
}
//取到用户信息,放行
//把用户信息放入request
request.
setAttribute(
"user", user);
//返回值决定handler是否执行。true:执行,false:不执行。
return
true;
}
@Override
public
void
postHandle(
HttpServletRequest
request,
HttpServletResponse
response,
Object
handler,
ModelAndView
modelAndView)
throws Exception {
// handler执行之后,返回ModelAndView之前
}
@Override
public
void
afterCompletion(
HttpServletRequest
request,
HttpServletResponse
response,
Object
handler,
Exception
ex)
throws Exception {
// 返回ModelAndView之后。
//响应用户之后。
}
}
以上是springmvc的拦截器,那么springboot中的拦截器又是怎么实现的呢?
springboot中使用拦截器,
- 自定义一个拦截器,
- 一个类
WebMvcConfigurer
并添加 - 在这个类中重写 addInterceptors方法,这里还有一点巧妙的是通过@Bean的方式,将日志引入
@Bean public LoginInterceptor loginInterceptor(){ return new LoginInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(this.loginInterceptor()).addPathPatterns("/**"); }
这个方式来添加拦截器,那么我们真实开发是怎么用的呢?
首先跟mvc一样,用一个类继承handlerintercapter
public class LoginInterceptor extends HandlerInterceptorAdapter { private JwtProperties jwtProperties; // 定义一个线程域,存放登录用户 private static final ThreadLocal<UserInfo> tl = new ThreadLocal<>(); public LoginInterceptor(JwtProperties jwtProperties) { this.jwtProperties = jwtProperties; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 查询token String token = CookieUtils.getCookieValue(request, "LY_TOKEN"); if (StringUtils.isBlank(token)) { // 未登录,返回401 response.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; } // 有token,查询用户信息 try { // 解析成功,证明已经登录 UserInfo user = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey()); // 放入线程域 tl.set(user); return true; } catch (Exception e) { // 抛出异常,证明未登录或超时,返回401 response.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; } } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { tl.remove(); } public static UserInfo getLoginUser() { return tl.get(); } }然后用mvcconfig实现webmvcconfigurer
@Configuration @EnableConfigurationProperties(JwtProperties.class) public class MvcConfig implements WebMvcConfigurer { @Autowired private JwtProperties jwtProperties; @Bean public LoginInterceptor loginInterceptor() { return new LoginInterceptor(jwtProperties); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor()) .addPathPatterns("/**"); } }