技术体系

文章目录

  • 一 HTML
    • 1 网页的组成部分
    • 2 HTML 概述
    • 3 HTML 标签
    • 4 常用标签
    • 5 表单与表单的提交
  • 二 CSS
    • 1 语法格式
    • 2 使用方法
  • 三 JavaScript
    • 1 概述
    • 2 与 HTML 结合的两种方式
    • 3 变量类型及特殊值
    • 4 关系、逻辑运算
    • 5 数组
    • 6 函数
    • 7 事件
    • 8 DOM (Document Object Model)
    • 9 DOM实例:验证用户名是否有效
  • 四 Tomcat
  • 五 Servlet
    • 1 举例:向数据库中添加表单信息
    • 2 Servlet 的继承关系
    • 3 Servlet 的生命周期
    • 4 HTTP 协议与 Session会话跟踪
    • 5 服务器端内部转发、重定向
    • 6 Servlet 保存作用域
    • 7 Servlet 的 init 方法与初始化参数设置
    • 8 ServletContext
  • 六 Thymeleaf
    • 1 配置过程
    • 2 Servlet 优化 - 合并同类方法
    • 3 Servlet 优化 - dispatchedServlet
  • 七 MVC
    • 1 概念
    • 2 降低各层间的耦合 - IOC 与 DI
    • 3 IOC 与 DI 的实现过程
  • 八 Filter
    • 1 概述
    • 2 使用 Filter 实现事务的原子性
  • 九 Listener
  • 十 总结

一 HTML

1 网页的组成部分

  • 内容(结构):在页面中可以看到的数据。我们称之为内容。一般使用 HTML 技术展示。
  • 表现:内容在页面上的展示形式,比如布局,颜色,大小等等。一般使用CSS 技术实现。
  • 行为:页面中元素与输入设备交互的响应。一般使用 JavaScript 技术实现。

2 HTML 概述

  • Hyper Text Markup Language (超文本标记语言),网页文件本身是一种文本文件,通过在文本文件中添加标记符,可以告诉浏览器如何显示其中的内容。
  • HTML 文件不需要编译,直接由浏览器进行解析执行。
  • 书写规范:大致由 head 和 body 两部分组成。
<html lang="en">  <!-- 页面开始 --><head>  <!-- HEAD --><meta charset="UTF-8"><title> 我的标题 </title></head><body>  <!-- BODY -->hello!</body></html>  <!-- 页面结束 -->

3 HTML 标签

  • 标签名对大小写不敏感。
  • 双标签的格式:<标签名> 封装的数据 </标签名>
    单标签的格式:<标签名/>
  • 标签拥有自己的属性,分为基本属性和事件属性。属性必须有值,属性值必须加引号。
  • 双标签必须正确关闭,不能交叉嵌套。
  • 想显示“转义字符”的问题(比如想打印出左尖括号<),用实体名称解决(对应的实体名称是&lt;)。
    <body onclick="alert('警告')">  <!-- 带有事件属性 -->点击body部分会出现警告 <hr/>  <!-- 单标签 --><font color="blue"> 蓝色的字体 </font>  <!-- 双标签,带有基本属性 --></body>

4 常用标签

重点是超链接、表格、表单

<!--标题标签,最大是h1,最小是h6,对齐可以选择 left/ center/ right-->
<h1 align="center"> 标题 </h1><!--字体-->
<font color="red" size="7"> 字体 </font><!--超链接,可选参数 target (_self 在当前页面跳转,_blank 在新页面跳转...)-->
<a href="www.baidu.com" target="_blank"> 超链接 </a><!--无序列表(有序将ul改为ol)-->
<ul><li> item1 </li><li> item2 </li>
</ul><!--图片img标签是图片标签,用来显示图片src属性可以设置图片的路径width属性设置图片的宽度height属性设置图片的高度border属性设置图片边框大小alt属性设置当指定路径找不到图片时,用来代替显示的文本内容绝对路径的正确格式是:  http://ip:port/工程名/资源路径
-->
<img src="./imgs/1.jpg" width="100" height="200"/><!--表格table 标签是表格标签border 设置表格标签width 设置表格宽度height 设置表格高度align 设置表格相对于页面的对齐方式cellspacing 设置单元格间距tr   是行标签th  是表头标签td  是单元格标签align 设置单元格文本对齐方式b 是加粗标签如果要实现跨行、跨列,改变 td 的 colspan、rowspan 属性
-->
<table align="center" border="1" width="300" height="300" cellspacing="0"><tr><th>1.1</th><th>1.2</th><th>1.3</th></tr><tr><td>2.1</td><td>2.2</td><td>2.3</td></tr><tr><td>3.1</td><td>3.2</td><td>3.3</td></tr>
</table><!--iframe标签,在原页面之上显示一个小的页面其中的 name 属性可以作为超链接的 target 属性,点击超链接后将在 iframe 中显示-->
<iframe src="1.html" width="500" height="400" name="abc"></iframe>
<a href="2.html" target="abc"> 超链接 </a>

