目录
简介
使用JSP+JavaBean可以完成项目的开发,但是项目开发的设计结构不清晰,所以不合适项目的结构划分,以及程序功能的扩充,代码有大量的重复操作。就需要Servlet类来改进,需要具备以下功能:
-
此类可以接收所有的请求参数的操作(包括头信息、数据);
-
此类可以跳转到任意的页面上;
-
此类可以灵活的配置Java程序。
Servlet是使用Java实现CGI程序,Serlet是采用多线程的方式进行处理,所以处理的性能会个非常的高。
开发Servlet程序需要有Servelt开发的支持包,是在Tomcat的安装路径之中的servlet-api.jar,将开发包配置到CLASSPATH之中就能够开发servlet程序了。
Servlet程序编写
servlet程序编写与web.xml配置
如果要进行Servlet的程序编写,需要使用Servlet类去继承javax.servlet.http.HttpServlet父类(该类是一个抽象类)。这个类要想处理用户的操作请求需要覆doGet()方法,该方法的主要功能是处理get()请求(用户访问地址,发送地址信息)。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { }
这里关注的不是内置对象的名称,而是内置对象的类型。
范例: 定义一个Servlet
package cn.ren.myservlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter pw = resp.getWriter() ; // 取得客户端的输出数据流
pw.print("hello");
}
}
Servlet是一个Java程序,Eclipse所有的Java程序编译过后都是默认保存在WEB-INF的目录下,安全性非常的高,用户无法访问,用户要想访问,需要修改web.xml文件的配置追加映射操作处理。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
<display-name>ServletProject</display-name>
<servlet>
<servlet-name>hello-servlet</servlet-name>
<servlet-class>cn.ren.myservlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello-servlet</servlet-name>
<!-- 浏览器中的访问路径 -->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
同一个Servlet可以映射不同的名称,只需要配置多个“<servlet-maping>”元素即可。
范例:配置多个名称
Servlet定义与执行流程分析
servlet是一个纯粹的Java程序,通过web.xml进行部署,可以发现配置web.xml的时候使用的是完整的路径,而后web容器就能够加载配置,显然这是反射的应用。
Servlet与表单
编写一个表单将表单提交给Servlet进行显示
1、建立一个InputServlet程序类
package cn.ren.myservlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class InputServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
String paramValue = req.getParameter("info") ;
PrintWriter pw = resp.getWriter() ; // 取得客户端的输出数据流
pw.print("<h1>" + paramValue + "</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
这里面没有路径的概念,路径完全由web.xml配置的
2、修改web.xml文件,追加InputServlet路径
<servlet>
<servlet-name>InputServlet</servlet-name>
<servlet-class>cn.ren.myservlet.InputServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>InputServlet</servlet-name>
<url-pattern>/InputServlet</url-pattern>
</servlet-mapping>
3、在根路径上建立一个input.html页面,该页面定义一个表单;
<%@ page language="java" pageEncoding="UTF-8"%>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<%
request.setCharacterEncoding("UTF-8") ;
%>
<form action="InputServlet" method="post">
请输入信息: <input type="text" name="info">
<input type="submit" value="提交">
</form>
</body>
</html>
如果此时将input.jsp放在其它路径下,那么直接访问InputServlet是不可能的,因为此时的InputServelet被映射到了根路径下。那么最好的解决方案是把Servlet也映射到与页面相同的路径之中。
Servlet映射路径配置操作原则:必须要与处理的JSP保持在同一一个路径下,否在无法进行准确的定位。
Servlet生命周期
简介
servlet程序也会有与之相似的生命周期:
之前使用的doGet()、doPost()就属于服务的处理,这个服务可以调用多次。
Servlet生命周期的控制都在HttpServlet、GenericServlet类中提供,只需要按照指定的方法名称进行方法的覆写,即可以实现生命周期的控制。
基础生命周期
如果要想实现一个Servlet基础的生命周期只需要找到三类方法:
-
初始化: public void init() throws ServletException;
-
服务处理:主要是考虑GET与POST请求处理服务,覆写doGet()、doPost();
-
销毁:public void destory();(不用覆写)
范例:编写一个基础生命周期控制
package cn.ren.myservlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class LifeCycleBvase extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("*** 初始化 ***");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("*** 服务 ****" + request.getMethod() + "**");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
@SuppressWarnings("unused")
private void destory() {
System.out.println("销毁");
}
}
<servlet>
<servlet-name>LifeCycleBvase</servlet-name>
<servlet-class>cn.ren.myservlet.LifeCycleBvase</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LifeCycleBvase</servlet-name>
<url-pattern>/life</url-pattern>
</servlet-mapping>
可以发现如下特点:
-
默认情况下,容器启动的时候不会对servlet进行初始化,只有在访问的时候才会;
-
服务方法可以使用多次;
-
销毁操作一般出现在以下一种情况下:
|- 容器关闭自动销毁
|-当项目之中配置了自动加载新的程序类,这个时候也会发生销毁处理。
Servlet扩展生命周期
1、Servlet程序默认是在程序第一次执行的时候进行初始化,但是往往很多时候需要在启动容器的时候就进行初始化,那么就需要修改web.xml。
<servlet>
<servlet-name>LifeCycleExtend</servlet-name>
<servlet-class>cn.ren.myservlet.LifeCycleExtend</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
2、在GenericServlet里面发现有两个初始化方法:
- 接收初始化参数配置:public void init(ServletConfig config) throws ServletException ;
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("*** 初始化 ***初始化参数:" + config.getInitParameter("msg"));
}
<servlet>
<servlet-name>LifeCycleExtend</servlet-name>
<servlet-class>cn.ren.myservlet.LifeCycleExtend</servlet-class>
<init-param>
<param-name>msg</param-name>
<param-value>ww.baidu.com</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
可以发现有参的init()替代无参的init();
3、服务使用的doGet()和doPost()方法,但是在GenericServlet里面有一个服务方法:
- 服务:public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException ;
如果在程序中覆写了service方法,那么doGet()、doPost()就会失效,原因如下:
用户的所有的请求都会交给service()完成,service会根据用户请求的类型进行方法的分配,例如:GET请求找到doGet()方法,但是如果子类中没有覆写此方法,那么就会使用HttpServlet中的方法抛出异常,显然这是模板设计模式。如果service()被覆写了,那么子类覆写的doGet()、doPost()方法就会失效。
内置对象取得
如果要想开发基本程序,至少需要:request、response、application、session。
取得session内置对像
服务器区分不同sessio的本质依靠的是Cookie,因为所有的session id都会在服务器第一次响应之后发送给客户端浏览器的Cookie,在用户每一次发送请求的时候都会将Cookie的数据以头信息的方式发送给服务器,服务器依靠request接收这些信息,所以要想解决session的问题就必须依赖HttpServletRequest接口完成,使用该接口中的如下方法:
- 取得当前的Seesion:public HttpSeesion getSession();如果现在的Session存在则直接取得,如果不存在则创建一个新session。
范例:取得Session对象
package cn.ren.myservlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@SuppressWarnings("serial")
public class ObjectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession ses = request.getSession() ; // 取得当前的Session对像
System.out.println("*** Seesion ID = " + ses.getId());
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
<servlet>
<servlet-name>ObjectServlet</servlet-name>
<servlet-class>cn.ren.myservlet.ObjectServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ObjectServlet</servlet-name>
<url-pattern>/obj</url-pattern>
</servlet-mapping>
既然可以取得Session对象,那么就可以进行登录验证。
取得application内置对象
在开发之中往往使用getServletContext()方法来代替application对象,在GenericServlet类中提供有:public ServletContext getServletContext()。
范例:取得application内置对象
package cn.ren.myservlet;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class ObjectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext app = super.getServletContext() ;
System.out.println("PATH = " + app.getRealPath("/"));
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
拥有application对象之后就意味着可以进行文件操作了。
Servlet跳转
在web开发之中存在两类跳转:客户端跳转、服务器端跳转。下面观察从servlet跳转到JSP。
Servlet实现客户端跳转
在HttpServletResponse里面提供有跳转方法: sendRedirect()。
范例:在根目录中定义一个show.jsp页面,作为跳转后的页面
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<h1>REQUEST属性:<%= request.getAttribute("paramA") %></h1>
<h1>SESSION属性:<%= session.getAttribute("paramB") %></h1>
</body>
</html>
范例:定义一个ForwardServlet.java
package cn.ren.myservlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class ForwardServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("paramA", "valueA");
request.getSession().setAttribute("paramB", "valueB");
response.sendRedirect("show.jsp");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
发现地址栏发生了改变,所以属于客户端跳转,跳转之后session属性可以继续传递,而request由于请求路径的变化所以无法向后进行传递。
Servlet实现服务器端跳转
如果要想传递request属性就需要使用:<jsp:forward\>标签指令、pageContext.forward()。可是标签指令无法在Servlet中出现,同时pageContext只能jsp页面使用。
要想在Servlet中实现服务器端跳转需要使用“javax.servlet.RequestDispatcher”接口,在该接口中提供两个方法:
-
跳转:public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException ;
-
包含:public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException ;
由于RequestDispatcher()是一个接口,如果要操作它需要使用接口的实例化对象,这个实例化需要依靠ServletRequest完成,在ServletRequest里面定义有一个方法:public RequestDispatcher getRequestDispatcher(String path) ;
范例:实现服务器端跳转
request.getRequestDispatcher("/show.jsp").forward(request, response);
在实际开发之中,往往会在Servlet程序中取出数据,在jsp页面显示(一般只会显示一次),所以往往会选择request属性范围传递,因为它占用的内存空间更小,释放得更快。