第1章--Servlet
Servlet简介
Servlet应用于?
浏览器发出HTTP请求,服务器接收请求后返回响应给浏览器。
接收请求后到返回响应之间:
服务器将请求对象转交给Servlet容器
Servlet容器根据HTTP请求的具体路径将请求转交给具体的Servlet
Servlet在收到请求后进行相应的处理逻辑后,将处理返回给服务器
服务器将HTTP响应返回给浏览器
Servlet是什么?
Servlet = Server + Applet -- 运行于Server的Applet
Applet (在web环境下运行与客户端的Java组件):没有main method,不能独立运行于JVM,需要特殊的容器装载运行,由容器管理生成销毁
Servlet:一个Servlet就是一个Java类,并提供基于请求-响应模式的Web服务
Servlet容器:装载和管理Servlet;为一个服务端程序,用于转交请求给Servlet
Eclipse中的Servlet配置:
prerequisite: Tomcat, Eclipse, Eclipse Maven插件
配置tomcat用户:
tomcat/conf/tomcat-users.xml文件
将文件中所有内容删除,加入以下内容并保存;
<?xml version='1.0' encoding='utf-8'?> <tomcat-users> <role rolename="manager-gui"/> <role rolename="manager-script"/> <user username="admin" password="123456" roles="manager-gui,manager-script"/>
</tomcat-users>
保存后启动tomcat,范围localhost:8080
点击右侧Manager App按钮,登陆,表明tomcat管理员配置成功
Maven中配置Tomcat服务器:
修改maven的配置文件~/.m2/settings.xml (different from maven/conf/settings.xml)
增加tomcat服务器:在<servers></servers>直接增加子元素
<server> <id>tomcat</id> <username>admin</username> <password>123456</password> </server>
Tomcat Maven插件配置:
在 web app的pom.xml 文件中的<plugins></plugins>直接增加子元素,增加代码:
<plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <url>http://localhost:8080/manager/text</url> <path>/web_project_template</path> <uriEncoding>UTF-8</uriEncoding> <finalName>web_project_template</finalName> <server>tomcat</server> </configuration> </plugin>
Servlet Hello World:
在web.controller包下New Class -- Superclass: HttpServlet
Override methods: doGet(HttpServletRequest, HttpServletResponse); service(HttpServletRequest, HttpServletResponse);
在service()中:syso.println("service method");
重写doGet():
System.out.println("doGet method"); PrintWriter pw = resp.getWriter(); pw.print("hello world"); pw.close();
src->main->webapp->WEB-INF->web.xml 配置对应的Servlet
<servlet> <servlet-name>TestServlet</servlet-name> <servlet-class>com.netease.server.example.web.controller.TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
运行:
Eclipse中对应项目,右键Run As--Run Configuration--双击左侧菜单Maven Build 以创建新的maven build--Name: maven deploy; Browser Workspace: 所要部署的项目; Goals: tomcat7:deploy
启动tomcat,点击run即可进行部署。
// Eclipse内置Tomcat-plugin:http://www.jianshu.com/p/8da9ca8c0667
Eclipse internal web browser:Window->Show View->Other->Internal Web Browser.
路径:http://localhost:8080/$project-path/$page-path
project-path: pom.xml中配置的<plugin>tomcat7-maven-plugin的path: /web_project_template
page-path: web.xml中配置的<servlet-mapping>url-pattern: /hello
Error:
[INFO] tomcatManager status code:200, ReasonPhrase:
[INFO] FAIL - Application already exists at path [/web_project_template]
solution: https://stackoverflow.com/questions/22671420/how-to-redeploy-a-war-on-remote-tomcat-7-using-maven-tomcat-plugin --> http://tomcat.apache.org/maven-plugin-2.2/tomcat7-maven-plugin/deploy-mojo.html#update
Now--it works. -- http://localhost:8080/web_project_template/hello
Servlet处理流程:
浏览器中输入地址 http://localhost:8080/web_project_template/hello
Servlet容器根据地址和配置文件web.xml找到对应的servlet: TestServlet.java
同时将请求传递给对应servlet的service方法
service方法是servlet的核心,每当客户端请求一个servlet对象时,会调用该对象的service方法,并传递给service方法一个HttpServletRequest对象和一个HttpServletResponse对象作为参数。
使用HTTP的get方法访问的servlet,则service方法会将相应的请求传递给doGet()处理,post同理
在doGet()方法中通过HttpServletResponse对象将"Hello World"返回给客户端
Servlet接口与实现类
Servlet生命周期:初始化、请求处理、销毁
初始化:init():客户端第一次请求servlet时,servlet容器会创建servlet对象的实例,此时servlet容器会调用servlet的init();如果在配置文件中配置了loadOnSetup元素,则servlet会在容器启动时做相应的加载。
请求处理:service():将不同的http请求转发给不同的servlet方法,常用doGet()/diPost()。
销毁:destroy():由servlet容器对servlet进行资源回收和清理
进入tomcat主页右边点击app manager,会看到所有部署的引用,点击web-project-template的stop,会执行destroy();
想要在servlet进行逻辑处理之前做一些准备工作,或在servlet实例销毁之前进行资源回收或清理的工作:
i.e. override method: init(); destroy();
init(); or init(ServletConfig config); ? 一般情况选择init();
init()中:syso.println("init method");
destroy()中:syso.println("destroy method");
get与post的区别:
get:传输方式-HTTP header;url可见;设计目的:获取数据;安全性低
post:传输方式-HTTP body;url不可见;设计目的:发送数据;安全性高
i.e.
/src/main/webapp下有静态页面getPostTest.html,
包含两个表单form,分别使用get和post方法,action="servlet/GetPostServlet"
对应有servlet对象GetPostServlet.java
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); String name1 = request.getParameter("name1"); String pw1 = request.getParameter("pw1"); out.println("<HTML>"); out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>"); out.println(" <BODY>"); out.print(" 调用doGet 方法 "); out.println("<br></br>"); out.println("用户名:" + name1); out.println("<br></br>"); out.println("密码:" + pw1); out.println(" </BODY>"); out.println("</HTML>"); out.flush(); out.close(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); String name2 = request.getParameter("name2"); String pw2 = request.getParameter("pw2"); out.println("<HTML>"); out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>"); out.println(" <BODY>"); out.print(" 调用doPost 方法 "); out.println("<br></br>"); out.println("用户名:" + name2); out.println("<br></br>"); out.println("密码:" + pw2); out.println(" </BODY>"); out.println("</HTML>"); out.flush(); out.close(); }
在web.xml对应servlet元素
<servlet> <servlet-name>GetPostServlet</servlet-name> <servlet-class>com.netease.server.example.web.controller.GetPostServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>GetPostServlet</servlet-name> <url-pattern>/servlet/GetPostServlet</url-pattern> </servlet-mapping>
部署后,地址栏 http://localhost:8080/web-project-template/getPostTest.html
点击按钮会跳转到 web_project_template/servlet/GetPostServlet
不同的是,get提交后的url为http://localhost:8080/web_project_template/servlet/GetPostServlet?name1=admin1&pw1=nopass.a1
而post提交后的url为http://localhost:8080/web_project_template/servlet/GetPostServlet
Servlet配置参数:
将配置信息以硬编码方式固定,是不好的习惯--代码维护成本高
那么如何配置呢?:ServletConfig
特点:
在Servlet初始化过程中,<init-param>参数将被封装到ServletConfig中
每个Servlet支持设置一个或者多个<init-param>
以Servlet为单位,不是全局共享
web.xml中,在<servlet>子元素中添加
<servlet> <init-param> <param-name>data1</param-name> <param-value>value1</param-value> </init-param> <init-param> <param-name>data2</param-name> <param-value>value2</param-value> </init-param> <servlet-name>ServletConfigServlet</servlet-name> <servlet-class>com.netease.server.example.web.controller.ServletConfigServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ServletConfigServlet</servlet-name> <url-pattern>/servlet/ServletConfig</url-pattern> </servlet-mapping>
从Servlet中获取对应的参数:
首先,拿到ServletConfig对象
然后配置对应的参数值即可
@Override public void init() throws ServletException { ServletConfig config = this.getServletConfig(); String v1 = config.getInitParameter(data1); System.out.println("v1 = " + v1); String v2 = config.getInitParameter(data2); System.out.println("v2 = " + v2); }
NB: DO NOT FORGET TO ADD A DOGET() METHOD TO HANDLE THE RESPONSE STUFF.
不同Servlet共享的配置信息:ServletContext
Servlet容器在启动的时候,会对每个web应用创建一个对应的ServletContext对象(全局只有一个)
在web.xml中进行ServletContext的配置:
<context-param> <param-name>globalData1</param-name> <param-value>123</param-value> </context-param> <!-- could be several pairs -->
<!-- no mapping to servlet -->
在任意Servlet中获取配置即可:
ServletContext ctx = this.getServletContext(); String globalValue1 = ctx.getInitParameter("globalData1"); String globalValue2 = ctx.getInitParameter("globalData2"); System.out.println("global value1: " + globalValue1 + "; global value2: " + globalValue2);
如果这些配置是事先不知道的呢?--ServletContext也支持动态信息-Attribute(Key-Value)
i.e. 通过ServletContext的动态属性,来完成信息的共享
在servlet a中:ctx.setAttribute("attribute" ,"111"); // set key-value pair
在servlet b中:String attribute1 = (String) ctx.getAttribute("attribute"); // get the value of attribute1
如果想要从外部资源里而不是web.xml中读取配置信息呢?
getResource();
getResourceAsStream();
getRealPath();
i.e. 使用ServletContext读取log4j的配置信息:
1. getResource();
URL url = ctx.getResource("/WEB-INF/classes/log4j.properties"); --import java.net;
--try catch: MalformedURLException
InputStream in = url.openStream(); --import java.io;
--try catch: IOException
写方法String getProperty(String, InputStream);
public static String getPropery(String key, InputStream in) { Properties props = new Properties(); try { props.load(in); } catch (IOException e) { e.printStackTrace(); } String value = props.getProperty(key); return value; }
调用该方法获取对应value
String propertyValue = GeneralUtil.getProperty("log4j.rootLogger", in);
即可。
完整代码:
try { URL url = ctx.getResource("/WEB-INF/classes/log4j.properties"); InputStream in = url.openStream(); String propertyValue = GeneralUtil.getPropery("log4j.rootLogger", in); System.out.println("property value: " + propertyValue); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
2. getResourceAsStream();
InputStream in = ctx.getResourceAsStream("/WEB-INF/classes/log4j.properties");
String p = GeneralUtil.getProperty("log4j.rootLogger", in);
3. getRealPath();
String path = ctx.getRealPath("/WEB-INF/classes/log4j.properties");
System.out.println("real path:" + path);
File file = new File(path);
InputStream in = new FileInputStream(file);
// try catch FileNotFoundException
String p = GeneralUtil.getProperty("log4j.rootLogger", in);
Servlet配置
Web应用程序的基本结构:
webapp:
1. 公共资源:通过url可以访问到的静态资源,如css, js, images, html,不同类型资源分类于不同目录
2. META-INF目录:定义了jar包的源信息,定义了包拓展属性、类加载路径、自定义属性等
3. WEB-INF目录:不提供给用户,无法通过浏览器访问。放置类文件和类所依赖的库。
包括两个子目录:classes和lib 和一个web.xml文件。classes放置编写的代码和编译后的类文件。lib放置web依赖的jar包。
web.xml部署描述服务,用于描述一个web应用。
web.xml文件:
用于设置web应用程序的组件部署信息。
Servlet容器需要支持部署描述符的所有元素。
web.xml文件中的配置:
1. Servlet声明
<servlet>
<servlet-name>servlet类的名称</servlet-name>
<servlet-class>servlet类的实际路径</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>和上面对应的servlet类名</servlet-name>
<url-pattern>servlet对外映射的相对路径(一般以/开头)</url-pattern>
<url-pattern>可有多个mapping路径 & "*"可做模糊匹配</url-pattern>
</servlet-mapping>
Servlet-Mapping匹配规则:
若两个servlet的url-pattern重复了,则
a. 精确路径匹配,完全匹配;
b. 最长路径匹配,最长前缀匹配(路径匹配最多的);
c. 扩展名匹配;
d. default servlet;
e. 如果没有default servlet,则放弃匹配返回报错。
规则从上往下按优先级顺序
i.e.
/hello --a--> Servlet1
/hello/world/index --b--> Servlet3
/admin.jsp --c--> Servlet4
/world --d--> Servlet5
/hello.jsp --c--> Servlet4
/hello/world/hello.jsp --b--> Servlet3
2. ServletConfig配置--位于Servlet元素
<servlet>
<init-param> 可有多个
<param-name> key </param-name>
<param-value> value </param-value>
</init-param>
<servlet-name>
...
3. ServletContext配置--全局
<context-param>
<param-name> key </param-name>
<param-value> value </param-value>
</context-param>
4. 若需要在Servlet容器启动时执行操作(init()为找Servlet第一次收到请求时的操作)
"load-on-startup"改变Servlet默认初始化时间--负数/无设置:第一次请求时加载
<servlet>
<load-on-startup>0</load-on-startup>
<servlet-name>...
...
5. 配置自定义错误页面 <error-page>
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
更高级的做法:添加exception-type元素捕获一个Java异常类型
6. 若用户在地址栏输入web app路径的url,如:http://localhost:8080/web_project_template/
会出现一个登录页面--index.html页面(首页/欢迎页面)
对应在部署描述中为<welcome-file-list>可包含一个或多个<welcome-file>子元素
指定多个欢迎页面的加载顺序:<welcome-file>在<welcome-file-list>中的顺序
7. 对于静态资源 打开文件 or 下载文件?
MIME(Multi-purpose Internet Mail Extensions)多用途互联网邮件扩展类型--发展为描述消息内容类型的互联网标准。
--设置某种扩展名文件的打开方式
<mime-mapping>定义扩展文件名映射类型
<extension>
<mime-type>
</mime-mapping>
下章预告:
Servlet session配置
Servlet filter配置
Servlet listener配置
Servlet单元测验
Servlet有什么特征?
- A.其它选项都是正确
- B.运行在Servlet容器里的程序
- C.Web服务
- D.一个Java类
Servlet容器是什么?
- A.装载和管理Servlet的服务端程序
- B.保存Servlet的程序
- C.Tomcat
- D.其它选项都不对
以下哪种关系跟Servlet与Servlet容器关系最接近?
- A.遥控器和电视机
- B.螺丝和螺丝刀
- C.子弹和枪
- D.CD与CD机
下面哪些内容不是部署描述符中的配置?
- A.<servlet>
- B.<list>
- C.<welcome-file-list>
- D.<error-page>
下面哪些是标准web应用程序包括的内容?
- A.web.xml
- B.公共资源
- C.WEB-INF
- D.其它选项都是
如果现在有以下7个servlet,且其url-pattern与对应的servlet的关系如下:
url-pattern |
servlet |
/abc |
Servlet1 |
/abcd |
Servlet2 |
/abc/* |
Servlet3 |
/abc/def |
Servlet4 |
/abc/def/* |
Servlet5 |
*.jsp |
Servlet6 |
/ |
Servlet7 |
如果现在请求的相对路径是/abc/def/gh,请求将发到哪个servlet?
- A.Servlet4
- B.Servlet3
- C.Servlet1
- D.Servlet5
如果现在请求的相对路径是/abcd.jsp,请求将会发到哪个Servlet?
- A.Servlet6
- B.Servlet7
- C.Servlet3
- D.Servlet2
如果现在请求的相对路径是/abc/def/gh.jsp,请求将会发到哪个Servlet?
- A.Servlet4
- B.Servlet3
- C.Servlet6
- D.Servlet5
下面的描述中哪项是ServletContext的作用?
- A.其它选项都是
- B.读取外部资源文件信息
- C.通过配置文件共享全局信息
- D.Servlet转发
下面哪项描述是正确的?
- A.http get方法,需要传输的数据是在http协议的header里,http post方法,需要传输的数据是在http协议的body里
- B.其它选项都是
- C.从设计目的上看,http get方法是获取数据,post方法是发送数据
- D.一般而言,http post方法比get方法更安全
Servlet生命周期包括哪些回调方法?
- A.init
- B.service
- C.destroy
- D.main
ServletContext读取外部配置文件的方法有哪些?
- A.getResource
- B.getPath
- C.getResourceAsStream
- D.getRealPath
部署描述符一定是xml文件,且命名必须是web.xml
- A.×
- B.√
Servlet-mapping中支持多个url-pattern对应同一个servlet
- A.×
- B.√
Servlet-mapping中的url-pattern支持模糊匹配
- A.√
- B.×
load-on-startup设置的值越小,servlet启动越晚
- A.×
- B.√
部署描述符中可以设置多个欢迎页面
- A.×
- B.√
ServletConfig对象只能在init方法中获取
- A.√
- B.×
ServletConfig只能配置一对init-param参数
- A.√
- B.×
Servlet init方法在其生命周期中只会被调用一次
- A.√
- B.×
Servlet destroy方法在请求结束后会被调用
- A.×
- B.√
ServletConfig中配置的参数可以在其它Servlet中获取
- A.√
- B.×