查询/下载最新版本和坐标的jar包
架构图
之前我们关注的是前端的解决方案(涉及到的技术有H5、CSS3、JavaScript,CSS升级为Bootstrap再升级到ElementUI,JavaScript升级到jQuery再升级到Vue+NodeJS)现在开始我们开始关注后端的解决方案,也就是服务器端到底干了什么,哪些技术来支持(SpringBoot、Maven、SpringMVC、Spring、Mybatis)。这样前后端都学习完,整个软件项目所需要的基本技术就全线贯通,就可以自己独立完成企业级项目的开发了。
下面我们来描述一个经典的业务请求过程:前端html页面发起ajax请求(http://localhost:8080/factoryController/findAll),访问SpringMVC框架的Controller控制层,SpringMVC框架解析请求,找到要调用的某个Controller,找到其中的findAll方法,同时把请求提交的参数封装到java对象中。之后Controller层把请求传递给Spring框架的Service业务层,Service层在把请求传递给Mybatis框架的Mapper持久层,Mapper访问MySQL数据库进行数据库表的查询,查询结果返回给Mapper层,Mapper再返回给Service层,Service再返回给Controller层。Controller把java数据转换为json字符串,返回给ajax调用,ajax进行回调并把json字符串转换为js对象,再在页面中就可以通过js/vue解析js对象,最终把数据展现到html页面中。
开发工具:前端采用HBuilderX,而后端采用eclipse/idea
项目管理:前端采用npm、webpack,而后端采用Maven、SpringBoot
web中间件:前端采用NodeJS,而后端采用Tomcat
用来下载并且管理jar包的工具,用来构建项目的方式
Maven是跨平台的项目管理工具。作为Apache组织中的一个颇为成功的开源项目,主要服务于基于java平台的项目构建、依赖管理和项目信息管理。无论是小型的开源类库项目,还是大型的企业级应用;无论是传统的瀑布式开发,还是流行的敏捷模式,Maven都能大显身手。
Java工程中我们自己去找jar,或者来自官网,或者来自网友的分享,或者来自项目团队的共享,不论何种方式,都需要把jar文件复制到lib目录中,并且buildpath。
Maven改变这种手动维护jar的方式,设计出一套自动维护jar的体系,已经广泛在软件项目中使用,是软件开发人员必须掌握的技术。
全新的设计体系:创先河的发明pom模型,引入了“仓库”、“依赖”、“坐标”和“命令”。
仓库:
Maven和我们之前学习的git很类似,其也是分布式架构,它有一个全球仓库,称为中央仓库,全球开发者都可以连接它来自动下载jar包,而无需去厂家官网下载了。都到一个中央仓库下载,中央仓库压力山大,于是全球各地做镜像仓库,如中国就有网易、阿里等镜像仓库。但每次都去外网下载,那不成天光交网费了。Maven当然不会这么做了,它还有本地仓库。下载一次后,不会再次下载的,除非你删除了。
当用户需要某个jar包时,先到本地仓库寻找,没有再去镜像仓库,没有再去中央仓库。中央仓库找到后,并不直接返回到本地仓库,而是保存一份到镜像仓库,镜像仓库返回本地仓库,本地仓库也保存一份,然后返回给调用者。这样设计是不是太精妙了,只需维护中央仓库,其它仓库自行维护。这就是maven的魅力,这种设计思想是我们开发者需要琢磨和借鉴的。
因为其全自动化,中央仓库默认,镜像仓库需要配置,而无需写一句代码。
仓库只解决了jar包从哪来和放在哪里,jar包千千万,我们有jdbc驱动,有junit单元测试,有spring框架,有mybatis等等,那如何去给我们的项目调用呢?
依赖:
每个核心jar包形成一个依赖,maven底层进行它相关的jar的自动导入
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.32version>
dependency>
坐标:
通过Maven下载很多的jar包,唯一的定位方式就是使用坐标。
为什么要有坐标呢?坐标的本质是什么呢?
Maven世界拥有大量构建,我们需要找一个用来唯一标识一个构建的统一规范。拥有了统一规范,就可以把查找工作交给机器,默认查找jar包。
看完有何感想?还没豁然开朗?坐标不就是形成一套文件存放规则,这样全球不同厂商的jar包都可以保存在maven仓库中,而不会冲突,各自在各自的目录中。哪怕自家的因为版本号的不同,也会放在不同的目录中,也就不会自家引起冲突。
同时最重要的是,拥有了统一规范,拥有了唯一命名,就可以把查找工作交给自动查找到所要的jar包。
这设计水平可见一斑。一套目录规则,就把jar自动化处理变成现实。
命令:
maven提供了很多命令,常用的有clean , install
接下来又是一个很牛的设计,这个思想非常值得品味。Maven借鉴前辈定义了一套生命周期。共有3个生命周期:clean、default、site,每个生命周期包含多个阶段phase。这个并没有什么称奇的,而接下来才是厉害的地方。
常用命令:
clean 清理
删除编译后的目录 target .java文件 .jar(xxx.class文件)
用法:
1.当出现编译问题时,使用clean。
2.项目打包前使用
compile 编译
test 测试
site 站点文档
package 打包 jar、war
deploy 部署到私服
install 安装jar到本地仓库中(打包)
项目发布,将xxx.class文件打包成xxx.jar
jar包位置:
1.target目录
2.本地仓库
run 运行
每个周期中运行一个命令时,在这个周期里的其他在该命令之前的phase步骤都会执行。如:执行install会自动执行compile(编译java变成了class),test(运行所有单元测试类),package(把整个项目零碎的class文件打包为jar包),最终把成品jar发布到本地仓库中。但执行install并不会自动执行clean。
小结
优点:
缺点:
官网
http://maven.apache.org/download.html
程序员操守:
apache-maven-3.6.3\conf\settings.xml 全局配置文件
默认从maven官网下载,全球都去下载,又是国外网站,因此速度缓慢。
配置为阿里镜像,速度快,但有时有错,如果有错,删除配置,默认从maven官网下载。
配置阿里私服镜像仓库,可以写在mirrors标签中间:
<mirror>
<id>aliid>
<name>ali Mavenname>
<mirrorOf>*mirrorOf>
<url>https://maven.aliyun.com/repository/public/url>
mirror>
默认仓库位置:C:\Users\tarena.m2,建议改变默认仓库位置的配置,否则重装操作系统时,可能会把仓库误删除。
课上使用默认配置 jar包版本下载2.5.3
路径: https://start.spring.io/
https://start.aliyun.com/
例子: 假设: A.jar 依赖 B.jar , B.jar 依赖于 C.jar
说明:在项目中 添加了web的jar包,则可以依赖其他的jar包,为项目提供支持。
如果pom.xml文件jar包文件报错. 检查本地仓库中的jar包文件是否完整.如果下载不完整. 则手动删除文件.之后重写下载.
说明: 当maven依赖jar包文件时,首先根据坐标查找本地仓库的路径,之后添加jar包文件. 之后再次加载该文件目录的xxx.pom文件.读取其中的依赖项,进行再次依赖.以此类推.
说明: maven数据传输 通过sha1的数字摘要 包装数据的完整性
SHA-1(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。
问题1:如果数据相同,采用相同的hash函数,则值是否相同?
答案:必然相同!!!!
问题2:常见hash编码,是多少位16进制数?
答案:8位16进制数
问题3:8位16进制数,取值区间多少?
答案:2^32 00000000-FFFFFFFF
(2^4)^8=2^32
问题4:1kb和的数据和1gb的数据,hash谁快?
答案:一样快
问题5:数据相同,则hash码相同;hash码相同,则数据相同吗?
答案:不一定,可能会发生hash碰撞
降低碰撞的概率,增大hash长度
当我们从远程仓库下载完jar包时,判断jar包是否被黑客篡改过
查询最新版本和坐标 http://search.maven.org/
在使用maven时,如果报内存溢出,如使用 mvn site会耗费大量内存,则修改默认配置。
D:\Maven\apache-maven-3.6.3\bin\mvn.cmd
在@REM set MAVEN\_OPTS=……后加入
set MAVEN\_OPTS= -Xms128m -Xmx512m
例如oracle、全文检索的IKAnalyzer分词器、Dubbox等。
解决办法:按maven坐标手动创建目录结构,将jar单独下载,放入其中。
远程仓库为国外网站,又是众矢之的,全球都到那里下载。常会因为网络故障导致jar下载不完全:
jsp-api-2.1.jar.lastUpdated -- 没下载全,不能用,用时会报错
mysql-connector-java-5.1.48.jar -- 下载OK的,才可以用
遇到这样的情况:
最恶劣的一种情况,下载出异常,也就是pom.xml会提示jar包有问题,可到maven本地仓库,jar也存在。这时可以打开jar包,看是否能打开。如果打不开,则删除,触发maven重新下载。
注意: Maven不同的myeclipse/eclipse,myclipse的maven的插件会调用不同版本的jar。不会缺少业务使用的jar。
Maven命令实际是一个jar包,运行前必须需下载maven的插件,运行时判断如果不存在会自动下载。
拷贝环境没问题的 同学的配置文件 和 仓库 。
通常在项目中,我们会同时依赖同一个构件的不同模块,如 spring-orm-3.2.0,spring-context-3.2.0,且多个模块版本相同,为了维护和升级方便,我们可以对其同一管理,这时可以使用到 Maven 属性,类似于变量的概念。
<properties>
<junit.version>4.10junit.version>
<spring.version>4.1.3.RELEASEspring.version>
properties>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>${spring.version}version>
dependency>命令插件
一键部署,执行每个命令会自动调用前面的命令。可以一次执行多个命名。只能执行本生命周期中的前面的命令。
每个maven命令就是一个jar包,一个maven插件,在第一次运行时下载。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>cn.tedugroupId>
<artifactId>cgb2106maven01artifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.48version>
dependency>
dependencies>
project>
是一个版本控制产品,用来实现资源的版本控制。
可以把资源随时上传到Git上,可以随时拉取下载
好处:快速恢复到历史版本,容错性高
远程仓库:是指 Gitee官网 的网址,存你已经上传上去的资源
本地仓库:是指 磁盘里的一个路径,存你即将要上传的资源
本地索引:是指将要提交的数据建立索引,方便查找定位
工作空间:保存了资源的位置
过程:工作空间 -> 本地索引 -> 本地仓库 -> 远程仓库
官网下载地址:https://git-scm.com/download/win
选择自己需要版本就行在实际开发中我们会遇到一些问题,电脑蓝屏,代码丢了,懊悔不?磁盘坏了,文件没了,懊悔不?时间长了,文件找不到了,懊悔不?手欠,之前代码运行好好的,非要去优化下,结果还不如以前,信心满满,之前代码没保存,懊悔不?怎么解决呢?版本控制就可以解决这些难题了。
版本控制(Version Control System),它功能强大,不仅能备份你的文件,还可以实现很多:
是一个版本控制产品,用来实现资源的版本控制.
可以把资源随时上传到Git上,可以随时拉取下载
好处: 快速恢复到历史版本. 容错性高.
远程仓库: 是指 Gitee官网 的网址,存你已经传上去的资源
本地仓库: 是指你磁盘里的一个路径,存你即将要上传的资源
本地索引: 是指将要提交的数据建立索引,方便查找定位
工作空间: 保存了资源的位置
过程: 工作空间 -> 本地索引 -> 本地仓库 -> 远程仓库
Workspace:开发者工作区,也就是你当前写代码的目录,它一般保持的是最新仓库代码。
Index / Stage:缓存区,最早叫Stage,现在新版本已经改成index,位于.git目录中,它用来存放临时动作,比如我们做了git add或者git rm,都是把文件提交到缓存区,这是可以撤销的,然后在通过git commit将缓存区的内容提交到本地仓库
Repository:仓库区,是仓库代码,你所有的提交都在这里,git会保存好每一个历史版本,存放在仓库区,它可以是服务端的也可以是本地的,因为在分布式中,任何人都可以是主仓库。
Remote:远程仓库,只能是别的电脑上的仓库,即服务器仓库。
每个人必须有自己的账号,先官网注册账号:
https://gitee.com/
Git-2.27.0-64-bit,一路next,安装完桌面右键菜单有下面两项,安装完成。选择Git Bash,进入git客户端。
git config --global user.name "fish-river" #设置了Gitee注册的用户名
git config --global user.email "[email protected]" #设置了Gitee注册的邮箱
# config:参数是用来配置git环境的
git config --list # 查看设置信息
git init #初始化一个Git的环境
#init:初始化当前目录为仓库,初始化后会自动将当前仓库设置为master
touch 1.txt #创建一个文件
git add 1.txt #添加,从工作空间到缓存空间
git commit -m "first commit" #从缓存空间提交到本地仓库
git commit --amend #重写上一次的提交信息
git remote add origin https://gitee.com/fish-river/cgb2106test.git
#添加到指定远程仓库里
git push -u origin master #从本地仓库推送到远程仓库
Username for 'https://gitee.com': #输入自己注册的账号
Password for 'https://[email protected]': #输入自己注册的密码
刷新Gitee官网
1, 把你要提交的资源拷贝到 本地仓库
2, 在本地仓库处, 执行以下Git命令提交资源
git add .
git commit -m "test"
git push -u origin master
# push:将本地仓库与远程仓库合并
# -u:将本地仓库分支与远程仓库分支一起合并,就是说将master的分支也提交上去,这样你就可以在远程仓库上看到你在本地仓库的master中创建了多少分支,
# 不加这个参数只将当前的master与远程的合并,没有分支的历史记录,也不能切换分支
# origin:远程仓库的意思,如果这个仓库是远程的那么必须使用这个选项
# master:提交本地matser分支仓库
git 工作区 缓存区 本地仓库 远程仓库
1.检查当前分支 git branch
2.创建分支 git checkout -b "新分支名称"
3.推送新分支 git push -u origin “新分支名称”
第一次推送需要写-u
4.将文件添加到缓存区 git add .
5.提交代码 git commit -m “提交的文件的描述信息”
6.推动代码到云端 git push
7.合并代码到主分支 git merge "分支名称"
在主线中操作
8.克隆代码 git clone “仓库地址”
9.将远程库的数据更新到本地 git pull
10.git强制删除分支:git branch -D
11.查看仓库信息:git remote
12.修改分支名称:git branch -m 分支名 "新的分支名"
13.查看日志简洁方法:git log
14.删除文件:git rm "文件名"
如果我们使用普通的命令,rm删除文件,git状态会提示你删除了文件,你只需要使用add重新提交一次就可以了。
说明:开发中需要创建自己的分支结构,如果分支中的代码准确无误,应该将分支结构并入主线(master)
步骤:
git checkout -b "新分支名称"
git push -u origin "新分支名称"
git add .
git commit -m “提交的文件的描述信息”
git push
提交到分支中git checkout master
git merge "分支名称"
(在本地库中合并,在主线中操作)git push
简化了Model测代码的编写。
以前的pojo类/实体类,需要自己提供get、set、toString、equals、hashCode
Lombok通过各种注解,简化了以上操作
@Data会自动生成get、set、toString、equals、hashCode
@NoArgsConstructor自动生成无参构造
@AllArgsConstructor自动生成全参构造
@Accessors(chain=true) -- 链式编程
以前的Java项目中,充斥着太多不友好的代码:POJO的getter/setter/toString;异常处理;I/O流的关闭操作等等,这些样板代码既没有技术含量,又影响着代码的美观,Lombok应运而生。
@Getter/@Setter: 作用类上,生成所有成员变量的getter/setter方法;作用于成员变量上,生成该成员变量的getter/setter方法。可以设定访问权限及是否懒加载等。
@ToString:作用于类,覆盖默认的toString()方法,可以通过of属性限定显示某些字段,通过exclude属性排除某些字段。
@EqualsAndHashCode:作用于类,覆盖默认的equals和hashCode
@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor:作用于类上,用于生成构造函数。有staticName、access等属性。
@AllArgsConstructor:生成全参构造器
@Data:作用于类上,是以下注解的集合:@ToString @EqualsAndHashCode @Getter @Setter @RequiredArgsConstructor
在pom.xml文件中输入:
右键 -> Genreate -> Dependency -> 搜索要插入的插件jar包名称
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cgb2106boot02artifactId>
<groupId>cn.tedugroupId>
<version>0.0.1-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>day16artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.20version>
dependency>
dependencies>
project>
package cn.tedu.pojo;
public class Car {
private Integer id;
private String name;
private String type;
private String color;
private Double price;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"id=" + id +
", name='" + name + '\'' +
", type='" + type + '\'' +
", color='" + color + '\'' +
", price=" + price +
'}';
}
}
使用lombok修改后的实体类:
package cn.tedu.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//使用lombok
@Data //自动生成get set toString hashCode equals
@NoArgsConstructor //自动生成无参构造
@AllArgsConstructor //自动生成全参构造
@Accessors(chain = true) //开启链式编程
public class Student {
private Integer id;
private String name;
private String sex;
}
package cn.tedu.controller;
import cn.tedu.pojo.Car;
import org.junit.Test;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("car")
public class Test {
//访问http://localhost:8080/car/find,
//在浏览器展示了{"id":100,"name":"保时捷","type":"Cayman T","color":"红色","price":641000.0}
@RequestMapping("get")
public Object get(){
Car c = new Car();
c.setId(100);
c.setName("保时捷");
c.setType("Cayman T");
c.setColor("红色");
c.setPrice(641000.00);
return c;
}
//单元测试(之前的写法)
@Test
public void get1(){
Car c = new Car();
// c.setId(new Integer(100));//写全是这样的
c.setId(100);//自动装箱
c.setName("保时捷");
c.setType("Cayman T");
c.setColor("红色");
c.setPrice(641000.00);
System.out.println(c);
}
//测试lombok
@Test
public void get2(){
Student s = new Student();
Student s2 = new Student(100,"泡泡老师","女");
// s.setId(100);
// s.setName("泡泡老师");
// s.setSex("女");
System.out.println(s2);
//对set()可以使用lombok的链式编程
s.setId(100).setName("泡泡老师").setSex("女");
System.out.println(s.getId()+s.getName()+s.getSex());
}
}
package com.jt.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
//标准的写法
//知识点:为什么需要添加无参构造???
// 利用反射机制实例化对象时,默认调用无参构造
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class User implements Serializable {
private Integer id;
private String name;
//链式加载的底层原理,重写了set方法,返回User对象
public User setId(Integer id){
this.id = id;
return this; // 代表当前对象!!!
}
public User setName(String name){
this.name = name;
return this;
}
}
问题: lombok使用需要提前安装lombok插件!!, 如果项目发布在Linux系统中.问: 系统是否需要提前安装插件?
答案: 不要,因为lombok插件只在编译期有效!!!
xxx.java文件编译为xxx.class文件.
知识衍生: 写的是java源码, 运行的是编译后的.class
每次修改代码修改之后,需要手动的重启服务器。在开发阶段 能否有一种高效的机制。 每次修改代码之后程序自动重启
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<version>2.5.3version>
dependency>
触发机制::程序的代码被修改了之后需要重启,需要配置IDEA自动的编译代码触发修改的状态。
组合键: ctrl + shift + alt + / 或者 ctrl + alt + a