1. servlet接值
概念: servlet使用 request 对象来接收请求路径和请求头中的数据:
void setCharacterEncoding("UTF-8"):设置请求编码。String getParameter(String name):通过name值接收单个请求参数。String[] getParameterValues(String name):通过name值接收多个请求参数。String getRequestURI():获取请求路径,不包括协议,IP和端口。StringBuffer getRequestURL:获取请求全路径,包括协议,IP和端口。String getServerName():获取服务器IP地址,如localhost。Int getServerPort():获取端口号,如8080。String getContextPath():获取项目名称,如/servlet3。String getServletPath():获取servlet路由,如/api/servlet/request。String getQueryString():获取查询串,如a=1&b=2。String getHeader(String name):通过name值获取指定的请求头信息。Cookie[] getCookies():获取请求头中的所有Cookie信息。- psm:
{{tomcat}}/servlet3/api/request?username=赵四&id=1&id=2
源码: RequestServlet.java
/**
* @author yap
*/
@WebServlet("/api/request")
public class RequestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
System.out.println("param: " + req.getParameter("username"));
System.out.println("params: " + Arrays.toString(req.getParameterValues("id")));
System.out.println("uri: " + req.getRequestURI());
System.out.println("url: " + req.getRequestURL());
System.out.println("serverName: " + req.getServerName());
System.out.println("serverPort: " + req.getServerPort());
System.out.println("contextPath: " + req.getContextPath());
System.out.println("servletPath: " + req.getServletPath());
System.out.println("queryString: " + req.getQueryString());
System.out.println("userAgent: " + req.getHeader("User-Agent"));
for (Cookie cookie : req.getCookies()) {
System.out.println(cookie.getName() + "-" + cookie.getValue());
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
2. servlet调用
概念: MVC架构就是对软件进行分层以达到解耦的目的:
- 视图层
view:只负责搭建前端页面:- 通过表单,超链接,AJAX等方式向控制层传递数据。
- 控制层
controller:只负责与视图层交互数据:- 接:接收视图层的请求相关数据。
- 调:调用业务层方法。
- 存:存储相关数据。
- 转:对视图层的请求做出响应。
- 模型层
model:从控制层分离出来的java类:- 实体类
pojo:用于映射数据库对象,存储和传输数据。 - 业务类
service:只负责对数据进行业务逻辑处理,面向接口开发。 - 数据类
dao:利用JDBC与数据库交互数据,面向接口开发。
- 实体类
- MVC架构案例-登陆:
- 数据库开发:数据库,账户,表,测试数据...
- JDBC开发:创建maven项目
pro-jdbc,install本地仓库。 - servlet3项目依赖:引入servlet,lombok和jdbc依赖。
- pojo开发:对应数据表的实体类。
- dao开发:开发登陆方法,测试。
- service开发:开发登陆业务方法,测试。
- controller开发:开发登陆接口方法。
- view开发:编写视图层界面。
- psm:
{{tomcat}}/api/account?meta=login&username=admin
MySQL
配置: 数据库名servlet 表名account.sql
CREATE SCHEMA SERVLET DEFAULT CHARSET UTF8MB4;
USE SERVLET;
DROP TABLE IF EXISTS ACCOUNT;
CREATE TABLE ACCOUNT
(
ID INT AUTO_INCREMENT COMMENT '主键',
USERNAME VARCHAR(50) NOT NULL COMMENT '账号' UNIQUE,
PASSWORD VARCHAR(50) NULL COMMENT '密码',
PRIMARY KEY (ID)
)
COMMENT '账号表';
INSERT INTO ACCOUNT (USERNAME, PASSWORD)
VALUES ('admin', ''),
('zhaosi', '123'),
('liuneng', '456');
COMMIT;
servlet3项目依赖
配置: pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.servlet3</groupId>
<artifactId>z4-servlet3</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--自己配置的jdbc maven包-->
<dependency>
<groupId>com.yap</groupId>
<artifactId>pro-jdbc</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>true</scope>
</dependency>
</dependencies>
</project>
配置: resources/db.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/servlet?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
jdbc.connectionPoolSize=10
pojo开发
源码: com.yap.work.pojo.Account.java
/**
* @Author Yap
*/
@Data
public class Account implements Serializable {
private Integer id;
private String username;
private String password;
}
dao开发
源码: com.yap.work.dao.AccountDao.java
/**
* @author yap
*/
public interface AccountDao {
/**
* 查询全部的账号信息
*
* @return 返回全部的账号信息
*/
List<Map<String, Object>> queryForList();
/**
* 通过账号查询信息
*
* @param username 账号信息
* @return 对应账号信息的一条结果
*/
Map<String, Object> queryByUsername(String username);
}
源码: com.yap.work.dao.AccountDaoImpl.java
/**
* @Author Yap
*/
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate = new JdbcTemplate();
@Override
public List<Map<String, Object>> queryForList() {
String sql = "SELECT ID, USERNAME, PASSWORD FROM ACCOUNT";
return jdbcTemplate.queryForList(sql);
}
@Override
public Map<String, Object> queryByUsername(String username) {
String sql = "SELECT ID, USERNAME, PASSWORD FROM ACCOUNT WHERE USERNAME = ?";
return jdbcTemplate.queryForMap(sql,username);
}
}
service开发
源码: com.yap.work.service.AccountServiceMap.java
/**
* @author yap
*/
public interface AccountServiceMap {
/**
*登陆业务 简查登陆是否成功
*
* @param username 前端传过来的账号
* @param password 前端传过来的密码
* @return 如果登录成功返回true,否则返回false
*/
boolean login(String username,String password);
}
源码: com.yap.work.service.impl.AccountServiceMapImpl.java
import com.yap.work.dao.AccountDao;
import com.yap.work.dao.impl.AccountDaoImpl;
import com.yap.work.service.AccountServiceMap;
import java.util.Map;
/**
* @Author Yap
*/
public class AccountServiceMapImpl implements AccountServiceMap {
private AccountDao accountDao = new AccountDaoImpl();
@Override
public boolean login(String username, String password) {
if (isNull(username) || password == null) {
return false;
}
Map<String, Object> userFromMysql = accountDao.queryByUsername(username);
if (userFromMysql != null && !userFromMysql.isEmpty()) {
return userFromMysql.get("password").equals(password);
}
return false;
}
private boolean isNull(String... strings) {
for (String str : strings) {
if (str == null || "".equals(str)) {
return true;
}
}
return false;
}
}
controller开发
源码: com.yap.work.servlet.AccountServlet.java
import com.yap.work.service.impl.AccountServiceMapImpl;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author yap
*/
@WebServlet("/api/account")
public class AccountServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
req.setCharacterEncoding("UTF-8");
final String LOGIN = "login";
String meta = req.getParameter("meta");
if (LOGIN.equals(meta)) {
login(req, resp);
}
}
private void login(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
boolean result = new AccountServiceMapImpl().login(username, password);
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println(result ? username + " 登陆成功..." : username + " 登陆失败...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
this.doGet(req, resp);
}
}
view开发
web: html/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base href="/servlet3/">
</head>
<body>
<section>
<form action="api/account" method="post">
<input type="hidden" name="meta" value="login">
<label for="username-ipt">账号:</label>
<input id="username-ipt" name="username">
<label for="password-ipt">密码:</label>
<input id="password-ipt" name="password">
<button type="submit">登录</button>
</form>
</section>
</body>
</html>
测试
源码: com.yap.dao.AccountDaoTest.java
/**
* @Author Yap
*/
public class AccountDaoTest {
@Test
public void queryForList() {
AccountDao accountDao = new AccountDaoImpl();
System.out.println(accountDao.queryForList());
}
}
源码: com.yap.service.AccountServiceMapImplTest.java
/**
* @Author Yap
*/
public class AccountServiceMapImplTest {
private AccountServiceMap accountServiceMap = new AccountServiceMapImpl();
@Test
public void login() {
String username = null;
String password = null;
System.out.println(accountServiceMap.login(username, password));
username = "";
password = "";
System.out.println(accountServiceMap.login(username, password));
username = "admin";
password = "";
System.out.println(accountServiceMap.login(username, password));
username = "";
password = "admin";
System.out.println(accountServiceMap.login(username, password));
username = "zhaosi";
password = "123";
System.out.println(accountServiceMap.login(username, password));
}
}
3. servlet存值
概念: servlet中使用 request,session 和 application 可以进行存取值操作,三者API方法一致,生命周期不同:
- 通用API方法:
setAttribute(K, V):以键值对的方式存值,同名覆盖。getAttribute(K):通过K取出对应的V,不存在返回null。
- request域:时效为一次请求,请求得到响应后,立即死亡。
- session域:时效为一次会话,浏览器关闭且在失效时间内无任何请求访问它时死亡:
req.getSession():通过请求获取HttpSession对象,若不存在则创建一个新的并返回。req.getSession(true):等效于getSession()。req.getSession(false):通过请求获取session,若不存在返回null。session.isNew():返回session是否是最新创建的。session.getId():返回sessionId。session.setMaxInactiveInterval():设置失效时间间隔-秒,-1表示session永生。session.getMaxInactiveInterval():查看失效时间间隔-秒,默认1800秒。session.invalidate():session立刻失效,等效于setMaxInactiveInterval(0)。
- application域:时效为一次应用,服务器重启,崩溃,关闭时死亡:
req.getServletContext():通过请求获取ServletContext对象。
也可以在
web.xml中指定<session-config>和<session-timeout>来设置session失效时间间隔-秒。
源码: StoreServlet.java
/**
* @author yap
*/
@WebServlet("/api/store")
public class StoreServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
final String REQUEST = "request";
final String SESSION = "session";
final String APPLICATION = "application";
String meta = req.getParameter("meta");
if (REQUEST.equals(meta)) {
request(req, resp);
} else if (SESSION.equals(meta)) {
session(req, resp);
} else if (APPLICATION.equals(meta)) {
application(req, resp);
}
}
private void request(HttpServletRequest req, HttpServletResponse resp) throws IOException {
req.setAttribute("req-name", "req-value-1");
req.setAttribute("req-name", "req-value-2");
System.out.println(req.getAttribute("req-name"));
System.out.println(req.getAttribute("abc"));
resp.getWriter().print("request: " + req.getAttribute("req-name"));
}
private void session(HttpServletRequest req, HttpServletResponse resp) throws IOException {
HttpSession session = req.getSession();
System.out.println(session.isNew() ? "session is new..." : "session is old...");
System.out.println("sessionId: " + session.getId());
System.out.println("inactiveInterval: " + session.getMaxInactiveInterval());
session.setMaxInactiveInterval(3600);
System.out.println("inactiveInterval: " + session.getMaxInactiveInterval());
session.setAttribute("session-name", "session-value-1");
session.setAttribute("session-name", "session-value-2");
System.out.println(session.getAttribute("session-name"));
System.out.println(session.getAttribute("abc"));
resp.getWriter().print("session: " + session.getAttribute("session-name"));
}
private void application(HttpServletRequest req, HttpServletResponse resp) throws IOException {
ServletContext application = req.getServletContext();
application.setAttribute("application-name", "application-value-1");
application.setAttribute("application-name", "application-value-2");
System.out.println(application.getAttribute("application-name"));
System.out.println(application.getAttribute("abc"));
resp.getWriter().print("application: " + application.getAttribute("application-name"));
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
3.1 session长连接原理
概念: HTTP协议是无状态的,每个请求对它来说都是新的,若需要长久保存数据,建议使用session域:
- 当session被创建时,会生成一个
sessionID,并响应到浏览器的cookie中存储起来。 - 当前浏览器以后的每一个请求都会携带这个
sessionID信息。 - 当web容器看到
sessionID后,会去寻找对应的session,并将其和本次请求关联起来。 - 所以不同的电脑,或者同一台电脑中的不同的浏览器都是不能共享session的。
3.2 存值域线程安全问题
概念: web容器为每个请求分配一个线程,这些线程是并发执行的,会产生线程安全问题:
- 线程安全问题:
- A线程进来
session.setAttribute("money", 100) - A线程挂起...
- B线程进来
session.setAttribute("money", 200) - B线程挂起...
- A线程苏醒,调用
session.getAttribute("money"),得到的居然是200
- A线程进来
- session域和application域中存储的资源都是线程不安全的,建议同步加锁,以效率换取安全:
- 不能使用this锁,因为它只能阻止同一个servlet中的不同线程,但不能阻止不同的servlet的线程。
- 不能在
doGet()或doPost()上加锁,因为非静态方法的锁也是this锁。 - 建议使用锁类型为session或application对象本身。
- request域是线程安全的,request中存储的资源在请求时就被销毁了:
- 请求属性和方法局部变量是servlet中线程最安全的位置。
- STM设计模式:该设计模式保证一个servlet一次只处理一个请求,效果和同步
doGet()/doPost()一样,使用方法很简单,只要让servlet实现SingleThreadModel接口即可,它有两种策略,一种是让请求排队,一种是维护servlet实例池,并发处理请求,具体使用哪种策略取决于容器开发商,但这两种策略,前者牺牲太多效率,后者违反了servlet的单例规范,所以目前STM已经被ServletAPI废弃掉了,对于STM,它早已退出江湖,而你只需要知道,这名战士,它曾经试图保护过servlet属性的线程安全,就足够了。
源码: SyncServlet.java
/**
* @author yap
*/
@WebServlet("/api/sync")
public class SyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
synchronized (session) {
session.setAttribute("money", 100);
}
ServletContext application = req.getServletContext();
synchronized (application) {
application.setAttribute("money", 200);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
4. servlet转页
概念: 转页方式指的就是响应方式,比如请求转发,重定向,回写JSON等:
- response常用API方法:
addCookie():添加一个Cookie对象,web容器会将其回写到客户端Cookie中。addHeader()/setHeader():添加/修改一个String类型的响应头数据。addIntHeader()/setIntHeader():添加/修改一个int类型的响应头数据。addDateHeader()/setDateHeader():添加/修改一个时间戳类型的响应头数据。setContentLength():设置响应数据的大小,覆盖真实数据大小。setContentType():设置响应的MIME数据类型和编码。setStatus():设置成功时的响应码状态,建议2345开头三位数字。getOutputStream():获取响应字节流ServletOutputStream实例。getWriter():获取响应字节流PrintWriter实例。sendRedirect():用于请求重定向,等效于响应头中的location字段。containsHeader():判断是否含有指定的响应头数据。
- 请求转发与重定向:
- 请求转发: 一次请求,URL无变化,效率高,只需从
webapp下开始设置后半段路径。 - 重定向: 二次请求,URL有变化,效率低,必须从项目发布名开始设置路径。
- psm:
{{tomcat}}/servlet3/api/response源码: ResponseServlet.java
- 请求转发: 一次请求,URL无变化,效率高,只需从
/**
* @author yap
*/
@WebServlet("/api/response")
public class ResponseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Cookie cookie = new Cookie("cookie-name", "cookie-value");
cookie.setMaxAge(60 * 5);
resp.addCookie(cookie);
System.out.println(resp.containsHeader("string-header"));
resp.addHeader("string-header", "string-header");
System.out.println(resp.containsHeader("string-header"));
resp.addIntHeader("int-header", 9527);
resp.addDateHeader("date-header", System.currentTimeMillis());
resp.setContentLength(1);
resp.setStatus(200);
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().print("response-data");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
4.1 请求转发
概念: 请求转发就是servlet将响应HTML页面的工作转发给另一个servlet或HTML,并支付 request 和 response 对象,最终返回静态HTML数据:
- 获取请求转发器
RequestDispatcher实例:req.getRequestDispatcher()- 形参传入转发的目标地址,即接手人的路径,约定以
/开头。
- 形参传入转发的目标地址,即接手人的路径,约定以
- 请求转发器转发的时候需要接手
request和response参数:forward():表示永久将请求转交,此后均为死代码,抛出IllegalStateException异常。include():表示暂时将请求转交,它们工作结束后再接管回来,由自己完成请求和响应的最后处理,如再次使用另一个include()或forward()等,实际开发中一般不使用。
- psm:
{{tomcat}}/servlet3/api/forward源码: ForwardServlet.java
/**
* @author yap
*/
@WebServlet("/api/forward")
public class ForwardServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
System.out.println("转发给index.html");
req.getRequestDispatcher("/index.html").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
4.2 重定向
概念: 重定向主要使用场景是避免请求重复转发导致错误数据:
- 重定向防刷新原理:
- 请求转发时:我向班长发送
想吃一个面包请求,班长会响应给我一个面包,但我因口吃说了十遍(页面刷新),于是得到十个面包,数据错误。 - 重定向时:我向班长发送
想吃一个面包请求,班长会响应给我面包店地址,但我因口吃说了十遍(页面刷新),于是得到十次地址,然后我按照地址自己去买,最终只得到一个面包,数据正常。
- 请求转发时:我向班长发送
- 通过响应发送重定向:
resp.sendRedirect()- 需要指定完整的目标路径,建议使用
req.getContextPath()动态拼接项目发布名。
- 需要指定完整的目标路径,建议使用
- psm:
{{tomcat}}/servlet3/api/redirect源码: RedirectServlet.java
/**
* @author yap
*/
@WebServlet("/api/redirect")
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
System.out.println("重定向到/index.html");
resp.sendRedirect(req.getContextPath() + "/index.html");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
4.3 AJAX响应
概念: AJAX请求因异步而无法使用请求转发和重定向,仅支持响应流回写数据:
- AJAX请求的请求头额外携带
x-requested-with=XMLHttpRequest信息。 - 对应AJAX请求响应的最佳数据格式是JSON字符串,注意KEY值必须添加双引号。
- 响应JSON数据时建议设置响应MIME类型为
application/json,此时AJAX可以直接得到一个JSON格式的数据,而省去解析JSON字符串的过程。 - psm:
{{tomcat}}/servlet3/api/ajax- 请求头添加
x-requested-with = XMLHttpRequest模拟AJAX请求 - GET方式:直接使用查询串传递
meta=ajax_type - POST方式:在
Body/x-www-form-urlencoded传递meta=ajax_type源码: com.yap.work.servlet.AjaxServlet.java
- 请求头添加
/**
* @author yap
*/
@WebServlet("/api/ajax")
public class AjaxServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
req.setCharacterEncoding("UTF-8");
String meta = req.getParameter("meta");
if (Meta.AJAX_TYPE.equals(meta)) {
ajaxType(req, resp);
}
}
interface Meta {
String AJAX_TYPE = "ajax_type";
String XML_HTTP_REQUEST = "XMLHttpRequest";
}
private void ajaxType(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
String xRequestedWith = req.getHeader("x-requested-with");
if (xRequestedWith != null && xRequestedWith.equals(Meta.XML_HTTP_REQUEST)) {
System.out.println("本次请求是AJAX请求,回写JSON数据...");
resp.setContentType("application/json;charset=UTF-8");
String jsonStr = "{\"username\":\"admin\",\"password\":\"123\"}";
resp.getWriter().print(jsonStr);
} else {
System.out.println("本次请求不是AJAX请求,转发到index.html...");
req.getRequestDispatcher("/index.html").forward(req, resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
5. JSON工具
概念: JSON工具可以将List,Map,Set或者一个单独的Pojo类转换成一个格式准确的JSON字符串。
5.1 jackson
概念: jackson是用来序列化和反序列化JSON的java开源框架,SpringMVC的默认JSON解析器,简单易用,速度快,占用内存低,性能较好,可以扩展和定制:
- 依赖:
jackson-core/jackson-annotations/jackson-databind。 ObjectMapper():构造ObjectMapper核心对象。setSerializationInclusion(JsonInclude.Include incl):设置序列化过程的参与者:- param1:
JsonInclude.Include.NON_NULL:null值不参与序列化。 - return:返回配置后的
ObjectMapper对象。
- param1:
configure(SerializationFeature f, boolean state):设置序列化某些配置的开启或禁止:- param1:
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS:是否将Date序列化成时间戳。 - param2:true表示开启配置,false表示禁止配置。
- return:返回配置后的
ObjectMapper对象。
- param1:
setDateFormat(DateFormat format):设置Date类型数据的序列化格式。- param1:
DataFormat日期模板对象。 - return:返回配置后的
ObjectMapper对象。
- param1:
writeValueAsString(Object obj):将数据序列化为JSON字符串并返回。writeValue(*, Object obj):将第二个参数序列化并通过第一个参数直接完成回写过程:- param1若为
File对象,表示将param2序列化后保存到指定的文件中。 - param1若为
PrintWriter对象,表示将param2序列化后回写到字符输出流中。 - param1若为
ServletOutputStream对象,表示将param2序列化后回写到字节输出流中。
- param1若为
- psm:
{{tomcat}}/servlet3/api/json?meta=jackson源码: com.yap.work.servlet.JsonServlet
/**
* @author yap
*/
@WebServlet("/api/json")
public class JsonServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
req.setCharacterEncoding("UTF-8");
String meta = req.getParameter("meta");
if (Meta.JACKSON.equals(meta)) {
jackson(req, resp);
} else if (Meta.GSON.equals(meta)) {
gson(req, resp);
}
}
interface Meta {
String JACKSON = "jackson";
String GSON = "gson";
}
private Map<String, Object> getMap(int num) {
Map<String, Object> map01 = new HashMap<>(2);
map01.put("map-key-1", "map-val-1");
map01.put("map-null-1", null);
map01.put("map-empty-1", "");
map01.put("map-date-1", new Date());
map01.put("map-gender-1", "1");
Map<String, Object> map02 = new HashMap<>(2);
map02.put("map-key-2", "map-val-2");
map02.put("map-null-2", null);
map02.put("map-empty-2", "");
map02.put("map-date-2", new Date());
map02.put("map-gender-2", "1");
return num == 1 ? map01 : map02;
}
private Account getAccount(int num) {
Account account01 = new Account(9527, "user-1", "pass-1");
Account account02 = new Account(9528, "user-2", "pass-2");
return num == 1 ? account01 : account02;
}
private void jackson(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// new objectMapper
ObjectMapper objectMapper = new ObjectMapper();
// writeValueAsString(): map to json-str
System.out.println(objectMapper.writeValueAsString(this.getMap(1)));
// writeValueAsString(): pojo to json-str
System.out.println(objectMapper.writeValueAsString(this.getAccount(1)));
// writeValueAsString(): list<pojo> to json-str
List<Account> accounts = new ArrayList<>();
accounts.add(this.getAccount(1));
accounts.add(this.getAccount(2));
System.out.println(objectMapper.writeValueAsString(accounts));
// new and config objectMapper
objectMapper = new ObjectMapper()
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.setDateFormat(new SimpleDateFormat("yyyy/MM/dd hh:mm:ss"));
// writeValue(): list<map> to json-str and response
List<Map<String, Object>> list = new ArrayList<>();
list.add(this.getMap(1));
list.add(this.getMap(2));
resp.setContentType("application/json;charset=utf-8");
objectMapper.writeValue(resp.getWriter(), list);
}
private void gson(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Gson gson = new Gson();
// toJson(): map to json-str
System.out.println(gson.toJson(this.getMap(2)));
// toJson(): pojo to json-str
System.out.println(gson.toJson(this.getAccount(2)));
// toJson(): list<pojo> to json-str
List<Account> accounts = new ArrayList<>();
accounts.add(this.getAccount(1));
accounts.add(this.getAccount(2));
System.out.println(gson.toJson(accounts));
// new gson by GsonBuilder
gson = new GsonBuilder()
.serializeNulls()
.setDateFormat("yyyy-MM-dd")
.create();
// toJson(): list<map> to json-str and response
List<Map<String, Object>> list = new ArrayList<>();
list.add(this.getMap(1));
list.add(this.getMap(2));
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().print(gson.toJson(list));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
5.2 GSON
概念: Gson是Google公司发布的序列化和反序列化JSON的java开源框架:
- 依赖:
gson。 Gson():构造Gson核心对象。GsonBuilder():构造GsonBuilder构造器对象。serializeNulls():设置序列化数据中的所有null值:- return:返回配置后的
GsonBuilder对象。
- return:返回配置后的
setDateFormat(String format):设置Date数据格式:- param1:日期格式字符串。
- return:返回配置后的
GsonBuilder对象。
create():通过GsonBuilder构造Gson核心对象。toJson(Object obj):将数据序列化为JSON字符串并返回。- psm:
{{tomcat}}/servlet3/api/json?meta=gson源码:
<!--jackson-core-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.7</version>
</dependency>
<!--jackson-annotations-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.7</version>
</dependency>
<!--jackson-databind-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.7</version>
</dependency>
<!--gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>