5 表单与表单的提交

表单类型单独列出,可以使用 table 实现对齐。

     <!-- 要顺利提交表单,需要为所有项加上 value 或者 name 属性 --><form action="http://localhost:8080" method="post">姓名:<input type="text" value="默认姓名"/></br>密码:<input type="password" value="default"/></br><!-- name 属性用于分组,同一组的选项互斥-->性别:<input type="radio" name="sex" checked="checked"/><input type="radio" name="sex"/></br>爱好:<input type="checkbox" checked="checked"/>跑步 <input type="checkbox"/>跳绳</br>国籍:<select><option>--请选择--</option><option selected="selected">CHN</option><option>USA</option></select><br/>简介:<textarea rows="10" cols="20">默认简介</textarea><br/>附件:<input type="file"/></br><input type="reset" value="重置"><input type="submit" value="提交"></form>

  • form 标签的 action 属性设置提交的服务器地址,method 属性设置提交的方式 GET(默认) 或 POST
  • GET 请求的特点是:
    1. 浏览器地址栏中的地址是:服务器地址 + ? + 请求参数(请求参数的格式是 name=value&name=value)
    2. 不安全
    3. 有数据长度的限制
  • POST 请求的特点:
    1. 浏览器地址栏中只有服务器地址(action的属性值)
    2. 相对于GET请求要安全
    3. 理论上没有数据长度的限制
  • 表单提交的时候,数据没有发送给服务器的三种情况:
    1. 表单项没有name属性值
    2. 单选 radio 、复选 checkbox、下拉列表中的 option 标签 都需要添加value属性,以便发送给服务器
    3. 表单项不在提交的form标签中

二 CSS

1 语法格式

/* 标签名选择器,样式绑定标签 */
label_name {  property1: val1;property2: val2;
}/* id选择器,样式绑定具体的id,id是人为设定的,每个实例不相同*/
#id {property1: val1;property2: val2;
}/* class选择器,样式绑定标签分配的class,可以多个实例绑定一个class */
.class class_name {property1: val1;property2: val2;
}/* 组合选择器,选择器间是并的关系 */
选择器1, 选择器2 ... {property1: val1;property2: val2;
}

2 使用方法

    <head>  <!-- HEAD --><meta charset="UTF-8"><title> my_title </title>/* 导入已经写好的CSS文件(推荐) */<link rel="stylesheet" type="text/CSS" href="mycss.css">/* 或者把CSS写到此处的 style 标签中 */<style type="text/css">label_name {  property1: val1;property2: val2;}</style></head>

三 JavaScript

1 概述

  • JS 运行在客户端,需要运行浏览器来解析执行 JavaScript 代码,和 Java 并无直接关系。
  • JS 是弱类型的语言,Java 是强类型的语言。强弱的差别在于定义变量后,变量的数据类型是否可变。
  • 特点是 交互性(信息的动态交互)、安全性(不允许直接访问本地硬盘)、跨平台性(只要是可以解释 JS 的浏览器都可以执行,和平台无关)。

2 与 HTML 结合的两种方式

  • 在 HTML 文件中的 head 部分,用 script 标签写入;
  • 在 script 标签中导入 JS 文件。

上述两种方法不能写在一个标签里。

<head><meta charset="UTF-8"><title>Title</title><script type="text/javascript">alert('JS嵌入方式1');</script><script src="myjs.js"></script> <!-- 文件内容: alert('JS嵌入方式2'); -->
</head>

3 变量类型及特殊值

关键字 对应类型
number 数值类型
string 字符串类型
object 对象类型
bool 布尔类型
function 函数类型

特殊值 含义
undefined 未定义,所有 JS 变量未赋于初始值的时候,默认值都是 undefined
null
NaN 非数字非数值

4 关系、逻辑运算

  • ==比较两个变量的值是否相等(比如,“123”==123是成立的), ===比较两个变量的类型和值是否相等;
  • JS 中所有变量都可以作为布尔值使用,0、null、undefined、空串 都认为是 false;
  • ||(或) &&(与) 具有短路特性,返回值为第一个导致结果的变量

5 数组

  • 数组不会出现越界的问题。在超过原来长度的位置赋值时,会进行自动扩容(读操作不会扩容)。
// 数组定义
var arr = [1, "abc"];// 数组遍历
for (var i = 0; i < arr.length; i++) {alert(arr[i])
}// 自动扩容,下标2、3、4的元素均为undefined
arr[5] = 2;

6 函数

  • JS 不允许函数重载。一个函数名只能对应一个具体实现。
        function noParam() {alert('无参函数调用');}noParam();function withParam(a, b) {alert("有参函数调用" + a + b);}withParam(1, 2);function withReturn(a, b) {alert("有参有返回值函数调用");return a + b;}alert(withReturn(10, 5));
  • 隐形参数 arguments 将所有实参组织为一个数组,通过下标可以访问所有参数。
        function testInvisibleParams() {var res = 0;for (var i = 0; i < arguments.length; i++) {res += arguments[i];}return res;}alert(testInvisibleParams(1, 2, 3, 4));

