Servelt简介
什么是Servlet
Servlet是sun公司提供的一门用于开发动态web资源的技术,Java Web应用程序中所有的请求–响应都是由Servlet来完成的。
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
Servlet是Java Web的核心程序,所有的网址最终都交给Servlet来处理。 Servlet处理的基本流程图:
⑴ 客户端(可能是Web浏览器)通过HTTP提出请求;
⑵ Web服务器接收到该请求后,将其发送给Servlet进行处理;
⑶ Servlet会将处理的后结果向WEB服务器返回应答;
⑷ Web服务器将从Servlet获得的应答发送给客户端。
Servlet入门程序
将Java普通类变为Servlet的三种方式:1)实现javax.servlet.Servlet接口;2)继承javax.servlet.GenericServlet类;3)继承javax.servlet.http.HttpServlet类。而一般情况下,只需要继承HttpServlet类,重写doGet()和doPost()方法即可。从方法名中可以看出doGet()方法是处理GET请求,doPost()方法是处理POST请求。
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>Hello World</title></head>");
out.println("<body>");
out.println("<h1>第一个Java Web项目</h1>");
out.println("</body>");
out.println("</html>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}
}
注意:一个Servlet程序编译完成后,实际上是无法立即访问的,因为所有的Servlet程序都是以*.class的形式存在的,所以还需要在WEB-INF\web.xml文件中进行Servlet程序的映射配置。
<servlet> // 定义Servlet
<servlet-name>helloServlet</servlet-name> // 进行Servlet映射
<servlet-class>com.luhui.HelloServlet</servlet-class> // 映射的Servlet类路径
</servlet>
<servlet-mapping> // servlet-mapping: 映射路径
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern> // url-pattern页面的映射路径
</servlet-mapping>
//servlet: servlet-name:
此时就可以访问了:http://127.0.0.1:8080/JavaWeb/hello
;JavaWeb是配置的项目名。
HttpServlet指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法。因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。
Servlet与普通Java类的区别
Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。
针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。
Servlet的URL路径
Servlet访问URL映射配置
由于客户端是通过URL地址来访问web服务器中的资源,因此Servlet程序要想被外界访问,就需要把Servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用元素和元素完成。
元素用于注册Servlet,它包含有两个主要的子元素:和,分别用于设置Servlet的注册名称和Servlet的完整类名。
一个元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:和,分别用于指定Servlet的注册名称和Servlet的对外访问路径。例如:
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.luhui.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
需要注意的是对于每一个Servlet实际上都可以配置多个名称,即多个元素的子元素的设置值可以是同一个Servlet的注册名,但是元素的子元素的值就不可以相同,否则启动就会报错。
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello.jsp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello.aspx</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello.php</url-pattern>
</servlet-mapping>
通过上面的配置,当我们想访问名称是HelloServlet的Servlet时,可以使用如下的几个地址去访问:HelloServlet被映射到了多个URL上:
http://localhost:8080/JavaWebt/hello
http://localhost:8080/JavaWeb/hello.jsp
http://localhost:8080/JavaWeb/hello.php
http://localhost:8080/JavaWeb/hello.aspx
1.2.2、Servlet访问URL使用通配符映射
在Servlet映射到的URL中也可以使用通配符,但是只能有两种固定的格式:
一种格式是"*.扩展名",
另一种格式是以正斜杠(/)开头并以"/*"结尾:" /* "。
<servlet-mapping>
<servlet-name>anyName</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>anyName</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
例如:
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
星号:* 可以匹配任意的字符,所以此时可以用任意的URL去访问HelloServlet这个Servlet。对于如下的一些映射关系:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do
那么
当请求URL为“/abc/a.html”,“/abc/”和“/”都匹配,哪个servlet响应: Servlet引擎将调用Servlet1。
当请求URL为“/abc”时,“/abc/”和“/abc”都匹配,哪个servlet响应: Servlet引擎将调用Servlet3。
当请求URL为“/abc/a.do”时,“/abc/”和“.do”都匹配,哪个servlet响应: Servlet引擎将调用Servlet1。
当请求URL为“/a.do”时,“/”和“.do”都匹配,哪个servlet响应:Servlet引擎将调用Servlet2。
当请求URL为“/xxx/yyy/a.do”时,“/”和“*.do”都匹配,哪个servlet响应:Servlet引擎将调用Servlet2。
匹配的原则就是"谁长得更像就找谁"
缺省Servlet
如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。凡是在web.xml文件中找不到匹配的元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求。
<servlet>
<servlet-name>ServletDemo</servlet-name>
<servlet-class>com.luhui.ServletDemo</servlet-class>
</servlet>
<!-- 将ServletDemo配置成缺省Servlet -->
<servlet-mapping>
<servlet-name>ServletDemo</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
当访问不存在的Servlet时,就使用配置的默认Servlet进行处理。
Servlet生命周期
Servlet生命周期
每个Servlet都有自己的生命周期,Servlet的生命周期由web服务器来维护。生命周期包括加载程序、初始化、服务、销毁、卸载5个部分:
- init方法
在一个Servlet的生命周期中,init方法只会被执行一次,之后无论用户执行多少次请求,都不会在调用该方法。关于init方法的执行时机,有两种方式可选,一般的是在服务器启动后第一个用户请求改Servlet是调用,你也可以设置该Servlet在服务器启动后自动执行。init方法负责简单的创建或者加载一些数据,这些数据将用于该Servlet的整个生命周期中。 - service方法
当一个客户请求改Servlet时,实际的处理工作全部有service方法来完成,service方法用来处理客户端的请求,并生成格式化数据返回给客户端。每一次请求服务器都会开启一个新的线程并执行一次service方法,service根据客户端的请求类型,调用doGet、doPost等方法。service是由web容器来调用的,我们无需对service具体内容做任何处理,service会自动的根据客户端的请求类型去调用doGet、doPost等方法,所以我们只需要做好doGet、doPost方法的实现就可以了。 - destroy方法
该方法在整个生命周期中,也是只会被调用一次,在Servlet对象被销毁是调用,在servlet中,我们可以做一些资源的释放等操作,执行destory方法之后的servlet对象,会等待jvm虚拟机的垃圾回收机制择时回收。 - doGet、doPost方法
实际的业务处理流程,service根据客户端的请求类型来自动匹配需要执行那个方法。
public class LifeServlet extends HttpServlet{
@Override
public void destroy() {
System.out.println(“销毁”);
}
@Override
public void init() throws ServletException {
System.out.println(“初始化”);
} }
取得初始化配置信息
可以通过ServletConfig的config对象来获取web.xml中配置的初始化参数。在GenericServlet类中有重载方法init(ServletConfig config)可以找到ServletConfig 接口。
在Servlet的配置文件web.xml中,可以使用一个或多个标签为servlet配置一些初始化参数。
<servlet>
<servlet-name>servletConfigServlet</servlet-name>
<servlet-class>com.luhui.ServletConfigServlet</servlet-class>
<!--配置ServletConfigServlet的初始化参数 -->
<init-param>
<param-name>username</param-name>
<param-value>luhui</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</init-param>
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>servletConfigServlet</servlet-name>
<url-pattern>/servletConfigServlet</url-pattern>
</servlet-mapping>
当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,我们通过ServletConfig对象就可以得到当前servlet的初始化参数信息。
public class ServletConfigServlet extends HttpServlet{
private String username;
private String password;
private String charset;
@Override
public void init(ServletConfig config) throws ServletException {
username = config.getInitParameter("username");
password = config.getInitParameter("password");
charset = config.getInitParameter("charset");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("初始化参数:"+username+"\t"+password+"\t"+charset);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
Servlet之间的跳转
Servlet之间可以相互跳转,从一个Servlet程序跳转到另一个Servlet。利用Servlet的跳转可以把一个项任务按模块分开。
客户端跳转(转向Forward)
转向(Forward)是通过RequestDispatcher对象的forward(HttpServletRequest request,HttpServletResponse response)方法来实现的。而RequestDispatcher对象可以通过HttpServletRequest的getRequestDispatcher(String path)方法获得。
public class ForwardServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path = request.getParameter("path");
System.out.println(path);
if("file".equals(path)){
request.getRequestDispatcher("/WEB-INF/web.xml").forward(request,response);
}else if("jsp".equals(path)){
request.getRequestDispatcher("/forword.jsp").forward(request,response);
}else if("servlet".equals(path)){
request.getRequestDispatcher("/lifeServlet").forward(request,response);
}else {
System.out.println("缺少参数");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
客户端跳转(重定向Redirect)
在Servlet中可以使用HttPServletResponse接口的sendRedirect()方法来进行重定向。
public class RedirectServlet extends HttpServlet{
private Map<String, Integer> map = new HashMap<String, Integer>();
@Override
public void init() throws ServletException {
map.put("/setup.exe",0);
map.put("/application.zip",0);
map.put("/01.mp3",0);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String fileName = req.getParameter("filename");
if(fileName != null){
int hit = map.get(fileName);
map.put(fileName,hit++);
resp.sendRedirect(req.getContextPath()+fileName);
}else{
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println("<html>");
out.println("<head><title>文件下载</title></head>");
out.println("<body>");
out.println("<fieldset align=center style=width:90%><legend>文件下载</legend");
out.println("<table width=100%>");
out.println("<tr>");
out.println("<td><b>文件名</b></td>");
out.println("<td><b>下载次数</b></td>");
out.println("<td></td>");
out.println("</tr><br>");
for(Map.Entry<String,Integer> entry : map.entrySet()){
out.println("<tr>");
out.println("<td>"+entry.getKey()+"</td>");
out.println("<td>"+entry.getValue()+"</td>");
out.println("<td><a href = '"+req.getRequestURI() +"?filename="+entry.getKey()+"'target='blank' onclick = 'location=location;'>下载</a> </td>");
out.println("</tr><br>");
}
out.println("</table>");
out.println("</body>");
out.println("</html>");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}