web前端

前端的代码如何转换成网页

通过浏览器的内核转化(解析和渲染)成用户看到的网页

web标准

  • 也叫网页标准,又一系列标准组成,大部分由W3C(World Wide Web Consortium,万维网联盟)负责制定。
  • 三个组成部分:
    • HTML:负责网页的结构(页面元素和内容)
    • CSS:负责网页的表现(页面元素的外观、位置等页面样式,如:颜色、大小等)
    • JavaScript:负责网页的行为(交互效果,如:轮播图)

HTML、CSS

定义

  • HTML(HyperText Markup Language):超文本标记语言

    • 超文本:超越了文本的限制,处理文字信息,还可以定义图片、音频、视频等内容
    • 标记语言:由标签构成的语言
      • HTML语言都是预定义好的,如:使用<a>展示超链接,<img>展示图片,<video>展示视频
      • HTML代码直接在浏览器中运行,HTML标签由浏览器解析
  • CSS(Cascading Style Sheet):层叠样式表,用于控制页面的样式(表现)。

HTML的结构标签示例

1
2
3
4
5
6
7
8
9
<html>
<head>
<title>标题</title>
</head>
<body>
<h1>Hello HTML</h1>
<img src="1.jpg"/>
</body>
</html>

HTML的一些语法特点

  • 标签不区分大小写
  • 标签的属性既可以使用双引号,也可以使用单引号
  • 语法结构松散(语法不严格)

CSS引入方式

  • 行内样式(不推荐):写在标签的style属性中

    • 只针对当前一个标签

    • 格式:属性名:属性值;

    1
    <h1> style="xxx: xxx; xxx: xxx;">标题</h1>
  • 内嵌样式:写在style标签中(可以写在页面任何位置,但通常约定在head标签中)

    • 对于当前HTML文件的所有该标签都生效
    1
    2
    3
    4
    5
    6
    <style>
    h1 {
    xxx: xxx;
    xxx :xxx;
    }
    </style>
  • 外联样式:写在一个单独的.css文件中(通过link标签从网页中引入)(link写在<head>中)

1
2
3
4
h1 {
xxx: xxx;
xxx: xxx;
}
1
<link rel="stylesheet" href="css/news.css">

CSS选择器

用来选取需要设置样式的元素(标签)

  • 元素选择器:如上
  • id选择器:
1
2
3
#hid {
color: red;
}
1
<h1 id="hid">CSS id Selector</h1>
  • 类选择器
1
2
3
.cls {
color: red;
}
1
<h1 class="cls">CSS class Selector</h1>

优先级:id选择器 > 类选择器 > 元素选择器

页面布局

盒子:页面中所有的元素(标签),都可以看成一个盒子,由盒子将页面的元素包含在一个矩形区域内,通过盒子的视角更方便的进行页面布局。

组成:内容区域(content)、内边距区域(padding)、边框区域(border)、外边距区域(margin)

与<div>和<span>一起使用

HTML实现

图片标签:<img>

格式:<img src=”…” width=”…” height=”…”>

  • src:图片资源路径

    • 绝对路径
      • 绝对磁盘路径
      • 绝对网络路径
    • 相对路径(推荐):./ :当前目录(即HTML文件所在目录)(可省略);../ :上一级目录
  • width:宽度(px,像素;%,相对于父元素的百分比)

  • height:高度(一般高度和宽度只写一个,就可以等比例缩放)

标题标签:<h1>

只有<h1> 到 <h6>

格式:<h1>……</h1>

水平线标签:<hr>

格式:<hr>

只需写一个<hr>就可以

无意义标签:<div> / <span>

没有语义的布局标签,用于将不是任何标签的文字进行格式的修改

特点:

  • <div>:一行只显示一个(独占一行);宽度默认时父元素的宽度,高度默认由内容撑开;可以设置宽高
  • <span>:一行可以显示多个(组合行内元素);宽度和高度默认由内容撑开;不可以设置宽高

超链接:<a>

格式:<a href=”…” target=”…”>央视网</a>

属性:href:资源访问的url;target:在何处打开(_self:默认值,在当前页面;_blank:在空白页面打开)

超链接默认蓝色加下划线,如果想修改需要用text-decoration来修改

视频标签:<video>

src:视频的url

controls:显示播放控件

width:宽度

height:高度

音频标签:<audio>

src:音频的url

controls:显示播放控件

段落标签:<p>

文本加粗标签:<b> / <strong>

换行标签:<br>

表格标签:<table> / <tr> / <td>

  • <table>:定义表格整体,包含多个<tr>。border:表格边框宽度;width:表格宽度;cellspacing:单元之间的空间
  • <tr>:表格的行,包含多个<td>
  • <td>:表格单元格(普通),如果是表头单元格,可以替换为<th>(自带居中和加粗)

表单标签:<form>

负责数据的采集,如:注册、登陆等数据的采集

表单项:不同类型的input元素、下拉列表、文本域等

  • <input>:定义表单项,通过type属性控制输入形式(text(默认)、password(密码字段)、radio(单选按钮)、checkbox(复选框)、file(文件上传)、date/time/datetime-local(日期/时间/日期时间)、number(数字输入框)、email(邮箱输入框)、hidden(隐藏框)、submit/reset/button(提交。重置/可点击按钮))(单选框和复选框中每个input均需要用<label></label>包含,尤其是单选框,表示不可多选)

  • <select>:定义下拉列表,<option>定义列表项

  • <textarea>:定义文本域

注意:表单项必须有name属性才可以提交

表单项中value属性可以控制传入的值

属性

  • action:规定当提交表单时向何处发送表单数据—URL(不指定就默认提交到当前页面)
  • method:规定用于发送表单数据的方式—POST/GET(默认为GET)
    • GET:表单数据拼接在url后面(?username=java),大小有限制
    • POST:表单数据在请求体中携带,大小没有限制

CSS实现

颜色:color

格式:color: red;

颜色表示形式

  • 关键字:red、blue
  • rgb表示法:rgb(0,0,255) (红绿蓝三原色)
  • 十六进制表示法:#ff0000 (每两位代表rgb的一位)(可以简写成#f00,即在每个rgb的两位相同时写一个即可)

字体:font-size

记得后面加上px

文本装饰:text-decoration

none:默认,标准文本

underline:下划线

overline:上划线

line-through:穿过文本的一条线

首行缩进:text-indent

常与<p>一起使用

设置行高:line-height

常与<p>一起使用

对齐方式:text-align

包括center、left、right

盒子模型相关:

width

height

box-sizing:指定宽高指的是哪个部分的(eg. border-box)

background-color

padding:内边距(上 右 下 左)(一致时可以只写一个)(写三个时分别为上 左右 下;两个时分别为上下 左右)(可以写auto,代表浏览器自动计算)

