在讨论Servlet之前,我们先来看看JSP运行机制:服务器运行JSP时候,底层是将JSP编译成为java类文件来运行的,这种类文件就是Servlet。
那么我们可不可以直接编写Servlet类呢?
1. 当我们在JSP里面写大量Java代码时,可以把Java代码分离到Servlet中。
2. 当我们希望程序运行得快一些时,可以使用Servlet。
下面讨论一下Servlet的生命周期
当客户端发送请求(Request)到Servlet中,Servlet第一次运行的时候会调用init()方法,然后调用service()、doGet()或doPost()方法,如果此时有第二个客户端再来访问这个Servlet,将直接调用service()、doGet()或doPost()方法,这些方法是采用多线程的方法实现以便能够让多个用户同时访问,当服务器关闭的时候,Servlet消亡,会自动调用destroy()方法。
Servlet生命周期实例
编写Servlet1.java的Servlet类,实现测试Servlet的生命周期,代码如下
[java] view plaincopy
package wang.servlets;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Servlet1 extends HttpServlet {
public Servlet1() {
//构造函数
System.out.println(“构造函数”);
}
public void init() throws ServletException {
//初始化函数,重写,第一次访问Servlet时自动调用
System.out.println(“初始化函数”);
}
//这个函数在以get方式请求这个Servlet时运行
//以get方式请求这个Servlet:包括链接、get方式表单提交、直接访问这个servlet等
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(“doGet”);
}
//这个函数在以post方式请求这个Servlet时运行
//以get方式请求这个Servlet:包括post方式表单提交等
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(“doPost”);
this.doGet(request, response);//将post的内容转接给doGet方法
}
/*service函数既可以接收get请求,也可以接收post请求
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
super.service(req, resp);
}*/
public void destroy() {
//消亡函数,重写
System.out.println(“消亡函数”);
}
}
在web.xml中注册上面的Servlet类,配置代码如下
[xhtml] view plaincopy
<servlet>
<servlet-name>Servlet1</servlet-name>
<servlet-class>wang.servlets.Servlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet1</servlet-name>
<url-pattern>/servlet/Servlet1</url-pattern> <!– 定义了Servlet1的访问方式 –>
</servlet-mapping>
然后启动服务器,在浏览器地址栏中输入:http://localhost:8080/ServletPro/servlet/Servlet1,运行结果如下
构造函数
初始化函数
doGet
从结果可以看出Servlet的执行过程,如果再次刷新页面,相当于另外一个客户端来访问,此时只会得到doGet的输出结果,由此说明初始化函数是第一次运行Servlet的时候才会被调用,然后停止服务器,会得到消亡函数的输出结果,说明调用了消亡函数。
如何在Servlet中访问JSP的内置对象
request:就是参数中的request。
response:就是参数中的response。
session:利用request.getSession()方法。
application:利用getServletContext()方法。
编写Servlet2.java的Servlet类,实现在Servlet中访问JSP的内置对象(九九乘法表的实现),代码如下
[java] view plaincopy
package wang.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class Servlet2 extends HttpServlet {
public Servlet2() {
}
public void init() throws ServletException {
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//怎样在Servlet内访问Jsp内置对象
PrintWriter out = response.getWriter();//得到out
//得到request:就是参数中的request
//得到response:就是参数中的response
HttpSession session = request.getSession();//得到session
ServletContext application = this.getServletContext();//得到application
for(int i=1;i<=9;i++) {
for(int j=1;j<=i;j++) {
out.print(i + “*” + j + “=” + i*j);
}
out.println();
}
}
public void destroy() {
}
}
运行结果将得到一个九九乘法表。
Servlet应该负责的怎样的工作?
上面程序存在一个问题,就是显示九九乘法表是在Servlet中实现的,一般情况下Servlet是负责动作或控制逻辑,而对于显示功能就应该使用JSP。但是Servlet最好不要有当量的逻辑,这样会造成Servlet很庞大,并且功能很混杂,这个时候就应该使用JavaBean来实现部分的逻辑。
下面介绍几种Servlet的高级用法
1. 实现页面的跳转。
2. 读取web.xml中的初始化参数。
3. 实现过滤器。
在Servlet内实现页面跳转
编写含有表单的JSP页面,form.jsp,代码如下
[xhtml] view plaincopy
<%@ page language=”java” import=”java.util.*” pageEncoding=”gb2312″%>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>My JSP ‘form.jsp’ starting page</title>
</head>
<body>
<form action=”/PrjFu8/servlet/Servlet3″>
输入一个消息:<input name=”message”/><br/>
<input type=”submit” value=”提交”/>
</form>
</body>
</html>
编写结果页面result.jsp,代码如下
[xhtml] view plaincopy
<%@ page language=”java” import=”java.util.*” pageEncoding=”gb2312″%>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>My JSP ‘form.jsp’ starting page</title>
</head>
<body>
这是结果页面
</body>
</html>
编写负责跳转用的Servlet类,Servlet3.java,首先采用重定向的方法
[java] view plaincopy
package wang.servlets;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Servlet3 extends HttpServlet {
public Servlet3() {
}
public void init() throws ServletException {
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//跳转方法1.浏览器地址栏变成了目标页面的url
response.sendRedirect(“/PrjFu8/result.jsp”);
}
public void destroy() {
}
}
运行form.jsp页面,点击提交按钮,将跳转到result.jsp页面中,由于上面采用的重定向的方法,所以跳转之后,地址栏上的url地址也发生了变化。
使用重定向方法进行跳转时,存在一个问题,就是在结果页面得不到request中的内容,包括参数和属性。
在Servlet3.java中的doGet方法中添加代码,测试能否得到request的内容
[xhtml] view plaincopy
String str = request.getParameter(“message”);
System.out.println(str);
在result.jsp中添加如下代码
<%
String str = request.getParameter(“message”);
out.println(str);
%>
运行form.jsp页面,输入消息,点击提交按钮,将跳转到result.jsp页面中,这时在控制台上得到正确的结果,但是在result.jsp中得不到request中的内容,结果页面中request内容丢失。
下面使用forward跳转方法,这种方法不会丢失request中的内容,在doGet方法添加相应代码,doGet方法如下
[java] view plaincopy
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String str = request.getParameter(“message”);
System.out.println(str);
//跳转方法1.浏览器地址栏变成了目标页面的url
//到了目标页面之后,request内的参数值、属性值丢失了
response.sendRedirect(“/PrjFu8/result.jsp”);
//跳转方法2.浏览器地址栏没有变成目标页面的url
//相当于在服务器内部将目标页面的输出送给客户端,request参数值、属性值没有丢失
ServletContext application = this.getServletContext();
RequestDispatcher rd = application.getRequestDispatcher(“/result.jsp”);
rd.forward(request, response);
/**
* 使用经验:如果在A页面有一些内容要在B页面显示,但是如果内容数量较大,并且是暂态数据
* 可以将内容不要放在session内,放在request内,用跳转方法2跳转到B页面显示,节省内存
* 跳转方法1相当于重新在客户端输入目标页面地址,重新请求
* 方法2相当于服务器内部的跳转
* 如果要跳转到服务器以外的url,必须使用方法1,比如跳转到百度
*/
}
读取web.xml中的参数
在web.xml中可以定义两种形式的参数,全局参数和局部参数
读取全局参数
在web.xml中定义全局参数,代码如下
[xhtml] view plaincopy
<?xml version=”1.0″ encoding=”UTF-8″?>
<web-app version=”2.5″
xmlns=”http://java.sun.com/xml/ns/javaee”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd”>
<!– 定义全局参数,所有的Servlet都可以读取 –>
<context-param>
<param-name>GlobalParam</param-name>
<param-value>Welcome(Global)</param-value>
</context-param>
<servlet>
<servlet-name>Servlet3</servlet-name>
<servlet-class>wang.servlets.Servlet3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet3</servlet-name>
<url-pattern>/servlet/Servlet3</url-pattern>
</servlet-mapping>
</web-app>
在Servlet中读取全局参数的代码可以编写在doGet方法,doGet方法代码如下
[java] view plaincopy
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//读取全局参数
ServletContext application = this.getServletContext();
String value = application.getInitParameter(“GlobalParam”);
System.out.println(value);
}
读取局部参数
在web.xml中定义局部参数,代码如下
[xhtml] view plaincopy
<servlet>
<servlet-name>Servlet3</servlet-name>
<servlet-class>wang.servlets.Servlet3</servlet-class>
<!– 定义局部参数,只有这个Servlet才能识别 –>
<init-param>
<param-name>LocalParam</param-name>
<param-value>Welcome(Local)</param-value>
</init-param>
</servlet>
在doGet读取参数的代码如下
[java] view plaincopy
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//读取全局参数
ServletContext application = this.getServletContext();
String value = application.getInitParameter(“GlobalParam”);
System.out.println(value);
//读取局部参数
String value2 = this.getInitParameter(“LocalParam”);
System.out.println(value2);
}
实现过滤器
过滤器能使每次提交(广义,所有的get,post提交)的时候自动做一些事情。
如何编写过滤器?
1.编写一个类,实现javax.servlet.Filter。
2.编写doFilter:FilterChain.doFilter(request, response)。
3.在web.xml中注册这个过滤器。
<filter>
<filter-name>名称</filter-name>
<filter-class>类</filter-class>
</filter>
<filter-mapping>
<filter-name>名称</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
案例:利用过滤器解决中文问题
编写过滤器类,EncodingFilter.java,实现解决中文问题,代码如下
[java] view plaincopy
package wang.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class EncodingFilter implements Filter {
private String encoding;
public EncodingFilter() {
System.out.println(“过滤器构造方法”);
}
public void init(FilterConfig config) throws ServletException {
//初始化函数,服务器运行能自动运行一次
System.out.println(“过滤器初始化函数”);
config.getInitParameter(“xxx”); //得到局部参数
encoding = config.getServletContext().getInitParameter(“encoding”);//得到全局参数
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//每次提交都会运行
System.out.println(“过滤器doFilter函数”);
request.setCharacterEncoding(encoding);//解决中文问题
//过滤器是在提交到达处理之前运行,所以这里要将这个请求向后传递
chain.doFilter(request, response);
}
public void destroy() {
//消亡时运行
System.out.println(“过滤器消亡函数”);
}
}
在web.xml中注册这个过滤器,代码如下
[xhtml] view plaincopy
<!– 注册过滤器 –>
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>wang.filter.EncodingFilter</filter-class>
</filter>
<!– 配置过滤器要过滤的对象 –>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
过滤器比较重要的作用
1. 做初始化工作,不仅仅是为过滤器本身初始化,而且可以为整个系统工作初始化工作,并且初始化工作只运行一次。
2. 每次做提交的时候(广义上的提交),doFilter()方法都会被调用,这就给我们带来比较好的作用,比如我们登录某个系统,当我们退出该系统时,直接点击浏览器上面的后退按钮,如果不做处理,就会返回到之前登录状态,这显然是不安全的。我们可以通过检查session是不是为空来控制,session为空的话就提示用户重新登录,所以要清空页面的session,这时可以使用过滤器,把要检查session的页面共同用一个过滤器来做过滤,每次访问它们之前都来检查session。只要在访问的时候要做检查的工作,我们都可以用过滤器来实现。