1. 前端Web开发
HTML:负责网页的结构
(页面元素和内容)。
CSS:负责网页的表现
(页面元素的外观、位置等页面样式,如:颜色、大小等)。
JavaScript:负责网页的行为
(交互效果)。
Vue:一套用于构建用户界面的渐进式JavaScript框架
。
Ajax:与服务器进行数据交换,网页实现布局更新。
Axios:基于promise的网络请求库,通过promise实现对ajax技术的一种封装。
YApi:可视化接口管理平台,YApi
Element Plus:基于Vue3的桌面端组件库,Element UI是基于Vue2的。
Nginx:Nginx是一款轻量级的Web服务器、反向代理服务器。
前端开发软件:Visual Studio Code。安装插件Chinese (Simplified)、Code Spell Checker、HTML CSS Support、JavaScript (ES6) code snippets、Mithril Emmet、Path Intellisense、vue3-snippets、VueHelper、Auto Close Tag、Auto Rename Tag、open in browser、Vetur、IntelliJ IDEA Keybindings。
1.1 HTML
HTML(HyperText Markup Language):超文本标记语言。w3school查看更多HTML5标签
(1)超文本:超越了文本的限制,比普通文本更强大。除了文字信息,还可以定义图片、音频、视频等。
(2)标记语言:由标签构成的语言。HTML标签都是预定义
好的,HTML代码直接在浏览器中运行,HTML标签由浏览器解析。
-
img标签
<img src="规定显示图像的URL" alt="规定图像的替代文本" /> alt: 如果无法显示图像(如由于网速太慢),浏览器将显示替代文本 width: 宽度(px ,像素;% ,相对于父元素的百分比) height: 高度
-
h1到h6标签
<h1> 最大的标题 </h1> <h2> 次最大标题 </h2> ... <h6> 最小的标题 </h6>
-
hr水平线标签、br换行标签
-
style样式标签
<style> 标签名1 { 属性名1: 属性值1; 属性名2: 属性值2; ... } ... </style>
-
link标签
<link rel="stylesheet" href="css样式表文件名.css">
-
布局标签div与span
<div> 一行只显示一个(独占一行);宽度默认是父元素的宽度,高度默认由内容撑开;可以设置宽和高 </div> <span> 一行可以显示多个;宽度和高度默认由内容撑开;不可以设置宽和高;span和div都是没有语义的布局标签 </span>
-
超链接标签
<a href="指定资源访问的url" target="指定在何处打开资源链接"> 网页中显示的链接描述 </a> target: 非必填属性 _self: 默认值,在当前页面打开 _blank: 在空白页面打开
-
视频、音频标签
<video src="规定视频的url" controls width="宽度(可选)" height="高度(可选)"></video> controls: 显示播放控件,controls="controls" 属性名=属性值可以省略为属性名 <audio src="规定音频的url" controls></audio>
-
段落标签
<p> 段落的内容 </p>
-
文本加粗标签
<b> 要加粗的文本 </b> <strong> 要强调的文本(同样可以实现加粗的效果,但具有强调语义) </strong>
-
表格标签
<table border="表格边框的宽度" width="表格的宽度" cellspacing="单元之间的空间"> <tr> <td> 该行的第一个单元格内容 </td> <td> 该行的第二个单元格内容 </td> <th> 对于表头单元格可用th标签 </th> </tr> </table>
-
表单标签
<form action="表单数据提交的url地址" method="表单提交方式"> 表单项标签(必须有name属性才能提交) </form> method: get: 表单数据拼接在url后面,如url?age=18,大小有限制 post: 表单数据在请求体(消息体)中携带,没有大小限制
-
表单项标签
(1) 表单项 <input type="表单项的输入形式" name="元素的名称,必须存在才能提交" > value: 指定元素的值 对于单选框或复选框(name属性必须一致),且需用<label for="应与相关元素的id属性相同">标签为input元素定义标注(标记),这样在点击标签时才会聚焦到相关的表单控件上。 (2) 下拉列表 <select name="下拉列表的名称"> <option value="值1"> 第1个下拉列表元素 </option> <option value="值2"> 第2个下拉列表元素 </option> </select> (3) 文本域 <textarea name="文本域的名称" cols="列数" rows="行数"></textarea>
1.2 CSS
CSS(Cascading Style Sheet):层叠样式表,用于控制页面的样式(表现)。w3school查看更多CSS3样式
- CSS引入方式
(1) 行内样式:写在标签的style属性中(不推荐,由于只能设置单个标签的样式),格式style=“属性名1: 属性值1; 属性名1: 属性值1; …”。
(2)内嵌样式:写在style标签中(可以写在页面任何位置,但通常约定写在head标签
中)。
(3)外联样式:写在一个单独的.css
文件中(需要通过link标签
在网页中引入,同样)。 - CSS选择器
(1)标签选择器:根据标签名
选择要设置样式的标签。优先级最低。
(2)id选择器:通过给标签设置id属性(同一个html文件中id值唯一),通过#id属性值
选择要设置样式的标签。优先级最高。
(3)class选择器:通过给标签设置class属性,通过.class属性值
选择要设置样式的标签。优先级中间。 - 盒子模型
(1)盒子:页面中所有的元素(标签),都可以看做是一个盒子,由盒子将页面中的元素包含在一个矩形区域内,通过盒子的视角更方便的进行页面布局。
(2)盒子模型组成:内容区域(content)、内边距区域(padding)、边框区域(border)、外边距区域(margin)。盒子是不包含外边距区域的。
box-sizing
:指定宽和高是针对哪块区域的。如content-box(css属性的width和height就指定了内容区域的大小);border-box(指定的是边框区域的宽和高)。
padding
:指定内边距(顺序上、右、下、左),若都相同,可只写一个。值为auto就会自动计算对应内边距大小。
border
:指定边框属性border-width(边框的宽度,都是按上右下左顺序分别指定)、border-style(边框样式,必须指定的,dotted(点状)、solid(实线)、double(双线)、dashed(虚线))和border-color(边框颜色)。
margin
:指定外边距,和内边距类似,都可设置某一个方位的外边距(margin_top、margin_right、margin_bottom、margin_left)。
常用属性:
- color:文本颜色,三种表达形式:关键字;rgb(红色,绿色,蓝色);十六进制表示,如#000000。
- font-size:字体尺寸大小。
- text-decoration:文本修饰:none(默认,定义标准的文本);underline(下划线);overline(上划线)。
- text-indent:规定文本块中首行文本的缩进,可设置具体的px像素值。
- line-height:设置行间距。
- text-align:规定元素中的文本的水平对齐方式,如left、center、right。
- background-color:设置背景颜色
1.3 JavaScript
JavaScript(简称:JS)是一门跨平台、面向对象的脚本语言(解释性语言),是用来控制网页行为的,它能使网页进行交互。ES6是最新的JavaScript版本。w3school查看更多JS用法
JavaScript引入方式
(1)内部脚本:将JS代码定义在HTML页面中,必须位于<script> </script>
标签之间,在HTML文档中,可以在任意地方,放置任意数量的script标签,一般会把脚本置于body元素的底部,可改善显示速度。
(2)外部脚本:将JS代码定义在外部JS文件中,然后引入到HTML页面中。外部JS文件中,只包含JS代码,不包含script标签。引入时script标签不能自闭合,应<script src="要引入的js路径"></script>
。
1.3.1 基础语法
-
书写语法
区分大小写;结尾分号可有可无;注释//和/**/;大括号表示代码块。(1) window.alert() //写入警告框,window可省略 (2) document.write() //写入HTML输出 (3) console.log() //写入浏览器控制台
-
变量
(1)var
:JavaScript是一门弱类型语言,变量可以存放不同类型的值,作用域比较大,全局变量;可以重复声明。
(2)let
:ES6新增,只在所在的代码块内有效,且不允许重复声明。
(3)const
:ES6新增,用来声明一个只读的常量(一旦声明,常量的值就不能改变)。 -
数据类型、操作符和流程控制语句
(1)原始类型(使用typeof 变量名
可获取数据类型)
(2)引用类型
(3)数据转换
(4)运算符
(5)流程控制语句
-
函数
(1) 函数定义方式一 function 函数名(参数1, 参数2...) { //形式参数不需要指定类型 //要执行的代码 //return 返回值; } (2) 函数定义方式二 var 函数名 = function(参数1, 参数2...) { //在JS中,函数调用可以传递任意个数的参数 } (3) 箭头函数 var 函数名 = (参数1, 参数2...) => { 要执行的代码 } //ES6新特性
1.3.2 对象
- Array数组
数组的长度和类型都是可变的。(1) var 数组名 = new Array(元素列表); (2) var 数组名 = [元素列表]; (3) 数组名.length //返回数组中元素的数量 (4) 数组名.forEach(函数) //遍历数组中的每个有值的元素(for循环没值的也会遍历),并调用一次传入的函数 (5) 数组名.push(元素列表) //将新元素添加到数组的末尾,并返回新的长度 (6) 数组名.splice(起始索引, 删除个数) //从数组中删除元素
- String字符串
(1) var 字符串名 = new String("字符串"); (2) var 字符串名 = "字符串"; (3) 字符串名.length //求字符串的长度 (4) 字符串名.charAt(索引值) //返回在指定位置的字符 (5) 字符串名.indexOf("子串") //检索字符串的位置 (6) 字符串名.trim() //去除字符串两边的空格 (7) 字符串名.substring(起始索引, 结束索引) //求子串,左闭右开
- 自定义对象
var 对象名 = { 属性名1: 属性值1, 属性名2: 属性值2, ... 函数名1: function(形参列表) {}, 函数名2(形参列表) {} };
- JSON对象
JSON(JavaScript Object Notation)是通过JavaScript对象标记法书写的文本,由于其语法简单,层次结构鲜明,现多用于作为数据载体,在网络中进行数据传输。(1) 定义JSON字符串 var json字符串 = '{"key1": value1, "key2": value2...}'; //key值必须用双引号括住 value: 数字:整数或浮点数 字符串:在双引号中 逻辑值:true或false 数组:在方括号中 对象:在花括号中 null (2) JSON字符串转为JS对象 var jsObject = JSON.parse(json字符串); (3) JS对象转为JSON字符串 var jsonStr = JSON.stringify(js对象);
- BOM对象
BOM(Browser Object Model)浏览器对象模型,允许JavaScript与浏览器对话,JavaScript将浏览器的各个组成部分封装为对象。由Window(浏览器窗口对象)、Navigator(浏览器对象)、Screen(屏幕对象)、History(历史记录对象)、Location(地址栏对象)组成。
(1)Window对象(直接使用window.来调用属性和方法,window.可省略)
(2)Location对象window.history //对History对象的只读引用 window.location //对于窗口或框架的Location对象 window.navigator //对Navigator对象的只读引用 window.alert("消息") //显示带有一段消息和一个确认按钮的警告框 window.confirm("消息") //显示带有一段消息以及确认按钮和取消按钮的对话框 window.setInterval(函数, 时间) //按照指定的周期(以毫秒计)来调用函数或计算表达式 window.setTimeout(函数,时间) //在指定的毫秒数后调用函数或计算表达式,只执行一次
location.href //获取当前页面完整的URL location.href = "url"; //设置url值,会自动跳转到对应的网址
- DOM对象
DOM(Document Object Model),文档对象模型,将标记语言(HTML、XML)的各个组成部分封装为对应的对象(Core DOM):Document(整个文档对象)、Element(元素对象)、Attribute(属性对象)、Text(文本对象)、Comment(注释对象)。JavaScript通过DOM,可以改变HTML元素的内容和样式、对事件作出反应、添加和删除HTML元素。(1) 根据id获取Element对象 var element对象名 = document.getElementById('id值'); (2) 根据标签名获取Element对象数组 var element对象数组名 = document.getElementsByTagName('标签名'); (3) 根据name获取Element对象数组 var element对象数组名 = document.getElementsByName('name值'); (4) 根据class获取Element对象数组 var element对象数组名 = document.getElementsByClassName('class值'); (5) 获取或更改Element对象的内容 var content = Element对象.innerHTML; Element对象.innerHTML = "内容";
1.3.3 事件
- 事件绑定
(1)通过HTML标签中的事件属性进行绑定,如在标签中指定点击事件οnclick=“on()”,在JS中定义on方法。
(2)通过DOM 元素属性绑定,如先获取元素对象,再通过元素属性onclick来指定点击事件。 - 常见事件
1.4 Vue
Vue是一套渐进式前端框架,免除原生JavaScript中的DOM操作,简化书写。它是基于MVVM
(Model-View-ViewModel)思想,实现数据的双向绑定
,将编程的关注点放在数据上。Vue官网
安装:
(1)下载安装node.js(JS运行环境)node.js,命令node -v检查版本,在以管理员身份情况下命令npm config set prefix “安装路径” 来配置npm的全局安装路径(通过npm命令install的文件存放的路径),命令npm config get prefix获取路径,命令npm config set registry https://registry.npm.taobao.org配置淘宝镜像。
(2)安装vue-cli(vue开发工具),命令npm install -g @vue/cli,命令vue -V检查版本,命令vue create my-project创建项目(项目名全小写,可用-连接),也可以用vue ui可视化创建项目,命令cd my-project转到项目目录,命令npm run serve运行。
(3)安装vue3高亮插件volar。
1.4.1 模板语法
- 文本插值:用双大括号
{{ 绑定变量名 }}
绑定普通文本,一般配合JS中的data()设置数据。 - 原始 HTML:用
v-html="绑定变量名"
指令可以识别HTML元素(标签)。 - 属性绑定:用
v-bind:属性名="绑定变量名"
绑定标签属性,v-bind:可简写为:。 - 支持单一表达式:在双大括号或v-指令中可使用单一表达式(加法、三元运算符等)
1.4.2 渲染
- 条件渲染
(1)v-if:用于条件性的渲染一块内容,这块内容只会在指令表达式返回真值时才会被渲染
(2)v-else:为对应的v-if添加一个"else区块",它不需要绑定数据。
(3)v-else-if:可配合v-if多次判断使用。
(4)v-show:用法和v-if一致,但会在DOM渲染中保留该元素(惰性的),相当于CSS中的display属性是否显示,而v-if则会销毁和重建。 - 列表渲染
(1)v-for:可以用数组来渲染一个列表,v-for=“item in items”,items是源数据的数组,而 item 是迭代项的别名。
(2):key:v-for是就地更新每个元素,而不会移动DOM元素顺序,通过:key来指定属性进而判断元素是否改变。 - 事件处理
v-on指令 (简写为@
) 来监听DOM事件,事件处理方法写在methods:{ }中,且方法可以接收传递的参数。 - 表单输入绑定
v-model指令可以实现表单输入的双向数据绑定,数据改变是实时更新的。v-model.lazy:输入框在每次change事件(如回车)后更新数据;v-model.trim:自动去除用户输入内容中两端的空格。
1.4.3 Vue组件
- 组件组成
<template> HTML标签 </template> <script> export default { name: '组件名', data() { }, //数据区域,v-bind和v-model绑定的数据必须在此处声明 methods: { }, //方法区 components: { }, //挂载组件 props:{ }, //父组件向子组件传递信息 mounted() { } //渲染完成后进行网络请求 } </script> <style scoped> CSS样式 </style> //scoped:指的是样式只在该组件中生效,不加则全局都共享该样式
- 加载组件:引入组件import … from …;挂载组件components;显示组件<组件名/>。
- 组件交互props
父组件调用子组件时将信息通过v-bind绑定,在子组件的props:{ }中指定接收的数据,需指明数据的type和default两个属性。如String、Number、Boolean、Array、Object、Function,数据类型为数组或对象的时候,default需要返回工厂模式(一个返回值为空的函数)。 - 自定义事件组件交互
$emit
自定义事件可以在组件中反向传递数据(子组件传递数据给父组件),在子组件中定义某事件的方法,在方法中调用this.$emit("事件名", this.要传递的数据)
,其中,事件名:父组件在调用子组件时要用@事件名
这一事件指定对应的事件方法,在方法中有参数,接收处理该参数即可。 - 组件的生命周期
1.4.4 Vue路由配置
通过vue-router路由管理页面之间的关系,Vue Router是Vue.js的官方路由,它与Vue.js核心深度集成,让用Vue.js构建单页应用变得轻而易举。Vue Router
- 简单使用
(1)路由配置:在routes常量中指定路由对象(包含path路径、component对应组件(对于首页采用在顶部import加载,而其它的则采用异步加载方式() => import('对应组件的路径')
))
(2)使用:router-link标签配合to属性指定跳转对应的path路径,实现不重新加载页面的情况下更改URL。必须用router-view标签对路由匹配的组件进行渲染。
(3)history模式的实现:createWebHashHistory():在URL里会加#,createWebHistory():必须在后端进行重定向。
(4)挂载路由:app.use(router)。 - 路由传递参数
(1)在路由配置中指定参数的key,如path: “/list/:id”
(2)在跳转过程中携带参数,即router-link标签的to属性中
(3)在跳转后的详情页面读取路由携带的参数,$route.params.
id获取参数 - 嵌套路由
在路由配置中添加children属性,一个包含子路由的数组(子路由的path不需要加/),如果默认需要父路由显示子路由的页面,就需要在父路由配置中添加重定向属性redirect来指定重定向路径。
1.4.5 Vue状态管理(Vuex),官方现在推荐Pinia
Vuex是一个专为Vue.js应用程序开发的状态管理模式+库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。能够在vue中管理公共的数据,便于开发和维护;在整个项目中可以实现组件之间的数据共享;储存在vuex中的数据都是响应式的,能够保持数据与页面上的数据保持同步。Vuex官网
- 在组件中读取状态
(1) this.$store.state.状态变量名 (2) import { mapState } from 'vuex'; //mapState辅助函数 computed: { ...mapState(["状态变量名"]) //这样就可以直接用状态变量名了,...为展开运算符 }
- Vuex核心概念
(1)State:用来存放数据的,通常将全局数据将保存在这里。
(2)Getter:计算属性,每一个都有返回值,可以state作为第一参数,如数据过滤,this.$store.getters
,mapGetters辅助函数。
(3)Mutation:更改Vuex的store中的状态的唯一方法是提交mutation,同步事务。类似一个事件,可用state作为第一参数,也可传入额外的参数payload(可以起别的名称,可以是任意类型,包括对象),通过this.$store.commit('方法名')
提交,或在methods属性中引入...mapMutations([方法名列表])
,再直接通过this.方法名()
即可调用。
(4)Action:提交的是mutation,而不是直接变更状态,可以包含任意异步操作,即Action决定Mutation什么时候去执行,Action的回调函数可以接收一个context上下文参数(与store实例具有相同方法和属性)。
(5)Module:将store分割成模块,每个模块里都有state,mutation,active,getter,当项目比较大时,可以分成很多的模块,甚至是嵌套子模块,这样的话就解决了多人写作开发中同名数据发生冲突导致被修改。(1) 注册action1 actions: { action名1 (context) { //context相当于this.$store context.commit('对应的mutation名') } } (2) 注册action2 actions: { action名2({ commit }) { //也可以传递其它参数 commit('对应的mutation名') //若mutation中有除state其它参数,这里同样需要额外的参数 } } (3) 分发Action this.$store.dispatch('action名') //同样可以传递参数进去 ...mapActions([方法名列表]) //就直接用this.action名()调用了
1.4.5 Vue3新特性
组合API
Vue3新增了一个setup
生命周期函数,在组合API中,使用setup()方法替换了beforeCreate和created,那么在这两个生命周期中的方法将放在setup中执行,因此setup无this。组合式API
setup(){ } 中声明变量(vue2在data中声明)、声明方法(vue2在methods中声明)、props传递数据、context获取上下文对象
(代替了this), 所有变量和方法都必须return出去才能被外部调用。
(1)ref与reactive声明变量:const 变量名=ref(变量值);const 变量名=reactive({ 属性名:属性值 });
(2)声明方法:在方法中改变变量的值的时候,需用其value属性来更改,如变量名.value=更改后的值。
(3)props:接收父组件传递的参数,setup(props) { props:{ 参数名:参数类型} props.参数名即可使用 }
(4)context:setup(props, context) { }
(5)在setup()中调用生命周期钩子,如onMounted(()=>{ }),且同一个生命周期函数可以存在多个。
(6)provide()和inject()可以实现嵌套组件之间的数据传递,只能在setup()中使用,父级组件使用provide()向下传递数据,子级组件使用inject()获取上层传递过来的数据,且不限层级。
(1) 父组件,先从vue中导入provide
provide("参数名", 值);
(2) 子组件,先从vue中导入inject
const 参数名 = inject("参数名");
1.4.6 Vue第三方组件
- 轮播图:swiper官网
1.5 Ajax
Ajax(Asynchronous JavaScript And XML),异步的JavaScript和XML。如使用XMLHttpRequest是一种Ajax的实现方式。
(1)数据交换:通过Ajax可以给服务器发送请求,并获取服务器响应的数据。
(2)异步交互:可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术。
1.6 Axios
Axios是一个基于promise的网络请求库,命令npm install --save axios安装axios。
- 在Vue组件中引用:import axios from “axios”
- 全局引用(在main.js中,在组件中通过
this.$axios
使用)import axios from "axios" const app = createApp(App); app.config.globalProperties.$axios = axios; app.mount('#app');
- get请求
(1) axios的普通get请求 this.$axios({ method: "get", url: "请求访问的url" }).then(res => { console.log(res.data); }) (2) axios的简写get请求 this.$axios.get("请求访问的url") .then(res => { console.log(res.data); })
- post请求
(1) axios的普通post请求 this.$axios({ method: "post", url: "请求访问的url", data: querystring.stringify(数据对象) //需要安装并引用npm install --save querystring }).then(res => { console.log(res.data); }) (2) axios的简写post请求 this.$axios.post("请求访问的url", querystring.stringify({数据对象})) .then(res => { console.log(res.data); })
- 网络请求封装,axios使用说明
(1) 封装axios的get和post到request.js文件 import axios from "axios" import querystring from "querystring" const instance = axios.create({ //网络请求的公共配置 timeout: 5000 }) const errorHandle = (status, info) => { switch (status) { case 400: console.log("语义有误"); break; case 401: console.log("服务器认证失败"); break; case 403: console.log("服务器拒绝访问"); break; case 404: console.log("地址错误"); break; case 500: console.log("服务器遇到意外"); break; case 502: console.log("服务器无响应"); break; default: console.log(info); break; } } //拦截器最常用的 //1. 发送数据之前 instance.interceptors.request.use( config => { if (config.method === "post") { //post需要对数据进行特殊处理:字符化 config.data = querystring.stringify(config.data) } //config:包含网络请求的所有信息 return config; }, error => Promise.reject(error) ) //2. 获取数据之前 instance.interceptors.response.use( response => response.status === 200 ? Promise.resolve(response) : Promise.reject(response), error => { const { response } = error; errorHandle(response.status, response.info); } ) export default instance; //导出网络请求 (2) 定义存放url的path.js文件夹 const base = { baseUrl: "http://iwenwiki.com", chengpin: "/api/blueberrypai/getChengpinDetails.php" } export default base; (3) 定义处理具体请求的index.js import axios from "../utils/request" import path from "./path" const api = { getChengpin(){ return axios.get(path.baseUrl + path.chengpin); } } export default api (4) 在需要进行网络请求的vue中使用 import api from "./api/index" api.getChengpin().then(res => { console.log(res.data); })
- 前端跨域proxy
JS采取的是同源策略(同源策略是浏览器的一项安全策略,浏览器只允许JS代码请求和当前所在服务器域名,端口,协议相同的数据接口上的数据),也就是说,当协议、域名、端口任意一个不相同时,都会产生跨域问题,跨域错误:Access-Control-Allow-Origin
。devServer: { //在vue.config.js中添加这一配置,且需重启vue-cli //port: 7000, //可以设置端口号为7000 proxy: { '/api': { target: '跨域的域名,如http://iwenwiki.com', //指定后其它url就可直接在这个基础上添加url changeOrigin: true } } }
1.7 Element Plus
Element Plus是基于Vue3的组件库,Element Plus组件库
- 加载Element Plus
(1)命令npm install element-plus --save安装
(2)全局引入:在main.js中导入 import store from ‘./store’ import ElementPlus from ‘element-plus’ 再use(ElementPlus),即可使用ElementPlus的组件标签。
(3)部分引入:命令npm install -D unplugin-vue-components unplugin-auto-import安装插件,修改vue.config.js内容const { defineConfig } = require('@vue/cli-service') const AutoImport = require('unplugin-auto-import/webpack') const Components = require('unplugin-vue-components/webpack') const { ElementPlusResolver } = require('unplugin-vue-components/resolvers') module.exports = defineConfig({ transpileDependencies: true, configureWebpack: { plugins: [ AutoImport({ resolvers: [ElementPlusResolver()], }), Components({ resolvers: [ElementPlusResolver()], }), ] } })
- Icon字体图标
需用命令npm install @element-plus/icons-vue安装,在项目src根目录下创建plugins文件夹,在文件夹下创建icons.js文件,最后在main.js中import elementIcon from ‘./plugins/icons’,并use(elementIcon)即可使用。import * as ElementPlusIconsVue from '@element-plus/icons-vue' export default { install: (app) => { for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } } };
1.8 Nginx
Nginx (engine x) 是一款轻量级的Web服务器、反向代理服务器,由于它的内存占用少,启动极快,高并发能力强,在互联网项目中广泛应用。
- 反向代理(reverse proxy):是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。这个过程对于客户端而言是透明的。
- 正向代理(forward proxy):是一个位于客户端和目标服务器之间的服务器(代理服务器),为了从目标服务器取得内容,客户端向代理服务器发送一个请求并指定目标,然后代理服务器向目标服务器转交请求并将获得的内容返回给客户端。比如访问外网使用VPN。
- 打包Vue项目并部署到Nginx上
(1)命令npm run build打包vue项目到list文件中。
(2)下载安装Nginx,Nginx下载
(3)将打包好的dist目录下的文件,复制到nginx安装目录的html目录下。
(4)更改Nginx的默认的80端口,conf文件夹下的nginx.conf文件修改端口号即可启动nginx.exe。
2. 后端Web开发
2.1 Maven
Maven是apache旗下的一个开源项目,是一款用于管理和构建java项目的工具。它基于项目对象模型(Project Object Model,POM)的概念,通过一小段描述信息来管理项目的构建。
(1)依赖管理:方便快捷的管理项目依赖的资源(jar包),避免版本冲突问题。
(2)统一项目结构:提供标准、统一的项目结构。
(3)项目构建:标准跨平台(Linux、Windows、MacOS)的自动化项目构建方式。
-
Maven安装与配置
(1)解压下载的Maven压缩包。Maven下载
(2)配置本地仓库:修改conf文件夹下的settings.xml中的<localRepository>
为一个指定目录(该标签是被注释的,需要拿出来配置)。
(3)配置阿里云私服:修改conf文件夹下的settings.xml中的<mirrors>
标签,更改mirror子标签为:<mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror>
(4)配置环境变量:新建MAVEN_HOME环境变量为解压目录,并将其bin目录加入Path环境变量。在cmd中使用命令mvn -v检验是否配置成功。
-
坐标
Maven中的坐标是资源的唯一标识,通过该坐标可以唯一定位资源位置,使用坐标来定义项目或引入项目中需要的依赖。
(1)groupId:定义当前Maven项目隶属组织名称(通常是域名反写)。
(2)artifactId:定义当前Maven项目名称(通常是模块名称)。
(3)version:定义当前项目版本号。 -
依赖配置
依赖是指当前项目运行所需要的jar包,一个项目中可以引入多个依赖。在dependency标签中声明依赖并刷新加入依赖即可。Maven仓库 -
依赖传递
依赖具有传递性。可以右键选择Diagrams中的Show Dependencies可视化查看依赖关系。
(1)直接依赖:在当前项目中通过依赖配置建立的依赖关系;
(2)间接依赖:被依赖的资源如果依赖其他资源,当前项目间接依赖其他资源。
(3)排除依赖:主动断开依赖的资源,被排除的资源无需指定版本,在exclusions标签下声明子标签。
(4)循环依赖:重构代码,消除循环依赖(改成单向依赖);使用适当的依赖范围和排除依赖(provided或test范围) -
依赖范围
依赖的jar包,默认情况下,可以在任何地方使用。可以通过scope标签设置其作用范围:
(1)compile(默认):主程序,测试程序,打包(运行)。如log4j
(2)test:测试程序。如junit
(3)provided:主程序,测试程序。如servlet-api
(4)runtime:测试程序,打包(运行)。如jdbc驱动 -
Maven生命周期
2.2 Web基础
-
HTTP协议
HTTP(超文本传输协议),规定了浏览器和服务器之间数据传输的规则。基于TCP协议:面向连接,安全;基于请求-响应模型:一起请求对应一次响应;HTTP协议是无状态的协议:对于事物处理没有记忆能力,每次请求-响应都是独立的,因此速度快,但是多次请求间不能共享数据。
(1)请求数据格式
请求行:请求数据第一行(请求方式、资源路径、协议)。
请求头:第二行开始,格式key:value。
请求体:POST请求,存放请求参数。
请求方式-GET
:请求参数在请求行中,没有请求体,GET请求大小是有限制的。
请求方式-POST
:请求参数在请求体中,POST请求大小是没有限制的。
(2)响应数据格式
响应行:响应数据第一行(协议、状态码、描述)。
响应头:第二行开始,格式key:value。
响应体:最后一部分,存放响应数据。 -
Tomact
Web服务器是一个软件程序,对HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷,主要功能是“提供网上信息浏览服务”。如Tomcat、WebLogic、WebSphere等。
Tomcat是一个开源免费的轻量级Web服务器,支持Servlet/JSP少量JavaEE规范,Tomcat官网
下载Tomcat压缩包并解压,打开bin文件夹下的startup.bat启动(若控制台乱码,修改conf文件夹下的logging.properties的java.util.logging.ConsoleHandler.encoding = GBK),输入http://localhost:8080验证是否安装成功。若端口冲突,可以在conf文件夹下的server.xml中更改端口号。
在IDEA中部署Tomcat:File->Settings->Build,Execution,Deployment->Application Servers->+;Run->Edit Configurations…->Tomcat Server->Local -
Postman
Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。作用:常用于进行接口测试。 -
请求
(1)原始方式:在原始的web程序中,获取请求参数,需要通过HttpServletRequest对象手动获取。
(2)SpringBoot方式
简单参数:参数名与形参变量名相同,定义形参即可接收参数。
简单实体对象:请求参数名与形参对象属性名相同,定义POJO接受即可。
复杂实体对象:按照对象层次结构关系即可嵌套POJO属性参数。
数组参数:请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数。
集合参数:请求参数名与形参中集合变量名相同,通过@RequestParam
绑定参数关系。
日期参数:使用@DateTimeFormat
注解完成日期参数格式转换。
JSON参数:JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数,需要用@RequestBody
注解标识。
路径参数:通过请求URL直接传递参数,使用{…}来标识该路径参数,需要使用@PathVariable
注解获取路径参数。 -
响应
(1)@ResponseBody
:位于Controller方法上/类上,作用:将方法返回值直接响应,如果返回值是实体对象/集合,将会转换为json格式响应。@RestController=@Controller+@ResponseBody
。
(2)统一响应结果:定义一个Result实体类,包含响应码code(整形,1代表成功,0代表失败);提示信息msg(String类型);返回的数据data(Object类型)。并创建3个静态方法(返回对象都是Result对象):只传data的success方法;无参success方法;只传msg的error方法。 -
静态资源目录
SpringBoot项目的静态资源默认存放目录为:classpath:/static;classpath:/public;classpath:/resources;
2.3 分层解耦
-
三层架构
(1)controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据。
(2)service:业务逻辑层,处理具体的业务逻辑。
(3)dao:数据访问层(Data Access Object)(持久层),负责数据访问操作,包括数据的增删改查。 -
分层解耦
(1)内聚:模块内部的功能联系。高内聚
(2)耦合:各个模块之间的依赖、关联程度。低耦合 -
IOC与DI
(1)控制反转
(Inversion Of Control):简称IOC
,对象的创建控制权由程序自身转移到容器(这种思想称为控制反转)。
(2)依赖注入
(Dependency Injection):简称DI
,容器为应用程序提供运行时所依赖的资源。
@Resource与@Autowired区别
:@Autowired是Spring框架提供的注解,@Resource是JDK提供的注解;@Autowired默认是按照类型注入,而@Resource默认是按照名称注入。
(3)Bean对象:IOC容器中创建、管理的对象。注解生效必须用@ComponentScan
扫描,启动类上声明注解@SpringBootApplication
中已经包含,默认扫描范围是启动类所在的包及其子包。
2.4 Mybatis
-
Mybatis快速入门
(1)创建SpringBoot项目时,选择SQL下的MyBatis Framework和MySQL Driver依赖。
(2)创建基本表和对应的pojo实体类(对于基本数据类型,最好使用包装类,不会有默认值的干扰)。
(3)在SpringBoot配置文件application.properties中配置数据库连接信息。# 驱动类名称 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # 数据库连接的url spring.datasource.url=jdbc:mysql://localhost:3306/test # 连接数据库的用户名 spring.datasource.username=root # 连接数据库的密码 spring.datasource.password=123456
(4)定义mapper类,并在类上用
@Mapper
注解:在运行时,会自动生成该接口的实现类对象(代理对象),并将该对象交给IOC容器管理;编写接口并使用@Select
等注解标明对应的SQL语句。配置SQL提示:选中写的SQL语句,右键->Show Context Actions->Inject language or reference->MySQL即可,若对于基本表还是没提示,则需连接MySQL数据库。 -
JDBC、数据库连接池、Lombok
JDBC(Java DataBase Connectivity),使用Java语言操作关系型数据库的规范(API接口),各数据库厂商去实现这套接口,提供数据库驱动jar包,真正执行的代码是驱动jar包中的实现类。
数据库连接池
是个容器,负责分配、管理数据库连接(Connection);它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏。优点:资源重用;提升系统响应速度;避免数据库连接遗漏。
所有的数据库连接池都必须实现官方(Sun)提供的数据库连接池接口DataSource,再通过getConnection()方法来获取连接对象Connection,目前最好的两个连接池:
(1)HikariCP:光连接池,SpringBoot默认的数据库连接池。(若需更改为别的连接池,则需导入对应的依赖)
(2)Druid(德鲁伊):功能比较全面,且扩展性较好的数据库连接池,比较方便对jdbc接口进行监控跟踪等。
Lombok是一个实用的Java类库,能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,并可以自动化生成日志变量,简化Java开发、提高效率。需要导入lombok依赖。同时还提供@Slf4j
注解来记录日志。
-
预编译SQL
(1)开启Mybatis日志到控制台,在SpringBoot配置文件application.properties中进行设置:# 指定Mybatis输出日志的位置,输出控制台 mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
(2)预编译SQL
性能更高
:在带有参数的SQL语句中,会先用占位符?
来代替,然后对该SQL语句进行SQL语法解析检查、优化SQL、编译SQL并将编译的SQL放入缓存,最后执行SQL(执行的同时会将参数对占位符?进行替换)。如此一来,对于不同的参数但SQL语句相同,只需编译一次,之后从缓存中取,最后执行时赋予不同的参数即可,进而提升了性能。
(3)预编译SQL更安全(防止SQL注入)
:SQL注入是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。例如:通过输入数据' or '1'='1
就可以使得SQL条件永远成立。 -
#{}与${}
(1)#{}:执行SQL时,会将#{}替换为?,生成预编译SQL,会自动设置参数值(参数值会自动加上引号)。使用时机:参数传递,都使用#{},可以防止SQL注入。
(2)${}:拼接SQL,直接将参数拼接在SQL语句中,存在SQL注入问题。使用时机:对表名、字段名进行动态设置时使用。 -
基础操作的注意点
(1)插入时的主键返回,可以在接口方法上使用@Options(keyProperty = "实体的主键属性", useGeneratedKeys = true)
,这样就会自动将生成的主键值,赋值给实体对象的主键属性。
(2)数据封装:实体类属性名和数据库表查询返回的字段名一致时,Mybatis会自动封装,反之则不会自动封装。
方法一:给字段起别名,让别名与实体类属性名一致。
方法二:通过@Results,@Result(配合column指定字段名、property指定属性名)注解手动映射封装。
方法三:开启Mybatis的驼峰命名自动映射开关(在SpringBoot配置文件application.properties中)。# 开启Mybatis的驼峰命名自动映射开关 mybatis.configuration.map-underscore-to-camel-case=true
(3)模糊查询时,要用concat函数对占位符和通配符进行拼接。
(4)在Springbot1.X版本/单独使用Mybatis时,接口的形参是不会保留的,因此需要使用@Param
注解来进行指定。 -
XML映射文件
(1)XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)。
(2)XML映射文件的namespace属性与Mapper接口全限定名一致。
(3)XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致。
MybatisX是一款基于IDEA的快速开发Mybatis的插件,为效率而生。XML映射文件的约束:XML约束
使用Mybatis的注解,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句。 -
动态SQL
动态SQL:随着用户的输入或外部条件的变化而变化的SQL语句。
(1)where标签:如果包含的if标签都不成立则where条件判断不生效。
(2)if标签:用于判断条件是否成立,使用test属性进行条件判断,如果条件为true,则拼接SQL。
(3)set标签:动态的在行首插入set关键字,并会删掉额外的逗号。(用在update语句中)
(4)foreach标签:用于一些批量操作,如批量删除,属性如下所示:
(5)sql标签与include标签:sql标签将公共sql语句抽取并指定id属性;include标签通过refid属性对sql标签引用。 -
分页插件PageHelper
导入依赖pagehelper-spring-boot-starter。PageHelper.startPage(page,pageSize); //传入当前页码和每页条数 //执行相关查询业务(该插件会自动加上对应的分页需要的SQL语句,计算总条数等信息) //将查询的列表强转成Page<实体>对象 //通过上一步的对象可获取所有信息,如getTotal()获取总条数,getResult()获取查询结果,getPages()获取总页数等
2.5 常用业务
2.5.1 文件上传
文件上传,是指将本地图片、视频、音频等文件上传到服务器,供其他用户浏览或下载的过程。
- 文件上传
(1)前端页面三要素:表单项type=“file”;表单提交方式post;表单的enctype属性为mulitipart/form-data。
(2)服务端接收文件:在Controller层使用MultipartFile类型的形参接收即可,只是临时存储。 - 本地存储
在服务端,接收到上传上来的文件之后,将文件存储在本地服务器磁盘中。问题:前端无法直接访问;服务器磁盘有限(不易扩容,且损坏就会丢失数据)。
MultipartFile常用方法://获取文件的原始的文件名 String originalFilename = multipartFile.getOriginalFilename(); //UUID构造唯一的识别码 int index = originalFilename.lastIndexOf("."); String extName = originalFilename.substring(index); String newFileName = UUID.randomUUID() + extName; //将文件存储在服务器的磁盘目录中 multipartFile.transferTo(new File("E:\\" + newFileName));
在SpringBoot中,文件上传,默认单个文件允许最大大小为1M,一次可以上传多个文件(MultipartFile数组或集合来接收)。如果需要上传大文件,可配置:# 配置单个文件最大上传大小 spring.servlet.multipart.max-file-size=10MB # 配置一次可上传文件的总大小 spring.servlet.multipart.max-request-size=100MB
- 云存储(阿里云OSS)
阿里云对象存储OSS(Object Storage Service),是一款海量、安全、低成本、高可靠的云存储服务。使用OSS可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。
(1)注册阿里云->开通对象存储服务(OSS)->创建bucket->获取AccessKey密钥。
(2)参照官方SDK编写入门程序,阿里云OSS官方文档
(3)案例集成OSS,创建工具类来实现对不同文件的云上传。
2.5.2 配置文件
-
参数配置化
对于一写配置参数,需要提取在配置文件中,便于修改和维护,如自定义阿里云OSS配置信息:可以在Springboot的配置文件application.properties中采用key=value的形式来定义,然后需用到的时候通过@Value
注解来注入外部配置的属性(位置:定义变量的上方),具体用法为@Value("${key}")
。 -
yml配置文件
SpringBoot项目中除了application.properties配置文件,还可以有application.yml配置文件(或application.yaml),不同之处在于,yml配置文件是分级的(推荐使用)。
(1)大小写敏感
(2)数值前边必须有空格,作为分隔符
(3)使用缩进标识层级关系,缩进时,不允许使用Tab键,只能使用空格(Idea会自动将Tab准换为空格)。
(4)缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。
(5)#标识注释,从这个字符一直到行尾,都会被解析器忽略。
(6)yml常用的数据格式如下所示:
-
@ConfigurationProperties
可以将同前缀的配置批量的封装到一个实体类中,通过Lombok的注解@Data构建set/get方法;通过@Component注解将其交给Spring容器进行管理;通过@ConfigurationProperties(prefix = “”)注解自动为对应的属性注入配置的值;最后通过将该实体类依赖注入即可使用。需要导入spring-boot-configuration-processor依赖
2.5.3 登录校验(会话技术、过滤器、拦截器、异常)
-
会话技术
会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束,在一次会话中可以包含多次请求和响应。
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
(1)Cookie
:客户端会话跟踪技术,服务器通过响应头Set-Cookie来设置Cookie数据,客户端通过请求头Cookie来携带Cookie数据。跨域(协议、域名/IP、端口任何一个不同就是跨域)。
(2)Session
:服务端会话跟踪技术,通过在服务器建立Session,同时借助Cookie会话技术存储Session的ID进而对不同的会话进行判断。
(3)令牌技术
-
JWT令牌
JWT(JSON Web Token):定义了一种简洁的(字符串)、自包含的格式,用于在通信双方以json数据格式安全的传输信息,由于数字签名的存在,这些信息是可靠的。JWT官网
组成:
(1)第一部分:Header(头),记录令牌类型、签名算法等。
(2)第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。前两部分都会通过Base64对json数据进行编码(Base64是一种基于64个可打印字符(A-Z a-z 0-9 + /)来表示二进制数据的编码方式)。
(3)第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定密钥,通过指定签名算法计算而来。
登录认证
:登录成功后,生成令牌;后续的每个请求,都要携带JWT令牌,系统在每次请求处理之前,先校验令牌,通过后,再处理。
(1)导入依赖jjwt
(2)登录后下发令牌(通过自定义工具类对生成和解析JWT令牌进行封装)public class JwtUtils { private static String signKey = "superm"; private static Long expire = 43200000L; //生成JWT令牌(参数为有效荷载) public static String generateJwt(Map<String, Object> claims) { String jwt = Jwts.builder() .addClaims(claims) //有效荷载 .signWith(SignatureAlgorithm.HS256, signKey) //签名算法和密钥 .setExpiration(new Date(System.currentTimeMillis() + expire)) //有效期 .compact(); return jwt; } //解析JWT令牌(返回值为有效荷载的内容) public static Claims parseJWT(String jwt) { Claims claims = Jwts.parser() .setSigningKey(signKey) .parseClaimsJws(jwt) .getBody(); return claims; } }
(3)对请求进行拦截验证解析令牌。
-
过滤器Filter
Filter过滤器是JavaWeb三大组件(Servlet、Filter、Listener)之一;过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能,例如一些通用的操作(登录校验、统一编码处理、敏感字符处理等)。
Filter过滤器的使用方法:
(1)定义Filter:定义一个类,实现Filter接口(jakarta.servlet.Filter),并重写其方法doFilter进行过滤操作(还有init初始化和destory销毁方法,有默认实现),在doFilter方法中先执行放行前逻辑,再通过filterChain.doFilter(servletRequest, servletResponse);
对拦截的请求进行放行,最后执行放行后的逻辑。
(2)配置Filter:定义的Filter类上加@WebFilter("/*")
注解来配置拦截资源的路径,并在启动类上加@ServletComponentScan
注解开启对Servlet组件的支持。
过滤器链:
一个Web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链。执行顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然顺序。
登录校验
@Slf4j @WebFilter("/*") public class LoginFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; //向下转型,HttpServletRequest具有Http协议的方法,如getHeader HttpServletResponse resp = (HttpServletResponse) servletResponse; //1. 获取请求的url String url = req.getRequestURL().toString(); log.info("请求的url:{}", url); //2. 判断是否是登录url if (url.contains("login")) { log.info("登录操作,放行..."); filterChain.doFilter(servletRequest, servletResponse); return; } //3. 获取请求头中的JWT令牌 String jwt = req.getHeader("token"); //4. 判断令牌是否存在,不存在,则返回错误结果(未登录) if(jwt==null){ log.info("请求头token为空,返回未登录的信息"); resp.getWriter().write(JSONObject.toJSONString(Result.error("NOT_LOGIN"))); //需导入fastjson2依赖 return; } //5. 令牌存在,则解析JWT令牌,若解析失败,则返回错误结果(未登录) try { JwtUtils.parseJWT(jwt); } catch (Exception e) { log.info("token解析失败,返回未登录的信息"); resp.getWriter().write(JSONObject.toJSONString(Result.error("NOT_LOGIN"))); //前端需对改信息判断跳转到登录页面 return; } //6. 放行 log.info("令牌合法,放行"); filterChain.doFilter(servletRequest, servletResponse); } }
-
拦截器Interceptor
拦截器是一种动态拦截方法调用的机制,类似于过滤器,由Spring框架提供,用来动态拦截控制器方法的执行。作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。
拦截器Interceptor的使用方法:
(1)定义拦截器:自定义拦截器类实现HandlerInterceptor接口,用@Component
注解将拦截器交由Spring容器,重写其三个方法:preHandle(目标资源方法运行前运行,若为true就放行,返回false则不放行);postHandle(请求处理后运行);afterCompletion(视图渲染完之后运行)。
(2)配置拦截器:定义一个配置类实现WebMvcConfigurer接口,用@Configuration
注解表明该类是配置类,重写addInterceptors方法,通过注入依赖获取拦截器对象,最后通过registry.addInterceptor(拦截器对象).addPathPatterns("/**").excludePathPatterns("/login");
来添加拦截器并指定拦截路径(还可以指定不拦截的路径)。
拦截路径:
执行流程:
-
异常处理
将Dao、Service、Controller的异常都通过throws Exception向上抛出,然后定义一个全局异常处理器类使用@RestControllerAdvice
注解标记,@RestControllerAdvice=@ResponseBody+@ControllerAdvice,对于不同的异常定义不同的方法并用@ExceptionHandler(异常类.class)
标注,可以是自定义的异常,也可以是系统异常,方法中的参数就是那个异常类的类型。
2.6 Spring事务管理
在业务层(Service)层的方法上、类上、接口上使用@Transactional
注解,将当前方法(一般对包含多个对数据库操作的增删改的方法)交给Spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务。
# 开启事务管理日志
logging.level.org.springframework.jdbc.support.JdbcTransactionManager=debug
- rollbackFor属性-事务回滚条件
默认情况下,只有出现RuntimeException才会回滚异常,rollbackFor属性用于控制出现何种异常类型,回滚事务。若设置rollbackFor = Exception.class则会对所有异常都会回滚事务。 - propagation属性-事务传播行为
事务传播行为:当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。
REQUIRES_NEW:当我们不希望事务之间相互影响时,可以使用该传播行为,例如:无论成功与否都记录日志。
- isolation属性-事务隔离级别:同MySQL数据库的隔离级别
- timeout属性-事务的超时时间:若事务超过时间限制还没完成,则自动回滚
- readOnly属性-是否为只读:默认为false,为了忽略那些不需要事务的方法,比如读取数据就可以设置为true
Spring事务失效
(1)在方法中捕获异常没有抛出去
(2)非事务方法调用事务方法(没有使用代理对象的情况)
(3)事务方法内部调用事务方法(没有使用代理对象时,虽使用事务传播,仍会失效)
(4)@Transactional标记的方法不是public
(5)抛出的异常与rollbackFor指定的异常不匹配,默认rollbackFor指定的异常为RuntimeException
(6)数据库引擎不支持事务,比如MySQL的MyISAM
(7)Spring的传播行为导致事务失效,比如: PROPAGATION_NEVER、PROPAGATION_NOT_SUP
2.7 Spring的AOP
AOP(Aspect Oriented Programming)(面向切面编程、面向方法编程),其实就是面向特定方法编程,需导入spring-boot-starter-aop依赖。应用场景:记录操作日志、权限控制、事务管理等。优点:代码无侵入、减少重复代码、提高开发效率、维护方便。
-
核心概念
(1)连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)。
(2)通知:Advice,指哪些重复的逻辑,也就是共享功能(最终体现为一个方法)。
(3)切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用。
(4)切面:Aspect,描述通知与切入点的对应关系(通知+切入点)。@Aspect
+@Component
注解指定切面类
(5)目标对象:Targer,通知所应用的对象。 -
通知类型
@Around
环绕通知需要自己调用ProceedingJoinPoint.proceed()
来让原始方法执行,且必须指定返回值Object类型来接收原始方法的返回值。
通知的执行顺序默认是按照切面类的类名字母顺序执行的;也可以通过@Order(数字)
注解在切面类上来控制顺序,数字越小,越先执行。
-
切入点表达式-execution
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
:多个切入点表达式可以通过且(&&)、或(||)、非(!)
来组合。
(1)访问修饰符可以省略。
(2)返回值类型、包名、类名、方法名可以使用星号*代表任意。
(3)包名和类名之间一个点.代表当前包下的类,两个点..
代表当前包及其子包下的类。
(4)参数列表可以使用两个点..
表示任意个数,任意类型的参数列表。
抽取公共切点表达式:使用@Pointcut
注解在某方法上进行抽取,调用时使用方法名()即可。 -
切入点表达式-annotation
自定义注解类,并使用@Retention()
和@Target()
注解指定自定义注解什么时候生效和注解的位置,然后在切入点方法上使用自定义的注解,最后在切面类的通知类型中使用@annotation(自定义注解全限定包名)
来指定切入点。 -
连接点JoinPoint
在Spring中用JoinPoint
抽象了连接点,用它可以获得方法执行时的相关信息,ProceedingJoinPoint
是其子类,对@Around通知只能用ProceedingJoinPoint,而其它四种通知,获取连接点信息只能使用JoinPoint对象。
(1)获取目标对象的类名:JoinPoint.getTarget().getClass().getName();
(2)获取目标方法的方法名:JoinPoint.getSignature().getName();
(3)获取目标方法运行时传入的参数:Object[] args = JoinPoint.getArgs();
(4)目标方法执行并返回值:Object result = ProceedingJoinPoint.proceed(); -
AOP-记录操作日志
日志信息包含:操作人、操作时间、执行方法的全类名、执行方法名、方法运行时参数、返回值、方法执行时长等。通过自定义注解对Controller层中需要记录日志的方法实现@Around通知的日志切入即可。
2.8 SpringBoot-元注解
@Retention
:指定注解的生命周期。
(1)RetentionPolicy.RUNTIME:始终不会丢弃,运行时也保留读注解,可以被JVM或其他使用反射机制读取该注解的信息。自定义的注解通常使用这种方式。
(2)RetentionPolicy.CLASS:类加载时丢弃,编译时保留(在class文件中存在,但JVM将会忽略,运行时无法获得)。默认使用这种方式。
(3)RetentionPolicy.SOURCE:编译阶段丢弃,自定义注解在编译结束之后就不再有意义,所以它们不会写入class字节码文件。@Overide、@SuppressWarnings 都属于这类注解。@Taget
:指定注解的使用位置。
(1)ElementType.TYPE:用于描述类、接口( 包括注解类型 )或 enum 声明。
(2)ElementType.METHOD:用于描述方法。
(3)ElementType.FIELD:用于描述成员变量、对象、属性( 包括enum实例 )。@Inherited
:指定被标注的类型是可以继承的。@Documented
:将注解信息添加在Javadoc中。@intertface
:用于声明注解的注解(并非是元注解)。
2.9 SpringBoot-配置文件优先级与Bean的管理
-
配置文件的优先级
命令行参数配置:-Dxxx = xxx(优先级最高,越往下优先级越低)
Java系统属性配置:--
xxx = xxx
application.properties > application.yml(主流) > application.yaml -
SpringBoot多环境配置
(1)application-dev.properties:开发环境
(2)application-test.properties:测试环境
(3)application-prod.properties:生产环境
在application.properties文件中通过spring.profiles.active属性来指定哪个具体的配置文件会被加载,如spring.profiles.active=dev对应加载开发环境的配置。 -
获取Bean
默认情况下,Spring项目启动时,会把Bean都创建好放在IOC容器中(仅限单例且没有延迟的Bean),如果想要主动获取这些Bean,先通过依赖注入获取Spring容器ApplicationContext对象,再通过如下方式获取即可:(1) 根据name获取Bean:Object getBean(String name) //需要强制转换 (2) 根据类型获取Bean:<T> T getBean(Class<T> requiredType) (3) 根据name获取Bean(带类型转换):<T> T getBean(String name, Class<T> requiredType)
-
Bean的作用域
可以通过@Scope
注解来指定Bean的作用域,通过@Lazy
注解延迟Bean的初始化(延迟到第一次使用)。
-
第三方Bean
通过在配置类中(@Configuration
注解的类)声明方法,并返回第三方对象,同时用@Bean
注解进行标记,此时方法名就是默认的Bean的名称(建议方法名就是第三方对象类的首字母小写)。如果第三方Bean需要依赖其它Bean对象,直接在Bean定义方法中设置形参即可,容器会根据类型自动装配。
2.10 SpringBoot原理
Spring框架比较繁琐(依赖和配置),SpringBoot底层的起步依赖
和自动配置
大大简化了Spring Framework中依赖和配置的繁琐。
- 起步依赖:诸如依赖spring-boot-starter-web通过Maven的依赖传递来实现对web需要的一些依赖进行导入。
自动配置
:SpringBoot的自动配置就是当Spring容器启动后,一些配置类、Bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。- 自动配置的方法
(1)在启动类上用@ComponentScan
开启组件扫描,指定扫描的包(会将全部的Bean加载到IOC容器)。使用繁琐,性能低。
(2)在启动类上用@Import({})
导入,导入的类会被Spring加载到IOC容器中,可以导入:普通类,配置类,ImportSelector接口实现类(可以将要交给Spring容器管理的全限定类名封装成数组),第三方依赖一般会使用自定义的@EnableXxxx注解
来封装@Import
注解进而完成对想要交由Spring容器管理的Bean对象的配置。 - 自动配置的原理
(1)@EnableAutoConfiguration
注解使用@Import
注解,导入了实现ImportSelector接口的实现类AutoConfigurationImportSelector。
(2)AutoConfigurationImportSelector重写了selectImports()
方法,用于确定将什么类交由Spring的IOC容器。
(3)selectImports()方法底层调用getAutoConfigurationEntry()
方法,获取可自动配置的配置类信息集合。
(4)getAutoConfigurationEntry()方法通过调用getCandidateConfigurations()
方法获取在配置文件中配置的所有自动配置类的集合。
(5)getCandidateConfigurations方法的功能:获取所有基于META-INF/spring.factories
文件、META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中配置类的集合。
SpringBoot并不会将那些配置类集合全部注册为Spring IOC容器的Bean,SpringBoot会根据@Conditional
注解条件装配。
- 自定义starter
在实际开发中,经常会定义一些公共组件,提供给各个项目团队使用,而在SpringBoot的项目中,一般会将这些公共组件封装为SpringBoot的starter。例如:自定义aliyun-oss-spring-boot-starter,实现想要使用阿里云OSS,直接注入AliyunOSSUtils就可以直接使用。
(1)创建aliyun-oss-spring-boot-starter模块,指定依赖管理功能。
(2)创建aliyun-oss-spring-boot-autoconfigure模块,在starter中引入该模块。
(3)在aliyun-oss-spring-boot-autoconfigure模块中定义自动配置功能,并定义自动配置文件META-INF/spring/xxxx.imports。
2.11 Maven高级
- 分模块设计与开发
将项目按照功能拆分成若干个子模块,方便项目的管理维护、扩展,也方便模块间的相互调用,资源共享。 - 继承
继承描述的是两个工程间的关系,与Java中的继承相似(只能单继承,但可以多重继承),子工程可以继承父工程中的配置信息,常见于依赖关系的继承。作用:简化依赖配置、统一管理依赖。通过parent标签
实现。
(1)继承关系的实现:
创建Maven模块,该工程为父工程,设置打包方式为pom
(默认是jar)。
在子工程的pom.xml文件中,配置继承关系(parent标签中指定父工程坐标和relativePath标签相对路径)。
在夫工程中配置各个工程共有的依赖(子工程会自动继承夫工程的依赖)。
(2)Maven版本锁定:
在Maven中,可以在父工程的pom文件中通过dependencyManagement
标签来统一管理依赖的版本(只是管理,在父工程并没有引入这些依赖)。子工程在引入依赖时,无需指定version版本号,父工程统一管理。若变更依赖版本,只需在父工程中统一变更,父工程还可以通过在properties标签中自定义属性(xxx.version标签
来指定版本),最后在依赖的version标签中通过${xxx.version}
引用属性。 - 聚合
聚合是指将多个模块组织成一个整体,同时进行项目构建。聚合工程是指一个不具有业务功能的“空”工程(有且仅有一个pom文件),一般父工程就可以作为聚合工程。在聚合工程中的pom文件中通过modules标签
设置当前聚合工程所包含的子模块名称,通过module标签设置子模块的相对路径。聚合工程中所包含的模块,在构建时,会自动根据模块间的依赖关系设置构建顺序,与聚合工程中模块的配置书写位置无关。 - 私服
私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的中央仓库,用于解决团队内毒的资源共享与资源同步问题。导入依赖时的查找顺序:本地仓库->私服->中央仓库。
项目版本:
(1)RELEASE(发行版本):功能趋于稳定、当前更新停止,可以用于发行的版本,存储在私服中的RELEASE仓库中。
(2)SNAPSHOT(快照版本):功能不稳定、尚处于开发中的版本,即快照版本,存储在私服的SNAPSHOT仓库中。
资源的上传与下载:
(1)设置私服的访问用户名/密码(在Maven的配置文件settings.xml中的servers中配置)
(2)设置私服的连接地址(在settings.xml中的mirrors中配置)<server> <id>maven-releases</id> <username>admin</username> <password>admin</password> </server> <server> <id>maven-snapshots</id> <username>admin</username> <password>admin</password> </server>
(3)指定从私服下载的依赖无论是RELEASE还是SNAPSHOT版本都可以使用(在settings.xml中的profiles中配置)<mirror> <id>maven-public</id> <mirrorOf>*</mirrorOf> <url>http://192.168.150.101:8081/repository/maven-public/</url> </mirror>
(4)在IDEA的Maven工程的pom文件中配置上传(发布)地址<profile> <id>allow-snapshots</id> <activation> <activeByDefault>true</activeByDefault> </activation> <repositories> <repository> <id>maven-public</id> <url>http://192.168.150.101:8081/repository/maven-public/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> </profile>
(5)发布项目,直接运行deploy生命周期即可<distributionManagement> <!-- release版本的发布地址 --> <repository> <id>maven-releases</id> <url>http://192.168.150.101:8081/repository/maven-releases/</url> </repository> <!-- snapshot版本的发布地址 --> <snapshotRepository> <id>maven-snapshots</id> <url>http://192.168.150.101:8081/repository/maven-snapshots/</url> </snapshotRepository> </distributionManagement>