border:边框(宽度 线条类型 颜色)

margin:外边框(上 右 下 左)

(如果只设置某一个方位的边框、内边距、外边距,可以在属性名后加上-位置,如:padding-top\padding-left…)

JavaScript

特点

  • 跨平台、面向对象的脚本语言(不需要编译)。用来控制网页行为,能使网页可交互
  • JS与Java是完全不同的语言,无论是概念还是设计,但是基础语法类似

引入方式

  • 内部脚本:将JS代码定义在HTML页面中
    • JS代码必须位于<script></script>标签之间
    • 在HTML文档中,可以在任意地方放置任意数量的<script>
    • 一般会把脚本置于<body>元素的底部,可以改善显示速度
1
2
3
<script>
alert("Hello JS")
</script>
  • 外部脚本:将JS代码定义在外部JS文件中,然后引入到HTML页面中
    • 外部JS文件中,只包含JS代码,不包含<script>标签
    • <script>标签不能自闭合(自闭合:本来要用一个配对的结束符号来关闭,然而它却自己关闭了,如:<script src=”js/demo.js”>)
1
<script src="js/demo.js"></script>

demo.js 如下:

1
alert("Hello JS")

基础语法

  • 区分大小写
  • 每行结尾分号可写可不写

输出语句:

  • 弹出警告框:window.alert()
  • 在浏览器中展示:document.write()
  • 写入浏览器控制台:console.log()

变量:

  • 用var关键字(variable)来声明变量
  • JS是一门弱类型语言,变量可以存放不同类型的值
  • 变量名需要遵循以下规则:
    • 组成字符可以是任何字母、数字、下划线或美元符号$
    • 数字不能开头
    • 建议使用驼峰命名

特点

  • 作用域大,全局变量
  • 可以重复定义

新增

  • let关键字:与var类似,但是是局部变量,而且不能重复定义
  • const关键字:声明只读的常量

数据类型:

  • 原始类型:number、string、boolean、null、undefined
  • 引用类型—object

(使用typeof运算符可以获取数据类型)

运算符:

  • 算术运算符
  • 赋值运算符
  • 比较运算符:(多一个===)
  • 逻辑运算符
  • 三元运算符

==与===的区别:==会进行类型转换,===不会进行类型转换,===会同时比较数据类型和数据

函数

定义:用function关键字

方式:

  • 方式一

    1
    2
    3
    function add(a, b){
    return a + b;
    }
  • 方式二

    1
    2
    3
    var add = function(a, b){
    return a + b;
    }

注意:

  • 形参不需要指明类型
  • 返回值不需要指明类型
  • 函数调用可以传递任意多个的参数,函数只会取前几个作为参数

对象

Array

定义

  • var 变量名 = new Array(元素列表);

  • var 变量名 = [元素列表];

访问

arr[索引] = 值;

特点:长度可变,类型可变(可以存储任意类型的数据)

属性

  • length:返回数组元素数量

方法

  • forEach ():遍历数组中有值的元素,并调用一次传入的函数
1
2
3
arr.forEach(function(e)){
console.log(e)
}

每次调用function,并将获得的值存入e

可简写为箭头函数:(…) => {…} (小括号中放形参列表)

1
2
3
arr.forEach((e) => {
console.log(e);
})
  • push():在末尾添加新元素,并返回新长度
  • splice():删除元素(传两个参:从哪个索引开始,删几个)

String

创建方式

  • var 变量名 = new String(“…”);
  • var 变量名 = “…”;

属性

  • length

方法

  • charAt()
  • indexOf():检索字符串位置
  • trim():去除字符串两边的空格
  • substring()

JSON

JS自定义对象

1
2
3
4
5
var 对象名 = {
属性名1: 属性值1,
属性名2: 属性值2,
函数名称: function(形参列表){}
};

函数可省略为: 函数名称 () {}

概念:JavaScript Object Notation,JS对象标记法

  • JSON是通过JS对象标记法书写的文本、
  • 由于语法简单,层次结构鲜明,现多用于作为数据载体,在网络中进行数据传输

定义

1
var 变量名 = '{"key1": value1, "key2": value2}';

value的数据类型:

  • 数字:直接写整数或浮点数
  • 字符串:放在双引号中
  • 逻辑值:直接写
  • 数组:放在方括号中
  • 对象:放在花括号中
  • null

JSON字符串转换为JS对象

var jsObject = JSON.parse(jsonStr);

(转换为对象后就可以通过.属性拿到内部某个属性值)

JS对象转换为JSON字符串

var jsonStr = JSON.stringify(jsObject);

BOM

概念:Browser Object Model,浏览器对象模型,允许JS与浏览器对话,JS将浏览器的各个组成部分封装为对象

组成

  • Window:浏览器窗口对象
    • 获取:
      • 直接使用window,其中window.可以省略。(比如window.alert(“Hello”)等价于alert(“Hello”))
    • 属性
      • history
      • location
      • navigation
    • 方法
      • alert():显示带有一段消息和一个确认按钮的警告框
      • confirm():显示带有一段消息以及确认按钮和取消按钮的对话框(返回布尔值)
      • setInterval():按照指定的周期(以毫秒计)来调用函数或计算表达式(执行多次)
      • setTimeout():在指定的毫秒数后调用函数或计算表达式(执行一次)
  • Navigator:浏览器对象
  • Screen:屏幕对象
  • History:历史记录对象
  • Location:地址栏对象
    • 介绍:地址栏对象
    • 获取:使用windows.location获取,其中windows.可以省略
    • 属性:
      • href:设置或返回完整的URL

DOM

概念:Document Object Model,文档对象模型

分类:将标记语言的各个组成部分封装为对应的对象

  • Document:整个文档对象
  • Element:元素对象(每一个标签)
  • Attribute:属性对象
  • Text:文本对象
  • Comment:注释对象

作用

  • 改变HTML元素的内容
  • 改变HTML元素的样式(CSS)
  • 对HTML DOM事件作出反应
  • 添加和删除HTML元素

组成

  • Core DOM - 所有文档类型的标准模型
    • Document:整个文档对象
    • Element:元素对象(每一个标签)
    • Attribute:属性对象
    • Text:文本对象
    • Comment:注释对象
  • XML DOM - XML文档的标准模型
  • HTML DOM - HTML文档的标准模型
    • Image:<img>
    • Button:<input type=’button’>
    • ……

获取方式

  • HTML中的Element对象可以通过Document对象获取,而Document对象是通过window对象获取的
  • Document对象中提供了以下获得Element元素对象的函数:
    • 根据id属性值,返回当Element对象:var h1 = document.getElementById(‘h1’);
    • 根据标签名,返回数组:getElementsByTagName
    • 根据name属性值,返回数组:getElementsByName
    • 根据class属性值,返回数组:getElementsByClassName

