Please enable Javascript to view the contents

Java Web开发复习

 ·  ☕ 24 分钟
    🏷️

TODO:这仅仅是一篇草稿 个人使用,内容有待完善不建议查看

TODO: 此文档还需完善请留言或联系作者

以上内容是为了防止此文被发布

Java Web 开发复习

Web 服务器

SpringBoot 内嵌支持的三款 Servlet 容器 ( TomcatJettyUndertow ) , 当下比较推荐的是 Undertow

  • Tomcat

  • Jetty

  • Undertow: Undertow 是一个采用 Java 开发的灵活的高性能 Web 服务器,提供包括 阻塞和基于 NIO 的非堵塞机制。Undertow 是红帽公司的开源产品,是 Wildfly 默认的 Web 服务器。

    WildFly,原名 JBoss AS(JBoss Application Server) 或者 JBoss,是一套应用程序服 务器,属于开源的企业级 Java 中间件软件。

Undertow

Spring Boot :Undertow - 简书

为什么很多 SpringBoot 开发者放弃了 Tomcat,选择了 Undertow?

Undertow 完全嵌入,不需要容器,只需通过 构建器 API 即可快速搭建 web 服务器;并且 Undertow 的生命周期完全由嵌入应用程序控制。

使用 undertow builder API 启动 Undertow:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class HelloWorldServer {
    public static void main(final String[] args) {
        Undertow server = Undertow.builder()
                .addHttpListener(8080, "localhost")
                .setHandler(new HttpHandler() {
                    @Override
                    public void handleRequest(final HttpServerExchange exchange) throws Exception {
                        exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
                        exchange.getResponseSender().send("Hello World");
                    }
                }).build();
        server.start();
    }
}

上面示例启动一个服务器处理所有 HTTP 请求并返回"Hello World"字符串,这种方式的优 点是简单,是最通用的一种方式。

在 Srping Boot 中使用 Undertow:

Srping Boot 已经完全继承了 Undertow 技术,我们只需要引入 Undertow 的依赖即可(先 排除 tomcat,再添加 undertow)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

配置好以后,我们启动应用程序,发现容器已经替换为 Undertow

tomcat

Web 应用和虚拟目录的映射:

  • Web 应用程序:指提供浏览器访问的程序,通常也称为 Web 应用。
  • 一个 Web 应用由多个静态 web 资源和动态 web 资源组成,如:
    • HTML、CSS、js 文件
    • jsp 文件、java 程序、支持 jar 包
    • 配置文件等
    • 组成 web 应用的这些文件通常我们会使用一个目录组织,这个目录称之为:web 应用所在目录
  • Web 应用开发好后,若想提供外界访问,需要把 web 应用所在目录交给 web 服务器管理 ,这个过程称之为虚拟目录的映射(需在配置文件中配置)。

web 应用的基本组成结构:

mail
 |
 |------ html、jsp、css、js等文件
 |
 |------ WEB-INF/
            |
            |--- classes/   (Java类)
            |
            |--- lib/      (jar包)
            |
            |---- web.xml   (web应用的配置文件)

2017.02.15

tomcat 作为运行 Servlet 的容器,其基本功能是负责接收和解析来自客户的请求,同时把 客户的请求传送给相应的 Servlet,并把 Servlet 的响应结果返回给客户。

enter image description here

