AJAX和servlet
AJAX的引入
同步交互
首先用户向HTTP服务器提交一个处理请求。接着服务器端接收到请求后,按照预先编写好的程序中的业务逻辑进行处理,比如和数据库服务器进行数据信息交换。最后,服务器对请求进行响应,将结果返回给客户端,返回一个HTML在浏览器中显示,通常会有CSS样式丰富页面的显示效果
异步交互
指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。在部分情况下,我们的项目开发中都会优先选择不需要等待的异步交互方式。将用户请求放入消息队列,并反馈给用户,系统迁移程序已经启动,你可以关闭浏览器了。然后程序再慢慢地去写入数据库去。这就是异步。异步不用等所有操作等做完,就响应用户请求。即先响应用户请求,然后慢慢去写数据库,用户体验较好
原生AJAX
进行异步数据交互,进行少量的数据交互.减少服务器和浏览器的压力.就需要跟服务器进行通信.JS作为一个脚本语言,是无法调用硬件(网卡).在早期时,浏览器的厂商就对JS暴露一套API.这套API就可以让JS与服务器进行通信.
核心方法
方法 描述 new XMLHttpRequest() 创建新的 XMLHttpRequest 对象 abort() 取消当前请求 getAllResponseHeaders() 返回头部信息 getResponseHeader() 返回特定的头部信息 open(method, url, async, user, psw) 规定请求 · method:请求类型 GET 或 POST · url:文件位置 · async:true(异步)或 false(同步) · user:可选的用户名称 · psw:可选的密码 send() 将请求发送到服务器,用于 GET 请求 send(string) 将请求发送到服务器,用于 POST 请求 setRequestHeader() 向要发送的报头添加标签/值对 核心属性
属性 描述 onreadystatechange 定义当 readyState 属性发生变化时被调用的函数(事件属性) readyState 保存 XMLHttpRequest 的状态。 · 0:请求未初始化 · 1:服务器连接已建立 · 2:请求已收到 · 3:正在处理请求 · 4:请求已完成且响应已就绪 responseText 以字符串返回响应数据 responseXML 以 XML 数据返回响应数据 status 返回请求的状态号 · 200: “OK” · 403: “Forbidden” · 404: “Not Found” 如需完整列表请访问 Http 消息参考手册 statusText 返回状态文本(比如 “OK” 或 “Not Found”)
AJAX之验证用户是否被占用
JS表单验证只能校验格式是否正确,但是无法验证用户名是否已经存在,这个就需要后台程序接受到数据后通过查询才能够完成的,那么这里就非常适用于使用异步方式校验,保证用于数据提交后,业务完成的成功率.提升用户体验感
前端代码
<html> <head> <title>$Title$</title> </head> <body> <form action="" method="post" > <p>用户名:<input type="text" name="username" id="username" onblur="checkUsername()"></p> <p>密码:<input type="text" name="password" id="password"></p> <p id="msg" style="color: red"></p> <button>注册</button> </form> <script> /* 判断用户是否存在 */ /** * 1. 用户输入用户名 * 2. 获取用户的用户名 当input 不再被输入时 失去焦点 onblur * 3. 发送给服务器 进行检查 * 1. 创建通信对象 * 2. 建立连接 * 3. 发送数据 * 4. 监听状态 * 5. 发送请求 * 6. 获取响应数据 */ // 1. 创建通信对象 var request = new XMLHttpRequest(); function checkUsername() { //获取用户的用户名 var username = document.getElementById("username").value; //2.建立连接 request.open("GET","/server?username="+username); request.onreadystatechange = stateChange; //发送请求 request.send(); } /** * 自定义一个函数 */ function stateChange() { console.log("我执行了") //200 请求成功 4 表示请求完成 if (request.status == 200 && request.readyState == 4){ var msg = request.responseText; console.log(msg); if (msg != 'ok'){ //将不正常的消息 显示在页面 document.getElementById("msg").innerText = msg; } } } </script> </body> </html>
后端代码
package com.abc.servlet; 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("/server") public class ServerServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("我执行了!"); // 获取用户名 String username = req.getParameter("username"); // 在ajax请求时 必须使用 响应对象 返回数据 resp.setCharacterEncoding("UTF-8"); resp.setContentType("text/html;charset=utf-8"); PrintWriter writer = resp.getWriter(); String msg = "ok"; if (username.equals("admin")){ msg = "用户名已经存在"; } writer.print(msg); writer.flush(); writer.close(); } }
AJAX的本质
ajax的本质是进行数据的交互.基于浏览器进行通信的请求,请求后返回的数据给程序自身,浏览器是不会解析和渲染这个数据的.需要程序自身处理数据.
原生Ajax技术存在以下问题:
浏览器的兼容性问题.特别是IE浏览器.
整个API相对操作繁琐.主要以下步骤:
- 创建请求对象
- 建立连接
- 监听状态
- 发送请求
- 处理请求的响应
AJAX数据格式处理
ajax只支持文本数据,不支持字节这样的数据的.如果响应的数据是简单的字符串,可以简单的处理,但是如果是复杂的数据.例如:将学生列表数据返回.所以在ajax中,一般返回数据不使用普通的文本,早期时使用XML格式.但是XML耗内存,XML为了保证可读性,保留特殊字符.例如:空格 换行.在传输时是会占用流量.基于这样的情况,后期使用新的字符串格式:JSON : Javascript Object Notation.JS对象简谱.是指使用JS对象的格式组织字符串.
JS对象中只有两种格式: { } 和 [ ]
- 使用{ } 代表对象
- 使用[ ] 代表数组
- 一般使用JSON格式返回ajax请求的数据格式.
AJAX结合jquery实现
jQuery的ajax
每次书写AJAX代码比较繁琐 步骤都是一样的,数据回显使用原生js代码也比较繁琐,可以使用jQuery对上述问题进行优化,jQuery不仅仅对dom操作进行了封装 同时也对AJAX提交和回显已经进行了封装,可大大简化AJAX的操作步骤
jQuery的ajax使用
- 在jquery中,jquery主要提供了三个方法进行ajax的请求:
- $.ajax(url,option) : ajax方式是jquery 对原生的ajax进行了一个封装,简化了ajax开发,同时也是其他ajax请求方法的基本的方法
- $.get(url,data,callbackfunction) : 这个方法基于ajax方法的,但是只支持get请求
- $.post(url,data,callbackfunction) : 这个方法也是基于ajax方法的,但是支持post请求.
- 在jquery中,jquery主要提供了三个方法进行ajax的请求:
jQuery.ajax()属性详解
URL:
- 要求为String类型的参数,(默认为当前页地址)发送请求的地址。
type:
- 要求为String类型的参数,请求方式(post或get)默认为get。注意其他http请求方法,例如put和delete也可以使用,但仅部分浏览器支持。
timeout:
- 要求为Number类型的参数,设置请求超时时间(毫秒)。此设置将覆盖$.ajaxSetup()方法的全局设置。
async:
- 是否异步
- 要求为Boolean类型的参数,默认设置为true,所有请求均为异步请求。如果需要发送同步请求,请将此选项设置为false。注意,同步请求将锁住浏览器,用户其他操作必须等待请求完成才可以执行。
data:
要求为Object或String类型的参数,发送到服务器的数据。如果已经不是字符串,将自动转换为字符串格式。get请求中将附加在url后。防止这种自动转换,可以查看 processData选项。对象必须为key/value格式,
例如{foo1:"bar1",foo2:"bar2"}转换为&foo1=bar1&foo2=bar2。如果是数组,JQuery将自动为不同值对应同一个名称。 例如{foo:["bar1","bar2"]}转换为&foo=bar1&foo=bar2。
dataType:
期望服务器返回的数据的格式,实际格式以服务器返回的数据格式为准.不同浏览器对该属性处理方式是不一样的.如:chrom会按照该类进行校验.如果不一致,没反应.Firefox浏览器,则会抛出转化失败的异常.
要求为String类型的参数,预期服务器返回的数据类型。如果不指定,JQuery将自动根据http包mime信息返回responseXML或responseText,并作为回调函数参数传递。
可用的类型如下:
xml:返回XML文档,可用JQuery处理。
html:返回纯文本HTML信息;包含的script标签会在插入DOM时执行。
script:返回纯文本JavaScript代码。不会自动缓存结果。除非设置了cache参数。注意在远程请求时(不在同一个域下),所有post请求都将转为get请求。
json:返回JSON数据。
jsonp:JSONP格式。使用JSONP形式调用函数时,例如myurl?callback=?,JQuery将自动替换后一个“?”为正确的函数名,以执行回调函数。
text:返回纯文本字符串。
beforeSend:
要求为Function类型的参数,发送请求前可以修改XMLHttpRequest对象的函数,例如添加自定义HTTP头。在beforeSend中如果返回false可以取消本次ajax请求。
XMLHttpRequest对象是惟一的参数。 function(XMLHttpRequest){ this; //调用本次ajax请求时传递的options参数 }
complete:
要求为Function类型的参数,请求完成后调用的回调函数(请求成功或失败时均调用)。参数:XMLHttpRequest对象和一个描述成功请求类型的字符串。
function(XMLHttpRequest, textStatus){ this; //调用本次ajax请求时传递的options参数 }
success:
要求为Function类型的参数,请求成功后调用的回调函数,有两个参数。
由服务器返回,并根据dataType参数进行处理后的数据。
描述状态的字符串。
function(data, textStatus){ //data可能是xmlDoc、jsonObj、html、text等等 this; //调用本次ajax请求时传递的options参数 }
error:
要求为Function类型的参数,请求失败时被调用的函数。该函数有3个参数,即XMLHttpRequest对象、错误信息、捕获的错误对象(可选)。ajax事件函数如下:
function(XMLHttpRequest, textStatus, errorThrown){ //通常情况下textStatus和errorThrown只有其中一个包含信息 this; //调用本次ajax请求时传递的options参数 }
contentType:
- 要求为String类型的参数,当发送信息至服务器时,内容编码类型默认为”application/x-www-form-urlencoded”。该默认值适合大多数应用场合。
实例:
前端代码
<html>
<head>
<title>Title</title>
</head>
<body>
<p id="loading" style="display: none">加载中</p>
<button id="ajaxBtn">ajax</button>
<script src="js/jquery.js"></script>
<script>
$("#ajaxBtn").click(function () {
let m = 10;
let option = {
type:"post",//请求的方法
data:{"username":"张三","password":"123"},//请求参数
//contentType:"application/x-www-form-urlencoded",//表单的 url 编码的方式提交数据 key=value&key2=value
//contentType:"application/json",//使用JSON格式提交数据
//timeout: //请求超时时间 浏览器 默认是不会主动的停止请求 请求没有完成 一般会等待.
//timeout:3000,//3秒 请求最多3秒 如果超过3秒就主动断开
async:false,//是否异步的参数 默认是 true 一般如果 请求的数据会参与后面的运算 ,使用同步
//同步 当整个ajax 都执行完 才能执行之后的代码
beforeSend:function(){//发送请求前调用的函数
console.log("beforeSend");
//展示
$("#loading").show();
},
dataType:"json",//期望服务器返回的数据
complete:function(rs){//请求完成调用 不论成功或者失败 都会调用
console.log("xxxxcomplete")
$("#loading").hide();
},
success:function (rs) {//只有当请求完成 且数据正常时才会调用
console.log("success=============");
console.log(rs);// rs 返回的数据
m = m + 100;
},
error:function (req,msg,ex) { //当服务器发生异常时触发或者网络请求失败出现问题时触发
console.log(req);// 请求对象
console.log(msg);//错误信息
console.log(ex);//异常对象
}
};
$.ajax(“/server”,option);
console.log("m:"+m);
});
</script>
</body>
</html>
后端代码
package com.abc.servlet;
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("/server")
public class ServerServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("我执行了!");
//int m = 0;
//System.out.println(10/m);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取用户名
String username = req.getParameter("username");
// 在ajax请求时 必须使用 响应对象 返回数据
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
String msg = "ok";
if (username.equals("admin")){
msg = "username is exist";
}
msg = "{\"msg\":\"username is exist\"}"; //JSON格式的字符串
writer.print(msg);
writer.flush();
writer.close();
}
}
jQuery实现AJAX的其他写法
$load()
jQuery load() 方法是简单但强大的 AJAX 方法,load() 方法从服务器加载数据,并把返回的数据放入被选元素中。默认使用 GET 方式 - 传递附加参数时自动转换为 POST 方式,
语法:
$(selector).load(URL,data,callback);
参数含义:
url: URL地址 data:待发送参数。 callback:载入成功时回调函数。
测试:
前端代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="js/jquery-1.11.1-min.js"></script> </head> <body> <button id="btn" onclick="sendAjax()">点我发送ajax请求</button> <script> function sendAjax() { /* load(url,[data],[callback]) * url 是跳转的地址,必填 * data: 跳转到指定地址,并且携带参数,参数格式采用json格式 * callback : 回调函数(发送请求结束后,等待响应的结果,在回调函数中,可以获取到响应结果) * 这个参数是 函数形参数 成功时回调函数 * */ $("button").load("/server",{"username":"admin"},function (data) { console.log(data); }) } </script> </body> </html>
后端代码
@WebServlet("/server") public class ServerServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("我执行了!"); //int m = 0; //System.out.println(10/m); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } // 获取用户名 String username = req.getParameter("username"); // 在ajax请求时 必须使用 响应对象 返回数据 resp.setCharacterEncoding("UTF-8"); PrintWriter writer = resp.getWriter(); String msg = "ok"; if (username.equals("admin")){ msg = "username is exist"; } msg = "{\"msg\":\"username is exist\"}"; //JSON格式的字符串 writer.print(msg); writer.flush(); writer.close(); } }
$.get()
这是一个简单的 GET 请求功能以取代复杂 .$.ajax 。请求成功时可调用回调函数。如果需要在出错时执行函数,请使用 $.ajax。只支持异步的请求.都是使用$.ajax的默认属性值.$.get就是type为get
语法为:
$.get(url,[data],[callback],[type])
参数含义:
url: URL地址 data:待发送参数。 callback:载入成功时回调函数。 type:返回内容格式,xml, html, script, json, text, _default 等价于 $.ajax({ url: url, data: data, success: success, dataType: dataType });
测试:
<html> <head> <title>Title</title> </head> <body> <div id="content"></div> <button id="getBtn">get</button> <script src="js/jquery.js"></script> <script> $("#getBtn").click(function () { /** * 使用get方法 进行ajax请求 */ let param = {'username':'张三','age':123,sex:"男"} $.get("/server",param,function (rs) { //function 请求成功时调用的函数 rs 就是返回的数据 console.log(rs); }) }); </script> </body> </html>
$.post()
这是一个简单的 POST 请求功能以取代复杂 $.ajax 。请求成功时可调用回调函数。如果需要在出错时执行函数,请使用 $.ajax。
语法为:
$.post(url,[data],[callback],[type])
参数含义:
url: URL地址 data:待发送参数。 callback:载入成功时回调函数。 type:返回内容格式,xml, html, script, json, text, _default 等价于 $.ajax({ type: 'POST', url: url, data: data, success: success, dataType: dataType });
测试:
<html> <head> <title>Title</title> </head> <body> <div id="content"></div> <button id="postBtn">post</button> <%-- 引入jquery --%> <script src="resources/jquery.js"></script> <script> $("#postBtn").click(function () { /** * 使用post方法 进行ajax请求 */ let param = {'username':'张三','age':123,sex:"男"} $.post("server",param,function (rs) { //function 请求成功时调用的函数 rs 就是返回的数据 console.log(rs); }) }); </script> </body> </html>
$.getJSON()
JSON是一种较为理想的数据传输格式,它能够很好的融合与JavaScript或其他宿主语言,并且可以被JS直接使用。使用JSON相比传统的通过 GET、POST直接发送“裸体”数据,在结构上更为合理,也更为安全。至于jQuery的getJSON()函数,只是设置了JSON参数的 ajax()函数的一个简化版本。就是将预期的返回的数据类型设置为:JSON.
语法为:
$.getJSON( url, //请求URL [data], //传参,可选参数 [callback] //回调函数,可选参数 ); 等价于 $.ajax({ url: url, data: data, success: callback, dataType: json });
仅仅是等效于上述函数,但是除此之外这个函数也是可以跨域使用的,相比get()、post()有一定优势。另外这个函数可以通过把请求url写 成”myurl?callback=X”这种格式,让程序执行回调函数X。
前端代码
<html> <head> <title>Title</title> </head> <body> <div id="content"></div> <button id="jsonBtn">getJson</button> <%-- 引入jquery --%> <script src="js/jquery.js"></script> <script> $("#jsonBtn").click(function () { /** * 使用post方法 进行ajax请求 */ let param = {'username':'张三','age':123,sex:"男"} $.getJSON("/server",param,function (rs) { //function 请求成功时调用的函数 rs 就是返回的数据 console.log(rs); }) }); </script> </body> </html>
跨域
- 由于ajax进行网络通信,js基于浏览器进行网络通信.浏览器出于安全的考虑,浏览器存在一个同源策略。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
servlet
servlet的作用
获取客户的请求信息
Servlet获取客户端的请求信息,基于ServletRequest对象,ServletRequest的子接口: HttpServletRequest。
HttpServletRequest这个接口包含:客户端的请求参数,客户端的请求头信息。
向客户端做出响应
Servlet向客户端做出响应,基于ServletResponse对象,ServletResponse的子接口:HttpServletResponse。
HttpServletResponse这个接口:获取输出流(字节流,字符流),向客户端返回响应头信息。
servlet的使用方法
创建servlet的三个步骤
步骤 描述 导入servlet-api.jar 在tomcat的lib文件夹下也有此jar包,开发过程中为防止报错需要导入,实际运行时需要移除此jar包避免版本冲突。 自定义类继承HttpServlet 继承后,重写service方法。 配置虚拟路径 1)可以通过注解配置 2)通过xml形式配置 使用注解形式
JavaWeb开发中,Servlet开发配置相对繁琐。在web3.0之后,提供一些简化web配置的数据,@WebServlet就可以取代在web.xml中对servlet的配置
@WebServlet("/hello") public class HelloServlet extends HttpServlet { /** * 该类中的service方法 被调用了,说明什么? * * 这个类 它叫servlet 运行在tomcat服务器内部的小程序 * 证明小程序 运行了 * * service 方法被调用,service 方法 是普通方法,应当由对象调用 * 因此,Servlet 对象被创建了。 * * who 创建了Servlet 对象 ? * * Tomcat 服务器 创建的 * * 什么时候创建的? 创建了几个? * * 只有当第一次通过浏览器访问该Servlet的时候,Servlet对象被创建 * * 一个 单例模式 * * 它怎么创建的 ? * * 通过反射创建的 ,由于通过反射创建对象由两种方式: * 方式一: 通过字节码对象调用newInstance方法 (Class.forName(类的全限定名).newInstance() 前提是类中必须由无参构造) * 方式二:通过构造器对象调用newInstance方法 (Class.forName(类的全限定名).getConstructor(参数.class类型).newInstance(参数值)) * * Servlet 是通过方式一 创建的对象 * * 理由: 第一,在web.xml中告知了servlet的全限定名,所以通过反射 能搞出字节码对象 * 第二:Servlet 类中,必须提供一个公共的无参构造器,说明采用的方式是第一种 * * * Servlet 的请求流程: * * 1. 当Tomcat服务器启动的时候,去加载web.xml文件 * 2. 读取web.xml ,就等同于读取了 servlet 的配置 * 3. 把配置信息获取到之后,存入了map 中,以 servlet 的 映射路径为key , 以 servlet 的全限定名为value * 4. 当用通过浏览器发出对servlet 的请求时,就立刻去map中匹配映射路径的内容如果,匹配成功,则意味着,获取到了servlet 的全限定名 * 5. 通过反射创建servlet 的实例。调用init方法,接着调用service * */ public HelloServlet(){ System.out.println("servlet create "); } // 当请求方式是get 的时候,进入doGet方法处理 @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println(“come in”); } }
使用xml形式
向服务器注册这个Servlet,servlet是要提供服务的
- 声明/定义servlet类
- servlet提供服务访问方式
public HelloServlet(){ System.out.println("servlet create "); } // 当请求方式是get 的时候,进入doGet方法处理 @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 生成动态的web 内容 给浏览器客户端 resp.setContentType("text/html;charset=utf8"); PrintWriter writer = resp.getWriter(); writer.println("
这是后台生成的动态web 内容 "+new Date() +" "); } }xml配置
<servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.bjpowernode.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
使用注解形式
平时开发中能用注解形式则用注解形式。但如果类是第3方的,由于我们不能去修改第3方的源代码,无法在类上添加注解,则使用xml形式最为合适。例如:后期SpringMVC框架中的核心类DispatcherServlet就是使用的xml形式作为配置
@WebServlet("/anno") public class HelloServlet extends HttpServlet { public HelloServlet(){ System.out.println("servlet create "); } // 当请求方式是get 的时候,进入doGet方法处理 @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 生成动态的web 内容 给浏览器客户端 // 告知浏览器 ,响应的数据是什么格式 ,用什么编码打开 // 告知浏览器,发送给浏览器的内容的格式以及使用指定的编码进行解析 // 这句代码必须要在resp 对象使用之前设置 resp.setContentType("text/html;charset=utf8"); PrintWriter writer = resp.getWriter(); writer.println("
这是后台生成的动态web 内容 "+new Date() +" "); } }
servlet的生命周期
Servlet的生命周期,是指Servlet的实例的创建到到销毁的过程。
// loadOnStartup 它的配置 是为了改变 servlet单例对象的创建时机 (tomcat服务器启动的时候 ,创建servlet对象) @WebServlet(value = "/life",loadOnStartup = 1) public class LifeCyleServlet extends HttpServlet { // 我们需要弄清楚 这些生命周期方法何时被调用, 才能确定我们可以利用这些方法,做什么事情 /** * 该servlet 是单例模式 * 只有当第一次通过浏览器访问的时候,才被创建出来 * 对象被创建出来以后,紧接着会调用init方法,并且init方法只会调用一次 * * 然后调用service方法,而service方法的执行时机是每次发送请求给该servlet * 即,service方法, 是用来处理浏览器的每次请求 * * * 当服务器正常关闭的时候 ,会调用 destroy方法,非正常关闭服务,则不执行该destroy方法 * * * 结论: * * init方法只执行一次,我们可以用来初始化一些数据 * * 根据destory 的特点, 不建议把收尾的工作交给该方法处理 * * */ public LifeCyleServlet(){ System.out.println("create 方法"); } @Override public void init(ServletConfig config) throws ServletException { System.out.println("init 方法"); // 假如 在init 方法中做了一个耗时的操作 耗时 5分钟 } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("service 方法"); } @Override public void destroy() { System.out.println("destroy 方法"); } }
结论:
- init 方法是初始化servlet的方法,默认第一次访问时初始化,且只会执行一次
- service 方法是每次访问都会调用,会执行多次
- destory 方法,是当服务器正常关闭时调用。执行一次,销毁servlet对象时执行.
- init –> service –> destory
注意:
Servlet是单例的模式.一个Servlet只有一个当前类对象.
默认init只会在第一次访问时,进行调用。如果希望服务器启动时,就希望调用init方法。
<servlet> <servlet-name>servle01</servlet-name> <servlet-class>com.powernode.servlet.LifeCyleServlet </servlet-class> <!-- 只要大于0 都会在加载完成后调用 --> <load-on-startup>1</load-on-startup> </servlet>
如果配置了:1则当应用服务器启动时就会调用init方法。
也可以在注解中使用:@WebServlet(urlPatterns = “/servlet”,loadOnStartup = 1)
servlet的初始化参数
在使用Servlet时,可以配置Servlet的一些初始化参数。为程序提供一些默认的配置。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>servle01</servlet-name> <servlet-class>com.powernode.servlet.ServletLife</servlet-class> <!-- servlet 默认的配置参数 是 map结构 --> <!--配置初始化参数: 什么是初始化参数? 当对象被创建之后,就可以获取到这些参数--> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> <!-- 以上就是在配置servlet的配置参数 --> <!-- 只要大于0 都会在加载完成后调用 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>servle01</servlet-name> <url-pattern>/servlet01</url-pattern> </servlet-mapping> </web-app>
取值
public class InitParamServlet extends HttpServlet { public InitParamServlet(){ System.out.println("create 方法"); } // ServletConfig 这个类的对象 谁创建的 ? 什么时候创建的? // tomcat 服务器创建的 在调用init 方法之前创建的 private ServletConfig config ; /** * * @param config * @throws ServletException */ @Override public void init(ServletConfig config) throws ServletException { System.out.println("init 方法"); // 假如 在init 方法中做了一个耗时的操作 耗时 5分钟 String encoding = config.getInitParameter("encoding"); System.out.println("encoding = " + encoding); this.config = config; } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("service 方法"); // servlet 对象 this String encoding = this.getInitParameter("encoding"); System.out.println("encoding = " + encoding); } @Override public void destroy() { System.out.println("destroy 方法"); } }
servlet的线程安全问题
- Servlet的init方法只会执行一次,Servlet是单例的。非线程安全的,当出现并发访问,共享数据无法保证一致性.在使用Servlet不要定义共享成员变量.
- 根据上图,Servlet实例是单例,多线程,servlet是非线程安全的。
servlet相关API
请求信息相关API
在Servlet中,应用服务器将所有的请求信息封装为了HttpServletRequest对象,可以根据HttpServletRequest对象获取请求相关信息.
请求参数
前端
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/request" method="get"> 账号: <input type="text" name="username"/><br/> 密码:<input type="password" name="password"/> <br/> 爱好:<input type="checkbox" name="hobby" value="java">java <input type="checkbox" name="hobby" value="c">c <input type="checkbox" name="hobby" value="c#">c# <input type="checkbox" name="hobby" value="android">android <br/> <input type="submit" value="登录"/> </form> </body> </html>
后台
package com.abc.servlet; 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; @WebServlet("/request") public class RequestServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 为何 表单中输入中文,接收的时候,是乱码呢? 因为 tomcat 服务器 是 外国人写的,人家默认使用的编码 为 iso8859-1 // 一定要在方法的第一行 增加编码设置 , 这个编码设置 只针对 post 请求 有效 , get 请求中的乱码 已经被 tomcat服务器高版本(8.0)进行处理 req.setCharacterEncoding("utf-8"); System.out.println("--------掌握-----------------------"); // 获取请求行中的信息 // 请求方式 String method = req.getMethod(); // 请求路径 String uri = req.getRequestURI(); StringBuffer url = req.getRequestURL(); // 协议 String protocol = req.getProtocol(); System.out.println("method = " + method); System.out.println("uri = " + uri); // 资源的映射路径 System.out.println("url = " + url); // 完整路径 System.out.println("protocol = " + protocol); // 获取请求头的信息 String accept = req.getHeader("Accept"); System.out.println("accept = " + accept); // 获取请求体中的信息 String username = req.getParameter("username"); System.out.println("username = " + username); String password = req.getParameter("password"); System.out.println("password = " + password); // getParamter 方法 只能获取 单个值 的情况 String hobby = req.getParameter("hobby"); System.out.println("hobby = " + hobby); // 获取param多个值 Map
parameterMap = req.getParameterMap(); parameterMap.forEach((x,y)-> System.out.println(x + Arrays.toString(y))); System.out.println("---------------不重要----------------------------"); // 获取一个key 对应有多个值的情况 String[] hobbies = req.getParameterValues("hobby"); System.out.println(Arrays.toString(hobbies)); } } 设置编码
在Servlet中,Servlet会在第一次获取请求参数时,将网络传输的流数据,解析为map集合。并且默认post请求是按照IOS-8859-1的编码格式进行解析的,以后再次获取请求参数时,直接从map容器中获取请求参数,根据map中key获取,key就是参数名称.
注意
- 如果要设置字符编码,必须在getParameter之前设置,否则由于只会解析一次,map容器就会存放使用IS0-8859-1转换的编码字符串,即Map中可能就是存放乱码数据。
//1.修改request对象编码 request.setCharacterEncoding("UTF-8");
获取请求行信息
获取请求的网络相关信息
package com.abc.servlet; 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.util.*; @WebServlet("/api") public class ServletApi extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { netInfo(req,resp); } /** * 网络信息相关的API * @param req * @param resp * @throws ServletException * @throws IOException */ private void netInfo(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取 网络请求的请求方法 String method = req.getMethod();//GET POST DELETE PUT 等等 System.out.println("请求方法是:"+method); //获取请求协议 String protocol = req.getProtocol(); System.out.println("请求协议是:"+protocol); //获取请求的URL StringBuffer url = req.getRequestURL();// 获取请求的URL System.out.println("请求的URL:"+url); //获取请求的资源路径 String uri = req.getRequestURI(); System.out.println("获取请求的URI:"+uri); } }
请求头信息
HttpServletRequest可以获取请求头信息。获取方式主要分为两类:
- 根据API提供固有的方法,获取相应的值。
- 根据API指定请求头key,获取对应的值。
package com.abc.servlet; 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.util.*; /** * @Description: Servlet API的介绍 */ @WebServlet("/api") public class ServletApi extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { headerInfo(req,resp); } /** * 请求头信息 * @param req * @param resp * @throws ServletException * @throws IOException */ private void headerInfo(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //内置的方法 String contentType = req.getContentType(); System.out.println("contentType : "+ contentType); int contentLength = req.getContentLength(); System.out.println("contentLength "+ contentLength); // 根据指定的头的key 获取对应的值 //text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 String accept = req.getHeader("Accept"); System.out.println("accept :"+accept); //获取所有的请求头name值 Enumeration
headerNames = req.getHeaderNames(); } }
响应信息相关的API
设置相应数据编码
设置响应编码,浏览器根据设置的响应编码进行数据解析编码,保证数据避免乱码
@Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //此时指定的是按照 UTF-8编码 指定将字符转化为字节的编码格式 resp.setCharacterEncoding("UTF-8"); //数据时 html的字符串 按照 UTF-8的编码 resp.setContentType("text/html;charset=UTF-8"); // tomcat 默认编码 ISO-8859-1 //文件是 : UTF-8编码 PrintWriter writer = resp.getWriter(); writer.print("你好 客户端!!!"); writer.flush(); writer.close(); }
获取响应流的信息
在Servlet响应流信息主要分为2类:
resp.getOutputStream();// 获取字节网络输出流 resp.getWriter();// 获取字符网络输出流
字节流 : 多用于文件的下载,处理一些非字符串数据
字符流 : 多用于处理文本,主要与ajax结合使用
验证码案例
使用Hutool的API生成验证码
先导入Hutool的jar包
-
package com.abc; import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.LineCaptcha; 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; @WebServlet("/img") public class Img extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //createLineCaptcha(宽度,高度,字符个数,干扰元素个数) LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(300, 100, 4, 6); //验证码图片中字符串 验证码字符串 String code = lineCaptcha.getCode();//放在session System.out.println(code); // 把验证码存入到session req.getSession().setAttribute("sessionCode",code); //使用响应流 将验证码图片数据输出 lineCaptcha.write(resp.getOutputStream()); } }
前端页面代码
使用ajax验证
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="js/jquery-3.6.1.min.js"></script> </head> <body> <form id="formId" action="/code" method="post"> 账号:<input type="text" name="username"/><br/> 密码:<input type="password" name="pwd"/><br/> 验证码:<input type="text" name="yzm"/> <img src="/img" onclick="clickMethod(this)" width="100px"/> <br/> <span style="color: red" id="tip"></span><br/> <input id="butId" type="button" value="登陆"> </form> <script> window.onload=function () { $("#butId").click(function () { $.post("/code",$("#formId").serialize(),function (data) { if (data.bol) { alert("登录成功") }else { $("#tip").text(data.hint) } }) }) } function clickMethod(img) { img.src="/img?time="+ new Date(); } </script> </body> </html>
后台验证代码
代码
package com.abc; import com.alibaba.fastjson.JSON; import com.powernode.util.JSONUtil; 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; @WebServlet("/code") public class Code extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); resp.setContentType("application/json;charset=utf-8"); String username = req.getParameter("username"); String pwd = req.getParameter("pwd"); String yzm = req.getParameter("yzm"); JSONUtil jsonUtil = new JSONUtil(); if (yzm == null || yzm == ""){ String s = JSON.toJSONString(jsonUtil.mick("验证码不能为空")); resp.getWriter().write(s); resp.getWriter().flush(); return; } Object sessionCode = req.getSession().getAttribute("sessionCode"); if (!yzm.equals(sessionCode)) { String s = JSON.toJSONString(jsonUtil.mick("验证码错误")); resp.getWriter().write(s); resp.getWriter().flush(); return; } if (username == null || username == ""){ String s = JSON.toJSONString(jsonUtil.mick("账号不能为空")); resp.getWriter().write(s); resp.getWriter().flush(); return; } if (pwd == null || pwd == ""){ String s = JSON.toJSONString(jsonUtil.mick("密码不能为空")); resp.getWriter().write(s); resp.getWriter().flush(); return; } if (!username.equals("abc") && !pwd.equals("123")){ String s = JSON.toJSONString(jsonUtil.mick("账号或密码错误")); resp.getWriter().write(s); resp.getWriter().flush(); return; }else { String s = JSON.toJSONString(jsonUtil); resp.getWriter().write(s); resp.getWriter().flush(); } } }
定义一个响应的数据类
package com.abc.util; public class JSONUtil { private boolean bol = true; private String hint ; public JSONUtil mick(String str){ this.hint = str; this.bol = false; return this; } public boolean isBol() { return bol; } public String getHint() { return hint; } }
效果图
文件的上传和下载
文件上传
如果实现文件上传功能 从0到1 去实现,其实是很复杂的, 本质是流的拷贝。难点在读取上传的文件的二进制流,市面上 提供了一个FileUpload 工具类, 来辅助我们完成文件的上传,下载, 今天我们使用更简单的方式,这种方式 只使用与 当前学习的servlet , 文件上传功能 ,已经被 servlet 3.0以上的版本 支持了
前端页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/upload" method="post" enctype="multipart/form-data"> 选择文件:<input type="file" name="fileImg"/><br/> <input type="submit" value="提交"/> </form> </body> </html>
后台代码
package com.abc.upload; import jdk.nashorn.internal.ir.CallNode; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; import java.io.IOException; import java.util.UUID; @MultipartConfig @WebServlet("/upload") public class FileUpload extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=utf-8"); //使用getPart()方法接收文件 Part fileImg = req.getPart("fileImg"); //获取文件的名字 String fileName = fileImg.getSubmittedFileName(); // System.out.println(fileName); //获取文件的大小 long size = fileImg.getSize(); // System.out.println(size); //获取文件的格式 String contentType = fileImg.getContentType(); // System.out.println(contentType); //文件上传路径 String partPath = "D:\\course11\\04-javaweb\\03-ajax&servlet\\uploadproject\\web\\upload"; //获取文件的后缀名 String substring = fileName.substring(fileName.lastIndexOf(".")); //随机生成一个文件名 String newImgName = UUID.randomUUID().toString(); //生成新的文件名 String name = newImgName + "." + substring; //上传文件 fileImg.write(partPath + "/" + name); } }
文件下载
前端页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 点击下载资源:<a href="/download?filename=1.jpg">1.jpg</a> </body> </html>
后台代码
package com.powernode.servlet; import com.sun.javaws.HtmlOptions; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URLEncoder; @WebServlet("/download") public class Download extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取下载文件的名称 String filename = req.getParameter("filename"); //获取下载文件的文件夹的路径 String path = "D:\\course11\\04-javaweb\\03-ajax&servlet\\downloadpeoject\\web\\download"; //获取下载文件的路径 String filePath = path + "/" + filename; // URLEncoder.encode(fileName,"utf-8") 把编码记性设置 , 必须要在 设置头信息的 同时 去修改编码 // 通过header 修改了 下载的文件的名称 如果不设置 header 它会使用默认名称 来作为下载文件的名称 (download) resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(filename,"utf-8") ); //下载的本质是copy,利用流来进行下载 //创建文件输入流 FileInputStream fileInputStream = new FileInputStream(new File(filePath)); //使用浏览器的默认下载路径 ServletOutputStream outputStream = resp.getOutputStream(); int len = 0; byte[] bytes = new byte[1024]; while ((len = fileInputStream.read(bytes)) != -1){ outputStream.write(bytes,0,len); outputStream.flush(); } outputStream.close(); fileInputStream.close(); } }
请求转发和重定向
请求转发
浏览器发出一个请求,由一个AServlet 进行接收处理,最后没有处理完毕,然后它(AServlet) 转交交给 (BServlet) 进行处理,把 AServlet 转交给 BServlet 这个动作叫请求转发
API:
request.getRequestDispatcher("跳转的路径").forward(request,response);
请求转发的特点
- 只发送一次请求
- 响应到浏览器的结果是一最后的servlet 为准
- 可以共享地址栏中的参数信息
- 可以访问 WEB-INF 下面的资源
- 不可以访问 站外资源 (不能跨域)
- 可以共享request 域对象的数据
重定向
浏览器发送请求到 a servlet ,然后 a servlet 完成一部分需求,告知浏览器,b servlet 能完成剩余的需求,因此,浏览器 会 再一次发送请求 到 b servlet 最终 完成了 整个请求
API:
response.sendRedirect("跳转路径")
重定向的特点
- 发送了多次请求(至少两次)
- 不能共享地址栏中的参数信息
- 最终显示的结果 以最后一个Servlet (和请求转发的特点一样)
- 不能访问WEB-INF 下面的资源
- 可以访问站外资源
- 不可以共享request 域对象中的数据
请求转发和重定向的选择
如果需要访问WEB-INF下面的资源 ,则 必须使用 请求转发
如果需要访问站外资源,则必须使用 重定向
其它,则可以 随便使用
一般是这样用的:如果地址栏的地址 应该 发生变化,则使用重定向
如果地址栏的地址,可以 不用变化,也能理解 则使用请求转发
转发与重定向的应用场景
- 应用场景:登录、购物车结算采用重定向,其他一律采用转发。
servlet的三大作用域
域对象的理解
- 域对象 : 存储数据的容器
- Servlet 中 有 4 个 能存储数据的 容器 (域对象)
- PageContext Request Session ServletContext (Application)
- 容器(域对象)的容量大小 是一样的,不同容器(域对象)的区别
- Request : 一次请求中有效 (只在请求转发中使用)
- Session: 一次会话中有效 (从浏览器打开,到关闭 称为一次会话)
- ServletContext (Application): 当前应用 (项目开启 到 关闭)
- 共同操作域对象 的API:
- 存:setAttribute(key,value)
- 取:getAttribute(key)
- 获取Reqeust 对象 : 直接使用
- 获取Session 对象 : request.getSession():
- 获取ServletContext 对象: request.getServletContext(): (上下文)
作用域的选择
能用小的 绝不用大的,够用原则:
Request < session < ServletContext