课程视频地址: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  =>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  =>() =>  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().setAttributereq.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 ,并且命名一个临时变量 commth: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 字符串