图:Servlet 容器响应客户请求访问特定 Servlet 的时序图(重要

时序图中: 对象 A 到对象 B 的箭头,表示发送消息,因此也可理解为 A 调用 B 的方法。

Tomcat 与 Servlet:

  • Servlet 规范规定,JavaWeb 应用必须采用固定的目录结构。
  • Servlet 规范把能够发布和运行 JavaWeb 应用的 Web 服务器称为Servlet 容器
  • tomcat 支持全部 JSP 以及 Servlet 规范。
  • tomcat 是使用 Java 写成,需要 java 运行环境。

Tomcat配置:

  • Tomcat 的启动和关闭脚本都会执行同目录下的 catalina.sh 脚本;catalina.sh 脚本允许输入命令行参数。比如 start、run、debug、embedded、stop 参数。为了方便可以自定义 tomcatStart 和 tomcatShut 这两个别名或脚本来控制
  • 配置虚拟主机:通过配置 server.xml 中配置 Host 标签来配置主机地址和网站路径。
  • 更改 tomcat 的 server.xml 配置文件后需要重启 tomcat 服务器。
  • web 应用可以被打包成一个 war 包,使用命令: jar -cvf news.war news;服务器会自动解压该 war
  • 包。
  • tomcat 中的 context 就代表了一个 web 应用。配置 context 元素中的 reloadable 元素,可以让 tomcat 自动加载更新后的 web 应用。
  • 在运行时,Servlet 容器的类加载器先加载 classes 目录下的类,再加载 lib 目录下的 jar 文件中的类。因此如果两个目录下存在同名的类,classes 目录下的类具有优先权。
  • tomcat 服务器本身还提供了一个前端控制台界面,其用户与密码在 tomcat 的 conf/tomcat-users.xml 文件来配置。该密码非常重要,不要透露。

HTTP 协议

可以通过 telnet 发送 http 请求,来获得服务器的 http 响应。并通过其测试 http 1.0 和 http 1.1 的区别。

对于请求下面的这样一个 html 页面,浏览器需要向服务器发送 5 次请求。第一次请求得 到整个 html 页面,后面三次请求获得三个图片和一个 js 文件。

1
2
3
4
<img src="1.jpg">
<img src="2.jpg">
<img src="3.jpg">
<script src="1.js">

上面的页面设计的不好,请求多次会加大服务器的压力;对此还有相应的优化方法

Chrome 开发者工具的简单使用:

直接在 Chrome 浏览器中打开开发者工具,切换到"Network"选项卡,然后按 F5 刷新页 面,在该选项卡中就会列出此次刷新请求的具体资源的详细信息,并会统计进行了多少次 请求(在 Name 栏的底部)。“Name"栏中列出了所有请求资源的名称,点击具体资源会出现 几个标签页,点击"Headers"可查看其对应的请求头,点击"Preview"可预览资源…

http 请求

请求行、请求头、空行、请求数据(如表单提交数据)

Post /hello.jsp HTTP/1.1
Accept: image/gif,image/jpeg, */*
Referer: http://localhost/login.html
...
Cache-Control: no-cache

username=Tom&password=1234&sublmit=submit   # Post方式的请求正文

用户如果没有设置,默认情况下浏览器向服务器发送的都是 get 请求,例如在浏览器直接 输入地址访问,点击超链接等。

如果想发送 post 请求,方法是在表单 form 标签中指定使用 post 。很显然当我们想要做 请求测试时会有些麻烦,

  • 对于 get 请求测试我们可以很方便的只需要在浏览器直接输入地址即可;
  • 但是对于 post 请求,难道我们还需要新建一个带有 form 表单前端页面来进行测试?为 了解决这一问题,我们可以使用现有的 Rest Client 工具来方便的进行测试,常见工具 有:
    • Advanced Rest Client (ARC)
    • Postman
    • Postwoman

(可以这样理解吗? 首先在浏览器输入网址使用 get 请求一个页面包含表单的网页, 然后我填写好表单后点击提交按钮,并且该表单的源代码中指定了使用 post,那么就会使 用 post 提交表单请求)

这样理解最好:
在浏览器中输入一个网址,回车后,此时浏览器向发送了一个 Get 请求(用于获取页面的源 代码??),而浏览器为了加载页面中的脚本和图片等(解析源代码??)又会自行发起请求 。比如打开一个 Github 上的仓库,共发起了 100 次请求。

发现老师讲的很好。有些东西如果自己摸索会话费很多时间,而且不一定能达到那种程度 的理解。要自己快速的动手去实施…来提高速度。可能前提是自己对这方面已经有了些 理解。今天发现多与人交流是可以锻炼自己的思维的。

get 提交的数据有长度限制(数据在请求头的第一行)
post 无限制(在请求头空行下方)

如果要在用户点击超链接时提交数据给服务器,则可直接在超链接的地址后添 加?usernaem=fan的这种 get 方式。

请求行: GET ...
请求头:

Accept: */*             -- 可接收的文件类型
Referer: http://localhost:8080/myhome/index.html       -- 从哪个网址进入此页面;可用于设置防盗链
User-Aget: ...                                          -- 客户机软件环境(比如系统,浏览器版本)
Range: 头指示服务器只传输部分Web资源。这个头可以用来实现断点续传功能。


Range:字段可以通过3种格式设置要传输的字节范围
Range: bytes=1000-2000      #
Range: bytes=1000-          #1000以后的所有内容
Range: bytes=1000           #传输最后1000个字节

get 请求示例:

一般不包含请求正文,因为没有意义。见此处的讨 论 HTTP GET with request body

GET /hello.htm HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive

post 请求示例:

包含请求正文

POST /cgi-bin/process.cgi HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Content-Type: application/x-www-form-urlencoded
Content-Length: length
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive

licenseID=string&content=string&/paramsXML=string

思考:观察一下 Idea 下的 Java Web 项目结构,其 webapp 目录下的 html 静态页面的 获取与 servlet 的关系。提示:这些 .html 文件是通过 get 请求获取的。

http 响应

HTTP /1.1 200 OK                        -- 状态行
Server: Microsoft-IIS/5.0               -- 多个响应头(服务器通过这些数据的描述信息,可以通知客户端如何处理它返回的数据)
Date: Thu, 13 Jul 2000 05:46:53 GMT
Content-Type: text/html
Content-Length: 2291
Cache-control: private
                                    -- 一个空行
<HTML>                              -- 响应正文(实体内容)
<head>
    <title>helloapp</title>
</head>
<BODY>
....
</body>
</html>

状态行:

格式:HTTP 版本号,状态码

状态码用于表示服务器对请求的处理结果,它是一个三位的十进制数。响应状态码分为 5 类:

状态码 含义
100~199 表示成功接收请求,要求客户端继续提交下一次请求才能完成整个处理过程
200~299 表示成功接收请求并已经完成整个处理过程,常用 200
300~399 为完成请求,客户端需进一步细化请求。例如,请求的资源已经移动一个新地址,常用 302(页面不位于此处,请访问别处,重定向),307 和 304(要求浏览器使用缓存)
400~499 客户端的请求有错误,常用 404(无此资源) 403(无权限)
500~599 服务器端出现错误,常用 500

响应头:

  • Location: 配合 302 状态码使用,用于告诉客户端去找谁(实现重定向,通过地址的改 变判断是否进行了重定向)。
  • Content-Encoding: 服务器通过这个头,指示数据的压缩格式
  • Content-Length:
  • Content-Type:
  • Last-Modified: 当前资源的缓存时间。
  • Refresh: 告诉浏览器隔多长时间刷新一次。(重要,聊天时获取最新数据,刷新实时图 表)
  • Content-Disposition: 告诉浏览器以下载方式打开数据
  • Expires: 缓存的保存时间,-1 和 0 表示不缓存。
  • Cache-Control: no-cache – 控制浏览器不要缓存
  • Pragma: no-cache – 控制浏览器不要缓存。 3 个都设置缓存(适用不同浏览器)

Servlet

Servlet 生命周期

创建 Servlet 实例有两个时机:

  • 客户端第一次请求某个 Servlet 时,Web 容器创建该 Servlet 的实例
  • Web 应用启动时立即创建 Servlet 实例,即 load-on-startup Servlet

每个 Servlet 的运行都遵循如下生命周期:

  1. 创建 Servlet 实例
  2. Web 容器调用 Servlet 的init方法,对 Servlet 进行初始化。
  3. Servlet 初始化后,将一直存在于容器中,用于响应客户端请求。如果客户端发送 Get 请求,容器将调用 Servlet 的 doGet 方法,(Post 类似);或者统一使用 service() 方法处理来响应用户请求。
  4. Web 容器决定销毁 Servlet 时,先调用 Servlet 的 destroy 方法,通常在关闭 Web 应用之时销毁 Servlet。

servlet 在初始化一次之后,就不再创建,servlet 是一个单例对象。

servlet 是单例

web.xml 文件

web.xml 被称为配置描述符,

Web.xml 配置(该文件在 web/WEB-INF 文件夹下)。

Servlet 3.0 相较于 Servlet 2.5:

新增了一些注解,简化的 javaweb 代码开发,可以省略 web.xml 配置文件 支 持异步处理(多线程技术) 支持可插性特性(书写的代码编译后生成的 class 文件可以 直接部署到其他项目的,自动加载执行)

一般的 web 工程中都会用到 web.xml,web.xml 主要用来配置,可以方便的开发 web 工程 。web.xml 主要用来配置 Filter、Listener、Servlet 等。但是要说明的是 web.xml 并不 是必须的,一个 web 工程可以没有 web.xml 文件。

response 和 request

后端会将前端发送的 request 转换为 JavaBean 对象

前后端数据交互

  • 前端需要使用到的技术为: Ajax

Ajax(Asynchronous JavaScript and XML)异步的 JS 和 XML。主要用来:

在前端进行局部刷新:即在不刷新整体页面的情况下,异步的向服务端发送请求,异步的接收服务端 的响应数据,异步的对当前页面的某一部分进行更新。

前端与服务器之间异步交互时使用的数据格式:

  • 都是以字符串的形式发送,并且这些字符串的格式为 JSON 字符串格式。

  • 前端: Ajax 原生是使用 XML 进行客户端和服务端之间的数据交互的。目前 XML 已经被 json 取 代了。

  • 服务端: 发送数据之前先将 JavaBean 转换成 JSON 格式的字符串。 服务端向客户端传递数据的时候,可能会有一些比较复杂的数据,比如:JavaBean 对象,或者是 JavaBean 对象的集合。这些数据使用文本形式传递太麻烦,可以把 JavaBean 对象转换成 JSON 格式的字符串,把这个字符串响应给客户端。客户端的 JS 在接收到这个字符串之后,会把它转换成一个 json 对象,传递给回调函数内部。

会话技术

略,见 content\posts\back-end\java-web\token认证.md

JSP

JSP 技术:它可以简化 html 书写,同时动态生成页面。

其实 JSP 底层就是一个 servlet

JSP 技术所开发的 Web 应用程序是基于 Java 的,它可以用一种简捷而快速的方法从 Java 程序生成 Web 页面

JSP基本已经不使用

过滤器

应用场景:

  • 登录权限检查
  • 浏览器发出的任何请求,通过过滤器统一处理中文乱码。

Maven

Maven 作用:

  • 依赖管理
  • 项目生命周期管理:编译、测试、打包、部署、运行。这些都是基于插件完成的,例如: 开发中使用的 tomcat 插件
  • maven 对工程分模块构建。 maven 工程有自己标准的 工程目录结构

补充项目结构图:

Maven 的常用命令:

  • clean 命令:清除编译产生的 target 文件夹内容,可以配合相应命令一起使用,如 mvn clean packagemvn clean test
  • complie 命令:该命令可以对 src/main/java 目录的下的代码进行编译
  • test 命令:测试命令,执行 src/test/java/ 下 junit 的测试用例
  • package 命令:mvn package,打包项目;打包后的项目会在 target 目录下找到
  • **install 命令:**打包后将其安装在本地仓库

Maven 插件:

Maven 是一个核心引擎,提供了基本的项目处理能力和建设过程的管理,以及一系列的插件 是用来执行实际建设任务。

maven 插件可以完成一些特定的功能。例如,

  • 集成 jdk 插件可以方便的修改项目的编译环境;
  • 集成 tomcat 插件后,无需安装 tomcat 服务器就可以运行 tomcat 进行项目的发布与测 试。

在 pom.xml 中通过 plugin 标签引入 maven 的功能插件。

依赖范围:(影响 编译、测试、运行)

  • compile:默认的依赖范围。在编译、测试、运行时都有效;最终会出现在 jar 包/war 包里
  • test:单元测试有效,其它无效。最后生成的 jar 包/war 包里没有这一类 jar 包
  • provided: jar 包在其它地方已经提供了,比如:jdk 已经提供了,或者 Tomcat/WebLogicWebSphere 里边提供了。 编译以及测试有效,最终不会出现在 war 包 或 jar 包 里
  • runtime:编译无效,测试有效,运行有效。比如:数据库驱动包。

JDBC与DataSource

JDBC(Java Data Base Connectivity,Java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。

DataSource是一个接口,它由驱动程序供应商实现。共有三种类型的实现:

  • 基本实现 - 生成标准的Connection对象
  • 连接池实现 - 生成自动参与连接池的Connection对象。此实现与中间层连接池管理器一起使用。
  • 分布式事务实现

Spring Boot默认使用 tomcat-jdbc 数据源;你也可以使用其它数据源,比如 Druid 。

Mybatis

它是一个持久层框架,解决项目对数据库的 CRUD 操作。

目前使用 Spring Data JPA 来实现数据持久化也是一种趋势。

Mybatis 采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化 操作。

ORM (Object Relational Mapping) 对象关系映射: 通过建立数据库表和 Java 实 体类的对应关系,从而实现操作实体类就相当于操作数据库表。

相关设计模式:工厂模式(Factory 工厂模式)、构造者模式(Builder 模式)、代理模 式

配置:

  • 配置文件:sqlMapConfig.xml:配置 mybatis 的环境,比如数据库的连接信息
  • 映射文件:UserDao.xml(一般对应 UserDao.java)或 UserMapper.xml
  • 注解

因为是 maven 项目,所有 xml 的配置文件应该放在 resources 下,而配置文件的读取 我们借助 Resources 类完成

几个插件:

  • 通用Mapper可以简化对单表的CRUD操作,
  • PageHelper分页插件可以帮我们自动拼接分页SQL,
  • 并且可以使用 MyBatis Geneator 来自动生成实体类(javabean),Mapper接口和Mapper xml代码,非常的方便。插件地址及作者链接https://gitee.com/free。

Mybatis 中的多表操作:

Mybatis 中的注解开发:

mybatis – MyBatis 3 | 简介

Spring

依赖注入

依赖注入:Dependency Injection。它是 spring) 框架核心 ioc 的具体实现。

那这种业务层和持久层的依赖关系,在使用 spring 之后,就让 spring 来维护了。简单的 说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。 这就是依赖注入。

能注入的数据:有三类

  1. 基本类型和 String
  2. 其他 bean 类型(自定义的 Bean,需要先在配置文件中或者注解中进行配置)
  3. 复杂类型/集合类型

能注入的方式:

  1. 第一种:使用构造函数提供
  2. 第二种:使用 set 方法提供(使用 p 名称空间注入)

更多理论知识请参考《Spring》

  • spring 中基于 XML 的 IOC 配置:

    applicationContext.xml,管理对象的创建。示例(spring01_di ),applicationContext.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
    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
    
        <!--
            ApplicationContext:只要一读取配置文件,默认情况下就会创建所有对象。(立即加载)
            BeanFactory:什么时候使,用什么时候创建对象。(延迟加载)
        -->
        <!--把对象的创建交给spring来管理-->
    
        <!-- 数据是写死的,那么该如何传入可变数据?? -->
    
        <!-- 构造器注入 -->
        <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl1">
            <constructor-arg name="name" value="Fan"/>
            <constructor-arg name="age" value="25"/>
            <!-- 对于Date这种引用类型(自定义的引用类型也一样),需要spring先为你创建一个对象,再通过ref引用 -->
            <constructor-arg name="birthday" ref="now"/>
        </bean>
    
        <bean id="now" class="java.util.Date"/>
    
        <!-- Set 注入:需要存在set方法 -->
        <bean id="accountService2" class="com.itheima.service.impl.AccountServiceImpl2">
            <property name="name" value="小明"/>
            <property name="age" value="20"/>
            <!-- 对于Date这中引用类型,需要spring先为你创建一个对象,再引用 -->
            <property name="birthday" ref="now2"/>
        </bean>
    
        <bean id="now2" class="java.util.Date"/>
    
        <!-- 使用p名称空间注入数据(本质还是调用set方法,所以需要存在set方法)-->
        <bean id="accountService3" class="com.itheima.service.impl.AccountServiceImpl2"
              p:name="胡歌" p:age="30" p:birthday-ref="now"/>
    
      <!-- 通过工厂方法注入 -->
      <!-- 使用场景:当一个类没有公开的构造方法时,比如单例类,但是它提供了静态方法getInstance()来获取实例 -->
      <bean id="stage" class="com.spring.entity.Stage" factory-method="getInstance"/>
    
      <!-- 指定Bean的作用域:scope,默认为 singleton -->
      <bean id="ticket" class="com.spring.entity.Ticket" scope="prototype"/>
    
      <!-- 指定初始化和销毁Bean时执行的方法 -->
      <bean id="light" class="com.spring.entity.Light"
            init-method="turnOnTheLight" destroy-method="turnOffTheLight"/>
    
        <!-- 如何注入集合属性 -->
        <bean id="accountService4" class="com.itheima.service.impl.AccountServiceImpl3">
            <!--数组-->
            <property name="myArrays">
                <set>
                    <value>aaa</value>
                    <value>bbb</value>
                    <value>ccc</value>
                </set>
            </property>
    
            <!--list集合(更多集合省略)-->
            <property name="myList">
                <set>
                    <value>AAAAA</value>
                    <value>BBBBB</value>
                    <value>张艺谋</value>
                </set>
            </property>
      </bean>
    
        <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
            <property name="runner" ref="queryRunner"/>
        </bean>
    
        <!-- 我们还可在此配置数据源(url,用户密码等):
         这里我们将配置信息写入jdbc.properties文件 -->
      <!--加载jdbc.properties 使用 ${jdbc.url}-->
        <context:property-placeholder location="jdbc.properties"/>
      <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driver}"/>
            <property name="jdbcUrl" value="${jdbc.url}"/>
            <property name="user" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <!--创建QueryRunner的对象
            QueryRunner queryRunner = new QueryRunner(ds);
         -->
        <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
            <constructor-arg name="ds" ref="dataSource"/>
        </bean>
    

    在测试类中的使用:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
        @Test
        public void test01() {
            ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
            //我们并非直接new一个AccountService
            AccountService as = (AccountService) ac.getBean("accountService");
            as.saveAccount();
        }
    
        @Test
        public void test02() {
            ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 这里 accountService2 是 配置文件中 bean 标签下的 id
            AccountService as = (AccountService) ac.getBean("accountService2");
            as.saveAccount();
        }
    
    

    另外可参考:

    Spring 构造器注入 | MrBird

    Spring Setter 方法注入 | MrBird

  • spring 中基于注解的 IOC

    参考 《Spring 第三天笔记》

    (1) 用于创建对象的注解@Component,它们的作用相当于<bean>标签

    @Component :标注一个普通的 spring Bean 类。它的 value 属性用于指定 bean 的 id。当我们不写时,它的默认值是当前类名,且首字母改小写。

    以及 @Component衍生注解

    • @Controller :用来修饰 WEB 层 类 ( 控制层 )(springMVC 延用了该注解 )
    • @Service :用来修饰 service 层类 ( 业务层)
    • @Repository :用来修饰 DAO 层类 ( 持久层)

    (2)用于注入数据的

    先来看一下之前的示例:

    1
    2
    
    <!-- 存在一个 id(名称) 和 类型 -->
    <bean id="ticket" class="com.spring.entity.Ticket"/>
    

    Snipaste_2020-02-06_17-15-07.png

    所以这里会出现按 id(名称) 和按类型方式注入的注解:

    @Autowired :自动按照类型注入。

    @Qualifier :在按照类型注入的基础之上再按照名称(id)注入。不能独立使用 。

    @Resource :直接按照 bean 的 id 注入。如果 id 属性不存在,可以再按照类型注入 。它可以独立使用;JSR-250 标准(基于 jdk)。它的 name 属性:用于指定 bean 的 id,如果指定 name,只能按照 bean 的 id 注入,不能按照类型注入。

