JavaWeb笔记

0x01 tomcat

tomcat是一个服务器中间件,用于运行java web的服务程序,特点是轻量级,适合初学者进行部署和学习。

1.1 下载tomcat

官网:https://tomcat.apache.org/

可以选择不同的版本,复现一些特殊的漏洞,可能依赖不同的tomcat版本。如果直接不能下载相应版本,我们可以通过
https://tomcat.apache.org/download-70.cgi 下载7版本的tomcat。可以在archive下选择不同的小版本。
开发的话,一般选择最新即可。

1.2 配置tomcat

如果是使用IDEA等IDE进行java编写和部署,一般不需要单独把tomcat配置环境变量。如果需要的话,可以按照这样的步骤进行配置:

打开终端,输入:
open .bash_profile

打开.bash_profile,编辑,插入如下语句(当然你也可以用vim编辑):
export PATH=$PATH:/Users/v0w/Environment/tomcat/apache-tomcat-9.0.41/bin

其中/Users/v0w/Environment/tomcat/apache-tomcat-9.0.41/bin是路径名,对应自己的路径名即可。

然后保存关闭。再在终端执行:
source ~/.bash_profile

此时你可以在终端尝试如下命令启动tomcat服务:
startup.sh
version.sh

配置文件
tomcat的配置文件是/conf/server.xml

<!--通过设置这里的port,更改java web的服务端口-->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!--通过修改这里的name,设置不同的hostname-->
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">

tomcat的默认javaweb应用的配置文件是/conf/web.xml
用来配置javaweb的路由,过滤器等,
当然,在存在/webapps/ROOT/WEB-INF/web.xml时,以此为webapps的web.xml为优先的web配置。

0x02 Maven

一个javaWeb项目在开发过程中,可能需要使用很多的jar包,需要开发者自行导入,这样的话,非常繁琐复杂。Maven工具可以帮助我们自动的导入jar包。

2.1 下载Maven

打开Maven官网下载页面: https://maven.apache.org/download.cgi
下载: apache-maven-3.6.3-bin.tar.gz
解压下载的安装包到某一目录,比如:/Users/xxx/Environment/apache-maven-3.6.3

2.2 配置环境变量

打开terminel输入以下命令: vim ~/.bash_profile 打开.bash_profile文件,在此文件中添加设置环境变量的命令

export M2_HOME="/Users/v0w/Environment/apache-maven-3.6.3"
export PATH=$PATH:$M2_HOME/bin

添加之后保存并,执行以下命令使配置生效:

source ~/.bash_profile

查看配置是否生效mvn -v

2.3 配置阿里云镜像

国内最好的maven repository
第一步:修改maven根目录下的conf文件夹中的setting.xml文件,内容如下:

<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>

第二步: pom.xml文件里添加

<repositories>  
<repository>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

建立本地仓库

<localRepository>/Users/v0w/Environment/apache-maven-3.6.3/maven-repo</localRepository>

2.4 IDEA下使用Maven

新建Maven

在IDEA中的配置:
Maven配置

使用archtype时,创建的webapp maven项目:
webapp Maven

不使用archtype时,创建的默认的maven项目:
默认maven

2.5 IDEA 配置tomcat

add configuration

tomcat详细配置

为什么会有这个问题:我们访问一个网站需要指定一个文件夹名称

解决警告问题。
解决警告问题

tomcat虚拟访问路径的配置

2.6 pom.xml文件

pom.xml是maven的核心配置文件:
pom.xml文件结构和各部分功能如下:
pom.xml文件结构

对于dependency,可以体现maven的高级之处:不仅会导入dependecy中明确配置的jar包,还会自动导入其依赖的其他jar包。

maven由于约定大于配置的性质,之后我们写的配置文件可能存在无法导出或者无法生效的问题。

解决方案:
配置resources,防止资源导出失败问题:

<!--在build中配置resources,防止资源导出失败问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>

0x03 Servlet

3.1 Servlet 简介

  • Servlet是sun公司开发动态web的技术
  • Servlet是一个接口,想开发一个动态web程序,分为两步:
    1. 编写一个java类,实现Servlet接口
    2. 将编写好的java程序部署到tomcat服务器中

我们把实现了Servlet接口的应用程序成为Servlet
Servlet接口有两个默认的实现类:

  • GenericServlet
  • HttpServlet
    继承接口如下:
op1=>operation: Servlet接口
op2=>operation: GenericServlet类
op3=>operation: HttpServlet类
op1->op2->op3

3.2 HelloServlet

1、 新建工程,使用Maven并优化
优化Maven:
增加目录:resources + java
将web.xml修改成最新版的web.xml
最新的web.xml

<?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"
metadata-complete="true">

</web-app >

2、编写Servlet

package top.v0w.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
PrintWriter writer = resp.getWriter(); // 响应流
writer.println("Hello, Servlet");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);
doGet(req, resp);
}
}

3、编写映射
为什么需要映射:我们写的是java程序,浏览器需要连接web服务器,我们需要在web服务器注册写的servlet,并且给浏览器一个可以访问的路径。

在web.xml中增加servlet的注册和路径映射

<!--  注册selevet  -->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>top.v0w.servlet.HelloServlet</servlet-class>
</servlet>
<!-- servlet路径映射 -->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

4、访问测试
测试成功

3.3 Servlet原理

Servlet运行流程

