Hang's Blog - 经验
type: channel
link: https://hang.domcer.com/index.php/category/%E7%BB%8F%E9%AA%8C/
items: Java Design | AbstractFactory
items: Java Design | AbstractDocument
items: Java | 代码简洁规范
items: 预警方案设计与实现
items: Elasticsearch | 聚合 (按日统计、根据字段去重等)
items: LeetCode | [20]-Easy 给定一个只包括 '('，')'，'{'，'}'，'['，']' 的字符串 s ，判断字符串是否有效
items: 游戏抽卡 | 概率制定分析与实现
items: Quartz | 启动报错: JobStore class 'org.quartz.impl.jdbcjobstore.JobStoreTX' props could not be configured
items: SpringBoot 启动报错 Unable to locate the default servlet for serving static content. Please set the 'defaultServletName' property explicitly.
items: Maven | Unable to make field private com.sun.tools.javac.processing.JavacProcessingEnvironment$DiscoveredProcessors

Java Design | AbstractFactory
type: item
date: 2024-05-29T16:51:00+08:00
description: IntentProvide an interface for creating families of related or dependent objects without specifying[指定] their concrete[具体] classes.ExplanationReal-world exampleImagine a furniture[家具] company that produces various styles of furniture: modern, Victorian[维多利亚时代], and rustic[乡村的]. Each style includes products like chairs, tables, and sofas. To ensure[确保] consistency[一致性] within each style, the company uses an Abstract Factory pattern.In this scenario[脚本], the Abstract Factory is an interface for creating families of related furniture objects (chairs, tables, sofas). Each concrete factory (ModernFurnitureFactory, VictorianFurnitureFactory, RusticFurnitureFactory) implements the Abstract Factory interface and creates a set of products that match the specific style. This way, clients can create a whole set of modern or Victorian furniture without worrying about the details of their instantiation[实例化]. This maintains[保持] a consistent style and allows easy swapping[交换] of one style of furniture for another.In plain wordsA factory of factories; a factory that groups the individual[单独的] but related/dependent factories together without specifying their concrete classes.Wikipedia saysThe abstract factory pattern provides a way to encapsulate[概括、封装] a group of individual factories that have a common theme without specifying their concrete classesProgrammatic ExampleTo create a kingdom we need objects with a common theme. The elven[精灵] kingdom needs an elven king, elven castle, and elven army whereas[而、反之] the orcish[兽人] kingdom needs an orcish king, orcish castle, and orcish army. There is a dependency between the objects in the kingdom.Translating the kingdom example above. First of all, we have some interfaces and implementation for the objects in the kingdom.public interface Castle { String getDescription(); } public interface King { String getDescription(); } public interface Army { String getDescription(); } // Elven implementations -> public class ElfCastle implements Cast…

Java Design | AbstractDocument
type: item
date: 2024-05-28T18:47:00+08:00
description: 意图 Intent抽象文档设计模式是一种结构设计模式，旨在通过为各种文档类型定义公共接口，提供一种一致的方式来处理分层和树状数据结构。它将核心文档结构与特定的数据格式分离，实现动态更新和简化维护。The Abstract Document design pattern is a structural design pattern that aims to provide a consistent way to handle hierarchical and tree-like data structures by defining a common interface for various document types. It separates the core document structure from specific data formats, enabling dynamic updates and simplified maintenance.解释 Explanation抽象文档模式允许处理附加的非静态属性。此模式使用特性的概念来实现类型安全，并将不同类的属性分离到一组接口中。The Abstract Document pattern enables handling additional, non-static properties. This pattern uses concept of traits to enable type safety and separate properties of different classes into set of interfaces.真实世界的例子 Real world example想象一下，在一个图书馆系统中，书籍可以有不同的格式和属性：实体书、电子书和有声读物。每种格式都有独特的属性，如实体书的页数、电子书的文件大小和有声读物的持续时间。摘要文档设计模式允许图书馆系统灵活地管理这些不同的格式。通过使用这种模式，系统可以动态存储和检索属性，而不需要为每种书籍类型提供刚性结构，从而在未来更容易添加新的格式或属性，而不会对代码库进行重大更改。Imagine a library system where books can have different formats and attributes: physical books, eBooks, and audiobooks. Each format has unique properties, such as page count for physical books, file size for eBooks, and duration for audiobooks. The Abstract Document design pattern allows the library system to manage these diverse formats flexibly. By using this pattern, the system can store and retrieve properties dynamically, without needing a rigid structure for each book type, making it easier to add new formats or attributes in the future without significant changes to the codebase.用简单的话说 In plain words抽象文档模式允许在对象不知情的情况下将属性附加到对象。Abstract Document pattern allows attaching properties to objects without them knowing about it.维基百科称 Wikipedia says一种面向对象的结构设计模式，用于在松散类型的键值存储中组织对象，并使用类型化视图公开数据。该模式的目的是在强类型语言中的组件之间实现高度的灵活性，在这种语言中，可以动态地将新属性添加到对象树中，而不会失去类型安全的支持。该模式利用特征将类的不同属性分离到不同的接口中。An object-oriented structural design pattern for organizing objects in loosely typed key-value stores and exposing the data using typed views. The purpose of the pattern is to achieve a hig…