注意: 1)以上三个注入都只能注入其他 bean 类型的数据,而基本类型和 String 类 型无法 使用上述注解实现。 2)集合类型的注入只能通过 XML 来实现。

@Value : 用于注入基本类型和 String 类型的数据。 它的 value 属性,用于指定数 据的值。它可以使用 spring 的 SpEL。

示例(spring02_anno_ioc):

1
2
3
4
5
6
7
8
9
//@Repository(value = "accountDao1")
// 对于 value属性我们可以省略 value
@Repository("accountDao1")
public class AccountDaoIml implements AccountDao {
    @Override
    public void saveAccount() {
        System.out.println("DAO: 保存账户 11111");
    }
}
 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
@Service("accountService1")
//@Scope("prototype")
public class AccountServiceImpl1 implements AccountService {

    //用于属性:set方法可以省略
    //@Autowired
    //@Qualifier("accountDao1")
    //@Resource(name = "accountDao1") // JDK 10 中存在问题
    private AccountDao accountDao;

    public AccountServiceImpl1() {
        System.out.println("AccountServiceImpl1 构造方法");
    }

    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }

    //用于 set 方法
    @Autowired
    @Qualifier("accountDao1")
    //@Resource(name = "accountDao1")
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    // 注解配置初始化和销毁
    @PostConstruct
    public void init() {
        System.out.println("AccountServiceImpl1对象 初始化");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("AccountServiceImpl1对象 销毁了");
    }
}

