Java Web基础入门和实战基础什么是ServletServlet是怎么工作的?什么是Jsp什么是Ajax?Post 方法与 Get 方法的区别?转发和重定向的区别Session是什么(还需深入了解)MVC结构是什么项目简介项目架构概述项目功能概述核心功能实现注册验证码生成验证码错误惩罚前端和服务器通信 - 表单提交前端和服务器通信-Ajax服务器连接数据库数据传输与同步
摘要:本文通过开发一个 《基于 Servlet 的用户登陆注册 + 自动挂机打怪RPG》 Web项目,掌握前后端分离开发,掌握MVC模型,学习前端技术(Html + CSS + JavaScript + jQuery + JSON)与后端技术(Servlet + JSP),学习基本的SQL 语句,以这个项目达到 Java 第二阶段:Web开发 的入门水平。
作者:@Galaxyzeta 百度@大小板栗仔
Servlet是 Server Applet 的简称,是运行在服务器上的,负责处理服务器与浏览器之间业务的程序(我觉得就是后端)。
本教程用 Tomcat 作为本机服务器,Servlet 运行在 Tomcat 上。以提交一个表单并跳转网页为例:浏览器用户点击按钮提交表单后,浏览器生成一个 request ,携带表单信息发往Tomcat 的 Servlet 容器。Servlet 容器根据 form 的 action 从 web.xml 中的Servlet 映射关系中选择正确的 Servlet 类,生成 request 和 response 对象,然后实例化Servlet的对象。该对象判断 request 是 GET 方法还是 POST 方法,然后执行相应的方法。正常情况下,表单都是用 POST 提交的,Servlet 对象调用 doPost(request, response)
方法,方法体中可以用 request.getParameter()
接收 request 携带的参数并进行后台处理,进行逻辑判断后,调用 response.sendRedirect()
重定向。处理完毕,服务器将 request 和 response 发给回浏览器,由浏览器进行跳转。
Jsp ( Java Server Pages ) 是运行在服务器上的网页,理解为嵌入了Java 部分用于和服务器通信的 Html 网页。其在编译时,Java 部分会转化为 Servlet 程序。
Ajax是 Asynchornized Javascript and XML 的简称,是能够在不重新全部加载页面的情况下,使用 Javascript 与服务器进行异步通信,并局部改变页面内容的一种技术。核心是 XMLHttpRequest 类对象的方法和属性。
request.getRequestDispatcher(url).forward(req, res)
。response.redirect(url)
Session 会话 存储在服务器上,用于保持服务器和客户端的连接,是基于 Cookie 实现的。比如实现用户自动登录功能:用户输入信息后request传递给服务器上运行的 Servlet,调数据库验证通过后给 Session 加上属性,记录用户已经登录过了。以后浏览器进入登陆界面时只要能获得先前记录在 Session 的属性,就自动登陆。Session 有过期时间,过期后自动销毁,也能调方法 session.invalidate()
手动无效化。
M - Model - 数据库层,V - View - 视图层,C - Controller - 控制器层。一般来说,view指前端网页,model指数据库,controller指servlet,负责M和V之间的数据交换。
以MySQL为底层数据库,Tomcat 作为 Servlet 运行的服务器,结合传统的前端技术进行开发。
用户注册
用户登录
挂机游戏界面
游戏数据与数据库同步
思路:用 canvas 标签绘图,先随机生成数字和字符存储在变量中,用绘图功能绘制在 canvas 上,然后画一些干扰线和图形生成简单的验证码。注意的是要先用getContext()
方法获取绘图区域。
function generateConfirmCode() {
user.str = "";
let id = document.getElementById("myCanvas");
let cont = id.getContext('2d');
let c = 0;
let colorSet = ['red','blue','black','cyan','green','purple','orange'];
let charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
let char;
//init
cont.fillStyle = 'white';
cont.beginPath();
cont.fillRect(0,0,id.width,id.height);
cont.closePath();
//draw text
cont.font = "40px 'Consolas'";
while(c < 4){
user.str += (char = charSet.charAt(Math.ceil(Math.random()*charSet.length)-1));
cont.fillStyle = colorSet[Math.ceil(Math.random()*colorSet.length)-1];
cont.textAlign = "left";
cont.fillText(char,40+c*40+(-20+Math.random()*20),40+(-20+Math.random()*20));
c++;
}
//draw additional lines
for(let x = 0; x < 10; x++){
cont.beginPath();
cont.strokeStyle = colorSet[Math.ceil(Math.random()*colorSet.length)-1];
cont.moveTo(id.width*Math.random(),id.height*Math.random());
cont.lineTo(id.width*Math.random(),id.height*Math.random());
cont.stroke();
cont.closePath();
}
return true;
}
思路:用 cookie 保存当前用户的尝试次数和惩罚秒数,防止刷新导致惩罚失效。可以用 document.cookie=str
添加cookie,如果cookie的键已经存在,值将会覆盖原有的键。cookie 可以结合Date
类设置 expires
属性来决定 cookie 失效日期。要删除 cookie,只需要将 expires
属性的对应时间设置到规定时间之后即可。
Cookie 样例:document.cookie = "username=John Doe; expires=Sun, 31 Dec 2017 12:00:00 UTC";
xxxxxxxxxx
//每次进入都会读取cookie
function syncWithCookie() {
let cookie = document.cookie.split(";"); //每条Cookie之间以分号分隔
console.log(cookie);
user.chance = 3;
user.ban = -1;
cookie.forEach(function(value, index, array){
let eqpos = value.indexOf("=");
let prop = value.substring(0, eqpos).trim(); //Cookie 的 key
let val = value.substring(eqpos+1).trim(); //Cookie 的 Value
switch(prop){
case "chance":user.chance = Number.parseInt(val); break;
case "ban":user.ban = Number.parseInt(val); break;
}
console.log("prop="+prop);
console.log("val="+val);
console.log("chance="+user.chance);
console.log("ban="+user.ban);
});
if (user.ban !== -1){
interval = window.setInterval(banCountDown, 1000);
}
}
思路:form 元素中包含 input 输入元素和一个 submit 按钮,form 提交时触发 onsubmit 事件,如果 onsubmit 事件的返回值是 true,表单的 action 事件才会触发,可以用这一点做表单验证。提交方法和 servlet 原理不再叙述。servlet 中调用doPost()
,方法体中用request.getParameter(name)
获取提交的参数。
主要代码:
xxxxxxxxxx
let xml = new XHLHttpRequst();
xml.onreadystatechange = function(){
if(xml.status === 200 && xml.readystate === 4) {
//do something... Example:
let json = eval(xml.responseText);
}
}
xml.open("POST","game?service=getShop",true);
xml.send();
解释:xml 物体发出一个 Post 请求,访问的 URL 是 game?service=getShop ( game是 xml配置文件中定义的 servlet 映射,查询的参数是service = getShop ),true 代表 Async 即异步。当服务器响应 200 并且 xml物体的状态码是 4(这两个标志代表已经获取到服务器数据了),读取响应内容并生成一个json。
同步和异步:同步状态下所有操作完成之前会导致代码阻塞影响用户体验,建议使用异步加载。
思路:编写数据库连接类,JDBC 常规流程操作:加载Driver,得到Connection,创建Statement,执行Statement,得到结果集ResultSet,遍历结果集提取数据。这些操作不再讲述。
由于数据交换过程中频繁用到 JSON,所以我写了从数据库信息直接提取JSON字符串数组的方法,便于处理。思路:获取结果集元数据对象 ResultSetMetaData rsmd = rs.getMetaData()
,其中包含了结果集字段的名称和存储数据等信息,然后确定结果集的大小(用rs.last()
rs.getRow()
获取最后一行序号,注意不是从0开始标号的),接下来遍历结果集,每一行都根据字段类型决定不同数据类型(主要分字符串和数字两种,便于在JavaScript中处理),逐行生成JSON字符串并添加到数组中。
xxxxxxxxxx
public String[] queryToJSONArray(String querySql ,Object[] objects){
connect("game");
try{
PreparedStatement pst = conn.prepareStatement(querySql);
for (int x = 1; x <= objects.length; x++) {
if (objects[x - 1] instanceof Integer) {
pst.setInt(x, (Integer) objects[x - 1]);
} else if (objects[x - 1] instanceof String) {
pst.setString(x, (String) objects[x - 1]);
}
}
//get query size
ResultSet rs = pst.executeQuery();
rs.last();
int size = rs.getRow();
//get column counts
ResultSetMetaData rsmd = rs.getMetaData();
int count = rsmd.getColumnCount();
//generate json
String[] strArr = new String[size];
rs.beforeFirst();
while(rs.next()){
StringBuilder sb = new StringBuilder("{");
for(int x=1; x <= count; x++) {
String columnName = rsmd.getColumnName(x);
String columnType = rsmd.getColumnTypeName(x);
//Handle different types so that data can be correctly joined in javaScripts.
if (columnType.equals("VARCHAR")){
//handle data as a String
sb.append('"').append(columnName).append("\":\"").append(rs.getString(columnName)).append("\",");
} else {
//handle data as a number
sb.append('"').append(columnName).append("\":").append(rs.getString(columnName)).append(",");
}
}
sb.deleteCharAt(sb.lastIndexOf(","));
sb.append('}');
strArr[rs.getRow()-1] = sb.toString();
}
return strArr;
} catch (SQLException se) {
se.printStackTrace();
} finally {
disconnect();
}
return null;
}
玩家登录游戏后通过Ajax向Servlet发送请求,Servlet 从数据库检索玩家信息并查询出玩家物品Json信息,并通过response writer
输出 Json 信息到前台。前台调用 Js 的 eval()
或 Json.parse()
方法解析 Json 字符串,转化为 Js 对象,便于之后的操作。
玩家可以通过点击保存游戏按钮进行保存,保存时通过 XMLHttpRequest
对象的 send()
方法将 Json 以参数形式发送到服务端,并由 Servlet 存入数据库。
xxxxxxxxxx
//保存数据到后台
function saveGame() {
console.log(JSON.stringify(player));
let xhr = new XMLHttpRequest();
xhr.open("POST","game?service=saveGame&name="+player.name);
//要发Form字符串头部要如下设置,加入 charset=‘utf-8’ 防止中文乱码
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
xhr.send("json="+JSON.stringify(player));
}