课程视频地址:https://www.bilibili.com/video/BV1AS4y177xJ?p=1
课程文档地址:https://heavy_code_industry.gitee.io/code_heavy_industry/pro001-javaweb/lecture/
编写静态页面 使用react脚手架搭建项目
使用 Semi UI框架
1 yarn add @douyinfe/semi-ui
编写 App.js 组件内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 import React, { useEffect } from 'react' ;import { Table, Col, Row } from '@douyinfe/semi-ui' ;import { IconMember } from '@douyinfe/semi-icons' ;import { Button } from '@douyinfe/semi-ui' ;import { Form, useFormApi } from '@douyinfe/semi-ui' ;import "./App.css" const { Column } = Table;function App ( ) { const [data, setData] = React.useState([]) const [formval, setFormVal] = React.useState({}) useEffect(() => { setData(() => { return [ { id: 1 , name: "苹果" , price: '20' , number: '5' , count: '100' }, { id: 2 , name: "香蕉" , price: '10' , number: '6' , count: '60' }, ] }) }, []) const DeleteBtn = (text, record, index ) => { return ( <Button onClick={() => deleteNowRow(record, index)} theme='solid' type='danger' >删除</Button> ); }; const deleteNowRow = (row, index ) => { setData(olddata => { olddata.splice(index, 1 ) return [...olddata] }) } const syncValidate = (values ) => { const errors = {}; if (!values.name) { errors.name = '请输入名称' ; } if (!values.price) { errors.price = '请输入价格' ; } if (!values.number) { errors.number = '请输入数量' ; } return errors; } const ComponentUsingFormApi = () => { const formApi = useFormApi(); const addVal = () => { let error = syncValidate(formval) if (!error.name) { formval.count = +formval.price * +formval.number setData(oldval => [...oldval, { id : oldval.length + 1 , ...formval }]) resetVal() } } const resetVal = () => { formApi.reset() }; return ( <> <Button theme='solid' htmlType="submit" type='secondary' onClick={addVal}>添加</Button> <Button theme='solid' type='warning' onClick={resetVal} style={{ marginLeft : 8 }}>重置</Button> </> ); }; return ( <> <Row> <Col span={20 } offset={2 } className="app-title" > <IconMember /> <h3>商品管理系统</h3> </Col> </Row> <Row> <Col span={20 } offset={2 }> <Table dataSource={data} pagination={false }> <Column title="名称" dataIndex="name" key="name" /> <Column title="价格" dataIndex="price" key="price" /> <Column title="数量" dataIndex="number" key="number" /> <Column title="小计" dataIndex="count" key="count" /> <Column title="操作" dataIndex="operate" key="operate" render={DeleteBtn} /> </Table> </Col> </Row> <Row style={{ marginTop : "15px" }}> <Col span={20 } offset={2 }> <Form initValues={formval} validateFields={syncValidate} onValueChange={value => setFormVal(() => value)} > <Form.Input field='name' label='名称' style={{ width : 266 }} /> <Form.Input field='price' label='价格' style={{ width : 266 }} /> <Form.Input field='number' label='数量' style={{ width : 266 }} /> {} <ComponentUsingFormApi /> </Form> </Col> </Row> </> ); } export default App;
然后再 index.js 中引入 App.js
1 2 3 4 5 6 7 8 9 10 11 12 import React from 'react' ;import ReactDOM from 'react-dom' ;import App from './App' ;import reportWebVitals from './reportWebVitals' ;ReactDOM.render( <App />, document .getElementById('root' ) ); reportWebVitals();
启动项目
效果展示
Tomcat 安装 首先进入官网进行下载
https://tomcat.apache.org/
这里我选择 8 版本
点击后页面往下滑,选择 zip 版本下载,选择压缩包下载不用安装就可使用
下载后解压到文件夹中,注意解压的目录不能有中文和空格 ,解压内容如下
接着点开bin目录,点击 startup.bat 运行即可
打开浏览器,输入 localhost:8080 查看,看到如下页面表示启动成功
部署项目到Tomcat 打开 Tomcat 解压目录中的 webapps 文件夹
打开 ROOT 文件夹
保留 WEB-INF 文件夹,其余文件全部替换为我们要部署的前端项目文件
重新运行 startup.bat,再次输入 localhost:8080
下载 Tomcat9 由于我使用的 java jdk版本是 java17,所以继续使用 tomcat8 的话会有问题
在 idea 中使用 tomcat 首先新建 Java 项目,默认创建 Java 项目不包含 Web 工程,我们需要手动创建。在项目名称上右键选择 Add Framework Support
选择 Web Applocation,之后点击OK
点击OK后,项目会自动创建一个 WEB 文件夹
然后在和 WEB-INF 平级新建一个 hello.html,同时删除 index.jsp
接着点击右上角的 Add Configuration
在弹出的框中点击左上角 + 号
点开后往下滑选择 Tomcat Server 下面的 Local
在打开的弹框中点击 Configure
选择 tomcat9
接着点击 Deployment
然后再点回 Server
配置完成后会有一个可用的tomcat服务,点击启动debug模式运行
等待服务启动
启动成功自动打开浏览器,发现出现 404 错误,这是因为我们创建的 html 文件名字是 hello.html,需要手动的在地址后面添加 hello.html 即可
现在我们手动的在地址后面添加 hello.html 再次访问
修改代码。刷新页面即可实时查看改动效果
那么怎么样能自动打开就是加上 hello.html 后缀呢。点击右上角的编辑配置
修改启动地址即可
重新启动即可自动打开 hello.html
使用 HttpServlet 接收表单参数 首先修改 hello.html 代码,添加 from 表单提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <form method ="post" action ="add" > 名称:<input type ="text" name ="name" /> <br /> 价格:<input type ="text" name ="price" /> <br /> 数量:<input type ="text" name ="number" /> <br /> <input type ="submit" value ="添加" /> </form > </body > </html >
导入 tomcat 依赖 接着引入 HttpServlet 包,这个包不在 java的 jdk 中,需要额外引入,点击 File,选择 Project Structure
如图
点开 + 号,选择 Library
选择 tomcat9 导入
点击应用
此时项目中就有 tomcat9 的依赖包
编写接收方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.songzx.javaweb01;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class addServer extends HttpServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name" ); String price = req.getParameter("price" ); String number = req.getParameter("number" ); System.out.println(name); System.out.println(price); System.out.println(number); } }
配置 xml 参数
执行过程
用户发送请求,action = add
项目中 web.xml 中找到 url-pattern = /add,对应第12行
找到第11行的 servlet-name = addServer
找到和 servlet-mapping 中 servlet-name 一致的 servlet,对应第7行
然后找到第8行地址所对应的 addServer 类
接收用户发送过来的请求
效果演示 启动项目后点击表单提交,可能出现如下错误
这时我们要检查环境变量 JAVA_HOME 对应的值是否是 java jdk 的低版本,我这里原来是 jdk8.0,修改为 jdk17
然后修改 tomcat 配置,把 jre 同步修改为 jdk17 版本
再次启动查看效果
导入jdbc完成数据插入到数据库 首先在 web -> WEB-INF 文件夹下新建 lib 文件夹,依次导入如下 jar 包
commons-dbutils-1.7.jar (操作数据库的第三jar包)
druid-1.1.10.jar (Druid连接池)
mysql-connector-java-8.0.28.jar(MySQL数据库驱动)
然后再项目上右键新建文件夹 resource
,文件夹的名称固定为这个,然后再该文件夹上右键选择 Mark Directory –> Sources Root
接着在此文件夹下新建配置文件 druid.properties
,
编写连接数据库的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class JdbcUtils { public static DataSource source = null ; static { try { InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties" ); Properties prop = new Properties(); prop.load(is); source = DruidDataSourceFactory.createDataSource(prop); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection () throws SQLException { Connection conn = source.getConnection(); return conn; } public static void closeConnection (Connection conn, Statement sta) { DbUtils.closeQuietly(conn); DbUtils.closeQuietly(sta); } }
修改 doPost 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class exer01 extends HttpServlet { @Override public void doPost (HttpServletRequest req, HttpServletResponse resp) throws IOException { Connection conn = null ; try { String name = req.getParameter("name" ); String price = req.getParameter("price" ); String number = req.getParameter("number" ); double aDouble = Double.parseDouble(price); int anInt = Integer.parseInt(number); conn = JdbcUtils.getConnection(); String sql = "insert into commodity(name,price,number) values (?,?,?)" ; QueryRunner runner = new QueryRunner(); int isOk = runner.update(conn, sql, name, aDouble, anInt); System.out.println(isOk > 0 ? "插入成功" : "插入失败" ); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,null ); } } }
重新启动,填写表单后点击提交
查询数据库,数据成功进入到表中
处理post接收中文乱码问题 如图,当表单填写中文后会出现乱码问题
解决方法:在 doPost 方法首行添加如下代码
1 req.setCharacterEncoding("utf-8" );
设置字符串的格式为 utf-8 的格式,再次发送中文信息
Server 的生命周期
init 初始化
service 服务中
destroy 销毁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class exer02 extends HttpServlet { public exer02 () { System.out.println("创建实例..." ); } @Override public void init () throws ServletException { System.out.println("初始化..." ); } @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("正在服务..." ); } @Override public void destroy () { System.out.println("实例销毁" ); } }
启动Tomcat服务器后,Tomcat会通过返回帮助我们创建类的实例对象,然后先调用 init 方法进行初始化,然后调用 service 方法判断请求方式,当关闭Tomcat服务器后调用 destory 方法销毁实例
默认情况下,会在第一次请求时同时进行初始化和服务操作,第二次请求开始之后不会再次初始化。所以这种模式是单例的,但同时也是线程不安全的
再第一次请求时进行初始化的优缺点:
优点:提高程序启动的速度
缺点:第一次请求时效率低。可以通过修改 servlet 的初始化时机来提交第一次访问的效率
修改 Servlet 的初始化时机 在 web.xml 中添加 load-on-startup
,这个值越小,实例创建的时机越早.最小不能低于0
启动服务后,观察控制台输出
当进行第一次访问时直接就是正在服务,从而不会再第一步请求时创建实例和初始化
Http的请求和响应 介绍 Http 被称为超文本传输协议
请求 请求包含下面三个部分
请求行
请求头
浏览器版本号
浏览器型号
浏览器可以接收的数据类型
……
请求主体
get 方式:没有请求体,但是有一个 queryString
post方式:有请求体,form data
json格式,有请求体,request payload
响应 响应包含下面的三个信息
响应行
响应头
服务器信息
服务器发给浏览器的内容(内容媒体类型、编码、长度等)
响应体
Http无状态 服务器无法判断两次请求是否来自不同的浏览器或者来自同一个浏览器,这种情况称为Http无状态。可以通过回话跟踪来解决
会话跟踪 简单概述:当浏览器第一次访问服务器时,服务器会自动分配一个 session id 给这个请求。当这个请求第二次来访问时,会携带者服务器分配给自己的 session id。服务器根据这个 session id 来区分每一个请求
可以通过代码来演示
1 2 3 4 5 6 7 8 9 public class exer03 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); String id = session.getId(); System.out.println(id); } }
当浏览器进行第一次访问时,服务器会自动分配一个 session id
当浏览器继续访问时,会将这个 session id 传给服务器
常用的 API
1 2 3 4 5 6 7 8 9 10 long creationTime = session.getCreationTime();boolean aNew = session.isNew();int maxInactiveInterval = session.getMaxInactiveInterval();session.setMaxInactiveInterval(2000 ); session.invalidate();
会话保存 在同一个会话中,我们可以保存数据。
分别使用两个方法:
req.getSession().setAttribute
req.getSession().getAttribute
添加数据
1 2 3 4 5 6 7 public class exer04 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getSession().setAttribute("uname" ,"lina" ); } }
获取数据
1 2 3 4 5 6 7 8 public class exer05 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Object uname = req.getSession().getAttribute("uname" ); System.out.println(uname); } }
此时我们启动 Tomcat,依次访问 exer04 和 exer05
当访问 exer04 后系统会帮我们保存 uname
的值为 lina
然后访问 exer05
成功获取到 uname
的值
然后换个浏览器访问 exer05
由于会话不同,所以获取不到 uname
的值
内部转发和重定向 内部转发 当浏览器访问服务器时,服务器在内部转发这个请求给到另外一个服务器去处理。此时浏览器是不知道内部转发了多少次的。
首先编写exer06,在这个里面吧请求转发给 exer07
1 2 3 4 5 6 7 8 public class exer06 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("exer06....." ); req.getRequestDispatcher("exer07" ).forward(req,resp); } }
exer07 里面只打印一语句话
1 2 3 4 5 6 public class exer07 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("exer07....." ); } }
然后启动 Tomcat,访问exer06 观察结果。
结果可以看到 exer06 和 exer07 都会执行
浏览器地址没有变化,并且只有一次请求
浏览器重定向 使用浏览器重定向浏览器的地址会发生变化,并且会发送两次请求
使用 resp.sendRedirect("exer07");
重定向
1 2 3 4 5 6 7 8 public class exer06 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("exer06....." ); resp.sendRedirect("exer07" ); } }
在 exer07 打印信息
1 2 3 4 5 6 public class exer07 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("exer07....." ); } }
启动Tomcat,访问exer06
此时浏览器的地址栏会发生改变,并且发送两次请求
thymeleaf 入门 导包 首先导入需要用到的包
attoparser-2.0.5.RELEASE.jar
javassist-3.20.0-GA.jar
ognl-3.1.26.jar
slf4j-api-1.7.25.jar
thymeleaf-3.0.14.RELEASE.jar
unbescape-1.1.6.RELEASE.jar
然后再 lib_thymeleaf 文件夹右键,选择 add as library
接着吧这个 lib 包添加到 module 中
接着在 problems 中将所有内容添加到 web 中
引入文件 直接复制下面的代码到 myssm.ViewBaseServlet
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package com.songzx.myssm;import org.thymeleaf.TemplateEngine;import org.thymeleaf.context.WebContext;import org.thymeleaf.templatemode.TemplateMode;import org.thymeleaf.templateresolver.ServletContextTemplateResolver;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class ViewBaseServlet extends HttpServlet { private TemplateEngine templateEngine; @Override public void init () throws ServletException { ServletContext servletContext = this .getServletContext(); ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext); templateResolver.setTemplateMode(TemplateMode.HTML); String viewPrefix = servletContext.getInitParameter("view-prefix" ); templateResolver.setPrefix(viewPrefix); String viewSuffix = servletContext.getInitParameter("view-suffix" ); templateResolver.setSuffix(viewSuffix); templateResolver.setCacheTTLMs(60000L ); templateResolver.setCacheable(true ); templateResolver.setCharacterEncoding("utf-8" ); templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(templateResolver); } protected void processTemplate (String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException, IOException { resp.setContentType("text/html;charset=UTF-8" ); WebContext webContext = new WebContext(req, resp, getServletContext()); templateEngine.process(templateName, webContext, resp.getWriter()); } }
修改 demo2.java 代码,继承 ViewBaseServlet 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package com.songzx.demo01;import com.songzx.comm.Comm;import com.songzx.dao.CommServer;import com.songzx.myssm.ViewBaseServlet;import com.songzx.utils.JdbcUtils;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;import java.sql.Connection;import java.sql.SQLException;import java.util.List;@WebServlet("/index") public class demo2 extends ViewBaseServlet { @Override public void doGet (HttpServletRequest req, HttpServletResponse resp) { Connection conn = null ; try { conn = JdbcUtils.getConnection(); CommServer commServer = new CommServer(); List<Comm> commList = commServer.getAllComm(conn); HttpSession session = req.getSession(); session.setAttribute("commlist" ,commList); super .processTemplate("index" ,req,resp); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } JdbcUtils.closeConnection(conn,null ); } }
配置 xml 配置一个前缀,一个后缀
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?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" > <context-param > <param-name > view-prefix</param-name > <param-value > /</param-value > </context-param > <context-param > <param-name > view-suffix</param-name > <param-value > .html</param-value > </context-param > </web-app >
编写 index.html 1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > Hello world</h1 > </body > </html >
启动 Tomcat,可以看到页面虽然访问的是 index,但实际看到的是 index.html 文件
动态遍历页面数据 编写 Index.html 页面。
th:if="${#lists.isEmpty(session.commlist)}";
判断session.commlist 是否为空
th:unless="${#lists.isEmpty(session.commlist)}";
判断session.commlist 是否不为空
th:each="comm : ${session.commlist}";
遍历session.commlist ,并且命名一个临时变量 comm
th:text="${comm.name}"
;设置标签的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <table border ="1" style ="border-collapse:collapse" > <tr > <th style ="width: 200px" > 名称</th > <th style ="width: 200px" > 价格</th > <th style ="width: 200px" > 数量</th > <th style ="width: 200px" > 操作</th > </tr > <tr th:if ="${#lists.isEmpty(session.commlist)}" > <td colspan ="4" > 没有库存</td > </tr > <tr th:unless ="${#lists.isEmpty(session.commlist)}" th:each ="comm : ${session.commlist}" > <th th:text ="${comm.name}" > </th > <th th:text ="${comm.price}" > </th > <th th:text ="${comm.number}" > </th > <th > 删除</th > </tr > </table > </body > </html >
运行 tomcat。数据库中的数据在页面中被渲染出来
Servlet 保存作用域 request 保存数据 只在这一次的响应中有效,使用浏览器重定向时会获取不到数据,例如:
定义 demo01 往 request 中保存数据
1 2 3 4 5 6 7 8 9 10 @WebServlet("/demo01") public class demo1 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("uname" ,"lili" ); resp.sendRedirect("demo02" ); } }
定义 demo02 读取 request 中的值
1 2 3 4 5 6 7 8 9 @WebServlet("/demo02") public class demo2 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Object uname = req.getAttribute("uname" ); System.out.println("uname=" +uname); } }
启动项目,控制台输出 null
然后我们使用服务器内部转发,在另外一个接口中读取数据
定义 demo03
1 2 3 4 5 6 7 8 9 10 @WebServlet("/demo03") public class demo03 extends HttpServlet { @Override protected void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute("uname" ,"lili" ); request.getRequestDispatcher("demo04" ).forward(request,response); } }
定义 demo04
1 2 3 4 5 6 7 8 @WebServlet("/demo04") public class demo04 extends HttpServlet { @Override protected void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Object uname = request.getAttribute("uname" ); System.out.println("uname=" + uname); } }
启动服务,可以发现获取到的 uname 的值
session 保存作用域 在 session 中保存的作用域在这一次的请求中都有效
定义 demo05
1 2 3 4 5 6 7 8 9 @WebServlet("/demo05") public class demo05 extends HttpServlet { @Override protected void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession().setAttribute("uname" ,"lili" ); response.sendRedirect("demo06" ); } }
定义 demo06
1 2 3 4 5 6 7 8 @WebServlet("/demo06") public class demo06 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Object uname = req.getSession().getAttribute("uname" ); System.out.println("uname=" + uname); } }
启动
context 保存数据 context 保存的数据在项目运行期间都有效
编写 demo07
1 2 3 4 5 6 7 8 9 10 11 12 @WebServlet("/demo07") public class demo07 extends HttpServlet { @Override protected void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = request.getServletContext(); context.setAttribute("uname" ,"lili" ); response.sendRedirect("demo08" ); } }
编写 demo08
1 2 3 4 5 6 7 8 9 10 11 @WebServlet("/demo08") public class demo08 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = req.getServletContext(); Object uname = context.getAttribute("uname" ); System.out.println(uname); } }
通过上下文,只要在服务运行期间,不管那个请求进来都可以获取到 uname 的值
使用 thymeleaf 完成增删改查案例 实现数据编辑功能 首先添加跳转并且传参
在 a 标签上添加 th:href="@{/edit.do(id=${comm.id})}"
语法:@{}
表示根目录,(id=${comm.id},xxxxx)
表示传递参数,需要传递多个时用逗号隔开
1 2 3 4 5 6 <tr th:unless ="${#lists.isEmpty(session.commlist)}" th:each ="comm : ${session.commlist}" > <td > <a th:href ="@{/edit.do(id=${comm.id})}" th:text ="${comm.name}" > aa</a > </td > <td th:text ="${comm.price}" > </td > <td th:text ="${comm.number}" > </td > <td > 删除</td > </tr >
添加 EditService ,在这个页面中通过 id 查询该数据,然后显示 edit 页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @WebServlet("/edit.do") public class EditService extends ViewBaseServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Connection conn = null ; try { String id = req.getParameter("id" ); int anInt = Integer.parseInt(id); CommServer server = new CommServer(); conn = JdbcUtils.getConnection(); Comm comm = server.getCommById(conn, anInt); req.getSession().setAttribute("commEdit" ,comm); super .processTemplate("edit" ,req,resp); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,null ); } } }
编写 edit.html
使用 th:object="${session.commEdit}"
读取在 session 中保存的 commEdit ,然后下面的都通过 th:value="*{id}"
方式显示数据
点击提交触发表单的 post 方法,请求接口 update.do
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <table border ="1" style ="border-collapse:collapse" th:object ="${session.commEdit}" > <form method ="post" action ="update.do" > <input type ="hidden" th:value ="*{id}" name ="id" > <tr > <th style ="width: 200px" > 名称</th > <th > <input th:value ="*{name}" name ="name" > </th > </tr > <tr > <th style ="width: 200px" > 价格</th > <th > <input th:value ="*{price}" name ="price" > </th > </tr > <tr > <th style ="width: 200px" > 数量</th > <th > <input th:value ="*{number}" name ="number" > </th > </tr > <tr > <th style ="width: 200px" colspan ="2" > <input type ="submit" value ="提交" > </th > </tr > </form > </table >
添加 UpdateService,在这个方法中获取表单提交方法中传递过来的表单数据,然后调用 updateCommById
方法完成数据更新。更新完成后使用浏览器重定向到 index,完成数据的重新渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 @WebServlet("/update.do") public class UpdateService extends ViewBaseServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8" ); Connection conn = null ; try { String id = req.getParameter("id" ); int anInt = Integer.parseInt(id); String name = req.getParameter("name" ); String price = req.getParameter("price" ); double aDouble = Double.parseDouble(price); String number = req.getParameter("number" ); int aNumber = Integer.parseInt(number); conn = JdbcUtils.getConnection(); Comm comm = new Comm(anInt, name, aDouble, aNumber); CommServer server = new CommServer(); server.updateCommById(conn,comm); resp.sendRedirect("index" ); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,null ); } } }
效果展示
数据删除 修改 index.html
使用 th:onclick="|deleteComm(${comm.id})|"
两个竖线表示内部可以写 ${}
符号,会自动识别里面的内容
1 2 3 4 5 6 <tr th:unless ="${#lists.isEmpty(session.commlist)}" th:each ="comm : ${session.commlist}" > <td > <a th:href ="@{/edit.do(id=${comm.id})}" th:text ="${comm.name}" > aa</a > </td > <td th:text ="${comm.price}" > </td > <td th:text ="${comm.number}" > </td > <td th:onclick ="|deleteComm(${comm.id})|" > 删除</td > </tr >
添加 js/index.js
1 2 3 4 5 function deleteComm (id ) { if (confirm("确认删除吗" )){ window .location.href = "delete.do?id=" + id; } }
添加 deleteCommById
删除方法
1 2 3 4 5 6 7 8 9 @Override public void deleteCommById (Connection conn, int id) { String sql = "delete from commodity where id = ?" ; try { super .execUpdate(conn,sql,id); } catch (SQLException throwables) { throwables.printStackTrace(); } }
新增 DeleteService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @WebServlet("/delete.do") public class DeleteService extends ViewBaseServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String id = req.getParameter("id" ); if (StringUtils.isNotEmpty(id)){ int aId = Integer.parseInt(id); Connection conn = null ; try { conn = JdbcUtils.getConnection(); CommServer server = new CommServer(); server.deleteCommById(conn,aId); resp.sendRedirect("index" ); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } }
效果展示
数据新增 在 index 中添加新增按钮
1 <button th:onclick ="addComm()" > 添加水果</button >
实现 addComm
方法
1 2 3 function addComm ( ) { window .location.href = "add.do" ; }
添加 AddService
在这个方法中展示 add.html 页面
1 2 3 4 5 6 7 @WebServlet("/add.do") public class AddService extends ViewBaseServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super .processTemplate("add" ,req,resp); } }
编写 add.html
,点击提交触发 post
方法,调用 saveAdd.do
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <table border ="1" style ="border-collapse:collapse" > <form method ="post" action ="saveAdd.do" > <tr > <th style ="width: 200px" > 名称</th > <th > <input name ="name" > </th > </tr > <tr > <th style ="width: 200px" > 价格</th > <th > <input name ="price" > </th > </tr > <tr > <th style ="width: 200px" > 数量</th > <th > <input name ="number" > </th > </tr > <tr > <th style ="width: 200px" colspan ="2" > <input type ="submit" value ="提交" > </th > </tr > </form > </table > </body > </html >
编写 SaveService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @WebServlet("/saveAdd.do") public class SaveService extends ViewBaseServlet { @Override public void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8" ); String name = req.getParameter("name" ); String price = req.getParameter("price" ); double dprice = Double.parseDouble(price); String number = req.getParameter("number" ); int inumber = Integer.parseInt(number); Comm comm = new Comm(name, dprice, inumber); Connection conn = null ; try { conn = JdbcUtils.getConnection(); CommServer server = new CommServer(); server.addComm(conn,comm); resp.sendRedirect("index" ); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,null ); } } }
添加方法 addComm
1 2 3 4 5 6 7 8 9 @Override public void addComm (Connection conn, Comm comm) { String sql = "insert into commodity(name,price,number) values(?,?,?)" ; try { super .execUpdate(conn,sql,comm.getName(),comm.getPrice(),comm.getNumber()); } catch (SQLException throwables) { throwables.printStackTrace(); } }
效果展示
数据分页显示 首先添加一个查询总数的方法,在 baseDAO 中添加如下方法
1 2 3 4 5 6 7 8 9 10 11 12 public long execQuerySize (Connection conn,String sql) throws SQLException { QueryRunner runner = new QueryRunner(); ScalarHandler<Long> scalarHandler = new ScalarHandler<>(); Long query = runner.query(conn, sql, scalarHandler); return query; }
然后再 commServer 中添加实现方法
1 2 3 4 5 6 7 8 9 10 11 @Override public Long getCommSize (Connection conn) { String sql = "select count(*) from commodity" ; long l = 0 ; try { l = super .execQuerySize(conn, sql); } catch (SQLException throwables) { throwables.printStackTrace(); } return l; }
修改分页查询的SQL
1 2 3 4 5 6 7 8 9 10 11 @Override public List<Comm> getAllComm (Connection conn,int pageNumber) { String sql = "SELECT * FROM commodity limit ?,5" ; List<Comm> commList = null ; try { commList = super .execQuery(Comm.class, conn, sql,(pageNumber-1 ) * 5 ); } catch (SQLException throwables) { throwables.printStackTrace(); } return commList; }
修改 indexServer,添加分页逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @WebServlet("/index") public class demo2 extends ViewBaseServlet { @Override public void doGet (HttpServletRequest req, HttpServletResponse resp) { Connection conn = null ; try { conn = JdbcUtils.getConnection(); CommServer commServer = new CommServer(); int pageNumber = 1 ; if (StringUtils.isNotEmpty(req.getParameter("page" ))){ pageNumber = Integer.parseInt(req.getParameter("page" )); } Long commSize = commServer.getCommSize(conn); int pageTotal = (int )(commSize / 5 + 1 ); System.out.println(pageTotal+"pageTotal" ); List<Comm> commList = commServer.getAllComm(conn,pageNumber); HttpSession session = req.getSession(); session.setAttribute("commlist" ,commList); session.setAttribute("page" ,pageNumber); session.setAttribute("pageTotal" ,pageTotal); super .processTemplate("index" ,req,resp); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } JdbcUtils.closeConnection(conn,null ); } }
在 index.html 中添加分页按钮
1 2 3 4 5 6 <div > <button th:onclick ="pageNub(1)" th:disabled ="|${session.page == 1}|" > 首页</button > <button th:onclick ="|pageNub(${session.page-1})|" th:disabled ="|${session.page <= 1}|" > 上一页</button > <button th:onclick ="|pageNub(${session.page+1})|" th:disabled ="|${session.page >= session.pageTotal}|" > 下一页</button > <button th:onclick ="|pageNub(${session.pageTotal})|" th:disabled ="|${session.page == session.pageTotal}|" > 尾页</button > </div >
实现 js 方法
1 2 3 function pageNub (page ) { window .location.href = "index?page=" + page; }
效果展示
添加模糊查询功能 首先在 index.html 中添加查询表单
1 2 3 4 5 <form method ="post" th:action ="@{/index}" > <input type ="hidden" name ="searchtag" value ="search" > <input placeholder ="请输入名称查询" name ="name" th:value ="${session.name}" > <input type ="submit" value ="查询" > </form >
修改 indexService
表单提交的是 post 方法,然后再 doPost 方法中去请求 doGet 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 @WebServlet("/index") public class demo2 extends ViewBaseServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8" ); doGet(req,resp); } @Override public void doGet (HttpServletRequest req, HttpServletResponse resp) { Connection conn = null ; try { HttpSession session = req.getSession(); int pageNumber = 1 ; String name = "" ; String searchtag = req.getParameter("searchtag" ); if (StringUtils.isNotEmpty(searchtag)){ name = req.getParameter("name" ); session.setAttribute("name" ,name); pageNumber = 1 ; }else { name = (String) session.getAttribute("name" ); } conn = JdbcUtils.getConnection(); CommServer commServer = new CommServer(); if (StringUtils.isNotEmpty(req.getParameter("page" ))){ pageNumber = Integer.parseInt(req.getParameter("page" )); } Long commSize = commServer.getCommSize(conn,name); int pageTotal = (int )((commSize - 1 ) / 5 + 1 ); System.out.println("总页数是" + pageTotal); List<Comm> commList = commServer.getAllComm(conn,pageNumber,name); session.setAttribute("commlist" ,commList); session.setAttribute("page" ,pageNumber); session.setAttribute("pageTotal" ,pageTotal); super .processTemplate("index" ,req,resp); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } JdbcUtils.closeConnection(conn,null ); } }
因为添加了查询字段,所以要同时修改 SQL 语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public List<Comm> getAllComm (Connection conn,int pageNumber,String keyword) { String sql = "SELECT * FROM commodity where name like ? limit ?,5" ; List<Comm> commList = null ; try { commList = super .execQuery(Comm.class, conn, sql,"%" + keyword + "%" ,(pageNumber-1 ) * 5 ); } catch (SQLException throwables) { throwables.printStackTrace(); } return commList; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override public Long getCommSize (Connection conn,String keyword) { String sql = "select count(*) from commodity where name like ?" ; long l = 0 ; try { l = super .execQuerySize(conn, sql,"%" + keyword + "%" ); } catch (SQLException throwables) { throwables.printStackTrace(); } return l; }
效果展示
Servlet Mvc 优化1 我们通过上面的案例可以看到每次实现一个功能是都要新建一个 Servlet,当我们功能特别多时就要新建很多的文件。造成代码不好维护
我们可以吧多个 servlet 合并到一个文件中,实现代码如下
在这个方法类中我们只有一个 WebServlet,地址为 comm.do ,然后通过获取请求的参数 sercode 的值来调用不同的方法来实现一个接口完成多个功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 @WebServlet("/comm.do") public class commService extends ViewBaseServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8" ); String sercode = "index" ; String sercode1 = req.getParameter("sercode" ); if (StringUtils.isNotEmpty(sercode1)){ sercode = sercode1; } switch (sercode){ case "index" : index(req,resp); break ; case "delete" : delete(req,resp); break ; case "edit" : edit(req,resp); break ; case "update" : update(req,resp); break ; case "showadd" : showadd(req,resp); break ; case "saveadd" : saveadd(req,resp); break ; } } private void index (HttpServletRequest req, HttpServletResponse resp) { Connection conn = null ; try { HttpSession session = req.getSession(); int pageNumber = 1 ; String name = "" ; String searchtag = req.getParameter("searchtag" ); if (StringUtils.isNotEmpty(searchtag)){ name = req.getParameter("name" ); session.setAttribute("name" ,name); pageNumber = 1 ; }else { Object name1 = session.getAttribute("name" ); if (name1 != null ){ name = (String) name1; } } conn = JdbcUtils.getConnection(); CommServer commServer = new CommServer(); if (StringUtils.isNotEmpty(req.getParameter("page" ))){ pageNumber = Integer.parseInt(req.getParameter("page" )); } Long commSize = commServer.getCommSize(conn,name); int pageTotal = (int )((commSize - 1 ) / 5 + 1 ); System.out.println("总页数是" + pageTotal); List<Comm> commList = commServer.getAllComm(conn,pageNumber,name); session.setAttribute("commlist" ,commList); session.setAttribute("page" ,pageNumber); session.setAttribute("pageTotal" ,pageTotal); super .processTemplate("index" ,req,resp); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } JdbcUtils.closeConnection(conn,null ); } private void delete (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String id = req.getParameter("id" ); if (StringUtils.isNotEmpty(id)){ int aId = Integer.parseInt(id); Connection conn = null ; try { conn = JdbcUtils.getConnection(); CommServer server = new CommServer(); server.deleteCommById(conn,aId); resp.sendRedirect("comm.do" ); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } private void edit (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Connection conn = null ; try { String id = req.getParameter("id" ); int anInt = Integer.parseInt(id); CommServer server = new CommServer(); conn = JdbcUtils.getConnection(); Comm comm = server.getCommById(conn, anInt); req.getSession().setAttribute("commEdit" ,comm); super .processTemplate("edit" ,req,resp); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,null ); } } private void update (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8" ); Connection conn = null ; try { String id = req.getParameter("id" ); int anInt = Integer.parseInt(id); String name = req.getParameter("name" ); String price = req.getParameter("price" ); double aDouble = Double.parseDouble(price); String number = req.getParameter("number" ); int aNumber = Integer.parseInt(number); conn = JdbcUtils.getConnection(); Comm comm = new Comm(anInt, name, aDouble, aNumber); CommServer server = new CommServer(); server.updateCommById(conn,comm); resp.sendRedirect("comm.do" ); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,null ); } } private void showadd (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super .processTemplate("add" ,req,resp); } private void saveadd (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8" ); String name = req.getParameter("name" ); String price = req.getParameter("price" ); double dprice = Double.parseDouble(price); String number = req.getParameter("number" ); int inumber = Integer.parseInt(number); Comm comm = new Comm(name, dprice, inumber); Connection conn = null ; try { conn = JdbcUtils.getConnection(); CommServer server = new CommServer(); server.addComm(conn,comm); resp.sendRedirect("comm.do" ); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,null ); } } }
优化2 上面我们使用的是 Switch case 来判断参数调用方法,我们可以通过反射来获取,避免大量的判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Method[] declaredMethods = this .getClass().getDeclaredMethods(); for (Method method : declaredMethods) { String name = method.getName(); if (sercode.equals(name)){ try { method.invoke(this ,req,resp); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
最终优化版本 我们将 commService 当成一个普通的类来使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 package com.songzx.demo01;import com.songzx.comm.Comm;import com.songzx.dao.CommServer;import com.songzx.utils.JdbcUtils;import com.songzx.utils.StringUtils;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;import java.sql.Connection;import java.sql.SQLException;import java.util.List;public class commService { public String index (String searchtag,String name,Integer page, HttpServletRequest req) { Connection conn = null ; try { if (page == null ){ page = 1 ; } HttpSession session = req.getSession(); if (StringUtils.isNotEmpty(searchtag)){ session.setAttribute("name" ,name); page = 1 ; }else { Object name1 = session.getAttribute("name" ); if (name1 != null ){ name = (String) name1; } } conn = JdbcUtils.getConnection(); CommServer commServer = new CommServer(); Long commSize = commServer.getCommSize(conn,name); int pageTotal = (int )((commSize - 1 ) / 5 + 1 ); System.out.println("总页数是" + pageTotal); List<Comm> commList = commServer.getAllComm(conn,page,name); session.setAttribute("commlist" ,commList); session.setAttribute("page" ,page); session.setAttribute("pageTotal" ,pageTotal); return "index" ; } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,null ); } return "error" ; } public String delete (String id) { if (StringUtils.isNotEmpty(id)){ int aId = Integer.parseInt(id); Connection conn = null ; try { conn = JdbcUtils.getConnection(); CommServer server = new CommServer(); server.deleteCommById(conn,aId); return "direct:comm.do" ; } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } return "error" ; } public String edit (String id, HttpServletRequest req) { System.out.println("id = " + id); Connection conn = null ; try { int anInt = Integer.parseInt(id); CommServer server = new CommServer(); conn = JdbcUtils.getConnection(); Comm comm = server.getCommById(conn, anInt); req.getSession().setAttribute("commEdit" ,comm); return "edit" ; } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,null ); } return "error" ; } public String update (String id,String name,String price,String number, HttpServletRequest req) throws IOException { req.setCharacterEncoding("utf-8" ); Connection conn = null ; try { int anInt = Integer.parseInt(id); double aDouble = Double.parseDouble(price); int aNumber = Integer.parseInt(number); conn = JdbcUtils.getConnection(); Comm comm = new Comm(anInt, name, aDouble, aNumber); CommServer server = new CommServer(); server.updateCommById(conn,comm); return "direct:comm.do" ; } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,null ); } return "error" ; } public String showadd () { return "add" ; } public String saveadd (String name,String price,String number, HttpServletRequest req) throws IOException { req.setCharacterEncoding("utf-8" ); double dprice = Double.parseDouble(price); int inumber = Integer.parseInt(number); Comm comm = new Comm(name, dprice, inumber); Connection conn = null ; try { conn = JdbcUtils.getConnection(); CommServer server = new CommServer(); server.addComm(conn,comm); return "direct:comm.do" ; } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,null ); } return "error" ; } }
编写核心控制器,通过读取 xml 配置文件动态匹配调用的类
编写 xml 配置文件 applicationConentext.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8" ?> <benas > <bean id ="comm" class ="com.songzx.demo01.commService" > </bean > </benas >
新建 DispatcherServlet.java,这里主要步骤
使用 @WebServlet("*.do")
拦截到所有后缀是 .do 的请求,然后根据请求名称去 xml 中找到对应的类
然后再 service
中获取映射类中的所有方法
通过 req.getParameter("sercode");
来判断当前调用的是这个类中的那个方法
之后通过 method.getParameters();
获取这个方法中接收的参数值和参数类型,遍历参数列表从 request 中获取请求中传递过来的值
然后通过 method.invoke(contentObje, parValues);
调用这个方法,并传递已经获取到的所有参数值
method.invoke
会吧方法的返回值返回,我们根据返回的值做出跳转逻辑判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 package com.songzx.myssm;import com.songzx.utils.StringUtils;import org.w3c.dom.*;import org.xml.sax.SAXException;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.ParserConfigurationException;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Parameter;import java.util.HashMap;import java.util.Map;@WebServlet("*.do") public class DispatcherServlet extends ViewBaseServlet { private Map<String,Object> beanMap = new HashMap<>(); public DispatcherServlet () { try { InputStream stream = this .getClass().getClassLoader().getResourceAsStream("applicationConentext.xml" ); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = dbf.newDocumentBuilder(); Document document = documentBuilder.parse(stream); NodeList nodeList = document.getElementsByTagName("bean" ); for (int i = 0 ; i < nodeList.getLength(); i++){ Node bean = nodeList.item(i); if (bean.getNodeType() == Node.ELEMENT_NODE){ Element elementNode = (Element) bean; String id = elementNode.getAttribute("id" ); String aClass = elementNode.getAttribute("class" ); Object beanObj = Class.forName(aClass).newInstance(); beanMap.put(id,beanObj); } } } catch (ParserConfigurationException | SAXException | ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } @Override public void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("请求进来" ); req.setCharacterEncoding("utf-8" ); String path = parseStr(req.getServletPath()); Object contentObje = beanMap.get(path); String sercode = req.getParameter("sercode" ); if (StringUtils.isEmpty(sercode)){ sercode = "index" ; } try { Method[] methods = contentObje.getClass().getDeclaredMethods(); for (Method method : methods){ if (sercode.equals(method.getName())){ method.setAccessible(true ); Parameter[] parameters = method.getParameters(); Object[] parValues = new Object[parameters.length]; for (int i = 0 ; i < parameters.length; i++) { Parameter parameter = parameters[i]; String name = parameter.getName(); if ("req" .equals(name)){ parValues[i] = req; }else if ("resp" .equals(name)){ parValues[i] = resp; }else if ("session" .equals(name)){ parValues[i] = req.getSession(); }else { String typeName = parameter.getType().getName(); String paraName = req.getParameter(name); parValues[i] = paraName; if ("java.lang.Integer" .equals(typeName) && paraName != null ){ parValues[i] = Integer.parseInt(paraName); } } } String directStr = (String) method.invoke(contentObje, parValues); if (directStr.startsWith("direct:" )){ String newDirect = directStr.substring("direct:" .length()); resp.sendRedirect(newDirect); }else { super .processTemplate(directStr,req,resp); } } } } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } private String parseStr (String str) { int lastIndex = str.lastIndexOf(".do" ); String newstr = str.substring(1 ,lastIndex); return newstr; } }
注意,在java jdk1.8 以后,通过 method.getParameters();
可以获取到参数的具体名称,不再是 arg0
,但是要设置一下,具体设置参数如下
添加 -parameters
Servlet常用APi 读取初始化参数 getInitParameter 获取初始化参数,getServletConfig
使用方法
配置 XML 文件代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > Demo01</servlet-name > <servlet-class > com.songzx.Servlet.demo01</servlet-class > <init-param > <param-name > uname</param-name > <param-value > 张三</param-value > </init-param > <init-param > <param-name > height</param-name > <param-value > 188</param-value > </init-param > </servlet > <servlet-mapping > <servlet-name > Demo01</servlet-name > <url-pattern > /demo01</url-pattern > </servlet-mapping > </web-app >
编写java代码,继承 HttpServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class demo01 extends HttpServlet { @Override public void init () throws ServletException { ServletConfig config = getServletConfig(); String uname = config.getInitParameter("uname" ); System.out.println("uname = " + uname); String height = config.getInitParameter("height" ); System.out.println("height = " + height); } }
上面除了通过 XML 配置外也可以通过注解的方式来配置初始化参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @WebServlet(urlPatterns = {"/demo01"},initParams = { @WebInitParam(name = "uname",value = "张三"), @WebInitParam(name = "height",value = "188") }) public class demo01 extends HttpServlet { @Override public void init () throws ServletException { ServletConfig config = getServletConfig(); String uname = config.getInitParameter("uname" ); System.out.println("uname = " + uname); String height = config.getInitParameter("height" ); System.out.println("height = " + height); ServletContext context = getServletContext(); String contextName = context.getInitParameter("contextName" ); System.out.println("contextName = " + contextName); } }
运行获取
获取上下文 getServletContext 获取对象上下文,在初始化 init 方法中调用 getServletContext 方法
配置 XML 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?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" > <context-param > <param-name > contextName</param-name > <param-value > classpath:com.songzx.Servlet.demo01</param-value > </context-param > <servlet > <servlet-name > Demo01</servlet-name > <servlet-class > com.songzx.Servlet.demo01</servlet-class > </servlet > <servlet-mapping > <servlet-name > Demo01</servlet-name > <url-pattern > /demo01</url-pattern > </servlet-mapping > </web-app >
编写java代码获取
1 2 3 4 5 6 7 8 9 10 public class demo01 extends HttpServlet { @Override public void init () throws ServletException { ServletContext context = getServletContext(); String contextName = context.getInitParameter("contextName" ); System.out.println("contextName = " + contextName); } }
初识MVC MVC:Model(模型),View(视图),Controller(控制器)
视图层:用于做数据展示以及和用户交互的界面
控制层:能够接收客户端的请求,具体的业务功能呢还要借助模型组件来完成
模型层:模型分为很多种,有比较简单的pojo,vo(value,object),有业务模型组件,有数据访问层
pojo/vo:值对象
DAO:数据访问对象
BO:业务对象
区分业务对象和数据访问对象
BAO中的方法都是单精度的或者称为细粒度的。什么是单精度:一个方法只考虑一个操作,例如增删改查方法,只考虑层增删改查
BO中的方法属于业务方法,属于粗粒度的。一个业务方法中会包含多个BAO方法:例如注册业务
首先要检查该用户是否已经注册,调用BAO中的select方法
然后往用户表中增加一条信息,调用BAO中的insert方法
往用户积分表中增加一条数据,新用户默认100积分,调用BAO中的insert方法
其他方法……
改造水果管理系统,增加业务层 添加一个接口 commServer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public interface commServer { List<Comm> getCommList (String keyword,Integer pageNub) ; Comm getCommById (Integer id) ; void addComm (Comm comm) ; void deleteCommById (Integer id) ; void updateCommById (Comm comm) ; Integer getPageCount (String keyword) ; }
添加这个接口的实现类 package com.songzx.service.imp.commServiceImp;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class commServiceImp implements commServer { private CommDao commDao = new CommDao(); private Connection conn = JdbcUtils.getConnection(); public commServiceImp () throws SQLException, ClassNotFoundException { } @Override public List<Comm> getCommList (String keyword, Integer pageNub) { return commDao.getAllComm(conn,pageNub,keyword); } @Override public Comm getCommById (Integer id) { try { return commDao.getCommById(conn,id); } catch (SQLException throwables) { throwables.printStackTrace(); } return null ; } @Override public void addComm (Comm comm) { commDao.addComm(conn,comm); } @Override public void deleteCommById (Integer id) { commDao.deleteCommById(conn,id); } @Override public void updateCommById (Comm comm) { commDao.updateCommById(conn,comm); } @Override public Integer getPageCount (String keyword) { Long commSize = commDao.getCommSize(conn, keyword); return (int )((commSize - 1 ) / 5 + 1 ); } }
然后将原本的 commService 重命名为 commController,并引入 commServiceImp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 public class commController { private commServiceImp server = new commServiceImp(); public String index (String searchtag,String name,Integer page, HttpServletRequest req) { if (page == null ){ page = 1 ; } HttpSession session = req.getSession(); if (StringUtils.isNotEmpty(searchtag)){ session.setAttribute("name" ,name); page = 1 ; }else { Object name1 = session.getAttribute("name" ); if (name1 != null ){ name = (String) name1; } } int pageTotal = server.getPageCount(name); System.out.println("总页数是" + pageTotal); List<Comm> commList = server.getCommList(name,page); session.setAttribute("commlist" ,commList); session.setAttribute("page" ,page); session.setAttribute("pageTotal" ,pageTotal); return "index" ; } public String delete (String id) { int aId = Integer.parseInt(id); server.deleteCommById(aId); return "direct:comm.do" ; } public String edit (String id, HttpServletRequest req) { int anInt = Integer.parseInt(id); Comm comm = server.getCommById(anInt); req.getSession().setAttribute("commEdit" ,comm); return "edit" ; } public String update (String id,String name,String price,String number, HttpServletRequest req) throws IOException { req.setCharacterEncoding("utf-8" ); int anInt = Integer.parseInt(id); double aDouble = Double.parseDouble(price); int aNumber = Integer.parseInt(number); Comm comm = new Comm(anInt, name, aDouble, aNumber); server.updateCommById(comm); return "direct:comm.do" ; } public String showadd () { return "add" ; } public String saveadd (String name,String price,String number, HttpServletRequest req) throws IOException { req.setCharacterEncoding("utf-8" ); double dprice = Double.parseDouble(price); int inumber = Integer.parseInt(number); Comm comm = new Comm(name, dprice, inumber); server.addComm(comm); return "direct:comm.do" ; } }
这样我们的业务层就添加进来了
实现控制翻转和依赖注入 上面的代码中我们在 commController 中引用了 commServiceImp
然后再 commServiceImp 中已用了 commDao
这种情况在实际开发中并不推荐
在实际开发中我们追求高内聚,低耦合,一个类只处理和自己本身业务相关的事情,尽量不和别的模块发生关系,下面我们就通过配置XML的方式来改变这种耦合
在 applicationConentext.xml 文件中添加代码,在xml中声明了每个类的地址和引用属性名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <benas > <bean id ="comm" class ="com.songzx.controller.commController" > <property name ="server" ref ="commService" > </property > </bean > <bean id ="commService" class ="com.songzx.service.imp.commServiceImp" > <property name ="commDao" ref ="commDao" > </property > </bean > <bean id ="commDao" class ="com.songzx.dao.CommDao" /> </benas >
然后新建 com.songzx.io.beanFactory
接口,根据benaid获取对应的class实例
1 2 3 4 public interface beanFactory { Object getBean (String id) ; }
创建接口实现类 BeanFactoryImp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 public class BeanFactoryImp implements beanFactory { private HashMap<String,Object> benaMap = new HashMap<>(); public BeanFactoryImp () { try { InputStream stream = this .getClass().getClassLoader().getResourceAsStream("applicationConentext.xml" ); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = dbf.newDocumentBuilder(); Document document = documentBuilder.parse(stream); NodeList nodeList = document.getElementsByTagName("bean" ); for (int i = 0 ; i < nodeList.getLength(); i++){ Node bean = nodeList.item(i); if (bean.getNodeType() == Node.ELEMENT_NODE){ Element elementNode = (Element) bean; String id = elementNode.getAttribute("id" ); String aClass = elementNode.getAttribute("class" ); Object beanObj = Class.forName(aClass).newInstance(); benaMap.put(id,beanObj); } } for (int i =0 ; i < nodeList.getLength(); i++){ Node bean = nodeList.item(i); if (bean.getNodeType() == Node.ELEMENT_NODE){ Element elementNode = (Element) bean; String id = elementNode.getAttribute("id" ); NodeList childNodes = elementNode.getChildNodes(); for (int j = 0 ; j < childNodes.getLength(); j++) { Node childItem = childNodes.item(j); if (childItem.getNodeType() == Node.ELEMENT_NODE){ Element eleChildNode = (Element) childItem; String proName = eleChildNode.getAttribute("name" ); String proRef = eleChildNode.getAttribute("ref" ); Object proObj = benaMap.get(proRef); Object beanObj = benaMap.get(id); Field beanKeyField = beanObj.getClass().getDeclaredField(proName); beanKeyField.setAccessible(true ); beanKeyField.set(beanObj,proObj); } } } } } catch (ParserConfigurationException | SAXException | ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } @Override public Object getBean (String id) { return benaMap.get(id); } }
然后把核心控制器中关系解析 xml 的代码删除掉,修改 DispatcherServlet
类,去掉初始化的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 @WebServlet("*.do") public class DispatcherServlet extends ViewBaseServlet { private BeanFactoryImp beanMap = new BeanFactoryImp(); @Override public void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("请求进来" ); req.setCharacterEncoding("utf-8" ); String path = parseStr(req.getServletPath()); Object contentObje = beanMap.getBean(path); String sercode = req.getParameter("sercode" ); if (StringUtils.isEmpty(sercode)){ sercode = "index" ; } try { Method[] methods = contentObje.getClass().getDeclaredMethods(); for (Method method : methods){ if (sercode.equals(method.getName())){ method.setAccessible(true ); Parameter[] parameters = method.getParameters(); Object[] parValues = new Object[parameters.length]; for (int i = 0 ; i < parameters.length; i++) { Parameter parameter = parameters[i]; String name = parameter.getName(); if ("req" .equals(name)){ parValues[i] = req; }else if ("resp" .equals(name)){ parValues[i] = resp; }else if ("session" .equals(name)){ parValues[i] = req.getSession(); }else { String typeName = parameter.getType().getName(); String paraName = req.getParameter(name); parValues[i] = paraName; if ("java.lang.Integer" .equals(typeName) && paraName != null ){ parValues[i] = Integer.parseInt(paraName); } } } String directStr = (String) method.invoke(contentObje, parValues); if (directStr.startsWith("direct:" )){ String newDirect = directStr.substring("direct:" .length()); resp.sendRedirect(newDirect); }else { super .processTemplate(directStr,req,resp); } } } } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } private String parseStr (String str) { int lastIndex = str.lastIndexOf(".do" ); String newstr = str.substring(1 ,lastIndex); return newstr; } }
过滤器 过滤的作用会在请求时进行拦截一次,然后放行,响应回来后再次拦截
要实现过滤器,需要在类中实现 Filter
接口,Filter
有三个方法,表示过滤器的声明周期
init
初始化
doFilter
拦截服务
destroy
销毁
实现过滤器
添加 service1
类,作为我们的 WebServlet
1 2 3 4 5 6 7 8 9 @WebServlet("/demo1.do") public class service1 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo01 Service..." ); req.getRequestDispatcher("hello.html" ).forward(req,resp); } }
创建 Filter
的实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @WebFilter("/demo1.do") public class filter1 implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("请求拦截" ); filterChain.doFilter(servletRequest,servletResponse); System.out.println("响应拦截" ); } @Override public void destroy () { } }
这里的注解声明为 @WebFilter("/demo1.do")
,要和 @WebServlet("/demo1.do")
同名,这样才能起到这个接口的拦截作用
然后启动服务,查看打印的顺序
除了设置单个过滤器,也可以设置过滤器链,表示一个接口有多个过滤器
分别新建 filter2
,filter3
,编写文件代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.szx.filters;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import java.io.IOException;@WebFilter("*.do") public class filter2 implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("A" ); filterChain.doFilter(servletRequest,servletResponse); System.out.println("AO" ); } @Override public void destroy () { } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.szx.filters;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import java.io.IOException;@WebFilter("*.do") public class filter3 implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("B" ); filterChain.doFilter(servletRequest,servletResponse); System.out.println("BO" ); } @Override public void destroy () { } }
说明:
@WebFilter("*.do")
也可以使用通配符,这里表示匹配所有以.do
结尾的请求
如果使用注解的方法添加过滤器,则过滤器链的调用顺序会按照文件名的字母顺序调用,如果首字母相同则按照文件顺序调用
在启动服务之前将 filter1
中的 @WebFilter("/demo1.do")
注释掉,观察控制台打印的顺序
过滤器的使用 添加过滤器,设置请求编码为 UTF-8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @WebFilter("*.do") public class requestFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { Filter.super .init(filterConfig); } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; request.setCharacterEncoding("utf-8" ); filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy () { Filter.super .destroy(); } }
然后就可以去掉 DispatcherServlet
中关于设置编码的操作
使用过滤器添加事务管理 新建 com.songzx.trans.Transmanager
类,这个类有三个方法,专门负责事务的开启和提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class Transmanager { public static void beginTrans () throws SQLException, ClassNotFoundException { JdbcUtils.getConnection().setAutoCommit(false ); } public static void commit () throws SQLException, ClassNotFoundException { JdbcUtils.getConnection().commit(); } public static void rollback () throws SQLException, ClassNotFoundException { JdbcUtils.getConnection().rollback(); } }
因为事务要使用同一个连接,所以要修改 JdbcUtils
,使用 ThreadLocal
保存全局唯一的 Connection
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class JdbcUtils { private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); public static Connection getConnection () throws SQLException, ClassNotFoundException { Connection conn = threadLocal.get(); if (conn != null ){ return conn; }else { String url = "jdbc:mysql://127.0.0.1:3306/javaweb" ; String user = "root" ; String password = "abc123" ; Class.forName("com.mysql.cj.jdbc.Driver" ); Connection connection = DriverManager.getConnection(url, user, password); threadLocal.set(connection); return threadLocal.get(); } } public static void closeConnection (Connection conn, Statement state) { DbUtils.closeQuietly(conn); DbUtils.closeQuietly(state); } }
然后新建过滤器 com.songzx.filter.connectionFilter
,拦截所有以为 .do 结尾的请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.songzx.filter;import com.songzx.trans.Transmanager;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import java.io.IOException;@WebFilter("*.do") public class connectionFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { Filter.super .init(filterConfig); } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { try { Transmanager.beginTrans(); System.out.println("开启事务" ); filterChain.doFilter(servletRequest,servletResponse); Transmanager.commit(); System.out.println("提交事务" ); } catch (Exception e) { e.printStackTrace(); try { Transmanager.rollback(); System.out.println("回滚事务" ); } catch (Exception classNotFoundException) { classNotFoundException.printStackTrace(); } } } @Override public void destroy () { Filter.super .destroy(); } }
注意:我们在过滤器中使用 try catch 来监听内部错误,这就要求我们需要在方法内部抛出异常,不能在方法里面处理异常,要不然过滤器监听不到错误,无法执行回滚操作
监听器 ServletContextListener
可以监听上下文的创建和销毁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.szx.listener;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.servlet.annotation.WebListener;@WebListener public class MyServerContextListener implements ServletContextListener { @Override public void contextInitialized (ServletContextEvent servletContextEvent) { System.out.println("上下文被初始化" ); } @Override public void contextDestroyed (ServletContextEvent servletContextEvent) { System.out.println("上下文销毁" ); } }
添加jar包
按照文件目录创建文件夹,然后把对应文件夹内的文件加载到其中
然后选择 Build Artifact
选择刚刚打包的文件进行build
随后会生成一个 jar 包,我们引用即可
IDEA导出jar包插件 https://blog.csdn.net/zhyhang/article/details/89060408
使用过滤器处理登录验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 package com.szx.z_filter;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.annotation.WebInitParam;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;import java.util.Arrays;import java.util.List;@WebFilter( // 需要拦截的页面 urlPatterns = {"*.do","*.html"}, initParams = { // 配置访问白名单 @WebInitParam( name = "bai", value = "/pro11/router.do?operate=page&path=pages/user/login," + "/pro11/user.do?null") } ) public class SessionFilter implements Filter { private List<String> baiList = null ; @Override public void init (FilterConfig filterConfig) throws ServletException { String pathval = filterConfig.getInitParameter("bai" ); String[] split = pathval.split("," ); baiList = Arrays.asList(split); } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String uri = request.getRequestURI(); String query = request.getQueryString(); String paht= uri + "?" + query; System.out.println("paht = " + paht); if (baiList.contains(paht)){ filterChain.doFilter(servletRequest,servletResponse); return ; }else { HttpSession session = request.getSession(); Object userInfo = session.getAttribute("userInfo" ); if (userInfo == null ){ response.sendRedirect("router.do?operate=page&path=pages/user/login" ); }else { filterChain.doFilter(servletRequest,servletResponse); } } } @Override public void destroy () { } }
java处理浮点数精度问题 首先看问题
当我们直接使用Double类型和一个整数类型做乘法运算时,可能会出现精度问题,如上图所示,有很多的小数位
解决办法,使用 BigDecimal,BigDecimal 只接受字符串类型,所以我们要把 Doubel 和 int 都转成字符串来计算
使用 kaptcha 生成验证码 首先导入jar包: kaptcha-2.3.2.jar
,下载地址:https://www.jb51.net/softs/546820.html
然后在 web.xml
中添加配置代码
1 2 3 4 5 6 7 8 <servlet > <servlet-name > KaptchaServlet</servlet-name > <servlet-class > com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > KaptchaServlet</servlet-name > <url-pattern > /kaptcha.jpg</url-pattern > </servlet-mapping >
在 index.html
页面中使用
1 2 3 <body > <img src ="kaptcha.jpg" /> </body >
kaptcha 在生成验证码的同时,会在 session 中保存一份当前的验证码数据,session 的 key 为:KAPTCHA_SESSION_KEY
,我们可以通过读取 session 来校验前端输入的验证码是否正确
1 2 3 4 5 6 7 8 9 @WebServlet("/verificationCode.do") public class VerificationCode extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); Object kaptcha_session_key = session.getAttribute("KAPTCHA_SESSION_KEY" ); System.out.println("kaptcha_session_key = " + kaptcha_session_key); } }
运行代码效果
关于 kaptcha 的更多配置属性
Constant
描述
默认值
kaptcha.border
图片边框,合法值:yes , no
yes
kaptcha.border.color
边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue.
black
kaptcha.border.thickness
边框厚度,合法值:>0
1
kaptcha.image.width
图片宽
200
kaptcha.image.height
图片高
50
kaptcha.producer.impl
图片实现类
com.google.code.kaptcha.impl.DefaultKaptcha
kaptcha.textproducer.impl
文本实现类
com.google.code.kaptcha.text.impl.DefaultTextCreator
kaptcha.textproducer.char.string
文本集合,验证码值从此集合中获取
abcde2345678gfynmnpwx
kaptcha.textproducer.char.length
验证码长度
5
kaptcha.textproducer.font.names
字体
Arial, Courier
kaptcha.textproducer.font.size
字体大小
40px
kaptcha.textproducer.font.color
字体颜色,合法值: r,g,b 或者 white,black,blue.
black
kaptcha.textproducer.char.space
文字间隔
2
kaptcha.noise.impl
干扰实现类
com.google.code.kaptcha.impl.DefaultNoise
kaptcha.noise.color
干扰颜色,合法值: r,g,b 或者 white,black,blue.
black
kaptcha.obscurificator.impl
图片样式: 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
com.google.code.kaptcha.impl.WaterRipple
kaptcha.background.impl
背景实现类
com.google.code.kaptcha.impl.DefaultBackground
kaptcha.background.clear.from
背景颜色渐变,开始颜色
light grey
kaptcha.background.clear.to
背景颜色渐变,结束颜色
white
kaptcha.word.impl
文字渲染器
com.google.code.kaptcha.text.impl.DefaultWordRenderer
kaptcha.session.key
session key
KAPTCHA_SESSION_KEY
kaptcha.session.date
session date
KAPTCHA_SESSION_DATE
配置属性的使用方法,以设置文字大小为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <servlet > <servlet-name > KaptchaServlet</servlet-name > <servlet-class > com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class > <init-param > <param-name > kaptcha.border.color</param-name > <param-value > blue</param-value > </init-param > <init-param > <param-name > kaptcha.border.thickness</param-name > <param-value > 3</param-value > </init-param > </servlet > <servlet-mapping > <servlet-name > KaptchaServlet</servlet-name > <url-pattern > /kaptcha.jpg</url-pattern > </servlet-mapping >
效果:
使用axios发送数据响应普通文本 首先前端发送请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <script src ="./js/vue.js" > </script > <script src ="./js/axios.min.js" > </script > <body > <div id ="app" > <input v-model ="name" placeholder ="姓名" /> <br > <input v-model ="pwd" placeholder ="密码" /> <br > <button v-on:click ="submt" > 提交</button > </div > <script > window .onload = function ( ) { var app = new Vue({ el: '#app' , data: { name: '' , pwd: '' , }, methods: { submt: function ( ) { axios.post('getUserInfo' , { name: this .name, pwd: this .pwd }) .then(function (response ) { console .log(response.data); }) .catch(function (error ) { console .log(error); }); } } }) } </script > </body > </html >
java接收请求并响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.szx.axios;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;@WebServlet("/getUserInfo") public class demo01 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setCharacterEncoding("utf-8" ); resp.setContentType("text/html;charset=utf-8" ); PrintWriter out = resp.getWriter(); out.write("接收到值" ); } }
接收和返回JSON数据 首先前端发送请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <script src="./js/vue.js"></script> <script src="./js/axios.min.js"></script> <body> <div id="app"> <input v-model="name" placeholder="姓名" /><br> <input v-model="pwd" placeholder="密码" /><br> <button v-on:click="submt">提交</button> </div> <script> window.onload = function () { var app = new Vue({ el: '#app', data: { name: '', pwd: '', }, methods: { submt: function () { axios.post('demo02', { name: this.name, pwd: this.pwd }) .then(function (response) { console.log(response.data); app.name = response.data.name app.pwd = response.data.pwd }) .catch(function (error) { console.log(error); }); } } }) } </script> </body> </html>
后端创建一个 User 的 pojo 类,属性名和前端传递过来的一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.szx.pojo;public class User { String name; String pwd; public String getName () { return name; } public void setName (String name) { this .name = name; } public String getPwd () { return pwd; } public void setPwd (String pwd) { this .pwd = pwd; } @Override public String toString () { return "User{" + "name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}' ; } }
后端接收请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.szx.axios;import com.google.gson.Gson;import com.szx.pojo.User;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.BufferedReader;import java.io.IOException;@WebServlet("/demo02") public class demo02 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { StringBuffer stringBuffer = new StringBuffer("" ); BufferedReader bufferedReader = req.getReader(); String jsonstr = "" ; while ((jsonstr = bufferedReader.readLine()) != null ){ stringBuffer.append(jsonstr); } jsonstr = stringBuffer.toString(); System.out.println("jsonstr = " + jsonstr); Gson gson = new Gson(); User user = gson.fromJson(jsonstr, User.class); System.out.println(user); user.setName("鸠摩智" ); user.setPwd("123456" ); String userJson = gson.toJson(user); resp.setCharacterEncoding("utf-8" ); resp.setContentType("application/json;utf-8" ); resp.getWriter().write(userJson); } }
上面代码中使用到了 gson-2.8.6.jar
,下载地址:https://search.maven.org/artifact/com.google.code.gson/gson/2.8.6/jar
Gson 有两个核心方法:
gson.fromJson
,把 JSON 字符串转成 Java 类
gson.toJson
,把 Java 类转成 JSON 字符串传递给前端,再次之前要设置 resp.setContentType("application/json;utf-8");
,来明确告诉浏览器发送的是一个 JSON 数据,这样浏览器会自动的把 JSON 字符串格式转成 JSON 数据,前端就不需要手动去格式化 JSON 字符串