028:拦截器&过滤器&视图层&异步源码分析
1 SpringMVC注解方式开启视图层
课程内容:
1.SpringMVC注解形式启动整合视图层
2.SpringMVC拦截器底层源码分析
3.SpringMVC异步处理有那些方式
4.使用Callable实现Http请求异步处理
SpringMVC注解形式启动整合视图层
@Configuration
@ComponentScan("com.mayikt.controller")
@EnableWebMvc
public class SpringMvcConfig {
// @EnableWebMvc 等同于开启springmvc注解方式
// @Configuration 替代xml
// @ComponentScan 替代扫包范围
// DispatcherServlet
@Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
internalResourceViewResolver.setPrefix("/WEB-INF/view/");
internalResourceViewResolver.setSuffix(".jsp");
return internalResourceViewResolver;
}
}
@Controller
public class IndexController {
@RequestMapping(value = "/pageIndex")
public String pageIndex() {
return "pageIndex";
}
}
pageIndex.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
完全基于Spring注解方式启动SpringMVC
测试结果:
2 Java过滤器与拦截器之间的区别
拦截器与过滤器之间有那些异同?
相同点:拦截器与过滤器都是基于Aop技术,对方法实现增强,都可以拦截请求方法。
不同点:
- 过滤器属于Servlet研发的,而拦截器技术属于SpringMVC研发的;
- 过滤器属于拦截Web请求,而拦截器不仅可以拦截请求还可以拦截普通方法;
- 过滤器会比拦截器先执行,拦截器封装的方法比过滤器拦截使用起来更加简单。
实际开发中绝大多数情况下,都会使用拦截器,
应用场景:
拦截器:权限控制、日志打印、参数验证、会话。
过滤器:编码转换、跨域解决、xss攻击
3 SpringMVC注解形式使用拦截器
自定义拦截器拦截请求Token
@Configuration
@ComponentScan("com.mayikt.controller")
// @EnableWebMvc
public class SpringMvcConfig extends WebMvcConfigurationSupport {
// implements WebMvcConfigurer也可生效
// extends WebMvcConfigurerAdapter 错误,已经过时了
// extends WebMvcConfigurationSupport也没有效果,@EnableWebMvc底层会帮助注入WebMvcConfigurationSupport子类,覆盖自定义配置类
// WebMvcConfigurationSupport帮助配置springmvc相关信息 web视图层相关内容
// 1.手动注入拦截器到spring中
@Bean
public TokenInterceptor tokenInterceptor() {
return new TokenInterceptor();
}
// 2.添加拦截器
@Override
public void addInterceptors(InterceptorRegistry interceptorRegistry){
interceptorRegistry.addInterceptor(tokenInterceptor()).addPathPatterns("/**");// 拦截所有的请求
}
}
public class TokenInterceptor implements HandlerInterceptor {
/**
* 请求方法前置拦截,如果返回true,表示会执行到目标方法(请求方法);返回false,不会执行目标方法
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getParameter("token");
System.out.println("token:" + token);
if (StringUtils.isEmpty(token)) {
response.setStatus(500);
response.getWriter().println("token is null");
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println(">>>postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println(">>>afterCompletion");
}
}
注意:使用拦截器一定要关闭EnableWebMvc 否则拦截器不会生效。
4 SpringMVC拦截器使用的特征
运行结果:
preHandle在业务处理器处理请求之前被调用;
postHandle在业务处理器处理请求执行完成后,生成视图之前执行;
afterCompletion在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。
afterCompletion()执行完成后开始渲染页面。
5 SpringMVC异步处理基本概念
在Web中为什么要使用异步?
目的:快速响应给客户端,防止客户端请求等待。单独开启一个线程处理。
缺点:线程安全问题、不能及时拿到结果、消耗CPU。
6 SpringMVC注解形式使用异步
@Controller
public class IndexController {
@Autowired
private MemberService memberService;
@ResponseBody
@RequestMapping(value = "/payToMember", produces = "text/html;charset=UTF-8")
public String payToMember() {
String memberResult = "结果为空!";
System.out.println("1.开始执行payToMember>>> thread name:" + Thread.currentThread().getName());
memberResult = memberService.member();
System.out.println("4.结束执行payToMember>>> thread name:" + Thread.currentThread().getName());
System.out.println("memberResult:" + memberResult);
return memberResult;
}
// 执行顺序 1423 返回结果 null
}
@Component
public class MemberService {
/**
* member方法会单独开启线程处理请求
* @return
*/
@Async
public String member() {
try {
System.out.println("2.开始调用会员服务接口...thread name:" + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("3.结束调用会员服务接口...thread name:" + Thread.currentThread().getName());
} catch (Exception e) {
}
return "member";
}
}
@Configuration
@ComponentScan(basePackages = {"com.mayikt.controller", "com.mayikt.service"})
@EnableAsync
public class SpringMvcConfig extends WebMvcConfigurationSupport {
}
运行结果:
7 SpringMVC异步形式Callable
Callable接口:能够让耗时时间的代码,交给单独线程处理,也能够拿到异步线程结果。
@Controller
public class IndexController {
@Autowired
private MemberService memberService;
@ResponseBody
@RequestMapping(value = "/asyncPay", produces = "text/html;charset=UTF-8")
public Callable<String> asyncPay() {
System.out.println("1.开始执行payToMember>>> thread name:" + Thread.currentThread().getName());
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
String member = memberService.member2();
return member;
}
};
System.out.println("4.结束执行payToMember>>> thread name:" + Thread.currentThread().getName());
return callable;
}
// 执行顺序 1423 返回结果 member
}
@Component
public class MemberService {
public String member2() {
try {
System.out.println("2.开始调用会员服务接口...thread name:" + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("3.结束调用会员服务接口...thread name:" + Thread.currentThread().getName());
} catch (Exception e) {
}
return "member";
}
}
运行报错:
xml配置文件中需新增true,转换成注解形式需要在WebInitializer类中增dispatcher.setAsyncSupported(true);
运行结果:
此时浏览器页面执行耗时代码仍会不断转圈,如果需求是异步返回结果且不想转圈,可以让客户端主动查询。
假设场景:支付调用会员,会员比较耗时间,调用支付接口马上响应给客户端告诉客户端正在等待会员响应,前台页面写一个定时器,服务端写一个接口,每隔1s查询支付调用会员是否成功,如果成功返回结果给客户端。