创建一个小的 web 应用, mvc, 麻雀虽小, 五脏俱全
补1: servlet没有main()方法, 他们受控与另外一个Java应用, 这个Java应用称为 容器, tomcat就是这么一个容器, web服务器(如apache)得到一个指向servlet的请求时, 服务器不是把这个请求给servlet本身, 而是交给部署该servlet的容器, 要由容器向servlet提供http请求和响应,而且由容器调用servlet的方法(doGet()和doPost()), 容器运行多个servlet线程来处理对同一servlet的多个请求(对每个请求分配一个线程,而不是每个用户)
补2: 容器的作用
通信支持,利用容器提供的方法, web服务器能轻松的与servlet进行通信
生命周期管理,容器控制着servlet的生与死
多线程支持, 容器会自动的接收每个servlet的请求,创建一个新的java线程
jsp 支持,负责将jsp翻译成真正的java
补3:容器处理请求的process ( 这个为准, 这个是正确的流程 )
a. 用户点击一个链接, 指向一个servlet而不是一个静态网页.
b. web 服务器接到这个请求后转发给容器, 容器接着创建两个对象, HttpServletRequest 和 HttpServletReponse
c. 容器根据请求中的URL找到相应的servlet, 为这个请求创建线程, 并把请求对象HttpServletRequest和响应对象HttpServletResponse传递给这个servlet线程. 此处要利用 web.xml 部署文件
d. 线程接下来调用service()方法, 根据请求的不同, service()方法调用 doGet() 和 doPost()方法
e. doGet()或doPost()生成动态网页, 并把这个网页塞到响应对象里
f. service()方法结束, 随之线程结束, 容器把响应对象装换为一个http响应,返回给web服务器, 然后删除请求和响应对象, web服务器发给客户.
补4: 流程图
progress
1. 请求初始页面
2. 提交表单
创建 开发环境
开发环境的目录结构, 等到部署WEB应用的时候, 要把这个目录结构适当地复制到特定容器所希望的位置上.
这个目录结构适用于 小中型项目
创建部署环境( 部署时, 只需要编译后的文件, 所以只需要classes文件 )
构建应用的路线图
1. 分析web应用的用户视图( 例如表单 等等 )
2. 分析体系结构
3. 建立创建和部署应用的开发环境和部署环境
4. 创建应用
开发WEB process
1. 创建 form.html 并分别放置在开发环境( projec->web->form.html) 和部署环境(tomcat->webApps->Project->form.html)
form.html 是一个静态网页
1: <html>
2: <head>
3: <title>Beer Room</title>
4: </head>
5: <body>
6: <h1 align=""center>Beer Selection Page</h1>
7:
8: <form method="POST" action="SelectBeer.do">
9: Select beer characteristics<p>
10: Color:
11: <select name="color" size="1">
12: <option>light
13: <option>amber
14: <option>brown
15: <option>dark
16: </select>
17: <br />
18: <center>
19: <input type="submit">
20: </center>
21: </form>
22: </body>
23:
24: </html>
2. 以上代码中, SelectBeer.do 只是一个逻辑名, 只是用来作为中间变量的作用, 所以需要确认XML配置文件, 将 SelectBeer.do 请求与指针的servlet 绑定, 所以编辑 web.xml文件, 并分别放置开发环境(Project->etc->web.xml) 和部署环境(tomcat->webApps->Project->WEB-INF->web.xml) 这个 web.xml 被叫做 部署描述文件(DD)
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://ww.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <servlet> <servlet-name>Ch3 Beer</servlet-name> <servlet-class>com.example.web.BeerSelect</select-class> </servlet> <servlet-mapping> <servlet-name>Ch3 Beer</servlet-name> <url-pattern>/SelectBeer.do</url-pattern> </servlet-mapping> </web-app>
其中, <url-pattern>/SelectBeer.do</url-pattern> 这里的 "/" 是一个规定.
这个web.xml 在容器启动时就会加载进内存, 当客户点击表单提交时, 容器会搜索web.xml文件, 根据用户提交的逻辑名找到真正对应的servlet名. 注意此处的 servlet-class 的路径就是 classes 里的路径, 而 url-pattern 是web 页面的路径, 因为我们的页面就放在 project 的目录下(即根目录下, /SelectBeer.do 表示在根目录下查找(虽然这个SelectBeer只是个逻辑名, 但是也要指定路径))
1) 根据表单, 请求的是 SelectBeer.do, 但是实际请求的是 /Project/SelectBeer.do 即 带上了webApps直接下边的目录就是根目录.
2) 容器搜索DD, 找到 <url-pattern> 与 /SelectBeer.do 匹配的一个<servlet-mapping>, 这里的/ 表示web应用上下文根.
3) 容器看到对应这个<url-pattern>的<servlet-name>是 “Ch3 Beer”, 但是这并不是实际servlet类文件的名字, "Ch3 Beer"是servlet名, 但不是servlet类的名字. 对容器来说, servlet名只是在DD中得一个标记作用. 用来做映射的.
4) 容器根据 "Ch3 Beer" 找到真正的servlet类.
5) 容器知道由哪个servlet 类来处理这个请求以后, 如果这个servlet还没有被初始化, 就会加载类, 并初始化该servlet.
6) 容器开启一个新的线程来处理这个请求, 并把请求传递给线程( 调用servlet的service()方法)
7) 容器把响应( 当然是通过web服务器) 发回给客户.
3. 容器找到匹配的servlet后, 开启一个新线程来处理这个请求, 并把请求传递给(servlet 的 service()方法 )
a 简单的 servlet
编译: javac –classpath D:apache-tomcat-7.0.42libservlet-api.jar –d classes src/com/example/web/BeerSelect.java
注意: 编译 BeerSelect 时由于用到了 BeerExpert类, 因为在 BeerSelect中有 import com.example.model;, 这也就引出了如何 import 自己的类的问题:
首先, 如果自己的需要引用的类和被引用的类在一个包中, 那么不需要 import 语句引用,
另外, 如果不在一个包中, 我们要在引用的类中添加 import 自己的类, 同时, 需要让 java 在编译时能够找到我们自己写的类, 有两种办法:
1. 将我们写的类的路径写入环境变量 classpath
2. 或者在编译时增加 classpath 参数, 将我们自己写的类的根目录包括其中, 例如:
javac –classpath D:apache-tomcat-7.0.42libservlet-api.jar;d:web_homeworkBeerProjectclasses –d classes src/com/example/web/BeerSelect.java
注意, classpath 有两个参数, 分隔符使用分号; 并且中间不能有空格.
首先要进入 project 这个根目录下编译, 因为只有这样, 才能确认懂爱 classes 这个目录, ( 以及后边要编译的java文件 )
- sourcepath 源路径, 注意这个源路径类似根路径, 因为 java 会根据 package 来查找类, 所以当提供源路径时, 只需提供到package的根部
注:程序到这卡住了, 查看资料, 补充如下:
form 中的 action 是处理表单内容的程序的地址, url-pattern是servlet的url映射, 要在外部访问一个servlet, 需要一个访问地址, url-pattern 就是干这个的, 为一个servlet指定了访问名称(逻辑名, 不是servlet名),action就通过这个名字访问这个servlet
model 层用来完成复杂的商业逻辑
改进版本的 servlet
4. 容器把响应( 当然是通过web服务器) 发回给客户
这部是隐藏的, 由容器完成
利用JSP的 Process
注意上图:model : BeerExpert 这里边有商业逻辑
control : servlet 是用来控制的
view : jsp 用来显示的
那么, view 与 model 之间没有任何联系, 从图上也可以看出, 只有通过控制器 servlet 来建立联系
view: (jsp)
1: <%@ page import="java.util.*" %>
2:
3: <html>
4: <body>
5: <h1 align="center">Beer Recommendations Jsp</h1>
6: <p>
7: <%
8: // servlet 传过来的从model获得的内容
9: // 虽然没有用上 response, 但是还是很有用的
10: List styles = (List)request.getAttribute("styles");
11: Iterator it = styles.iterator();
12: while (it.hasNext()) {
13: out.print("<br>try: " + it.next());
14: }
15:
16: %>
17:
18: </p>
19:
20: </body>
21: </html>
control: (servlet)
/* * BeerSelect2.java * ----------------------------------------- * The second version about servlet. */ package com.example.web; import com.example.model.*; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; public class BeerSelect extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // 不需要定义 printwriter 等一系列流操作 String c = request.getParameter("color"); BeerExpert be = new BeerExpert(); List result = be.getBrands(c); // 为请求对象增加一个属性,供JSP使用,注意JSP要寻找styles request.setAttribute("styles", result); // 为JSP请求一个分派器 RequestDispatcher view = request.getRequesetDispatcher("result.jsp"); view.forward(request, response); } }
model: (java)
/* * BeerExpert.java * --------------------------------- * 处理商业逻辑,连接数据库等。 */ package com.example.model; import java.util.*; public class BeerExpert { public List getBrands(String color) { List brands = new ArrayList(); if (color.equals("amber")) { brands.add("Jack Amber"); brands.add("Red Moose"); } else { brands.add("Jail Pale Ale"); brands.add("Gout Stout"); } return(brands); } }