框架
Spring 的核心
依赖注入(DI)是 Spring 最核心的技术点(我们在后面还会继续运用到),Spring 所有的技术方案都是基于 DI来发展的。对于初学者来说理解 DI 会有一些困难,所以我们先要做到的是熟练使用 Spring 的技能。
使用 Spring 大大降低了开发难度和助力团队开发,它更加强调了面向对象,所以等大家习惯了之后你会发现就像喝开水一样简单。
在正式学习之前,希望大家先有一个概念:先熟练掌握它的要求,再去理解它的底层原理,要不然这门课会让你崩溃的哦,所谓实践出真知
Maven入门
Maven是一个项目管理和构建自动化工具。是java必须需要掌握的工具
Maven 提供了一个命令行工具可以把工程打包成 Java 支持的格式(比如 jar),并且支持部署到中央仓库里,这样使用者只需要通过工具就可以很快捷的运用其他人写的代码,只需要你添加依赖即可
从这个架构里可以看到借助于中央仓库,我们可以把 Java 代码任意的共享给别人,这对于团队协同开发来说是至关重要的,可以说Java工程化发展到现在,Maven起到了决定性的作用。
Maven 使用惯例优于配置的原则。它要求在没有定制之前,所有的项目都有如下的结构(其实我们之前的 Java 工程目录都是遵循了这个结构规范)
目录 | 目的 |
---|---|
${basedir} | 存放 pom.xml 和所有的子目录 |
${basedir}/pom.xml | Maven 的项目配置文件 |
${basedir}/src/main/java | 项目的 java 源代码 |
${basedir}/src/main/resources | 项目的资源,比如 property 文件 |
${basedir}/src/test/java | 项目的测试类,比如 JUnit 代码 |
${basedir}/src/test/resources | 测试使用的资源 |
这里的 ${basedir}
代表的是 Java 工程的根路径,在我们这里就是工程的根目录啦。一个 Maven 项目在默认情况下会产生 JAR (Java的一种压缩格式)文件,另外,编译后的 classes 会放在${basedir}/target/classes
下面,JAR 文件会放在 ${basedir}/target
下面。
所以如果你的代码放错位置,那么程序是没办法完成编译、执行工作的。
Maven安装
Maven官网下载
Maven命令
使用Maven要在命令行(终端软件)里输入指令的方式来执行,注意命令要在工程的根目录下执行
mvn clean compile
编译命令,Maven 会自动扫描 src/main/java 下的代码并完成编译工作,执行完,会在根目录下生成 target/classes 目录(存放所有的class)
mvn clean package
编译并打包命令,这个命令是 compile 和 package 的集合,也就是说会先执行 compile 命令,然后在执行 jar 打包命令,这个的结果会把所有的 java 文件和资源打包成一个 jar
,jar 是 java 的一个压缩格式,方便我们灵活的运用多个代码
mvn clean install
执行安装命令,这个命令是 compile 和 package、install 的集合也就是说会先执行 compile 命令,然后在执行 jar 打包命令,然后执行 install 命令安装到本地的 Maven 仓库目录里,这个目录是${user home}/.m2
这个 ${user_home}
指的就是你的电脑登录用户名的个人目录
如果大家在本地完成了 Maven 的安装,大家可以打开我的电脑进
行看一看的
这个操作基本和 mvn clean package
看起来一样,所以我就不演示
mvn compile exec:java -Dexec.mainClass=${main}
这个命令的意思是在 compile 执行完后,执行运行 Java 的命令,具体执行哪个 Java 类是由 -Dexec.mainclass=${main}
参数指定的比如我们想执行 com.first.Test
类,那么这个完整的命令就是
mvn compile exec:java -Dexec.mainClass=com.first.Test
Maven的核心概念
Maven的配置文件是一个强约定的XML格式文件,文件名为pom.xml
重点掌握 pom.xml 的格式规范
POM(Project Object Model)
一个 Java 项目所有的配置都放置在 POM 文件中,大概有如下的行为:
-
定义项目的类型、名字
-
管理依赖关系
-
定制插件的
-
- Maven坐标
-
- Maven工程属性
-
- Maven依赖
-
- Maven插件
补充介绍一下,XML 格式可能是第一次接触,其实 HTML 语言也是 XML 格式,不过 XML 格式会严格遵守 标记语言 的要求,那就是有开始标签和结束标签,比如1.0,我们在自定义 pom.xml 时候,如果没有写完整的开始、结束标签那就会出错,这里我们就不详细展开了,大家多看几次也就了解啦。
- Maven 坐标
<groupId>com.oageux.course</groupId>
<artifactId>app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
这四个标签组成了 Maven 的坐标,所谓坐标就是一种位置信息Maven 的坐标决定了这个 Maven 工程部署后存在 Maven 仓库的文件位置,所以这个坐标信息是必须要指定的。
groupld
groupld 就像一个文件夹一样,它的命名和 Java 的包比较一致,这里一般只用小写的英文字母和字符.
,比如这里的com.oageux.course
。一般来说一个公司会设置自己的 groupld 避免和其他公司重合,个人开发者也一样。
artifactld
artifactld 有点像文件名一样,在一个 groupld 内,它应该是唯一的你不能使用中文或者特殊字符,从规范上来说只能使用小写的英文字母、.
、-
、_
。比如:app、member.shared
这些都可以
packaging
Maven 工程执行完后会把整个工程打包成 packaging
指定的文件格式,默认情况下 packaging
的值是 jar
,所以如果 pom.xml
文件中没有声明这个标签,那就是 jar
packaging 有如下的几种格式
- jar
- war
- ear
- pom
多数情况下,我们使用的都是 jar
,其他格式我们在以后会逐步运用到,这里有个概念就好。
version
version 很有意思的,它基本上遵守了软件工程中对版本号的约定。
在 Maven 的世界里,会把一个工程分为两个状态,这也是软件工程里最最常用的规范。
- SNAPSHOT 这个单词翻译过来的意思是快照,实际上代表了当前程序还处于不稳定的阶段,随时可以再修改,所以在我们开发的时候我们会在版本号后面加上
SNAPSHOT
关键字 - RELEASE RELEASE 和 SNAPSHOT 是对立面的,所以它代表的就是稳定,一般我们正式发布的时候,都会把 version 改为
RELEASE
当然你可以不用特意的加上RELEASE
,因为只要不是SNAPSHOP
,那就是RELEASE
了解了工程状态之后,我们再来看版本号的约定,在软件工程里,我们一般会用三位数字来表示版本号,所以大概是 x.x.x
这样的格式,
比如说 iPhone 11 搭配的操作系统的版本是 ios 13.1.2
, 正如你所想的这是一个 RELEASE 版本
三位版本号如何使用呢,也是有规则的
-
第一位代表的是主版本号
主版本号一般是团队约定来的,上面iOS 的例子中 13 就是主版本号
-
第二位代表的是新增功能
上面例子中的 1代表的就是新增功能后的版本,这个数字表明苹果在 13 这个版本里,有了1次新增功能的行为
-
第三位代表的是 bugfix 后的版本
bugfix 是修复代码缺陷、bug 的行为,我们知道苹果经常会发布一些版本用于修复问题,所以上面的
2
就是 bugfix 版本从这个数字你可以大致判断,苹果做了两次 bug 修复
有些时候,我们也有可能用两位版本,那就是没有第一位的主版本号(因为某些时候,可能不需要主版本号)具体选用哪种根据团队的情况来做选择,不用纠结,大家约定好就行。
在编程过程中约定大于一切,请大家记住这句话
软件的版本大多时候是从 1.0.0
开始的,在开发状态下那就是``1.0.0-SNAPSHOT ,请注意这个格式
[version]-SNAPSHOT` ,不能写错啦。
如果你新增了功能,那么版本号就是 1.1.0-SNAPSHOT
,如果你要正式发布了,那么版本号就是 1.1.0
一般来说每一位的最大值就是100,所以不要超过100哦,印象中只有微软操作系统的版本号才超大,因为它的历史悠久功能特别特别多。。。
最后还有一个约定,那就是执行
mvn package、mvn install
命令生成的 jar 文件名是[artifactId]-[version].jar
,你可以看看刚才的演示执行的 target 目录下是不是有一个
app-1.0.0-SNAPSHOT.jar
文件
- Maven 属性配置
Maven 的属性配置是用来做参数设置的,如
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
首先它的格式是在 properties 标签内,这个是固定的格式。properties内的标签可以自定义,但是一般来说只能是小写英文字母+.
当然默认也有一些公共参数是可以调整的,比如我们这里的
-
java.version
代表设置一个参数:
java.version
它的值是1.8
-
maven.compiler.source
这个参数是指定 Maven 编译时候源代码的 JDK 版本
${java.version}
这个值有点特殊,它是一个动态值,${key}
这个语法会动态找到key
这个参数配置的值,所以上面的例子中${java.version}
的实际值是1.8
-
project.build.sourceEncoding
这个参数指定的是工程代码源文件的文件编码格式,一般情况下我们都设置成
UTF-8
-
maven.compiler.target
这个参数作用是是按照这个值来进行编译源代码,比如这里的例子是按照 JDK1.8 进行编译
依赖管理 dependencies
dependency 就是用于指定当前工程依赖其他代码库的,Maven 会自动管理 jar 依赖
一旦我们在 pom.xml 里声明了 dependency 信息,会先去本地用户目录下的
.m2
文件夹内查找对应的文件,如果没有找到那么就会触发从中央仓库下载行为,下载完会存在本地的.m2
文件夹内
添加fastjson库:
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
请注意,一个 pom.xml 只能存在一个 dependencies 标签,可以有多个 dependency,因为我们很有可能依赖多个库
大家仔细观察这个 dependency
标签,你会发现dependency
标签的内容其实就是 Maven 坐标,所以说只要有坐标我们就可以建立依赖
也可以继续添加okhttp3依赖
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.2.2</version>
</dependency>
</dependencies>
一般我们会把别人写的代码库称为三方库,自己、团队写的称为二方库,这个概念请大家记住,以后我们可能会这样描述内容的
中央仓库
https://central.sonatype.com/?smo=true
间接依赖
间接依赖是 mvn 成功的核心要素,简单的来说,如果一个 remote
程依赖了 okhttp 库,而当前工程 locale
依赖了 remote
工程,这个时候 locale
工程也会自动依赖了 okhttp ,省时又省力是不是?
插件体系 plugins
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
这里声明了一个maven-compiler-plugin
插件用于执行maven compile 的,你会发现 maven 的插件其实也是存放在中央仓库的坐标,也就是一切都是 jar。
关于插件,大家能够看懂,知道如何添加即可,不用特别纠结概念,因为不同的插件是配合不同的工程来设定的。现在深究它意义不大等我们用了很多工程的时候,自然会明白了。
配置并创建Spring工程
目前大部分使用的是Spring6
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.9.RELEASE</version>
</dependency>
Spring强调的是面向接口编程,基本都有接口和实现类
package com.gongsiming;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import com.youkeda.service.MessageService;
/**
* Application
*/
@ComponentScan
public class Application {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
MessageService msgService = context.getBean(MessageService.class);
System.out.println(msgService.getMessage());
}
}
初学的时候要先学会用再去理解
上面使用了以下知识点:
- 注解
- Spring Bean
- Spring 扫描
- Spring 生命周期
Spring开发和java开发不同点
看一下上图,实际上我们的演示的例子的工作就是调用了Messageservice
实例的getMessage()
方法。
看起来好像是 Java 方式更简单点,但是大家仔细对比一下,你会发现在 Spring 当中,我们如果想调用 Messageservice
就可以直接从上下文获取,而不需要关心它的实现类、如何实例化实现类,从而达到了真正的解耦合。
这就像我们日常中使用 U 盘,只要确定它是 USB 接口就知道可以用,管它到达是谁、怎么生产来的。
当代码越多的时候,Spring 的效率就会体现出来,比原生 Java 开发要快很多倍
所以这也是 Spring 最大的价值,完全的屏蔽了实现细节,让使用者可以专注与接口的定义和运用即可,这对多人协同开发的时候非常非常有用,屏蔽实现方式也就意味着降低了工程的复杂度,因为只要开发双方约定好接口就可以一起工作,而不是你在这里写一段代码,我在那里写一段代码,完全乱套了。
往后学习到 Spring 更多知识和大型项目的时候,大家会对这个概念有更深的理解,现在不理解也没关系。