3.4 Mapping问题

  • 一个Servlet可以映射多个路径
  • 可以使用通配符进行映射<url-pattern>/hello/*</url-pattern>
  • 可以自定义后缀

3.5 ServletContext

web容器在启动的时候,会为每个Servlet创建一个ServletContext对象,它代表当前的web应用。

  • 共享数据:多个Servlet之间进行通信等
  • 请求转发
  • 获取初始化参数
  • 读取配置文件

这里补充一下请求转发和重定向的区别:
请求转发和重定向

3.6 HttpServletRequest

  • 获取前端传递的参数
  • 请求转发

3.6.1 获取请求参数

req.getParameter("username");
req.getParameterValues("hobbies");

3.6.2 请求转发

req.getRequestDispatcher("/sucess.jsp").forward(req,resp);

3.7 HttpServletResponse

应用:

  • 向浏览器输出信息
  • 下载文件
  • 生成验证码
  • 实现重定向

3.7.1 下载文件

编写Servlet

package top.v0w.servlet;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;

public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 获取文件的下载路径
String realPath = "/Users/v0w/Desktop/test.jpg";
System.out.println("path: "+realPath);
//2. 下载文件的文件名
String filename = realPath.substring(realPath.lastIndexOf("/")+1);
//3. setHeaders是浏览器支持下载的数据类型
resp.setHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode(filename, "utf-8"));
//4. 获取下载文件的输入流
FileInputStream in = new FileInputStream(realPath);
//5. 获取缓冲区
int len = 0;
byte[] buffer = new byte[1024];
//6. OutputStream
ServletOutputStream out = resp.getOutputStream();
//7. FileInputStream into buffer, buffer into client through ServletOutputStream
while ((len = in.read(buffer))>0){
out.write(buffer,0, len);
}
in.close();
out.close();
}

}

注册Servlet

<servlet>
<servlet-name>download</servlet-name>
<servlet-class>top.v0w.servlet.DownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>download</servlet-name>
<url-pattern>/down</url-pattern>
</servlet-mapping>

3.7.2 验证码

验证码Servlet

package top.v0w.servlet;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 浏览器自动刷新
resp.setHeader("refresh","3");

//内存中创建一个图片
BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
// 得到图片
Graphics2D g = (Graphics2D) image.getGraphics(); //类似画笔
// 设置图片背景颜色
g.setBackground(Color.white);
g.fillRect(0,0,80,20);
// 给图片写数据
g.setColor(Color.blue);
g.setFont(new Font("aa",Font.BOLD,20));
g.drawString(makeRandom(),0,20);

//告诉浏览器格式,如何打开
resp.setContentType("image/jpg");
resp.setDateHeader("expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");

ImageIO.write(image,"jpg",resp.getOutputStream());
}

private String makeRandom(){
Random random = new Random();
String num = random.nextInt(999999)+"";
//处理009999这种情况
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 6-num.length(); i++) {
sb.append(0);
}
sb.append(num);
return sb.toString();
}

}

注册Servlet

<servlet>
<servlet-name>img</servlet-name>
<servlet-class>top.v0w.servlet.ImageServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>img</servlet-name>
<url-pattern>/img</url-pattern>
</servlet-mapping>

结果真不戳

3.7.3 实现重定向

常见场景:用户登录等

void sendRedirect(String var1) throws IOException;
package top.v0w.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
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.text.DateFormat;
import java.util.Date;

public class CookieServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
req.setCharacterEncoding("utf-8");

PrintWriter pw = resp.getWriter();
Cookie[] cookies = req.getCookies();

for (Cookie cookie : cookies) {
if(cookie.getName().equals("time")){
long time = Long.parseLong(cookie.getValue());
Date datetime = new Date(time);
pw.write(datetime.toString());
}
}

Cookie c = new Cookie("time",System.currentTimeMillis()+"");
resp.addCookie(c);
}
}

Cookie

3.9 Session

package top.v0w.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;

public class SessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
PrintWriter pw = resp.getWriter();

//getSession
HttpSession session = req.getSession();
session.setAttribute("name","V0W");

String SessionId = session.getId();
if(session.isNew()){
pw.write("u are new one, id is "+SessionId);
}else{
pw.write("old one, id is "+SessionId);
}

String name = (String) session.getAttribute("name");
pw.write("name: "+name);


/* Session做了什么事情:
* Cookie jsessionid = new Cookie("JSESSIONID", SessionId);
* resp.addCookie(jsessionid)
* */
}
}

JSESSIONID

Session失效:

session.invalidate();

可以在web程序的web.xml中设置session时间等配置,也可以在Tomcat等容器的web.xml中设置session时间。

<!-- session-config包含一个子元素session-timeout.定义web站台中的session参数.  -->
<session-config>
<!-- 定义这个web站台所有session的有效期限.单位为分钟. 例子中为600分钟 -->
<session-timeout>600</session-timeout>
</session-config>

优先级:Servlet中API设置 > 程序/web.xml设置 > Tomcat/conf/web.xml设置

0x04 JSP

4.1 JSP原理

Java Server Page: 可以嵌入Java代码,实现动态功能。

服务器会把jsp编译成java代码和class, jsp本质上就是servlet。