测试类:

 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
   @Test
    public void test01() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService as = (AccountService) ac.getBean("accountService1");
        as.saveAccount();
    }

    @Test
    public void test02() {
        // 测试 Scope
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService as1 = (AccountService) ac.getBean("accountService1");
        AccountService as2 = (AccountService) ac.getBean("accountService1");
        System.out.println(as1 == as2);
        //as.saveAccount();
    }

    @Test
    public void test03() {
        //测试 初始化 和 销毁
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService as = (AccountService) ac.getBean("accountService1");
        as.saveAccount();
        ((ClassPathXmlApplicationContext) ac).close();
    }

除此之外:我们还需要指定 spring 在创建容器时要扫描的包;我们可以在

applicationContext.xml 中配置:

1
2
3
<!--告知spring在创建容器时要扫描的包,配置所需要的标签不是在
  beans 的约束中,而是一个名称为context名称空间和约束中-->
<context:component-scan base-package="com.itheima"></context:component-scan>

也可以通过,创建 SpringConfiguration.java 配置类加注解的方式来实现;这样一来我们 就可以完全替换 xml 文件。一般我们将其放在 config 包下,示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/**
 * SpringConfiguration.java
 * 配置类,作用与之前的 applicationContext.xml 一样
 */
@Configuration  //表明它是一个配置类
// 指明它要扫描的包
@ComponentScan(value = {"com.itheima"})
// 下面两个注解是可选的
// 我们还可以导入其他配置类 JdbcConfig.class
@Import(value = JdbcConfig.class)
//指明属性文件 jdbc.properties,其中classpath,表示类路径下
@PropertySource("classpath:jdbc.properties")
public class SpringConfiguration {

}
 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
