?前端开发知识点又分 HTML、css 、js
而每一种都有成熟的框架学习但是学习框架前还是要把,原生的这些前端开发技能做一个认识不至于两眼一抹黑、都会一点一点更新的,莫急,莫烦
HTML 全称为 HyperText Markup Language,译为超文本标记语言。
HTML 不是一种编程语言,是一种描述性的标记语言。
作用:HTML是负责描述文档语义的语言。
所谓的超文本,有两层含义:
(1)图片、音频、视频、动画、多媒体等内容,成为超文本,因为它们超出了文本的限制。
(2)不仅如此,它还可以从一个文件跳转到另一个文件,与世界各地主机的文件进行连接。即:超级链接文本。
HTML 不是一种编程语言,是一种描述性的标记语言。这主要有两层含义:
(1)标记语言是一套标记标签。比如:标签<a>表示超链接、标签<img>表示图片、标签<h1>表示一级标题等等,它们都是属于 HTML 标签。
说的通俗一点就是:网页是由网页元素组成的,这些元素是由 HTML 标签描述出来,然后通过浏览器解析,就可以显示给用户看了。
(2)编程语言是有编译过程的,而标记语言没有编译过程,HTML标签是直接由浏览器解析执行。
HTML 格式的文件是一个纯本文文件(就是用txt文件改名而成),用一些标签来描述语义,这些标签在浏览器页面上是无法直观看到的,所以称之为“超文本标记语言”。
接下来,我们需要学习 HTML 中的很多“标签对儿”,这些“标签对儿”能够给文本不同的语义。
比如,面试的时候问你,<h1> 标签有什么作用?
关乎“语义”的更深刻理解,等接下来我们学习了各种标签,就明白了。
其中,我们专门来对XHTML做一个介绍。
XHTML介绍: XHTML:Extensible Hypertext Markup Language,可扩展超文本标注语言。 XHTML的主要目的是为了取代HTML,也可以理解为HTML的升级版。 HTML的标记书写很不规范,会造成其它的设备(ipad、手机、电视等)无法正常显示。 XHTML与HTML4.0的标记基本上一样。 XHTML是严格的、纯净的HTML。
我们稍后将对XHTML的编写规范进行介绍。
我们打开 VS Code 软件,新建一个文件,名叫test.html(注意,文件名是test,后缀名是html),保存到本地。
紧接着,在文件里,输入html:5,然后按一下键盘上的Tab键,就可以自动生成如下内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
</html>
上面的内容,就是 html 页面的骨架。我们在此基础之上,新增几个标签,完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h3>我是三级标题</h3>
<img src="" alt="">
<a href="https://www.jd.com">我是超链接,可以点击一下</a>
</body>
</html>
标签写完之后,我们用 chrome 浏览器打开上面这个test.html文件,看看页面效果:
到此,第一个简单的 HTML 页面就写完了。是不是很有成就感?
HTML标签通常是成对出现的(双边标记),比如 <div> 和 </div>;也有少部分单标签(单边标记),如:<br />、<hr />和<img src="images/1.jpg" />等。
属性与标记之间、各属性之间需要以空格隔开。属性值以双引号括起来。
标签名定义说明<html></html>HTML标签页面中最大的标签,我们成为根标签<head></head>文档的头部注意在head标签中我们必须要设置的标签是title<titile></title>文档的标题让页面拥有一个属于自己的网页标题<body></body>文档的主体元素包含文档的所有内容,页面内容 基本都是放到body里面的
方式1:在 VS Code 中新建 html 文件,输入html:5,按 Tab键后,自动生成的代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Document</title>
</head>
<body>
</body>
</html>
方式2:在Sublime Text中安装Emmet插件。新建html文件,输入html:5,按Tab键后,自动生成的代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
</body>
</html>
方式3:在Sublime Text中安装Emmet插件。新建html文件,输入html:xt,按Tab键后,自动生成的代码如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Document</title>
</head>
<body>
</body>
</html>
上面的方式2和方式3中,我们会发现,第一行的内容有些不太一样,这就是我们接下来要讲的文档声明头。
任何一个标准的HTML页面,第一行一定是一个以<!DOCTYPE ……>开头的语句。这一行,就是文档声明头,即 DocType Declaration,简称DTD。
DTD可告知浏览器文档使用哪种 HTML 或 XHTML 规范。
HTML4.01这个版本是IE6开始兼容的。HTML5是IE9开始兼容的。如今,手机、移动端的网页,就可以使用HTML5了,因为其兼容性更高。
说个题外话,html1 至 html3 是美国军方以及高等研究所用的,并未对外公开。
HTML4.01里面有两大种规范,每大种规范里面又各有3种小规范。所以一共6种规范(见下图)。
HTML4.01里面规定了普通和XHTML两大种规范。HTML觉得自己有一些规定不严谨,比如,标签是否可以用大写字母呢?<H1></H1>所以,HTML就觉得,把一些规范严格的标准,又制定了一个XHTML1.0。在XHTML中的字母X,表示“严格的”。
总结一下,HTML4.01一共有6种DTD。说白了,HTML的第一行语句一共有6种情况:
下面对上图中的三种小规范进行解释:
strict:
表示“严格的”,这种模式里面的要求更为严格。这种严格体现在哪里?有一些标签不能使用。 比如,u标签,就是给一个本文加下划线,但是这和HTML的本质有冲突,因为HTML最好是只负责语义,不要负责样式,而u这个下划线是样式。所以,在strict中是不能使用u标签的。
那怎么给文本增加下划线呢?今后将使用css属性来解决。
XHTML1.0更为严格,因为这个体系本身规定比如标签必须是小写字母、必须严格闭合标签、必须使用引号引起属性等等。
Transitional:表示“普通的”,这种模式就是没有一些别的规范。
Frameset:表示“框架”,在框架的页面使用。
在sublime输入的html:xt,x表示XHTML,t表示transitional。
在HTML5中极大的简化了DTD,也就是说HTML5中就没有XHTML了。HTML5的DTD(文档声明头)如下:
<!DOCTYPE html>
下面这行标签,用于指定页面的语言类型:
<html lang="en">
最常见的语言类型有两种:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta name="Author" content="">
<meta name="Keywords" content="厉害很厉害" />
<meta name="Description" content="网易是中国领先的互联网技术公司,为用户提供免费邮箱、游戏、搜索引擎服务,开设新闻、娱乐、体育等30多个内容频道,及博客、视频、论坛等互动交流,网聚人的力量。" />
<title>Document</title>
</head>
<body>
</body>
</html>
面试题:
头标签内部的常见标签如下:
meta 标签:
meta表示“元”。“元”配置,就是表示基本的配置项目。
常见的几种 meta 标签如下:
(1)字符集 charset:
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
字符集用meta标签中的charset定义,charset就是charactor set(即“字符集”),即网页的编码方式。
字符集(Character set)是多个字符的集合。计算机要准确的处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。
上面这行代码非常关键, 是必须要写的代码,否则可能导致乱码。比如你保存的时候,meta写的和声明的不匹配,那么浏览器就是乱码。
utf-8是目前最常用的字符集编码方式,常用的字符集编码方式还有gbk和gb2312等。关于“编码方式”,我们在下一段会详细介绍。
(2)视口 viewport:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
width=device-width :表示视口宽度等于屏幕宽度。
viewport 这个知识点,初学者还比较难理解,以后学 Web 移动端的时候会用到。
(3)定义“关键词”:
举例如下:
<meta name="Keywords" content="网易,邮箱,游戏,新闻,体育,娱乐,女性,亚运,论坛,短信" />
这些关键词,就是告诉搜索引擎,这个网页是干嘛的,能够提高搜索命中率。让别人能够找到你,搜索到你。
(4)定义“页面描述”:
meta除了可以设置字符集,还可以设置关键字和页面描述。
只要设置Description页面描述,那么百度搜索结果,就能够显示这些语句,这个技术叫做SEO(search engine optimization,搜索引擎优化)。
设置页面描述的举例:
<meta name="Description" content="网易是中国领先的互联网技术公司,为用户提供免费邮箱、游戏、搜索引擎服务,开设新闻、娱乐、体育等30多个内容频道,及博客、视频、论坛等互动交流,网聚人的力量。" />
效果如下:
上面的几种<meta>标签都不用记,但是另外还有一个<meta>标签是需要记住的:
<meta http-equiv="refresh" content="3;http://www.baidu.com">
上面这个标签的意思是说,3秒之后,自动跳转到百度页面。
title 标签:
用于设置网页标题:
<title>网页的标题</title>
title标签也是有助于SEO搜索引擎优化的。
base标签:
<base href="/">
base 标签用于指定基础的路径。指定之后,所有的 a 链接都是以这个路径为基准。
<body>标签的属性有:
<body>标签另外还有一些属性,这里用个例子来解释:
上方代码中,当我们对点我点我这几个字使用超链时,link属性表示默认显示的颜色、alink属性表示鼠标点击但是还没有松开时的颜色、vlink属性表示点击完成之后显示的颜色。效果如下:
计算机,不能直接存储文字,存储的是编码。
计算机只能处理二进制的数据,其它数据,比如:0-9、a-z、A-Z,这些字符,我们可以定义一套规则来表示。假如:A用110表示,B用111表示等。
ASCII码: 美国发布的,用1个字节(8位二进制)来表示一个字符,共可以表示2^8=256个字符。 美国的国家语言是英语,只要能表示0-9、a-z、A-Z、特殊符号。
ANSI编码: 每个国家为了显示本国的语言,都对ASCII码进行了扩展。用2个字节(16位二进制)来表示一个汉字,共可以表示2^16=65536个汉字。例如: 中国的ANSI编码是GB2312编码(简体),对6763汉字进行编码,含600多特殊字符。另外还有GBK(简体)。 日本的ANSI编码是JIS编码。 台湾的ANSI编码是BIG5编码(繁体)。
GBK: 对GB2312进行了扩展,用来显示罕见的、古汉语的汉字。现在已经收录了2.1万左右。并提供了1890个汉字码位。K的含义就是“扩展”。
Unicode编码(统一编码): 用4个字节(32位二进制)来表示一个字符,想法不错,但效率太低。例如,字母A用ASCII表示的话一个字节就够,可用Unicode编码的话,得用4个字节表示,造成了空间的极大浪费。A的Unicode编码是0000 0000 0000 0000 0000 0000 0100 0000
UTF-8(Unicode Transform Format)编码: 根据字符的不同,选择其编码的长度。比如:一个字符A用1个字节表示,一个汉字用2个字节表示。
毫无疑问,开发中,都用UTF-8编码吧,准没错。
中文能够使用的字符集两种:
字库规模: UTF-8(字很全) > gb2312(只有汉字)
重点1:避免乱码
我们用meta标签声明的当前这个html文档的字库,一定要和保存的文件编码类型一样,否则乱码(重点)。
拿 sublime编辑器举例,当我们不设置的时候,sublime默认类型就是UTF-8。而一旦更改为gb2312的时候,就一定要记得设置一下sublime的保存类型: 文件→ set File Encoding to → Chinese Simplified(GBK)。VS Code 的道理一样。
重点2:UTF-8和gb2312的比较
保存大小:UTF-8(更臃肿、加载更慢) > gb2312 (更小巧,加载更快)
总结:
列出2个使用情形:
1) 你们公司是做日本动漫的,经常出现一些日语动漫的名字,网页要使用UTF-8。如果用gb2312将无法显示日语。 2) 你们公司就是中文网页,极度的追求网页的显示速度,要使用gb2312。如果使用UTF-8将每个汉字多一个byte,所以5000个汉字,多5kb。
我们亲测:
我们是怎么查看网页的编码方式的呢?在浏览器中打开网页,右键,选择“查看网页源代码”,找到meta标签中的charset属性即可。
那么,我们为什么可以查看网页的源代码呢?因为这个打开的html网页已经存到我的临时文件夹里了,临时文件夹里的html是纯文本文件,纯文本文件自然可以查看网页的源代码。
(1)所有标记元素都要正确的嵌套,不能交叉嵌套。正确写法举例:<h1><font></font></h1>
(2)所有的标记都必须小写。
(3)所有的标签都必须闭合。
(4)所有的属性值必须加引号。<font color="red"></font>
(5)所有的属性必须有值。<hr noshade="noshade">、<input type="radio" checked="checked" />
(6)XHTML文档开头必须要有DTD文档类型定义。
HTML只在乎标签的嵌套结构,嵌套的关系。谁嵌套了谁,谁被谁嵌套了,和换行、tab无关。换不换行、tab不tab,都不影响页面的结构。
也就是说,HTML不是依靠缩进来表示嵌套的,而是看标签的嵌套关系。但是,我们发现有良好的缩进,代码更易读。建议大家都正确缩进标签。
百度为了追求极致的显示速度,所有HTML标签都没有换行、都没有缩进(tab),HTML和换不换行无关,标签的层次依然清晰,只不过程序员不可读了。如下图所示:
HTML中所有的文字之间,如果有空格、换行、tab都将被折叠为一个空格显示。
举例如下:
标签不封闭的结果是灾难性的。
标签不封闭的举例如下:
TML5的基本结构和它的语法变化
HTML5的兼容性,它可以在老版本的浏览器上正常运行。
HTML5的实用性,内部没有特别的复杂功能,只是封装了一些常用的简单功能。
HTML5的基本结构,文档的根元素依然是<html.../>,这个不变。在<html.../>元素里包含<head.../>和<body.../>两个子元素。<head.../>元素主要定义HTML5的页面头,<title.../>元素用于定义页面标量,除了这些在<head.../>元素中定义meta、样式单等信息;<body.../>元素用于定义页面主体,包括页面的文本内容和大部分标签。
HTML5的语法变化
1、标签不再区分大小写
在HTML5中如:<P>程序世界</p>元素的开始标签和结束标签大小写不匹配,但是这符合HTML5的规范。
2、元素可以省略结束标签
HTML5非常宽容,它允许部分HTML元素省略标签,甚至允许同时省略开始标签和结束标签。具体来说有一下三种:
(1)空元素语法的元素:area base br col command embed hr img input keygen link mate param source wbr 。
这些空元素标签不允许将开始标签和结束标签分开定义。
(2)可以省略结束标签的元素:colgroup dt dd li optgroup option p rt rp thead tbody tfoot tr td th。
这种语法纯属向以前那些不规范的HTML页面妥协。
(3)可以省略全部标签的元素:html head body tbody。
3、允许省略属性值的属性
XHTML要求所有元素的所有属性名都应该小写,所有属性都必须指定属性值,不能简写,而且所有属性值必须使用引号引起来。
HTML5相比起来比较松散,允许部分“标志性”的属性可以省略属性值。当然也支持那种严格的语法。
4、允许属性值不使用引号
传统的XHTML按XML规范对属性值进行要求,要求所有的属性值都必须用引号引起来,但HTML5允许直接给出属性值,即使不放在引号中也是正确的。
MyBatis 是一款优秀的持久层框架,Spring Boot 官方虽然没有对 MyBatis 进行整合,但是 MyBatis 团队自行适配了对应的启动器,进一步简化了使用 MyBatis 进行数据的操作。
因为 Spring Boot 框架开发的便利性,所以实现 Spring Boot 与数据访问层框架(例如 MyBatis)的整合非常简单,主要是引入对应的依赖启动器,并进行数据库相关参数设置即可。
在 MySQL 中,先创建了一个数据库 springbootdata,然后创建了两个表 t_article 和 t_comment 并向表中插入数据。其中评论表 t_comment 的 a_id 与文章表 t_article 的主键 id 相关联。
# 创建数据库
CREATE DATABASE IF NOT EXISTS springbootdata DEFAULT CHARACTER SET utf8;
# 选择使用数据库
USE springbootdata;
# 创建表 t_article 并插入相关数据
DROP TABLE IF EXISTS t_article;
CREATE TABLE t_article
(
id int(20) NOT NULL AUTO_INCREMENT COMMENT '文章id',
title varchar(200) DEFAULT NULL COMMENT '文章标题',
content longtext COMMENT '文章内容',
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
?
INSERT INTO t_article VALUES (1, 'Spring Boot 基础入门', '从入门到精通讲解...');
INSERT INTO t_article VALUES (2, 'Spring Cloud 基础入门', '从入门到精通讲解...');
?
# 创建表 t_comment 并插入相关数据
DROP TABLE IF EXISTS t_comment;
CREATE TABLE t_comment
(
id int(20) NOT NULL AUTO_INCREMENT COMMENT '评论id',
content longtext COMMENT '评论内容',
author varchar(200) DEFAULT NULL COMMENT '评论作者',
a_id int(20) DEFAULT NULL COMMENT '关联的文章id',
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO t_comment VALUES (1, '很全、很详细', 'lucy', 1);
INSERT INTO t_comment VALUES (2, '赞一个', 'tom', 1);
INSERT INTO t_comment VALUES (3, '很详细', 'eric', 1);
INSERT INTO t_comment VALUES (4, '很好,非常详细', '张三', 1);
INSERT INTO t_comment VALUES (5, '很不错', '李四', 2);
使用 Spring Initializr 来初始化项目。
项目名:springbootmybatis
包名:com.renda
启动器:SQL 的 MyBatis Framework、MySQL Driver,Web 的 Spring Web
com.renda.pojo.Comment
public class Comment {
private Integer id;
private String content;
private String author;
private Integer aId;
// getter setter toString ...
}
com.renda.pojo.Article
public class Article {
private Integer id;
private String title;
private String content;
// getter setter toString ...
}
application.properties 更名为 application.yml。在 application.properties 配置文件中进行数据库连接配置:
spring:
datasource:
url: jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC&characterEncoding=UTF-8
username: root
password: password
需求:实现通过 ID 查询 Comment 信息。
com.renda.mapper.CommentMapper
public interface CommentMapper {
@Select("select * from t_comment where id=#{id}")
Comment findById(Integer id);
}
com.renda.SpringbootmybatisApplication
@SpringBootApplication
@MapperScan("com.renda.bootmybatis.mapper") //执行扫描mapper的包名
public class SpringbootmybatisApplication {
?
public static void main(String[] args) {
SpringApplication.run(SpringbootmybatisApplication.class, args);
}
?
}
导入 Junit 的依赖,增加测试方法:
com.renda.SpringbootmybatisApplicationTests
@RunWith(SpringRunner.class)
@SpringBootTest
class SpringbootmybatisApplicationTests {
?
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
private CommentMapper commentMapper;
?
@Test
void findCommentById() {
Comment comment=commentMapper.findById(1);
System.out.println(comment);
}
?
}
控制台中查询的 Comment 的 aId 属性值为 null,没有映射成功。这是因为编写的实体类 Comment 中使用了驼峰命名方式将 t_comment 表中的 a_id 字段设计成了 aId 属性,所以无法正确映射查询结果。
为了解决上述由于驼峰命名方式造成的表字段值无法正确映射到类属性的情况,可以在 Spring Boot 全局配置文件 application.yml 中添加开启驼峰命名匹配映射配置,示例代码如下:
mybatis:
configuration:
# 开启驼峰命名匹配映射
map-underscore-to-camel-case: true
第一、二步骤使用 Free Mybatis plugin 插件生成:使用 IDEA 连接 Database,然后选中要自动生成代码的表,右键 -> mybatis-generator -> 按照需求输入信息,点击 ok。
public interface ArticleMapper {
int deleteByPrimaryKey(Integer id);
?
int insert(Article record);
?
int insertSelective(Article record);
?
Article selectByPrimaryKey(Integer id);
?
int updateByPrimaryKeySelective(Article record);
?
int updateByPrimaryKey(Article record);
}
resources 目录下创建一个统一管理映射文件的包 mapper,并在该包下编写与 ArticleMapper 接口方应的映射文件 ArticleMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.renda.mapper.ArticleMapper">
<resultMap id="BaseResultMap" type="com.renda.pojo.Article">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="title" jdbcType="VARCHAR" property="title" />
<result column="content" jdbcType="VARCHAR" property="content" />
</resultMap>
<sql id="Base_Column_List">
id, title, content
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_article
where id=#{id,jdbcType=INTEGER}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from t_article
where id=#{id,jdbcType=INTEGER}
</delete>
<insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.renda.pojo.Article" useGeneratedKeys="true">
insert into t_article (title, content)
values (#{title,jdbcType=VARCHAR}, #{content,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.renda.pojo.Article" useGeneratedKeys="true">
insert into t_article
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="title !=null">
title,
</if>
<if test="content !=null">
content,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="title !=null">
#{title,jdbcType=VARCHAR},
</if>
<if test="content !=null">
#{content,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.renda.pojo.Article">
update t_article
<set>
<if test="title !=null">
title=#{title,jdbcType=VARCHAR},
</if>
<if test="content !=null">
content=#{content,jdbcType=VARCHAR},
</if>
</set>
where id=#{id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.renda.pojo.Article">
update t_article
set title=#{title,jdbcType=VARCHAR},
content=#{content,jdbcType=VARCHAR}
where id=#{id,jdbcType=INTEGER}
</update>
</mapper>
在项目中编写的 XML 映射文件,Spring Boot 并无从知晓,所以无法扫描到该自定义编写的 XML 配置文件,还必须在全局配置文件 application.yml 中添加 MyBatis 映射文件路径的配置,同时需要添加实体类别名映射路径,示例代码如下:
mybatis:
configuration:
# 开启驼峰命名匹配映射
map-underscore-to-camel-case: true
# 加载 resources/mapper 文件夹下的所有的 xml 文件
mapper-locations: classpath:mapper/*.xml
# 配置 XML 映射文件中指定的实体类别名路径
type-aliases-package: com.renda.pojo
@RunWith(SpringRunner.class)
@SpringBootTest
class SpringbootmybatisApplicationTests {
@Autowired
private ArticleMapper articleMapper;
?
@Test
void findArticleById() {
Article article=articleMapper.selectByPrimaryKey(1);
System.out.println(article);
}
}
在项目的 pom.xml 中添加如下:
<!-- redis 依赖包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在 application.yml 中配置redis数据库连接信息,如下:
spring:
redis:
# Redis 服务器地址
host: 192.168.186.128
# Redis 服务器连接端口
port: 6379
jedis:
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 18
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: 3000
# 连接池中的最大空闲连接
max-idle: 20
# 连接池中的最小空闲连接
min-idle: 2
# 连接超时时间(毫秒)
timeout: 3000
# Redis 数据库索引(默认为 0)
database: 0
将 RedisTemplate 实例包装成一个工具类,便于对 redis 进行数据操作。
package com.renda.util;
?
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
?
import java.util.concurrent.TimeUnit;
?
/**
* @author Renda Zhang
* @since 2020-10-30 1:00
*/
@Component
public class RedisUtils {
?
@Autowired
private RedisTemplate redisTemplate;
?
/**
* 读取缓存
*/
public Object get(final String key) {
return redisTemplate.opsForValue().get(key);
}
?
/**
* 写入缓存
*/
public boolean set(String key, Object value) {
boolean result=false;
try {
redisTemplate.opsForValue().set(key, value, 1, TimeUnit.DAYS);
result=true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
?
/**
* 更新缓存
*/
public boolean getAndSet(final String key, String value) {
boolean result=false;
try {
redisTemplate.opsForValue().getAndSet(key, value);
result=true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
?
/**
* 删除缓存
*/
public boolean delete(final String key) {
boolean result=false;
try {
redisTemplate.delete(key);
result=true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
写一个测试用例类来完成对 redis 的整合。
@RunWith(SpringRunner.class)
@SpringBootTest
class SpringbootmybatisApplicationTests {
?
// 写入,key:1,value:mysql 数据库中 id 为 1 的 article 记录
@Autowired
private RedisUtils redisUtils;
?
@Test
void writeRedis() {
redisUtils.set("1", articleMapper.selectByPrimaryKey(1));
System.out.println("success");
}
?
@Test
void readRedis() {
Article article=(Article) redisUtils.get("1");
System.out.println(article);
}
?
}
前端模板引擎技术的出现,使前端开发人员无需关注后端业务的具体实现,只关注自己页面的呈现效果即可,并且解决了前端代码错综复杂的问题、实现了前后端分离开发。Spring Boot 框架对很多常用的模板引擎技术(如:FreeMarker、Thymeleaf、Mustache 等)提供了整合支持。
Spring Boot 不太支持常用的 JSP 模板,并且没有提供对应的整合配置,这是因为使用嵌入式 Servlet 容器的 Spring Boot 应用程序对于 JSP 模板存在一些限制 :
Thymeleaf 是一种现代的基于服务器端的 Java 模板引擎技术,也是一个优秀的面向 Java 的 XML、XHTML、HTML5 页面模板,它具有丰富的标签语言、函数和表达式,在使用 Spring Boot 框架进行页面设计时,一般会选择 Thymeleaf 模板。
在 HTML 页面上使用 Thymeleaf 标签,Thymeleaf 标签能够动态地替换掉静态内容,使页面动态展示。
为了更直观的认识 Thymeleaf,下面展示一个在 HTML 文件中嵌入了 Thymeleaf 的页面文件,示例代码如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" media="all"
href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
<title>Thymeleaf</title>
</head>
<body>
<p th:text="${hello}">Hello Thymeleaf</p>
</body>
</html>
上述代码中,“xmlns:th="http://www.thymeleaf.org" 用于引入 Thymeleaf 模板引擎标签,使用关键字 th 标注标签是 Thymeleaf 模板提供的标签,其中,th:href="@{/css/gtvg.css}" 用于引入外联样式文件,th:text="${hello}" 用于动态显示标签文本内容。
常用标签:
标准表达式:
变量表达式 ${...} 主要用于获取上下文中的变量值,示例代码如下:
<p th:text="${title}">这是标题</p>
示例使用了 Thymeleaf 模板的变量表达式 ${...} 用来动态获取 P 标签中的内容,如果当前程序没有启动或者当前上下文中不存在 title 变量,该片段会显示标签默认值“这是标题”;如果当前上下文中存在 title 变量并且程序已经启动,当前 P 标签中的默认文本内容将会被 title 变量的值所替换,从而达到模板引擎页面数据动态替换的效果。
同时,Thymeleaf 为变量所在域提供了一些内置对象,具体如下所示:
#ctx:上下文对象
#vars:上下文变量
#locale:上下文区域设置
#request:(仅限 Web Context)HttpServletRequest 对象
#response:(仅限 Web Context)HttpServletResponse 对象
#session:(仅限 Web Context)HttpSession 对象
#servletContext:(仅限 Web Context)ServletContext 对象
结合上述内置对象的说明,假设要在 Thymeleaf 模板引擎页面中动态获取当前国家信息,可以使用 #locale 内置对象,示例代码如下:
The locale country is: <span th:text="${#locale.country}">China</span>
上述代码中,使用 th:text="${#locale.country}" 动态获取当前用户所在国家信息,其中标签内默认内容为 China,程序启动后通过浏览器查看当前页面时,Thymeleaf 会通过浏览器语言设置来识别当前用户所在国家信息,从而实现动态替换。
选择变量表达式和变量表达式用法类似,一般用于从被选定对象而不是上下文中获取属性值,如果没有选定对象,则和变量表达式一样,示例代码如下:
<div th:object="${book}">
<p>titile: <span th:text="*{title}">标题</span>.</p>
</div>
*{title} 选择变量表达式获取当前指定对象 book 的 title 属性值。
消息表达式 #{...} 主要用于 Thymeleaf 模板页面国际化内容的动态替换和展示,使用消息表达式 #{...} 进行国际化设置时,还需要提供一些国际化配置文件。
链接表达式 @{...} 一般用于页面跳转或者资源的引入,在 Web 开发中占据着非常重要的地位,并且使用也非常频繁,示例代码如下:
<a th:href="@{http://localhost:8080/order/details(orderId=${o.id})}">view</a>
<a th:href="@{/order/details(orderId=${o.id},pid=${p.id})}">view</a>
上述代码中,链接表达式 @{...} 分别编写了绝对链接地址和相对链接地址。在有参表达式中,需要按照 @{路径(参数名称=参数值,参数名称=参数值...)} 的形式编写,同时该参数的值可以使用变量表达式来传递动态参数值。
片段表达式 ~{...} 用来标记一个片段模板,并根据需要移动或传递给其他模板。其中,最常见的用法是使用 th:insert 或 th:replace 属性插入片段,示例代码如下:
<div th:insert="~{thymeleafDemo::title}"></div>
上述代码中,使用 th:insert 属性将 title 片段模板引用到该标签中。thymeleafDemo 为模板名称,Thymeleaf 会自动查找 /resources/templates/ 目录下的 thymeleafDemo 模板,title 为片段名称。
1) Thymeleaf 模板基本配置
首先在 Springbootdemo2 项目中使用 Thymeleaf 模板,首先必须保证引入 Thymeleaf 依赖,示例代码如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
其次,在全局配置文件中配置 Thymeleaf 模板的一些参数。一般 Web 项目都会使用下列配置,示例代码如:
spring:
thymeleaf:
# 在开发阶段,为了验证建议关闭缓存
cache: true
# 模板编码
encoding: UTF-8
# 应用于模板的模板模式
mode: HTML5
# 指定模板页面存放路径
prefix: classpath:/templates/
# 指定模板页面名称的后缀
suffix: .html
上述配置中,spring.thymeleaf.cache 表示是否开启 Thymeleaf 模板缓存,默认为 true,在开发过程中通常会关闭缓存,保证项目调试过程中数据能够及时响应;spring.thymeleaf.prefix 指定了 Thymeleaf 模板页面的存放路径,默认为classpath:/templates/;spring.thymeleaf.suffix 指定了 Thymeleaf 模板页面的名称后缀,默认为 .html。
2)静态资源的访问
开发 Web 应用时,难免需要使用静态资源。Spring boot 默认设置了静态资源的访问路径。
使用 Spring Initializr 方式创建的 Spring Boot 项目,默认生成了一个 resources 目录,在 resources 目录中的 public、resources、static 三个子目录下,Spring boot 默认会挨个从public、resources、static 里面查找静态资源。
1)创建 Spring Boot 项目,引入 Thymeleaf 依赖。
2)编写配置文件。
编辑 application.yml 全局配置文件,在该文件中对 Thymeleaf 模板页面的数据缓存进行设置。
# thymeleaf 页面缓存设置(默认为 true),开发中方便调试应设置为 false,上线稳定后应保持默认 true
spring:
thymeleaf:
cache: false
使用 spring.thymeleaf.cache=false 将 Thymeleaf 默认开启的缓存设置为了 false,用来关闭模板页面缓存。
3)创建 web 控制类
在项目中创建名为 com.renda.controller 的包,并在该包下创建一个用于前端模板页面动态数据替换效果测试的访问实体类 LoginController。
@Controller
public class LoginController {
?
@RequestMapping("/toLogin")
public String toLoginView(Model model){
model.addAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));
return "login"; // resources/templates/login.html
}
?
}
toLoginView() 方法用于向登录页面 login.html 跳转,同时携带了当前年份信息 currentYear。
4)创建模板页面并引入静态资源文件。
在 classpath:/templates/ 目录下引入一个用户登录的模板页面 login.html。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1,shrink-to-fit=no">
<title>用户登录界面</title>
<link th:href="@{../static/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{../static/css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">
<!-- 用户登录 form 表单 -->
<form class="form-signin">
<img class="mb-4" th:src="@{../static/img/login.png}" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal">请登录</h1>
<input type="text" class="form-control"
th:placeholder="用户名" required="" autofocus="">
<input type="password" class="form-control"
th:placeholder="密码" required="">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> 记住我
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit" >登录</button>
<p class="mt-5 mb-3 text-muted">? <span th:text="${currentYear}">2019</span>-<span th:text="${currentYear}+1">2020</span></p>
</form>
</body>
</html>
通过 xmlns:th="http://www.thymeleaf.org 引入了 Thymeleaf 模板标签;
使用 th:href 和 th:src 分别引入了两个外联的样式文件和一个图片;
使用 th:text 引入了后台动态传递过来的当前年份 currentYear。
5)效果测试
可以看出,登录页面 login.html 显示正常,在页面底部动态显示了当前日期 2020-2021,而不是文件中的静态数字 2019-2020。这进一步说明了 Spring Boot 与 Thymeleaf 整合成功,完成了静态资源的引入和动态数据的显示。
实战技能补充:lombok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<!-- 编译期间生效 -->
<scope>provided</scope>
</dependency>
需求:实现用户的 CRUD 功能
初始化数据库信息:
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`
(
id int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
username varchar(100) DEFAULT NULL COMMENT '用户名',
password varchar(100) DEFAULT NULL COMMENT '密码',
birthday varchar(100) DEFAULT NULL COMMENT '生日',
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `user` VALUES (1, 'zhangsan', '123', '2020-10-1');
INSERT INTO `user` VALUES (2, 'lisi', '123', '2020-10-2');
INSERT INTO `user` VALUES (3, 'wangwu', '123', '2020-10-10');
INSERT INTO `user` VALUES (4, 'yuanjing', '123', '2020-10-11');
使用 Spring Initializr 新建一个工程 springbootuser,选择依赖:Developer Tools -> Lombok,Web -> Spring Web,SQL -> [MyBatis Framework、MySQL Driver]。
<!-- 引入阿里巴巴数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.3</version>
</dependency>
使用 FreeMyBatis 生成实体类。
使用 FreeMyBatis 生成 UserMapper 相关的代码。
com.renda.pojo.User
@Data // Lombok 自动生成 getter 和 setter
public class User implements Serializable {
/**
* 用户id
*/
private Integer id;
?
/**
* 用户名
*/
private String username;
?
/**
* 密码
*/
private String password;
?
/**
* 生日
*/
private String birthday;
?
private static final long serialVersionUID=1L;
}
com.renda.mapper.UserMapper
public interface UserMapper {
int deleteByPrimaryKey(Integer id);
?
int insert(User record);
?
int insertSelective(User record);
?
User selectByPrimaryKey(Integer id);
?
int updateByPrimaryKeySelective(User record);
?
int updateByPrimaryKey(User record);
}
src\main\resources\mapper\UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.renda.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.renda.pojo.User">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="username" jdbcType="VARCHAR" property="username" />
<result column="password" jdbcType="VARCHAR" property="password" />
<result column="birthday" jdbcType="VARCHAR" property="birthday" />
</resultMap>
<sql id="Base_Column_List">
id, username, `password`, birthday
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from user
where id=#{id,jdbcType=INTEGER}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from user
where id=#{id,jdbcType=INTEGER}
</delete>
<insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.renda.pojo.User" useGeneratedKeys="true">
insert into user (username, `password`, birthday
)
values (#{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{birthday,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.renda.pojo.User" useGeneratedKeys="true">
insert into user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username !=null">
username,
</if>
<if test="password !=null">
`password`,
</if>
<if test="birthday !=null">
birthday,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="username !=null">
#{username,jdbcType=VARCHAR},
</if>
<if test="password !=null">
#{password,jdbcType=VARCHAR},
</if>
<if test="birthday !=null">
#{birthday,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.renda.pojo.User">
update user
<set>
<if test="username !=null">
username=#{username,jdbcType=VARCHAR},
</if>
<if test="password !=null">
`password`=#{password,jdbcType=VARCHAR},
</if>
<if test="birthday !=null">
birthday=#{birthday,jdbcType=VARCHAR},
</if>
</set>
where id=#{id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.renda.pojo.User">
update user
set username=#{username,jdbcType=VARCHAR},
`password`=#{password,jdbcType=VARCHAR},
birthday=#{birthday,jdbcType=VARCHAR}
where id=#{id,jdbcType=INTEGER}
</update>
</mapper>
com.renda.service.UserService
public interface UserService {
?
/**
* 查询所有
*/
List<User> queryAll();
?
/**
* 通过 ID 查询
*/
User findById(Integer id);
?
/**
* 新增
*/
void insert(User user);
?
/**
* 通过 ID 删除
*/
void deleteById(Integer id);
?
/**
* 修改
*/
void update(User user);
?
}
com.renda.service.impl.UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
?
@Autowired
private UserMapper userMapper;
?
@Override
public List<User> queryAll() {
return userMapper.queryAll();
}
?
@Override
public User findById(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
?
@Override
public void insert(User user) {
// 将除 id 外所有的列都拼接入 SQL 语句
// userMapper.insert(user);
// 只将不为空的列才拼接入 SQL 语句(优先使用,减少高并发下数据传输)
userMapper.insertSelective(user);
}
?
@Override
public void deleteById(Integer id) {
userMapper.deleteByPrimaryKey(id);
}
?
@Override
public void update(User user) {
userMapper.updateByPrimaryKeySelective(user);
}
?
}
com.renda.controller.UserController
/**
* restful 格式进行访问
* 查询:GET
* 新增: POST
* 更新:PUT
* 删除: DELETE
*
* @author Renda Zhang
* @since 2020-10-31 1:36
*/
@RestController
@RequestMapping("/user")
public class UserController {
?
@Autowired
private UserService userService;
?
/**
* 查询所有
*/
@GetMapping("/query")
public List<User> queryAll(){
return userService.queryAll();
}
?
/**
* 通过 ID 查询
*/
@GetMapping("/query/{id}")
public User queryById(@PathVariable Integer id){
return userService.findById(id);
}
?
/**
* 删除
*/
@DeleteMapping("/delete/{id}")
public String delete(@PathVariable Integer id){
userService.deleteById(id);
return "删除成功";
}
?
/**
* 新增
*/
@PostMapping("/insert")
public String insert(User user){
userService.insert(user);
return "新增成功";
}
?
/**
* 修改
*/
@PutMapping("/update")
public String update(User user){
userService.update(user);
return "修改成功";
}
?
}
重命名 application.properties 为 application.yml
src\main\resources\application.yml
# 服务器配置
server:
port: 8090
?
spring:
# 数据源配置
datasource:
name: druid
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/springbootdata?characterEncoding=utf-8&serverTimezone=UTC
username: root
password: password
?
# 整合 MyBatis
mybatis:
# 声明 MyBatis 文件所在的位置
mapper-locations: classpath:mapper/*Mapper.xml
com.renda.SpringbootuserApplication
@SpringBootApplication
// 使用的 Mybatis, 扫描 com.renda.mapper
@MapperScan("com.renda.mapper")
public class SpringbootuserApplication {
?
public static void main(String[] args) {
SpringApplication.run(SpringbootuserApplication.class, args);
}
?
}
需求:将 Spring Boot 项目使用 maven 指令打成 jar 包并运行测试。
1)添加打包组件将项目中的资源、配置、依赖包打到一个 jar 包中;可以使用 maven 的 package 命令。
2)部署:java -jar 包名
确保 pom.xml 文件中有如下的打包组件:
<build>
<plugins>
<!-- 打 jar 包时如果不配置该插件,打出来的 jar 包没有清单文件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
部署运行:
java -jar springbootuser-0.0.1-SNAPSHOT.jar
想了解更多,欢迎关注我的微信公众号:Renda_Zhang
*请认真填写需求信息,我们会在24小时内与您取得联系。