7 事件

  • 常用事件
事件 操作
onload(加载完成) 常用做页面 JS 代码初始化操作
onclick(单击) 按钮的点击响应操作
onblur(失去焦点) 输入框失去焦点后验证其输入内容是否合法
onchange(内容发生改变) 下拉列表和输入框内容发生改变后操作
onsubmit(表单提交) 表单提交前,验证所有表单项是否合法
  • 事件需要进行注册才能使用:

    • 静态注册:通过 HTML 标签的事件属性,直接赋于事件响应后的代码;
    • 动态注册:先通过 JS 代码得到标签的 dom 对象,然后再通过 dom 对象.事件名 = function(){} 这种形式赋于事件响应后的代码。
  • onload 事件
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script type="text/javascript">function onloadEvent() {alert("静态注册onload事件,需要在body标签下添加 οnlοad='onloadEvent' ")}window.onload = function () {alert("动态注册onload事件")}</script>
</head>
<body></body>
</html>
  • onclick 事件
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script type="text/javascript">function onclickEvent() {alert("静态注册onclick事件")}window.onload = function () {<!-- document对象表示整个页面,通过id获取button对象 -->var btn2_obj = document.getElementById("btn2");<!-- 绑定行为 -->btn2_obj.onclick = function () {alert("动态注册onclick事件")}}</script>
</head>
<body><button onclick="onclickEvent()">静态注册的按钮</button><button id="btn2">动态注册的按钮</button>
</body>
</html>
  • onblur 事件(同上)
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script type="text/javascript">function onblurEvent() {console.log("静态失去焦点");}window.onload = function () {var pw = document.getElementById("p");pw.onblur = function () {console.log("动态失去焦点");}}</script>
</head>
<body>账户:<input type="text" onblur="onblurEvent()"/>密码:<input type="password" id="p"/>
</body>
</html>
  • onchange 事件,绑定的是 select 而非 option 标签
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script type="text/javascript">function onchangeEvent() {alert("静态注册onchange");}window.onload = function () {var select2 = document.getElementById("s");select2.onchange = function () {alert("动态注册onchange")}}</script>
</head>
<body>选择1:<select onchange="onchangeEvent()"><option>选项11</option><option>选项12</option></select>选择2:<select id="s"><option>选项21</option><option>选项22</option></select>
</body>
</html>
  • onsubmit 事件,绑定的是表格而非按钮,返回 false 则不会提交表单
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script type="text/javascript">function onsubmitEvent() {alert("静态注册onsubmit");<!--如果发现不合法,返回false,阻止提交-->return false;}window.onload = function () {var form2 = document.getElementById("f");form2.onsubmit = function () {alert("动态注册onsubmit");<!--如果发现不合法,返回false,阻止提交-->return false;}}</script>
</head>
<body><form action="http://localhost:8080" onsubmit="return onsubmitEvent()"> <!--静态注册 return 不能少--><input type="submit" value="静态注册提交"></form><form action="http://localhost:8080" id="f"><input type="submit" value="动态注册提交"></form>
</body>
</html>

8 DOM (Document Object Model)

  • 简单来说,将整个 HTML 文件视为一个 document 对象,并把其中所有的标签对象化,形成了树型结构。
  • document 实例提供查询方法,使用优先级从高到低:getElementByID(返回一个实例),getElementByName(可以数组形式返回多个实例),getElementByTagName(以数组形式返回指定标签的对象)。注意:页面加载完之后才能进行查询!不仅限于放在 onload 事件中。
  • document 实例提供创建方法:createElement
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script type="text/javascript">window.onload = function () {var new_div = document.createElement("div");new_div.innerHTML = "你好";document.body.appendChild(new_div);  <!-- 在加载完后执行 -->}</script>
</head>
<body></body>
</html>

9 DOM实例:验证用户名是否有效

限制用户名由数字和字母组成,长度区间为 [5, 12]

<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script>window.onload = function () {var tip = document.getElementById("tip");var btn = document.getElementById("btn");var text = document.getElementById("text");btn.onclick = function () {var input = text.value;var pattern = /^\w{5,12}$/;if (pattern.test(input)) {  <!-- 匹配正则表达式 -->tip.innerHTML = "通过";  <!-- innerHTML 返回的是开始和结束标签之间的值,可读写-->} else {tip.innerHTML = "不通过";}}}</script>
</head>
<body>
<input type="text" id="text">
<span id="tip" style="color: brown"></span>
<button id="btn">检查</button>
</body>
</html>

四 Tomcat

  • 简单来说,Tomcat 就是一个运行JAVA的网络服务器。
  • HTML 文件通过本地访问,使用的协议是 file,执行的操作是直接获取并解析;
  • IDEA 2021.3.1 创建JavaWeb工程的方法
  • 通过WEB服务器访问,使用的协议是 http,经过了客户端请求与服务器响应的过程,执行操作如下图:

五 Servlet