// JdbcConfig.class 配置类
public class JdbcConfig {

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    /**
     * @Bean 注解:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
     *    属性name:用于指定 bean 的 id,默认为当前方法的名称
     *
     * 如果方法有参数,spring 框架会去容器中查找有没有可用的bean对象,
     * 如果有bean对象,将对象通过方法的形参注入到方法中使用。
     * 查找的方式和Autowired 注解的作用是一样的
     *
     * @param dataSource
     * @return 一个QueryRunner对象
     */
    @Bean(name = "runner")
    @Scope("prototype")
    //有多个数据源,我们可以使用 @Qualifier 明确指定使用哪个数据源
    public QueryRunner createQueryRunner(@Qualifier(value = "ds2") DataSource dataSource) {
        return new QueryRunner(dataSource);
    }

    //数据源一
    @Bean(name = "ds")
    public DataSource createDataSource() {
        ComboPooledDataSource ds = null;
        try {
            ds = new ComboPooledDataSource();
            ds.setDriverClass(driver);
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);

        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
        return ds;
    }

    // 数据源二
    @Bean(name = "ds2")
    public DataSource createDataSource2() {
        ComboPooledDataSource ds = null;
        try {
            ds = new ComboPooledDataSource();
            ds.setDriverClass("com.mysql.cj.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/itheima_ssm");
            ds.setUser("root");
            ds.setPassword("fan123");

        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
        return ds;
    }
}

测试(上的变化):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    @Test
    public void findAllAccount() {
        //注意这一语句,与之前的不同之处
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

        AccountService accountService = (AccountService) ac.getBean("accountService");
        List<Account> allAccount = accountService.findAllAccount();
        for (Account account : allAccount) {
            System.out.println(account);
        }
    }

另外可参考: (推荐 ⭐)

Spring 自动装配 Bean | MrBird

Spring 自动检测 Bean | MrBird

Spring 创建这个类的时候,默认采用的单例的模式进行创建的

Spring Bean 生命周期:

bean装载到spring应用上下文中的典型生命过程.png

自动化装配 bean

Spring 从两个角度来实现自动化装配:

