servlet和CGI
我们知道Web服务器(如apache)只擅长提供静态页面,因此在动态应用场景,Web服务器会把请求转发给另一个能够动态处理的辅助应用(helper app),这个辅助应用就是CGI,而CGI可以用C、Python、PHP、Perl等等来实现。请求和应答如下图:
而Java的CGI实现就是Servlet,如下图:
由于servlet没有main()方法,因此它受控于web Container app(容器),而Tomcat就是一个这样的容器。Tomcat容器的作用提供通信支持、声明周期管理、多线程支持、声明方式实现安全、JSP支持等,看参考Tomcat初识。它的流程是这样的:
- Client发出一个请求到WebServer;
- WebServer接收到请求好转发给Web Container;
- Web Container根据URL在部署描述文件(web.xml)中找到对应的servlet;
- Web Container调用对相应的servlet的service()方法;
- servlet的service()方法根据请求的类型get/post调用相应的处理逻辑;
- 处理完毕后servlet返回结果;
servlet实例
- 构建项目
我们使用Eclipse+Maven来构建Java的servlet应用,以前曾经写过一篇超入门级的:maven + eclipse + tomcat 实战JSP,文章里使用的maven构建再导入到eclipse中,当然你也可以从eclipse来使用maven插件来构建项目,我的测试项目如下:
如果提示错误
The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path
这是因为没有添加servlet类库,可以在pom添加servlet的依赖库,也可以添加server runtime的tomcat server.
默认的部署文件web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> </web-app>
- 运行
建立完毕后我们可以通过Run As -> Run on Server就可以浏览index.jsp了(我用的Eclipse是Eclipse Java EE IDE for Web Developers.),显示“Hello World!”。
添加Servlet:项目"mytest"右键->New->Other->Web->Servlet,ClassName填上“HelloOther”,Next会让设置URL路径,初始值等,再Next可以看到你需要的方法如doGet、doPost等,Finish后你会看到web.xml:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>HelloOther</servlet-name> <display-name>HelloOther</display-name> <description></description> <servlet-class>net.oseye.web.HelloOther</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloOther</servlet-name> <url-pattern>/HelloOther</url-pattern> </servlet-mapping> </web-app>
在net.oseye.web.HelloOthe类的doGet方法体中编码:
response.getWriter().println("Hello Other!");
- 打包WAR
可以使用mvn命令创建WAR包:
mvn package
PS:这里我们只使用Tomcat,没看到使用Web Server呀?这是因为Tomcat本身就具有基本的HTTP服务器的功能,只是没有Apache等专业的Web Server健壮和完整。
简单的MVC
前面的示例我们在servlet中使用了
response.getWriter().println("Hello Other!");
但如果是整个HTML页面将会非常麻烦,因此我们考虑使用MVC架构,servlet充当Controller角色,JSP充当View,Java Class充当Model,结构图如下:
通过Controller(servlet)从Model(DB)中获取数据,然后把数据包装成Servlet的请求属性,然后把请求(request和reponse)转发至Jsp(View),JSP生成一个HTML页面,并使用应答对象(response)返回至容器,示例图如下:
接着上面的示例代码,在net.oseye.web.HelloOthe类的doGet方法体中编码:
request.setAttribute("word", "Hello Other!"); RequestDispatcher view=request.getRequestDispatcher("HelloOther.jsp"); view.forward(request, response);
HelloOther.jsp的代码如下:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <%=request.getAttribute("word")%> </body> </html>
这就实现了MVC结构,项目结构图如下:
后记
- ServletConfig和ServletConext
Servlet的无参构造函数只是让Servlet变成一个对象,而init()方法让其成为一个Servleet对象,每个Servlet都有一个ServletConfig对象,用于访问部署配置文件信息,而且可以通过ServletConfig对象访问ServletContext。每个Web应用都有一个ServletContext对象,用来访问容器相关信息以及部署配置文件,其实ServletContext称为AppContext更合适一些。
部署(DD)文件web.xml是这样的:<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>email</param-name> <param-value>kevin@oseye.net</param-value> </context-param> <servlet> <servlet-name>HelloOther</servlet-name> <display-name>HelloOther</display-name> <description></description> <servlet-class>net.oseye.web.HelloOther</servlet-class> <init-param> <param-name>tel</param-name> <param-value>13910567832</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>HelloOther</servlet-name> <url-pattern>/HelloOther</url-pattern> </servlet-mapping> </web-app>
String tel=getServletConfig().getInitParameter("tel"); String email=getServletContext().getInitParameter("email"); response.getWriter().println("tel:"+tel+",email:"+email);
- 应答除了分派给JSP,还可以使用PrintWriter和ServletOutputStream,前者是字符,后者是流。获取方式:
PrintWriter pr=response.getWriter(); ServletOutputStream os=response.getOutputStream();
- 请求和应答常用的方法
request.getParameter(arg0) //获取get/post参数值 request.getHeader(arg0) //获取头信息 request.getCookies() //获取cookies request.getSession() //获取session request.getRequestDispatcher(arg0) //分派请求 response.sendRedirect(String arg0) //重定向 response.setHeader(String arg0, String arg1) //设置头信息,如果重复就替换 response.addHeader(String arg0, String arg1) //添加头信息 response.setContentType(arg0) //设置ContentType,避免手动拼写错误