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
<Connector port ="8080" protocol ="HTTP/1.1" connectionTimeout ="20000" redirectPort ="8443" /> <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
添加之后保存并,执行以下命令使配置生效:
查看配置是否生效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
在IDEA中的配置:
使用archtype
时,创建的webapp maven
项目:
不使用archtype
时,创建的默认的maven项目:
2.5 IDEA 配置tomcat
为什么会有这个问题:我们访问一个网站需要指定一个文件夹名称
解决警告问题。
tomcat虚拟访问路径的配置
2.6 pom.xml文件 pom.xml是maven的核心配置文件: pom.xml文件结构和各部分功能如下:
对于dependency,可以体现maven的高级之处:不仅会导入dependecy中明确配置的jar包,还会自动导入其依赖的其他jar包。
maven由于约定大于配置 的性质,之后我们写的配置文件可能存在无法导出或者无法生效的问题。
解决方案: 配置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程序,分为两步:
编写一个java类,实现Servlet接口
将编写好的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 { PrintWriter writer = resp.getWriter(); writer.println("Hello, Servlet" ); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
3、编写映射 为什么需要映射:我们写的是java程序,浏览器需要连接web服务器,我们需要在web服务器注册写的servlet,并且给浏览器一个可以访问的路径。
在web.xml中增加servlet的注册和路径映射
<servlet > <servlet-name > hello</servlet-name > <servlet-class > top.v0w.servlet.HelloServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > hello</servlet-name > <url-pattern > /hello</url-pattern > </servlet-mapping >
4、访问测试
3.3 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 { String realPath = "/Users/v0w/Desktop/test.jpg" ; System.out.println("path: " +realPath); String filename = realPath.substring(realPath.lastIndexOf("/" )+1 ); resp.setHeader("Content-Disposition" , "attachment;filename=" + URLEncoder.encode(filename, "utf-8" )); FileInputStream in = new FileInputStream(realPath); int len = 0 ; byte [] buffer = new byte [1024 ]; ServletOutputStream out = resp.getOutputStream(); 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 )+"" ; 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 ;
3.8 Cookie 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); } }
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(); 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失效:
可以在web程序的web.xml中设置session时间等配置,也可以在Tomcat等容器的web.xml中设置session时间。
<session-config > <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 () public void _jspService (final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
判断请求类型
内置一些对象
final javax.servlet.jsp.PageContext pageContext;final javax.servlet.ServletContext application;final javax.servlet.ServletConfig config;javax.servlet.jsp.JspWriter out = null ; final java.lang.Object page = this ;javax.servlet.jsp.JspWriter _jspx_out = null ; javax.servlet.jsp.PageContext _jspx_page_context = null ;
输出页面增加的代码
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;
以上对象可以在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表达式 依赖
<dependency > <groupId > javax.servlet.jsp</groupId > <artifactId > javax.servlet.jsp-api</artifactId > <version > 2.3.3</version > <scope > provided</scope > </dependency > <dependency > <groupId > javax.servlet.jsp.jstl</groupId > <artifactId > jstl</artifactId > <version > 1.2</version > </dependency > <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
<dependency > <groupId > javax.servlet.jsp.jstl</groupId > <artifactId > jstl</artifactId > <version > 1.2</version > </dependency > <dependency > <groupId > taglibs</groupId > <artifactId > standard</artifactId > <version > 1.1.2</version > </dependency > <dependency > <groupId > javax.servlet.jsp.jstl</groupId > <artifactId > jstl-api</artifactId > <version > 1.2</version > </dependency > <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只需要
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
Controller
接受用户请求(req用户请求,Session信息…)
交给业务层处理对应的代码
控制试图的跳转
实现一个CRUD操作数据库Hero的小项目: 文件结构:
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 { @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 执行后" ); } @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 { @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); } @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; @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; } }
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序列化时,字段的不同行为: 比如自定义输出,控制字段的排序,日期显示格式,序列化标记等。
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 提供了更为丰富便捷的方法,方便我们对于对象属性的操作。我们看一下源码。
@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(), 0 ); assertEquals(newPerson.getFullName(), listOfPersons.get(0 ).getFullName()); }
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还包含了其它数据处理类库,此外不作说明。
maven pom.xml
<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)); mapper.writeValue(newFile, person); 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; ... } {"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; @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)); mapper.writeValue(newFile, person); 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); Person p2 = mapper.readValue(jsonString, Person.class); System.out.println(p2); 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
<dependency > <groupId > com.google.code.gson</groupId > <artifactId > gson</artifactId > <version > 2.8.5</version > </dependency >
创建Gson对象 Gson提供了两种创建对象的方式:
直接使用Gson构造方法创建:Gson gson = new Gson();
使用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()); 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函数进行实际的访问
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的内容为服务端传递回来的文本.