java学习笔记,包括Spring、Maven、数据库连接池等。
Spring微服务方面的知识见之前的文章《Spring微服务实战(第2版)》读书笔记 。
推荐:
大部分内容来自 Java 全栈知识体系-著作权归@pdai所有。
JavaGuide-著作权归JavaGuide(javaguide.cn)所有 。
Java研发自测 https://java-self-testing.github.io/java-self-testing-book/ 。
类图推荐使用 plantuml 绘制,更多语法及使用请参考: http://plantuml.com/ 。
开发配置注意事项
Spring Boot 官方不再支持 Spring Boot 的 2.x 版本了,之后全力维护 3.x;而 Spring Boot 3.x 对 JDK 版本的最低要求是 17。
把Server URL替换成阿里云的镜像 https://start.aliyun.com 就可以选择Java8 来构建SpringBoot项目了。
随着IDEA和Maven不断更新迭代,确保两者的兼容性是开发者不可忽视的任务。开发者在选择IDE和Maven版本时,需要考虑项目需求和现有环境,优先保证版本间的最大兼容性。对于维护老旧项目的开发者,推荐使用Maven 3.6.3及其兼容的IDE版本。
Java基础
java的开发源码格式
Java的源代码通常保存在扩展名为.java的文件中。这些文件包含了Java类的定义,每个文件通常包含一个公共类或接口。当编译Java源代码时,Java编译器(javac)会生成扩展名为.class的字节码文件。这些文件包含了可以被Java虚拟机(JVM)执行的字节码。
如果有多个.class文件或者其他资源(如图片、音频、配置文件等),可以将它们打包到一个JAR(Java Archive)文件中,以便于分发和部署。JAR文件是一个包含了多个.class文件和其他资源的压缩文件,它可以被JVM直接执行。
对于Web应用程序,可以使用WAR(Web Application Archive)文件格式。WAR文件是一种特殊类型的JAR文件,用于打包Java Web应用程序的所有组件,包括Servlets、JSP、HTML、CSS、JavaScript、图像文件、库文件等。
java的war和jar区别:
java的war和jar包都是源代码编译后的产物。
• 文件类型:WAR文件是Web Application Archive的缩写,主要用于分发和部署Web应用程序。
JAR文件是Java Archive的缩写,主要用于分发Java类库和应用程序。
• 内容:WAR文件通常包含JSP、HTML、JavaScript等Web资源文件,以及Java类、库等。JAR文件主要包含Java类和库。
• 使用场景:WAR文件主要用于Web服务器或应用服务器,如Tomcat、Jetty、WebLogic等。JAR文件可以在任何安装了Java环境的地方运行。
• 结构:WAR文件有特定的目录结构,如WEB-INF目录用于存放配置文件和类文件等。JAR文件没有特定的目录结构,通常按照开发者的需要来组织。
• 打包工具:WAR文件通常使用Ant、Maven等工具进行打包。JAR文件可以使用Java自带的jar命令进行打包。
• 运行方式:WAR文件需要部署到服务器上才能运行。JAR文件可以直接通过java -jar命令运行。
注解
注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。它是框架学习和设计者必须掌握的基础。
它主要的作用有以下四方面:
- 生成文档,通过代码里标识的元数据生成javadoc文档。
- 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
- 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
- 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例。
注解的常见分类:
- Java自带的标准注解
包括@Override、@Deprecated和@SuppressWarnings
,分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告,用这些注解标明后编译器就会进行检查。 - 元注解
元注解是用于定义注解的注解,包括@Retention、@Target、@Inherited、@Documented,@Retention
用于标明注解被保留的阶段,@Target
用于标明注解使用的范围(即:被修饰的注解可以用在什么地方),@Inherited
用于标明注解可继承,@Documented
用于标明是否生成javadoc文档。 - 自定义注解,可以根据自己的需求定义注解,并可用元注解对自定义注解进行注解。
反射
每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。类加载相当于 Class 对象的加载。类在第一次使用时才动态加载到 JVM 中,可以使用Class.forName("com.mysql.jdbc.Driver")
这种方式来控制类的加载,该方法会返回一个 Class 对象。反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
在Java中,Class类与java.lang.reflect类库一起对反射技术进行了全力的支持。在反射包中,我们常用的类主要有Constructor类表示的是Class 对象所表示的类的构造方法,利用它可以在运行时动态创建对象、Field表示Class对象所表示的类的成员变量,通过它可以在运行时动态修改成员变量的属性值(包含private)、Method表示Class对象所表示的类的成员方法,通过它可以动态调用对象的方法(包含private)。
反射机制详解。异常
Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: Error 和 Exception。其中 Error 用来表示 JVM 无法处理的错误,Exception 分为两种:
受检异常 : 需要用 try…catch… 语句捕获并进行处理,并且可以从异常中恢复;
非受检异常 : 是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。
异常关键字
try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。
finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
throw – 用于抛出异常。
throws – 用在方法签名中,用于声明该方法可能抛出的异常。JVM
JVM相关知识体系详解
JVM,全称Java Virtual Machine(Java虚拟机),是一个运行在计算机上的程序,其核心任务是运行Java字节码文件。 JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。同时,JVM还具备动态优化功能,可以根据实际运行情况进行调整和优化。
Java代码间接翻译成字节码,储存字节码的文件再交由运行于不同平台上的JVM虚拟机去读取执行,从而实现一次编写,到处运行的目的。类加载
类加载的过程包括了加载(查找并加载类的二进制数据)、验证(确保被加载的类的正确性)、准备(为类的静态变量分配内存,并将其初始化为默认值,这些内存都将在方法区中分配。)、解析(把类中的符号引用转换为直接引用)、初始化(为类的静态变量赋予正确的初始值)五个阶段。在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也成为动态绑定或晚期绑定)。另外注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。内存结构
Java 虚拟机定义了若干种程序运行期间会使用到的运行时数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机退出而销毁。另外一些则是与线程一一对应的,这些与线程一一对应的数据区域会随着线程开始和结束而创建和销毁。线程私有:程序计数器、虚拟机栈、本地方法区;线程共享:堆、方法区, 堆外内存(Java7的永久代或JDK8的元空间、代码缓存)。 - 程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。
在 Hotspot JVM 中,直接将本地方法栈和虚拟机栈合二为一 - 虚拟机栈(Java Virtual Machine Stacks)主管 Java 程序的运行,它保存方法的局部变量、部分结果,并参与方法的调用和返回。
- 本地方法栈用于管理本地方法的调用。
栈是运行时的单位,而堆是存储的单位。栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放、放在哪。
- 堆内存
此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数据都在这里分配内存。
为了进行高效的垃圾回收,虚拟机把堆内存逻辑上划分成三块区域(分代的唯一理由就是优化 GC 性能):
- 新生带(年轻代):
新对象和没达到一定年龄的对象都在新生代。
当填充年轻代时,执行垃圾收集。这种垃圾收集称为 Minor GC。年轻一代被分为三个部分——伊甸园(Eden Memory)和两个幸存区(Survivor Memory,被称为from/to或s0/s1),默认比例是8:1:1。 - 老年代(养老区):
被长时间使用的对象,老年代的内存空间应该要比年轻代更大。通常,垃圾收集是在老年代内存满时执行的。老年代垃圾收集称为 主GC(Major GC),通常需要更长的时间。 - 元空间(JDK1.8 之前叫永久代):
像一些方法中的操作临时对象等,JDK1.8 之前是占用 JVM 内存,JDK1.8 之后直接使用物理内存。
设置堆内存大小和 OOM:
Java 堆用于存储 Java 对象实例,那么堆的大小在 JVM 启动的时候就确定了,我们可以通过 -Xmx 和 -Xms 来设定-Xms
用来表示堆的起始内存,等价于 -XX:InitialHeapSize
。-Xmx
用来表示堆的最大内存,等价于 -XX:MaxHeapSize
。
如果堆的内存大小超过 -Xmx 设定的最大内存, 就会抛出 OutOfMemoryError 异常。我们通常会将 -Xmx 和 -Xms 两个参数配置为相同的值,其目的是为了能够在垃圾回收机制清理完堆区后不再需要重新分隔计算堆的大小,从而提高性能。
- 方法区:用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。
GC 垃圾回收
垃圾收集主要是针对堆和方法区进行;程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收。
现在的商业虚拟机采用分代收集算法,它根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。一般将堆分为新生代和老年代。新生代使用: 复制算法;老年代使用: 标记 - 清除 或者 标记 - 整理 算法。
CMS 收集器:CMS(Concurrent Mark Sweep),Mark Sweep 指的是标记 - 清除算法。
G1 收集器:G1(Garbage-First),它是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器。G1最大的特点是引入分区的思路,弱化了分代的概念,合理利用垃圾收集各个周期的资源,解决了其他收集器甚至CMS的众多缺陷。Spring
Spring 框架的特性: - 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API。
- 控制反转:IOC——Inversion of Control,指的是将对象的创建权交给 Spring 去创建。使用 Spring 之前,对象的创建都是由我们自己在代码中new创建。而使用 Spring 之后。对象的创建都是给了 Spring 框架。
- 依赖注入:DI——Dependency Injection,是指依赖的对象不需要手动调用 setXX 方法去设置,而是通过配置赋值。
- 面向切面编程:Aspect Oriented Programming——AOP。
- 容器:Spring 是一个容器,因为它包含并且管理应用对象的生命周期。
- 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
- 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上 Spring 自身也提供了表现层的 SpringMVC 和持久层的 Spring JDBC)。
Spring 的核心容器是其他模块建立的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 SpEL 表达式语言模块组成,没有这些核心容器,也不可能有 AOP、Web 等上层的功能。具体介绍如下。
- Beans 模块:提供了框架的基础部分,包括控制反转和依赖注入。
- Core 核心模块:封装了 Spring 框架的底层部分,包括资源访问、类型转换及一些常用工具类。
- Context 上下文模块:建立在 Core 和 Beans 模块的基础之上,集成 Beans 模块功能并添加资源绑定、数据验证、国际化、Java EE 支持、容器生命周期、事件传播等。 ApplicationContext 接口是上下文模块的焦点。
- SpEL 模块:提供了强大的表达式语言支持,支持访问和修改属性值,方法调用,支持访问及修改数组、容器和索引器,命名变量,支持算数和逻辑运算,支持从 Spring 容器获取 Bean,它也支持列表投影、选择和一般的列表聚合等。
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
控制反转是通过依赖注入实现的,其实它们是同一个概念的不同角度描述。通俗来说就是IoC是设计思想,DI是实现方式。
DI—Dependency Injection,即依赖注入:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。
简单来说,Bean 代指的就是那些被 IoC 容器所管理的对象。我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。目前Ioc 配置的主流方式是 注解 + Java 配置。
注解配置:通过在类上加注解的方式,来声明一个类交给Spring管理,Spring会自动扫描带有@Component,@Controller,@Service,@Repository
这四个注解的类,然后帮我们创建并管理,前提是需要先配置Spring的注解扫描器。
常用的注入方式主要有三种:构造方法注入(Construct注入),setter注入,基于注解的注入(接口注入)。
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程。
Spring将AOP的思想引入框架之中,通过预编译方式和运行期间动态代理实现程序的统一维护的一种技术。OOP面向对象编程,针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程的某个步骤或阶段,以获得逻辑过程的中各部分之间低耦合的隔离效果。这两种设计思想在目标上有着本质的差异。
AOP通过预编译方式和运行期间动态代理实现程序功能的统一维护,适合于封装横切关注点,具体可以在以下场景中使用:
● 性能监测:通过AOP可以记录方法运行时间,监控性能,帮助定位系统瓶颈和优化点。
● 访问控制:实现权限控制,确保只有经过授权的用户才能访问特定资源。
● 事务管理:在调用方法前开启事务,调用方法后提交关闭事务,支持声明式事务处理。
● 缓存优化:通过缓存查询结果,减少对数据库的访问,提高系统性能。
● 对象池管理:管理对象池,如连接池、线程池等,优化资源使用。
● 日志记录:记录跟踪、优化、校准、性能优化、持久化、资源池、同步等操作的信息,便于问题追踪和系统分析。
Spring Boot
Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的EnterpriseJavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的Java对象(Plain Old Java Object,POJO)实现了EJB的功能。
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。所以SpringBoot的设计策略是通过开箱即用和约定大于配置 来解决配置重的问题的。
mybatis-plus
https://github.com/baomidou/mybatis-plus
官方网站:https://baomidou.com/
MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
H2是一个用Java开发的嵌入式数据库,它本身只是一个类库,可以直接嵌入到应用项目中。
H2 Database Engine 官方网站,H2最大的用途在于可以同应用程序打包在一起发布,这样可以非常方便地存储少量结构化数据。它的另一个用途是用于单元测试。启动速度快,而且可以关闭持久化功能,每一个用例执行完随即还原到初始状态。H2的第三个用处是作为缓存,作为NoSQL的一个补充。
JDBC(JavaDataBase Connectivity)是用Java语言向数据库发送SQL语句。
对象关系映射(Object Relational Mapping,简称ORM), 简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将java程序中的对象自动持久化到关系数据库中。
Spring Data JPA 是 Spring Data 项目的一部分,它提供了一种简化的数据访问方式,用于与关系型数据库进行交互。它基于 Java Persistence API(JPA) 标准,并提供了一套简洁的 API 和注解,使开发人员能够通过简单的 Java 对象来表示数据库表,并通过自动生成的 SQL 语句执行常见的 CRUD 操作。Spring Data JPA 通过封装 JPA 的复杂性,简化了数据访问层的开发工作,使开发人员能够更专注于业务逻辑的实现。它还提供了丰富的查询方法的定义、分页和排序支持、事务管理等功能,使开发人员能够更方便地进行数据访问和操作。
热部署和热加载是在应用正在运行的时候,自动更新(重新加载或者替换class等)应用的一种能力。(PS:spring-boot-devtools提供的方案也是要重启的,只是无需手动重启能实现自动加载而已。)
默认情况下,改变资源/META-INF/maven,/META-INF/resources,/resources,/static,/public,或/templates不触发重新启动,但确会触发现场重装。如果要自定义这些排除项,可以使用该spring.devtools.restart.exclude属性。
Spring Boot 常用注解:
参数校验:Java开发者在Java API规范 (JSR303) 定义了Bean校验的标准validation-api,但没有提供实现。hibernate validation是对这个规范的实现,并增加了校验注解如@Email、@Length等。Spring Validation是对hibernate validation的二次封装,用于支持spring mvc参数自动校验。
OpenAPI 规范(OAS)定义了一个标准的、语言无关的 RESTful API 接口规范,它可以同时允许开发人员和操作系统查看并理解某个服务的功能,而无需访问源代码,文档或网络流量检查(既方便人类学习和阅读,也方便机器阅读)。
Swagger3完全遵循了 OpenAPI 规范。Swagger 官网地址:https://swagger.io/ 。Knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案, 前身是swagger-bootstrap-ui,取名kni4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍!
目前使用Java生成接口文档的最佳实现: SwaggerV3(OpenAPI)+ Knife4J。
当我们讨论接口的幂等性时一般是在说:以相同的请求调用这个接口一次和调用这个接口多次,对系统产生的影响是相同的。如果一个接口满足这个特性,那么我们就说这个 接口是一个幂等接口。
常见的保证幂等的方式:
(1)数据库层面
● 悲观锁(这种方式很少被使用,如果大量线程由于获取for update锁处于等待状态,不利于系统并发操作。)
● 唯一ID/索引 :针对的是插入操作。
● 乐观锁(基于版本号或者时间戳):针对更新操作。
(2)分布式锁
分布式锁的key必须为业务的唯一标识,通常用redis分布式锁或者zookeeper来实现分布式锁。
(3)token机制
适用操作:插入操作、更新操作、删除操作;需要生成全局唯一 Token 串;需要使用第三方组件 Redis 进行数据效验。
应用部署
通过Maven打包成jar,最为常用,因为可以脚本化,这是所有自动化部署的前提。在pom中使用SpringBoot的build插件spring-boot-maven-plugin
。
使用第三方JAR包: 方案一:安装到Maven仓库(比如私服Maven仓库); 方案二:使用systemPath属性。调用spring-boot-maven-plugin,执行repackage把tomcat和resource,lib等合成一个新的jar。想要将系统jar打进去,必须配置includeSystemScope。最终会将lib放入BOOT-INF\lib。
docker镜像打包,运行和管理:通过docker-maven-plugin
将springboot应用打包成docker镜像,通过Docker桌面化管理工具或者Idea Docker插件进行管理。
Lombok
Lombok 是一款 Java 工具,通过注解自动处理如 getter/setter、toString、equals 和 hashCode 等常见代码,减少样板代码。安装 Lombok 需要在 IDE(如 IntelliJ IDEA)中添加插件,并在 Maven 或 Gradle 项目中配置依赖。
Lombok 的 @Data 注解是一个综合注解,它包含了 @Getter、@Setter、@ToString、@EqualsAndHashCode 和 @NoArgsConstructor 注解的功能。使用 @Data 注解,我们可以通过一个注解实现多个功能。
Maven
Maven是一个Java项目的管理和构建工具。
了解不同版本的IDEA对Maven的兼容性,对于确保开发环境的稳定运行和项目按期交付是非常重要的。注意:针对一些老项目 maven还是尽量采用 3.6.3版本,针对idea各个版本的兼容性就很兼容。
查看maven与jdk版本对应关系 https://maven.apache.org/docs/history.html 。
Maven 仓库有三种类型:
- 本地(local)
默认情况下,不管Linux还是 Windows,每个用户在自己的用户目录下都有一个路径名为 .m2/repository/ 的仓库目录。
移动.m2目录到其他磁盘:在maven环境变量所在目录, 修改\conf\settings.xml文件的<localRepository>F:\maven-repository</localRepository>
,中间内容替换成自己本地仓库的地址。本地仓库位置的配置改变之后,还需要对IDE进行配置。 - 中央(central)
Maven维护了一个中央仓库(repo1.maven.org),所有第三方库将自身的jar以及相关信息上传至中央仓库,Maven就可以从中央仓库把所需依赖下载到本地。如果访问Maven的中央仓库非常慢,我们可以选择一个速度较快的Maven的镜像仓库。阿里云Maven中央仓库 https://developer.aliyun.com/mvn/guide 。
settings.xml配置文件,在标签中添加 mirror 子节点: 123456<mirror><id>aliyunmaven</id><mirrorOf>*</mirrorOf><name>阿里云公共仓库</name><url>https://maven.aliyun.com/repository/public</url></mirror>
如果我们要引用一个第三方组件,比如okhttp,如何确切地获得它的groupId、artifactId和version?方法是通过 https://central.sonatype.com/ 搜索关键字,找到对应的组件后,直接复制。
- 远程(remote)
它是开发人员自己定制仓库,包含了所需要的代码库或者其他工程中用到的 jar 文件。
依赖关系:
Maven定义了几种依赖关系,分别是compile、test、runtime和provided:
scope | 说明 | 示例 |
---|---|---|
compile | 编译时需要用到该jar包(默认) | commons-logging |
test | 编译Test时需要用到该jar包 | junit |
runtime | 编译时不需要,但运行时需要用到 | mysql |
provided | 编译时需要用到,但运行时由JDK或某个服务器提供 | servlet-api |
其中,默认的compile是最常用的,Maven会把这种类型的依赖直接放入classpath。
经常用到的phase其实只有几个:
clean:清理 ;
compile:编译 ;
test:运行测试 ;
package:打包 ;
经常使用的命令有:
mvn clean:清理所有生成的class和jar;
mvn clean compile:先清理,再执行到compile;
mvn clean test:先清理,再执行到test,因为执行test前必须执行compile,所以这里不必指定compile;
mvn clean package:先清理,再执行到package。
- Maven 多模块管理
多模块管理简单地来说就是将一个项目分为多个模块,每个模块只负责单一的功能实现。直观的表现就是一个 Maven 项目中不止有一个 pom.xml 文件,会在不同的目录中有多个 pom.xml 文件,进而实现多模块管理。多模块管理除了可以更加便于项目开发和管理,还有如下好处:降低代码之间的耦合性(从类级别的耦合提升到 jar 包级别的耦合);减少重复,提升复用性;每个模块都可以是自解释的(通过模块名或者模块文档);模块还规范了代码边界的划分,开发者很容易通过模块确定自己所负责的内容。多模块管理下,会有一个父模块,其他的都是子模块。父模块通常只有一个 pom.xml,没有其他内容。父模块的 pom.xml 一般只定义了各个依赖的版本号、包含哪些子模块以及插件有哪些。不过,要注意的是,如果依赖只在某个子项目中使用,则可以在子项目的 pom.xml 中直接引入,防止父 pom 的过于臃肿。数据库连接池
在Java中,数据库连接池(Connection Pooling)是一种重要的资源管理技术,它预先创建并维护一组数据库连接,以供应用程序在需要时重用。
- 数据库连接池的作用
提高性能:数据库连接的创建和关闭通常是一个资源密集型的操作,需要消耗大量的时间和系统资源。通过重用预先创建的连接,连接池能够显著减少这种开销,从而提高应用程序的性能。
资源管理:连接池可以帮助管理和控制对数据库的连接数量,防止因为创建过多的连接而耗尽系统资源。它可以根据需要动态地增加或减少连接的数量,以优化资源的使用。
提高可靠性:通过连接池,应用程序可以更有效地处理数据库连接的失败和超时。当连接出现问题时,连接池可以自动尝试重新建立连接,或者从池中提供一个可用的连接,从而保持应用程序的稳定性和可用性。 - 数据库连接池的工作原理
当应用程序需要连接到数据库时,它首先会向连接池请求一个连接。
如果连接池中有可用的连接,它将返回一个给应用程序。
如果连接池中没有可用的连接,它将根据配置创建一个新的连接(如果允许的话)或者等待一个连接被释放。
应用程序使用完连接后,会将连接归还给连接池,而不是直接关闭它。 - Java中常见的数据库连接池
DBCP:Apache提供的数据库连接池,速度相对较快,但因自身存在BUG,某些框架已不再提供支持。
C3P0:一个开源组织提供的一个数据库连接池,速度相对较慢,但稳定性还可以。
Proxool:sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较C3P0差一点。
HikariCP:俗称光连接池,是目前速度最快的连接池。
Druid:阿里提供的数据库连接池,据说是集DBCP、C3P0、Proxool优点于一身的数据库连接池。 - 数据库连接池的配置
数据库连接池的配置通常包括以下几个参数:
maxActive:最大活动连接数,即连接池中同时能使用的最大连接数。
maxIdle:最大空闲连接数,即连接池中空闲的连接数量超过这个数时,将多余的连接释放掉。
maxWait:最大等待时间,即当连接池中没有可用连接时,等待获取连接的最大时间。
driverClassName:数据库驱动类的全限定名。
url:要连接的数据库的URL。
username 和 password:连接数据库的用户名和密码。