本篇将介绍以下几点
Servlet是基于Java技术的web组件,容器托管的,用于生成动态内容。像其他基于Java的组件技术一样,
Servlet也是基于平台无关的Java类格式,被编译为平台无关的字节码,可以被基于Java技术的webserver
动态加载并运行。容器(平时我们所使用的tomcat就是其中一种servlet容器),有时候也叫做servlet引擎,是webserver为支持servlet功能扩展的部分。客户端
通过Servlet容器实现的请求/应答模型与Servlet交互。(引用自oracle官方servlet3.1规范文档)
servlet按照一个严格定义的生命周期被管理,该生命周期包括:如何被加载?实例化?初始化?处理客户端请求?何时结束服务?
该生命周期可以通过Servlet接口中的API来表示:init、service、destroy
servlet容器负责加载和实例化servlet,加载和实例化可以发生在容器启动时,或者延迟初始化直到容器有请求需要处理时。(通过开发者配置来确定)
servlet容器必须在处理客户端请求之前,对servlet实例进行初始化(即调用Servlet.init接口)。可以完成一些读取持久化配置数据、初始化资源等一次性的动作。
完成初始化之后,servlet容器可以使用该servlet来处理客户端请求。(容器通过开发者的配置,即servlet-mapping来寻找适合当前请求的servlet)客户端请求由ServletRequest类型来封装表示、Servlet响应由ServletResponse类型来封装表示。这两个类型的对象都由容器进行实例化,在调用Servlet处理客户端请求时传递给Servlet的service方法。在Http请求的场景下,容器提供的实现对应为HttpServletRequest、HttpServletResponse。一个servlet实例应对多个客户端请求的情况,导致了我们需要在处理请求时保证线程安全。
在servlet技术中,除了Servlet接口用于处理请求这个组件接口外,还存在Filter、Listener这两个重要的组件接口。
其中Filter是一种代码重用的技术,运行运行过程中改变进入资源的请求和资源返回的响应中的有效负载和header信息。即可以在分发请求给servlet处理之前对请求进行拦截,之后再servlet完成处理,返回响应后对响应进行拦截。可以用于日志记录、验证等需求。
和servlet生命周期一样,应用同样存在生命周期。监听应用生命周期事件可以让开发人员更好的控制ServletContext、HTTPSession和ServletRequest的生命周期,可以更好的进行代码分解。Servlet事件监听器支持在ServletContext、HTTPSession和ServletRequest状态改变时进行事件通知。
实现自己的Filter可以通过实现接口javax.servlet.Filter来完成,之后通过web.xml或者注解配置到Servlet容器中,让容器在处理请求时应用此时配置的Filter
public class FirstFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println(getClass().getName() + " init()"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println(getClass().getName() + " doFilter()"); chain.doFilter(request, response); } @Override public void destroy() { } }
Listener的种类可以分为:
在收到客户端请求时,web容器确定转发到哪个web应用(获取servlet上下文路径),之后用于映射到servlet的路径是请求对象的请求URL减去上下文和路径参数部分,之后应用以下步骤来找出servlet来处理请求,短路原则,一旦找到匹配的servlet,之后的步骤直接跳过
通过@WebListener注解配置ServletContextListener实现类,在容器初始化servlet上下文时,调用ServletContext的API来注册
@WebListener public class FirstServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { ServletContext servletContext = sce.getServletContext(); ServletRegistration.Dynamic dynamic = servletContext.addServlet("firstServlet", FirstServlet.class); dynamic.addMapping("/first", "/first/*"); dynamic.setLoadOnStartup(1); ServletRegistration.Dynamic dynamic2 = servletContext.addServlet("secondServlet", SecondServlet.class); dynamic2.addMapping("/second", "/second/*"); dynamic2.setLoadOnStartup(1); // 异步servlet ServletRegistration.Dynamic firstAsyncServlet = servletContext.addServlet("firstAsyncServlet", FirstAsyncServlet.class); firstAsyncServlet.setLoadOnStartup(1); firstAsyncServlet.setAsyncSupported(true); firstAsyncServlet.addMapping("/async/first", "/async/first/*"); // TODO Filter调用顺序??? FilterRegistration.Dynamic filterRegistration = servletContext.addFilter("secondFilter", SecondFilter.class); filterRegistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), true, "/first"); FilterRegistration.Dynamic filterDynamic = servletContext.addFilter("firstFilter", FirstFilter.class); filterDynamic.addMappingForUrlPatterns( EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, "/first"); // 非阻塞IO servlet ServletRegistration.Dynamic firstNoBIOServlet = servletContext.addServlet("firstNoBIOServlet", FirstNoBlockIOServlet.class); firstNoBIOServlet.setLoadOnStartup(1); firstNoBIOServlet.setAsyncSupported(true); firstNoBIOServlet.addMapping("/nobio/first", "/nobio/first/*"); } @Override public void contextDestroyed(ServletContextEvent sce) { } }
为什么可以使用注解直接来配置Listener、Filter、Servlet,又提供了ServletContainerInitializer这种初始化容器的机制?
在spring中利用基于SPI机制的ServletContainerInitializer来初始化容器,具体实现方式是:
类SpringServletContainerInitializer实现了接口ServletContainerInitializer,并指定了WebApplicationInitializer作为初始化类(AbstractAnnotationConfigDispatcherServletInitializer作为其便利的抽象类,开发者可以继承该类作为初始化容器的配置类)。如:
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { // 指定ROOT ApplicationContext的配置类 return new Class[]{RootConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { // 指定Web ApplicationContext的配置类 return new Class[]{WebConfig.class}; } @Override protected String[] getServletMappings() { // 指定DispatcherServlet的servlet-mapping,此处指定为default servlet。任何未找到映射的请求都会由DispatcherServlet来处理请求 return new String[]{"/"}; } @Override protected Filter[] getServletFilters() { // 配置Filter return super.getServletFilters(); } }
spring中利用监听器ServletContextListener初始化web应用的父ApplicationContext
这篇文章中梳理了Servlet中的常用技术,主要涉及Servlet、Filter、Listener的知识点和配置细节。之后引申出springMVC是如何利用这些知识点来构建一个web框架的。servlet作为java web开发中的基石是每个开发者都必须掌握的技能。关于springMVC中的更多原理细节将在后续文章整理发出,期待你的关注