我们在开发中无时无刻都在与Servlet进行接触,只是因为框架的封装性,我们很少直接地去操作servlet,但再怎么封装,基本的思路都不会变化,变得只是实现的方式,
Servlet是什么:
网上的回答基本是Servlet是Java类,用于处理业务逻辑,
详细一点说Servlet确实是Java类,他是在Tomcat包中的,我们知道Tomcat是服务器,他为我们封装Request和Response,我们只需要实现Servlet接口就可以拿到封装好的Request和Response对象,是不是很方便呢,那么Servlet起到的作用就是在拿到封装好的Request后进行业务逻辑处理,然后返回Response,Servlet的整个生命周期由Tomcat控制,
因此,通过这个就能很好的实现开发业务逻辑与Http封装的解藕,开发也会更加高效,
Servlet的作用:
我们可以通过顶层接口来分析Servlet的作用:
这几个方法都是由容器去调用,我们自己去调用是无效的,
- init,这个接受一个容器帮我们封装好的ServletConfig,然后保存到本地
- getServletConfig返回先前保存到本地的ServletConfig
- getServletInfo返回一些version或者author等信息
- service接受请求对象,用作开发人员业务处理,完毕后返回响应对象
- destroy,容器销毁servlet
我们去来到源码中的GenericServlet,看看他是怎样为我们去实现的:
public abstract class GenericServlet implements Servlet, ServletConfig{private transient ServletConfig config;@Overridepublic void init(ServletConfig config) throws ServletException {this.config = config;this.init();}@Overridepublic String getInitParameter(String name) {return getServletConfig().getInitParameter(name);}@Overridepublic abstract void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;@Overridepublic ServletConfig getServletConfig() {return config;}
}
这里截取了部分方法实现,可以看到,使用了模板方法模式,
反正我手长:模板方法模式zhuanlan.zhihu.comGenericServlet是abstract类,你可以很经常地在abstract类中看见这种设计,
另外还有一个HttpServlet,
它的注释让我们不要去实现service方法,因为他已经为我们封装了请求分发的逻辑,比如判断一个请求是get还是post,然后分发到具体的方法上去,
if (method.equals(METHOD_GET)) {doGet(req, resp);} else if (method.equals(METHOD_HEAD)) {doHead(req, resp);} else if (method.equals(METHOD_POST)) {doPost(req, resp);} else if (method.equals(METHOD_PUT)) {doPut(req, resp);} else if (method.equals(METHOD_DELETE)) {doDelete(req, resp);} else if (method.equals(METHOD_OPTIONS)) {doOptions(req,resp);} else if (method.equals(METHOD_TRACE)) {doTrace(req,resp);} else {//// Note that this means NO servlet supports whatever// method was requested, anywhere on this server.//String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[1];errArgs[0] = method;errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);}
我们需要实现的是doGet、doPost这些方法,在里面写我们的业务逻辑,这里又有模板方法模式的身影
Servlet有两个息息相关的类,
ServletContext和ServletConfig,
对应JSP中九大作用域中的application和config对象,本质是用于做数据分享,
ServletContext:
Web应用有且仅有一个ServleContext,在应用起动的时候会被创建,他可以实现应用内的数据共享,
但是仅限单机版的应用程序,因为他仅仅是保存在一个JVM创建的上下文中,如果是分布式的,那建议使用数据库、session或者其他组件来代替
ServletConfig:
/*** A servlet configuration object used by a servlet container to pass* information to a servlet during initialization.*/
用于容器向Servlet传递信息,
在ServletConfig和ServletContext都有getInitParameter方法, 这两个方法的都能从web.xml中获取参数,但是是有区别的。
可以在xml中配置,
<!-- 通过ServletContext#getInitParameter获取 -->
<context-param><param-name>test</param-name><param-value>ServletContext</param-value></context-param><servlet><servlet-name>testServletConfig</servlet-name><servlet-class>com.web.test.TestServletConfig</servlet-class><!-- 通过ServletConfig#getInitParameter获取 --> <init-param><param-name>testServletConfig</param-name><param-value>getFromServletConfig</param-value></init-param><init-param><param-name>encode</param-name><param-value>utf-8</param-value></init-param></servlet>
如何获得ServletContext和ServletConfig:
ServletConfig:可以通过Servlet#getServletConfig获取,该方法就在Servlet中,Servlet是在ServletContext初始化之后才初始化(Servlet可以在Web应用启动或者发送请求的时候初始化,这要看load-on-startup的配置,当这个值>=0(相反为不配置或者<0)时,Web应用启动的时候就会初始化),Servlet初始化之后你才能获得ServletConfig。
ServletContext:在web.xml中读取到<context-param>的时候就会创建ServletContext,然后将<context-param>中的值(配置文件的地址)塞到ServletContext中,
所以理论上来说,这一步之后,Web容器中的内容都能获取ServletContext了,就看源码设计者觉得哪个地方需要就提供一个方法,然后Tomcat启动的时候会负责调用方法或者注入ServletContext等一系列操作:
- 实现WebApplicationInitializer接口
- 注册监听器,实现ServletContextListener
- ServletConfig#getServletContext
- ServletRequest#getServletContext
- HttpSession#getServletContext
侧面也表现了ServletContext和ServletConfig的作用范围,
还有一个有意思的点,你只要是在能获取到ServletContext的地方,你就可以通过ServletContext#setAttribute来设置值,这个操作对整个Web应用都可见,不需要返回ServletContext,因为我前面说过Web应用有且仅有一个ServleContext,出现的地方都是传递的引用:
JavaWeb项目中有一个WEB-INF的文件夹,可以放置一些不能被客户端直接访问的数据,但不能被客户端访问并不代表不能被访问,实际上可以通过Servlet映射或则重定向来访问,
RequestDispatcher rd = request.getRequestDispatcher("./WEB-INF/view/a.jsp");
rd.forward(request, response);<servlet><servlet-name>Customer</servlet-name><jsp-file>/WEB-INF/customer.jsp</jsp-file>
</servlet>
<servlet-mapping><servlet-name>Customer</servlet-name><url-pattern>/User</url-pattern>
</servlet-mapping>