Java | 代码简洁规范
type: item
date: 2024-03-06T11:13:00+08:00
description: 前言做开发也有了一段时间，好的代码和烂的代码也看了很多，所以想记录下来平日里面自己开发的一些规范，方便后续其他人阅读学习或借鉴。之前有读过《代码整洁之道》，感觉受益匪浅，里面讲的很多内容都是日常会犯的一些小毛病，而且书中教了大家该如何让代码看起来更舒服，细节决定了更多事情，如果一个人写代码的风格换来换去，只会让这一片代码受到污染，即使是小的空格和空行，也需要多加注意。先记录下来，慢慢更新吧！或许是强迫症还是洁癖使然，让我对代码规范比较敏感例如这里是否多个空行，那里是否少个空格有问过其他同事，他们貌似对这些并无所谓，觉得能开发出来就好开门见山1. 空格所有特殊符号前后都应有空格，最常见的可以直接使用 idea 自带的代码整理，自动帮忙规范。规定：定义变量的等于号前后都需要有空格循环或者IF这种特殊词与括号都需要有空格来隔离。[scode type="red"]坏示例[/scode]String name="example"; int age= 1; int age =1; if(A ==B) if(A<B)[scode type="green"]好示例[/scode]String name = "example"; int age = 1; if (A == B)2. 类开始和结束的空行有很多人喜欢类开始的时候，直接开始写代码，在结束的时候又莫名奇妙多出来很多空行，亦或是在分割方法的时候没有空行，或者多个空行，这样都会让代码显得很不规范。规定：在新的类写代码时，应先空一行，再开始编写代码在类结束时，应该与上一个最近的方法紧贴，没有空行每个方法之间只且需要一个空行来隔离不同类型的变量应该用空行来隔离变量与方法之间应该用空行来隔离如果出现 List 类型的变量，应该以 xxxList 为命名，取消类似英文复数的定义，比如pages等，这种很容易让人引起歧义如果出现 Map 类型的变量，应该以 xxxMap 为命名[scode type="red"]坏示例[/scode]public class ApiRequestTotal extends BaseBasicDashboardCalculate { private static final String NAME = "example"; private String name; private String type; @Override public Map<String, Object> getConditions() { Map<String, Object> map = new HashMap<>(); map.put("finalDecisionCode", "Accept"); return map; } @Override public Map<String, Object> getNestedConditions() { return new HashMap<>(); } @Override public BasicDashboardMetricTypeEnum getMetricType() { return BasicDashboardMetricTypeEnum.API_REQUEST_TOTAL; } }[scode type="green"]好示例[/scode]public class ApiRequestTotal extends BaseBasicDashboardCalculate { private static final String NAME = "example"; private String name; private String displayName; @Override public Map<String, Object> getConditions() { Map<String, Object> map = new HashMap<>(); map.put("finalDecisionCode", "Accept"); return map; } @Override public Map<String, Object> getNestedConditions() { return new HashMap<>(); } @Override public BasicDashboardMetricTypeEnum getMetricType() { return BasicDashboardMetricTypeEnum.API_REQUEST_TOTAL; } }3. 属性和变量的定义这块的定义其实是很糟糕的，经常看到很多代码，打开一个类的时候，迎头一棒全是定义，导致要一直往下翻找代码，且定义的很不规范，一样类型的内容应该遵守从…

