- 博客(208)
- 资源 (3)
- 收藏
- 关注
原创 基于贫血模型的MVC开发模式VS领域建模开发模式
大部分工程师都是做业务开发的,很多业务系统都是基于MVC三层架构来开发的,确切点说,这是一种基于贫血模型的MVC三层架构开发模式。这种开发模式已经成为标准的Web项目开发模式,但它却违反了面向对象编程风格,是一种彻彻底底的面向过程的编程风格。特别是领域驱动设计(DDD)盛行之后,这种基于贫血模型的传统开发模式就更加被人诟病,而基于充血模型的DDD开发模式越来越被人提倡。
2023-03-22 11:57:53 660
原创 关于提升业务系统扩展性的一些思考
软件架构设计是一个工程问题,设计的目的是为了能以最低的成本来满足构建和运维系统的需求。其实几乎所有的软件系统在设计之初都被要求是可以扩展的。对于一套持续运行的系统来说,运维的成本往往是开发成本的10倍以上。设计一套可扩展的软件系统去支撑多变的业务,已经成为了软件设计的一个主导原则。
2022-10-08 16:30:51 1914
转载 如何减少软件设计和实现之间鸿沟
在软件领域,有一个古老的神话:**即我能保证设计和代码实现完全一致**。这的确是一个非常有价值的目标。试想下,如果我们的系统毫无设计,或者设计和代码实现毫无关联,在当今软件如此复杂的情况下,其实现和维护难度可想而知。.........
2022-07-26 11:23:00 908
原创 基于接口而非实现编程,你真的理解了吗
“基于接口而非实现编程”这条原则的英文描述是:“Program to an interface, not an implementation”。我们理解这条原则的时候,千万不要一开始就与具体的编程语言挂钩,局限在编程语言的“接口”语法中(比如Java中的 interface)。这条原则是一条比较抽象、泛化的设计思想。实际上,理解这条原则的关键,就是理解其中的“接口”两个字,这里的“接口”是泛指,可以理解为“抽象”。
2022-02-23 15:32:52 2703 1
原创 领域建模应对软件复杂性初体验
在互联网各个在小厂维护过业务系统的开发们,应该都抱怨过编写得很烂的代码,大部分业务系统的代码都是过程式的面条,前人挖坑后人填坑,几乎是每迭代一次就能埋下一个坑,需求只要实现了就行,完全不追求设计美感。我们每天都在使用的各大APP或网站,我丝毫不怀疑,它们后台系统,一定充斥着可读性差毫无设计美感与OO思想的代码,这些过程式的面条代码,耗费了程序员的主要精力。博主常常和同事开玩笑,如果公司各个系统的代码开源,股价至少跌一半。
2022-02-09 16:33:10 800
原创 你的八股文可能已经过时了
Java中接口的目的是定义公开的API,而隐藏实现细节,所以在JDK8以前都不支持默认和静态方法。但是出于便捷性的考虑,JDK8开始支持在接口定义一个方法的默认实现,这样当一个接口有大量实现类的情况下,可以在不破坏原有实现的前提下迭代API。JDK8中接口类的默认实现解决了模型抽象中的很多问题,随之而来的问题是如果在接口的默认方法中需要共享一些代码段,只能将这些代码段抽象出一个新的函数。如果这个函数是静态的(JDK8支持接口中定义静态函数),那么在函数里就无法访问其他非静态API。
2024-06-06 10:40:21 746
原创 谨慎使用Lombok的@Builder注解
现在很多程序员都习惯使用Lombok来使代码更加 “简洁”。但是使用Lombok也会造成很多问题,尤其@Builder 有个很大的坑,已经见过好几次由于使用@Builder注解导致默认值失效的问题,如果测试时没有在意这个问题,就很容易引发线上问题。
2023-09-01 11:54:55 799
转载 从Servlet、Servlet容器及Web容器的演进历程总结框架设计的套路
为什么要有Servlet ?为什么要有Servlet容器?什么是Web容器、HTTP服务器?今儿咱们就来盘盘,并且从中来总结架构和框架的设计套路。
2023-04-07 15:24:22 427 1
原创 AQS与Synchronized异曲同工的加锁流程
在并发多线程的情况下,为了保证数据安全性,一般我们会对数据进行加锁,通常使用Synchronized或者ReentrantLock同步锁。Synchronized是基于JVM实现,而ReentrantLock是基于Java代码层面实现的,底层是继承的AQS
2023-02-20 15:06:02 565
原创 Java 8的函数式接口使用示例
有且只有一个抽象方法的接口被称为函数式接口,函数式接口适用于函数式编程的场景,Lambda就是Java中函数式编程的体现,可以使用Lambda表达式创建一个函数式接口的对象,一定要确保接口中有且只有一个抽象方法,这样Lambda才能顺利的进行推导。
2023-02-15 16:02:28 1171
原创 刨根问底:Java动态运行Groovy可能有哪些坑
Groovy代码能够与Java代码很好地结合,也能用于扩展现有代码。相对于Java,它在编写代码的灵活性上有非常明显的提升。Groovy是动态编译语言,广泛用作脚本语言和快速原型语言,主要优势之一就是它的生产力。Groovy代码通常要比Java代码更容易编写,而且编写起来也更快,这使得它有足够的资格成为开发工作包中的一个附件。...
2022-07-13 13:14:59 6289
原创 微服务(Microservice)那点事儿
一个微服务架构的系统是一个分布式系统,系统内会由若干个应用组成,每个应用就是一个微服务。根据需求,每个微服务应用可以部署一定数量的服务器,也可以支持横向扩展,随时支持流量的变化。
2022-07-04 14:00:00 1253
原创 重看《Redis设计与实现》后记录几个要点
一提到 Redis,我们的脑子里马上就会出现一个词:“快。”但是你有没有想过,Redis 的快,到底是快在哪里呢?实际上,这里有一个重要的表现:它接收到一个键值对操作后,能以微秒级别的速度找到数据,并快速完成操作。...........................
2022-06-14 20:20:47 921
原创 用自定义类加载器实现Java类隔离
类隔离就是让不同模块的jar包用不同的类加载器加载,要做到这一点,就需要让JVM能够使用自定义的类加载器加载我们写的类以及其关联的类。
2022-06-04 15:43:49 3336 4
原创 HBase架构及原理初探
HBase是一个分布式的、列式的、实时查询的、非关系型数据库,可以处理PB级别的数据,吞吐量可以到的百万查询/每秒;其诞生的理论基础是Google大数据三驾马车之一的BigTable论文。
2022-05-26 13:34:06 1445
原创 JVM运行时数据区域——为什么jdk8用元空间替换了永久代
JVM 运行时数据区域(堆和栈存哪些内容、堆和栈的区别、方法区存哪些内容、为什么 jdk8 用元空间替换了永久代)
2022-03-31 17:57:30 1725
原创 令人头疼的缓存与数据库一致性问题
当我们的系统引入缓存组件之后,性能得到了大幅度提升,但是随之而来的是代码需要引入一定的复杂度,比如缓存的更新策略,写入策略,过期策略等,而其中最可能导致程序员加班的莫过于缓存和数据库的一致性问题了。
2022-03-21 20:50:46 3156
原创 《从零开始学架构》读后总结
每一个程序员都有一个架构师的梦,可理想很丰满,现实很骨感---大部程序员工作中都做着CRUD,但进取心与持续学习的心态还是要有的。掌握架构设计的相关理论是成为架构师的前提。在看了一本《从零开始学架构》后,发现架构设计是有套路的。......
2022-01-14 16:09:05 2360
转载 分布式锁的CAP分析
前言微服务的流行,使得现在基本都是分布式开发,也就是同一份代码会在多台机器上部署运行,此时若多台机器需要同步访问同一个资源(同一时间只能有一个节点机器在运行同一段代码),就需要使用到分布式锁。然而做好一个分布式锁并不容易,要考虑的点非常多,建议架构能力一般的公司对于分布式锁还是使用现有的开源框架来做(例如Redis的Redisson、Zookeeper的Curator、etcd等等),如果需要基于Redis、ZK进行自研的话,建议阅读接下来讨论的几个要点。AP与CP的选择首先我觉得最重要的就是考虑分布
2021-11-25 16:55:51 696
转载 死磕CMS垃圾收集器
面试官:今天还是来聊聊CMS垃圾收集器呗?候选者:嗯啊…候选者:如果用Seria和Parallel系列的垃圾收集器:在垃圾回收的时,用户线程都会完全停止,直至垃圾回收结束!候选者:CMS的全称:Concurrent Mark Sweep,翻译过来是「并发标记清除」,是老年代垃圾收集器候选者:用CMS对比上面的垃圾收集器(Seria和Parallel和parNew):它最大的不同点就是「并发」:在GC线程工作的时候,用户线程「不会完全停止」,用户线程在「部分场景下」与GC线程一起并发执行。候选者:
2021-11-09 17:19:13 419
原创 学透并发编程——LeetCode多线程协作经典题
LeetCode上专门的多线程题目版块,本文通过解决几道leetCode上的多线程协作交替打印的编程题,来加深对并发编程的理解,并训练一下并发编程思想。
2021-11-09 16:53:07 1618
原创 用代码实现生产者消费者模型
用代码实现生产者消费者模型,3个生产者每隔3秒生产1个单位的数据,2个消费者每隔1秒消费一个单位的数据,共享资源有界。要求不能出现死锁或者占用CPU不释放的情况。
2021-09-06 19:02:47 894
原创 再谈线程安全
线程安全“线程安全”不是指线程的安全,而是指内存的安全。为什么如此说呢?这和操作系统有关。目前主流操作系统都是多任务的,即多个进程同时运行。为了保证安全,每个进程只能访问分配给自己的内存空间,而不能访问别的进程的,这是由操作系统保障的。在每个进程的内存空间中都会有一块特殊的公共区域,通常称为堆(内存)。进程内的所有线程都可以访问到该区域,这就是造成问题的潜在原因。假设某个线程把数据处理到一半,觉得很累,就去休息了一会,回来准备接着处理,却发现数据已经被修改了,不是自己离开时的样子了。可能被其它线程修改
2021-08-17 14:08:27 242
原创 数据结构如何造就Redis的快
作为一种键值数据库,为啥Redis能有这么突出的表现呢?一方面,这是因为它是内存数据库,所有操作都在内存上完成,内存的访问速度本身就很快。另一方面,这要归功于它的数据结构。键值对(key-value对)是按一定的数据结构来组织的,操作键值对最终就是对数据结构进行增删改查操作,高效的数据结构是Redis快速处理数据的基础。Redis中的键的类型只能为String(字符串),值支持五种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Sorted Set(有序集合,也叫Zs
2021-08-16 15:20:18 427
转载 学透回溯算法
其实回溯算法和我们常说的 DFS 算法非常类似,本质上就是一种暴力穷举算法。回溯算法和 DFS 算法的细微差别是:回溯算法是在遍历「树枝」,DFS 算法是在遍历「节点」,解决一个回溯问题,实际上就是一个决策树的遍历过程。
2021-08-06 11:50:49 611
转载 Linux的进程、线程、⽂件描述符是什么
说到进程,恐怕⾯试中最常⻅的问题就是线程和进程的关系了,那么先说⼀ 下答案:在Linux系统中,进程和线程⼏乎没有区别。Linux中的进程就是⼀个数据结构,看明⽩就可以理解⽂件描述符、重定向、管道命令的底层⼯作原理,最后我们从操作系统的⾓度看看为什么说线程和进程基本没有区别。进程是什么⾸先,抽象地来说,我们的计算机就是这个东⻄:这个⼤的矩形表⽰计算机的内存空间,其中的⼩矩形代表进程,左下⾓的圆 形表⽰磁盘,右下⾓的图形表⽰⼀些输⼊输出设备,⽐如⿏标键盘显⽰器等等。另外,注意到内存空间被划分为了两块
2021-08-06 11:05:53 582
原创 如何用Redis实现分布式锁
为什么需要分布式锁在聊分布式锁之前,有必要先解释一下,为什么需要分布式锁。与分布式锁相对就的是单机锁,我们在写多线程程序时,避免同时操作一个共享变量产生数据问题,通常会使用一把锁来互斥以保证共享变量的正确性,其使用范围是在同一个进程中。如果换做是多个进程,需要同时操作一个共享资源,如何互斥呢?现在的业务应用通常是微服务架构,这也意味着一个应用会部署多个进程,多个进程如果需要修改MySQL中的同一行记录,为了避免操作乱序导致脏数据,此时就需要引入分布式锁了。想要实现分布式锁,必须借助一个外部系统,所有
2021-07-30 19:05:55 38752 26
原创 Java并发编程——CompletableFuture类
从Future接口开始java.util.concurrent.Future接口是Java 5添加的类,用来描述一个异步计算的结果。可以使用该接口的isDone()方法检查计算是否完成,或者使用get()阻塞住调用线程,直到计算完成返回结果,也可以使用cancel()方法停止任务的执行。ExecutorService es = Executors.newFixedThreadPool(10);Future<Integer> f = es.submit(() ->{ /
2021-07-27 15:18:39 4220
原创 OceanBase简介及其与MySQL的比较
OceanBase是阿里巴巴和蚂蚁金服完全自主研发的通用的分布式关系型数据库,定位为商用企业级数据库。OceanBase能提供金融级别的可靠性,目前主要应用用于金融行业,同时也适用于非金融行业场景。它融合传统关系数据库和分布式系统的优势,利用普通的PC服务器组成数据库集群,拥有出色的线性扩展性。
2021-07-05 20:47:00 53070 6
原创 理解单调栈与单调队列
单调栈单调栈:栈内的元素按照某种方式排序下单调递增或单调递减,如果新入栈的元素破坏的单调性,就弹出栈内元素,直到满足单调性。单调栈分为单调递增栈和单调递减栈:单调递增栈:栈中数据出栈的序列为单调递减序列;单调递减栈:栈中数据出栈的序列为单调递增序列。维护单调递增栈遍历数组中每一个元素,执行入栈:每次入栈前先检验栈顶元素和进栈元素的大小。如果栈空或进栈元素大于栈顶元素则直接入栈;如果进栈元素小于等于栈顶元素,则出栈,直至进栈元素大于栈顶元素。 //单调递增栈 Stack<Int
2021-06-27 12:48:16 1780 6
原创 本地方法栈、JVM栈、本地内存和JVM Heap的区别与关系
在Java出现之前,像C/C++这样的编译型语言写出来的代码都是编译为机器码(machine code)后直接运行的,machine code其实就是native code,直接和操作系统交互。对于内存,主要分三部分:1)存储可执行代码(冯·诺依曼的存储程序的思想),即编译后的machine code;2)用来保存代码执行时用到的局部变量,即stack;3)代码执行时,动态找操作系统申请(最终要归还给操作系统)的heap;对于Heap的分配和归还都是由程序代码手工维护的。如下图所示,写一段C++
2021-05-24 21:51:12 2985 3
原创 写时复制(Copy-On-Write)思想在Java中的应用
前言写时复制(Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这个过程对其他的调用者是透明的(transparently)。此作法的主要优点是如果调用者没有修改该资源,就不会有副本(private copy)被建立,因
2021-05-20 20:01:21 1814 6
原创 读完《深入理解Java虚拟机》后记录几个问题
以下总结的问题都是在针对HotSpot虚拟机展开讨论,这些问题其实都可以在下面这本秘籍中找到答案——《深入理解Java虚拟机》几乎是每一个Java开发者必备宝典
2021-05-14 17:20:59 563
原创 Spring源码分析——一次HTTP请求在Spring中的处理过程
在网上有很多Spring处理请求的流程图,但是都是比较简单的过程,都是围绕的DispatcherServlet进行说明的,这里我们将从一个请求如何进入spring中开始,到返回结果结束进行说明。
2021-05-13 21:37:44 3452
原创 Spring源码分析——Bean的注册
背景Spring版本:5.1.14.RELEASE仔细阅读依赖注入(Dependency Injection)框架是如何实现的这篇博文后,我们大致知道了一个DI容器是如何创建bean的。总体来,bean的创建流程分为如下三步:定义化:从xml文件中解析bean的定义(获得BeanDefinition实例),之后创建bean都是通过bean的定义去创建。实例化:实例化的过程是一个创建Bean的过程,即调用Bean的构造函数,单例的Bean放入单例池中。初始化:初始化的过程是一个赋值的过程,即调用B
2021-05-12 10:42:38 657
原创 适配器模式及其在Java日志体系中的应用
适配器模式的原理适配器模式的英文翻译是 Adapter Design Pattern。顾名思义,这个模式就是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。对于这个模式,有一个经常被拿来解释它的例子,就是USB转接头充当适配器,把两种不兼容的接口,通过转接变得可以一起工作。原理很简单,适配器模式有两种实现方式:类适配器和对象适配器。其中,类适配器使用继承关系来实现,对象适配器使用组合关系来实现。两种模式的类图如下:具体实现的示例代码如下。//
2021-03-23 19:36:40 681 1
《从零开始学架构》精华读书笔记
2023-05-09
阿里巴巴Java开发手册
2017-03-01
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人