  • 组件扫描(component scanning):Spring 会自动应用上下文中所创建的 bean
  • 自动装配(autowiring):Spring 自动满足 bean 之间的依赖。

组件扫描和自动装配组合在一起就能发挥出强大的威力,它们能够将你的显 式配置降低 到最少。

  1. 创建可被发现的 bean:

    • @Component:作为组件类,并为其创建 bean
    • @ComponentScan:扫描某个包的组件,即扫描包下的@Component。 将@ComponentScan放在 javaConfig 类(配置类)上使用。
  2. 实现自动装配

    使用@Autowired,用于方法之上;或使用@Inject。自动装配就是让 Spring自 动满足 bean 依赖的一种方法,在满足依赖 的过程中,会在 Spring 应用上下文中 寻找匹配某个 bean 需求的其他 bean

Spring 整合 Junit

观察上面的测试代码,我们仍然需要使用 ApplicationContext 示例的 getBean() 方 法;但当我们将 Spring 和 Junit 进行了整合后变成:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
// 如果使用的是配置类则是下面的注解
//@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceImplTest2 {
  // 自动注入
    @Autowired
    private AccountService as = null;

    @Test
    public void findAllAccount() {
        List<Account> allAccount = as.findAllAccount();
        for (Account account : allAccount) {
            System.out.println(account);
        }
    }
}

