分享点点滴滴,滴水穿石
`@ControllerAdvice` 是 Spring MVC 中用于定义全局行为的注解,如异常处理、数据绑定和预处理。它从 `@Component` 派生,确保被扫描并纳入容器。`@ExceptionHandler` 用于全局异常处理,提供统一的错误响应。例如,当处理不当的异常时,它能返回友好的错误信息。`@InitBinder` 在数据绑定前对参数进行处理,如格式转换。`@ModelAttribute` 可以用于全局绑定模型属性,如登录用户信息。Spring MVC 通过 `DispatcherServlet` 和 `HandlerAdapter` 在请求处理流程中应用这些全局配置。
这里我们先给出问题的全面回答:`Redis`到底是多线程还是单线程程序要看是针对哪个功能而言,**对于核心业务功能部分(命令操作处理数据),Redis是单线程的,主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程,所以一般我们认为Redis是个单线程程序。但是从整个框架层面出发严格来说Redis是多线程的。**
这里先说说我的观点哈,仅是个人观点哦,不喜勿喷。现在这些框架层出不穷,其实吧个人感觉没必要过度关注,因为这些框架并没有完完全全做到推陈出新,反倒是有一点互相“学习copy”的感觉,并没有那么新颖强大、从无到有的一个过程。那说回今天的主题ORM框架,在Java后端技术栈里面我们都知道`MyBatis`是主流的ORM框架,现在很多公司都在使用着,后来在`MyBatis`基础上出现了两个比较主流的增强框架`Mybatis-Plus`和`Fluent-MyBatis`
MyBatis-Plus (简称 MP,下文就使用简称啦)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。官网地址:https://baomidou.com/ 有以下特性:
我们在日常排查问题过程中知道,入参传错是导致接口调用失败的常见原因之一。特别是提供给第三方调用的**回调接口和openAPI接口**,由于无法保证第三方开发人员的水平,经常问题不断,反反复复找你问为啥掉不通,甚至吐槽写的“啥玩意接口”,这时候你肯定一脸懵逼,怒火中烧,想展开撕逼甩锅大战,但是对方有可能是甲方金主爸爸并且你没有第一时间掌握证据证明证是对方调用的问题,你只能忍着问他是如何调接口的,卑微请求他把传参发过来看看。。。为了扭转局势,挺直腰杆怼回去:能不能靠谱点?今天我们就来讲讲系统服务中如何优雅地实现统一打印接口API参数日志,方便服务端开发快速甩锅还能拿出证据!!!
在当下互联网高速发展的时代下,涉及到用户的隐私数据安全越发重要,一旦泄露将造成不可估量的后果。所以现在的业务系统开发中都会对用户隐私数据加密之后存储落库,同时还要求后端返回数据给前台之前进行数据脱敏。所谓脱敏处理其实就是将数据进行混淆隐藏,如将用户的手机号脱敏展示为`178****5939,采用 * 进行隐藏,以免泄露个人隐私信息
`ZooKeeper `是一个**开放源码的分布式协调服务**,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户。 分布式应用程序可以基于` Zookeeper` 实现诸如**数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列**等功能。
`SPI`全称`Service Provider Interface`,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。本质是**将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类**。这样可以在运行时,动态为接口替换实现类。通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类,进而实现可插拔,解耦。
我们都知道随着业务系统的发展和使用,数据库存储的业务数据量会越来越大,逐渐成为了业务系统的瓶颈。在阿里巴巴开发手册中也建议:单表行数超过500万行或者单表容量超过2GB才推荐进行分库分表,如果预计三年后数据量根本达不到这个级别,请不要在创建表时就分库分表。数据库最终都是存储在磁盘上,随着数据量变大,会导致数据操作变得缓慢,无论是计算还是IO,但是话又说回来,单表数据量大就一定要进行分库分表操作吗?答案是否定的,因为分库分表本身是一个“很重”的操作,这里就不卖关子了,直接来看看分库分表带来的以下问题和挑战
接着之前我们对Spring AOP以及基于AOP实现事务控制的上文,今天我们来看看平时在项目业务开发中使用声明式事务`@Transactional`的失效场景,并分析其失效原因,从而帮助开发人员尽量避免踩坑。
整合redis和caffeine实现多级缓存,解决上面单一缓存的痛点,从而做到相互补足
对于一个系统应用而言,使用数据库进行数据存储是必然的,意味着开发过程中事务的使用及控制也是必不可少的,当然事务是数据库层面的知识点并不是`Spring`框架所提出的。使用JDBC开发时,我们使用`connnection`对事务进行控制,使用`MyBatis`时,我们使用`SqlSession`对事务进行控制,缺点显而易见,当我们切换数据库访问技术时,事务控制的方式总会变化,所以`Spring` 就在这些技术基础上,提供了统一的控制事务的接口。Spring的事务分为:编程式事务控制和声明式事务控制
`Spring AOP`是`Spring`框架中极为重要的核心功能,和`Spring IOC`并称为`Spring`的两大核心模块。顾名思义,**AOP 即 Aspect Oriented Programming,翻译为面向切面编程**。OOP面向对象编程是纵向地对一个事物的抽象,一个对象包括静态的属性信息、动态的方法信息等。而AOP是横向地对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程。
我们也都知道在日常开发系统过程中,数据安全是非常重要的。特别是在当今互联网时代,个人隐私安全极其重要,一旦个人用户数据遭到攻击泄露,将会造成灾难级的事故问题。所有之前我们基于接口层进行数据安全处理是远远不够的,今天我们就来谈谈如何Model层(数据访问层)怎样做到优雅数据加密存储、模糊匹配及其脱敏展示,本文的主题:**数据加密存储、模糊匹配和脱敏展示**。
在Spring Boot项目中提高接口安全的核心所在:**加密和加签**,加固接口参数、验证复杂度。 **加密:**对参数进行加密传输,拒绝接口参数直接暴露,这样就可以有效做到防止别人轻易准确地获取到接口参数定义和传参格式要求了。 **加签:**对接口参数进行加签,可以有效防止接口参数被篡改和接口参数被重放恶刷。
多租户问题,其是一种架构设计方式,就是在一台或者一组服务器上运行的SaaS系统,可以为多个租户(客户)提供服务,目的是为了让多个租户在互联网环境下使用同一套程序,且保证租户间的数据隔离。从这种架构设计的模式上,不难看出来,多租户架构的重点就是同一套程序下多个租户数据的隔离。由于租户数据是集中存储的,所以要实现数据的安全性,就是看能否实现对租户数据的隔离,防止租户数据不经意或被他人恶意地获取和篡改
当下基于`Spring Boot`框架开发的系统几乎都是前后端分离的,也都是基于`RESTFUL`风格进行接口定义开发的,意味着前后端开发大部分数据的传输格式都是json,因此定义一个统一规范的数据格式返回有利于前后端的交互与UI的展示
循环依赖其实就是一个闭环,像图中情况二Spring在创建单例bean A的时候发现引用了B,这时候就会去容器中查找单例bean B,发现没有然后就会创建bean B,创建bean B时又发现引用了bean A,这时候又会去容器中查找bean A,发现没有,接下来就会循环重复上面的步骤,这是不是像极了死锁?其实循环依赖就是一个死循环的过程
之前我们在总结Spring扩展点:后置处理器时谈到了Spring Bean的生命周期和其对Spring框架原理理解的重要性,所以接下来我们就来分析一下Bean生命周期的整体流程。首先Bean就是一些Java对象,只不过这些Bean不是我们主动new出来的,而是交个Spring IOC容器创建并管理的,因此Bean的生命周期受Spring IOC容器控制
`Spring`框架中大致提供了以下三个核心后置处理器:**`BeanDefinitionRegistryPostProcessor`,`BeanFactoryPostProcessor`,`BeanPostProcessor`**,其他的后置处理器都是继承自这三个
**条件装配**是`Spring Boot`一大特点,根据是否满足指定的条件来决定是否装配 Bean ,做到了动态灵活性,starter的自动配置类中就是使用@Conditional及其衍生扩展注解@ConditionalOnXXX做到了自动装配的
我们一直在强调`Spring Boot`能成为当下主流首选开发框架的主要原因在于其核心思想:**约定大于配置,自动配置,条件装配**。基于这些特性使得`Spring Boot`集成其他框架非常简单快捷
当下`Spring Boot`之所以能成为主流首选开发框架,得益于其核心思想:**约定大于配置**和`Spring`提供的基于注解配置式开发,解决了繁琐的`XML`文件配置问题,大大提高了开发效率。基于`Spring MVC`三层架构框架开发的项目中大量用到`@Controller, @Service...`等注解,即使这些类在不同包路径下,都能被注入到`Spring`容器中,然后可以相互之间进行依赖注入、使用。这时候就有一个问题了:`Spring`是如何将声明了`@Component`注解的Bean注入到`Spring`容器当中的呢?怎么做到bean的类定义可以随意写在不同包路径下?
`@Import` 是 Spring 基于 Java 注解配置的主要组成部分,`@Import` 注解提供了类似 `@Bean` 注解的功能,向Spring容器中注入bean,也对应实现了与Spring XML中的<import/>元素相同的功能
`@Resource`和`@Autowired`都是实现bean的注入,在日常开发中使用非常频繁,但是使用体验不太一样,笔者喜欢用`@Resource`,因为在使用`@Autowired`时IDEA会出现一些警告爆红提示
随着`Spring Boot`的盛行,注解配置式开发受到了大家的青睐,从此告别了基于`Spring`开发的繁琐`XML`配置。这里先来提纲挈领的了解一下`Spring`内部对于配置注解的定义,如`@Component、@Configuration、@Bean、@Import`等注解,从功能上来讲,这些注解所负责的功能的确不相同,但是
将调度行为抽象形成“调度中心”公共平台,而平台自身并不承担业务逻辑,“**调度中心**”负责发起调度请求。将任务抽象成分散的JobHandler,交由“执行器”统一管理,“**执行器**”负责接收调度请求并执行对应的JobHandler中业务逻辑。因此,“调度”和“任务”两部分可以相互解耦,提高系统整体稳定性和扩展性;
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,是完全由java开发的一个开源的任务日程管理系统,“任务进度管理器”就是一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统。其功能类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能,作为一个优秀的开源调度框架
**AQS: AbstractQueuedSynchronizer**,顾名思义,翻译过来叫**抽象队列同步器**,它是JUC并发编程的**基石**,定义了一套多线程访问共享资源的同步器框架,众多同步类底层都是基于AQS实现的,如常用的ReentrantLock、Semaphore、CountDownLatch等。
在当今高流量、高并发的互联网业务场景下,**并发编程技术**显得尤为重要,不管是哪一门编程语言,掌握并发编程技术是个人进阶的必经之路。时隔一个半月没有写技术博客文章,有点生疏了。。。闲话少叙,接下来我将围绕并发编程知识点进行总结讲解,这里从并发编程入门开始,讲述Java内存模型和并发的三大特性。
License,即版权许可证,一般用于收费软件给付费用户提供的访问许可证明。根据应用部署位置的不同,一般可以分为以下两种情况讨论: - 应用部署在开发者自己的云服务器上,如现在的saas模式的软件供应商就是这样部署的。这种情况下用户通过账号登录的形式远程访问,因此只需要在账号登录的时候校验目标账号的有效期、访问权限等信息即可。 - 应用部署在客户的内网环境,即本地化部署。因为这种情况开发者无法控制客户的网络环境,也不能保证应用所在服务器可以访问外网,因此通常的做法是使用服务器许可文件,在应用启动的时候加载
最近刷抖音发现上线了 **IP** 属地的功能,小伙伴在发表动态、发表评论以及聊天的时候,都会显示自己的 **IP** 属地信息,其核心意义是让用户更具有真实性,减少虚假欺骗事件。正好最近本人开发获取客户端ip,做一些接口限流,黑白名单等需求功能,顺路就研究了一下怎么解析**IP**获取归属地问题。
这里先把问题陈述一下:**springboot整合nacos做配置中心,使用@NacosConfigurationProperties注解注入bean属性,当nacos前台客户端没有配置这个bean的属性,项目
`Eureka` 是一个服务注册与发现框架,是 Netflix 开源的**注册中心**组件,分为 Eureka **Client** 和 Eureka **Server**
Feign是一款Java语言编写的HttpClient绑定器,在Spring Cloud微服务中用于实现微服务之间的声明式调用。Feign 可以定义请求到其他服务的接口,用于微服务间的调用,**不用自己再写http请求(eg:使用spring自带的restTemplate或者httpClinents工具构建http请求调用第三方服务接口**,在客户端实现,调用此接口就像远程调用其他服务一样,当请求出错时可以调用接口的实现类来返回
`动态sql`的一大难点就是根据参数动态生成最终查询sql,我们熟知的orm框架`mybatis`为了实现动态sql能力,引入大量的标签,功能强大的同时也带来使用的复杂性,至于`mybatis-plus`是怎么实现的,大家自行查阅资料,我想复杂性并不低。当然我这里想说一下,全方面完美地实现动态sql本身就是一件复杂的事情。
CompletableFuture由Java 8提供,是实现异步化的工具类,上手难度较低,且功能强大,支持通过函数式编程的方式对各类操作进行组合编排。 CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步[回调](https://so.csdn.net/so/search?q=回调&spm=1001.2101.3001.7020)、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利。
在我们业务系统数据量不大的时候,单库单表完全可以支撑现有业务,数据再大一点搞个MySQL主从同步读写分离也能对付,这时候我们使用数据库自增id就足够了。但随着业务数据日渐增长,主从同步也扛不住了,就需要对数据库进行分库分表,但分库分表后需要有一个唯一ID来标识一条数据,数据库的自增ID显然不能满足需求;还有就是某些场景需要唯一编号标识,比如订单号,用户编号等都需要有`唯一ID`做标识。此时一个能够生成`全局唯一ID`的系统是非常必要的。那么这个`全局唯一ID`就叫`分布式ID`。
根据加锁范围:MySQL里面的锁可以分为:全局锁、表级锁、行级锁
索引是一种能提高数据库查询效率的有序的数据结构。它可以比作一本字典的目录,可以帮你快速找到对应的记录。索引一般存储在磁盘的文件中,它是占用物理空间的
**索引是一种用于快速查询和检索数据的数据结构。常见的索引结构有: B 树, B+树和 Hash** 通常来讲,索引就像一本中华字典的目录,通过目录可以快速定位查找某个汉字在哪一页,如果一页一页去查找某个汉字,效率之慢可想而知。我们可以通过创建索引提高查询速度,创建唯一索引保证字段唯一性,但是创建索引和维护索引需要耗费许多时间。当对表中的数据进行增删改的时候,如果数据有索引,那么索引也需要动态的修改,会降低 SQL 执行效率。索引需要使用物理文件存储,也会耗费一定空间。
Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。 而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。
**Kafka传统定义**:Kafka是一个分布式的基于发布/订阅模式的消息队列(Message Queue),主要应用于大数据实时处理领域。**发布/订阅**:消息的发布者不会将消息直接发送给特定的订阅者,而是将发布的消息 分为不同的类别,订阅者只接收感兴趣的消息。
主从复制是指将一台Redis服务器的数据复制到其他的Redis服务器。前者称为主节点(master/leader) 后者称为从节点(slave/follower) 数据的复制是单向的。只能由主节点到从节点。Master以写为主,Slave 以读为主。
在 Redis 6.0 中,非常受关注的第一个新特性就是多线程。这是因为,Redis 一直被大家熟知的就是它的单线程架构,虽然有些命令操作可以用后台线程或子进程执行(比如数据删除、快照生成、AOF 重写),但是,**从网络 IO 处理到实际的读写命令处理,都是由单个线程完成的**。随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 IO 的处理上,也就是说,单个主线程处理网络请求的速度跟不上底层网络硬件的速度
Redis 的大部分操作在内存上完成,再加上它采用了高效的数据结构,例如哈希表和跳表,这是它实现高性能的一个重要原因。另一方面,就是 Redis 采用了多路复用机制,使其在网络 IO 操作中能并发处理大量的客户端请求,实现高吞吐率
正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。
**幂等性**原本是数学上的概念,即使公式:f(x)=f(f(x)) 能够成立的数学性质。用在编程领域,则意为对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。
MQ:消息队列(message queue): 顾名思义,MQ本质是个队列,FIFO先入先出,只不过队列中存放的内容是message,还是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务。使用了MQ之后,消息发送上游只需要依赖MQ,不用依赖其他服务
操作日志几乎存在于每个系统中,而这些系统都有记录操作日志的一套 API。操作日志和系统日志不一样,操作日志必须要做到简单易懂。所以如何让操作日志不跟业务逻辑耦合,如何让操作日志的内容易于理解,如何让操作日志的接入更加简单?上面这些都是本文要回答的问题。我们主要围绕着如何“优雅”地记录操作日志展开描述,希望对从事相关工作的同学能够有所帮助或者启发。