参考教程:菜鸟教程
目录
Servlet基础概念
Servlet
Java Servlet 是运行在 Web 服务器或应用服务器上的程序(注意,Servlet是这样一类程序的总称),它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
使用 Servlet可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
Java Servlet 是运行在带有支持 Java Servlet 规范的解释器的 web 服务器上的 Java 类。
Servlet 可以使用 javax.servlet 和 javax.servlet.http 包创建,它是 Java 企业版的标准组成部分,Java 企业版是支持大型开发项目的 Java 类库的扩展版本,这些类实现 Java Servlet 和 JSP 规范
Servlet 架构
Servlet 任务
- 读取客户端(浏览器)发送的显式的数据。这包括网页上的 HTML 表单,或者也可以是来自 applet 或自定义的 HTTP 客户端程序的表单。
- 读取客户端(浏览器)发送的隐式的 HTTP 请求数据。这包括 cookies、媒体类型和浏览器能理解的压缩格式等等。
- 处理数据并生成结果。这个过程可能需要访问数据库,执行 RMI 或 CORBA 调用,调用 Web 服务,或者直接计算得出对应的响应。
- 发送显式的数据(即文档)到客户端(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML 或 XML)、二进制文件(GIF 图像)、Excel 等。
- 发送隐式的 HTTP 响应到客户端(浏览器)。这包括告诉浏览器或其他客户端被返回的文档类型(例如 HTML),设置 cookies 和缓存参数,以及其他类似的任务。
Tomcat
Apache Tomcat 是一款实现 Java Servlet 和 JavaServer Pages 技术的开源软件,可以作为测试 Servlet 的独立服务器,而且可以集成到 Apache Web 服务器。
下载完Tomcat之后,进入目录
\bin\startup.bat 执行脚本启动服务器
\bin\shutdown.bat 关闭服务器
导入jar包
通常需要手动在项目中导入apache-tomcat\lib\servlet-api.jar以使用javax.servlet.*
如果使用Maven等工具创建项目,需要在pom.xml(配置文件)中手动添加依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
Servlet 生命周期
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。
- Servlet 通过调用 init () 方法进行初始化。
- Servlet 调用 service() 方法来处理客户端的请求。
- Servlet 通过调用 destroy() 方法终止(结束)。
- 最后,由 JVM 的垃圾回收器进行垃圾回收。
init() 方法被设计成只调用一次。它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。
service() 方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。
service() 方法由容器调用,service 方法在适当的时候调用 doGet、doPost、doPut、doDelete 等方法。所以不用对 service() 方法做任何动作,只需要根据来自客户端的请求类型来重写 doGet() 或 doPost() 即可。
destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用。destroy() 方法让的Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。在调用 destroy() 方法之后,servlet 对象被标记为垃圾回收。
在这里,Servlet是一个统称,表明是服务 HTTP 请求并实现 javax.servlet.Servlet 接口的 Java 类。Web 应用程序开发人员通常编写 Servlet 来扩展 javax.servlet.http.HttpServlet,并实现 Servlet 接口的抽象类专门用来处理 HTTP 请求。
Servlet部署/IDEA创建Web项目
默认情况下,Servlet 应用程序位于路径 <Tomcat-installation-directory>/webapps/ROOT 下,且类文件放在 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/classes 中。
并要对<Tomcat-installation-directory>/webapps/ROOT/WEB-INF/ 的 web.xml 文件进行相应的配置。这种修改很麻烦,通常创建Web项目更方便。
使用IDEA+maven创建Web项目,IDEA要使用企业版,否则无法开发web项目
贴一片教程:|---传送门---|,这教程虽然很啰嗦,但大体上差不多
JAVA Web项目结构图
├── pom.xml //maven的配置文件
└── src
├── main
│ ├── java //java代码的目录
│ │ └── mygroup
│ │ ├── controller
│ │ │ ├── HomeController.java
│ │ │ └── PersonController.java
│ │ ├── dao
│ │ │ └── PersonDao.java
│ │ └── model
│ │ └── Person.java
│ ├── resources //静态资源目录
│ │ ├── db.properties
│ │ ├── log4j.xml
│ │ └── META-INF
│ │ └── persistence.xml
│ └── webapp //web应用部署根目录
│ ├── index.html //因为是静态html文件,不用放到WEB-INF目录下
│ ├── META-INF
│ │ ├── context.xml
│ │ └── MANIFEST.MF
│ ├── resources //css,js等静态资源是不能放到WEB-INF目录下的,因为WEB-INF下的资源,客户端无法直接访问
│ │ └── css
│ │ └── screen.css
│ └── WEB-INF //jsp都会放到这里,以保证用户无法直接访问jsp,而是通过controller这个目录下的所有内容客户端都无法直接访问,所以不要放静态文件
│ ├── spring
│ │ ├── app
│ │ │ ├── controllers.xml
│ │ │ └── servlet-context.xml
│ │ ├── db.xml
│ │ └── root-context.xml
│ ├── views
│ │ ├── edit.jsp
│ │ ├── home.jsp
│ │ └── list.jsp
│ └── web.xml
└── test
├── java
│ └── mygroup
│ ├── controller
│ │ ├── DataInitializer.java
│ │ ├── HomeControllerTest.java
│ │ └── PersonControllerTest.java
│ └── dao
│ └── PersonDaoTest.java
└── resources
├── db.properties
├── log4j.xml
├── test-context.xml
└── test-db.xml
JSP与HTML与Servlet
简单的说jsp是Servlet的简化版,html的复杂版,一般来说,Servlet用于处理数据,用在控制层,jsp用在视图层。
参考:魔法门传送
Servlet项目包结构
涉及到 WEB-INF 子目录的 Web 应用程序结构是所有的 Java web 应用程序的标准,并由 Servlet API 规范指定。给定一个顶级目录名 myapp,目录结构如下所示:
/myapp /images /WEB-INF /classes /lib
WEB-INF 子目录中包含应用程序的部署描述符,名为 web.xml。所有的 HTML 文件都位于顶级目录 myapp 下。
WEB-INF/classes 目录包含了所有的 Servlet 类和其他类文件,类文件所在的目录结构与他们的包名称匹配。例如,如果有一个完全合格的类名称 com.myorg.MyServlet,那么这个 Servlet 类必须位于以下目录中:
/myapp/WEB-INF/classes/com/myorg/MyServlet.class
Servlet 实例
package study;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 扩展HttpServlet,位于apache-tomcat/lib/下的servlet-api.jar里
*/
public class HelloWorld extends HttpServlet {
private String message;
@Override
public void init() throws SecurityException{
message="Hello World";
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");//设置响应报文格式
PrintWriter out=response.getWriter();//获取写入流
out.println("<h1>"+message+"</h1>");//将浏览器作为终端进行标准打印
}
public void destory(){
}
}
然后需要在webapp/WEB_INF/下的web.xml进行相应配置
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<!--web的响应控制都要在web.xml中进行相关的配置-->
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--设置servlet,name代表ID,class为对应的类,mapping表示映射,由ID映射对应的所访问的url路径-->
<!--url-pattern-name-class-->
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>study.HelloWorld</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>
</web-app>
由以上过程我们可以看到,每一个页面对应一个Servlet,是一个对HttpServlet进行了相应扩展的class,也就是我们自己写的Servlet,在Servlet里定义一系列行为,最基本的包括init():初始化;service:重写doGet()/doPost()等由request设置对应的response;destory():断开连接后的工作,写好Servlet,要在web.xml下进行相应的配置,每一个class有对应的name,每一个name要对应相应的url-pattern,也就是访问页面时的url。
Servlet编写
Servlet与Get/Post
前端页面与服务器交互响应通过一些定义好的方法进行说明,如get/post等。
GET 方法向页面请求发送已编码的用户信息。页面和已编码的信息中间用 ? 字符分隔,如下所示:
http://www.test.com/hello?key1=value1&key2=value2
GET 方法是默认的从浏览器向 Web 服务器传递信息的方法,它会产生一个很长的字符串,出现在浏览器的地址栏中。
POST方法也是向服务器传输数据,但是是作为单独的消息发送,与Get功能一样
servlet通过doGet()方法处理get请求,doPost()方法处理post()请求
可以调用 request.getParameter() 方法来获取表单参数的值。
中间Servlet:
package study;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* 响应Get方法,request通过get传递参数值,由getParameter()方法获取表单参数值,并进行解析返回response
*/
@WebServlet("/HelloForm")
public class HelloForm extends HttpServlet {
private static final long serialVersionUID=1L;//序列化ID
private static Map<String,String> storge=new HashMap<>();
public HelloForm(){
super();
storge.put("1","LSL-test");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
response.setContentType("text/html;charset=UTF-8");
String id=request.getParameter("id");//由getParameter()方法获取表单参数值
PrintWriter out=response.getWriter();
out.println("" +
"<html>" +
" <head>" +
" <title>使用Get由id获取name</title>" +
" </head>" +
" <body>" +
" id="+id+
" name="+storge.get(id) +
" </body>" +
"</html>");
}
}
前端简单页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>lsl-使用Get由id获取name</title>
</head>
<body>
<!--form表示表单,action对应访问路径,method对应方法
input表示输入,name是提交的name,value为提交的输入内容,submit对应的value是显示的内容
点击提交时响应action,因为已经设置method为get,所以request自动将id=1发送过去-->
<form action="$项目名称(部署的war_exploded名称)/HelloForm" method="get">
id:<input type="text" name="id"/>
<input type="submit" value="提交">
</form>
</body>
</html>
使用注解@WebServlet("/XXX")与配置web.xml效果一样,但需注意访问路径
可以看到,get方法是默认的request的方法,通过get发送数据,可以直接通过url?key=value&..这样的形式,但前提是你必须知道相应的key到底是什么,也可以前端自己设置,进行表单提交,Servlet进行处理后返回对应的response。
读取表单数据
web的核心是访问资源与数据通信,访问资源包括浏览与下载,浏览html页面,下载相应资源。数据通信就是与服务器进行交互,给予服务器数据返回响应,后台如何读取数据呢,Servlet提供三个最基本方法。
在这之前,前端的表单的展现形式是标签<form>,标签包含多个字标签,<name>就是表单选项的name。
<form action="/java_servlet_jsp_war_exploded/ReadParams" method="post" target="_blank">
<input type="checkbox" name="maths" checked="checked" /> 数学
<input type="checkbox" name="physics" /> 物理
<input type="checkbox" name="chemistry" checked="checked" /> 化学
<input type="submit" value="选择学科" />
</form>
Servlet读取表单数据有三个方法
- getParameter():如果知道具体的name,传入即返回前端对应的value
- getParameterNames():返回一个枚举类型的迭代器Enumeration,通过迭代获取表单的所有name。
Enumeration paraNames=request.getParameterNames();//所有表单数据参数
while(paraNames.hasMoreElements()){
String paraName=(String)paraNames.nextElement();
}
- getParameterValues():如果参数出现一次以上,则调用该方法,并返回多个值,例如复选框。
String[] paramValues=request.getParameterValues(paraName);//由name获取数据值
即前端表单与后台相互连通对应。
Http获取请求头部信息
请求request的头部包含许多重要信息
对此,Servlet提供许多方法以访问头部信息
下面的方法可用在 Servlet 程序中读取 HTTP 头。这些方法通过 HttpServletRequest 对象可用。
Enumeration headerNames = request.getHeaderNames();//获取http请求头部信息name,返回一个枚举
while(headerNames.hasMoreElements()) {
String paramName = (String)headerNames.nextElement();//http头部所定义的参数name
String paramValue = request.getHeader(paramName);//由name获取头部信息value,即请求的当前状态
}
Http设置响应头部信息
Servlet也可以对返回的响应也可以进行设置
响应通常包括一个状态行、一些响应报头、一个空行和文档。
Servlet提供一系列setter()方法对其响应进行相应的设置。
response.setIntHeader("Refresh",1);//设置响应头部信息,5秒自动刷新
response.setContentType("text/html;charset=UTF-8");//设置响应头部
Http设置头部状态码
状态码就是http报文头部表示当前报文状态的信息
以下是可能从 Web 服务器返回的 HTTP 状态码和相关的信息列表:
一般只需要响应头部设置状态码,返回信息即可
这些方法通过 HttpServletResponse 对象可用。
response.sendError(777, "响应状态码,可自行设置");
过滤器Filter
过滤器用于在请求与响应之前进行拦截并处理,类似于面向切面的概念,请求不能一味的直接交给Servlet,先要进行拦截,拦截时对一系列信息进行检查或者包装等等操作,最终再返回,通过继承Filter接口实现。过滤器当然可以有多个,因此就形成了过滤链,用类 FilterChain表示,其只有一个doFilter()方法,表示将当前请求或响应交给下一个过滤器。
过滤器是一个实现了 javax.servlet.Filter 接口的 Java 类。javax.servlet.Filter 接口定义了三个方法:
Filter的生命周期存在于整个服务器的运行周期,当服务器启动时,创建Filter对象并调用init(),filter对象只会创建一次,init方法也只会执行一次,之后会根据web.xml或注解配置的url启动对应的过滤器,每有一次访问即调用一次doFilter()方法,最后服务器关闭时调用destory()。
package study;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
//Filter接口实现过滤,用于在请求与响应之前进行拦截并处理然后再交给Servlet
//Filter接口主要要实现三个方法
/**
* public void init(FilterConfig filterConfig)
* web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。
* public void doFilter (ServletRequest, ServletResponse, FilterChain)
* 该方法完成实际的过滤操作,当客户端请求方法与过滤器设置匹配的URL时,Servlet容器将先调用过滤器的doFilter方法。FilterChain用户访问后续过滤器。
* public void destroy()
* Servlet容器在销毁过滤器实例前调用该方法,在该方法中释放Servlet过滤器占用的资源。
*/
//使用web.xml与注解类似,<filter>标签初始化一个FilterConfig对象,传给init
@WebFilter("/*")
public class LogFilter implements Filter {
@Override
public void init(FilterConfig config){
System.out.println("过滤初始化-LogFilter-init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws ServletException,IOException{
System.out.println("开始过滤-LogFilter-doFilter");
chain.doFilter(request,response);//将请求与响应传入过滤链
}
@Override
public void destroy() {
System.out.println("过滤销毁-LogFilter-destroy");
}
}
同Servlet一样,Filter也要在web.xml下进行相应配置或使用注解。
<filter>
<filter-name>LogFilter</filter-name>
<filter-class>com.runoob.test.LogFilter</filter-class>
<init-param>
<param-name>filterName</param-name>
<param-value>filterValue</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>LogFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
/*代表所有url都过滤
其中<init-param>标签在init()中有用,init()传入的FilterConfig即代表过滤器初始实例,可以调用
getInitParameter("filterName");
方法获取配置的对应的filterValue
通过使用注解@WebFilter("/XXX")效果相同
当然可以使用多个过滤器,调用过滤链方法即可
异常处理
如果请求抛出异常或包含错误状态码(404等),想要返回对应的错误页面,也应该对应一个Servlet,即发生错误时跳转到对应的Servlet。同时在web.xml中设置标签<error-page>
<error-page>包含子标签<error-code>对应的错误状态码,<exception-type>抛出异常类型,<location>跳转到对应的Servlet
<error-page>
<error-code>404</error-code>
<location>/ErrorHandler</location>
</error-page>
<error-page>
<exception-type>java.io.IOException</exception-type >
<location>/ErrorHandler</location>
</error-page>
如果只有<location>则默认所有错误都进行跳转
package study;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ErrorHandler")
public class ErrorHandler extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
Throwable throwable=(Throwable)request.getAttribute("javax.servlet.error.exception");//获取异常属性
Integer statusCode=(Integer)request.getAttribute("javax.servlet.error.status_code");//获取状态码
String servletName=(String)request.getAttribute("javax.servlet.error.servlet_name");//获取servlet
servletName=servletName==null?"未知Servlet":servletName;
String requestUri=(String)request.getAttribute("javax.servlet.error.request_uri");//获取请求uri
requestUri=requestUri==null?"未知URI":requestUri;
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
String title = " Error/Exception 信息处理";
String docType = "<!DOCTYPE html>\n";
out.println(docType +
"<html>\n" +
"<head><title>" + title + "</title></head>\n" +
"<body bgcolor=\"#f0f0f0\">\n");
out.println("<h1>抛出错误时的显示页面</h1>");
if (throwable == null && statusCode == null){
out.println("<h2>错误信息丢失</h2>");
out.println("请返回 <a href=\"" +
response.encodeURL("http://localhost:8080/") +
"\">主页</a>。");
}else if (statusCode != null) {
out.println("错误代码 : " + statusCode);//如果错误状态码则显示状态码
}else{//如果抛出异常则显示异常信息
out.println("<h2>错误信息</h2>");
out.println("Servlet Name : " + servletName +
"</br></br>");
out.println("异常类型 : " +
throwable.getClass( ).getName( ) +
"</br></br>");
out.println("请求 URI: " + requestUri +
"<br><br>");
out.println("异常信息: " +
throwable.getMessage( ));
}
out.println("</body></html>");
}
// 处理 POST 方法请求的方法
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
其中通过getAttribute()获取request的相关属性信息,返回一个Object,,需要向下转型,如
Throwable throwable=(Throwable)request.getAttribute("javax.servlet.error.exception");//获取异常属性
Integer statusCode=(Integer)request.getAttribute("javax.servlet.error.status_code");//获取状态码
String servletName=(String)request.getAttribute("javax.servlet.error.servlet_name");//获取servlet
Cookie处理
如果第一次访问服务器,服务器读取表单数据,然后返回的响应中设置相应cookie,下一次发送服务器的cookie中就有相应的信息,主要目的就是方便用户。
第一次读取:Browser--->Server(读取Cookie,没有信息,response设置Cookie)--->response返回给Browse,Browse设置Cookie
已经有Cookie:Browser---->Server(读取Cookie获取信息)<->DataBase
Servlet中的Cookie处理主要使用类Cookie,代表一个Cookie信息
Servlet Cookie 处理需要对中文进行编码与解码,方法如下:
String str = java.net.URLEncoder.encode("中文","UTF-8"); //编码
String str = java.net.URLDecoder.decode("编码后的字符串","UTF-8"); // 解码
Cookie 通常设置在 HTTP 头信息中,,Set-Cookie 头包含了一个名称值对、一个 GMT 日期、一个路径和一个域。名称和值会被 URL 编码。Cookie以键值对的形式展现。
以下是在 Servlet 中操作 Cookie 时可使用的有用的方法列表。
通过 Servlet 设置 Cookie 包括三个步骤:
(1) 创建一个 Cookie 对象:调用带有 cookie 名称和 cookie 值的 Cookie 构造函数,cookie 名称和 cookie 值都是字符串。
Cookie cookie = new Cookie("key","value");
(2) 设置最大生存周期:使用 setMaxAge 方法来指定 cookie 能够保持有效的时间(以秒为单位)。下面将设置一个最长有效期为 24 小时的 cookie。
cookie.setMaxAge(60*60*24);
(3) 发送 Cookie 到 HTTP 响应头:使用 response.addCookie 来添加 HTTP 响应头中的 Cookie,如下所示:
response.addCookie(cookie);
读取Cookie也很简单:
Cookie[] cookies=request.getCookies();//获取cookies
删除Cookie:
调用Cookie的setMaxAge()方法将最大生存周期设置为0就相当于删除Cookie
示例:
package study;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.net.URLEncoder;
@WebServlet("/SetAndReadCookie")
public class SetAndReadCookie extends HttpServlet {
private static final long serialVersionUID=1L;
public SetAndReadCookie(){
super();
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException,IOException{
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");//都设置字符集
Cookie[] cookies=request.getCookies();//获取cookies
PrintWriter out=response.getWriter();
out.println("<!DOCTYPE html>\n" +
"<html>\n" +
"<head><title>" + "读取/设置Cookie" + "</title></head>\n" +
"<body bgcolor=\"#f0f0f0\">\n" );
//如果访问时没有cookie则设置cookie
if(cookies.length==3){
Cookie id=new Cookie("id",request.getParameter("id"));//由表单获取数据并设置cookie
Cookie name=new Cookie("name", URLEncoder.encode(request.getParameter("name"),"UTF-8"));
id.setMaxAge(24*60*60);//设置服务器的cookie的存在时间
name.setMaxAge(24*60*60);
response.addCookie(id);//在响应头部中增加cookie即可,服务器会保存,下一次自动发给服务器
response.addCookie(name);
out.print("第一次访问网站或者cookie过期,已重置cookie");
}else{
out.println("<h2>已获取到Cookie,Cookie 名称和值</h2>");
for(Cookie cookie:cookies){
out.print("名称:" + cookie.getName( ) + ",");//读取cookie
out.print("值:" + URLDecoder.decode(cookie.getValue(), "utf-8") +" <br/>");
}
}
out.println("</body></html>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
Session会话
session会话是一个抽象概念,session存储于服务器内存中,相当于服务器存储连接用户信息的临时容器,session可以用cookie,重写url
等方式实现,每一次连接会在服务器产生一个session,使用session-id进行标识,每刷新一次或者开一个子窗口浏览器会自动偷偷发送session-id给服务器
对于Servlet来说就是cookie中的JSESSIONID,通过识别JSESSIONID服务器知道仍然是这个链接所以直接可以抽取出信息并进行处理,如果不是这样,因为http是无状态
连接,所以不会保存访问信息,不能浏览网页的时候刷新一下就重新登录一次吧?因此就出现了session这个抽象概念,访问静态html页面没必要创建session,
访问动态页面,如用户登录时就需要,当然浏览器请求或者Servlet自动创建,用户并不知道,这还是为了提供更好的用户体验,可自行设置session的存在时间。
session的实现方式
1.cookie
一个 Web 服务器可以分配一个唯一的 session 会话 ID 作为每个 Web 客户端的 cookie,对于客户端的后续请求可以使用接收到的 cookie 来识别。
2.隐藏的表单字段
一个 Web 服务器可以发送一个隐藏的 HTML 表单字段,以及一个唯一的 session 会话 ID,如下所示:
<input type="hidden" name="sessionid" value="12345">
该条目意味着,当表单被提交时,指定的名称和值会被自动包含在 GET 或 POST 数据中。每次当 Web 浏览器发送回请求时,session_id 值可以用于保持不同的 Web 浏览器的跟踪。
这可能是一种保持 session 会话跟踪的有效方式,但是点击常规的超文本链接(<A HREF...>)不会导致表单提交,因此隐藏的表单字段也不支持常规的 session 会话跟踪。
3.URL 重写
您可以在每个 URL 末尾追加一些额外的数据来标识 session 会话,服务器会把该 session 会话标识符与已存储的有关 session 会话的数据相关联。
例如,http://w3cschool.cc/file.htm;sessionid=12345,session 会话标识符被附加为 sessionid=12345,标识符可被 Web 服务器访问以识别客户端。
Servlet中的session
Servlet 提供 HttpSession 接口,该接口提供了一种跨多个页面请求或访问网站时识别用户以及存储有关用户信息的方式。
Servlet 容器使用这个接口来创建一个 HTTP 客户端和 HTTP 服务器之间的 session 会话。会话持续一个指定的时间段,跨多个连接或页面请求。
通过调用 HttpServletRequest 的公共方法 getSession() 来获取 HttpSession 对象,如下所示:
HttpSession session = request.getSession();
你需要在向客户端发送任何文档内容之前调用 request.getSession()。下面总结了 HttpSession 对象中可用的几个重要的方法:
package study;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
@WebServlet("/SessionTrack")
public class SessionTrack extends HttpServlet {
private static final long serialVersionUID=1L;
public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException {
HttpSession session=request.getSession(true);//如果不存在session对象则创建
Date createTime=new Date(session.getCreationTime());//获取session的创建时间
Date lastAccessTime=new Date(session.getLastAccessedTime());//获取最后一次传递session的时间
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置简单日期格式
/*
session会话是一个抽象概念,session存储于服务器内存中,相当于服务器存储连接用户信息的临时容器,session可以用cookie,重写url
等方式实现,每一次连接会在服务器产生一个session,使用session-id进行标识,每刷新一次或者开一个子窗口浏览器会自动偷偷发送session-id给服务器
对于Servlet来说就是cookie中的JSESSIONID,通过识别JSESSIONID服务器知道仍然是这个链接所以直接可以抽取出信息并进行处理,如果不是这样,因为http是无状态
连接,所以不会保存访问信息,不能浏览网页的时候刷新一下就重新登录一次吧?因此就出现了session这个抽象概念,访问静态html页面没必要创建session,
访问动态页面,如用户登录时就需要,当然浏览器请求或者Servlet自动创建,用户并不知道,这还是为了提供更好的用户体验
*/
String visitCountKey ="visitCount";
int visitCount = 0;
String userKey ="userID";//session信息值存储于本地服务器
String user ="LSL";
//如果该session没有设置相应的key则直接在服务器设置需要的键值
if(session.getAttribute(visitCountKey) == null) {
session.setAttribute(visitCountKey, 0);
}
// 检查是是新的访问者(由session追踪)
if (session.isNew()){
session.setAttribute(userKey, user);
} else {
visitCount = (Integer)session.getAttribute(visitCountKey);
visitCount++;
}
session.setAttribute(visitCountKey, visitCount);//更新session
// 设置响应内容类型
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html>\n" +
"<html>\n" +
"<head><title>" + "Servlet Session 会话实例 "+ "</title></head>\n" +
"<body bgcolor=\"#f0f0f0\">\n" +
"<h1 align=\"center\">" + "Servlet Session 会话实例 " + "</h1>\n" +
"<h2 align=\"center\">Session 信息</h2>\n" +
"<table border=\"1\" align=\"center\">\n" +
"<tr bgcolor=\"#949494\">\n" +
" <th>Session 信息</th><th>值</th></tr>\n" +
"<tr>\n" +
" <td>id</td>\n" +
" <td>" + session.getId() + "</td></tr>\n" +
"<tr>\n" +
" <td>创建时间</td>\n" +
" <td>" + df.format(createTime) +
" </td></tr>\n" +
"<tr>\n" +
" <td>最后访问时间</td>\n" +
" <td>" + df.format(lastAccessTime) +
" </td></tr>\n" +
"<tr>\n" +
" <td>用户 ID</td>\n" +
" <td>" + user +
" </td></tr>\n" +
"<tr>\n" +
" <td>访问统计:</td>\n" +
" <td>" + visitCount + "</td></tr>\n" +
"</table>\n" +
"</body></html>");
}
}
这样服务器自动创建session实例并保存session信息,在规定时间内由session-id标识每一个session,如果是处于同一个session则通过getAttribute()等方法获取原来的信息,如果是第一次连接则创建session并通过setAttribute()等方法设置信息。
访问数据库
最原始的方式即通过JDBC访问数据库。
以mysql8.0.15为例
1.导入JDBC驱动
Class.forName(JDBC_DRIVER);//8.0.15中,JDBC_DRIVER="com.mysql.cj.jdbc.Driver";
2.连接数据库,返回连接对象
Connection connection= DriverManager.getConnection(DB_URL,USER,PASSWORD);
8.0.15中,DB_URL后要加上时区,否则抛出异常
DB_URL="jdbc:mysql://localhost:3306/lsl?serverTimezone=UTC";
3.进行实际连接,返回操作对象
Statement statement=connection.createStatement();
4.执行sql,返回结果集或者boolean值
ResultSet resultSet=statement.executeQuery(sql);
package study;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
@WebServlet("/DatabaseAccess")
public class DatabaseAccess extends HttpServlet {
private static final long serialVersionUID=1L;
static final String JDBC_DRIVER="com.mysql.cj.jdbc.Driver";
static final String DB_URL="jdbc:mysql://localhost:3306/lsl?serverTimezone=UTC";
static final String USER="root";
static final String PASSWORD="123456";
public DatabaseAccess(){
super();
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException{
Connection connection=null;
Statement statement=null;
String sql="select * from test";
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html>\n"+
"<html>\n" +
"<head><title>" +"Servlet数据库访问" + "</title></head>\n" +
"<body bgcolor=\"#f0f0f0\">\n" +
"<h1 align=\"center\">" + "Servlet数据库访问" + "</h1>\n");
try {
Class.forName(JDBC_DRIVER);
connection= DriverManager.getConnection(DB_URL,USER,PASSWORD);
statement=connection.createStatement();
ResultSet resultSet=statement.executeQuery(sql);
//迭代结果集
while (resultSet.next()){
out.println("ID: " + resultSet.getString("id"));
out.println(",姓名:" + resultSet.getString("name"));
out.println(", 性别: " + resultSet.getString("sex"));
out.println(", 年龄: " + resultSet.getString("age"));
out.println("<br />");
}
resultSet.close();//关闭
}catch (Exception e){
e.printStackTrace();
}finally {
try {
//迭代完之后记得关闭资源
statement.close();
connection.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
@Override
protected void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException{
doGet(request,response);
}
}
前端与数据库通过中间servlet交互,如果想要提取表单值并插入数据库中,可以这样操作:
/编写预处理 SQL 语句
String sql= "INSERT INTO websites1 VALUES(?,?,?,?,?)";
//实例化 PreparedStatement
ps = conn.prepareStatement(sql);
//传入参数,这里的参数来自于一个带有表单的jsp文件,很容易实现
ps.setString(1, request.getParameter("id"));
ps.setString(2, request.getParameter("name"));
ps.setString(3, request.getParameter("url"));
ps.setString(4, request.getParameter("alexa"));
ps.setString(5, request.getParameter("country"));
//执行数据库更新操作,不需要SQL语句
ps.executeUpdate();
sql = "SELECT id, name, url FROM websites1";
ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();//获取查询结果
网页重定向
当因为某种原因需要进行页面的自动跳转时,即页面重定向
servlet中页面重定向有两种方法
1.response调用sendRedirect()方法
2.设置头部状态码及指定跳转页面
String site = "http://www.baidu.com" ;
response.setStatus(response.SC_MOVED_TEMPORARILY);
response.setHeader("Location", site);
package study;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/PageRedirect")
public class PageRedirect extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException{
response.setContentType("text/html;charset=UTF-8");
response.sendRedirect("http://www.baidu.com");//调用sendRedirect()重定向
}
public void doPost(HttpServletRequest request,HttpServletResponse response)throws IOException{
doGet(request,response);
}
}
网页访问量计数器
单个网页只需要在初始化servlet的init()方法中初始化计数器为0即可(或者如果服务器重启则从数据库中读取数据),由servlet的生命周期可知,每次访问都会调用doGet()等方法,对应计数器加1即可。或者使用过滤器Filter即可。
总点击量可通过设置过滤器filter完成。
Servlet 自动刷新页面
调用方法设置头部自动刷新即可
public void setIntHeader(String header, int headerValue)
如:setIntHeader("Refresh",1)即设置页面每隔一秒自动刷新一次。