1 举例:向数据库中添加表单信息

  1. 客户端请求表单页面 add.html(表单指定 action=“add”(可以自定义) method=“post”),服务器返回。
  2. 客户端填写表单,使用 HttpRequest 实例提交给服务器。
  3. 服务器的具有一个类 ,继承自 HttpServlet 类,其中的 doPost() 方法定义了 post 请求的处理与返回过程,调用 DAO 将表单信息写入数据库。
//@WebServlet("/add")
public class AddServlet extends HttpServlet {@Overridepublic void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// method=post 设置编码方式防止乱码,且在获取参数的操作之前(放在首行)request.setCharacterEncoding("UTF-8");String name = request.getParameter("name");System.out.println(name);// 调用DAO// ...}
}
  1. 如何指定 action=“add” 与自定义类 AddServlet 的绑定?
    一种方式是使用 web.xml 配置(另一种是通过在 AddServlet 注释 @WebServlet("/add") ):
  <!--指定servlet信息--><servlet><servlet-name>AddServlet</servlet-name><servlet-class>com.atguigu.servlets.AddServlet</servlet-class></servlet><!--设定servlet映射--><servlet-mapping><servlet-name>AddServlet</servlet-name><url-pattern>/add</url-pattern>  <!--action="add"--></servlet-mapping>

2 Servlet 的继承关系

  • 继承关系: HttpServlet 具体实现类 -> GenericServlet 抽象类 -> Servlet 接口
  • Servlet中的核心方法:service()
  • 当收到请求,service 方法会自动响应(tomcat 容器调用),在 HttpServlet 中分析请求的方式:到底是get、post、head 等等,然后再决定调用具体的 doXX 的方法
  • 在 HttpServlet 中,doXX 方法默认都是405(报错信息是找不到方法实现),除非子类去实现对应的 doXX 方法,否则默认会报405错误。

3 Servlet 的生命周期

  • 三个重要的方法:init、service、destory
  • 默认情况下, 第一次接收请求时,Servlet 会进行实例化(调用构造方法)、初始化(调用init())、然后服务(调用service());从第二次请求开始,每一次都是服务(调用service());当容器关闭时,其中的所有的 Servlet 实例会被销毁(调用destroy())。
  • 如果需要提高响应速度,应该设置 Servlet 的初始化时机,在第一次请求之前执行实例化和初始化。具体做法是,在 web.xml 中的 <servlet> 中添加 <load-on-startup> 标签。
  • Servlet 是单例的、线程不安全的。单例是指,对于一个确定的 Servlet 类型,所有的请求都是同一个 Servlet 实例去响应;由于 Servlet 线程不安全,所以尽量避免在其中设置成员变量,如果设置尽量不要读写。

4 HTTP 协议与 Session会话跟踪

  • HTTP 协议是无状态的,需要通过会话跟踪技术保存用户的历史信息。
  • HTTP 请求与响应报文的格式略。
  • 常用的API:
    request.getSession() -> 获取当前的会话,没有则创建一个新的会话
    request.getSession(true) -> 效果和不带参数相同
    request.getSession(false) -> 获取当前会话,没有则返回null,不会创建新的
    session.getId() -> 获取sessionID
    session.isNew() -> 判断当前session是否是新的
    session.getMaxInactiveInterval() -> session的非激活间隔时长,默认1800秒
    session.setMaxInactiveInterval()
    session.invalidate() -> 强制让会话立即失效

5 服务器端内部转发、重定向

  • 服务器端内部转发对客户端是不可见的,执行方法:request.getRequestDispatcher("...").forward(request, response)

  • 重定向对客户端可见,并且会显示地更改 URL,执行方法:response.sendRedirect("...")

6 Servlet 保存作用域

  1. request:一次请求响应内有效。
  2. session:一次会话内有效。
  3. application:一次应用内有效,除非关闭服务器。
public class Demo extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// request 级别req.setAttribute("property1", 1);// session 级别HttpSession session = req.getSession();session.setAttribute("property2", 2);// application 级别ServletContext servletContext = req.getServletContext();servletContext.setAttribute("property3", 3);}
}

7 Servlet 的 init 方法与初始化参数设置

  • 两种 init 方法:
    如果需要进行一些初始化操作,重写无参的 init 方法
 // Servlet中的初始化方法有两个:init() , init(config)// 其中带参数的方法代码如下:public void init(ServletConfig config) throws ServletException {this.config = config ;init();  // *调用无参的init*}// 另外一个无参的init方法如下:public void init() throws ServletException{}
  • 初始化参数设置
1. 通过 web.xml 配置<servlet><servlet-name>Demo01Servlet</servlet-name><servlet-class>com.atguigu.servlet.Demo01Servlet</servlet-class><!--设置参数--><init-param><param-name>hello</param-name><param-value>world</param-value></init-param></servlet>2. 通过注解
@Webservlet(urlPatterns = {"/demo01"} ,initParams = {@WebInitParam(name="hello",value="world")})
  • 读取初始化参数:
ServletConfig config = getServletConfig();  // 获取config对象
config.getInitParameter("hello");  // 读取初始化的值

8 ServletContext

  • ServletContext是一个全局的储存信息的空间,服务器开始就存在,服务器关闭才释放(application级别)
  • 类似 session,可以想象成一个 map
  • 多个Servlet可以通过ServletContext对象来实现数据间的共享,如果是涉及到不同用户共享数据,而这些数据量不大,同时又不希望写入数据库中,我们就可以考虑使用 ServletContext 实现
  • 初始化 ServletContext 时,不能写在具体的某个 servlet 标签中,因为它是全局的
<!--写在servlet之外-->
<context-param><param-name>name</param-name><param-value>gavin</param-value>
</context-param><servlet>  <servlet-name>MyServlet</servlet-name>  <servlet-class>com.gavin.servlet.MyServlet</servlet-class>  <init-param>  <param-name>encoding</param-name>  <param-value>utf-8</param-value>  </init-param>
</servlet>

六 Thymeleaf

1 配置过程

  1. 添加依赖, maven 中添加:
        <dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf</artifactId><version>3.0.14.RELEASE</version></dependency>
  1. 新建一个类 ViewBaseServlet 继承 HttpServlet
public class ViewBaseServlet extends HttpServlet {private TemplateEngine templateEngine;@Overridepublic void init() throws ServletException {// 1.获取ServletContext对象ServletContext servletContext = this.getServletContext();// 2.创建Thymeleaf解析器对象ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);// 3.给解析器对象设置参数// ①HTML是默认模式,明确设置是为了代码更容易理解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");// 4.创建模板引擎对象templateEngine = new TemplateEngine();// 5.给模板引擎对象设置模板解析器templateEngine.setTemplateResolver(templateResolver);}protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {// 1.设置响应体内容类型和字符集resp.setContentType("text/html;charset=UTF-8");// 2.创建WebContext对象WebContext webContext = new WebContext(req, resp, getServletContext());// 3.处理模板数据templateEngine.process(templateName, webContext, resp.getWriter());}
}
  1. 在 web.xml 配置前缀和后缀,根据逻辑视图名称得到物理视图名称。
    逻辑视图名称 : index
    物理视图名称 : view-prefix + 逻辑视图名称 + view-suffix
    真实的视图名称: / index .html
    <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>
  1. 创建自定义的 Servlet 类,继承 ViewBaseServlet,调用super.processTemplate("index",request,response); 定位到指定 HTML 页面。
  2. 对于跳转到的 HTML 页面,需要显示查询内容。设置 <html lang="en" xmlns:th="http://www.thymeleaf.org">,使用 thymeleaf 的标签完成操作。

2 Servlet 优化 - 合并同类方法

  • 之前的一个 Servlet 只提供一种方法,此处的改进是将同类的多种方法合并到一个 Servlet 中
  • 使用反射代替 switch-case
@WebServlet("/fruit.do")
public class FruitServlet extends ViewBaseServlet {private final FruitDAO fruitDAO = new FruitDAOImpl();/*** 设置一个Fruit 的 meta servlet*/@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {req.setCharacterEncoding("UTF-8");String operation = (String) req.getAttribute("operation");// 简化了switch-caseMethod[] methods = this.getClass().getDeclaredMethods();for (Method method: methods) {method.setAccessible(true);if (operation.equals(method.getName())) {try {method.invoke(this, req, resp);return ;} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}}throw new IOException("输入操作错误");  // 对应default}private void insertFruit(HttpServletRequest request, HttpServletResponse response) {// ...}private void selectFruit(HttpServletRequest request, HttpServletResponse response) {// ...}
}

3 Servlet 优化 - dispatchedServlet

  • 相当于一个 meta 的 Servlet,称为 DispatchedServlet,继承自 ViewBaseServlet。合并所有种类的 Servlet,并将原来的 Servlet 转为 Controller,剥夺其注释 @WebServlet
  • 在 src 目录下创建 applicationContext.xml,利用 bean 标签,为参数名和 Controller 建立映射
  • DispatchedServlet 的 init 方法根据 xml 配置 Map<String,Object> beanMap
<?xml version="1.0" encoding="utf-8"?><beans><!-- 这个bean标签的作用是 将来servletpath中涉及的名字对应的是fruit,那么就要FruitController这个类来处理 --><bean id="fruit" class="com.atguigu.fruit.controllers.FruitController"/>
</beans>
  • 使用时,向 DispatchedServlet 传递参数,由其 service 方法负责解析,并根据 beanMap 调用指定的 Controller,具体步骤:

    • 获取参数:获取即将要调用的方法的参数签名信息
    • 执行方法:Object returnObj = method.invoke(controllerBean , parameterValues);
    • 视图处理:根据返回值调用重定向等 String returnStr = (String)returnObj; if(returnStr.startWith("redirect:")){ .... } else if.....

七 MVC

1 概念