预警方案设计与实现
type: item
date: 2024-03-04T17:21:00+08:00
description: 前言最近成为了 Warning Master (调侃）因为最近接手了3个预警项目，都是要做各个类别数值统计计算指标的预警感觉挺有趣的，就想记录下来这三个项目分别为：某A外企啤酒公司某B国际航空公司某C会员超市公司[scode type="blue"]小小的项目背景[/scode]某A外企啤酒公司项目要做预警是由于在2023年底的时候爆出来了一个生产问题，是由于当时代码分支记录混乱，最后上线的包版本缺少了部分 commit 提交，最后引发了比较惨烈的生产问题，结果是领导也没有深究（毕竟是领导在分支上管理不太好，也没有同步消息，修复的BUG在一个bugfix分支，没有在项目分支上）。紧接着引发了客户投诉，说是出现了好几天的数据异常，导致他们判断有误，所以客户要求我们赠送一个预警系统（简易），以此来兜住这种类似的问题。项目管理说最好以最小的成本完成该内容，甚至没有前端，纯后端进行数据计算统计、触发预警等。开发周期大概5-7天，由我来主导设计架构。在2024年初的时候，某A外企啤酒公司项目又爆了一个生产问题，本次生产事故是产品缺陷，导致报错影响了主流程判断。是由于客户项目是在好几年前买了的，是1.1.0版本，目前我们的版本已经是1.5.4+了，这个问题已经在新版本修复，但是旧版本没有修复，最终导致本次报错。客户认为该内容属于产品缺陷，我们应在修复产品缺陷时回归同步项目代码，公司侧认为只在新版本进行更新和维护，旧版本出现且新版本已修复问题应按需所求去同步。因此客户发起二期预警的需求，本次二期预警改动量较大，影响第一次的整体内容（由于第一次项目经理要求以最简单的方式开发，没有留太多拓展的口子）某B国际航空公司项目是客户购买的看板趋势产品，要求携带预警功能，该预警功能设计架构与某A外企啤酒公司项目又不一致，且我在有一段时间既参加A有参加B的开发任务，导致思维也比较跳跃。某C会员超市公司项目，目前主管只让我评估了对应的工作量，还没有进行开发，所以目前也没有开始设计。方案设计下面我会讲解两种方案设计，分别是 某A外企啤酒公司 与 某B国际航空公司 项目现在的预警方案设计。他们其中有一些共同点，也有一些不同的底层设计，具体是由于需求所导向。某A外企啤酒公司每天数据量: 25w+数据来源: Elasticsearch需求说明: 支持天类型与小时类型的预警 （天类型：当前天开始至目前时间的数据量 与 过去七天均值进行对比，看环比上升或下降率是否满足预警要求；小时类型：当前小时段与过去七天该小时段均值进行对比，看环比上升或下降率是否满足预警要求）额外补充: 没有前端页面展示，需要手动在某配置中写 Json，仅后端去做数据计算和触发[scode type="green"]说明[/scode]这个项目主要就是两个定时任务来运转整体预警系统的，定时任务分别去做存储和触发两个事情。我们的数据来源基本上是通过读取 Elasticsearch 获取的，在读取数据时已经进行了大量的聚合操作，并在返回结果后再进行一次加工。由于该项目预警需求最小时间片是按照小时进行对比的，所以在读取 Elasticsearch 数据后，可以按照 1 小时分片单位将对应的数据存入 MySQL 中。在触发预警的定时任务中，因为分成天类型与小时类型，我将其分成了两部分，该小时的数据实时的从 Elasticsearch 中读取，该小时之前的数据从 MySQL 中读取，最后将两个数据进行合并以及加工，再进行对比，计算出最终结果。至于为什么分着读取，是由于如果一直实时的去操作 Elasticsearch 可能会对其产生一些影响，且这些数据在上个小时后就已经结束了使命，是一个"死数据"，完全可以存储到 MySQL 当中，为之后预警的其他操作提供一个稳定的数据来源。这种形式也可以加快预警触发的效率，如果在 Elasticsearch 中数据量很大，并且做了极多的 Aggregation 聚合操作，会导致相应慢、节点阻塞等情况。在后续的 MySQL 中，可以定期清理往期数据，保证一个清洁的数据源。由于没有前端的交互，整个流程的触发和操作基本上都是从后端角度去考虑的，包括后续对读取数据行为的优化。整体开发所需的工作量并不是很多，但是一些细节仍需多加考虑。在最终的预警通知中，也应提供高拓展、多可用的方式，或许以后会兼容 邮箱、钉钉、AI电话、短信等方式，对不同的通知人群进行不同的预警方式。其实在这个设计之初我还设计了类似群组用户之类的功能，目前的预警，只要其中一个指标触发了阈值、满足条件后，就会推送给所有配置好的“推送人”，其实这里应该要去分开，不同的预警配置推送给不同的用户，并且应新增一个用户群组，例如运维组、管理组，可以设置全局推送等权限；部分某项目上的人员可以单独对某指标或多个指标进行监控。可惜该功能…

Elasticsearch | 聚合 (按日统计、根据字段去重等)
type: item
date: 2023-12-08T15:07:00+08:00
description: 前言最近在公司做了一个类似大盘、看板的功能，所有的数据全部存储在ES里面，完全需要自己去ES里面捞数据，并没有一个结构化、过滤好的数据源，完全需要自己对ES的数据进行过滤、筛选、统计，所以就需要用到聚合了，下面是功能图，在图里面其实展示了很多重要信息根据时间范围、其他条件进行筛选数据指标数据以每天为单位进行展示统计所有数据，变成总量数据这里就需要考验到对ES聚合的使用了，因为不可能查询每个指标，都访问一遍ES，不仅指标多，指标也要根据日期进行分桶，不可能每次都发一次请求，即便是多线程，那也对ES压力太大了，使用了过多的网络资源来满足需求，况且时间范围越多，请求次数也越多，所以很不合理这个时候就体现出来聚合的重要性了，ES底层自己支持大量的聚合方式，可以满足按天为单位聚合数据，展示每天数据量有多少，也可以在其上层进行过滤，展示某个数据每天有多少 （不得不说，这简直太棒了）底层的指标都是我来计算提供数据的，所以对这块很熟，也使用了策略设计模式去做这个内容，满足其拓展性聚合是什么首先要明白，聚合是什么聚合分析是数据库中重要的功能特性，完成对一个查询的数据集中数据的聚合计算，如：找出某字段（或计算表达式的结果）的最大值、最小值，计算和、平均值等。ES作为搜索引擎兼数据库，同样提供了强大的聚合分析能力。对一个数据集求最大、最小、和、平均值等指标的聚合，在ES中称为指标聚合(metric)而关系型数据库中除了有聚合函数外，还可以对查询出的数据进行分组group by，再在组上进行指标聚合。在ES中group by称为分桶，桶聚合bucketing通过聚合，我们可以统计出绝大部分的数据，也可以查出来自己想要的指标，满足其使用性用法按天聚合数据{ "aggs": { "daily_hits_true": { "filter": { "bool": { "must": [ { "range": { "statDate": { "gte": 1700668800000, "lt": 1701359999000 } } }, { "nested": { "path": "searchFields", "query": { "bool": { "must": [ { "term": { "searchFields.name": "C_S_TDRESULT" } }, { "term": { "searchFields.value.raw": "true" } } ] } } } } ] } }, "aggs": { "daily_hits": { "date_histogram": { "field": "statDate", "interval": "1d", "format": "yyyy-MM-dd" } } } } } }上面的聚合就是一个非常简单的例子，我根据时间范围(statDate)进行筛选数据，判断C_S_TDRESULT是否为true进行过滤最后添加一个 daily_hits 聚合条件统计出每天的数据这里的聚合使用的是ES的 date_histogram，他可以按照指定时间范围的内容进行统计，例如几小时，也是完全可以的field: 根据哪个字段进行统计interval: 时间周期是多少，1d就是按照一天为单位统计数据format: 展示出来的时间格式是什么，如果按照1小时统计，需要填写小时的单位，需求是按照天统计，所以不关注时分秒的内容去重聚合去重聚合顾名思义，假如你数据存在一个 PHONE_NUMBER 字段第1条数据: PHONE_NUMBER = qwe第2条数据: PHONE_NUMBER = abc第3条数据: PHONE_NUMBER = qwe最终去重聚合统计出来的结果是2，这个2的数据就是聚合后的结果{ "aggs": { "daily_hits": { "date_histogram": { "field": "statDate", "interval": "1d", "format": "yyyy-MM-dd" }, "aggregations": { "searchFields": { "nested": { "path": "searchFields" }, "aggregations": { "relativeInAcNumFilter": { "filter": { "bool": { "must": [ { "term": { "searchFields.name": "S_S_MOBINO" } } ] } }, "aggregations": { "relativeInAcNum": { "cardinality": { "field": "searchFields.value.raw" } } }…

LeetCode | [20]-Easy 给定一个只包括 '('，')'，'{'，'}'，'['，']' 的字符串 s ，判断字符串是否有效
type: item
date: 2023-12-08T15:03:00+08:00
description: 题目/** * @Author hang.wang * @Date 2023/11/24 10:22 * * 给定一个只包括 '('，')'，'{'，'}'，'['，']' 的字符串 s ，判断字符串是否有效。 * * 有效字符串需满足： * * 左括号必须用相同类型的右括号闭合。 * 左括号必须以正确的顺序闭合。 * 每个右括号都有一个对应的相同类型的左括号。 * * * 示例 1： * 输入：s = "()" * 输出：true * * 示例 2： * 输入：s = "()[]{}" * 输出：true * * 示例 3： * 输入：s = "(]" * 输出：false * * 提示： * 1 <= s.length <= 104 * s 仅由括号 '()[]{}' 组成 */代码public class Class20 { public static void main(String[] args) { Class20 class20 = new Class20(); System.out.println(class20.isValid("([)]")); System.out.println(class20.isValid("()")); System.out.println(class20.isValid("()[]{}")); System.out.println(class20.isValid("(]")); } public char exchange(char c) { switch (c) { case '(': return ')'; case ')': return '('; case '[': return ']'; case ']': return '['; case '{': return '}'; case '}': return '{'; } return ' '; } public boolean isValid(String s) { if (s.length() % 2 != 0) { return false; } if (s.length() == 1) { if (isPost(s.charAt(0))) { return false; } } Deque<Character> queue = new ArrayDeque<>(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (isPost(c)) { try { Character remove = queue.removeFirst(); if (exchange(c) != remove) { return false; } } catch (Exception e) { return false; } } else { queue.addFirst(c); } } return queue.isEmpty(); } public boolean isPost(char c) { switch (c) { case ')': case ']': case '}': return true; } return false; } }解析利用堆栈的特性，如果判断是前括号，就存进去，如果是后括号，就取出来一个值，然后判断这俩是否匹配

游戏抽卡 | 概率制定分析与实现
type: item
date: 2023-08-14T09:22:00+08:00
description: 前言众所周知，市面上越来越多的游戏都出了抽卡/抽奖类商业化付费内容，这些往往可以带来不小的一笔收益，这其中的原理却值得让人分析，这并不是一个简单的数值概率抽奖概念，里面更深藏着很多值得深究的东西。特点要有保底大部分抽奖游戏，并不是告知具体概率后，玩家群体就要一味的抽下去，里面包含着保底概念，虽然氪佬很有钱，但是他们也不是傻子，没有任何可以保底的机制，就像无底洞一样，在尝试了成百上千次之后，还是没有抽到自己心爱的道具，他们也只会留下一句啥b游戏市面上其实有很多不错的例子：很多日系游戏：大多数都有100发或200发必中米哈游等系列游戏：90发小保底，180发大保底，小保底的意思是必出金，大保底的意思是必出当期UP角色说实话在好几年前的游戏里面，貌似并没有保底这个概念，不知道从何而起出现了保底概念，大概是二次元游戏入军国内的时候，引发的突变，还依稀记得小时候玩CF的时候，抽奖的箱子真是纯随机要有限期首先要探讨为什么要有期限，如果一个宝箱、抽奖卡池一直都在，那么就会给玩家一种错觉诶？我好像什么时候抽都可以，反正他一直在，什么时候有足够的资金，再去抽奖所以，卡池里面的物品一定是要有期限的，可以进行周期性轮换，这样当某个特殊道具出现的时候，即使用户当时没有足够的资金支持，他也会去想办法获得这个内容，就像王者荣耀等。但是感觉这些年，王者荣耀的皮肤越出越多，已经脱离了原本的内容，很多角色、皮肤甚至都画崩了，还是比较失望的要有限定限定顾名思义，可以出现1次，也可以出现2次，但是出现的时间一定是少中又少，并且在卡池出来之前就大肆宣传，让用户群体感知到这个卡池内容特别珍贵要有兑换现在很多抽奖卡池，在抽奖后都会累计抽奖次数，当抽奖次数满足一定情况后，可以拿获取的点数兑换相应自定义道具概率计算概率一直是一个很玄学的东西，表面上很多游戏厂商写着出现某个特殊道具的概率是多少，但是到自己的身上是不是感觉概率很低，但是看到身边很多人都可以抽到，又感觉莫名其妙，有被冒犯到的感觉。其实很多游戏厂商，表面上给用户群体展示的抽奖概率并不是真实的，但是这个真实性，某种意义上来说，确实是对劲的例如有一些抽奖方式喜欢限定道具发放，在一个时间段期间，道具一共会有100个，在最开始的时间段抽奖，是正常的概率，但是当特殊道具被其他用户抽走后，别人再怎么抽，都是抽不出来的也有一些游戏厂商进行动态平衡，因为抽奖还是太随机，只能说抽奖次数越高，越能体现出来中奖的概率是否符合，所以很多游戏厂商为了防止真的出现很多离谱抽奖概率的存在，他们动态的去统计抽奖概率，如果中奖的概率偏高，那么一定时间段期限就抽不到内容，换句话来说，他们根据当前的概率进行动态平衡调整，保证ALL数据满足给用户呈现的抽奖概率所以，能看见真纯随机概率的抽奖越来越少，即使概率放的很低，很多厂商也会觉得要去做防止离谱中奖的情况，对于用户群体就不是很友好了，但是可以有效的避免，大量出现中奖人数的情况

Quartz | 启动报错: JobStore class 'org.quartz.impl.jdbcjobstore.JobStoreTX' props could not be configured
type: item
date: 2023-06-27T13:49:00+08:00
description: 堆栈如下Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schedulerFactoryBean' defined in class path resource [cn/tongdun/fangsheng/schedule/config/ScheduleConfig.class]: Invocation of init method failed; nested exception is org.quartz.SchedulerException: JobStore class 'org.quartz.impl.jdbcjobstore.JobStoreTX' props could not be configured. [See nested exception: java.lang.NoSuchMethodException: No setter for property 'txIsolationLevelReadCommitted'] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657) ... 48 common frames omitted Caused by: org.quartz.SchedulerException: JobStore class 'org.q…

SpringBoot 启动报错 Unable to locate the default servlet for serving static content. Please set the 'defaultServletName' property explicitly.
type: item
date: 2023-06-21T16:15:00+08:00
description: 2023-06-21 16:07:18.705+0800 [] [main] WARN org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultServletHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'defaultServletHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: Unable to locate the default servlet for serving static content. Please set the 'defaultServletName' property explicitly.主要来说就是这一段 Unable to locate the default servlet for serving static content. Please set the 'defaultServletName' property explicitly.这是由于我升级了SpringBoot的版本，Servlet提供的DefaultServlet不自动注册，需要在 application.properties配置server.servlet.register-default-servlet=true

Maven | Unable to make field private com.sun.tools.javac.processing.JavacProcessingEnvironment$DiscoveredProcessors
type: item
date: 2023-06-16T13:57:00+08:00
description: 今天使用maven打包的时候出现了报错Unable to make field private com.sun.tools.javac.processing.JavacProcessingEnvironment$DiscoveredProcessors com.sun.tools.javac.processing.JavacProcessingEnvironment.discoveredProcs accessible: module jdk.compiler does not "opens com.sun.tools.javac.processing" to unnamed module @654c7d2d这是由于使用了高版本java导致的，由于这个项目是刚刚从git拽下来的，使用的java还是默认的17，改成java8即可