事件监听

事件:

HTML事件是发生在HTML元素上的”事情“。比如:点击按钮、按下键盘……

事件监听:

JS可以在事件被侦测到时执行代码

事件绑定

  • 方式一:通过HTML标签中的事件属性进行绑定
1
2
3
4
5
6
7
<input type="button" onclick="on()" value="按钮1">

<script>
function on(){
alert('我被点击了');
}
</script>
  • 方式二:通过DOM元素属性进行绑定
1
2
3
4
5
6
7
<input type="button" id="btn" value="按钮2">

<script>
document.getElementById('btn').onclick=function(){
alert('我被点击了');
}
</script>

常见事件

  • onclick:点击
  • onblur:失去焦点
  • onfocus:获得焦点
  • onload:页面或图像完全加载
  • onsubmit:表单提交
  • onkeydown:按下键盘上某个键
  • onmouseover:鼠标移到某元素之上
  • onmouseout:鼠标从某元素移开

Vue

概述

是一套前端框架,免除原生JS中的DOM操作,简化书写。

基于MVVM(Model-View-ViewModer)思想,实现数据的*双向绑定**,将编程的关注点放在数据上。

格式

  • 新建HTML页面,引入Vue.js文件
1
<script src="js/vue.js"></script>
  • 在JS代码区域,创建Vue核心对象,定义数据类型
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
new Vue{{
el: "#app",
data: {
message: “Hello Vue"
},
methods: {
handle: functiono(){
alert("点击");
}
}
}}
</script>
  • 编写视图
1
2
3
4
5
<div id="app">
<input type="text" v-model="message">
{{message}}
<input type="button" value="点击" v-on:click="handle()">
</div>

插值表达式

  • 形式:
  • 内容可以是:变量、三元运算符、函数调用、算术运算

指令

  • v-bind:动态绑定属性值,如:href、css样式等
  • v-model:为表单元素创建双向数据绑定(可简写为@)
  • v-on:为html标签添加事件
  • v-if:为true时渲染某元素,否则不渲染(就是没有了)
  • v-else-if:跟if组合用
  • v-else:跟if组合用
  • v-show:展示某元素(区别:切换的是display属性的值,true则展示,否则隐藏,而不是没有)
  • v-for:列表渲染,变量容器的元素或对象的属性

注意:

  • 通过v-bind或者v-model绑定的变量,必须在数据模型中声明

生命周期

概念:指一个对象从创建到销毁的整个过程

八个阶段:每触发一个生命周期事件,会自动执行一个生命周期方法(钩子)

  • beforeCreate
  • created
  • beforeMount
  • mounted:挂载完成——Vue初始化成功,HTML页面渲染成功。(发送请求到服务端,加载数据)
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed

组件

以.vue结尾

组成部分

  • <template>:模板部分,由它生成HTML代码
  • <script>:控制模板的数据来源和行为,相当于js文件
  • <style>:css样式部分

开发流程

  • 创建页面,完成页面的整体布局规划
  • 布局中各个部分的组件实现
  • 列表数据的异步加载,并渲染展示

路由

前端路由:URL中的hash(#号)与组件之间的对应关系

Vue Router

组成:

  • VueRouter:路由器类,根据路由请求在路由视图中动态渲染选中的组件
  • <router-link>:请求链接组件,浏览器会解析为<a>
    • eg. <router-link to=”/dept”>部门</router-link>
  • <router-view>:动态视图组件,用来渲染展示与路由路径对应的组件
    • eg. 写在APP.vue的<template>中:<router-view></router-view>

web后端

maven

是一款管理和构建java项目的工具

作用

  • 依赖管理:方便快捷的管理项目依赖的资源(jar包),避免版本冲突问题
  • 统一项目结构:提供标准、统一的项目结构
  • 项目构建:标准跨平台的自动化项目构建方式

组成

  • groupId:项目隶属组织名称(通常是域名反写)
  • artifactId:项目名称
  • version:项目版本号

依赖

  • 依赖具有传递性:分为直接依赖和间接依赖

    • 为了主动断开依赖的资源,需要排除依赖——<exclusions>(被排除的资源无需指定版本)
  • 依赖范围:用<scope>…</scope>设置其作用范围

    • 包括主程序有效、测试程序有效、参与打包运行三种范围
  • 生命周期:为了所有的maven项目构建过程进行抽象和统一,包括以下三套生命周期:

    • clean:清理工作
      • clean:移除上一次构建生成的文件
    • default:核心工作
      • compile:编译项目源代码
      • test:使用合适的单元测试框架进行测试(junit)
      • package:将编译后的文件打包
      • install:安装项目到本地仓库
    • site:生成报告、发布站点等

    注意:在同一套生命周期中当运行后面的阶段时,前面的阶段都会运行。

起步依赖

简化依赖配置,引入起步依赖就相当于引入了这一块业务开发所需要的全部依赖。原理是Maven的依赖传递。

  • spring-boot-starter-web:包含了web应用开发所需的常见依赖

  • spring-boot-starter-test:包含了单元测试所需的常见依赖

起步依赖的版本无须声明,在父工程中进行了统一管理

HTTP

概念:

Hyper Text Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则。

特点:

  • 基于TCP协议:面向连接,安全
  • 基于请求-响应模型:一次请求对应一次响应
  • 无状态:对于事务处理没有记忆能力。每次请求-响应都是独立的。
    • 缺点:多次请求间不能共享数据
    • 优点:速度快

请求数据格式:

  • 请求行:请求数据第一行(请求方式、资源路径、协议)

  • 请求头:第二行开始,格式key:value

    • Host:请求的主机名
    • User-Agent:浏览器版本
    • Accept:浏览器能接收的资源类型
    • Accept-Language:浏览器偏好的语言,服务器可以据此返回不同语言的网页
    • Accept-Encoding:浏览器可以支持的压缩类型
    • Content-Type:请求主体的数据类型
    • Content-Length:请求主体的大小(单位:字节)
  • 请求体:Post请求,存放请求参数

请求方式-GET:请求参数在请求行中,没有请求体,如:/brand/findAll?name=OPPO&status=1。GET请求大小是有限制的。

请求方式-POST:请求参数在请求体中,POST请求大小是没有限制的。

响应格式:

  • 响应行:响应数据第一行(协议、状态码、描述)
    • 200:客户端请求成功
    • 404:请求资源不存在,一般是URL输入错误,或者网站资源被删除了
    • 500:服务器发生不可预期的错误
  • 响应头:第二行开始,格式key:value
    • Content-Type:响应内容的类型
    • Content-Length:响应内容的长度(字节数)
    • Content-Encoding:响应压缩算法
    • Cache-Control:客户端应该如何缓存
    • Set-Cookie:告诉浏览器为当前页面所在的域设置cookie
  • 响应体:存放响应数据

Web服务器

是一个软件程序,对HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。主要功能是”提供网上信息浏览服务“。

  • 对HTTP协议的操作进行封装,简化web程序开发
  • 部署web项目,对外提供网上信息浏览服务

Tomcat

  • 一个轻量级的web容器,支持servlet、jsp等少量javaEE规范
  • 也称为web容器、servlet容器(servlet程序需要依赖于Tomcat才能运行)

(HTTP协议默认端口号为80,如果将Tomcat端口号改为80,则将来访问Tomcat时将不需要输入端口号)

基于SpringBoot开发的web应用程序,内置了tomcat服务器,当启动类运行时,会自动启动内嵌的tomcat服务器。

请求响应

概述

请求响应:

  • 请求(HttpServletRequest):获取请求数据
  • 响应(HttpServletResponse):设置响应数据
  • BS架构:Browser/Server,浏览器/服务器架构模式,客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。(优点:维护方便;缺点:体验一般)
  • CS架构:Client/Server,客户端/服务器架构模式。(优点:体验好;缺点:开发、维护麻烦)

简单参数

原始方式:

  • Controller方法形参中声明HttpServletRequest对象
  • 调用对象的getParameter(参数名)

SpringBoot中接收简单参数

  • 请求参数名与方法形参变量名相同
  • 会自动进行类型转换

@RequestParam注解

  • 方法形参名称与请求参数名称不匹配,通过该注解完成映射
  • 该注解的required属性默认为true,代表请求参数必须传递

实体参数

简单实体参数:请求参数名与形参对象属性名相同即可

复杂实体对象:请求参数名与形参对象属性名相同,按照对象层次结构关系即可

数组集合参数

数组:请求参数名与形参中数组变量名相同,可以直接使用数组封装

集合:请求参数名与形参中集合变量名相同,通过@RequestParam绑定参数关系(因为默认是用数组传递的)

日期参数

使用@DateTimeFormat注解完成日期参数格式转换

eg.(@DateTimeFormat(pattern = “yyyy-MM-dd HH:mm:ss”) LocalDateTime updateTime)

JSON参数

JSON数据键名与形参对象属性名相同,需要使用@RequestBody标识

路径参数

通过请求URL直接传递参数,使用{…}来标识路径参数,需要使用**@PathVariable**获取路径参数

一个方法中可以有多个路径参数,每个路径参数前均需要注解

响应

@ResponseBody

  • 类型:方法注解、类注解
  • 位置:Controller方法/类上
  • 作用:将方法返回值直接响应,如果返回值类型是实体对象/集合,将会转换成JSON格式响应
  • 说明:@RestController=@Controller+@ResponseBody(所以一般写@RestController)

统一响应结果

Result(code, msg, data)

分层解耦

三层架构

为了单一职责原则,一个类或一个方法只做一件事,所以web开发有了三层架构

  • controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据
  • service:业务逻辑层,处理具体的业务逻辑
  • dao:数据访问层(Data Access Object)(持久层),负责数据访问操作,包括数据的增删改查

好处:复用性强、便于维护、利于扩展

分层解耦

  • 内聚:软件中各个功能模块内部的功能联系
  • 耦合:衡量软件中各个层/模块之间的依赖、关联的程度
  • 软件设计原则:高内聚低耦合

控制反转:Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器)