  • MVC(Model–View–Controller)模式是软件工程中的一种软件架构模式,它把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
  • Controller 作为 Model 和 View 部分的“胶水”,本身不适合包含太多的逻辑。

2 降低各层间的耦合 - IOC 与 DI

  • DI(Dependency Injection,依赖注入)是 IOC(Inversion of Control,控制反转)最常用的方法。
  • IOC 是一种设计思想,核心是,将设计好的对象交给容器控制,而不是传统的在对象内部直接控制。 把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散的耦合。
  • Spring 所倡导的开发方式就是如此,所有的类都会在 spring 容器中登记,告诉 spring 你是个什么东西,你需要什么东西,然后 spring 会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring 容器来控制,也就是说控制对象生存周期的不再是引用它的对象,而是 spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被 spring 控制,所以这叫控制反转。
  • IOC 容器实际上就是 map(key,value),里面存的是各种对象(在 xml 里配置的 bean 节点、service、controller、component),在项目启动的时候会读取配置文件里面的 bean 节点,根据全限定类名使用反射创建对象并放到 map 里。


通过 IOC 容器,上述过程转为:

  • IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过 DI 来实现的。在类中需要使用到的对象,全部通过反射从第三方容器注入而不是自己创建
  • 比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个 Connection 对象,有了 spring 只需要告诉 spring ,A中需要一个 Connection,至于这个 Connection 怎么构造,何时构造,A不需要知道。在系统运行时,spring 会在适当的时候制造一个 Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection 才能正常运行,而这个 Connection 是由 spring 注入到A中的,依赖注入的名字就这么来的。

3 IOC 与 DI 的实现过程

  1. 对于 Servlet - Controller - Service - DAO 的结构,首先将用到的类,及其依赖关系,注册到 applicationContext.xml 中。
<?xml version="1.0" encoding="utf-8"?><beans><!-- FruitDAO不依赖于其它组件 --><bean id="fruitDAO" class="com.atguigu.fruit.dao.impl.FruitDAOImpl"/><!-- FruitService依赖于FruitDAO(FruitService类包含FruitDAO属性) --><bean id="fruitService" class="com.atguigu.fruit.service.impl.FruitServiceImpl"><!-- property标签用来表示属性;name表示属性名;ref表示引用其他bean的id值--><property name="fruitDAO" ref="fruitDAO"/></bean><!-- FruitController依赖于FruitService(FruitController类包含FruitService属性) --><bean id="fruit" class="com.atguigu.fruit.controllers.FruitController"><property name="fruitService" ref="fruitService"/></bean><!-- FruitServlet中具有 包含所有对象的 map,以BeanFactory作为 map 的包装 -->
</beans>
  1. 使用 BeanFactory 读取 xml 配置,填充 map,并设置依赖关系(比如 A 以 B 作为属性,执行此过程前将属性值设置为 null,在此过程将 A 的对应属性设置为 B 的实例,完成依赖注入)

BeanFactory 实现:

public class ClassPathXmlApplicationContext implements BeanFactory {private Map<String,Object> beanMap = new HashMap<>();public ClassPathXmlApplicationContext(){try {InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");//1.创建DocumentBuilderFactoryDocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();//2.创建DocumentBuilder对象DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder() ;//3.创建Document对象Document document = documentBuilder.parse(inputStream);//4.获取所有的bean节点NodeList beanNodeList = document.getElementsByTagName("bean");for(int i = 0 ; i<beanNodeList.getLength() ; i++){Node beanNode = beanNodeList.item(i);if(beanNode.getNodeType() == Node.ELEMENT_NODE){Element beanElement = (Element)beanNode ;String beanId =  beanElement.getAttribute("id");String className = beanElement.getAttribute("class");Class beanClass = Class.forName(className);//创建bean实例Object beanObj = beanClass.newInstance() ;//将bean实例对象保存到map容器中beanMap.put(beanId , beanObj) ;//到目前为止,此处需要注意的是,bean和bean之间的依赖关系还没有设置}}//5.组装bean之间的依赖关系for(int i = 0 ; i<beanNodeList.getLength() ; i++){Node beanNode = beanNodeList.item(i);if(beanNode.getNodeType() == Node.ELEMENT_NODE) {Element beanElement = (Element) beanNode;String beanId = beanElement.getAttribute("id");NodeList beanChildNodeList = beanElement.getChildNodes();for (int j = 0; j < beanChildNodeList.getLength() ; j++) {Node beanChildNode = beanChildNodeList.item(j);if(beanChildNode.getNodeType()==Node.ELEMENT_NODE && "property".equals(beanChildNode.getNodeName())){Element propertyElement = (Element) beanChildNode;String propertyName = propertyElement.getAttribute("name");String propertyRef = propertyElement.getAttribute("ref");//1) 找到propertyRef对应的实例Object refObj = beanMap.get(propertyRef);//2) 将refObj设置到当前bean对应的实例的property属性上去Object beanObj = beanMap.get(beanId);Class beanClazz = beanObj.getClass();Field propertyField = beanClazz.getDeclaredField(propertyName);propertyField.setAccessible(true);propertyField.set(beanObj,refObj);}}}}} catch (ParserConfigurationException e) {e.printStackTrace();} catch (SAXException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();}}@Overridepublic Object getBean(String id) {return beanMap.get(id);}
}
  1. 重写 Servlet 的 无参 init 方法和 service 方法。其中 init 方法负责获取 BeanMap,service 方法负责解析方法调用,执行方法调用,处理返回值。
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet{private BeanFactory beanFactory ;public DispatcherServlet(){}public void init() throws ServletException {super.init();beanFactory = new ClassPathXmlApplicationContext();}@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//设置编码request.setCharacterEncoding("UTF-8");//假设url是:  http://localhost:8080/pro15/hello.do//那么servletPath是:    /hello.do// 我的思路是:// 第1步: /hello.do ->   hello   或者  /fruit.do  -> fruit// 第2步: hello -> HelloController 或者 fruit -> FruitControllerString servletPath = request.getServletPath();servletPath = servletPath.substring(1);int lastDotIndex = servletPath.lastIndexOf(".do") ;servletPath = servletPath.substring(0,lastDotIndex);Object controllerBeanObj = beanFactory.getBean(servletPath);String operate = request.getParameter("operate");if(StringUtil.isEmpty(operate)){operate = "index" ;}try {Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();for(Method method : methods){if(operate.equals(method.getName())){//1.统一获取请求参数//1-1.获取当前方法的参数,返回参数数组Parameter[] parameters = method.getParameters();//1-2.parameterValues 用来承载参数的值Object[] parameterValues = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {Parameter parameter = parameters[i];String parameterName = parameter.getName() ;//如果参数名是request,response,session 那么就不是通过请求中获取参数的方式了if("request".equals(parameterName)){parameterValues[i] = request ;}else if("response".equals(parameterName)){parameterValues[i] = response ;}else if("session".equals(parameterName)){parameterValues[i] = request.getSession() ;}else{//从请求中获取参数值String parameterValue = request.getParameter(parameterName);String typeName = parameter.getType().getName();Object parameterObj = parameterValue ;if(parameterObj!=null) {if ("java.lang.Integer".equals(typeName)) {parameterObj = Integer.parseInt(parameterValue);}}parameterValues[i] = parameterObj ;}}//2.controller组件中的方法调用method.setAccessible(true);Object returnObj = method.invoke(controllerBeanObj,parameterValues);//3.视图处理String methodReturnStr = (String)returnObj ;if(methodReturnStr.startsWith("redirect:")){        //比如:  redirect:fruit.doString redirectStr = methodReturnStr.substring("redirect:".length());response.sendRedirect(redirectStr);}else{super.processTemplate(methodReturnStr,request,response);    // 比如:  "edit"}}}} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}
}

八 Filter

1 概述

