diff --git a/README.en.md b/README.en.md index b71e85f2..5feadbc3 100644 --- a/README.en.md +++ b/README.en.md @@ -139,7 +139,6 @@ In addition,[GeeksforGeeks]( https://www.geeksforgeeks.org/fundamentals-of-alg 1. [Database Basics Summary](docs/database/数据库基础知识.md) 2. **[MySQL Knowledge Summary](docs/database/mysql/mysql知识点&面试题总结.md)** (Must see:+1:) -4. [One Thousand Lines MySQL Study Notes](docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md) 5. [MySQL High Performance Optimization Specification Recommendations](docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md) **Important knowledge points:** diff --git a/README.md b/README.md index f503d99c..1c798a62 100755 --- a/README.md +++ b/README.md @@ -195,8 +195,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle **知识点/面试题总结:** - **[MySQL知识点总结](./docs/database/mysql/mysql-questions-01.md)** (必看 :+1:) -- [一千行 MySQL 学习笔记](./docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md) -- [MySQL 高性能优化规范建议](./docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md) +- [MySQL 高性能优化规范建议总结](./docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md) **重要知识点:** diff --git a/docs/.vuepress/sidebar.ts b/docs/.vuepress/sidebar.ts index 1a7e7c86..45e522d0 100644 --- a/docs/.vuepress/sidebar.ts +++ b/docs/.vuepress/sidebar.ts @@ -245,7 +245,6 @@ export const sidebarConfig = defineSidebarConfig({ collapsable: true, children: [ "mysql-questions-01", - "a-thousand-lines-of-mysql-study-notes", "mysql-high-performance-optimization-specification-recommendations", { text: "重要知识点", diff --git a/docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md b/docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md index 28ffa499..308c4c72 100644 --- a/docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md +++ b/docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md @@ -11,22 +11,25 @@ tag: ### 基本操作 -```mysql +```sql /* Windows服务 */ --- 启动MySQL - net start mysql +-- 启动 MySQL + net start mysql -- 创建Windows服务 - sc create mysql binPath= mysqld_bin_path(注意:等号与值之间有空格) + sc create mysql binPath= mysqld_bin_path(注意:等号与值之间有空格) /* 连接与断开服务器 */ -mysql -h 地址 -P 端口 -u 用户名 -p 密码 -SHOW PROCESSLIST -- 显示哪些线程正在运行 -SHOW VARIABLES -- 显示系统变量信息 +-- 连接 MySQL + mysql -h 地址 -P 端口 -u 用户名 -p 密码 +-- 显示哪些线程正在运行 + SHOW PROCESSLIST +-- 显示系统变量信息 + SHOW VARIABLES ``` ### 数据库操作 -```mysql -/* 数据库操作 */ ------------------ +```sql +/* 数据库操作 */ -- 查看当前数据库 SELECT DATABASE(); -- 显示当前时间、用户名、数据库版本 @@ -49,7 +52,8 @@ SHOW VARIABLES -- 显示系统变量信息 ### 表的操作 -```mysql +```sql +/* 表的操作 */ -- 创建表 CREATE [TEMPORARY] TABLE[ IF NOT EXISTS] [库名.]表名 ( 表的结构定义 )[ 表选项] 每个字段必须有数据类型 @@ -130,7 +134,7 @@ SHOW VARIABLES -- 显示系统变量信息 ### 数据操作 -```mysql +```sql /* 数据操作 */ ------------------ -- 增 INSERT [INTO] 表名 [(字段列表)] VALUES (值列表)[, (值列表), ...] @@ -152,7 +156,7 @@ SHOW VARIABLES -- 显示系统变量信息 ### 字符集编码 -```mysql +```sql /* 字符集编码 */ ------------------ -- MySQL、数据库、表、字段均可设置编码 -- 数据编码与客户端编码不需一致 @@ -175,7 +179,7 @@ SET NAMES GBK; -- 相当于完成以上三个设置 ### 数据类型(列类型) -```mysql +```sql /* 数据类型(列类型) */ ------------------ 1. 数值类型 -- a. 整型 ---------- @@ -277,7 +281,7 @@ set(val1, val2, val3...) ### 列属性(列约束) -```mysql +```sql /* 列属性(列约束) */ ------------------ 1. PRIMARY 主键 - 能唯一标识记录的字段,可以作为主键。 @@ -332,7 +336,7 @@ set(val1, val2, val3...) ### 建表规范 -```mysql +```sql /* 建表规范 */ ------------------ -- Normal Format, NF - 每个表保存一个实体信息 @@ -351,7 +355,7 @@ set(val1, val2, val3...) ### SELECT -```mysql +```sql /* SELECT */ ------------------ SELECT [ALL|DISTINCT] select_expr FROM -> WHERE -> GROUP BY [合计函数] -> HAVING -> ORDER BY -> LIMIT a. select_expr @@ -416,20 +420,20 @@ h. DISTINCT, ALL 选项 ### UNION -```mysql +```sql /* UNION */ ------------------ - 将多个select查询的结果组合成一个结果集合。 - SELECT ... UNION [ALL|DISTINCT] SELECT ... - 默认 DISTINCT 方式,即所有返回的行都是唯一的 - 建议,对每个SELECT查询加上小括号包裹。 - ORDER BY 排序时,需加上 LIMIT 进行结合。 - 需要各select查询的字段数量一样。 - 每个select查询的字段列表(数量、类型)应一致,因为结果中的字段名以第一条select语句为准。 + 将多个select查询的结果组合成一个结果集合。 + SELECT ... UNION [ALL|DISTINCT] SELECT ... + 默认 DISTINCT 方式,即所有返回的行都是唯一的 + 建议,对每个SELECT查询加上小括号包裹。 + ORDER BY 排序时,需加上 LIMIT 进行结合。 + 需要各select查询的字段数量一样。 + 每个select查询的字段列表(数量、类型)应一致,因为结果中的字段名以第一条select语句为准。 ``` ### 子查询 -```mysql +```sql /* 子查询 */ ------------------ - 子查询需用括号包裹。 -- from型 @@ -463,7 +467,7 @@ h. DISTINCT, ALL 选项 ### 连接查询(join) -```mysql +```sql /* 连接查询(join) */ ------------------ 将多个表的字段进行连接,可以指定连接条件。 -- 内连接(inner join) @@ -492,7 +496,7 @@ select info.id, info.name, info.stu_num, extra_info.hobby, extra_info.sex from i ### TRUNCATE -```mysql +```sql /* TRUNCATE */ ------------------ TRUNCATE [TABLE] tbl_name 清空数据 @@ -506,7 +510,7 @@ TRUNCATE [TABLE] tbl_name ### 备份与还原 -```mysql +```sql /* 备份与还原 */ ------------------ 备份,将数据的结构与表内数据保存起来。 利用 mysqldump 指令完成。 @@ -532,7 +536,7 @@ mysqldump [options] --all--database ### 视图 -```mysql +```sql 什么是视图: 视图是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。但是,视图并不在数据库中以存储的数据值集形式存在。行和列数据来自由定义视图的查询所引用的表,并且在引用视图时动态生成。 视图具有表结构文件,但不存在数据文件。 @@ -566,7 +570,7 @@ CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] VIEW view_name ### 事务(transaction) -```mysql +```sql 事务是指逻辑上的一组操作,组成这组操作的各个单元,要不全成功要不全失败。 - 支持连续SQL的集体成功或集体撤销。 - 事务是数据库在数据完整性方面的一个功能。 diff --git a/docs/database/redis/redis-questions-01.md b/docs/database/redis/redis-questions-01.md index f8496252..34dead45 100644 --- a/docs/database/redis/redis-questions-01.md +++ b/docs/database/redis/redis-questions-01.md @@ -75,17 +75,6 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来 相信看了上面的对比之后,我们已经没有什么理由可以选择使用 Memcached 来作为自己项目的分布式缓存了。 -### 缓存数据的处理流程是怎样的? - -![正常缓存处理流程](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/database/redis/normal-cache-process.png) - -简单来说就是: - -1. 如果用户请求的数据在缓存中就直接返回。 -2. 缓存中不存在的话就看数据库中是否存在。 -3. 数据库中存在的话就更新缓存中的数据。 -4. 数据库中不存在的话就返回空数据。 - ### 为什么要用 Redis/为什么要用缓存? 下面我们主要从“高性能”和“高并发”这两点来回答这个问题。 diff --git a/docs/home.md b/docs/home.md index 49178859..48bce296 100644 --- a/docs/home.md +++ b/docs/home.md @@ -200,8 +200,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle **知识点/面试题总结:** - **[MySQL知识点总结](./database/mysql/mysql-questions-01.md)** (必看 :+1:) -- [一千行 MySQL 学习笔记](./database/mysql/a-thousand-lines-of-mysql-study-notes.md) -- [MySQL 高性能优化规范建议](./database/mysql/mysql-high-performance-optimization-specification-recommendations.md) +- [MySQL 高性能优化规范建议总结](./database/mysql/mysql-high-performance-optimization-specification-recommendations.md) **重要知识点:** @@ -390,7 +389,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle ### 超时&重试 -[超时&重试详解](./high-vailability/timeout-and-retry.md) +[超时&重试详解](./high-availability/timeout-and-retry.md) ### 集群 diff --git a/docs/interview-preparation/project-experience-guide.md b/docs/interview-preparation/project-experience-guide.md index a38bb544..5c2cf6f3 100644 --- a/docs/interview-preparation/project-experience-guide.md +++ b/docs/interview-preparation/project-experience-guide.md @@ -21,7 +21,7 @@ category: 面试准备 我面试过很多求职者,简历上看着有微服务的项目经验,结果随便问两个问题就知道根本不是自己做的或者说做的时候压根没认真思考。这种情况会给我留下非常不好的印象。 -我在 **[《Java 面试指北》](../zhuanlan/java-mian-shi-zhi-bei.md)**的「面试准备篇」中也说过: +我在 **[《Java 面试指北》](../zhuanlan/java-mian-shi-zhi-bei.md)** 的「面试准备篇」中也说过: > 个人认为也没必要非要去做微服务或者分布式项目,不一定对你面试有利。微服务或者分布式项目涉及的知识点太多,一般人很难吃透。并且,这类项目其实对于校招生来说稍微有一点超标了。即使你做出来,很多面试官也会认为不是你独立完成的。 > @@ -71,6 +71,8 @@ Github 或者码云上面有很多实战类别项目,你可以选择一个来 ![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/javamianshizhibei/project-experience-guide.png) +这篇文章一共推荐了 15+ 个实战项目,有业务类的,也有轮子类的,有开源项目、也有视频教程。对于参加校招的小伙伴,我更建议做一个业务类项目加上一个轮子类的项目。 + ## 我跟着视频做的项目会被面试官嫌弃不? 很多应届生都是跟着视频做的项目,这个大部分面试官都心知肚明。 diff --git a/docs/system-design/framework/spring/spring-design-patterns-summary.md b/docs/system-design/framework/spring/spring-design-patterns-summary.md index 460954e6..885d7572 100644 --- a/docs/system-design/framework/spring/spring-design-patterns-summary.md +++ b/docs/system-design/framework/spring/spring-design-patterns-summary.md @@ -5,50 +5,56 @@ tag: - Spring --- -JDK 中用到了哪些设计模式?Spring 中用到了哪些设计模式?这两个问题,在面试中比较常见。我在网上搜索了一下关于 Spring 中设计模式的讲解几乎都是千篇一律,而且大部分都年代久远。所以,花了几天时间自己总结了一下,由于我的个人能力有限,文中如有任何错误各位都可以指出。另外,文章篇幅有限,对于设计模式以及一些源码的解读我只是一笔带过,这篇文章的主要目的是回顾一下 Spring 中的设计模式。 +“JDK 中用到了哪些设计模式? Spring 中用到了哪些设计模式? ”这两个问题,在面试中比较常见。 -Design Patterns(设计模式) 表示面向对象软件开发中最好的计算机编程实践。 Spring 框架中广泛使用了不同类型的设计模式,下面我们来看看到底有哪些设计模式? +我在网上搜索了一下关于 Spring 中设计模式的讲解几乎都是千篇一律,而且大部分都年代久远。所以,花了几天时间自己总结了一下。 + +由于我的个人能力有限,文中如有任何错误各位都可以指出。另外,文章篇幅有限,对于设计模式以及一些源码的解读我只是一笔带过,这篇文章的主要目的是回顾一下 Spring 中的设计模式。 ## 控制反转(IoC)和依赖注入(DI) -**IoC(Inversion of Control,控制反转)** 是Spring 中一个非常非常重要的概念,它不是什么技术,而是一种解耦的设计思想。它的主要目的是借助于“第三方”(Spring 中的 IOC 容器) 实现具有依赖关系的对象之间的解耦(IOC容器管理对象,你只管使用即可),从而降低代码之间的耦合度。**IOC 是一个原则,而不是一个模式,以下模式(但不限于)实现了IoC原则。** +**IoC(Inversion of Control,控制反转)** 是 Spring 中一个非常非常重要的概念,它不是什么技术,而是一种解耦的设计思想。IoC 的主要目的是借助于“第三方”(Spring 中的 IoC 容器) 实现具有依赖关系的对象之间的解耦(IOC 容器管理对象,你只管使用即可),从而降低代码之间的耦合度。 + +**IoC 是一个原则,而不是一个模式,以下模式(但不限于)实现了 IoC 原则。** ![ioc-patterns](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/ioc-patterns.png) -**Spring IOC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。** IOC 容器负责创建对象,将对象连接在一起,配置这些对象,并从创建中处理这些对象的整个生命周期,直到它们被完全销毁。 +**Spring IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。** IoC 容器负责创建对象,将对象连接在一起,配置这些对象,并从创建中处理这些对象的整个生命周期,直到它们被完全销毁。 -在实际项目中一个 Service 类如果有几百甚至上千个类作为它的底层,我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IOC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。关于Spring IOC 的理解,推荐看这一下知乎的一个回答: ,非常不错。 +在实际项目中一个 Service 类如果有几百甚至上千个类作为它的底层,我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IOC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。 -**控制反转怎么理解呢?** 举个例子:"对象a 依赖了对象 b,当对象 a 需要使用 对象 b的时候必须自己去创建。但是当系统引入了 IOC 容器后, 对象a 和对象 b 之前就失去了直接的联系。这个时候,当对象 a 需要使用 对象 b的时候, 我们可以指定 IOC 容器去创建一个对象b注入到对象 a 中"。 对象 a 获得依赖对象 b 的过程,由主动行为变为了被动行为,控制权反转,这就是控制反转名字的由来。 +> 关于 Spring IOC 的理解,推荐看这一下知乎的一个回答: ,非常不错。 + +**控制反转怎么理解呢?** 举个例子:"对象 a 依赖了对象 b,当对象 a 需要使用 对象 b 的时候必须自己去创建。但是当系统引入了 IOC 容器后, 对象 a 和对象 b 之前就失去了直接的联系。这个时候,当对象 a 需要使用 对象 b 的时候, 我们可以指定 IOC 容器去创建一个对象 b 注入到对象 a 中"。 对象 a 获得依赖对象 b 的过程,由主动行为变为了被动行为,控制权反转,这就是控制反转名字的由来。 **DI(Dependecy Inject,依赖注入)是实现控制反转的一种设计模式,依赖注入就是将实例变量传入到一个对象中去。** ## 工厂设计模式 -Spring使用工厂模式可以通过 `BeanFactory` 或 `ApplicationContext` 创建 bean 对象。 +Spring 使用工厂模式可以通过 `BeanFactory` 或 `ApplicationContext` 创建 bean 对象。 **两者对比:** -- `BeanFactory` :延迟注入(使用到某个 bean 的时候才会注入),相比于`ApplicationContext` 来说会占用更少的内存,程序启动速度更快。 -- `ApplicationContext` :容器启动的时候,不管你用没用到,一次性创建所有 bean 。`BeanFactory` 仅提供了最基本的依赖注入支持,` ApplicationContext` 扩展了 `BeanFactory` ,除了有`BeanFactory`的功能还有额外更多功能,所以一般开发人员使用` ApplicationContext`会更多。 +- `BeanFactory` :延迟注入(使用到某个 bean 的时候才会注入),相比于`ApplicationContext` 来说会占用更少的内存,程序启动速度更快。 +- `ApplicationContext` :容器启动的时候,不管你用没用到,一次性创建所有 bean 。`BeanFactory` 仅提供了最基本的依赖注入支持,`ApplicationContext` 扩展了 `BeanFactory` ,除了有`BeanFactory`的功能还有额外更多功能,所以一般开发人员使用`ApplicationContext`会更多。 -ApplicationContext的三个实现类: +`ApplicationContext` 的三个实现类: 1. `ClassPathXmlApplication`:把上下文文件当成类路径资源。 -2. `FileSystemXmlApplication`:从文件系统中的 XML 文件载入上下文定义信息。 -3. `XmlWebApplicationContext`:从Web系统中的XML文件载入上下文定义信息。 +2. `FileSystemXmlApplication`:从文件系统中的 XML 文件载入上下文定义信息。 +3. `XmlWebApplicationContext`:从 Web 系统中的 XML 文件载入上下文定义信息。 Example: ```java import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; - + public class App { public static void main(String[] args) { ApplicationContext context = new FileSystemXmlApplicationContext( "C:/work/IOC Containers/springframework.applicationcontext/src/main/resources/bean-factory-config.xml"); - + HelloApplicationContext obj = (HelloApplicationContext) context.getBean("helloApplicationContext"); obj.getMsg(); } @@ -59,24 +65,22 @@ public class App { 在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常、资源使用过量、或者不一致性的结果。 -**使用单例模式的好处:** +**使用单例模式的好处** : - 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销; - 由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。 **Spring 中 bean 的默认作用域就是 singleton(单例)的。** 除了 singleton 作用域,Spring 中 bean 还有下面几种作用域: -- prototype : 每次请求都会创建一个新的 bean 实例。 -- request : 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。 -- session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。 -- global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话 +- **prototype** : 每次获取都会创建一个新的 bean 实例。也就是说,连续 `getBean()` 两次,得到的是不同的 Bean 实例。 +- **request** (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。 +- **session** (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。 +- **application/global-session** (仅 Web 应用可用): 每个 Web 应用在启动时创建一个 Bean(应用 Bean),,该 bean 仅在当前应用启动时间内有效。 +- **websocket** (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。 -**Spring 实现单例的方式:** +Spring 通过 `ConcurrentHashMap` 实现单例注册表的特殊方式实现单例模式。 -- xml : `` -- 注解:`@Scope(value = "singleton")` - -**Spring 通过 `ConcurrentHashMap` 实现单例注册表的特殊方式实现单例模式。Spring 实现单例的核心代码如下** +Spring 实现单例的核心代码如下: ```java // 通过 ConcurrentHashMap(线程安全) 实现单例注册表 @@ -85,7 +89,7 @@ private final Map singletonObjects = new ConcurrentHashMap singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null"); synchronized (this.singletonObjects) { - // 检查缓存中是否存在实例 + // 检查缓存中是否存在实例 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { //...省略了很多代码 @@ -109,17 +113,28 @@ public Object getSingleton(String beanName, ObjectFactory singletonFactory) { } ``` +**单例 Bean 存在线程安全问题吗?** + +大部分时候我们并没有在项目中使用多线程,所以很少有人会关注这个问题。单例 Bean 存在线程问题,主要是因为当多个线程操作同一个对象的时候是存在资源竞争的。 + +常见的有两种解决办法: + +1. 在 Bean 中尽量避免定义可变的成员变量。 +2. 在类中定义一个 `ThreadLocal` 成员变量,将需要的可变成员变量保存在 `ThreadLocal` 中(推荐的一种方式)。 + +不过,大部分 Bean 实际都是无状态(没有实例变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。 + ## 代理设计模式 ### 代理模式在 AOP 中的应用 -AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,**却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来**,便于**减少系统的重复代码**,**降低模块间的耦合度**,并**有利于未来的可拓展性和可维护性**。 +**AOP(Aspect-Oriented Programming,面向切面编程)** 能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。 -**Spring AOP 就是基于动态代理的**,如果要代理的对象,实现了某个接口,那么Spring AOP会使用**JDK Proxy**,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用 **Cglib** 生成一个被代理对象的子类来作为代理,如下图所示: +**Spring AOP 就是基于动态代理的**,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 **JDK Proxy** 去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 **Cglib** 生成一个被代理对象的子类来作为代理,如下图所示: ![SpringAOPProcess](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/SpringAOPProcess.jpg) -当然你也可以使用 AspectJ ,Spring AOP 已经集成了AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。 +当然,你也可以使用 AspectJ ,Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。 使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。 @@ -127,9 +142,9 @@ AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无 **Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。** Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。 - Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单, +Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单, -如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比Spring AOP 快很多。 +如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比 Spring AOP 快很多。 ## 模板方法 @@ -139,7 +154,7 @@ AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无 public abstract class Template { //这是我们的模板方法 public final void TemplateMethod(){ - PrimitiveOperation1(); + PrimitiveOperation1(); PrimitiveOperation2(); PrimitiveOperation3(); } @@ -147,7 +162,7 @@ public abstract class Template { protected void PrimitiveOperation1(){ //当前类实现 } - + //被子类实现的方法 protected abstract void PrimitiveOperation2(); protected abstract void PrimitiveOperation3(); @@ -159,7 +174,7 @@ public class TemplateImpl extends Template { public void PrimitiveOperation2() { //当前类实现 } - + @Override public void PrimitiveOperation3() { //当前类实现 @@ -168,7 +183,7 @@ public class TemplateImpl extends Template { ``` -Spring 中 `jdbcTemplate`、`hibernateTemplate` 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。一般情况下,我们都是使用继承的方式来实现模板模式,但是 Spring 并没有使用这种方式,而是使用Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性。 +Spring 中 `JdbcTemplate`、`HibernateTemplate` 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。一般情况下,我们都是使用继承的方式来实现模板模式,但是 Spring 并没有使用这种方式,而是使用 Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性。 ## 观察者模式 @@ -178,7 +193,7 @@ Spring 中 `jdbcTemplate`、`hibernateTemplate` 等以 Template 结尾的对数 #### 事件角色 - `ApplicationEvent` (`org.springframework.context`包下)充当事件的角色,这是一个抽象类,它继承了`java.util.EventObject`并实现了 `java.io.Serializable`接口。 +`ApplicationEvent` (`org.springframework.context`包下)充当事件的角色,这是一个抽象类,它继承了`java.util.EventObject`并实现了 `java.io.Serializable`接口。 Spring 中默认存在以下事件,他们都是对 `ApplicationContextEvent` 的实现(继承自`ApplicationContextEvent`): @@ -191,7 +206,7 @@ Spring 中默认存在以下事件,他们都是对 `ApplicationContextEvent` #### 事件监听者角色 -`ApplicationListener` 充当了事件监听者角色,它是一个接口,里面只定义了一个 `onApplicationEvent()`方法来处理`ApplicationEvent`。`ApplicationListener`接口类源码如下,可以看出接口定义看出接口中的事件只要实现了 `ApplicationEvent`就可以了。所以,在 Spring中我们只要实现 `ApplicationListener` 接口的 `onApplicationEvent()` 方法即可完成监听事件 +`ApplicationListener` 充当了事件监听者角色,它是一个接口,里面只定义了一个 `onApplicationEvent()`方法来处理`ApplicationEvent`。`ApplicationListener`接口类源码如下,可以看出接口定义看出接口中的事件只要实现了 `ApplicationEvent`就可以了。所以,在 Spring 中我们只要实现 `ApplicationListener` 接口的 `onApplicationEvent()` 方法即可完成监听事件 ```java package org.springframework.context; @@ -224,7 +239,7 @@ public interface ApplicationEventPublisher { 1. 定义一个事件: 实现一个继承自 `ApplicationEvent`,并且写相应的构造函数; 2. 定义一个事件监听者:实现 `ApplicationListener` 接口,重写 `onApplicationEvent()` 方法; -3. 使用事件发布者发布消息: 可以通过 `ApplicationEventPublisher ` 的 `publishEvent()` 方法发布消息。 +3. 使用事件发布者发布消息: 可以通过 `ApplicationEventPublisher` 的 `publishEvent()` 方法发布消息。 Example: @@ -244,7 +259,7 @@ public class DemoEvent extends ApplicationEvent{ return message; } - + // 定义一个事件监听者,实现ApplicationListener接口,重写 onApplicationEvent() 方法; @Component public class DemoListener implements ApplicationListener{ @@ -272,41 +287,43 @@ public class DemoPublisher { ``` -当调用 `DemoPublisher ` 的 `publish()` 方法的时候,比如 `demoPublisher.publish("你好")` ,控制台就会打印出:`接收到的信息是:你好` 。 +当调用 `DemoPublisher` 的 `publish()` 方法的时候,比如 `demoPublisher.publish("你好")` ,控制台就会打印出:`接收到的信息是:你好` 。 ## 适配器模式 适配器模式(Adapter Pattern) 将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作。 -### Spring AOP中的适配器模式 +### Spring AOP 中的适配器模式 -我们知道 Spring AOP 的实现是基于代理模式,但是 Spring AOP 的增强或通知(Advice)使用到了适配器模式,与之相关的接口是`AdvisorAdapter ` 。 +我们知道 Spring AOP 的实现是基于代理模式,但是 Spring AOP 的增强或通知(Advice)使用到了适配器模式,与之相关的接口是`AdvisorAdapter` 。 -Advice 常用的类型有:`BeforeAdvice`(目标方法调用前,前置通知)、`AfterAdvice`(目标方法调用后,后置通知)、`AfterReturningAdvice`(目标方法执行结束后,return之前)等等。每个类型Advice(通知)都有对应的拦截器:`MethodBeforeAdviceInterceptor`、`AfterReturningAdviceInterceptor`、`ThrowsAdviceInterceptor` 等等。 +Advice 常用的类型有:`BeforeAdvice`(目标方法调用前,前置通知)、`AfterAdvice`(目标方法调用后,后置通知)、`AfterReturningAdvice`(目标方法执行结束后,return 之前)等等。每个类型 Advice(通知)都有对应的拦截器:`MethodBeforeAdviceInterceptor`、`AfterReturningAdviceInterceptor`、`ThrowsAdviceInterceptor` 等等。 Spring 预定义的通知要通过对应的适配器,适配成 `MethodInterceptor` 接口(方法拦截器)类型的对象(如:`MethodBeforeAdviceAdapter` 通过调用 `getInterceptor` 方法,将 `MethodBeforeAdvice` 适配成 `MethodBeforeAdviceInterceptor` )。 -### Spring MVC中的适配器模式 +### Spring MVC 中的适配器模式 -在Spring MVC中,`DispatcherServlet` 根据请求信息调用 `HandlerMapping`,解析请求对应的 `Handler`。解析到对应的 `Handler`(也就是我们平常说的 `Controller` 控制器)后,开始由`HandlerAdapter` 适配器处理。`HandlerAdapter` 作为期望接口,具体的适配器实现类用于对目标类进行适配,`Controller` 作为需要适配的类。 +在 Spring MVC 中,`DispatcherServlet` 根据请求信息调用 `HandlerMapping`,解析请求对应的 `Handler`。解析到对应的 `Handler`(也就是我们平常说的 `Controller` 控制器)后,开始由`HandlerAdapter` 适配器处理。`HandlerAdapter` 作为期望接口,具体的适配器实现类用于对目标类进行适配,`Controller` 作为需要适配的类。 -**为什么要在 Spring MVC 中使用适配器模式?** Spring MVC 中的 `Controller` 种类众多,不同类型的 `Controller` 通过不同的方法来对请求进行处理。如果不利用适配器模式的话,`DispatcherServlet` 直接获取对应类型的 `Controller`,需要的自行来判断,像下面这段代码一样: +**为什么要在 Spring MVC 中使用适配器模式?** + +Spring MVC 中的 `Controller` 种类众多,不同类型的 `Controller` 通过不同的方法来对请求进行处理。如果不利用适配器模式的话,`DispatcherServlet` 直接获取对应类型的 `Controller`,需要的自行来判断,像下面这段代码一样: ```java -if(mappedHandler.getHandler() instanceof MultiActionController){ - ((MultiActionController)mappedHandler.getHandler()).xxx -}else if(mappedHandler.getHandler() instanceof XXX){ - ... -}else if(...){ - ... -} +if(mappedHandler.getHandler() instanceof MultiActionController){ + ((MultiActionController)mappedHandler.getHandler()).xxx +}else if(mappedHandler.getHandler() instanceof XXX){ + ... +}else if(...){ + ... +} ``` 假如我们再增加一个 `Controller`类型就要在上面代码中再加入一行 判断语句,这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 – 对扩展开放,对修改关闭。 ## 装饰者模式 -装饰者模式可以动态地给对象添加一些额外的属性或行为。相比于使用继承,装饰者模式更加灵活。简单点儿说就是当我们需要修改原有的功能,但我们又不愿直接去修改原有的代码时,设计一个Decorator套在原有代码外面。其实在 JDK 中就有很多地方用到了装饰者模式,比如 `InputStream`家族,`InputStream` 类下有 `FileInputStream` (读取文件)、`BufferedInputStream` (增加缓存,使读取文件速度大大提升)等子类都在不修改`InputStream` 代码的情况下扩展了它的功能。 +装饰者模式可以动态地给对象添加一些额外的属性或行为。相比于使用继承,装饰者模式更加灵活。简单点儿说就是当我们需要修改原有的功能,但我们又不愿直接去修改原有的代码时,设计一个 Decorator 套在原有代码外面。其实在 JDK 中就有很多地方用到了装饰者模式,比如 `InputStream`家族,`InputStream` 类下有 `FileInputStream` (读取文件)、`BufferedInputStream` (增加缓存,使读取文件速度大大提升)等子类都在不修改`InputStream` 代码的情况下扩展了它的功能。 ![装饰者模式示意图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/Decorator.jpg) @@ -316,7 +333,7 @@ Spring 中配置 DataSource 的时候,DataSource 可能是不同的数据库 Spring 框架中用到了哪些设计模式? -- **工厂设计模式** : Spring使用工厂模式通过 `BeanFactory`、`ApplicationContext` 创建 bean 对象。 +- **工厂设计模式** : Spring 使用工厂模式通过 `BeanFactory`、`ApplicationContext` 创建 bean 对象。 - **代理设计模式** : Spring AOP 功能的实现。 - **单例设计模式** : Spring 中的 Bean 默认都是单例的。 - **模板方法模式** : Spring 中 `jdbcTemplate`、`hibernateTemplate` 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。 @@ -327,20 +344,10 @@ Spring 框架中用到了哪些设计模式? ## 参考 -- 《Spring技术内幕》 +- 《Spring 技术内幕》 - - -- +- - - - - -## 公众号 - -如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 - -**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取! - -**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 - -![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git a/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md b/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md index 84d7a5b4..3cb002a8 100644 --- a/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md +++ b/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md @@ -533,7 +533,7 @@ public class GlobalExceptionHandler { ## Spring 框架中用到了哪些设计模式? -关于下面这些设计模式的详细介绍,可以看我写的 [Spring 中的设计模式详解](https://javaguide.cn/system-design/framework/spring/spring-design-patterns-summary.html) 这篇文章。 +> 关于下面这些设计模式的详细介绍,可以看我写的 [Spring 中的设计模式详解](https://javaguide.cn/system-design/framework/spring/spring-design-patterns-summary.html) 这篇文章。 - **工厂设计模式** : Spring 使用工厂模式通过 `BeanFactory`、`ApplicationContext` 创建 bean 对象。 - **代理设计模式** : Spring AOP 功能的实现。