核心 老博客静态化迁移实战
先说背景:我手上有一个运行多年的个人博客 jackssybin.cn,原来是 Solo/Bolo 系统,通过 Tomcat 部署,数据在 MySQL 里。后来我希望把它迁成一个更轻、更容易维护的静态站:文章继续保留,旧链接尽量不失效,样式尽量接近原站,但不再依赖 Tomcat、后台和数据库服务。 这篇文章记录的是一次完整迁移过程。不是泛泛而谈“把博客迁到静态站”,而是把一个真实的 Solo/Bolo 项目拆开,迁移到 VuePress ...
这里汇总历史技术笔记、专题阅读路线、系统教程和常用导航。可以按最新文章浏览,也可以直接进入教程中心或专题路线学习。
轻量展示,每小时更新:2026/5/30 17:43:32
先说背景:我手上有一个运行多年的个人博客 jackssybin.cn,原来是 Solo/Bolo 系统,通过 Tomcat 部署,数据在 MySQL 里。后来我希望把它迁成一个更轻、更容易维护的静态站:文章继续保留,旧链接尽量不失效,样式尽量接近原站,但不再依赖 Tomcat、后台和数据库服务。 这篇文章记录的是一次完整迁移过程。不是泛泛而谈“把博客迁到静态站”,而是把一个真实的 Solo/Bolo 项目拆开,迁移到 VuePress ...
1.LocalDateTime时间转换 import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; public class DateTimeConversionExample { public static void main(String[] args) { // 获取当前日期时间 LocalDateTime now = LocalDateTime.now(); // 方法1:使用预定义格式器 DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; String isoString = now.format(isoFormatter); System.out.println("ISO格式: " + isoString); // 方法2:自定义格式 DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:....
============================================================================== 按照非索引列更新 在可重复读的事务隔离级别下,在非索引列上进行更新和删除会对所有数据行进行加锁,阻止其他会话对边进行任何数据的增删改操作。 如果更新或删除条件为c3=4且c3列上没有索引则: 不允许其他会话插入任意记录,因为所有记录的主键索引上存在X排他锁,无法申请插入意向X锁(lock_mode X insert intention waiting Record lock) 不允许其他会话删除任意记录,因为所有记录的主键索引上存在X排他锁 不允许其他会话更新任意记录。因为所有记录的主键索引上存在X排他锁 ##=========================================## 测试数据: CREATE TABLE tb4001 ( id bigint(20) NOT NULL AUTO_INCREMENT, c1 int(11) DEFAULT NULL, c2 varchar(200) DEFAULT N....
1.背景 Spring的核心思想就是容器,当容器refresh的时候,外部看上去风平浪静,其实内部则是一片惊涛骇浪,汪洋一片。Springboot更是封装了Spring,遵循约定大于配置,加上自动装配的机制。很多时候我们只要引用了一个依赖,几乎是零配置就能完成一个功能的装配。 我非常喜欢这种自动装配的机制,所以在自己开发中间件和公共依赖工具的时候也会用到这个特性。让使用者以最小的代价接入。想要把自动装配玩的转,就必须要了解spring对于bean的构造生命周期以及各个扩展接口。当然了解了bean的各个生命周期也能促进我们加深对spring的理解。业务代码也能合理利用这些扩展点写出更加漂亮的代码。 在网上搜索spring扩展点,发现很少有博文说的很全的,只有一些常用的扩展点的说明。 所以在这篇文章里,我总结了几乎Spring & Springboot所有的扩展接口,以及各个扩展点的使用场景。并且整理出了一个bean在spring内部从被加载到最后初始化完成所有可扩展点的顺序调用图。从而我们也能窥探到bean是如何一步步加载到spring容器中的。 2.可扩展的接口启动调用顺序图 ....
@ConditionalOnExpression 根据表达式选择性加载 @ConditionalOnProperty 根据配置选择性加载 #消费者总开关,0关1开 mq.cumsumer.enabled=1 #rocketmq消费者开关,true开启,false关闭 rocketmq.comsumer.enabled=false #rabbitmq消费者开关,true开启,false关闭 rabbitmq.comsumer.enabled=true #消费者选择 mq.comsumer=rabbitmq //布尔值和数字 @Component @RabbitListener(queues = "monitorDataQueue") @ConditionalOnExpression("${mq.cumsumer.enabled:0}==1&&${rabbitmq.comsumer.enabled:false}") //字符串 @Component @RabbitListener(queues = "monitorDataQueue") @ConditionalOnExp....
Spring Boot Actuator可以帮助你监控和管理Spring Boot应用,比如健康检查、审计、统计和HTTP追踪等。所有的这些特性可以通过JMX或者HTTP endpoints来获得。 Actuator同时还可以与外部应用监控系统整合,比如 Prometheus, Graphite, DataDog, Influx, Wavefront, New Relic等。这些系统提供了非常好的仪表盘、图标、分析和告警等功能,使得你可以通过统一的接口轻松的监控和管理你的应用。 Actuator使用Micrometer来整合上面提到的外部应用监控系统。这使得只要通过非常小的配置就可以集成任何应用监控系统。 预备知识: | 监控 | micrometer | actuator | promethes | | - | - | - | - | | 概念 | 类似slf4j门面模式。提供接口和方法 | 调用micrometer对springboot体系提供了健康检查,对spring体系的扩展。也可以自定义监控。 | 监控的一类客户端。获取的数据格式需要定制化。micrometer有单独的门面扩....
概念上的区别 反射是在运行状态把Java类中的各种成分映射成相应的Java类,可以动态的获取所有的属性以及动态调用任意一个方法,强调的是运行状态。 内省(IntroSpector) 是Java 语言对 JavaBean(简称VO)类属性、事件的一种缺省处理方法。内省机制是通过反射来实现。例如类User中有属性name,那么必定有getName,setName方法,我们可以通过他们来获取或者设置值。Java提供了一套API来访问某个属性的getter/setter方法,这些API存放在java.beans中。 内省涉及的类 Introspector BeanInfo PropertyDescriptor 实现内省的步骤 通过类 Introspector 的 getBeanInfo方法 来获取某个对象的 BeanInfo 信息。 通过 BeanInfo 来获取属性描述器(PropertyDescriptor)。 通过获取的属性描述器就可以获取某个属性对应的 getter/setter 方法。 通过反射机制调用获取到的getter/setter 方法。 测试User类; /** *....
一、业务场景三 为了便于理解,我们通过一个业务场景来入手。 有一个电商系统架构优化工作,该系统中包含用户和订单2个主要实体,每个实体涵盖数据量如下表所示: 实体数据量增长趋势 用户千万级每日10万 订单亿级每日百万级,后续可能千万级 从上表中发现,目前订单数据量已达上亿,并且每日以百万级速度增长,之后还可能是千万级。 面对如此大的数据量,此时存储订单的数据库竟然还是一个单库单表。对于单库单表而言,一旦数据量实现疯狂增长,无论是IO还是CPU都会扛不住。 为了使系统抗住千万级数据量的压力,各种SQL优化都已经做完,最终确定下来的方式是将订单表拆分,再进行分布存储,这也就是本章我们要讨论的内容——分库分表。 说到分库分表解决方案,我们首先需要做的就是搞定拆分存储的技术选型问题。 二、拆分存储的技术选型 关于拆分存储常用的技术解决方案,市面上目前主要分为4种:MySQL的分区技术、NoSql、NewSQL、基于MySQL的分库分表。 1、MySQL的分区技术 MySQL的分区主要在文件存储层做文章,它可以将一张表的不同存放在不同存储文件中,这对使用者来说比较透明。 在以往的实战项....
业务场景二 某 SaaS 客服系统,系统里有一个工单查询功能,工单表中存放了几千万条数据,且查询工单表数据时需要关联十几个子表,每个子表的数据也是超亿条。 面对如此庞大的数据量,跟前面的冷热分离一样,每次客户查询数据时几十秒才能返回结果,即便我们使用了索引、SQL 等数据库优化技巧,效果依然不明显。 加上工单表中有些数据是几年前的,但是这些数据涉及诉讼问题,需要继续保持更新,因此无法将这些旧数据封存到别的地方,也就没法通过前面的冷热分离方案来解决。 最终采用了查询分离的解决方案,才得以将这个问题顺利解决:将更新的数据放在一个数据库里,而查询的数据放在另外一个系统里。因为数据的更新都是单表更新,不需要关联也没有外键,所以更新速度立马得到提升,数据的查询则通过一个专门处理大数据量的查询引擎来解决,也快速地满足了实际的查询需求。 通过这种解决方案处理后,每次查询数据时,500ms 内就可得到返回结果,客户再也不抱怨了。 通过上面这个例子,大家对查询分离的业务场景已经有了一定认知,但如果想掌握整个业务场景,继续往下看吧。 什么是查询分离? 关于查询分离的概念,从简单的字面意思上也好理解,即每次....
业务场景一 曾经经历过供应链相关的架构优化,当时平台上有一个订单功能,里面的主表有几千万数据量,加上关联表,数据量达到上亿。 这么庞大的数据量,让平台的查询订单变得格外迟缓,查询一次都要二三十秒,而且多点击几次就会出现宕机。比如业务员多次查询时,数据库的 CPU 会立马狂飙,服务器线程也降不下来。 当时,我们尝试了优化表结构、业务代码、索引、SQL 语句等办法来提高响应速度,但这些方法治标不治本,查询速度还是很慢。 考虑到我们手头上还有其他优先级高的需求需要处理,为此,我们跟业务方反馈:“这功能以后你们能不用就不用,暂时先忍受一下。”可经过一段时间后,业务方实在受不了了,直接跟我们放狠话,无奈之下我们屈服了。 最终,我们决定采用一个性价比高的解决方案,简单方便地解决了这个问题。在处理数据时,我们将数据库分成了冷库和热库 2 个库,不常用数据放冷库,常用数据放热库。 通过这样的方法处理后,因为业务员查询的基本是近期常用的数据,常用的数据量大大减少了,就再也不会出现宕机的情况了,也大大提升了数据库响应速度。 其实上面这个方法,就是“冷热分离”。 一、什么是冷热分离 冷热分离就是在处理数据....
ApplicationContext是什么? 简单来说就是Spring中的容器,可以用来获取容器中的各种bean组件,注册监听事件,加载资源文件等功能。 Application Context获取的几种方式 1 直接使用Autowired注入 @Component public class Book1 { @Autowired private ApplicationContext applicationContext; public void show (){ System.out.println(applicationContext.getClass()); } } 2 利用 spring4.3 的新特性 使用spring4.3新特性但是存在一定的局限性,必须满足以下两点: 构造函数只能有一个,如果有多个,就必须有一个无参数的构造函数,此时,spring会调用无参的构造函数 构造函数的参数,必须在spring容器中存在 @Component public class Book2 { private ApplicationContext applicationContext; p....
参考: 一、墨菲定律:如果事情可能出错,它就会出错。 二、布鲁克定律:大部分情况下,为已经延期的软件项目增加人手只会让项目延期得更厉害。 三、霍夫施塔特定律:项目的实际完成时间总是比预期的要长。 四、康威定律:组织所设计的系统的结构受限于组织的通信结构。(如果某人想要改变的东西属于其他人,那么他就很难改变这些东西。根据目标软件架构来组建团队可以更容易实现软件架构,而这就是对抗康威法律的一种有效方式 —— 微服务等架构) 五、 8/2 法则:80%的成果源于 20%的原因。 六、彼得法则:在一个等级制度中,每个员工都倾向于晋升到他无法胜任的职位。 七、摩尔定律:计算机的处理速度每两年翻一番! 八、沃斯定律:软件比硬件更容易变慢。(可以参考摩尔定律) 九、克努特优化法则:过早优化是万恶之源。(先写代码,然后找出瓶颈,最后才优化!) 十、诺维格定律:当一家公司在某个领域的市场占有率超过 50% 以后,将无法再使市场占有率翻番,就必须寻找新的市场。 十一、羊群效应:从众心理 十二、蝴蝶效应:微小的变化能带来巨大的连锁反应 十三、青蛙效应:温水煮青蛙,生于忧患死于安乐 十四、破窗效应:墙倒众人推....
1.pom文件中引用如下 <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency> 2.添加swagger2配置文件 @Configuration @EnableSwagger2 public class SwaggerConfiguration { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.*.controller")) .paths(PathSelectors.any()) .build(); } priv....
一、前言 由于项目中读写分离,或者分库分表导致数据库连接有很多。这个时候我们常常会切换多数据源进行业务的合并。mybatis-plus 团队新增了dynamic-datasource-spring-boot-starter 用来动态切换数据源。 <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.1.0</version> </dependency> 二、配置 2.1pom配置 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <de....
在MySQL 5.7 password字段已从mysql.user表中删除,新的字段名是“authenticalion_string”. 选择数据库:use mysql; 更新root的密码: update user set authentication_string=password('新密码') where user='root' and Host='localhost'; flush privileges;
想要了解反射生成class和创建java对象,首先我们要了解什么是反射? 一、什么是反射? Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。 二、反射生成Class的三种方式 1.第一种方式(利用getClass()方法) User user = new User(); Class class= user.getClass(); 2.第二种方式(直接对象的.class) Class cla = User.class 3.第三种方式(Class.forName()) Class cla = Class.forName("com.jackssy.User"); 注意:此种方法通过对象的全路径来获取Class的,当对象不存在时,会出现ClassNotFoundException异常。详细的可以看下Class.forName()的底层代码。 三、反射生成java对象的两种方式 1.第一种方式newInstance(); 调用public....
先放结果 1000000 行文本读取结果比对: BufferedReader 耗时: 49ms Scanner 耗时: 653ms Apache Commons IO 耗时: 44ms InputStreamReader 耗时: 191ms FileInputStream 耗时: 3171ms BufferedInputStream 耗时: 70ms FileUtils 耗时: 46ms Files 耗时: 99ms 24488656 行文本读取结果比对: BufferedReader 耗时: 989ms Scanner 耗时: 11899ms Apache Commons IO 耗时: 568ms InputStreamReader 耗时: 3377ms FileInputStream 耗时: 78903ms BufferedInputStream 耗时: 1480ms FileUtils 耗时: 16569ms Files 耗时: 25162ms 可见, 当文件较小时: ApacheCommonsIO 流 表现最佳; FileUtils,BufferedReader 居其二;....
问题: 你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题? 分析: 先做一个说明,从理论上来说,有两种处理思维,一种需保证数据强一致性,这样性能肯定大打折扣;另外我们可以采用最终一致性,保证性能的基础上,允许一定时间内的数据不一致,但最终数据是一致的。 1 强一致性思想 这种考虑方式就要用到分布式事务,比如2PC、tcc、Paxos协议等都可以保证一致性。 我们还可以通过读请求和写请求串行化,串到一个内存队列里去。 串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下多几倍的机器去支撑线上的一个请求。 2 最终一致性思想 从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。这种方案下,我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。 那么接下来,我们只需要讨论更新的策略了。 (1)先更新....
Drools语法-Language 关键词 Hard keywords(Cannot use any): true,false,null Soft keywords(avoid use) lock-on-active date-effective date-expires no-loop auto-focus activation-group agenda-group ruleflow-group entry-point duration package import dialect salience enabled attributes rule extend when then template query declare function global eval not in or and exists forall accumulate collect from action reverse result end over init 注释 单行注释 rule "Testing Comments" when // this is a single line com....