  • 继承自 Filter 类,具有 init, doFilter, destory 方法
  • 可以使用注解的形式 @WebFilter("fruit.do"),也可以在 web.xml 中配置
  • 使用注解形式,当具有多个 Filter 时,按照类名的字典序组织过滤顺序
@WebFilter("*.do")
public class Demo01Filter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("before");//放行filterChain.doFilter(servletRequest,servletResponse);System.out.println("after");}@Overridepublic void destroy() {}
}

2 使用 Filter 实现事务的原子性

  • 利用 Filter 的机制,在放行之前撤销 conn 的自动提交,在返回时手动执行提交,并在捕获到异常时执行回滚
  • 要求事务执行的过程中,遇到的异常要层层向外抛出,直到被 Filter 捕获,而不是就地解决
  • conn 绑定 ThreadLocal 实例,再进一步保存到 ThreadLocalMap 中,保证了多个 DAO 共用同一个数据库连接,不会因为连接的关闭而自动提交,破坏事务的原子性

九 Listener

  • 监听某个组件的某种行为,当这种行为被监听到时,调用指定的方法
  • 可以用于完成初始化的创建,以及结束时的销毁操作
  • 一个应用实例:IOC 容器的 beanMap,当监听到 ServletContext 对象创建时,在监听器内进行初始化,而非在 Servletinit 方法中完成
// 监听上下文启动,在上下文启动的时候去创建IOC容器,然后将其保存到 application 作用域
// 后面DispatchedServlet 再从 application 作用域中去获取IOC容器@WebListener
public class ContextLoaderListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent servletContextEvent) {//1.获取ServletContext对象ServletContext application = servletContextEvent.getServletContext();//2.获取上下文的初始化参数(bean类型以及依赖关系)String path = application.getInitParameter("contextConfigLocation");//3.创建IOC容器BeanFactory beanFactory = new ClassPathXmlApplicationContext(path);//4.将IOC容器保存到application作用域application.setAttribute("beanFactory", beanFactory);}@Overridepublic void contextDestroyed(ServletContextEvent servletContextEvent) {// ...}
}

十 总结

后端学习 - JavaWeb相关推荐