依赖注入:Dependency Injection,简称DI。容器为应用程序提供运行时所依赖的资源。

Bean对象:IOC容器创建、管理的对象

IOC和DI:

  • Service层和Dao层的实现类,交给IOC容器管理
    • 在类前加上@Component
  • 为Controller和Service注入运行依赖的对象
    • 在成员变量前加上@Autowired

Bean的声明

要把某个对象交给IOC容器管理,需要在对应的类上加上如下注解之一

  • @Component:声明Bean的基础注解。以下三个都是它的衍生注解。不属于以下三类时使用。
  • @Controller:标注在控制器类上。
  • @Service:标注在业务类上。
  • @Repository:标注在数据访问类上。

注意:

  • 声明bean时,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
  • 使用以上四个注解都可以声明bean,但在springboot集成web开发中,声明控制器bean只能用@Controller

Bean组件扫描

  • 前面声明的bean四大注解,要想生效,还需要被组件扫描注解@ComponentScan扫描
  • @ComponentScan注解虽然没有显示配置,但是实际上已经包含在了启动类声明注解@SpringBootApplication中,默认扫描的范围是启动类所在包及其子包

Bean注入

  • @Autowired注解,默认是按照类型进行,如果存在多个相同类型的bean,将会报错
  • 解决方案:
    • @Primary:在@Service前加。
    • @Qualifier:在@Autowired后加。eg.@Qualifier(“empServiceA”)
    • @Resource:代替@Autowired的位置。eg.@Resource(name=”empServiceB”)

@Resource和@Autowired的区别

  • 后者是spring框架提供的注解,而前者是JDK提供的注解
  • 后者默认是按照类型注入,而前者默认是按照名称注入

开发规范

Restful

REST(REpresentation State Transfer),表述性状态转换,是一种软件架构风格。

  • URL定位资源
  • HTTP动词描述操作
  • 前后端交互统一响应结果Result(包括code响应码、msg响应信息、data返回数据)

注意

  1. REST是风格,是约定方式,不是规定,可以打破
  2. 描述模块的功能通常使用复数来表示此类资源,而非单个资源

日志小技巧

  1. 添加注解@Slf4j
  2. 添加Log.info()

eg.

1
2
3
4
5
6
7
8
9
@Slf4j
@RestController
public class DeptController{
@GetMapping("/depts")
public Result list(){
Log.info("查询全部部门数据");
return Result.success();
}
}

@RequestMapping

一个完整的请求路径,应该是类上的@RequestMapping的value属性+方法上的@RequestMapping的value属性。

@RequestParam

设置请求参数默认值:

@RequestParam(defaultValue=”1”)

PageHelper分页插件

  • 引入依赖:pagehelper-spring-boot-starter

  • 使用:

    • EmpServiceImpl:
    1
    2
    3
    PageHelper.startPage(pageNum, pageSize);
    List<Emp> list = empMapper.list();
    Page<Emp> page = (Page<Emp>)list;
    • EmpMapper:
    1
    2
    @Select("select * from emp")
    public List<Emp> list();

文件上传

简介

  • 文件上传,是指将本地图片、视频、音频等文件上传到服务器,供其他用户浏览或下载的过程
  • 文件上传在项目中应用非常广泛,eg. 发微博、发朋友圈

前端页面三要素