// 初始化
public void _jspInit()
// 销毁
public void _jspDestroy()
// jspService
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
  1. 判断请求类型

  2. 内置一些对象

    final javax.servlet.jsp.PageContext pageContext;//页面上下文
    final javax.servlet.ServletContext application;//applicationContext
    final javax.servlet.ServletConfig config;//config
    javax.servlet.jsp.JspWriter out = null;//out
    final java.lang.Object page = this;//page this
    javax.servlet.jsp.JspWriter _jspx_out = null;//out
    javax.servlet.jsp.PageContext _jspx_page_context = null;//页面上下文
  3. 输出页面增加的代码

    try {
    response.setContentType("text/html; charset=UTF-8");
    pageContext = _jspxFactory.getPageContext(this, request, response,
    null, false, 8192, true);
    _jspx_page_context = pageContext;
    application = pageContext.getServletContext();
    config = pageContext.getServletConfig();
    out = pageContext.getOut();
    _jspx_out = out;
  4. 以上对象可以在JSP直接使用

jsp流程图

4.2 JSP语法

<%--jsp表达式:
<%= 变量或者表达式%>
--%>
<%= new java.util.Date()%>

<%-- <% Java代码 %> --%>
<%
int sum=0;
for (int i = 0; i < 50; i++) {
sum+=i;
}
out.write("<h1>"+sum+"</h1>");
%>

jsp声明

<%!
private int globalVar = 0;
static {
System.out.println("Loading");
}
%>

jsp声明生成java代码放到index_jsp.java类里面,jsp表达式等生成java代码放到_jspService方法中。

4.3 JSP指令

<%@page errorPage="error.jsp" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

4.4 九大内置对象

1. PageContext
2. Request
3. Response
4. Session
5. Application(ServletContext)
6. config (ServletConfig)
7. out
8. page
9. exception

举例

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
pageContext.setAttribute("name1","V0W1");
request.setAttribute("name2","v0w2");
session.setAttribute("name3","V0W3");
application.setAttribute("name4","V0W4");
%>

<%
String name1 = (String) pageContext.findAttribute("name1");
String name2 = (String) pageContext.findAttribute("name2");
String name3 = (String) pageContext.findAttribute("name3");
String name4 = (String) pageContext.findAttribute("name4");
String name5 = (String) pageContext.findAttribute("name5");
%>

<%--使用EL表达式来取最快 ${} 且对于不存在的变量,不会显示--%>
<h2>取出的值为</h2>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3>${name5}</h3>

</body>
</html>

4.5 jsp标签&JSTL表达式&EL表达式

依赖

<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>

</dependencies>

jsp标签

<%--包含文件--%>
<jsp:include page="index.jsp"></jsp:include>

<jsp:forward page="index.jsp">
<jsp:param name="name" value="V0W"/>
<jsp:param name="age" value="12"/>
</jsp:forward>
<%--相当于index.jsp?name=V0W&age=12--%>

JSTL标签

JSTL标签库的使用就是为了弥补jsp标签的不足。
需要依赖一些jar包:
1:jstl-1.2
2:standard-1.1.2
3:jstl-api-1.2.1
4:jstl-impl-1.2

pom.xml

<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl-api -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.glassfish.web/jstl-impl -->
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jstl-impl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>

引入JSTL标签

<%--核心标签是最常用的 JSTL标签。引用核心标签库的语法如下:--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%--核心标签是最常用的 JSTL标签。引用核心标签库的语法如下:--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>JSTL</title>
</head>
<body>

<hr>
<form action="JSTL.jsp" method="get">
<%--
EL 表达式获取表单的数据
${param.username}
--%>
<input type="text" name="username" value="${param.username}">
<input type="submit" value="提交">
</form>

<%--判断提交的用户名是admin,就登录成功--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
<c:out value="管理员欢迎你" />
</c:if>

<c:out value="${isAdmin}" />

</body>
</html>

效果图
其他标签需要的时候,参考菜鸟教程-jsp-jstl标签即可

EL表达式

不同版本的tomcat是否默认开启对EL表达式的支持,是不一定的。
所以为了保证EL表达式能够正常使用,需要在<%@page标签里加上isELIgnored="false"
使用EL表达式,非常简单。

输出
比如使用JSTL输出要写成

<c:out value="${name}" /> 

但是用EL只需要

${name}

EL表达式可以从pageContext,request,session,application四个作用域中取到值,如果4个作用域都有name属性怎么办?
EL会按照从高到低的优先级顺序获取
pageContext>request>session>application

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<c:set var="name" value="${'gareen-pageContext'}" scope="page" />
<c:set var="name" value="${'gareen-request'}" scope="request" />
<c:set var="name" value="${'gareen-session'}" scope="session" />
<c:set var="name" value="${'gareen-application'}" scope="application" />

4个作用域都有name,优先获取出来的是 : ${name}

结合JSTL标签<c:forEach>使用,可以进一步简化代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.*"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<%
List<String> heros = new ArrayList<String>();
heros.add("塔姆");
heros.add("艾克");
heros.add("巴德");
heros.add("雷克赛");
heros.add("卡莉丝塔");
request.setAttribute("heros",heros);
%>

<table width="200px" align="center" border="1" cellspacing="0">
<tr>
<td>编号</td>
<td>英雄</td>
</tr>

<c:forEach items="${heros}" var="hero" varStatus="st" >
<tr>
<td>${st.count}</td>
<td>${hero}</td>
</tr>
</c:forEach>
</table>

取参数

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.*" isELIgnored="false"%>

${param.name}

0x05 MVC框架

早期架构
仅仅使用Servlet缺点:

  • Servlet不仅要准备数据,还要准备html。 尤其是准备html,可读性非常差,维护起来也很麻烦

仅仅使用jsp缺点:

  • 虽然编写html方便了,但是写java代码不如在Servlet中那么方便

结合Serlvet和JSP进行数据的显示,就是一种MVC的思想。

  • M 代表 模型(Model)
  • V 代表 视图(View)
  • C 代表 控制器(controller)

模型是什么呢? 模型就是数据,就是dao,bean
视图是什么呢? 就是网页, JSP,用来展示模型中的数据
控制器是什么? 控制器用来把不同的数据,显示在不同的视图上。 在这个例子的,Servlet就是充当控制器的角色,把Hero对象,显示在JSP上。

控制器的作用就是把不同的数据(Model),显示在不同的视图(View)上。

  • Model
    • 业务逻辑层(Service)
    • 数据持久层CRUD(DAO)
  • View
    • 显示数据
    • 提供链接,发起Servlet请求
  • Controller
    • 接受用户请求(req用户请求,Session信息…)
    • 交给业务层处理对应的代码
    • 控制试图的跳转

MVC框架

实现一个CRUD操作数据库Hero的小项目:
文件结构:
Structure

bean.Hero

package top.v0w.bean;

public class Hero {

public int id;
public String name;
public float hp;
public int damage;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getHp() {
return hp;
}
public void setHp(float hp) {
this.hp = hp;
}
public int getDamage() {
return damage;
}
public void setDamage(int damage) {
this.damage = damage;
}

}

dao.HeroDAO(也是copy how2j的代码)

package top.v0w.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import top.v0w.bean.Hero;

public class HeroDAO {

public HeroDAO() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2j?characterEncoding=UTF-8", "root",
"V0Wldl.n1ub1");
}