  1. JavaWeb项目+MVC三层架构+Mysql+Tomcat+汽车配件销售系统前后端+可以用于学习javaweb项目入门

    JavaWeb项目+MVC三层架构+Mysql+Tomcat+汽车配件销售系统前后端+可以用于学习javaweb项目入门 可以用于课程设计.毕业设计的知识点入门学习 提示:此资源仅用于javaweb网 ...

  2. JavaWeb是什么?如何学习JavaWeb的体系

    java语言是当前最流行的开发语言之一.它的风格十分接近C++语言,是一个纯粹的面向对象的程序设计语言.Java继承了C++语言面向对象技术的核心,舍弃了C++语言中复杂和一些容易出错的特性,并在内存 ...

  3. Java后端学习路线分享

    Java后端学习路线?最近有些网友问我如何学习 Java 后端,还有些是想从别的方向想转过来,但都不太了解 Java 后端究竟需要学什么,究竟要从哪里学起,哪些是主流的 Java 后端技术等等,导致想 ...

  4. Java——Web后端学习路线

    文章目录 Java后端学习路线 第一部分: Java基础 第二部分: Java高级 第三部分: JavaWEB 第四部分: 主流框架和项目管理 第五部分: 分布式 微服务 并行架构 第六部分 : De ...

  5. 关于我在学习Javaweb时对Linux服务器安装配置jdk,tomcat,mysql的一些学习心得

    学习javaweb有一段时间了,最近刚好完成一个小的web项目. 勿喷!!!!!!!! 主要参考问题: 1.jdk,tomcat,mysql的安装配置. 2.tomcat,mysql的开机自启(我不会 ...

  6. 【java后端学习路线3】SSM+Linux+Git学习指南,985本海硕自学转码

    JAVA后端学习路线 路线总览 javase->Mysql->计算机网络->JavaWeb->Maven(1)->Spring->SpringMVC->Myb ...

  7. C/C++后端学习秘籍

    C/C++后端学习秘籍 文章目录 C/C++后端学习秘籍 前言 一.算法训练 1.拓扑顺序 2.功夫传人 3.二叉树中的最低公共祖先 二.工程项目训练 day1.Qt(串口的使用) 三.总结 前言 今 ...

  8. 大学四年Java后端学习路线规划,所有私藏资料我都贡献出来了,不看毕业肯定后悔!!!

    一定要走在学校前面自学,规划好自己的时间,按照自己的路线走. 大学四年Java后端学习路线规划,所有私藏资料我都贡献出来了,不看毕业肯定后悔!!! 学习路线与资源方法 一.第一件事,很重要!!! 二. ...

  9. Java后端学习路线(适合科班、非科班和已工作的仔)

    前言 今天看到一篇关于后端学习的硬核博文,感觉原博主说的很详细,涉及面很广,很适合正准备提升自己的小伙伴,故转载此文,与大家共勉,原作者用一个完整的电商系统作为切入点,带着大家看看,我们需要学些啥,原 ...

最新文章

  1. T-SQL 根据年月日创建DateTime
  2. Java Web学习总结-文件下载
  3. netmiko 记录日志_netmiko连接网络设备
  4. php 上传多个txt文件上传,一个多文件上传的例子(原创)
  5. (转)#ifndef的用法
  6. 超励志!从中专生到教授,他32岁成为国家杰青!
  7. Inception v2/v3原理与实现
  8. Unknown system variable 'tx_isolation'报错
  9. 2019CSP-S初赛知识点汇总
  10. 有一个XxY的网格,一个机器人只能走格点且只能向右或向下走,要从左上角走到右下角。请设计一个算法,计算机器人有多少种走法。 给定两个正整数int x,int y,请返回机器人的走法数目。保证x+y小
  11. YYText实现文本与下划线,删除线偏移
  12. 安化云台山:中秋月圆夜,赏月正当时
  13. leetcode每日一题1219. 黄金矿工 DFS深搜 暴力AC 正月初五祝大家财源广进~
  14. PROTEL99画异形焊盘
  15. 第二章 第十六节:字典的循环嵌套
  16. leetCode:33. 搜索旋转排序数组
  17. IntelliJ IDEA激活地址License server
  18. 开源的图可视化工具——Graphviz
  19. 开发APP的流程是怎样的
  20. Spring AOP之---基于JDK动态代理和CGLib动态代理的AOP实现

热门文章

  1. Asp.net core中Migration工具使用的交流分享
  2. 跨平台与云端创新,为企业和开发者开辟更广阔的未来
  3. [转]IIS7全新管理工具AppCmd.exe的命令使用
  4. Android之通过ContentResolver获取手机图片和视频的路径和生成缩略图和缩略图路径
  5. Android之让代码跑在主线程(无context上下文)的封装
  6. TCP/IP的基本工作原理
  7. Android之Bitmap的内存优化方案总结
  8. Android之ADB常用命令
  9. unity5.x Translate平移移动 以及GetComponent获取组件
  10. oracle 删除空间不足,oracle表空间扩容、创建、删除(解决表空间不足问题)