以往进行web项目开发都需要在web.xml配置servlet、filter、listener,在Servlet3.0可以通过注解的方式配置它们(注意:必须用tomcat7以上版本)
@WebServlet
@WebServlet("/hello") public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("hello..."); } }
ServletContainerInitializer
Servlet容器在启动应用的时候会扫描当前应用每一个jar包里面的META-INF/services/javax.servlet.ServletContainerInitializer;我们可以按照规范在项目的类路径下创建一个META-INF/services/并配置名字为javax.servlet.ServletContainerInitializer的文本,内容就是我们实现ServletContainerInitializer接口的全类名,应用启动就会运行这个实现类的方法,通过@HandlesTypes可以传入我们需要的类
编码的方式添加Web组件(Servlet、Filter、Listener)
使用ServletContext必须在项目启动的时候注册Web组件,有2种实现方式:ServletContainerInitializer、ServletContextListener接口获取到的ServletContext进行添加
Filter
public class UserFilter implements Filter { @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { // 过滤请求 System.out.println("UserFilter...doFilter..."); //放行 arg2.doFilter(arg0, arg1); } @Override public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } }
HttpServlet
public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub resp.getWriter().write("tomcat..."); } }
ServletContextListener
监听项目的启动和停止
public class UserListener implements ServletContextListener { //监听ServletContext销毁 @Override public void contextDestroyed(ServletContextEvent arg0) { // TODO Auto-generated method stub System.out.println("UserListener...contextDestroyed..."); } //监听ServletContext启动初始化 @Override public void contextInitialized(ServletContextEvent arg0) { // 获取到servletContext进行添加组件 ServletContext servletContext = arg0.getServletContext(); System.out.println("UserListener...contextInitialized..."); } }
ServletContainerInitializer
容器启动的时候,会运行onStartup方法
@HandlesTypes获取指定的类型下面的子类(实现类,子接口等)传递到arg0中
ServletContext代表一个Web应用上下文
@HandlesTypes(value={HelloService.class}) public class MyServletContainerInitializer implements ServletContainerInitializer { /** * Set<Class<?>> arg0:感兴趣的类型的所有子类型; * ServletContext arg1:代表当前Web应用的ServletContext; */ @Override public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException { // TODO Auto-generated method stub System.out.println("感兴趣的类型:"); for (Class<?> claz : arg0) { System.out.println(claz); } //注册组件 ServletRegistration ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet()); //配置servlet的映射信息 servlet.addMapping("/user"); //注册Listener sc.addListener(UserListener.class); //注册Filter FilterRegistration FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class); //配置Filter的映射信息 filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*"); } }
异步处理
支持Servlet的异步处理功能须开启asyncSupported=true(开启异步模式)
@WebServlet(value="/async",asyncSupported=true) public class HelloAsyncServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("主线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis()); AsyncContext startAsync = req.startAsync();
//开始异步处理 startAsync.start(new Runnable() { @Override public void run() { try { System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis()); sayHello();
//异步处理完毕 startAsync.complete(); //获取到异步上下文 AsyncContext asyncContext = req.getAsyncContext(); //获取响应 ServletResponse response = asyncContext.getResponse(); response.getWriter().write("hello async..."); System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis()); } catch (Exception e) { } } }); System.out.println("主线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis()); } public void sayHello() throws Exception{ System.out.println(Thread.currentThread()+" processing..."); Thread.sleep(3000); } }
servlet3.0整合springMVC
- 在spring-mvc.jar里的/META-INF/services/javax.servlet.ServletContainerInitializer记录了org.springframework.web.SpringServletContainerInitializer,上面章节讲过web容器在启动的时候会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer并加载这个文件指定的类
- 在这个类中标注了@HandlesTypes(WebApplicationInitializer.class),所以在应用启动的时候会加载WebApplicationInitializer接口下的所有组件,并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类);这个组件有三个抽象类:
- AbstractContextLoaderInitializer:
- createRootApplicationContext() //创建根容器
- AbstractDispatcherServletInitializer:
- createServletApplicationContext() //创建web的ioc容器
- createDispatcherServlet() //创建DispatcherServlet,将它添加到到ServletContext中
- getServletMappings() //servlet映射地址
- AbstractAnnotationConfigDispatcherServletInitializer //注解方式配置
- createRootApplicationContext() //创建根容器
- getRootConfigClasses() //传入spring的配置类
- createServletApplicationContext() //创建web的ioc容器
- getServletConfigClasses() //获取配置类
实现
以注解方式启动SpringMVC,我们只要继承AbstractAnnotationConfigDispatcherServletInitializer实现抽象方法(指定DispatcherServlet的配置信息)
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //获取根容器的配置类;(Spring的配置文件) 父容器; @Override protected Class<?>[] getRootConfigClasses() { // TODO Auto-generated method stub return new Class<?>[]{RootConfig.class}; } //获取web容器的配置类(SpringMVC配置文件) 子容器; @Override protected Class<?>[] getServletConfigClasses() { // TODO Auto-generated method stub return new Class<?>[]{AppConfig.class}; } //获取DispatcherServlet的映射信息 // /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp; // /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的; @Override protected String[] getServletMappings() { // TODO Auto-generated method stub return new String[]{"/"}; }
spring容器
//Spring的容器不扫描controller;父容器 @ComponentScan(value="com.atguigu",excludeFilters={ @Filter(type=FilterType.ANNOTATION,classes={Controller.class}) }) public class RootConfig { }
web容器
@EnableWebMvc等同于xml配置<mvc:annotation-driven/>
配置组件(视图解析器、视图映射、静态资源映射、拦截器。。。) extends WebMvcConfigurerAdapter
//SpringMVC只扫描Controller;子容器 //useDefaultFilters=false 禁用默认的过滤规则; @ComponentScan(value="com.atguigu",includeFilters={ @Filter(type=FilterType.ANNOTATION,classes={Controller.class}) },useDefaultFilters=false) @EnableWebMvc public class AppConfig extends WebMvcConfigurerAdapter {//视图解析器 @Override public void configureViewResolvers(ViewResolverRegistry registry) { // TODO Auto-generated method stub //默认所有的页面都从 /WEB-INF/ xxx .jsp //registry.jsp(); registry.jsp("/WEB-INF/views/", ".jsp"); } //静态资源访问 @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { // TODO Auto-generated method stub configurer.enable(); } //拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { // TODO Auto-generated method stub //super.addInterceptors(registry); registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**"); } }
public class MyFirstInterceptor implements HandlerInterceptor { //目标方法运行之前执行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // TODO Auto-generated method stub System.out.println("preHandle..."+request.getRequestURI()); return true; } //目标方法执行正确以后执行 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub System.out.println("postHandle..."); } //页面响应以后执行 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub System.out.println("afterCompletion..."); } }
springmvc异步处理
springmvc异步处理有两种方式:controller方法返回类型是Callable和DeferredResult
Callable
- 在方法返回类型为Callable的时候,将callable对象提交到TaskExecutor,主线程会开启一个隔离的线程进行异步处理,剩余流程继续执行不会等待
- 方法执行完DispatcherServlet和所有的Filter退出web容器的线程,但是response保持打开状态
- 当Callable返回结果,SpringMVC将请求又重新派发给容器进行之前的处理,继续进行视图渲染流程等(从收请求-视图渲染)
@ResponseBody @RequestMapping("/async01") public Callable<String> async01(){ System.out.println("主线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis()); Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis()); Thread.sleep(2000); System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis()); return "Callable<String> async01()"; } }; System.out.println("主线程结束..."+Thread.currentThread()+"==>"+System.currentTimeMillis()); return callable; }
执行流程:HandlerInterceptor的preHandle()-》目标方法,执行完后DispatcherServlet及所有的Filter退出线程-》执行Callable,完成后再次重发请求,拦截器会又一次打印preHandle(),并且剩余的postHandle、afterCompletion依次执行
DeferredResult
将deferredResult保存到队列中,写个监听器监听这个队列是否变化,如果有就触发create()进行业务处理,从队列拿到DeferredResult并把处理的结果保存回去
@ResponseBody @RequestMapping("/createOrder") public DeferredResult<Object> createOrder(){ DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000, "create fail..."); DeferredResultQueue.save(deferredResult); return deferredResult; } @ResponseBody @RequestMapping("/create") public String create(){ //创建订单 String order = UUID.randomUUID().toString(); DeferredResult<Object> deferredResult = DeferredResultQueue.get(); deferredResult.setResult(order); return "success===>"+order; }
public class DeferredResultQueue { private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedQueue<DeferredResult<Object>>(); public static void save(DeferredResult<Object> deferredResult){ queue.add(deferredResult); } public static DeferredResult<Object> get( ){ return queue.poll(); } }
异步的拦截器
- 原生API的AsyncListener
- 实现AsyncHandlerInterceptor接口
同步:request请求-》目标方法运行之前执行拦截器的preHandle()-》目标方法-》目标方法执行正确以后执行拦截器的postHandle()-》页面响应以后执行afterCompletion
控制器返回Callable