public int getTotal() {
int total = 0;
try (Connection c = getConnection(); Statement s = c.createStatement();) {

String sql = "select count(*) from hero";

ResultSet rs = s.executeQuery(sql);
while (rs.next()) {
total = rs.getInt(1);
}

System.out.println("total:" + total);

} catch (SQLException e) {

e.printStackTrace();
}
return total;
}

public void add(Hero hero) {

String sql = "insert into hero values(null,?,?,?)";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {

ps.setString(1, hero.name);
ps.setFloat(2, hero.hp);
ps.setInt(3, hero.damage);

ps.execute();

ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
int id = rs.getInt(1);
hero.id = id;
}
} catch (SQLException e) {

e.printStackTrace();
}
}

public void update(Hero hero) {

String sql = "update hero set name= ?, hp = ? , damage = ? where id = ?";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {

ps.setString(1, hero.name);
ps.setFloat(2, hero.hp);
ps.setInt(3, hero.damage);
ps.setInt(4, hero.id);

ps.execute();

} catch (SQLException e) {

e.printStackTrace();
}

}

public void delete(int id) {

try (Connection c = getConnection(); Statement s = c.createStatement();) {

String sql = "delete from hero where id = " + id;

s.execute(sql);

} catch (SQLException e) {

e.printStackTrace();
}
}

public Hero get(int id) {
Hero hero = null;

try (Connection c = getConnection(); Statement s = c.createStatement();) {

String sql = "select * from hero where id = " + id;

ResultSet rs = s.executeQuery(sql);

if (rs.next()) {
hero = new Hero();
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
hero.name = name;
hero.hp = hp;
hero.damage = damage;
hero.id = id;
}

} catch (SQLException e) {

e.printStackTrace();
}
return hero;
}

public List<Hero> list() {
return list(0, Short.MAX_VALUE);
}

public List<Hero> list(int start, int count) {
List<Hero> heros = new ArrayList<Hero>();

String sql = "select * from hero order by id desc limit ?,? ";

try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {

ps.setInt(1, start);
ps.setInt(2, count);

ResultSet rs = ps.executeQuery();

while (rs.next()) {
Hero hero = new Hero();
int id = rs.getInt(1);
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
hero.id = id;
hero.name = name;
hero.hp = hp;
hero.damage = damage;
heros.add(hero);
}
} catch (SQLException e) {

e.printStackTrace();
}
return heros;
}

}

filter.CharEncodingFilter

package top.v0w.filter;

import javax.servlet.*;
import java.io.IOException;

public class CharEncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init filter");
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=UTF-8");

chain.doFilter(request,response);
}

@Override
public void destroy() {
System.out.println("destroy filter");
}
}

servlet.ListHeroServlet

package top.v0w.servlet;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import top.v0w.bean.Hero;
import top.v0w.dao.HeroDAO;

public class HeroListServlet extends HttpServlet {

protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<Hero> heros = new HeroDAO().list();
request.setAttribute("heros", heros);
request.getRequestDispatcher("listHero.jsp").forward(request, response);

}
}

servlet.DeleteHeroServlet

package top.v0w.servlet;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import top.v0w.bean.Hero;
import top.v0w.dao.HeroDAO;

public class HeroListServlet extends HttpServlet {

protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<Hero> heros = new HeroDAO().list();
request.setAttribute("heros", heros);
request.getRequestDispatcher("listHero.jsp").forward(request, response);

}
}

webapp/listHero.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.*"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<table align='center' border='1' cellspacing='0'>
<tr>
<td>id</td>
<td>name</td>
<td>hp</td>
<td>damage</td>
<td>edit</td>
<td>delete</td>
</tr>
<c:forEach items="${heros}" var="hero" varStatus="st">
<tr>
<td>${hero.id}</td>
<td>${hero.name}</td>
<td>${hero.hp}</td>
<td>${hero.damage}</td>
<td><a href="editHero?id=${hero.id}">edit</a></td>
<td><a href="deleteHero?id=${hero.id}">delete</a></td>
</tr>
</c:forEach>
</table>

web.xml

<?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"
metadata-complete="true">

<servlet>
<servlet-name>HeroListServlet</servlet-name>
<servlet-class>top.v0w.servlet.HeroListServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HeroListServlet</servlet-name>
<url-pattern>/listHero</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>HeroDeleteServlet</servlet-name>
<servlet-class>top.v0w.servlet.HeroDeleteServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HeroDeleteServlet</servlet-name>
<url-pattern>/deleteHero</url-pattern>
</servlet-mapping>