1
2
3
4
5
6
<form action="/upload" method="post" enctyoe="multipart/form-data">
姓名:<input type="text" name="username"><br>
年龄:<input type="text" name="age"><br>
头像:<input type="file" name="image"><br>
<input type="submit" value="提交">
</form>
  • 表单提交方式 method=”post”
  • 表单属性 enctyoe=”multipart/form-data”:表单提交时作为多个数据提交
  • 表单项 type=”file”

服务端接收文件

1
2
3
4
5
6
7
@RestController
public class UploadController {
@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image){
return Result.success();
}
}
  • 通过MultipartFile这个API接收上传的文件,上传上来临时文件,当请求响应结束后,临时文件会自动删除

本地存储

将上传的文件存储到本地服务器磁盘中

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
public class UploadController {
@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) throws Exception{
//获取原始文件名
String originalFilename = image.getOriginalFilename();
//获得文件后缀
int index = originalFilename.lastIndexOf(".");
String extname = originalFilename.substring(index);
//构造唯一的文件名(不重复) - uuid(通用唯一识别码)
String newFileName = UUID.randomUUID().toString() + extname;
//将文件存储在服务器磁盘目录中
image.transferTo(new File("E:\\images\\" + newFileName));
return Result.success();
}
}

修改最大允许上传文件

在SpringBoot中,文件上传,默认单个文件允许最大为1M

1
2
3
4
#配置单个文件最大上传大小
spring.servlet.multipart.max-file-size=10MB
#配置单个请求最大上传大小(一次请求可以上传多个文件)
spring.servlet.multipart.max-request-size=100MB

缺点:前端无法直接访问、磁盘空间限制、磁盘损坏

第三方服务—阿里云

通用思路

  • 准备工作
    • 注册阿里云(实名认证)
    • 充值
    • 开通对象存储服务(OSS)
    • 创建bucket—存储空间是用户用于存储对象(Object,就是文件)的容器,所有的对象都必须隶属于某个存储空间
    • 获取AccessKey(密钥)
  • 参考官方SDK编写入门程序
  • 集成使用

SDK:Software Development Kit的缩写,软件开发工具包,包括辅助软件开发的依赖(jar包)、代码示例等

集成步骤:

  • 引入阿里云OSS上传文件工具类
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
@Component
public class AliOSSUtils {
@Value("${aliyun.oss.endpoint}")
private String endpoint;
@Value("${aliyun.oss.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.oss.accessKeySecret}")
private String accessKeySecret;
@Value("${aliyun.oss.bucketName}")
private String bucketName;
public String upload(MultipartFile image) throws Exception {
//获取上传文件的输入流
InputStream inputStream = file.getInputStream();
//获取原始文件名
String originalFilename = image.getOriginalFilename();
//获得文件后缀
int index = originalFilename.lastIndexOf(".");
String extname = originalFilename.substring(index);
//构造唯一的文件名(不重复) - uuid(通用唯一识别码)
String newFileName = UUID.randomUUID().toString() + extname;
//上传文件到OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);
//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + newFileName;
ossClient.shutdown();
return url;
}
}
  • 上传图片接口开发
1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class UploadController {

@Autowired
private AliOSSUtils aliOSSUtils;

@PostMapping("/upload")
public Result upload(MultipartFile image) throws Exception{
String url = aliOSSUtils.upload(image);
return Result.success(url);
}
}

配置文件(application.properties)

参数配置化

将技术或服务所使用的参数直接配置在配置文件中,交给配置文件进行统一的管理和维护。

java代码中使用**@Value**注解通常用于外部配置的属性注入,具体用法:@Value(“${配置文件中的key}”)

yml格式文件

优势:

  • XML:臃肿
  • properties:层次结构不清晰
  • yml/yaml:简洁,数据为中心(推荐)

基本语法:

  • 大小写敏感
  • 数值前必须有空格,作为分隔符
  • 使用缩进来表示层级关系,缩进时,不允许使用Tab键,只能用空格(但是idea里可以将Tab自动识别为空格)
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
  • #表示注释,从这个字符一直到行尾,都会被解析器忽略

数据格式:

  • 对象/Map集合:
1
2
3
4
user:
name: zhangsan
age: 18
password: 123456
  • 数组/List/Set集合:
1
2
3
4
hobby:
- java
- game
- sport

@ConfigurationProperties

改进方式

在utils文件夹中添加AliOSSProperties:

1
2
3
4
5
6
7
8
9
@Data
@Component
@ConfigurationProperties(prefix="aliyun.oss")
public class AliOSSProperties {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}

原先的AliOSSUtils改为:

1
2
3
4
5
6
7
8
9
10
11
@Component
public class AliOSSUtils {
@Autowired
private AliOSSProperties aliOSSProperties;

private String endpoint = aliOSSProperties.getEndpoint;
private String accessKeyId = aliOSSProperties.getAccessKeyId;
private String accessKeySecret = aliOSSProperties.getAccessKeySecret;
private String bucketName = aliOSSProperties.getBucketName;
……
}

@ConfigurationProperties与@Value

相同:

  • 都是用来注入外部配置的属性的

不同:

  • @Value注解只能一个一个的进行外部属性的注入
  • @ConfigurationProperties可以批量的将外部的属性配置注入到bean对象的属性中

登录功能

登录校验

会话技术

  • 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中包含多次请求和响应。
  • 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一个浏览器,以便在同一次会话的多次请求间共享数据。
  • 会话跟踪方案:
    • 客户端会话跟踪技术:Cookie
    • 服务端会话跟踪技术:Session
    • 令牌技术

Cookie

  • 优点:HTTP协议支持的技术
  • 缺点:
    • 移动端APP无法使用
    • 不安全,用户可以自己禁用
    • 不能跨域(跨域的三个维度:协议、IP/域名、端口)

Session

  • 优点:存储在服务端,安全
  • 缺点:
    • 服务器集群环境下无法直接使用Session
    • Cookie的缺点

令牌技术:(主流)

  • 优点:
    • 支持PC端、移动端
    • 解决集群环境下的认证问题
    • 减轻服务器端存储压力(不需要服务器端存储任何的数据)
  • 缺点:需要自己实现

JWT令牌

JSON Web Token,定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。

组成

  1. Head(头),记录令牌类型,签名算法。通过Base64编码获得。
  2. Payload(有效载荷),携带一些自定义信息、默认信息。通过Base64编码获得。
  3. Signaure(签名),防止Token被篡改,确保安全性。将header、payload加入指定密钥,通过指定签名算法计算而来。

Base64:基于64个可打印字符(A-Z,a-z,0-9,+,/)来进行二进制数据的编码方式

场景:登录认证

  1. 令牌生成:登录成功后,生成令牌,并返回给前端
  2. 令牌校验:再请求到达服务端后,对令牌进行统一拦截、校验。后续每个请求,都需要携带JWT令牌,系统在每次请求处理之前,先校验令牌,通过后再处理