SpEL 表达式

在 Spring 配置过程中有时会用到 SpEL,所以有必要了解一下。

Spring 表达式语言(Spring Expression Language,SpEL)

SpEL 的写法: #{ SpEL expression }

JSP 中的 EL 表达式的基本形式为:${表达式} , SpEL 也兼容这种写法

请参考:

Spring 事务控制与 AOP

见《Spring 5 第三天,第四天笔记》

Spring Batch 批处理 -解道 Jdon

Spring MVC

Spring MVC 4.2.4 RELEASE 中文文档完整翻译稿

我们的开发架构一般都是基于两种形式,

  • 一种是 C/S 架构,也就是 客户端 / 服务器
  • 另一种是 B/S 架构,也就是 浏览器 / 服务器

在 JavaEE 开发中,几乎全都是 基于 B/S 架构的开发。那么在 B/S 架构中,系统标 准的三层架构包括:表现层、业务层、持久层。

  • 表现层:也就是我们常说的 web 层。它负责接收客户端请求,向客户端响应结果, 通常客户端使用 http 协议请求 web 层,web 需要接收 http 请求,完成 http 响应。 表现层包括展示层和控制层:

    • 控制层负责接收请求,
    • 展示层负责结果的展示。

    表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响 应给客户端。表现层的设计一般都使用 MVC 模型。(MVC 是表现层的设计模型,和其 他层没有关系

  • 业务层:也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项目的 需求息息相关。web 层依赖业务层,但是业务层不依赖 web 层。

    业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性。(也 就是我们说的,事务应该放到业务层来控制)

  • 持久层:也就是我们是常说的 dao 层。负责数据持久化,包括数据层即数据库和数 据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口 ,业务层需要通过数据访问层将数据持久化到数据库中。通俗的讲,持久层就是和数据库 交互,对数据库表进行增删改查的。

服务器端分层三层框架:

graph LR;
  A(浏览器) --"请求参数"--> B("表现层: Spring MVC") -->
  C("业务层:Srping框架") --> D("持久层:MyBatis")
  B --"响应结果"--> A
  C --> B
  D --> C

MVC 设计模式:

  • M(Model)模型:通常指的就是我们的数据模型。JavaBean
  • V (View)视图:通常指的就是我们的 jsp 或者 html。作用一般就是展示数据的。通常 视图是依据模型数据创建的。
  • C(Controller)控制器:是应用程序中处理用户交互的部分。作用一般就是处理程序逻 辑的。 Servlet

Spring MVC 已经成为目前最主流的 MVC 框架之一;它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持 RESTful 编程风格的请求 。

Request 流程,生命周期:

生命周期

Spring MVC 搭建过程:

  1. 在 IDEA 中创建 Maven Web 工程

  2. 添加 Maven 依赖

  3. 配置 web.xml 文件

    配置前端控制器:DispatcherServlet

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
        <servlet>
            <servlet-name>dispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!-- 配置Servlet的初始化参数,读取spring mvc 的配置文件,创建spring 容器 -->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc.xml</param-value>
            </init-param>
            <!-- 配置Tomcat启动时加载的对象 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>dispatcherServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    
  1. 配置 springmvc.xml 文件

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
        <!-- 开启组件的扫描-->
        <context:component-scan base-package="com.itheima"/>
    
        <!-- 配置视图解析器 -->
        <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!-- 在此示例中,存在/WEB-INF/pages/目录,且该目录下有一个叫做 -->
            <property name="prefix" value="/WEB-INF/pages/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
    
        <!-- 注意:Spring MVC中需要用到各种处理器适配器等,我们可以使用bean标签一个个导入,也可以直接使用下面的标签直接一次性导入 -->
      <!-- 简写为:启用Spring mvc -->
        <mvc:annotation-driven/>
    
  1. 编写前端页面 (这里是 jsp 页面)

    编写 index.jsp ;创建 /WEB-INF/pages/ 目录,并在该目录下创建 success.jsp

  2. 编写 Controller 类

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    @Controller
    @RequestMapping(path = "/user") //路径可拼接
    public class HelloController {
        /**
         * 请求参数
         * 访问路径是: /user/hello
         */
        @RequestMapping(path = "/hello")
        public String sayHello() {
            System.out.println("Hello SpringMVC");
            return "success";
        }
    
        /**
         * 访问地址:/user/testRequestMapping?username=test
         */
        @RequestMapping(path = "/testRequestMapping", method = {RequestMethod.GET},
                params = {"username=test"}, headers = "accept")
        public String testRequestMapping() {
            System.out.println("测试 testRequestMapping");
            return "success";
        }
    }
    

另外可参考:

⭐ 推荐 :@RequestMapping 与@GetMapping 和@PostMapping 等新注释

RESTful Web 服务

RESTful Web 服务控制器只返回对象,对象数据作为 JSON / XML 直接写入 HTTP 响应。

我们可以使用 @RestController 注解 来方便的完成此功能。

@RestController 注解相当于 @ResponseBody@Controller

Spring Boot

再添两个重量级仓库:

入门示例 :spring-boot-demo-helloworld

推荐:什么是 Spring Boot? -解道 Jdon

Spring Boot 干货系列:(一)优雅的入门篇 | 嘟嘟独立博客

Spring Boot:启动原理解析

@SpringBootApplication = @EnableAutoConfiguration + @Configuration + @ComponentScan

推荐使用 Spring Initializr 初始化 Spring Boot 项目

Spring Boot 的入门需要理解的概念:

  • Spring Boot 父级依赖的概念
  • 起步依赖 spring-boot-starter-xx 的概念
  • 应用入口类的作用

Spring Boot 2.x 基础教程:工程结构推荐 - 简书

热部署:有多种方式,这里只介绍一种

使用 spring-boot-devtools 进行热部署

Spring Cloud

dyc87112/SpringCloud-Learning: Spring Cloud 基础教程,持续连载更新中

Spring Cloud Alibaba 与 Spring Boot、Spring Cloud 之间不得不说的版本关系 | 程序猿 DD

Spring Cloud 基于 Spring Boot 构建

如何做到前后端分离

我的职业是前端工程师【七】:你真的懂前后端分离吗? - 简书

前后端分离

RESTful Web 服务与传统的 MVC 开发一个关键区别是返回给客户端的内容的创建方式 :传统的 MVC 模式开发会直接返回给客户端一个视图,但是 RESTful Web 服务一般会将 返回的数据以 JSON 的形式返回,这也就是现在所推崇的前后端分离开发。

发送前端请求

  • Rest Client (简单)
  • 通过 Swagger 的后台 API 接口
  • 还有一个关键的技术:ajax

Mock 模拟后端响应

服务器端渲染和客户端渲染 - Wayne-Zhu - 博客园

服务端渲染 · GitBook

6. quarkus-quickstarts 感觉不错

Quarkus 开源的 Java 多种框架 demo 项目集合。这些示例项目可以快速启动、结构清 晰,初学者可用作 Java 的实战项目,老手可以当作项目脚手架。启动示例:

mvn quarkus:dev
mvn clean package -Pnative
./target/amqp-quickstart-1.0-SNAPSHOT-runner
您的鼓励是我最大的动力
alipay QR Code

Felix
作者
Felix
如无必要,勿增实体。

3

目录