<filter>
<filter-name>CharEncodingFiler</filter-name>
<filter-class>top.v0w.filter.CharEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharEncodingFiler</filter-name>
<url-pattern>/*</url-pattern>

</filter-mapping>

</web-app>

0x06 Filter

Filter就像一个一个哨卡,用户的请求需要经过Filter。并且可以有多个过滤器:

过滤器
自己编写的Filter需要实现javax.servlet.Filter接口

package top.v0w.filter;

import javax.servlet.*;
import java.io.IOException;

public class CharactorEncodingFilter implements Filter {
//初始化: web服务器启动时,进行初始化
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化");
}

//实际过滤器
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=UTF-8");

System.out.println("chain 执行前");
chain.doFilter(request,response);
System.out.println("chain 执行后");
}

// 销毁: web服务器关闭时,才销毁
@Override
public void destroy() {
System.out.println("过滤器销毁");
}
}

web.xml进行过滤器的注册

<filter>
<filter-name>CharEncodingFiler</filter-name>
<filter-class>top.v0w.filter.CharactorEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharEncodingFiler</filter-name>
<url-pattern>/show</url-pattern>
</filter-mapping>

0x07 Listener 监听器

package top.v0w.listener;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class OnlineCountListener implements HttpSessionListener {
// 创建Session监听,一旦创建Session,就会触发这个
@Override
public void sessionCreated(HttpSessionEvent se) {
ServletContext ctx = se.getSession().getServletContext();
Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");

if(onlineCount == null){
onlineCount = new Integer(1);
}else{
int count = onlineCount.intValue();
onlineCount = new Integer(count+1);
}

ctx.setAttribute("OnlineCount",onlineCount);

}

// 销毁Session监听,一旦Session销毁,会触发这个
@Override
public void sessionDestroyed(HttpSessionEvent se) {
ServletContext ctx = se.getSession().getServletContext();
Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");

if(onlineCount == null){
onlineCount = new Integer(0);
}else{
int count = onlineCount.intValue();
onlineCount = new Integer(count-1);
}

ctx.setAttribute("OnlineCount",onlineCount);
}
}

web.xml 注册监听器

<listener>
<listener-class>top.v0w.listener.OnlineCountListener</listener-class>
</listener>

0x08 JSON

8.1 简介与简单使用

JSON JavaScript 对象表示法(JavaScript Object Notation) 是一种存储数据的方式。
以键值对的形式进行定义

<script>
var gareen = {"name":"盖伦","hp":616};

document.write("英雄名称: " + gareen.name + "<br>");
document.write("英雄血量: " + gareen.hp + "<br>");
</script>

通过方括号[] 创建JSON 数组,可以用类似数组的方式来获取单个对象

<script>

var heros=
[
{"name":"盖伦","hp":616},
{"name":"提莫","hp":313},
{"name":"死歌","hp":432},
{"name":"火女","hp":389}
]

document.write("JSON数组大小"+heros.length);
document.write( "第4个英雄是:" + heros[3].name);
</script>

字符串转换成JSON对象

<script>
var s1 = "{\"name\":\"盖伦\"";
var s2 = ",\"hp\":616}";
var s3 = s1+s2;

document.write("这是一个JSON格式的字符串:" + s3);
document.write("<br>");
var gareen = eval("("+s3+")");
document.write("这是一个JSON对象: " + gareen);

</script>

JSON对象转换成字符串

<script>
var hero = {"name":"盖伦","hp":"616"};
document.write("这是一个json 对象:"+ hero);
document.write("<br>");
var heroString = JSON.stringify(hero)
document.write("这是一个json 字符串:"+ heroString );
</script>

8.2 FastJson

8.2.1 简介

Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。
Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象。
Fastjson 源码地址:https://github.com/alibaba/fastjson

Fastjson 中文 Wiki:https://github.com/alibaba/fastjson/wiki/Quick-Start-CN

8.2.2 使用

你可以在 maven 中央仓库中直接下载:
http://repo1.maven.org/maven2/com/alibaba/fastjson/
或者配置 maven 依赖:

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>x.x.x</version>
</dependency>

8.2.3 将Java对象转换成Json格式字符串(序列化)

JavaBean Person类

package test.v0w;

import com.alibaba.fastjson.annotation.JSONField;
import java.util.Date;

public class Person {

@JSONField(name = "AGE")
private int age;

@JSONField(name = "FULL NAME")
private String fullName;

// 这里的注解时factjson的注解,表示这个字段的序列化名称,还可以通过其他字段对filed转换json时进行不同的规定
@JSONField(name = "DATE OF BIRTH",format = "yyyy-MM-dd")
private Date dateOfBirth;

public Person(int age, String fullName, Date dateOfBirth) {
super();
this.age = age;
this.fullName= fullName;
this.dateOfBirth = dateOfBirth;
}

// 标准 getters & setters
}

FastJson.java 对Person对象进行序列化,转换成Json字符串

package test.v0w;

import com.alibaba.fastjson.JSON;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class Fastjson {

private static List<Person> listOfPersons = new ArrayList<Person>();

@Before
public void setUp() {
listOfPersons.add(new Person(15, "John Doe", new Date()));
listOfPersons.add(new Person(20, "Janette Doe", new Date()));
}

@Test
public void whenJavaList_thanConvertToJsonCorrect() {
String jsonOutput= JSON.toJSONString(listOfPersons);
System.out.println(jsonOutput);
}
}

输出:

[{"AGE":15,"DATE OF BIRTH":"2020-12-31","FULL NAME":"John Doe"},{"AGE":20,"DATE OF BIRTH":"2020-12-31","FULL NAME":"Janette Doe"}]

如果美化一下格式,输出:
[
{
"AGE": 15,
"DATE OF BIRTH": "2020-12-31",
"FULL NAME": "John Doe"
},
{
"AGE": 20,
"DATE OF BIRTH": "2020-12-31",
"FULL NAME": "Janette Doe"
}
]

我们可以通过定义不同的注解,指定进行Json序列化时,字段的不同行为:
比如自定义输出,控制字段的排序,日期显示格式,序列化标记等。

  • format 参数用于格式化 date 属性。

    • @JSONField(format="yyyyMMdd")
  • 默认情况下, FastJson 库可以序列化 Java bean 实体, 但我们可以使用 serialize 指定字段不序列化。

    • @JSONField(deserialize=false)
    • @JSONField(serialize=false)
  • 使用 ordinal 参数指定字段的顺序

    • @JSONField(ordinal = 1) Json序列化后第一个字段
    • @JSONField(ordinal = n) Json序列化后第n个字段

    具体可以参见@JSONField注解定义:
    reference

JsonField配置方式
FieldInfo 可以配置在 getter/setter 方法或者字段上。也可以直接配置在field字段上.

FastJson BeanToArray序列化功能

String jsonOutput= JSON.toJSONString(listOfPersons, SerializerFeature.BeanToArray);

输出结果为:

[
[
15,
"2020-12-31",
"John Doe"
],
[
20,
"2020-12-31",
"Janette Doe"
]
]

即直接对字段数据进行序列化,但是并不将其与FieldName对应。

8.2.4 创建Json对象

创建 JSON 对象非常简单,只需使用 JSONObject(fastJson提供的json对象) 和 JSONArray(fastJson提供json数组对象) 对象即可。

我们可以把JSONObject 当成一个 Map<String,Object> 来看,只是 JSONObject 提供了更为丰富便捷的方法,方便我们对于对象属性的操作。我们看一下源码。
reference

@Test
public void whenGenerateJson_thanGenerationCorrect() throws ParseException {
JSONArray jsonArray = new JSONArray();
for (int i = 0; i < 2; i++) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("AGE", 10);
jsonObject.put("FULL NAME", "Doe " + i);
jsonObject.put("DATE OF BIRTH", "2016/12/12 12:12:12");
jsonArray.add(jsonObject);
}
String jsonOutput = jsonArray.toJSONString();
System.out.println(jsonOutput);
}

8.2.5 Json格式字符串解析成Java对象(反序列化)

    @Test
public void whenJson_thanConvertToObjectCorrect() {
Person person = new Person(20, "John Doe", new Date());
System.out.println("Old Person: "+ person);
String jsonObject = JSON.toJSONString(person);
System.out.println("Json String: "+jsonObject);

Person newPerson = JSON.parseObject(jsonObject, Person.class);
System.out.println("New Person: "+ newPerson);

// assertEquals(newPerson.getAge(), 20); // 如果我们设置序列化化为 default true
assertEquals(newPerson.getAge(), 0); // 如果我们设置序列化化为 false
assertEquals(newPerson.getFullName(), listOfPersons.get(0).getFullName()); //assertEquals(expected,actual) 判断两个值是否相等
}

output:

Old Person: Person{age=20, fullName='John Doe', dateOfBirth=Thu Dec 31 14:33:25 CST 2020}
Json String: {"DATE OF BIRTH":"2020-12-31","FULL NAME":"John Doe"}
New Person: Person{age=0, fullName='John Doe', dateOfBirth=Thu Dec 31 00:00:00 CST 2020}

8.3 Jackson

8.3.1 简介

Jackson框架是基于Java平台的一套数据处理工具,被称为“最好的Java Json解析器”。
Jackson框架包含了3个核心库:core,databind,annotations.
Jackson还包含了其它数据处理类库,此外不作说明。

  • 流式API - 读取并将JSON内容写入作为离散事件。 JsonParser读取数据,而JsonGenerator写入数据。它是三者中最有效的方法,是最低的开销和最快的读/写操作。它类似于Stax解析器XML。

  • 树模型 - 准备JSON文件在内存里以树形式表示。 ObjectMapper构建JsonNode节点树。这是最灵活的方法。它类似于XML的DOM解析器。

  • 数据绑定 - 转换JSON并从POJO(普通Java对象)使用属性访问或使用注释。它有两个类型。

    • 简单的数据绑定 - 转换JSON和Java Maps, Lists, Strings, Numbers, Booleans 和null 对象。
    • 全部数据绑定 - 转换为JSON从任何JAVA类型。
  • Jackson Home Page:https://github.com/FasterXML/jackson

  • Jackson Wiki:http://wiki.fasterxml.com/JacksonHome

  • Jackson doc: https://github.com/FasterXML/jackson-docs

  • Jackson Download Page:http://wiki.fasterxml.com/JacksonDownload

  • Jackson Guide Blog: https://www.baeldung.com/jackson

maven pom.xml

<!-- jackson request jar package -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.3</version>
</dependency>

8.3.2 Java 对象转Json字符串

@Test
public void test() throws IOException {
ObjectMapper mapper = new ObjectMapper();
Person person = new Person(18, "V0WKeep3r", new Date());
File newFile= new File("/Users/v0w/Desktop/person.json");
// 序列化
// 是否可以序列化
System.out.println(mapper.canSerialize(Person.class));
// Json 写入文件
mapper.writeValue(newFile, person);
// pojo -> Json String
String jsonString = mapper.writeValueAsString(person);
System.out.println(jsonString);
}

output:

true
{"age":18,"fullName":"V0WKeep3r","dateOfBirth":1609399813373}

Jackson注解
与fastjson类似,Jackson也可以通过注解的方式对java类的filed进行设置,从而使其在序列化成json字符串时,呈现不同的形式。
1、 @JsonPropertyOrder({ "name", "id" })注解可以指定不同属性的输出顺序

@JsonPropertyOrder({ "fullName", "age" , "dateOfBirth"})
public class Person {
private int age;
private String fullName;
private Date dateOfBirth;
...
}

// output
{"fullName":"V0WKeep3r","age":18,"dateOfBirth":1609400550595}

2、@JsonValue指示库将用于序列化整个实例的单个方法。

例如,在一个枚举中,我们用@JsonValue注释getName,以便任何这样的实体都通过其名称序列化:

public enum TypeEnumWithValue {
TYPE1(1, "Type A"), TYPE2(2, "Type 2");

private Integer id;
private String name;

// standard constructors

@JsonValue
public String getName() {
return name;
}
}

现在这是我们的测试:

@Test
public void whenSerializingUsingJsonValue_thenCorrect()
throws JsonParseException, IOException {

String enumAsString = new ObjectMapper()
.writeValueAsString(TypeEnumWithValue.TYPE1);

assertThat(enumAsString, is(""Type A""));
}

3、**@JsonRootName**
@JsonRootName注释时,如果包裹被启用,以指定的包装中使用的根目录的名称。

{
"id": 1,
"name": "John"
}

它会像这样包装:

{
"User": {
"id": 1,
"name": "John"
}
}

因此,让我们看一个例子。使用@JsonRootName注释,表明这个潜在的包装实体的名称:

@JsonRootName(value = "user")
public class UserWithRoot {
public int id;
public String name;
}

默认情况下,包装器的名称将为类的名称–UserWithRoot。通过使用注释,我们得到了看上去更干净的user

@Test
public void whenSerializingUsingJsonRootName_thenCorrect()
throws JsonProcessingException {

UserWithRoot user = new User(1, "John");

ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);//需要开启这个
String result = mapper.writeValueAsString(user);

assertThat(result, containsString("John"));
assertThat(result, containsString("user"));
}

输出:

{
"user":{
"id":1,
"name":"John"
}
}

同样也可以用于xml的序列化封装,此处不提。
更多类似注解,请参考:https://www.baeldung.com/jackson-annotations

8.3.3 Json 字符串转Java对象

package test.v0w;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.util.Date;

public class Jackson {

@Test
public void test() throws IOException {
ObjectMapper mapper = new ObjectMapper();
Person person = new Person(18, "V0WKeep3r", new Date());
File newFile= new File("/Users/v0w/Desktop/person.json");
// 序列化
// 是否可以序列化
System.out.println(mapper.canSerialize(Person.class));
// Json 写入文件
mapper.writeValue(newFile, person);
// pojo -> Json String
String jsonString = mapper.writeValueAsString(person);
System.out.println(jsonString);

// 反序列化
// 从文件反序列化
Person p1 = mapper.readValue(new File("/Users/v0w/Desktop/person.json"), Person.class);
System.out.println(p1);
// 从json串反序列化
Person p2 = mapper.readValue(jsonString, Person.class);
System.out.println(p2);

// json Tree解析 此方式存在多种取数据的方法
JsonNode root = mapper.readTree(newFile);
System.out.println(root);
System.out.println(root.get("age"));
System.out.println(root.at("/fullName"));
System.out.println(root.findValue("dateOfBirth"));
}
}

output:

true
{"fullName":"V0WKeep3r","age":18,"dateOfBirth":1609400550595}
Person{age=18, fullName='V0WKeep3r', dateOfBirth=Thu Dec 31 15:42:30 CST 2020}
Person{age=18, fullName='V0WKeep3r', dateOfBirth=Thu Dec 31 15:42:30 CST 2020}
{"fullName":"V0WKeep3r","age":18,"dateOfBirth":1609400550595}
18
"V0WKeep3r"
1609400550595

8.4 Gson

Gson是Google开源的一个JSON库,被广泛应用在Android开发中。

maven pom.xml

<!-- Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>

创建Gson对象
Gson提供了两种创建对象的方式:

  1. 直接使用Gson构造方法创建:Gson gson = new Gson();
  2. 使用GsonBuilder创建:Gson gson = new GsonBuilder().create();

相比直接使用构造方法,GsonBuilder创建的方式更灵活,因为它支持对Gson的配置。

将对象转换为JSON

package test.v0w;

import com.google.gson.*;
import org.junit.Test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;

public class GsonTest {

@Test
public void test() throws IOException {
Person person = new Person(18, "V0Wkeep3r", new Date());
Gson gson = new GsonBuilder().create();
String jsonString = gson.toJson(person);
System.out.println(jsonString);

ArrayList<Person> accountList = new ArrayList<Person>();
accountList.add(person);
System.out.println(gson.toJson(accountList));

}

}

结果:

{"age":18,"fullName":"V0Wkeep3r","dateOfBirth":"Dec 31, 2020 4:34:41 PM"}
[{"age":18,"fullName":"V0Wkeep3r","dateOfBirth":"Dec 31, 2020 4:34:41 PM"}]

将JSON转换为对象

由于Java中的泛型存在类型擦除的问题,所以使用泛型接收JSON解析结果的时候有点特殊。

普通对象解析

@Test
public void Test_fromJson(){
Gson gson = new GsonBuilder().create();
String JsonString = "{\"age\":18,\"fullName\":\"V0Wkeep3r\",\"dateOfBirth\":\"Dec 31, 2020 4:34:41 PM\"}";
Person person = gson.fromJson(JsonString, Person.class);
System.out.println(person);
}

结果:

Person{age=18, fullName='V0Wkeep3r', dateOfBirth=Thu Dec 31 16:34:41 CST 2020}

泛型对象解析

@Test
public void Test_fromJson2(){
Gson gson = new GsonBuilder().create();
String listJson = "[{\"age\":18,\"fullName\":\"V0Wkeep3r\",\"dateOfBirth\":\"Dec 31, 2020 4:34:41 PM\"}]";
List personList = gson.fromJson(listJson, new TypeToken<List<Person>>(){}.getType()); // new TypeToken<List<Person>>(){}.getType()
System.out.println("PersonList size = " + personList.size());
System.out.println("PersonList content = "+personList);
}

结果:

PersonList size = 1
PersonList content = [Person{age=18, fullName='V0Wkeep3r', dateOfBirth=Thu Dec 31 16:34:41 CST 2020}]

字段复用-@SerializedName注解进行重新命名
在开发中有时会对Bean对象进行复用,但可能有几个字段的命名和当前的对象不一致,这样在解析JSON的时候就不能正确赋值。Gson提供了字段复用功能——@SerializedName,可用一个字段接收不同的JSON字段。

// json字符串中手机号的字段为phone或telNumber时都可正确解析
@SerializedName("phone")
private String telNumber;

// json字符串中用户名的字段为userName、user_name、uname或u_name时都可正确解析
@SerializedName(value = "userName", alternate = {"user_name", "uname", "u_name"})
private String userName;

Gson配置

除了以上用法,Gson还提供了丰富的配置选项,包括:空值过滤,字段命名规则,自定义解析器,自定义序列化/反序列化等。

空值问题

Gson默认情况下会过滤空值字段,但有时在提交数据给后台时,即便字段为空,也需要传给后台,此时可通过GsonBuilder进行配置。

@Test
public void Test_null(){
Gson gson = new GsonBuilder().serializeNulls().create();
Person account = new Person(99, "Freeman");
System.out.println(gson.toJson(account));
}

结果:

{"age":99,"fullName":"Freeman","dateOfBirth":null}

从结果可以看出,dataOfBirth字段被输出了,而前面直接创建Gson转换的时候没有输出dataOfBirth字段。

reference: https://juejin.cn/post/6844903765603336206

0x09 Ajax

通过AJAX Asynchronous JavaScript And XML 实现异步刷新

9.1 用途

在一些需要较为实时响应的情况下使用,比如想检测一个账号是否已经存在。
功能:如果提交的用户名是abc就打印存在,否则就可以使用
想实现这个功能,我们可能需要通过jsp去查或者响应,但是怎么样都不是实时的。

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>

<%
String name = request.getParameter("name");

if("abc".equals(name))
out.print("<font color='red'>已经存在</font>");
else
out.print("<font color='green'>可以使用</font>");

%>
<html>

<a href="https://how2j.cn/study/checkName.jsp?name=abc">checkName.jsp?name=abc</a>
<br>
<a href="https://how2j.cn/study/checkName.jsp?name=def">checkName.jsp?name=def</a>

</html>

而使用Ajax,可以实时响应这个账号是否存在。

<span>输入账号 :</span>
<input id="name" name="name" onkeyup="check()" type="text">
<span id="checkResult"></span>

<script>
var xmlhttp;
function check(){
var name = document.getElementById("name").value;
var url = "https://how2j.cn/study/checkName.jsp?name="+name;

xmlhttp =new XMLHttpRequest();
xmlhttp.onreadystatechange=checkResult; //响应函数
xmlhttp.open("GET",url,true); //设置访问的页面
xmlhttp.send(null); //执行访问
}

function checkResult(){
if (xmlhttp.readyState==4 && xmlhttp.status==200)
document.getElementById('checkResult').innerHTML=xmlhttp.responseText;

}
</script>

9.2 原理与编写

原理图

1. 创建XHR对象 XMLHttpRequest
XHR对象是一个javascript对象,它可以在用户没有感觉的情况下,就像背后运行的一根小线程一般,悄悄的和服务器进行数据交互
AJAX就是通过它做到无刷新效果的。

2. 设置响应函数
XHR对象的作用是和服务器进行交互,所以既会发消息给服务器,也能接受服务器返回的响应。
当服务器响应回来的时候,调用怎么处理呢?
通过 xmlhttp.onreadystatechange=checkResult 就可以指定用checkResult 函数进行处理。

3. 设置并发出请求
通过open函数设置背后的这个小线程,将要访问的页面url ,在本例中就是
/study/checkName.jsp

xmlhttp.open("GET",url,true);

通过send函数进行实际的访问

xmlhttp.send(null);

null表示没有参数,因为参数已经通过”GET” 方式,放在url里了。
只有在用”POST”,并且需要发送参数的时候,才会使用到send。
类似这样:

xmlhttp.send("user="+username+"&password="+password)

4. 处理响应信息
在checkResult 函数中处理响应

function checkResult(){
if (xmlhttp.readyState==4 && xmlhttp.status==200)
document.getElementById('checkResult').innerHTML=xmlhttp.responseText;
}

xmlhttp.readyState 4 表示请求已完成
xmlhttp.status 200 表示响应成功
xmlhttp.responseText; 用于获取服务端传回来的文本
document.getElementById('checkResult').innerHTML 设置span的内容为服务端传递回来的文本.

文章作者: V0WKeep3r
文章链接: http://v0w.top/2020/12/30/JavaWebNote/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 V0W's Blog
支付宝打赏
微信打赏