生成

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void genJwt(){
Map<String,Object> claims = new HashMap<>();
claims.put("id",1);
claims.put("username","Tom");
String jwt = Jwts.builder()
.setClaims(claims) //自定义内容(载荷)
.signWith(SignatureAlgorithm.HS256,"itheima") //签名算法
.setExpiration(new Date(System.currentTimeMillis() + 12*3600*1000)) //有效期
.compact();
System.out.println(jwt);
}

校验

1
2
3
4
5
6
7
8
@Test
public void parseJwt(){
Claims claims = Jwts.parser()
.setSigningKey("itheima") //指定签名密钥
.parseClaimsJws("……") //解析令牌
.getBody();
System.out.println(claims);
}

注意

  • JWT校验时使用的签名密钥,必须和生成JWT令牌时使用的密钥是配套的
  • 如果JWT令牌解析校验时报错,则说明JWT令牌被篡改或失效了,令牌非法

过滤器Filter

概述

  • Filter过滤器,JavaWeb三大组件(Servlet、Filter、Listener)之一
  • 过滤器可以把资源的请求拦截下来,从而实现一些特殊的功能
  • 过滤器一般完成一些通用的操作,如:登录校验、统一编码处理、敏感字符处理等

使用

  • 定义Filter:定义一个类,实现Filter接口,并重写其所有方法
1
2
3
4
5
6
7
8
9
10
11
12
13
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException { //初始化方法,Web服务器启动,创建Filter时调用,只调用一次
Filter.super.init(filterConfig);
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){ //拦截到请求时,调用该方法,可调用多次
System.out.println("拦截到了请求");
chain.doFilter(request, response);
}
public void destroy(){ //销毁方法,服务器关闭时调用,只调用一次
Filter.super.destroy();
}
}
  • 配置Filter:Filter类加上@WebFilter注解,配置拦截资源的路径,引导类上加@ServletComponentScan开启Servlet组件(Java Web)支持(加在@SpringBootApplication之前)

