文章目录
什么是容器
servlet中没有main()
方法,servlet受控于另一个Java应用,这个Java应用叫作 容器,Tomcat就是这样一个容器。
客户端发出请求,Web服务器应用接收到请求并转发该请求,但并不是把请求转给servlet本身,而是交给部署了该servlet的容器,由容器提供HTTP请求和响应,且由容器调用servlet的方法,如doPost()
、doGet()
。
容器能提供什么
容器是怎么处理请求的
- 用户点击一个链接,其URL指向servlet,而不是静态页面;
- 容器看出来这个请求要的是一个servlet,所以容器创建两个对象:
HttpServletRequest
和HttpServletResponse
; - 容器根据请求的URL找到正确的servlet,为这个请求创建或分配一个线程,并把请求和响应对象传递给servlet线程;
- 容器调用servlet中的
service()
方法。根据请求的类型,service()
会调用doGet()
或doPost()
,假设接下来调用的是doGet()
; doGet()
生成动态页面,并把这个页面填入响应对象中;- 线程结束,容器把响应对象转换成HTTP响应,并将HTTP响应返回给客户端,然后删除请求和响应对象。
使用部署描述文件将URL映射到servlet
把servlet部署到容器时,会创建一个部署描述文件,这个部署描述文件会告诉容器如何运行servlet和JSP。
下面就是部署描述文件web.xml
的内容。
一个servlet可以有三个名字,分别是,
- 客户知道的URL名 :
http://localhost:8080/ch1/Serv1
- 部署人员知道的内部名:
Chapter1 Servlet
- 实际文件名(完全限定类名):
Ch1Servlet
web.xml
中的<servlet>
元素将内部名映射到完全限定类名,<servlet-mapping>
元素将内部名映射到URL名,<servlet-name>
把<servlet>
元素与相应的<servlet-mapping>
元素绑定。
一个请求到来时,容器在运行时会询问“对于这个请求的URL,我应该调用哪个servlet”,<servlet-mapping>
元素能根据URL给出内部名,通过内部名又可以找到servlet对应的类名。
MVC
public class DatingServlet extends HttpServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException{
// 业务逻辑
// ...
// HTML打印到输出流
PrintWriter out = response.getWriter();
out.println("something really ugly goes here.");
}
}
public class DatingServlet extends HttpServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException{
// 业务逻辑
// ...
// 把请求转发给JSP,由JSP处理响应HTML
// ...
}
}
以上代码实现了 业务逻辑 与 表示 的分离,但业务逻辑仍放在servlet里。
MVC,实现了 业务逻辑 与 表示 的分离,且业务逻辑放在一个独立的Java类里,这个Java类就是MVC中的M,即模型。servlet承担着控制器(C)的角色,在模型(M)和视图(V)之间协调。看个例子来理解吧。
分析用户视图及体系结构
用户视图
体系结构
- 客户端请求
form.html
,容器获得form.html
,并将form.html
返回给客户端; - 用户在
form.html
呈现的页面里选择颜色,并点击Submit,客户端会携带请求数据向容器发送POST请求; - 容器根据URL找到正确的servlet,并将请求数据传递给servlet;
- servlet调用模型即Java类
BeerExpert
获得回答,然后将该回答添加至请求对象中,并将该请求对象转发给JSP; - JSP从请求对象中获得回答,生成页面;
- 容器将页面返回到客户端。
创建开发环境
创建部署环境
- 在Tomcat上部署web应用,web应用根目录必须放在Tomcat主目录
webapps
中 web.xml
必须放在WEB-INF
中,这是Servlet规范
开发、编译和测试
V1版本
//BeerSelect.java
package com.example.web;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import com.example.model.*;
public class BeerSelect extends HttpServlet{
public void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException,ServletException{
// response.setContentType("text/html");
// PrintWriter out = response.getWriter();
// out.println("Beer Selection Advice<br>");
// String c = request.getParameter("color");
// out.println("<br>Got beer color " + c);
String c = request.getParameter("color");
BeerExpert be = new BeerExpert();
List<String> result = be.getBrands(c);
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("Beer Selection Advice<br>");
Iterator it = result.iterator();
while(it.hasNext()){
out.println("<br>try: "+ it.next());
}
}
}
- 在目录beerV1下执行
javac -classpath src;d:\tomcat\lib\servlet-api.jar -d classes src/com/example/web/BeerSelect.java
,编译servlet。
【遇到问题】:在目录beerV1下执行javac -classpath d:\tomcat\lib\servlet-api.jar -d classes src/com/example/web/*.java
,编译servlet时,报错提示“程序包com.example.model不存在”。
【问题原因】:-classpath
指向唯一路径,导致无法找到import。
【解决方法】:用;
隔开,用相对路径。
//BeerExpert.java
package com.example.model;
import java.util.*;
public class BeerExpert{
public List<String> getBrands(String color){
List<String> brands = new ArrayList<String>();
if(color.equals("amber")){
brands.add("Jack Amber");
brands.add("Red Moose");
}else{
brands.add("Jail Pale Ale");
brands.add("Gout Stout");
}
return brands;
}
}
- 在目录beerV1下执行
javac -d classes src/com/example/model/*.java
,编译类BeerExpert
。 - 把相应文件复制到部署环境。
- 双击D:\tomcat\bin\startup.bat启动。
- 浏览器访问
http://localhost:8080/Beer-v1/form.html
,选择颜色为"amber",返回相应建议。
V2版本
V1版本里,servlet输出响应(打印建议);V2版本里,servlet会调用JSP生成输出。
//BeerSelect.java
package com.example.web;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import com.example.model.*;
public class BeerSelect extends HttpServlet{
public void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException,ServletException{
// response.setContentType("text/html");
// PrintWriter out = response.getWriter();
// out.println("Beer Selection Advice<br>");
// String c = request.getParameter("color");
// out.println("<br>Got beer color " + c);
String c = request.getParameter("color");
BeerExpert be = new BeerExpert();
List<String> result = be.getBrands(c);
// response.setContentType("text/html");
// PrintWriter out = response.getWriter();
// out.println("Beer Selection Advice<br>");
// Iterator it = result.iterator();
// while(it.hasNext()){
// out.println("<br>try: "+ it.next());
// }
request.setAttribute("styles",result);
RequestDispatcher view = request.getRequestDispatcher("result.jsp");
view.forward(request,response);
}
}
<!-- result.jsp -->
<%@ page import="java.util.*" %>
<html>
<body>
Beer Selection Advice<br>
<%
List<String> styles = (List<String>)request.getAttribute("styles");
Iterator it = styles.iterator();
while(it.hasNext()){
out.println("<br>try: " + it.next());
}
%>
</body>
</html>
- 在目录beerV1下执行
javac -classpath src;d:\tomcat\lib\servlet-api.jar -d classes src/com/example/web/BeerSelect.java
,编译servlet。 - 把
result.jsp
复制到部署环境
- 双击D:\tomcat\bin\shutdown.bat关闭Tomcat,双击startup.bat重启Tomcat。
- 浏览器访问
http://localhost:8080/Beer-v1/form.html
,选择颜色为"amber",返回相应建议。