Maven 的配置文件是一个强约定的XML 格式文件,它的文件名一定是pom.xml
。
1、POM (Project Object Model)
一个 Java 项目所有的配置都放置在 POM 文件中,大概有如下的行为:
- 定义项目的类型、名字
- 管理依赖关系
- 定制插件的
1.maven坐标
<groupId></groupId>
<artifactId>app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
这四个标签组成了 Maven 的坐标,所谓坐标就是一种位置信息,Maven 的坐标决定了这个 Maven 工程部署后存在 Maven 仓库的文件位置,所以这个坐标信息是必须要指定的。
groupId
groupId 就像一个文件夹一样,它的命名和 Java 的包比较一致,这里一般只用小写的英文字母和字符。
artifactId
artifactId 有点像文件名一样,在一个 groupId 内,它应该是唯一的,你不能使用中文或者特殊字符,从规范上来说只能使用小写的英文字母、.
、-
、_
。比如:app、member.shared
这些都可以
packaging
Maven 工程执行完后会把整个工程打包成packaging
指定的文件格式,默认情况下packaging
的值是jar
,所以如果pom.xml
文件中没有声明这个标签,那就是 jar
packaging 有如下的几种格式
- jar
- war
- ear
- pom
多数情况下,我们使用的都是jar。
version
-
SNAPSHOT 这个单词翻译过来的意思是快照,实际上代表了当前程序还处于不稳定的阶段,随时可以再修改,所以在我们开发的时候我们会在版本号后面加上
SNAPSHOT
关键字 -
RELEASE RELEASE 和 SNAPSHOT 是对立面的,所以它代表的就是稳定,一般我们正式发布的时候,都会把 version 改为
RELEASE
。当然你可以不用特意的加上RELEASE
,因为只要不是SNAPSHOT
,那就是RELEASE
在软件工程里,我们一般会用三位数字来表示版本号,所以大概是x.x.x
这样的格式,比如说 iPhone 11 搭配的操作系统的版本是iOS 13.1.2
,正如你所想的这是一个 RELEASE 版本 。
-
第一位代表的是主版本号
主版本号一般是团队约定来的,上面 iOS 的例子中
13
就是主版本号 -
第二位代表的是新增功能
上面例子中的
1
代表的就是新增功能后的版本,这个数字表明苹果在 13 这个版本里,有了 1 次新增功能的行为 -
第三位代表的是 bugfix 后的版本
bugfix 是修复代码缺陷、bug 的行为,我们知道苹果经常会发布一些版本用于修复问题,所以上面的
2
就是 bugfix 版本,从这个数字你可以大致判断,苹果做了两次 bug 修复
1.2 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 进行编译
1、依赖管理 dependencies
dependency 就是用于指定当前工程依赖其他代码库的,Maven 会自动管理 jar 依赖
一旦我们在 pom.xml 里声明了 dependency 信息,会先去本地用户目录下的
.m2
文件夹内查找对应的文件,如果没有找到那么就会触发从中央仓库下载行为,下载完会存在本地的.m2
文件夹内
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
一个 pom.xml 只能存在一个 dependencies 标签,可以有多个 dependency,因为我们很有可能依赖多个库。
1.1中央仓库
Maven 会把所有的 jar 都存放在中央仓库里,我们可以通过 Maven Central Repository Search 这个网站来搜索 jar。阿里云的镜像服务器 仓库服务https://maven.aliyun.com/mvn/search1.2间接依赖
如果一个remote
工程依赖了 okhttp 库,而当前工程locale
依赖了 remote
工程,这个时候locale
工程也会自动依赖了 okhttp .
2、插件体系 plugins
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
spring工程
spring的maven坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
Spring 强调的是面向接口编程,所以大多数情况下 Spring 代码都会有接口和实现类。
Spring 最大的价值,完全的屏蔽了实现细节,让使用者可以专注与接口的定义和运用即可。
在 Spring 当中,我们如果想调用MessageService
就可以直接从上下文获取,而不需要关心它的实现类、如何实例化实现类,从而达到了真正的解耦合。
Annotation (注解)
Java的注解是一种元数据,可以用来为程序代码添加信息和标记。注解在Java中以@
符号开头,用于提供给编译器、解释器或其他工具一些额外的信息。注解可以用于类、方法、字段等各种程序元素上,以便在运行时或编译时执行特定的操作。
本质上来说 Annotation(注解) 是 Java 推出的一种注释机制,后面我们统一叫 Annotation,和普通的注释有个显著的特别,那就是 Annotation 是可以在编译、运行阶段读取的,注释很明显不可以。
能够在编译、运行阶段读取信息,这就给了我们很多的扩展空间,而且不会污染源代码。我们可以借助它来实现一些增强功能,在 Spring 当中就重度使用了 Annotation,通过运行阶段动态的获取 Annotation 从而完成很多自定义的行为。
从另外一个角度来看,Annotation 也是一个 Java 类,只是这个类太特殊了点。
一个 Annotation 是由多个 Annotation 组合而成的。
1、Target
java.lang.annotation.Target
自身也是一个注解,它只有一个数组属性,**用于设定该注解的目标范围,**比如说可以作用于类或者方法等。因为是数组,所以可以同时设定多个范围
具体可以作用的类型配置在 java.lang.annotation.ElementType
枚举类中。
- ElementType.TYPE 可以作用于 类、接口类、枚举类上
- ElementType.FIELD 可以作用于类的属性上
- ElementType.METHOD 可以作用于类的方法上
- ElementType.PARAMETER 可以作用于类的参数
2、Retention
java.lang.annotation.Retention
自身也是一个注解,它用于声明该注解的生命周期,简单的来说就是在 Java 编译、运行的哪个环节有效,它的值定义在java.lang.annotation.RetentionPolicy
枚举类中,有三个值可以选择.
1.SOURCE: 也就是说是纯注释作用 2.CLASS: 也就是在编译阶段是有效的 3.RUNTIME: 在运行时有效.
@Retention(RetentionPolicy.RUNTIME)
3、Documented
java.lang.annotation.Documented
自身也是一个注解,它的作用是将注解中的元素包含到 JavaDoc 文档中,一般情况下,都会添加这个注解的.
4、@interface
@interface
就是声明当前的 Java 类型是 Annotation,固定语法,就是这样写就好.
5、Annotation 属性
String value() default "";
Annotation 的属性有点像类的属性一样,它约定了属性的类型(这个类型是基础类型:String、boolean、int、long),和属性名称(默认名称是 value ,在引用的时候可以省略),default 代表的是默认值。
静态码
静态代码块的语法比较特殊,不像方法一样,没有参数也没有返回值。只要在类代码中写 static {}
就好,系统加载这个类的时候,会自动执行静态代码块中的代码。
,既然称之为"静态"代码块,就意味着无论这个类被实例化多少次(new Hello()
),静态代码块都只执行一次。
记住:静态代码块要写在类中,而不能写在方法中
Spring Bean
在 Spring 的世界中,所有的 Java 对象都会通过 IoC 容器转变为 Bean(Spring 对象的一种称呼,以后我们都用 Bean 来表示 Java 对象),构成应用程序主干和由 Spring IoC 容器管理的对象称为 beans,beans 和它们之间的依赖关系反映在容器使用的配置元数据中。基本上所有的 Bean 都是由接口+实现类完成的,用户想要获取 Bean 的实例直接从 IoC 容器获取就可以了,不需要关心实现类.
Spring 主要有两种配置元数据的方式,一种是基于 XML、一种是基于 Annotation 方案的,
Annotation 类型的 IoC 容器对应的类是
org.springframework.context.annotation.AnnotationConfigApplicat
AnnotationConfigApplicationContext
这个类的构造函数有两种
- AnnotationConfigApplicationContext(String ... basePackages) 根据包名实例化
- AnnotationConfigApplicationContext(Class clazz) 根据自定义包扫描行为实例化
只要我们在类上引用这类注解,那么都可以被 IoC 容器加载
@Component
注解是通用的 Bean 注解,其余三个注解都是扩展自Component
@Service
正如这个名称一样,代表的是 Service Bean@Controller
作用于 Web Bean@Repository
作用于持久化相关 Bean
实际上这四个注解都可以被 IoC 容器加载,一般情况下,我们使用@Service
;如果是 Web 服务就使用@Controller
加注解的作用,是让 Spring 系统 自动 管理 各种实例。
所谓 管理 ,就是用 @Service
注解把 SubjectServiceImpl
和 SongServiceImpl
等等所有服务实现,都标记成 Spring Bean ;然后,在任何需要使用服务的地方,用 @Autowired
注解标记,告诉 Spring 这里需要注入实现类的实例。
@Service
和 @Autowired
是相辅相成的:如果 SongServiceImpl 没有加 @Service
,就意味着没有标记成 Spring Bean ,那么即使加了 @Autowired
也无法注入实例;而 private SongService songService;
属性忘记加 @Autowired
,Spring Bean 亦无法注入实例。二者缺一不可。
spring resource
Maven 执行 package 的时候,会把resources目录下的文件一起打包进 jar 包里( jar 是 Java 的压缩文件)。
jar文件是从包路径
开始的。Maven 工程编译后,会自动去掉 src/main/java。``src/main/resources
目录.
classpath
在 Java 内部当中,我们一般把文件路径称为 classpath,所以读取内部的文件就是从 classpath 内读取,classpath 指定的文件不能解析成 File 对象,但是可以解析成 InputStream,可以借助 Java IO读取出来了。
classpath 类似虚拟目录,它的根目录是从 /
开始代表的是 src/main/java
或者src/main/resources
目录
读取文件可以使用commons-io库
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
public class Test {
public static void main(String[] args) {
// 读取 classpath 的内容
InputStream in = Test.class.getClassLoader().getResourceAsStream("data.json");
// 使用 commons-io 库读取文本
try {
String content = IOUtils.toString(in, "utf-8");
System.out.println(content);
} catch (IOException e) {
// IOUtils.toString 有可能会抛出异常,需要我们捕获一下
e.printStackTrace();
}
}
}
InputStream in = Test.class.getClassLoader().getResourceAsStream("data.json");
这段代码的含义就是从 Java 运行的类加载器(ClassLoader)实例中查找文件,Test.class
指的当前的 Test.java 编译后的 Java class 文件。
在 Spring 当中定义了一个 org.springframework.core.io.Resource
类来封装文件,这个类的优势在于可以支持普通的 File 也可以支持 classpath 文件。
并且在 Spring 中通过 org.springframework.core.io.ResourceLoader
服务来提供任意文件的读写,你可以在任意的 Spring Bean 中引入 ResourceLoader.
public interface FileService {
String getContent(String name);
}
实现类
import fm.douban.service.FileService;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
@Service
public class FileServiceImpl implements FileService {
@Autowired
private ResourceLoader loader;
@Override
public String getContent(String name) {
try {
InputStream in = loader.getResource(name).getInputStream();
return IOUtils.toString(in,"utf-8");
} catch (IOException e) {
return null;
}
}
}
FileService fileService = context.getBean(FileService.class);
String content = fileService.getContent("classpath:data/urls.txt");
System.out.println(content);
在 Spring Resource 当中,把本地文件、classpath文件、远程文件都封装成 Resource 对象来统一加载。
流程
init方法, 通过注解来声明 init 。
import javax.annotation.PostConstruct;
@Service
public class SubjectServiceImpl implements SubjectService {
@PostConstruct
public void init(){
System.out.println("启动啦");
}
}
方法上添加@PostConstruct
注解,就代表该方法在 Spring Bean 启动后会自动执行。