拦截路径

  • 具体路径:/login
  • 目录拦截:/emps/*
  • 拦截所有:/*

过滤器链

  • 一个web应用中,可以配置多个过滤器,多个过滤器形成一个过滤器链
  • 顺序:优先级按照过滤器类名(字符串)的自然排序

登录校验过滤器

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
@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
//1.获取请求url
String url = req.getRequestYRL().toString();

//2.判断请求url中是否包含login,若包含,说明时登录操作,放行
if(url.contains("login")){
chain.doFilter(request, response);
return;
}

//3.获取请求头中的令牌(token)
String jwt = req.getHeader("token");

//4.判断令牌是否存在
if(!StringUtils.hasLength(jwt)){
Result error = Result.error("NOT_LOGIN");
//手动转换 对象->json (阿里巴巴fastJSON)
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return;
}

//5.解析token
try{
JWTUtils.parseJWT(jwt);
} catch (Exception e) {
e.printStackTrace();
Result error = Result.errOr("NOT_LOGIN");
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return;
}

//6.放行
chain.doFilter(request, response);
}
}

拦截器Interceptor

概述

  • 概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行。
  • 作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。

使用

  • 定义拦截器,实现HandlerInterceptor接口,并重写其所有方法
1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override //目标资源方法执行前执行,返回true则放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
return true;
}
@Override //目标资源方法执行后执行
public void postHandle(HttpServletRequest req, HttpServletResponse resp, Object handler, ModelAndView modelAndView) {
}
@Override //视图渲染完毕后执行,最后执行
public void afterCompletion(HttpServletRequest req, HttpServletResponse resp, Object handler, Exception ex) {
}
}
  • 注册拦截器
1
2
3
4
5
6
7
8
9
@Configuration
public class WebConfig implements WebMvcConfigurer{
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
}
}

*这里的/*表示拦截所有

拦截路径

  • addPathPatterns:需要拦截的资源
  • excludePathPatterns:不需要拦截的资源

拦截路径:

  • /*:一级路径(包括/depts,不包括/depts/1)
  • /**:任意级路径

过滤器于拦截器同时存在

先执行过滤器,过滤器放行后执行拦截器

登录校验拦截器

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
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override //目标资源方法执行前执行,返回true则放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
//1.获取请求url
String url = req.getRequestYRL().toString();

//2.判断请求url中是否包含login,若包含,说明时登录操作,放行
if(url.contains("login")){
return true;
}

//3.获取请求头中的令牌(token)
String jwt = req.getHeader("token");

//4.判断令牌是否存在
if(!StringUtils.hasLength(jwt)){
Result error = Result.error("NOT_LOGIN");
//手动转换 对象->json (阿里巴巴fastJSON)
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return false;
}

//5.解析token
try{
JWTUtils.parseJWT(jwt);
} catch (Exception e) {
e.printStackTrace();
Result error = Result.errOr("NOT_LOGIN");
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return false;
}

//6.放行
return true;
}
}

Filter与Interceptor

  • 接口规范不同:前者需要实现Filter接口,后者需要实现HandlerInterceptor接口
  • 拦截范围不同:前者拦截所有资源,后者之后拦截Spring环境中的资源

异常处理

方案一

在Controller的方法中进行try…catch处理(代码臃肿,不推荐)

方案二

全局异常处理器(简单,推荐)

1
2
3
4
5
6
7
8
@RestControllerAdvice
public class GlobalExceptionHandler{
@ExceptionHandler(Exception.class) //捕获所有的异常
public Result ex(Exception ex){
ex.printStackTrace();
return Result.error("操作失败");
}
}

@RestControllerAdvice = @ControllerAdvice + @ResponseBody

事务管理&AOP

事务管理

概念

事务是一组操作的集合,它是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败。

操作

  • 开启事务(一组操作开始前,开启事务):start transaction / begin;
  • 提交事务(这组操作全部成功后,提交事务):commit;
  • 回滚事务(中间任何一个操作出现异常,回滚事务):rollback;

注解

  • 注解:@Transactional
  • 位置:业务(service)层的方法上、类上、接口上
  • 作用:将当前方法交给spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务

事务属性—回滚

rollbackFor

  • 默认情况下,只有出现RuntimeException才回滚异常。rollbackFor属性用于控制出现何种的异常类型才会回滚事务。
  • @Transactional(rollbackFor = Exception.class) 代表所有的异常都会回滚。

事务属性—传播行为

propagation

  • 事务传播行为:指当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。
    • REQUIRED:(默认值)需要事务,有则加入,无则创建新事务。
    • REQUIRES_NEW:需要新事务,无论有无,总是创建新事务。

eg. @Transactional(propagation = Propagation.REQUIRES_NEW) 代表会创建一个新的事务。

当被调用的事务方法和原先的事务方法在一个事务中时,一旦出现问题就会全部回滚,所以用REQUIRES_NEW可以事务之间不影响,比如下订单前需要记录日志,无论订单保存成功与否都需要保证日志记录能够记录成功

AOP

概述

  • AOP:Aspect Oriented Programming(面向切片编程、面向方面编程),就是面向特定方法编程。
  • 实现:动态代理是面向切面编程最主流的实现,而SpringAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定方法进行编程。
  • 场景:记录操作日志,权限控制,事务管理……
  • 优势:代码无侵入,减少重复代码,提高开发效率,维护方便

开发步骤

场景:统计每一个业务方法的执行耗时

  • 导入依赖
  • 编写AOP程序:针对于特定方法根据业务需要进行编程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Slf4j
@Component
@Aspect //AOP类
public class TimeAspect {
@Around("execution(* com.itheima.service.*.*(..))") // 切入点表达式,这里表示业务层的所有方法
public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
// 1.记录开始时间
long begin = System.currentTimeMillis();
//2.调用原始方法运行
Object result = joinPoint.proceed();
//3.记录结束时间,计算方法执行耗时
long end = System.currentTimeMillis();
log.info(joinPoint.getSignature() + "方法执行耗时: {}ms", end - begin);

return result;
}
}

核心概念

  • 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
  • 通知:Advice,指重复的逻辑,也就是共性功能(最终体现为一个方法)
  • 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时会被应用
  • 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
  • 目标对象:Target,通知所应用的对象

执行流程

一旦进行AOP程序的开发,最终运行的就不再是原始的目标对象,而是基于目标对象所生成的代理对象。

通知类型

  • @Aroud:环绕通知,标注的通知方法在目标方法前后都被执行
  • @Before:前置通知,标注的通知方法在目标方法前被执行
  • @After:后置通知(最终通知),标注的通知方法在目标方法后被执行,无论是否有异常都会执行
  • @AfterReturning:返回后通知,标注的通知方法在目标方法后被执行,有异常不会执行
  • @AfterThrowing:异常后通知,标注的通知方法发生异常后执行

注意

  • @Around环绕通知需要自己调用ProceedingJoinPoint.proceed()来让原始方法执行,其他通知不需要考虑目标方法执行
  • @Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值

@PointCut

该注解的作用是将公共的切点表达式抽取出来,需要用到时引用该切点表达式即可。

1
2
3
4
5
6
7
@Pointcut("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")
public void pt(){}

@Around("pt()")
public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
……
}

private:仅能在当前切面类中引用

public:在其他外部的切面类中也能引用

通知顺序

当有多个切面的切入点都匹配了目标方法时,多个通知方法都会被执行。

  • 不同切面类中,默认按照切面类的类名字母顺序
    • 目标方法前的通知方法:字母排名靠前的先执行
    • 目标方法后的通知方法:字母排名靠前的后执行
  • 用@Order(数字)加在切面类上来控制顺序
    • 目标方法前的通知方法:数字小的先执行
    • 目标方法后的通知方法:数字小的后执行

切入点表达式

execution(…):根据方法的签名来匹配

主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配

语法:execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)

其中?表示可省略:

  • 访问修饰符:比如pulic,protected
  • 包名.类名.:不建议省略
  • throws 异常:是方法上声明抛出的异常,不是实际抛出的异常

通配符:

  • *:单个独立的任意符号,可以通配任意返回值、包名、类名、方法名,任意类型的一个参数,也可以通配包、类、方法名的一部分
  • ..:多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数

注意:根据业务需要,可以使用&&、||、!来组合比较复杂的切入点表达式

书写建议:

  • 所有业务方法名在命名时尽量规范。方便切入点表达式快速匹配(如:查询类方法均以find开头等)
  • 描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性
  • 在满足业务需要前提下,尽量缩小切入点的匹配范围(如:包名匹配尽量不使用..,使用*匹配单个包)

@annotation(…):根据注解匹配

用于匹配标识有特定注解的方法

eg. @annotation(com.itheima.aop.MyLog)可以匹配所有有@MyLog注解的方法

需要在com.itheima.aop目录下新建一个命名为MyLog的Annotation:

1
2
3
4
@Retention(RetentionPolicy.RUNTIME) //运行时有效
@Target(ElementType.METHOD) //在Method上生效
public @interface MyLog {
}

连接点

在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等。

  • 对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint
  • 对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型

示例:

  • joinPoint.getTarget().getClass().getName() // 获取目标类名
  • joinPoint.getSignature() // 获取目标方法签名
  • joinPoint.getSignature().getName() // 获取目标方法名
  • joinPoint.getArgs() // 获取目标方法运行参数

获取当前用户

获取request对象,从请求头中获取到jwt令牌,解析令牌获取出当前用户的id

1
2
3
4
5
6
7
@Autowired
private HttpServletRequest request;
……{
String jwt = request.getHeader("token");
Claims claims = JwtUtils.parseJWT(jwt);
Integer operateUser = (Integer)claims.get("id");
}

SpringBoot

配置

properties > yml > yaml

虽然SpringBoot支持多种格式配置文件,但是在项目开发时,推荐使用统一的一种配置(yml是主流)

Bean管理

获取bean

  • 默认在Spring项目启动后,会把bean都创建好放在IOC容器中(主要针对于默认的单例非延迟加载的bean,正常情况下还会受到作用域和延迟初始化影响),如果想要主动获得这些bean,方式如下:
    • 根据name获取bean:Object getBean(String name)
    • 根据类型获取bean:<T> T getBean(Class<T> requiredType)
    • 根据name获取bean(带类型转换):<T> T getBean(Stirng name, Class<T> requiredType)

bean的作用域

  • singleton:容器中同名称的bean只有一个实例(单例)(默认)
  • prototype:每次使用该bean时会创建新的实例(非单例)

通过@Scope注解来配置作用域

eg.

1
2
3
4
5
6
//@Lazy
@Scope("prototype")
@RestController
RequestMapping("/depts")
public class DeptController{
}

注意

  • 默认singleton的bean,在容器启动时被创建,可以使用@Lazy注解来延迟初始化(延迟到第一次使用时)
  • 实际开发大部分的bean是单例的,一般不需要配置Scope属性

第三方bean

如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component及衍生注解声明bean的,就需要用到@Bean注解

1
2
3
4
5
6
7
@SpringBootApplication
public class SpringbootWebConfig2Application{
@Bean //将方法返回值交给IOC容器管理,成为IOC容器的bean对象
public SAXReander saxReader(){
return new SAXReader();
}
}

若要管理第三方bean对象,建议进行集中分类配置,可以通过@Configuration注解声明一个配置类

1
2
3
4
5
6
7
@Configuration //配置类
public class CommonConfig {
@Bean
public SAXReader saxReader(){
return new SAXReader();
}
}

注意

  • 通过@Bean注解的name/value属性指定bean名称,如果未指定,默认是方法名
  • 如果第三方bean需要依赖其他的bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配

@Component及衍生注解与@Bean注解的使用场景

  • 项目中自定义的使用@Component及衍生注解
  • 项目中引入第三方的使用@Bean注解

SpringBoot原理

起步依赖

maven的依赖传递

自动配置

当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要手动去声明,从而简化了开发,省去了繁琐的配置操作。

自动配置方案

  • 方案一:@ComponentScan组件扫描
1
2
3
4
@Component({"com.example","com.itheima"})  // 还需要包括原先的目录,因为是声明后是覆盖关系
@SpringBootApplication
public class SpringbootWebConfig2Application {
}

使用繁琐,性能低

  • 方案二:@Import导入。使用@Import导入的类会被Spring加载到IOC容器中,导入形式主要包括:
    • 导入普通类
    • 导入配置类
    • 导入ImportSelector接口实现类
    • @EnableXxx注解,封装@Import注解(推荐)
1
2
3
4
@Impor({TokenParser.class,HeaderConfig.class})
@SpringBootApplication
public class SpringbootWebConfig2Application {
}

自动配置原理

@SpringBootApplication

该注解标识在SpringBoot工程引导类上,是SpringBoot中最重要的注解,由三部分组成:

  • @SpringBootConfiguration:该注解与@Configuration注解作用相同,用来声明当前也是一个配置类
  • @ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包
  • @EnableAutoConfiguration:SpringBoot实现自动化配置的核心注解

注意:SpringBoot会根据@Conditional注解条件装配,而不是全部注册为IOC容器的bean。

@Conditional
  • 作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring IOC容器中
  • 位置:方法、类
  • @Conditional本身是一个父注解,派生出大量的子注解:
    • @ConditionalOnClass:判断环境中是否有对应字节码文件(name = “…”),有才注册bean到IOC容器
    • @ConditionalOnMissingBean:判断环境中是否有对应的bean(可以根据类型value属性或名称name属性来判断是否存在),没有才注册bean到IOC容器。通常用来设置一个默认的bean对象。
    • @ConditionalOnProperty:判断配置文件中是否有对应属性和值(name = “…”, havingValue = “…”),有才注册bean到IOC容器

自定义starter

在实际开发中,经常会定义一些公共组件,提供给各个项目团队使用。而在SpringBoot的项目中,一般会将这些公共组件封装为SpringBoot的starter。

起步依赖分类:

  • SpringBoot官方:spring-boot-starter-xxx
  • 其他技术提供:xxx-spring-boot-starter

eg.

需求:自定义aliyun-oss-spring-boot-starter,完成阿里云OSS操作工具类AliyunOSSUtils的自动配置

目标:引入起步依赖之后,要使用阿里云OSS,注入AliyunOSSUtils直接使用即可

步骤

  • 创建aliyun-oss-spring-boot-starter模块(依赖管理功能)
  • 创建aliyun-oss-spring-boot-autoconfigure模块(自动配置功能),在starter中引入该模块
  • 在aliyun-oss-spring-boot-autoconfigure模块中定义自动配置功能,并定义自动配置文件META-INF/spring/xxx.imports

Maven高级

分模块设计与开发

分模块设计:将项目按照功能拆分成若干个子模块

原因:方便项目的管理维护、扩展,也方便模块间的相互调用,资源共享

注意:分模块开发需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕,然后进行拆分。

继承与聚合

继承关系

继承描述的是两个工程间的关系,与java中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承

作用:简化依赖配置、统一管理依赖

实现:<parent>…</parent>

  • 创建maven模块tlias-parent,该工程为父工程,设置打包方式为pom(默认jar)
    • <packaging>pom</packaging>
  • 在子工程的pom.xml文件中,配置继承关系
    • <relativePath>../tlias-parent/pom.xml</relativePath>
  • 在父工程中配置各个工程共有的依赖(子工程会自动继承父工程的依赖)

打包方式

  • jar:普通模块打包,springboot项目基本都是jar包(内嵌tomcat运行)
  • war:普通web程序打包,需要部署在外部的tomcat服务器中运行
  • pom:父工程或聚合工程,该模块不写代码,仅进行依赖管理

版本锁定

在maven中,可以在父工程的pom文件中通过<dependencyManagement>来统一管理依赖版本

注意:子工程引入依赖时,无需指定<version>版本号,父工程统一管理。变更依赖版本,只需在父工程中统一变更。

自定义属性/引用属性:放在<properties>…</properties>中

<dependencyManagement>与<dependencies>的区别:

  • 后者是直接依赖,在父工程配置了依赖,子工程会直接继承下来
  • 前者是统一管理依赖版本,不会直接依赖,还需要在子工程中引入所需依赖(无需指定版本)

聚合

聚合:将多个模块组织成一个整体,同时进行项目的构建

聚合工程:一个不具有业务功能的”空“工程(有且仅有一个pom文件)(所以使用父工程作为聚合工程)

作用:快速构建项目(无需根据依赖关系手动创建,直接在聚合工程上构建即可)

写法:maven中可以通过<modules>设置当前聚合工程所包含的子模块名称

1
2
3
4
5
<modules>
<module>../tlias-pojo</module>
<module>../tlias-utils</module>
<module>../tlias-web-management</module>
</modules>

注意:聚合工程所包含的模块,在构建时,会自动根据模块间的依赖关系设置构建顺序,与聚合工程中模块的配置书写位置无关。

继承与聚合:

  • 作用
    • 聚合用于快速构建项目
    • 继承用于简化依赖配置、统一管理依赖
  • 相同点:
    • pom.xml文件打包方式均为pom,可以将两种关系制作到同一个pom文件中
    • 均属于设计类模块,并无实际的模块内容
  • 不同点:
    • 聚合是在聚合工程中配置关系,聚合可以感知到参与聚合的模块有哪些
    • 继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己

私服

介绍

私服是一种特殊的远程仓库,架设在局域网内的仓库服务,用来代理位于外部的中央仓库,用于解决团队内部的资源共享与资源同步问题。

依赖查找顺序

  • 本地仓库
  • 私服
  • 中央仓库

注意:私服在企业项目开发中,一个项目/公司,只需要一台即可。

资源上传与下载

  • 设置私服的访问用户名/密码(settings.xml中的servers中配置)
  • IDEA的maven工程的pom文件中配置上传(发布)地址
  • 设置私服依赖下载的仓库组地址(settings.xml中的mirrors、profiles中配置)
  • 执行deploy

项目版本

  • RELEASE(发行版本):功能趋于稳定,当前更新停止,可以用于发行的版本,存储在私服中的RELEASE仓库中
  • SNAOSHOT(快照版本):功能不稳定、尚处于开发中的版本,存储在私服中的SNAOSHOT仓库中