<?xml version="1.0" encoding="utf-8"?>
<ul>
  <li>阿里巴巴Java代码规范（即《阿里巴巴Java开发手册》）是一套由阿里巴巴集团总结并发布的Java编程最佳实践，旨在提升代码质量、可读性、可维护性和团队协作效率。该规范已被广泛应用于国内众多企业和开发团队，并被集成到IDE工具中（如AlibabaJavaCodingGuidelines插件）。</li>
  <li>LRU（LeastRecentlyUsed，最近最少使用）是一种常用的缓存淘汰算法。在Java中实现LRU算法，通常结合哈希表（HashMap）和双向链表（DoublyLinkedList）来实现，以保证get和put操作的时间复杂度为O(1)。Java中可以通过继承LinkedHashMap快速实现，也可以手动实现一个完整的LRU缓存类。下面分别介绍这两种方式。✅方法一：继承LinkedHashMap（简单实现）importjava.util.LinkedHashMap;importjava.util.Map;publicclassLRUCache&lt;K,V&gt;extendsLinkedHashMap&lt;K,V&gt;{privatefinalintcapacity;publicLRUCache(intcapacity){//accessOrder=true表示按访问顺序排序（LRU关键）super(capacity,0.75f,true);this.capacity=capacity;}//重写removeEldestEntry方法，控制缓存容量@OverrideprotectedbooleanremoveEldestEntry(Map.Entry&lt;K,V&gt;eldest){returnsize()&gt;capacity;}//示例使用publicstaticvoidmain(String[]args){LRUCache&lt;Integer,String&gt;cache=newLRUCache&lt;&gt;(3);cache.put(1,"A");cache.put(2,"B");cache.put(3,"C");System.out.println(cache.keySet());//[1,2,3]cache.get(2);//访问2System.out.println(cache.keySet());//[1,3,2]（2移动到末尾）cache.put(4,"D");//超出容量，淘汰最久未使用的1System.out.println(cache.keySet());//[3,2,4]}}⚠️注意：这种方式虽然简洁，但不适用于高并发场景（因为LinkedHashMap不是线程安全的），且对细节控制较少。✅方法二：手写LRU（推荐，理解原理）importjava.util.HashMap;importjava.util.Map;classLRUCacheManual{staticclassNode{intkey;intvalue;Nodeprev;Nodenext;publicNode(intkey,intvalue){this.key=key;this.value=value;}}privatefinalintcapacity;privatefinalMap&lt;Integer,Node&gt;cache;privatefinalNodehead;privatefinalNodetail;publicLRUCacheManual(intcapacity){this.capacity=capacity;this.cache=newHashMap&lt;&gt;();this.head=newNode(-1,-1);this.tail=newNode(-1,-1);head.next=tail;tail.prev=head;}//获取缓存publicintget(intkey){if(!cache.containsKey(key)){return-1;}Nodenode=cache.get(key);moveToHead(node);//访问后移到头部returnnode.value;}//添加缓存publicvoidput(intkey,intvalue){if(cache.containsKey(key)){Nodenode=cache.get(key);node.value=value;moveToHead(node);}else{NodenewNode=newNode(key,value);cache.put(key,newNode);addToHead(newNode);if(cache.size()&gt;capacity){NodetailNode=removeTail();cache.remove(tailNode.key);}}}privatevoidaddToHead(Nodenode){node.prev=head;node.next=head.next;head.next.prev=node;head.next=node;}privatevoidremoveNode(Nodenode){node.prev.next=node.next;node.next.prev=node.prev;}privatevoidmoveToHead(Nodenode){removeNode(node);addToHead(node);}privateNoderemoveTail(){Nodelast=tail.prev;removeNode(last);returnlast;}}//示例使用publicclassMain{publicstaticvoidmain(String[]args){LRUCacheManualcache=newLRUCacheManual(2);cache.put(1,1);cache.put(2,2);System.out.println(cache.get(1));//返回1，访问后移到头部cache.put(3,3);//淘汰key=2（最久未使用）System.out.println(cache.get(2));//返回-1（已淘汰）cache.put(4,4);//淘汰key=3System.out.println(cache.get(1));//返回-1System.out.println(cache.get(3));//返回-1System.out.println(cache.get(4));//返回4}}🔍实现原理说明：双向链表：维护访问顺序，最近使用的放在链表头，最久未使用的在链表尾。HashMap：实现O(1)的快速查找。put/get操作：get：若存在，返回值并移到链表头；否则返回-1。put：若已存在，更新值并移到头部；若不存在，插入头部，超出容量时删除尾部节点。✅进阶建议（生产环境）：使用ConcurrentHashMap和synchronized或ReentrantLock实现线程安全。可结合volatile或ThreadLocal优化性能。考虑使用java.util.concurrent包下的结构，或直接使用GuavaCache等成熟库。📦实际项目推荐（使用Guava）：importcom.google.common.cache.CacheBuilder;importcom.google.common.cache.Cache;Cache&lt;Integer,String&gt;cache=CacheBuilder.newBuilder().maximumSize(100).expireAfterWrite(10,TimeUnit.MINUTES).build();cache.put(1,"A");Stringvalue=cache.getIfPresent(1);GuavaCache内置了LRU机制，功能更强大，适合生产环境。✅线程安全的LRUCache实现（基于双向链表+HashMap+ReentrantLock）importjava.util.HashMap;importjava.util.Map;importjava.util.concurrent.locks.ReentrantReadWriteLock;publicclassThreadSafeLRUCache&lt;K,V&gt;{staticclassNode&lt;K,V&gt;{Kkey;Vvalue;Node&lt;K,V&gt;prev;Node&lt;K,V&gt;next;publicNode(Kkey,Vvalue){this.key=key;this.value=value;}}privatefinalintcapacity;privatefinalMap&lt;K,Node&lt;K,V&gt;&gt;cache;privatefinalNode&lt;K,V&gt;head;privatefinalNode&lt;K,V&gt;tail;privatefinalReentrantReadWriteLocklock=newReentrantReadWriteLock();privatefinalReentrantReadWriteLock.ReadLockreadLock=lock.readLock();privatefinalReentrantReadWriteLock.WriteLockwriteLock=lock.writeLock();publicThreadSafeLRUCache(intcapacity){if(capacity&lt;=0){thrownewIllegalArgumentException("Capacitymustbepositive");}this.capacity=capacity;this.cache=newHashMap&lt;&gt;();this.head=newNode&lt;&gt;(null,null);this.tail=newNode&lt;&gt;(null,null);head.next=tail;tail.prev=head;}//获取缓存值publicVget(Kkey){readLock.lock();try{Node&lt;K,V&gt;node=cache.get(key);if(node==null){returnnull;}//移动到头部（最近使用）moveToHead(node);returnnode.value;}finally{readLock.unlock();}}//添加缓存值publicvoidput(Kkey,Vvalue){if(key==null||value==null){return;}writeLock.lock();try{if(cache.containsKey(key)){//更新值并移到头部Node&lt;K,V&gt;node=cache.get(key);node.value=value;moveToHead(node);}else{//创建新节点Node&lt;K,V&gt;newNode=newNode&lt;&gt;(key,value);cache.put(key,newNode);addToHead(newNode);//超出容量，移除尾部节点if(cache.size()&gt;capacity){Node&lt;K,V&gt;tailNode=removeTail();cache.remove(tailNode.key);}}}finally{writeLock.unlock();}}//移除节点privatevoidremoveNode(Node&lt;K,V&gt;node){node.prev.next=node.next;node.next.prev=node.prev;}//添加到头部privatevoidaddToHead(Node&lt;K,V&gt;node){node.next=head.next;node.prev=head;head.next.prev=node;head.next=node;}//移动到头部privatevoidmoveToHead(Node&lt;K,V&gt;node){removeNode(node);addToHead(node);}//移除尾部节点（最久未使用）privateNode&lt;K,V&gt;removeTail(){Node&lt;K,V&gt;last=tail.prev;removeNode(last);returnlast;}//获取当前缓存大小（线程安全）publicintsize(){readLock.lock();try{returncache.size();}finally{readLock.unlock();}}//清空缓存（可选）publicvoidclear(){writeLock.lock();try{cache.clear();head.next=tail;tail.prev=head;}finally{writeLock.unlock();}}}🧪使用示例publicclassMain{publicstaticvoidmain(String[]args){ThreadSafeLRUCache&lt;Integer,String&gt;cache=newThreadSafeLRUCache&lt;&gt;(2);cache.put(1,"A");cache.put(2,"B");System.out.println(cache.get(1));//输出:Acache.put(3,"C");//淘汰key=2System.out.println(cache.get(2));//输出:null（已被淘汰）System.out.println(cache.get(3));//输出:CSystem.out.println(cache.get(1));//输出:Acache.put(4,"D");//淘汰key=1System.out.println(cache.get(1));//输出:nullSystem.out.println(cache.get(4));//输出:D}}🔐线程安全说明：使用ReentrantReadWriteLock：get操作使用读锁，允许多个线程并发读。put、remove、结构变更操作使用写锁，独占访问。读写分离，提高并发性能。所有共享状态（cache、链表结构）在读写时都被锁保护。🚀生产建议：如果你使用的是Java8+并追求高性能，也可以考虑使用synchronized方法+ConcurrentHashMap，在简单场景下性能更好。更推荐使用GuavaCache或Caffeine，它们是线程安全、高性能、支持过期策略、权重控制等的成熟缓存库。📦使用Caffeine（推荐生产环境）importcom.github.benmanes.caffeine.cache.Cache;importcom.github.benmanes.caffeine.cache.Caffeine;Cache&lt;Integer,String&gt;cache=Caffeine.newBuilder().maximumSize(100).build();cache.put(1,"A");Stringvalue=cache.getIfPresent(1);Caffeine是目前Java领域最优秀的本地缓存库，基于改进的LRU算法，线程安全，性能优于Guava。✅终极版LRUCache（基于Caffeine）1.添加依赖（Maven）&lt;dependency&gt;&lt;groupId&gt;com.github.benmanes.caffeine&lt;/groupId&gt;&lt;artifactId&gt;caffeine&lt;/artifactId&gt;&lt;version&gt;3.1.8&lt;/version&gt;&lt;!--推荐使用最新稳定版--&gt;&lt;/dependency&gt;2.完整实现代码importcom.github.benmanes.caffeine.cache.Cache;importcom.github.benmanes.caffeine.cache.Caffeine;importcom.github.benmanes.caffeine.cache.RemovalListener;importjava.util.concurrent.ConcurrentMap;importjava.util.concurrent.TimeUnit;publicclassUltimateLRUCache&lt;K,V&gt;{privatefinalCache&lt;K,V&gt;cache;//构造函数：可配置最大容量、过期时间、移除监听等publicUltimateLRUCache(intmaximumSize,longexpireAfterWriteSeconds,longexpireAfterAccessSeconds){Caffeine&lt;K,V&gt;caffeine=Caffeine.newBuilder().maximumSize(maximumSize)//最大缓存条目数（基于LRU回收）//写入后过期（可选）.expireAfterWrite(expireAfterWriteSeconds,TimeUnit.SECONDS)//访问后过期（可选，与expireAfterWrite二选一或同时使用）.expireAfterAccess(expireAfterAccessSeconds,TimeUnit.SECONDS)//使用弱引用键（可选：允许GC回收不再使用的键）.weakKeys()//使用软引用值（可选：内存不足时自动回收，避免OOM）//.softValues()//开启统计功能（命中率、加载次数等）.recordStats()//缓存项被移除时的监听（可用于日志、清理资源等）.removalListener((RemovalListener&lt;K,V&gt;)(key,value,cause)-&gt;{System.out.println("Key"+key+"removed,value:"+value+",cause:"+cause);});this.cache=caffeine.build();}//获取缓存值publicVget(Kkey){returncache.getIfPresent(key);}//写入缓存publicvoidput(Kkey,Vvalue){cache.put(key,value);}//移除缓存publicvoidinvalidate(Kkey){cache.invalidate(key);}//清空所有缓存publicvoidinvalidateAll(){cache.invalidateAll();}//获取统计信息（命中率、请求次数等）publicStringstats(){returncache.stats().toString();}//获取当前缓存大小publiclongsize(){returncache.estimatedSize();}//获取缓存map的快照（不可变）publicConcurrentMap&lt;K,V&gt;asMap(){returncache.asMap();}}3.使用示例publicclassMain{publicstaticvoidmain(String[]args)throwsInterruptedException{UltimateLRUCache&lt;Integer,String&gt;cache=newUltimateLRUCache&lt;&gt;(3,10,15);cache.put(1,"A");cache.put(2,"B");cache.put(3,"C");System.out.println(cache.get(1));//Acache.put(4,"D");//超出容量，自动淘汰最久未使用的（如2）System.out.println(cache.get(2));//null（被淘汰）System.out.println(cache.get(3));//CSystem.out.println(cache.get(4));//D//模拟访问1，延长其生命周期System.out.println(cache.get(1));//A//输出缓存统计信息System.out.println("Stats:"+cache.stats());System.out.println("Size:"+cache.size());//查看当前缓存中的键值System.out.println("Currententries:"+cache.asMap());}}🚀特性说明（“终极”理由）特性说明线程安全Caffeine内部使用高性能并发结构，天然支持多线程LRU回收策略默认基于最近最少使用淘汰，自动管理过期机制支持写入后过期（expireAfterWrite）、访问后过期（expireAfterAccess）弱引用键.weakKeys()：键可被GC回收，避免内存泄漏软引用值.softValues()：值在内存不足时被回收（可选，慎用）统计功能监控命中率、未命中、淘汰数量等异步加载（可扩展）可结合AsyncCache实现异步加载高性能比Guava和ConcurrentHashMap更快，尤其在高并发下🔧高级扩展：支持自动加载（LoadingCache）importcom.github.benmanes.caffeine.cache.LoadingCache;LoadingCache&lt;Integer,String&gt;loadingCache=Caffeine.newBuilder().maximumSize(100).expireAfterWrite(10,TimeUnit.MINUTES).recordStats().build(key-&gt;"default-"+key);//自动加载函数Stringvalue=loadingCache.get(1);//如果不存在，自动加载📊输出示例（stats）CacheStats{hitCount=3,missCount=1,hitRate=0.750,...}可用于监控缓存效率。✅推荐配置组合（生产常用）Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(5,TimeUnit.MINUTES).recordStats().weakKeys()//键用弱引用.build();⚠️注意：不要同时使用softValues()和weakValues()，推荐优先使用maximumSize+expireAfterWrite控制内存。✅SpringBoot2.6+集成Caffeine缓存第一步：添加依赖（Maven）&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-cache&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;com.github.benmanes.caffeine&lt;/groupId&gt;&lt;artifactId&gt;caffeine&lt;/artifactId&gt;&lt;/dependency&gt;第二步：启用缓存（SpringBoot主类）importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cache.annotation.EnableCaching;@SpringBootApplication@EnableCaching//开启缓存注解支持publicclassCacheApplication{publicstaticvoidmain(String[]args){SpringApplication.run(CacheApplication.class,args);}}第三步：配置Caffeine缓存管理器importcom.github.benmanes.caffeine.cache.Caffeine;importcom.github.benmanes.caffeine.cache.RemovalListener;importorg.springframework.cache.CacheManager;importorg.springframework.cache.annotation.EnableCaching;importorg.springframework.cache.caffeine.CaffeineCache;importorg.springframework.cache.support.SimpleCacheManager;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjava.util.Arrays;importjava.util.concurrent.TimeUnit;@Configuration@EnableCachingpublicclassCaffeineCacheConfig{@BeanpublicCacheManagercacheManager(){SimpleCacheManagercacheManager=newSimpleCacheManager();CaffeineCacheuserCache=buildCache("userCache",100,10,15);CaffeineCacheorderCache=buildCache("orderCache",50,5,10);cacheManager.setCaches(Arrays.asList(userCache,orderCache));returncacheManager;}privateCaffeineCachebuildCache(Stringname,intmaxSize,longexpireAfterWrite,longexpireAfterAccess){returnnewCaffeineCache(name,Caffeine.newBuilder().maximumSize(maxSize).expireAfterWrite(expireAfterWrite,TimeUnit.MINUTES).expireAfterAccess(expireAfterAccess,TimeUnit.MINUTES).weakKeys().recordStats().removalListener((RemovalListener&lt;Object,Object&gt;)(key,value,cause)-&gt;{System.out.println("Cache["+name+"]-Key:"+key+",removeddueto:"+cause);}).build());}}上面配置了两个缓存区：userCache和orderCache，可根据业务隔离。第四步：在Service中使用缓存注解importorg.springframework.cache.annotation.Cacheable;importorg.springframework.cache.annotation.CachePut;importorg.springframework.cache.annotation.CacheEvict;importorg.springframework.stereotype.Service;@ServicepublicclassUserService{//从缓存中获取，key=userId@Cacheable(value="userCache",key="#userId")publicStringgetUserById(IntegeruserId){System.out.println("LoadinguserfromDB:"+userId);//模拟数据库查询return"User-"+userId;}//更新缓存@CachePut(value="userCache",key="#userId")publicStringupdateUser(IntegeruserId,Stringname){System.out.println("Updatinguser:"+userId+"-&gt;"+name);returnname;}//删除缓存@CacheEvict(value="userCache",key="#userId")publicvoiddeleteUser(IntegeruserId){System.out.println("Evictinguser:"+userId);}//清空整个缓存区@CacheEvict(value="userCache",allEntries=true)publicvoidclearUserCache(){System.out.println("CleareduserCache");}}第五步：测试使用（Controller示例）importorg.springframework.web.bind.annotation.*;importorg.springframework.beans.factory.annotation.Autowired;@RestControllerpublicclassUserController{@AutowiredprivateUserServiceuserService;@GetMapping("/user/{id}")publicStringgetUser(@PathVariableIntegerid){returnuserService.getUserById(id);}@PutMapping("/user/{id}")publicStringupdateUser(@PathVariableIntegerid,@RequestParamStringname){returnuserService.updateUser(id,name);}@DeleteMapping("/user/{id}")publicvoiddeleteUser(@PathVariableIntegerid){userService.deleteUser(id);}@DeleteMapping("/cache/clear")publicStringclearCache(){userService.clearUserCache();return"Usercachecleared";}}第六步：查看缓存统计信息（可选监控）@AutowiredprivateCacheManagercacheManager;@GetMapping("/cache/stats")publicStringgetStats(){org.springframework.cache.Cachecache=cacheManager.getCache("userCache");if(cache!=null&amp;&amp;cache.getNativeCache()instanceofcom.github.benmanes.caffeine.cache.Cache){varnativeCache=(com.github.benmanes.caffeine.cache.Cache&lt;?,?&gt;)cache.getNativeCache();returnnativeCache.stats().toString();//命中率、淘汰数等}return"Cachenotfoundorstatsnotavailable";}配置说明（推荐用于生产）配置项推荐值说明maximumSize1000~10000控制内存使用，LRU自动淘汰expireAfterWrite5~30分钟写入后过期，适合数据变更频繁场景expireAfterAccess10~20分钟最后一次访问后过期weakKeys()✅推荐避免键导致内存泄漏recordStats()✅推荐通过.stats()监控性能removalListener可选可用于清理资源或打日志🧩高级：使用application.yml配置（可选）虽然Caffeine不支持全配置化，但你可以通过@ConfigurationProperties自定义配置，实现动态化。例如：cache:caffeine:user:maximum-size:100expire-after-write:10expire-after-access:15再通过@ConfigurationProperties注入配置，动态创建缓存。</li>
  <li>事务的启动机制在进入@Transactional注解方法后即获取数据库连接，待首次数据库操作时开启事务，方法结束后提交或回滚并归还连接。但当方法处理时间较长时候，由于方法不能快速结束，导致数据库连接池无法及时归还，极易出现应用耗尽数据库连接池问题，进而导致系统崩溃。使用规范对大事务进行优化，针对内存耗时操作、远程http请求等非数据库操作，建议放到@Transactional注解方法的外部，避免过早或过长时间占用数据库连接，减少数据库大事务风险，避免客户端数据库连接池连接不足的问题事务注解@Transactional禁止放在java类维度，避免无谓的事务封装禁止在纯查询操作方法上使用事务注解明确事务注解仅对public方法有效，protected、private方法上使用@Transactional均无效并且非事务方法调用同类内部采用@Transactional修饰的方法时，事务不会生效，异常不会触发回滚严禁在事务方法内捕获异常而不抛出，如采用@Transactional修饰方法当研发自行添加了try/catch捕获异常且未抛出异常时，@Transactional无法自动回滚避免过早或过长时间占用数据库连接对大事务进行优化，针对内存耗时操作、远程http请求等非数据库操作，建议放到@Transactional注解方法的外部，避免过早或过长时间占用数据库连接，减少数据库大事务风险，避免客户端数据库连接池连接不足的问题反例问题：整个方法被@Transactional包裹，数据库连接从开始到结束一直被占用，即使中间没有DB操作。可能导致连接池耗尽。@ServicepublicclassOcrService{@AutowiredprivateOcrServiceMapperocrServiceMapper;@AutowiredprivateRestTemplaterestTemplate;@Transactionalpublicvoidinsert(OcrPojoocrPojo){//1.耗时的内存操作（不应该在事务中）heavyInMemoryProcessing();//2.远程调用（不应该在事务中）restTemplate.postForEntity("https://api.example.com/getOcr",ocrId,String.class);//3.保存ocr信息（DB操作）ocrServiceMapper.insert(ocrPojo);//事务直到这里才提交，连接长时间被占用❌}}正例拆分事务，非DB操作移出事务@ServicepublicclassOcrService{@AutowiredprivateDemoServicedemoService;@AutowiredprivateRestTemplaterestTemplate;publicvoidbuildInsert(OcrPojoocrPojo){//第一步：非事务操作（内存处理+远程调用）heavyInMemoryProcessing();restTemplate.postForEntity("https://api.example.com/getOcr",ocrId,String.class);//第二步：在独立事务中保存ocrdemoService.insert(ocrPojo);}}@ServicepublicclassDemoService{@AutowiredprivateOcrServiceMapperocrServiceMapper@Transactionalpublicvoidinsert(OcrPojoocrPojo){ocrServiceMapper.insert(ocrPojo);}}禁止类维度事务注解事务注解@Transactional禁止放在java类维度，避免无谓的事务封装反例问题：@Transactional注解不应随意加在Java类的维度（即类级别）上，尤其是当类中包含多个方法，且并非所有方法都需要事务时。这样做容易导致不必要的事务封装，进而引发性能问题或意外的事务传播行为。@Service@TransactionalpublicclassOcrService{publicvoidinsert(OcrPojoocrPojo){//保存ocr（需要事务）//...}publicvoiddemo(){//纯远程调用，无需事务❌却被事务包裹restTemplate.post(...);}publicStringselectOcrById(Stringid){//查询ocr操作，无需事务❌却被事务包裹returnocrInfo;}}正例将@Transactional添加到具体需要事务的方法上@ServicepublicclassOcrService{@Transactionalpublicvoidinsert(OcrPojoocrPojo){//保存ocr（需要事务）//只有这个方法需要完整事务}publicvoiddemo(){//无事务，避免占用数据库连接restTemplate.post(...);}publicStringselectOcrById(Stringid){//无事务，避免占用数据库连接returnocrInfo;}}禁止在纯查询操作方法上使用事务注解反例问题：纯查询操作被纳入事务，占用数据库连接；在高并发场景下，可能导致连接池耗尽；事务日志记录、事务管理器介入，增加系统开销。@ServicepublicclassOrderService{@TransactionalpublicOrderfindById(Longid){returnorderRepository.findById(id);}}正例避免占用连接@ServicepublicclassOrderService{publicOrderfindById(Longid){returnorderRepository.findById(id);}}明确事务注解仅对public方法有效@Transactional注解仅对public方法有效，这是Spring基于代理机制实现事务控制的一个重要限制Spring的@Transactional是通过AOP代理（JDK动态代理或CGLIB）实现的。当方法调用通过代理对象进入时，Spring才能拦截并开启事务上下文。如果方法不是public的，代理机制将无法正常工作，导致事务不生效。@ServicepublicclassOrderService{//❌protected方法：事务不生效@TransactionalprotectedvoidupdateOrderProtected(Orderorder){orderRepository.save(order);}//❌private方法：事务不生效，且代理完全无法拦截@TransactionalprivatevoidlogOperation(){//记录日志操作}//❌package-private（默认访问级别）：事务不生效@TransactionalvoidsendNotification(){restTemplate.post(...);}}内部自调用也无法触发事务非事务方法调用同类内部采用@Transactional修饰的方法时，事务不会生效，异常不会触发回滚Spring的@Transactional是通过AOP代理实现的。只有当方法调用来自类的外部（即通过代理对象调用）时，事务拦截器才会生效。如果是在类内部通过this直接调用，会绕过代理，导致事务失效反例@ServicepublicclassOrderService{@TransactionalpublicvoidcreateOrder(Orderorder){orderRepository.save(order);}//非事务方法调用同类中的事务方法publicvoidprocessOrder(Orderorder){createOrder(order);//❌通过this.createOrder()直接调用，事务不生效}}正例推荐做法：将事务方法拆分到不同类中，职责分离，事务控制更清晰。@ServicepublicclassOrderService{@AutowiredprivateDemoServicedemoService;publicvoidprocessOrder(Orderorder){//✅跨类调用，事务生效demoService.createOrder(order);}}@ServiceclassDemoService{@TransactionalpublicvoidcreateOrder(Orderorder){orderRepository.save(order);}}严禁在事务方法内捕获异常而不抛出严禁在@Transactional修饰的事务方法中捕获异常后不重新抛出，否则会导致Spring事务无法感知异常，从而无法触发回滚，造成数据不一致。Spring的声明式事务（@Transactional）是基于代理的。它通过拦截方法的异常抛出来判断是否需要回滚。如果异常被try-catch捕获并“吞掉”（即不抛出），Spring会认为方法执行成功，从而提交事务，即使内部出错了反例捕获异常但未抛出，即使paymentService.charge()抛出异常，由于被try-catch捕获且未重新抛出，Spring会认为方法执行成功，事务正常提交，但实际业务已失败，导致数据不一致。@ServicepublicclassOrderService{@AutowiredprivateOrderRepositoryorderRepository;@AutowiredprivatePaymentServicepaymentService;@TransactionalpublicvoidcreateOrder(Orderorder){orderRepository.save(order);try{paymentService.charge(order.getAmount());}catch(Exceptione){//❌错误：捕获了异常但没有抛出，事务不会回滚log.error("支付失败：",e);//没有throw，事务正常提交}}}正确做法一捕获后手动回滚如果你必须捕获异常并处理，应主动标记事务回滚：@TransactionalpublicvoidcreateOrder(Orderorder){orderRepository.save(order);try{paymentService.charge(order.getAmount());}catch(Exceptione){log.error("支付失败：",e);//✅抛出异常，触发回滚thrownewRuntimeException("支付失败，事务将回滚",e);}}正确做法二使用@Transactional(rollbackFor=Exception.class)明确回滚策略默认情况下，Spring事务只对RuntimeException和Error自动回滚，对checkedexception（如IOException）不回滚。使用rollbackFor可以扩展回滚范围。@Transactional(rollbackFor=Exception.class)publicvoidcreateOrder(Orderorder)throwsPaymentException{orderRepository.save(order);//抛出checkedexception也能回滚paymentService.charge(order.getAmount());}正确做法三捕获异常后选择性处理，再抛出自定义异常@Transactional(rollbackFor=BusinessException.class)publicvoidcreateOrder(Orderorder){orderRepository.save(order);try{paymentService.charge(order.getAmount());}catch(PaymentExceptione){log.error("支付异常",e);//✅抛出被事务识别的异常thrownewBusinessException("支付失败",e);}}正确做法四不想抛出异常，可以手动回滚事务@TransactionalpublicvoidcreateOrder(Orderorder){orderRepository.save(order);try{paymentService.charge(order.getAmount());}catch(Exceptione){log.error("支付失败，手动回滚",e);//手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}}</li>
  <li>通用Mapper（即tk.mybatis）是一个基于MyBatis的通用CRUD框架，它通过泛型和注解的方式，为实体类自动生成常用的增删改查（CRUD）SQL操作，无需编写XML或SQL，大幅提升开发效率。它不是MyBatis的替代品，而是对MyBatis的增强工具，让开发者专注于复杂业务SQL，而不用重复写基础操作。核心优势优点说明✅减少重复代码不用手写insert、update、delete、selectById等基础方法✅使用简单只需继承通用接口，即可拥有通用方法✅支持注解配置实体类通过注解映射数据库字段，无需XML✅兼容MyBatis完全兼容原生MyBatis，可与自定义SQL混用✅支持多种数据库MySQL、Oracle、SQLServer、PostgreSQL等主流数据库✅高度可扩展可自定义通用方法，支持插件机制修改MybatisGeneratortk.mybatis默认的生成器不带lombok，需要修改本地仓库的以下jar包&lt;dependency&gt;&lt;groupId&gt;org.mybatis.generator&lt;/groupId&gt;&lt;artifactId&gt;mybatis-generator-core&lt;/artifactId&gt;&lt;version&gt;1.4.2&lt;/version&gt;&lt;/dependency&gt;编译以下代码为LombokPlugin.class文件，然后替换mybatis-generator-core相应版本下org.mybatis.generator.plugins包下的LombokPlugin.class文件。存在则替换，不存在则新增packageorg.mybatis.generator.plugins;importjava.util.List;importorg.mybatis.generator.api.IntrospectedColumn;importorg.mybatis.generator.api.IntrospectedTable;importorg.mybatis.generator.api.Plugin;importorg.mybatis.generator.api.PluginAdapter;importorg.mybatis.generator.api.dom.java.Method;importorg.mybatis.generator.api.dom.java.TopLevelClass;publicclassLombokPluginextendsPluginAdapter{@Overridepublicbooleanvalidate(List&lt;String&gt;warnings){returntrue;}publicbooleanmodelBaseRecordClassGenerated(TopLevelClasstopLevelClass,IntrospectedTableintrospectedTable){topLevelClass.addImportedType("lombok.Data");topLevelClass.addImportedType("lombok.Builder");topLevelClass.addAnnotation("@Data");topLevelClass.addAnnotation("@Builder");returntrue;}publicbooleanmodelSetterMethodGenerated(Methodmethod,TopLevelClasstopLevelClass,IntrospectedColumnintrospectedColumn,IntrospectedTableintrospectedTable,Plugin.ModelClassTypemodelClassType){returnfalse;}publicbooleanmodelGetterMethodGenerated(Methodmethod,TopLevelClasstopLevelClass,IntrospectedColumnintrospectedColumn,IntrospectedTableintrospectedTable,Plugin.ModelClassTypemodelClassType){returnfalse;}}这样魔改后就支持生成的代码使用lombok插件完整POM文件postgresql版本，需要其他的数据库版本请自行替换依赖&lt;?xmlversion="1.0"encoding="UTF-8"?&gt;&lt;projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;&lt;groupId&gt;com.uhaiin&lt;/groupId&gt;&lt;artifactId&gt;mybatis-generator&lt;/artifactId&gt;&lt;version&gt;1.0.0&lt;/version&gt;&lt;properties&gt;&lt;maven.compiler.source&gt;21&lt;/maven.compiler.source&gt;&lt;maven.compiler.target&gt;21&lt;/maven.compiler.target&gt;&lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;&lt;/properties&gt;&lt;dependencies&gt;&lt;!--Mybatis通用mappertk单独使用，自己独有+自带版本号--&gt;&lt;dependency&gt;&lt;groupId&gt;org.mybatis&lt;/groupId&gt;&lt;artifactId&gt;mybatis&lt;/artifactId&gt;&lt;version&gt;3.5.13&lt;/version&gt;&lt;/dependency&gt;&lt;!--MybatisGenerator自己独有+自带版本号--&gt;&lt;dependency&gt;&lt;groupId&gt;org.mybatis.generator&lt;/groupId&gt;&lt;artifactId&gt;mybatis-generator-core&lt;/artifactId&gt;&lt;version&gt;1.4.2&lt;/version&gt;&lt;/dependency&gt;&lt;!--通用Mapper--&gt;&lt;dependency&gt;&lt;groupId&gt;tk.mybatis&lt;/groupId&gt;&lt;artifactId&gt;mapper&lt;/artifactId&gt;&lt;version&gt;4.2.3&lt;/version&gt;&lt;/dependency&gt;&lt;!--postgresql--&gt;&lt;dependency&gt;&lt;groupId&gt;org.postgresql&lt;/groupId&gt;&lt;artifactId&gt;postgresql&lt;/artifactId&gt;&lt;version&gt;42.7.5&lt;/version&gt;&lt;/dependency&gt;&lt;!--persistence--&gt;&lt;dependency&gt;&lt;groupId&gt;javax.persistence&lt;/groupId&gt;&lt;artifactId&gt;persistence-api&lt;/artifactId&gt;&lt;version&gt;1.0.2&lt;/version&gt;&lt;/dependency&gt;&lt;!--lombok--&gt;&lt;dependency&gt;&lt;groupId&gt;org.projectlombok&lt;/groupId&gt;&lt;artifactId&gt;lombok&lt;/artifactId&gt;&lt;version&gt;1.18.36&lt;/version&gt;&lt;optional&gt;true&lt;/optional&gt;&lt;/dependency&gt;&lt;/dependencies&gt;&lt;build&gt;&lt;resources&gt;&lt;resource&gt;&lt;directory&gt;${basedir}/src/main/java&lt;/directory&gt;&lt;includes&gt;&lt;include&gt;**/*.xml&lt;/include&gt;&lt;/includes&gt;&lt;/resource&gt;&lt;resource&gt;&lt;directory&gt;${basedir}/src/main/resources&lt;/directory&gt;&lt;/resource&gt;&lt;/resources&gt;&lt;plugins&gt;&lt;plugin&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;&lt;version&gt;3.4.1&lt;/version&gt;&lt;configuration&gt;&lt;excludes&gt;&lt;exclude&gt;&lt;groupId&gt;org.projectlombok&lt;/groupId&gt;&lt;artifactId&gt;lombok&lt;/artifactId&gt;&lt;/exclude&gt;&lt;/excludes&gt;&lt;/configuration&gt;&lt;/plugin&gt;&lt;plugin&gt;&lt;groupId&gt;org.mybatis.generator&lt;/groupId&gt;&lt;artifactId&gt;mybatis-generator-maven-plugin&lt;/artifactId&gt;&lt;version&gt;1.4.2&lt;/version&gt;&lt;configuration&gt;&lt;configurationFile&gt;${basedir}/src/main/resources/generatorConfig.xml&lt;/configurationFile&gt;&lt;overwrite&gt;true&lt;/overwrite&gt;&lt;verbose&gt;true&lt;/verbose&gt;&lt;/configuration&gt;&lt;dependencies&gt;&lt;!--postgresql--&gt;&lt;dependency&gt;&lt;groupId&gt;org.postgresql&lt;/groupId&gt;&lt;artifactId&gt;postgresql&lt;/artifactId&gt;&lt;version&gt;42.2.28&lt;/version&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;tk.mybatis&lt;/groupId&gt;&lt;artifactId&gt;mapper&lt;/artifactId&gt;&lt;version&gt;4.2.3&lt;/version&gt;&lt;/dependency&gt;&lt;/dependencies&gt;&lt;/plugin&gt;&lt;/plugins&gt;&lt;/build&gt;&lt;/project&gt;配置文件数据库配置在resources目录下新增：db.propertiespackage.name=com.uhaiinjdbc.driverClass=org.postgresql.Driverjdbc.url=jdbc:postgresql://ip:port/db?currentSchema=public&amp;encoding=UTF-8jdbc.username=usernamejdbc.password=password生成配置在resources目录下新增：generatorConfig.xml&lt;?xmlversion="1.0"encoding="UTF-8"?&gt;&lt;!DOCTYPEgeneratorConfigurationPUBLIC"-//mybatis.org//DTDMyBatisGeneratorConfiguration1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"&gt;&lt;generatorConfiguration&gt;&lt;propertiesresource="db.properties"/&gt;&lt;contextid="postgresql"targetRuntime="MyBatis3Simple"defaultModelType="flat"&gt;&lt;propertyname="beginningDelimiter"value="`"/&gt;&lt;propertyname="endingDelimiter"value="`"/&gt;&lt;plugintype="tk.mybatis.mapper.generator.MapperPlugin"&gt;&lt;propertyname="mappers"value="tk.mybatis.mapper.common.Mapper"/&gt;&lt;propertyname="caseSensitive"value="true"/&gt;&lt;/plugin&gt;&lt;plugintype="org.mybatis.generator.plugins.LombokPlugin"&gt;&lt;propertyname="hasLombok"value="true"/&gt;&lt;/plugin&gt;&lt;commentGenerator&gt;&lt;propertyname="suppressDate"value="true"/&gt;&lt;!--是否去除自动生成的注释true：是：false:否--&gt;&lt;propertyname="suppressAllComments"value="true"/&gt;&lt;/commentGenerator&gt;&lt;jdbcConnectiondriverClass="${jdbc.driverClass}"connectionURL="${jdbc.url}"userId="${jdbc.username}"password="${jdbc.password}"&gt;&lt;/jdbcConnection&gt;&lt;javaModelGeneratortargetPackage="${package.name}.entity"targetProject="src/main/java"/&gt;&lt;sqlMapGeneratortargetPackage="${package.name}.mapper"targetProject="src/main/java"/&gt;&lt;javaClientGeneratortargetPackage="${package.name}.mapper"targetProject="src/main/java"type="XMLMAPPER"/&gt;&lt;tabletableName="sys_user"domainObjectName="User"&gt;&lt;!--非主键自增的可以不写--&gt;&lt;generatedKeycolumn="user_id"sqlStatement="JDBC"/&gt;&lt;/table&gt;&lt;/context&gt;&lt;/generatorConfiguration&gt;生成代码eclipse配置eclipse运行插件配置新增以下配置：mybatis-generator:generate以后需要运行时候只需要修改完generatorConfig.xml中的&lt;tabletableName="sys_user"domainObjectName="User"&gt;&lt;!--非主键自增的可以不写--&gt;&lt;generatedKeycolumn="user_id"sqlStatement="JDBC"/&gt;&lt;/table&gt;即可运行eclipse插件配置idea双击执行插件使用方法把生成的实体类、Mapper.java、Mapper.xml拷贝到项目中即可使用，注意项目中的pom文件需要引入以下依赖：&lt;!--预编译工具--&gt;&lt;dependency&gt;&lt;groupId&gt;org.projectlombok&lt;/groupId&gt;&lt;artifactId&gt;lombok&lt;/artifactId&gt;&lt;optional&gt;true&lt;/optional&gt;&lt;/dependency&gt;&lt;!--PostgreSQL驱动--&gt;&lt;dependency&gt;&lt;groupId&gt;org.postgresql&lt;/groupId&gt;&lt;artifactId&gt;postgresql&lt;/artifactId&gt;&lt;scope&gt;runtime&lt;/scope&gt;&lt;/dependency&gt;&lt;!--通用Mapper4之tk.mybatis--&gt;&lt;dependency&gt;&lt;groupId&gt;tk.mybatis&lt;/groupId&gt;&lt;artifactId&gt;mapper&lt;/artifactId&gt;&lt;version&gt;4.3.0&lt;/version&gt;&lt;/dependency&gt;&lt;!--MyBatisSpringBootStarter--&gt;&lt;dependency&gt;&lt;groupId&gt;org.mybatis.spring.boot&lt;/groupId&gt;&lt;artifactId&gt;mybatis-spring-boot-starter&lt;/artifactId&gt;&lt;version&gt;4.0.1&lt;/version&gt;&lt;/dependency&gt;在springboot项目的启动类上添加扫描注解，注意使用tk.mybatis@MapperScan(basePackages="com.example.mapper")@SpringBootApplicationpublicclassApplication{}与MyBatis-Plus对比特性tk.mybatisMyBatis-Plus通用CRUD✅支持✅支持，更强大主键策略需手动配置内置多种策略（@TableId）分页支持需配合PageHelper内置分页插件代码生成需额外工具提供代码生成器活跃度已基本停止更新活跃维护，生态丰富学习成本低，简单直接稍高，功能多💡建议：新项目可优先考虑MyBatis-Plus，但老项目或轻量级场景仍可使用总结tk.mybatis通用Mapper适合：单表操作多的项目想快速上手、减少模板代码不想引入复杂框架的轻量级项目一句话总结：用一个接口继承，换80%的CRUD不用手写。</li>
  <li>实现了一个基于SpringBoot的轻量级API防火墙，通过拦截器机制提供实时防护能力。对所有API请求进行“前置检查“，支持配置IP白名单、黑名单，白名单优先级更高。系统采用GuavaCache实现高性能内存缓存，支持QPS限制。技术选型数据库：postgresql+通用mappertk.mybatis缓存：GuavaCacheSpringBoot：4JDK：21对于分布式场景，后续可以扩展Redis+Lua实现统一限流；但在本文场景下，先聚焦单机轻量化防护。核心表接口访问日志表CREATETABLEdemo.firewall_access_log(idint8NOTNULL,--主键IDip_addressvarchar(100)NOTNULL,--IP地址api_pathvarchar(200)NOTNULL,--API路径user_agentvarchar(500)NULL,--User-Agentrequest_methodvarchar(10)NULL,--请求方法status_codeint4NULL,--响应状态码block_reasonvarchar(100)NULL,--拦截原因request_timetimestampDEFAULTCURRENT_TIMESTAMPNULL,--请求时间response_timeint8NULL,--响应时间(毫秒)CONSTRAINTfirewall_access_log_pkeyPRIMARYKEY(id));CREATEINDEXidx_api_timeONdemo.firewall_access_logUSINGbtree(api_path,request_time);CREATEINDEXidx_ip_timeONdemo.firewall_access_logUSINGbtree(ip_address,request_time);COMMENTONTABLEdemo.firewall_access_logIS'接口访问日志表';--ColumncommentsCOMMENTONCOLUMNdemo.firewall_access_log.idIS'主键ID';COMMENTONCOLUMNdemo.firewall_access_log.ip_addressIS'IP地址';COMMENTONCOLUMNdemo.firewall_access_log.api_pathIS'API路径';COMMENTONCOLUMNdemo.firewall_access_log.user_agentIS'User-Agent';COMMENTONCOLUMNdemo.firewall_access_log.request_methodIS'请求方法';COMMENTONCOLUMNdemo.firewall_access_log.status_codeIS'响应状态码';COMMENTONCOLUMNdemo.firewall_access_log.block_reasonIS'拦截原因';COMMENTONCOLUMNdemo.firewall_access_log.request_timeIS'请求时间';COMMENTONCOLUMNdemo.firewall_access_log.response_timeIS'响应时间(毫秒)';接口限流规则表CREATETABLEdemo.firewall_rule(idint8NOTNULL,--主键IDrule_namevarchar(100)NOTNULL,--规则名称api_patternvarchar(200)NOTNULL,--API路径匹配模式qps_limitint4DEFAULT100NULL,--QPS限制enabledboolDEFAULTtrueNULL,--是否启用descriptionvarchar(500)NULL,--规则描述created_timetimestampDEFAULTCURRENT_TIMESTAMPNULL,--创建时间updated_timetimestampDEFAULTCURRENT_TIMESTAMPNULL,--更新时间CONSTRAINTfirewall_rule_pkeyPRIMARYKEY(id));COMMENTONTABLEdemo.firewall_ruleIS'接口限流规则表';--ColumncommentsCOMMENTONCOLUMNdemo.firewall_rule.idIS'主键ID';COMMENTONCOLUMNdemo.firewall_rule.rule_nameIS'规则名称';COMMENTONCOLUMNdemo.firewall_rule.api_patternIS'API路径匹配模式';COMMENTONCOLUMNdemo.firewall_rule.qps_limitIS'QPS限制';COMMENTONCOLUMNdemo.firewall_rule.enabledIS'是否启用';COMMENTONCOLUMNdemo.firewall_rule.descriptionIS'规则描述';COMMENTONCOLUMNdemo.firewall_rule.created_timeIS'创建时间';COMMENTONCOLUMNdemo.firewall_rule.updated_timeIS'更新时间';INSERTINTOfirewall_rule(id,rule_name,api_pattern,qps_limit,enabled,description,created_time,updated_time)VALUES(1,'用户登录限流','/api/auth/login',10,true,'用户登录接口限流，防止暴力破解','2026-01-2613:48:18.421','2026-01-2613:48:18.421');INSERTINTOfirewall_rule(id,rule_name,api_pattern,qps_limit,enabled,description,created_time,updated_time)VALUES(2,'订单接口限流','/api/order/**',50,true,'订单相关接口限流','2026-01-2613:48:18.421','2026-01-2613:48:18.421');INSERTINTOfirewall_rule(id,rule_name,api_pattern,qps_limit,enabled,description,created_time,updated_time)VALUES(3,'支付接口限流','/api/payment/**',20,true,'支付相关接口限流','2026-01-2613:48:18.421','2026-01-2613:48:18.421');INSERTINTOfirewall_rule(id,rule_name,api_pattern,qps_limit,enabled,description,created_time,updated_time)VALUES(4,'文件上传限流','/api/upload/**',30,true,'文件上传接口限流','2026-01-2613:48:18.421','2026-01-2613:48:18.421');INSERTINTOfirewall_rule(id,rule_name,api_pattern,qps_limit,enabled,description,created_time,updated_time)VALUES(5,'数据导出限流','/api/export/**',5,true,'数据导出接口限流，防止大量导出','2026-01-2613:48:18.421','2026-01-2613:48:18.421');INSERTINTOfirewall_rule(id,rule_name,api_pattern,qps_limit,enabled,description,created_time,updated_time)VALUES(6,'默认API限流','/api/test/**',10,true,'默认API接口限流规则','2026-01-2613:48:18.421','2026-01-2615:43:53.418');INSERTINTOfirewall_rule(id,rule_name,api_pattern,qps_limit,enabled,description,created_time,updated_time)VALUES(7,'demo限流','/demo',2,true,'默认API接口限流规则','2026-01-2613:48:18.421','2026-01-2615:43:53.418');接口访问统计表CREATETABLEdemo.firewall_statistics(idint8NOTNULL,--主键IDstat_datedateNOTNULL,--统计日期api_pathvarchar(200)NOTNULL,--API路径total_requestsint8DEFAULT0NULL,--总请求数blocked_requestsint8DEFAULT0NULL,--被拦截请求数avg_response_timenumeric(10,2)DEFAULT0NULL,--平均响应时间created_timetimestampDEFAULTCURRENT_TIMESTAMPNULL,--创建时间updated_timetimestampDEFAULTCURRENT_TIMESTAMPNULL,--更新时间CONSTRAINTfirewall_statistics_pkeyPRIMARYKEY(id),CONSTRAINTuk_date_apiUNIQUE(stat_date,api_path));COMMENTONTABLEdemo.firewall_statisticsIS'接口访问统计表';--ColumncommentsCOMMENTONCOLUMNdemo.firewall_statistics.idIS'主键ID';COMMENTONCOLUMNdemo.firewall_statistics.stat_dateIS'统计日期';COMMENTONCOLUMNdemo.firewall_statistics.api_pathIS'API路径';COMMENTONCOLUMNdemo.firewall_statistics.total_requestsIS'总请求数';COMMENTONCOLUMNdemo.firewall_statistics.blocked_requestsIS'被拦截请求数';COMMENTONCOLUMNdemo.firewall_statistics.avg_response_timeIS'平均响应时间';COMMENTONCOLUMNdemo.firewall_statistics.created_timeIS'创建时间';COMMENTONCOLUMNdemo.firewall_statistics.updated_timeIS'更新时间';ip黑名单表CREATETABLEdemo.firewall_blacklist(idint8NOTNULL,--主键IDip_addressvarchar(100)NOTNULL,--IP地址reasonvarchar(200)NULL,--封禁原因expire_timetimestampNULL,--过期时间(NULL表示永久)enabledboolDEFAULTtrueNULL,--是否启用created_timetimestampDEFAULTCURRENT_TIMESTAMPNULL,--创建时间updated_timetimestampDEFAULTCURRENT_TIMESTAMPNULL,--更新时间CONSTRAINTfirewall_blacklist_pkeyPRIMARYKEY(id),CONSTRAINTuk_ipUNIQUE(ip_address));COMMENTONTABLEdemo.firewall_blacklistIS'ip黑名单表';--ColumncommentsCOMMENTONCOLUMNdemo.firewall_blacklist.idIS'主键ID';COMMENTONCOLUMNdemo.firewall_blacklist.ip_addressIS'IP地址';COMMENTONCOLUMNdemo.firewall_blacklist.reasonIS'封禁原因';COMMENTONCOLUMNdemo.firewall_blacklist.expire_timeIS'过期时间(NULL表示永久)';COMMENTONCOLUMNdemo.firewall_blacklist.enabledIS'是否启用';COMMENTONCOLUMNdemo.firewall_blacklist.created_timeIS'创建时间';COMMENTONCOLUMNdemo.firewall_blacklist.updated_timeIS'更新时间';INSERTINTOfirewall_blacklist(id,ip_address,reason,expire_time,enabled,created_time,updated_time)VALUES(1,'10.163.193.196/32','恶意攻击IP',NULL,NULL,'2026-01-2613:57:46.428','2026-01-2615:35:34.028');ip白名单表CREATETABLEdemo.firewall_whitelist(idint8NOTNULL,--主键IDip_addressvarchar(100)NOTNULL,--IP地址descriptionvarchar(200)NULL,--描述enabledboolDEFAULTtrueNULL,--是否启用created_timetimestampDEFAULTCURRENT_TIMESTAMPNULL,--创建时间updated_timetimestampDEFAULTCURRENT_TIMESTAMPNULL,--更新时间CONSTRAINTfirewall_whitelist_pkeyPRIMARYKEY(id),CONSTRAINTuk_whitelist_ipUNIQUE(ip_address));COMMENTONTABLEdemo.firewall_whitelistIS'ip白名单表';--ColumncommentsCOMMENTONCOLUMNdemo.firewall_whitelist.idIS'主键ID';COMMENTONCOLUMNdemo.firewall_whitelist.ip_addressIS'IP地址';COMMENTONCOLUMNdemo.firewall_whitelist.descriptionIS'描述';COMMENTONCOLUMNdemo.firewall_whitelist.enabledIS'是否启用';COMMENTONCOLUMNdemo.firewall_whitelist.created_timeIS'创建时间';COMMENTONCOLUMNdemo.firewall_whitelist.updated_timeIS'更新时间';INSERTINTOfirewall_whitelist(id,ip_address,description,enabled,created_time,updated_time)VALUES(1,'127.0.0.1/32','本地回环地址',true,'2026-01-2613:48:35.552','2026-01-2613:48:35.552');INSERTINTOfirewall_whitelist(id,ip_address,description,enabled,created_time,updated_time)VALUES(2,'::1/128','IPv6本地回环地址',true,'2026-01-2613:48:35.552','2026-01-2613:48:35.552');INSERTINTOfirewall_whitelist(id,ip_address,description,enabled,created_time,updated_time)VALUES(3,'192.168.1.100/32','管理员IP地址',true,'2026-01-2613:48:35.552','2026-01-2613:48:35.552');配置文件集成postgresql+mybatis+firewall#数据库配置（PostgreSQL）spring:jackson:#核心基础配置default-property-inclusion:non_nulldate-format:yyyy-MM-ddHH:mm:sstime-zone:Asia/Shanghaidatasource:driver-class-name:org.postgresql.Driverurl:jdbc:postgresql://ip:port/db?currentSchema=publicusername:usernamepassword:passwordhikari:pool-name:uhaiinHikariPoolminimum-idle:5maximum-pool-size:20connection-timeout:30000idle-timeout:600000max-lifetime:1800000connection-test-query:SELECT1auto-commit:trueleak-detection-threshold:60000data-source-properties:cachePrepStmts:trueuseServerPrepStmts:trueprepStmtCacheSize:250prepStmtCacheSqlLimit:2048rewriteBatchedStatements:true#MyBatis配置mybatis:mapper-locations:-classpath:mapper/firewall/*.xml-classpath:mapper/user/*.xmltype-aliases-package:-com.uhaiin.firewall.entity-com.uhaiin.user.entityconfiguration:map-underscore-to-camel-case:truelog-impl:org.apache.ibatis.logging.stdout.StdOutImpl#生产关闭#TKMyBatis（通用Mapper）配置mapper:mappers:tk.mybatis.mapper.common.Mapper#指定通用Mapper接口，核心配置identity:POSTGRESQL#主键生成策略适配PostgreSQL（支持自增主键serial/bigserial）not-empty:true#更新时是否忽略空值（true：只更新非空字段，推荐）style:camelhump#字段名风格：驼峰（与mybatis的下划线转驼峰配合）enable-method-cache:true#开启方法缓存（可选）#管理端点配置management:endpoints:web:exposure:include:health,info,metricsendpoint:health:show-details:always#防火墙配置firewall:enabled:truedefault-qps-limit:100cache-size:1000exclude-paths:-/firewall/**-/actuator/**-/static/**-/favicon.ico核心代码接口访问日志实体类packagecom.uhaiin.firewall.entity;importlombok.AllArgsConstructor;importlombok.Builder;importlombok.Data;importlombok.NoArgsConstructor;importjavax.persistence.Column;importjavax.persistence.Id;importjavax.persistence.Table;importjava.time.LocalDateTime;/***表名：firewall_access_log*/@Table(name="firewall_access_log")@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassFirewallAccessLog{/***主键ID*/@Id@Column(name="id")privateLongid;/***IP地址*/@Column(name="ip_address")privateStringipAddress;/***API路径*/@Column(name="api_path")privateStringapiPath;/***User-Agent*/@Column(name="user_agent")privateStringuserAgent;/***请求方法*/@Column(name="request_method")privateStringrequestMethod;/***响应状态码*/@Column(name="status_code")privateIntegerstatusCode;/***拦截原因*/@Column(name="block_reason")privateStringblockReason;/***请求时间*/@Column(name="request_time")privateLocalDateTimerequestTime;/***响应时间(毫秒)*/@Column(name="response_time")privateLongresponseTime;/***检查是否被拦截**@return是否被拦截*/publicbooleanisBlocked(){returnblockReason!=null&amp;&amp;!blockReason.trim().isEmpty();}/***检查是否成功响应**@return是否成功*/publicbooleanisSuccess(){returnstatusCode!=null&amp;&amp;statusCode&gt;=200&amp;&amp;statusCode&lt;300;}/***获取响应时间描述**@return响应时间描述*/publicStringgetResponseTimeDescription(){if(responseTime==null){return"未知";}if(responseTime&lt;100){return"快速("+responseTime+"ms)";}elseif(responseTime&lt;500){return"正常("+responseTime+"ms)";}elseif(responseTime&lt;1000){return"较慢("+responseTime+"ms)";}else{return"缓慢("+responseTime+"ms)";}}/***获取状态描述**@return状态描述*/publicStringgetStatusDescription(){if(isBlocked()){return"已拦截:"+blockReason;}if(statusCode==null){return"未知";}returnswitch(statusCode){case200-&gt;"成功";case400-&gt;"请求错误";case401-&gt;"未授权";case403-&gt;"禁止访问";case404-&gt;"未找到";case429-&gt;"请求过多";case500-&gt;"服务器错误";default-&gt;"状态码:"+statusCode;};}}ip黑名单实体类packagecom.uhaiin.firewall.entity;importlombok.AllArgsConstructor;importlombok.Builder;importlombok.Data;importlombok.NoArgsConstructor;importjavax.persistence.Column;importjavax.persistence.Id;importjavax.persistence.Table;importjava.time.LocalDateTime;/***表名：firewall_blacklist*/@Table(name="firewall_blacklist")@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassFirewallBlacklist{/***主键ID*/@Id@Column(name="id")privateLongid;/***IP地址*/@Column(name="ip_address")privateStringipAddress;/***封禁原因*/privateStringreason;/***过期时间(NULL表示永久)*/@Column(name="expire_time")privateLocalDateTimeexpireTime;/***是否启用*/privateBooleanenabled;/***创建时间*/@Column(name="created_time")privateLocalDateTimecreatedTime;/***更新时间*/@Column(name="updated_time")privateLocalDateTimeupdatedTime;/***检查IP是否已过期**@return是否已过期*/publicbooleanisExpired(){returnexpireTime!=null&amp;&amp;LocalDateTime.now().isAfter(expireTime);}/***检查IP是否有效（启用且未过期）**@return是否有效*/publicbooleanisValid(){return(enabled==null||enabled)&amp;&amp;!isExpired();}/***检查IP地址是否匹配**@paramip要检查的IP地址*@return是否匹配*/publicbooleanmatches(Stringip){if(ipAddress==null||ip==null){returnfalse;}//支持CIDR格式的IP段匹配if(ipAddress.contains("/")){returnmatchesCidr(ip,ipAddress);}//支持通配符匹配if(ipAddress.contains("*")){Stringpattern=ipAddress.replace("*",".*");returnip.matches(pattern);}//精确匹配returnipAddress.equals(ip);}/***CIDR格式IP段匹配**@paramipIP地址*@paramcidrCIDR格式的IP段*@return是否匹配*/privatebooleanmatchesCidr(Stringip,Stringcidr){try{String[]parts=cidr.split("/");if(parts.length!=2){returnfalse;}StringnetworkIp=parts[0];intprefixLength=Integer.parseInt(parts[1]);//简单的IPv4CIDR匹配实现longipLong=ipToLong(ip);longnetworkLong=ipToLong(networkIp);longmask=(0xFFFFFFFFL&lt;&lt;(32-prefixLength))&amp;0xFFFFFFFFL;return(ipLong&amp;mask)==(networkLong&amp;mask);}catch(Exceptione){returnfalse;}}/***IP地址转换为长整型**@paramipIP地址*@return长整型值*/privatelongipToLong(Stringip){String[]parts=ip.split("\\.");if(parts.length!=4){thrownewIllegalArgumentException("InvalidIPaddress:"+ip);}longresult=0;for(inti=0;i&lt;4;i++){result=(result&lt;&lt;8)+Integer.parseInt(parts[i]);}returnresult;}}规则实体类packagecom.uhaiin.firewall.entity;importlombok.AllArgsConstructor;importlombok.Builder;importlombok.Data;importlombok.NoArgsConstructor;importjavax.persistence.Column;importjavax.persistence.Id;importjavax.persistence.Table;importjava.time.LocalDateTime;importjava.util.List;/***表名：firewall_rule*/@Table(name="firewall_rule")@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassFirewallRule{/***主键ID*/@Id@Column(name="id")privateLongid;/***规则名称*/@Column(name="rule_name")privateStringruleName;/***API路径匹配模式*/@Column(name="api_pattern")privateStringapiPattern;/***QPS限制*/@Column(name="qps_limit")privateIntegerqpsLimit;/***是否启用*/privateBooleanenabled;/***规则描述*/privateStringdescription;/***创建时间*/@Column(name="created_time")privateLocalDateTimecreatedTime;/***更新时间*/@Column(name="updated_time")privateLocalDateTimeupdatedTime;/***黑名单IP列表（运行时使用，不存储在数据库）*/privateList&lt;String&gt;blackIps;/***白名单IP列表（运行时使用，不存储在数据库）*/privateList&lt;String&gt;whiteIps;/***检查API路径是否匹配此规则**@paramapiPathAPI路径*@return是否匹配*/publicbooleanmatches(StringapiPath){if(apiPattern==null||apiPath==null){returnfalse;}//支持通配符匹配Stringpattern=apiPattern.replace("**",".*").replace("*","[^/]*");returnapiPath.matches(pattern);}/***获取有效的QPS限制**@returnQPS限制，默认100*/publicintgetEffectiveQpsLimit(){returnqpsLimit!=null&amp;&amp;qpsLimit&gt;0?qpsLimit:100;}/***检查规则是否启用**@return是否启用，默认true*/publicbooleanisEffectiveEnabled(){returnenabled==null||enabled;}}接口访问统计实体类packagecom.uhaiin.firewall.entity;importlombok.AllArgsConstructor;importlombok.Builder;importlombok.Data;importlombok.NoArgsConstructor;importjavax.persistence.Column;importjavax.persistence.Id;importjavax.persistence.Table;importjava.math.BigDecimal;importjava.time.LocalDate;importjava.time.LocalDateTime;/***表名：firewall_statistics*/@Table(name="firewall_statistics")@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassFirewallStatistics{/***主键ID*/@Id@Column(name="id")privateLongid;/***统计日期*/@Column(name="stat_date")privateLocalDatestatDate;/***API路径*/@Column(name="api_path")privateStringapiPath;/***总请求数*/@Column(name="total_requests")privateLongtotalRequests;/***被拦截请求数*/@Column(name="blocked_requests")privateLongblockedRequests;/***平均响应时间*/@Column(name="avg_response_time")privateBigDecimalavgResponseTime;/***创建时间*/@Column(name="created_time")privateLocalDateTimecreatedTime;/***更新时间*/@Column(name="updated_time")privateLocalDateTimeupdatedTime;/***计算拦截率**@return拦截率（百分比）*/publicdoublegetBlockRate(){if(totalRequests==null||totalRequests==0){return0.0;}longblocked=blockedRequests!=null?blockedRequests:0;return(double)blocked/totalRequests*100;}/***计算成功率**@return成功率（百分比）*/publicdoublegetSuccessRate(){return100.0-getBlockRate();}/***获取拦截率描述**@return拦截率描述*/publicStringgetBlockRateDescription(){doublerate=getBlockRate();if(rate==0){return"无拦截";}elseif(rate&lt;1){return"极低("+String.format("%.2f",rate)+"%)";}elseif(rate&lt;5){return"较低("+String.format("%.2f",rate)+"%)";}elseif(rate&lt;20){return"中等("+String.format("%.2f",rate)+"%)";}else{return"较高("+String.format("%.2f",rate)+"%)";}}/***获取响应时间描述**@return响应时间描述*/publicStringgetResponseTimeDescription(){if(avgResponseTime==null){return"未知";}doubletime=avgResponseTime.doubleValue();if(time&lt;100){return"快速("+String.format("%.1f",time)+"ms)";}elseif(time&lt;500){return"正常("+String.format("%.1f",time)+"ms)";}elseif(time&lt;1000){return"较慢("+String.format("%.1f",time)+"ms)";}else{return"缓慢("+String.format("%.1f",time)+"ms)";}}/***增加请求统计**@paramisBlocked是否被拦截*@paramresponseTime响应时间*/publicvoidaddRequest(booleanisBlocked,longresponseTime){//增加总请求数this.totalRequests=(this.totalRequests!=null?this.totalRequests:0)+1;//增加拦截数if(isBlocked){this.blockedRequests=(this.blockedRequests!=null?this.blockedRequests:0)+1;}//更新平均响应时间if(this.avgResponseTime==null){this.avgResponseTime=BigDecimal.valueOf(responseTime);}else{//计算新的平均值BigDecimalcurrentTotal=this.avgResponseTime.multiply(BigDecimal.valueOf(this.totalRequests-1));BigDecimalnewTotal=currentTotal.add(BigDecimal.valueOf(responseTime));this.avgResponseTime=newTotal.divide(BigDecimal.valueOf(this.totalRequests),2,BigDecimal.ROUND_HALF_UP);}//更新时间this.updatedTime=LocalDateTime.now();}}ip白名单实体类packagecom.uhaiin.firewall.entity;importjava.time.LocalDateTime;importjavax.persistence.Column;importjavax.persistence.Id;importjavax.persistence.Table;importlombok.AllArgsConstructor;importlombok.Builder;importlombok.Data;importlombok.NoArgsConstructor;/***表名：firewall_whitelist*/@Table(name="firewall_whitelist")@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassFirewallWhitelist{/***主键ID*/@Id@Column(name="id")privateLongid;/***IP地址*/@Column(name="ip_address")privateStringipAddress;/***描述*/privateStringdescription;/***是否启用*/privateBooleanenabled;/***创建时间*/@Column(name="created_time")privateLocalDateTimecreatedTime;/***更新时间*/@Column(name="updated_time")privateLocalDateTimeupdatedTime;/***检查IP是否有效（启用）**@return是否有效*/publicbooleanisValid(){returnenabled==null||enabled;}/***检查IP地址是否匹配**@paramip要检查的IP地址*@return是否匹配*/publicbooleanmatches(Stringip){if(ipAddress==null||ip==null){returnfalse;}//支持CIDR格式的IP段匹配if(ipAddress.contains("/")){returnmatchesCidr(ip,ipAddress);}//支持通配符匹配if(ipAddress.contains("*")){Stringpattern=ipAddress.replace("*",".*");returnip.matches(pattern);}//精确匹配returnipAddress.equals(ip);}/***CIDR格式IP段匹配**@paramipIP地址*@paramcidrCIDR格式的IP段*@return是否匹配*/privatebooleanmatchesCidr(Stringip,Stringcidr){try{String[]parts=cidr.split("/");if(parts.length!=2){returnfalse;}StringnetworkIp=parts[0];intprefixLength=Integer.parseInt(parts[1]);//简单的IPv4CIDR匹配实现longipLong=ipToLong(ip);longnetworkLong=ipToLong(networkIp);longmask=(0xFFFFFFFFL&lt;&lt;(32-prefixLength))&amp;0xFFFFFFFFL;return(ipLong&amp;mask)==(networkLong&amp;mask);}catch(Exceptione){returnfalse;}}/***IP地址转换为长整型**@paramipIP地址*@return长整型值*/privatelongipToLong(Stringip){String[]parts=ip.split("\\.");if(parts.length!=4){thrownewIllegalArgumentException("InvalidIPaddress:"+ip);}longresult=0;for(inti=0;i&lt;4;i++){result=(result&lt;&lt;8)+Integer.parseInt(parts[i]);}returnresult;}}mapper采用通用mapper：tk.mybatisimporttk.mybatis.mapper.common.Mapper;publicinterfaceFirewallAccessLogMapperextendsMapper&lt;FirewallAccessLog&gt;{}publicinterfaceFirewallBlacklistMapperextendsMapper&lt;FirewallBlacklist&gt;{}publicinterfaceFirewallRuleMapperextendsMapper&lt;FirewallRule&gt;{}publicinterfaceFirewallStatisticsMapperextendsMapper&lt;FirewallStatistics&gt;{}publicinterfaceFirewallWhitelistMapperextendsMapper&lt;FirewallWhitelist&gt;{}WebConfigimportcom.uhaiin.common.interceptor.FirewallInterceptor;importjakarta.annotation.Resource;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;importjava.util.Arrays;importjava.util.List;/***Web配置类**/@ConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{@ResourceprivateFirewallInterceptorfirewallInterceptor;@Value("${firewall.exclude-paths:/actuator/**,/static/**,/css/**,/js/**,/images/**,/favicon.ico}")privateStringexcludePathsStr;@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){//解析排除路径List&lt;String&gt;excludePaths=Arrays.asList(excludePathsStr.split(","));registry.addInterceptor(firewallInterceptor)//拦截所有请求.addPathPatterns("/**")//排除指定路径.excludePathPatterns(excludePaths);}}Interceptorpackagecom.uhaiin.common.interceptor;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.google.common.cache.Cache;importcom.google.common.cache.CacheBuilder;importcom.uhaiin.common.utils.SnowflakeIdGenerator;importcom.uhaiin.firewall.entity.FirewallAccessLog;importcom.uhaiin.firewall.entity.FirewallRule;importcom.uhaiin.firewall.service.FirewallService;importcom.uhaiin.firewall.service.RuleManagerService;importjakarta.annotation.Resource;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Component;importorg.springframework.web.servlet.HandlerInterceptor;importjava.io.IOException;importjava.nio.charset.StandardCharsets;importjava.time.LocalDateTime;importjava.util.HashMap;importjava.util.Map;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicInteger;/***防火墙拦截器**/@Slf4j@ComponentpublicclassFirewallInterceptorimplementsHandlerInterceptor{privatefinalObjectMapperobjectMapper=newObjectMapper();@ResourceprivateRuleManagerServiceruleManagerService;@ResourceprivateFirewallServicefirewallService;@Value("${firewall.enabled:false}")privatebooleanfirewallEnable;@Value("${firewall.default.qps-limit:100}")privateintdefaultQpsLimit;@Value("${firewall.cache-size:10000}")privatelongmaximumSize;/***QPS限制缓存-存储每个IP+API的访问计数*/privatefinalCache&lt;String,AtomicInteger&gt;qpsCache=CacheBuilder.newBuilder().maximumSize(maximumSize).expireAfterWrite(1,TimeUnit.MINUTES).build();@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler){//设置字符集为UTF-8response.setCharacterEncoding(StandardCharsets.UTF_8.name());longstartTime=System.currentTimeMillis();StringipAddress=getClientIpAddress(request);StringapiPath=request.getRequestURI();StringuserAgent=request.getHeader("User-Agent");Stringmethod=request.getMethod();if(!firewallEnable){//没有启用防火墙则直接返回true不拦截returntrue;}log.info("防火墙拦截检查:IP={},API={},Method={}",ipAddress,apiPath,method);try{//1.检查白名单if(ruleManagerService.isWhitelisted(ipAddress)){log.info("IP{}在白名单中，允许访问",ipAddress);logAccess(ipAddress,apiPath,userAgent,method,200,null,startTime);returntrue;}//2.检查黑名单if(ruleManagerService.isBlacklisted(ipAddress)){log.warn("IP{}在黑名单中，拒绝访问",ipAddress);blockRequest(response,"IP地址被列入黑名单",403);logAccess(ipAddress,apiPath,userAgent,method,403,"IP黑名单拦截",startTime);returnfalse;}//3.获取匹配的防火墙规则FirewallRulerule=ruleManagerService.getMatchingRule(apiPath);if(rule==null){//使用默认规则rule=createDefaultRule(apiPath);}//4.QPS限制检查if(!checkQpsLimit(ipAddress,apiPath,rule)){log.warn("IP{}访问{}超过QPS限制{}",ipAddress,apiPath,rule.getEffectiveQpsLimit());blockRequest(response,"访问频率过高，请稍后再试",429);logAccess(ipAddress,apiPath,userAgent,method,429,"QPS限制拦截",startTime);returnfalse;}//5.记录正常访问logAccess(ipAddress,apiPath,userAgent,method,200,null,startTime);log.info("IP{}访问{}通过防火墙检查",ipAddress,apiPath);returntrue;}catch(Exceptione){log.error("防火墙拦截器处理异常:IP={},API={}",ipAddress,apiPath,e);logAccess(ipAddress,apiPath,userAgent,method,500,"系统异常",startTime);//异常情况下允许通过，避免影响正常业务returntrue;}}/***检查QPS限制**@paramipAddressIP地址*@paramapiPathAPI路径*@paramrule防火墙规则*@return是否通过检查*/privatebooleancheckQpsLimit(StringipAddress,StringapiPath,FirewallRulerule){Stringkey=ipAddress+":"+apiPath;intqpsLimit=rule.getEffectiveQpsLimit();if(qpsLimit&lt;=0){//无限制returntrue;}AtomicIntegercounter=qpsCache.getIfPresent(key);if(counter==null){counter=newAtomicInteger(0);qpsCache.put(key,counter);}intcurrentCount=counter.incrementAndGet();returncurrentCount&lt;=qpsLimit;}/***创建默认规则**@paramapiPathAPI路径*@return默认规则*/privateFirewallRulecreateDefaultRule(StringapiPath){FirewallRulerule=newFirewallRule();rule.setRuleName("默认规则");rule.setApiPattern(apiPath);rule.setQpsLimit(defaultQpsLimit);rule.setEnabled(true);returnrule;}/***阻止请求并返回错误响应**@paramresponseHTTP响应*@parammessage错误消息*@paramstatusCode状态码*@throwsIOExceptionIO异常*/privatevoidblockRequest(HttpServletResponseresponse,Stringmessage,intstatusCode)throwsIOException{response.setStatus(statusCode);response.setContentType("application/json;charset=UTF-8");Map&lt;String,Object&gt;result=newHashMap&lt;&gt;();result.put("success",false);result.put("code",statusCode);result.put("message",message);result.put("timestamp",System.currentTimeMillis());StringjsonResponse=objectMapper.writeValueAsString(result);response.getWriter().write(jsonResponse);response.getWriter().flush();}/***记录访问日志**@paramipAddressIP地址*@paramapiPathAPI路径*@paramuserAgentUser-Agent*@parammethod请求方法*@paramstatusCode状态码*@paramblockReason拦截原因*@paramstartTime开始时间*/privatevoidlogAccess(StringipAddress,StringapiPath,StringuserAgent,Stringmethod,intstatusCode,StringblockReason,longstartTime){try{FirewallAccessLogaccessLog=newFirewallAccessLog();accessLog.setId(SnowflakeIdGenerator.next());accessLog.setIpAddress(ipAddress);accessLog.setApiPath(apiPath);accessLog.setUserAgent(userAgent);accessLog.setRequestMethod(method);accessLog.setStatusCode(statusCode);accessLog.setBlockReason(blockReason);accessLog.setRequestTime(LocalDateTime.now());accessLog.setResponseTime(System.currentTimeMillis()-startTime);//异步记录日志，避免影响性能firewallService.logAccessAsync(accessLog);}catch(Exceptione){log.error("记录访问日志失败:IP={},API={}",ipAddress,apiPath,e);}}/***获取客户端真实IP地址**@paramrequestHTTP请求*@returnIP地址*/privateStringgetClientIpAddress(HttpServletRequestrequest){String[]headers={"X-Forwarded-For","X-Real-IP","Proxy-Client-IP","WL-Proxy-Client-IP","HTTP_X_FORWARDED_FOR","HTTP_X_FORWARDED","HTTP_X_CLUSTER_CLIENT_IP","HTTP_CLIENT_IP","HTTP_FORWARDED_FOR","HTTP_FORWARDED","HTTP_VIA","REMOTE_ADDR"};for(Stringheader:headers){Stringip=request.getHeader(header);if(ip!=null&amp;&amp;!ip.isEmpty()&amp;&amp;!"unknown".equalsIgnoreCase(ip)){//多个IP时取第一个if(ip.contains(",")){ip=ip.split(",")[0].trim();}returnip;}}returnrequest.getRemoteAddr();}}实现类防火墙实现类packagecom.uhaiin.firewall.service.impl;importcom.uhaiin.common.utils.SnowflakeIdGenerator;importcom.uhaiin.firewall.entity.FirewallAccessLog;importcom.uhaiin.firewall.entity.FirewallStatistics;importcom.uhaiin.firewall.mapper.FirewallAccessLogMapper;importcom.uhaiin.firewall.mapper.FirewallStatisticsMapper;importcom.uhaiin.firewall.service.FirewallService;importjakarta.annotation.Resource;importlombok.extern.slf4j.Slf4j;importorg.springframework.scheduling.annotation.Async;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;importtk.mybatis.mapper.entity.Example;importtk.mybatis.mapper.entity.Example.Criteria;importjava.math.BigDecimal;importjava.time.LocalDate;importjava.time.LocalDateTime;@Slf4j@ServicepublicclassFirewallServiceImplimplementsFirewallService{@ResourceprivateFirewallAccessLogMapperaccessLogMapper;@ResourceprivateFirewallStatisticsMapperstatisticsMapper;/***异步记录访问日志**@paramaccessLog访问日志*/@Override@Async@TransactionalpublicvoidlogAccessAsync(FirewallAccessLogaccessLog){try{accessLogMapper.insert(accessLog);//更新统计数据updateStatistics(accessLog);}catch(Exceptione){log.error("异步记录访问日志失败:{}",accessLog,e);}}/***更新统计数据**@paramaccessLog访问日志*/privatevoidupdateStatistics(FirewallAccessLogaccessLog){try{LocalDatetoday=LocalDate.now();StringapiPath=accessLog.getApiPath();//查询今日统计Exampleexample=newExample(FirewallStatistics.class);Criteriacriteria=example.createCriteria();criteria.andEqualTo("statDate",today).andEqualTo("apiPath",apiPath);FirewallStatisticsstats=statisticsMapper.selectOneByExample(example);if(stats==null){//创建新的统计记录stats=newFirewallStatistics();stats.setId(SnowflakeIdGenerator.next());stats.setStatDate(today);stats.setApiPath(apiPath);stats.setTotalRequests(1L);stats.setBlockedRequests(accessLog.isBlocked()?1L:0L);stats.setAvgResponseTime(BigDecimal.valueOf(accessLog.getResponseTime()));stats.setCreatedTime(LocalDateTime.now());stats.setUpdatedTime(LocalDateTime.now());statisticsMapper.insert(stats);}else{//更新现有统计记录stats.addRequest(accessLog.isBlocked(),accessLog.getResponseTime());stats.setUpdatedTime(LocalDateTime.now());statisticsMapper.updateByPrimaryKeySelective(stats);}}catch(Exceptione){log.error("更新统计数据失败:{}",accessLog,e);}}}规则实现类packagecom.uhaiin.firewall.service.impl;importcom.uhaiin.firewall.entity.FirewallBlacklist;importcom.uhaiin.firewall.entity.FirewallRule;importcom.uhaiin.firewall.entity.FirewallWhitelist;importcom.uhaiin.firewall.mapper.FirewallBlacklistMapper;importcom.uhaiin.firewall.mapper.FirewallRuleMapper;importcom.uhaiin.firewall.mapper.FirewallWhitelistMapper;importcom.uhaiin.firewall.service.RuleManagerService;importjakarta.annotation.PostConstruct;importjakarta.annotation.Resource;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Service;importtk.mybatis.mapper.entity.Example;importtk.mybatis.mapper.entity.Example.Criteria;importjava.time.LocalDateTime;importjava.util.List;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;importjava.util.stream.Collectors;@Slf4j@ServicepublicclassRuleManagerServiceImplimplementsRuleManagerService{/***规则缓存*/privatefinalMap&lt;String,FirewallRule&gt;ruleCache=newConcurrentHashMap&lt;&gt;();/***黑名单缓存*/privatefinalMap&lt;String,FirewallBlacklist&gt;blacklistCache=newConcurrentHashMap&lt;&gt;();/***白名单缓存*/privatefinalMap&lt;String,FirewallWhitelist&gt;whitelistCache=newConcurrentHashMap&lt;&gt;();@ResourceprivateFirewallRuleMapperruleMapper;@ResourceprivateFirewallBlacklistMapperblacklistMapper;@ResourceprivateFirewallWhitelistMapperwhitelistMapper;/***初始化加载规则*/@PostConstructpublicvoidinit(){log.info("初始化防火墙规则管理器...");refreshRules();refreshBlacklist();refreshWhitelist();log.info("防火墙规则管理器初始化完成，加载规则:{},黑名单:{},白名单:{}",ruleCache.size(),blacklistCache.size(),whitelistCache.size());}/***获取匹配指定API路径的规则**@paramapiPathAPI路径*@return匹配的规则，如果没有匹配则返回null*/@OverridepublicFirewallRulegetMatchingRule(StringapiPath){if(apiPath==null){returnnull;}//优先精确匹配FirewallRuleexactMatch=ruleCache.get(apiPath);if(exactMatch!=null&amp;&amp;exactMatch.isEffectiveEnabled()){returnexactMatch;}//模式匹配for(FirewallRulerule:ruleCache.values()){if(rule.isEffectiveEnabled()&amp;&amp;rule.matches(apiPath)){returnrule;}}returnnull;}/***检查IP是否在黑名单中**@paramipAddressIP地址*@return是否在黑名单中*/@OverridepublicbooleanisBlacklisted(StringipAddress){if(ipAddress==null){returnfalse;}//精确匹配FirewallBlacklistexactMatch=blacklistCache.get(ipAddress);if(exactMatch!=null&amp;&amp;exactMatch.isValid()){returntrue;}//模式匹配for(FirewallBlacklistblacklist:blacklistCache.values()){if(blacklist.isValid()&amp;&amp;blacklist.matches(ipAddress)){returntrue;}}returnfalse;}/***检查IP是否在白名单中**@paramipAddressIP地址*@return是否在白名单中*/@OverridepublicbooleanisWhitelisted(StringipAddress){if(ipAddress==null){returnfalse;}//精确匹配FirewallWhitelistexactMatch=whitelistCache.get(ipAddress);if(exactMatch!=null&amp;&amp;exactMatch.isValid()){returntrue;}//模式匹配for(FirewallWhitelistwhitelist:whitelistCache.values()){if(whitelist.isValid()&amp;&amp;whitelist.matches(ipAddress)){returntrue;}}returnfalse;}/***获取所有规则**@return规则列表*/@OverridepublicList&lt;FirewallRule&gt;getAllRules(){Exampleexample=newExample(FirewallRule.class);example.orderBy("id").asc();returnruleMapper.selectByExample(example);}/***获取所有黑名单**@return黑名单列表*/@OverridepublicList&lt;FirewallBlacklist&gt;getAllBlacklist(){Exampleexample=newExample(FirewallBlacklist.class);example.orderBy("id").asc();returnblacklistMapper.selectByExample(example);}/***获取所有白名单**@return白名单列表*/@OverridepublicList&lt;FirewallWhitelist&gt;getAllWhitelist(){Exampleexample=newExample(FirewallWhitelist.class);example.orderBy("id").asc();returnwhitelistMapper.selectByExample(example);}/***添加或更新规则**@paramrule规则*@return是否成功*/@OverridepublicbooleansaveRule(FirewallRulerule){try{if(rule.getId()==null){ruleMapper.insert(rule);}else{ruleMapper.updateByPrimaryKeySelective(rule);}refreshRules();log.info("规则保存成功:{}",rule.getRuleName());returntrue;}catch(Exceptione){log.error("规则保存失败:{}",rule.getRuleName(),e);returnfalse;}}/***删除规则**@paramid规则ID*@return是否成功*/@OverridepublicbooleandeleteRule(Longid){try{ruleMapper.deleteByPrimaryKey(id);refreshRules();log.info("规则删除成功:{}",id);returntrue;}catch(Exceptione){log.error("规则删除失败:{}",id,e);returnfalse;}}/***添加黑名单**@paramblacklist黑名单*@return是否成功*/@OverridepublicbooleanaddBlacklist(FirewallBlacklistblacklist){try{blacklistMapper.insert(blacklist);refreshBlacklist();log.info("黑名单添加成功:{}",blacklist.getIpAddress());returntrue;}catch(Exceptione){log.error("黑名单添加失败:{}",blacklist.getIpAddress(),e);returnfalse;}}/***删除黑名单**@paramid黑名单ID*@return是否成功*/@OverridepublicbooleandeleteBlacklist(Longid){try{blacklistMapper.deleteByPrimaryKey(id);refreshBlacklist();log.info("黑名单删除成功:{}",id);returntrue;}catch(Exceptione){log.error("黑名单删除失败:{}",id,e);returnfalse;}}/***添加白名单**@paramwhitelist白名单*@return是否成功*/@OverridepublicbooleanaddWhitelist(FirewallWhitelistwhitelist){try{whitelistMapper.insert(whitelist);refreshWhitelist();log.info("白名单添加成功:{}",whitelist.getIpAddress());returntrue;}catch(Exceptione){log.error("白名单添加失败:{}",whitelist.getIpAddress(),e);returnfalse;}}/***删除白名单**@paramid白名单ID*@return是否成功*/@OverridepublicbooleandeleteWhitelist(Longid){try{whitelistMapper.deleteByPrimaryKey(id);refreshWhitelist();log.info("白名单删除成功:{}",id);returntrue;}catch(Exceptione){log.error("白名单删除失败:{}",id,e);returnfalse;}}/***更新黑名单**@paramblacklist黑名单*@return是否成功*/@OverridepublicbooleanupdateBlacklist(FirewallBlacklistblacklist){try{blacklistMapper.updateByPrimaryKeySelective(blacklist);refreshBlacklist();log.info("黑名单更新成功:{}",blacklist.getIpAddress());returntrue;}catch(Exceptione){log.error("黑名单更新失败:{}",blacklist.getIpAddress(),e);returnfalse;}}/***更新白名单**@paramwhitelist白名单*@return是否成功*/@OverridepublicbooleanupdateWhitelist(FirewallWhitelistwhitelist){try{whitelistMapper.updateByPrimaryKeySelective(whitelist);refreshWhitelist();log.info("白名单更新成功:{}",whitelist.getIpAddress());returntrue;}catch(Exceptione){log.error("白名单更新失败:{}",whitelist.getIpAddress(),e);returnfalse;}}/***刷新规则缓存*/@OverridepublicvoidrefreshRules(){try{Exampleexample=newExample(FirewallRule.class);Criteriacriteria=example.createCriteria();criteria.andEqualTo("enabled",true);//添加排序example.orderBy("id").asc();List&lt;FirewallRule&gt;rules=ruleMapper.selectByExample(example);ruleCache.clear();ruleCache.putAll(rules.stream().collect(Collectors.toMap(FirewallRule::getApiPattern,rule-&gt;rule,(oldBlackList,newBlackList)-&gt;newBlackList)));log.debug("规则缓存刷新完成，共加载{}条规则",ruleCache.size());}catch(Exceptione){log.error("刷新规则缓存失败",e);}}/***刷新黑名单缓存*/@OverridepublicvoidrefreshBlacklist(){try{Exampleexample=newExample(FirewallBlacklist.class);Criteriacriteria=example.createCriteria();criteria.andEqualTo("enabled",true).andCondition("expire_timeISNULLORexpire_time&gt;",LocalDateTime.now());example.orderBy("id").asc();List&lt;FirewallBlacklist&gt;blacklists=blacklistMapper.selectByExample(example);blacklistCache.clear();blacklistCache.putAll(blacklists.stream().collect(Collectors.toMap(FirewallBlacklist::getIpAddress,blacklist-&gt;blacklist,(oldBlackList,newBlackList)-&gt;newBlackList)));log.debug("黑名单缓存刷新完成，共加载{}条记录",blacklistCache.size());}catch(Exceptione){log.error("刷新黑名单缓存失败",e);}}/***刷新白名单缓存*/@OverridepublicvoidrefreshWhitelist(){try{Exampleexample=newExample(FirewallWhitelist.class);Criteriacriteria=example.createCriteria();criteria.andEqualTo("enabled",true);example.orderBy("id").asc();List&lt;FirewallWhitelist&gt;whitelists=whitelistMapper.selectByExample(example);whitelistCache.clear();whitelistCache.putAll(whitelists.stream().collect(Collectors.toMap(FirewallWhitelist::getIpAddress,whitelist-&gt;whitelist,(oldBlackList,newBlackList)-&gt;newBlackList)));log.debug("白名单缓存刷新完成，共加载{}条记录",whitelistCache.size());}catch(Exceptione){log.error("刷新白名单缓存失败",e);}}}定时任务定期清理过期规则packagecom.uhaiin.firewall.scheduled;importcom.uhaiin.firewall.entity.FirewallAccessLog;importcom.uhaiin.firewall.entity.FirewallBlacklist;importcom.uhaiin.firewall.mapper.FirewallAccessLogMapper;importcom.uhaiin.firewall.mapper.FirewallBlacklistMapper;importcom.uhaiin.firewall.service.RuleManagerService;importjakarta.annotation.Resource;importlombok.extern.slf4j.Slf4j;importorg.springframework.context.annotation.Configuration;importorg.springframework.scheduling.annotation.EnableScheduling;importorg.springframework.scheduling.annotation.Scheduled;importorg.springframework.stereotype.Component;importorg.springframework.transaction.annotation.Transactional;importtk.mybatis.mapper.entity.Example;importtk.mybatis.mapper.entity.Example.Criteria;importjava.time.LocalDate;importjava.time.LocalDateTime;@Component@Configuration@EnableScheduling@Slf4jpublicclassFirewallTask{@ResourceprivateFirewallAccessLogMapperaccessLogMapper;@ResourceprivateRuleManagerServiceruleManagerService;@ResourceprivateFirewallBlacklistMapperfirewallBlacklistMapper;/***定时清理旧的访问日志（每天凌晨2点执行）*/@Transactional@Scheduled(cron="002**?")voidcleanOldAccessLogs(){try{//保留30天LocalDateTimecutoffTime=LocalDateTime.now().minusDays(30);Exampleexample=newExample(FirewallAccessLog.class);Criteriacriteria=example.createCriteria();criteria.andLessThan("requestTime",cutoffTime);intdeleted=accessLogMapper.deleteByExample(example);log.info("清理30天前的访问日志，删除{}条记录",deleted);}catch(Exceptione){log.error("清理旧访问日志失败",e);}}/***定时清理旧的统计数据（每天凌晨3点执行）*/@Transactional@Scheduled(cron="003**?")voidcleanOldStatistics(){try{//保留90天LocalDatecutoffDate=LocalDate.now().minusDays(90);Exampleexample=newExample(FirewallAccessLog.class);Criteriacriteria=example.createCriteria();criteria.andLessThan("requestTime",cutoffDate);intdeleted=accessLogMapper.deleteByExample(example);log.info("清理90天前的统计数据，删除{}条记录",deleted);}catch(Exceptione){log.error("清理旧统计数据失败",e);}}/***定时刷新缓存（每5分钟）*/@Scheduled(fixedRate=5*60*1000)@TransactionalpublicvoidscheduledRefresh(){ruleManagerService.refreshRules();ruleManagerService.refreshBlacklist();ruleManagerService.refreshWhitelist();//清理过期的黑名单try{LocalDateTimenow=LocalDateTime.now();Exampleexample=newExample(FirewallBlacklist.class);Example.Criteriacriteria=example.createCriteria();criteria.andLessThanOrEqualTo("expireTime",now);intcleaned=firewallBlacklistMapper.deleteByExample(example);if(cleaned&gt;0){log.info("清理过期黑名单{}条",cleaned);ruleManagerService.refreshBlacklist();}}catch(Exceptione){log.error("清理过期黑名单失败",e);}}}POM&lt;?xmlversion="1.0"encoding="UTF-8"?&gt;&lt;projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0https://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;&lt;parent&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;&lt;version&gt;4.0.0&lt;/version&gt;&lt;relativePath/&gt;&lt;!--lookupparentfromrepository--&gt;&lt;/parent&gt;&lt;groupId&gt;com.uhaiin&lt;/groupId&gt;&lt;artifactId&gt;demo&lt;/artifactId&gt;&lt;version&gt;0.0.1&lt;/version&gt;&lt;name&gt;demo&lt;/name&gt;&lt;description&gt;Abuginthecodeisworthtwointhedocumentation&lt;/description&gt;&lt;properties&gt;&lt;java.version&gt;21&lt;/java.version&gt;&lt;/properties&gt;&lt;dependencies&gt;&lt;!--启动器--&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter&lt;/artifactId&gt;&lt;/dependency&gt;&lt;!--web--&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;&lt;/dependency&gt;&lt;!--SpringBootStarterActuator--&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;&lt;/dependency&gt;&lt;!--测试套件--&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;&lt;scope&gt;test&lt;/scope&gt;&lt;/dependency&gt;&lt;!--自动配置类--&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-autoconfigure&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-configuration-processor&lt;/artifactId&gt;&lt;optional&gt;true&lt;/optional&gt;&lt;/dependency&gt;&lt;!--预编译工具--&gt;&lt;dependency&gt;&lt;groupId&gt;org.projectlombok&lt;/groupId&gt;&lt;artifactId&gt;lombok&lt;/artifactId&gt;&lt;optional&gt;true&lt;/optional&gt;&lt;/dependency&gt;&lt;!--热部署工具--&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-devtools&lt;/artifactId&gt;&lt;scope&gt;runtime&lt;/scope&gt;&lt;optional&gt;true&lt;/optional&gt;&lt;/dependency&gt;&lt;!--PostgreSQL驱动--&gt;&lt;dependency&gt;&lt;groupId&gt;org.postgresql&lt;/groupId&gt;&lt;artifactId&gt;postgresql&lt;/artifactId&gt;&lt;scope&gt;runtime&lt;/scope&gt;&lt;/dependency&gt;&lt;!--通用Mapper4之tk.mybatis--&gt;&lt;dependency&gt;&lt;groupId&gt;tk.mybatis&lt;/groupId&gt;&lt;artifactId&gt;mapper&lt;/artifactId&gt;&lt;version&gt;4.3.0&lt;/version&gt;&lt;/dependency&gt;&lt;!--MyBatisSpringBootStarter--&gt;&lt;dependency&gt;&lt;groupId&gt;org.mybatis.spring.boot&lt;/groupId&gt;&lt;artifactId&gt;mybatis-spring-boot-starter&lt;/artifactId&gt;&lt;version&gt;4.0.1&lt;/version&gt;&lt;/dependency&gt;&lt;!--fastjson2--&gt;&lt;dependency&gt;&lt;groupId&gt;com.alibaba.fastjson2&lt;/groupId&gt;&lt;artifactId&gt;fastjson2&lt;/artifactId&gt;&lt;version&gt;2.0.60&lt;/version&gt;&lt;/dependency&gt;&lt;!--Jackson(JSON处理)--&gt;&lt;dependency&gt;&lt;groupId&gt;com.fasterxml.jackson.core&lt;/groupId&gt;&lt;artifactId&gt;jackson-databind&lt;/artifactId&gt;&lt;/dependency&gt;&lt;!--Guava(限流器)--&gt;&lt;dependency&gt;&lt;groupId&gt;com.google.guava&lt;/groupId&gt;&lt;artifactId&gt;guava&lt;/artifactId&gt;&lt;version&gt;33.5.0-jre&lt;/version&gt;&lt;exclusions&gt;&lt;exclusion&gt;&lt;artifactId&gt;checker-qual&lt;/artifactId&gt;&lt;groupId&gt;org.checkerframework&lt;/groupId&gt;&lt;/exclusion&gt;&lt;/exclusions&gt;&lt;/dependency&gt;&lt;/dependencies&gt;&lt;build&gt;&lt;plugins&gt;&lt;plugin&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;&lt;configuration&gt;&lt;excludeDevtools&gt;true&lt;/excludeDevtools&gt;&lt;excludes&gt;&lt;exclude&gt;&lt;groupId&gt;org.projectlombok&lt;/groupId&gt;&lt;artifactId&gt;lombok&lt;/artifactId&gt;&lt;/exclude&gt;&lt;/excludes&gt;&lt;/configuration&gt;&lt;/plugin&gt;&lt;/plugins&gt;&lt;/build&gt;&lt;/project&gt;</li>
  <li>阿里巴巴Java代码规范（即《阿里巴巴Java开发手册》）是一套由阿里巴巴集团总结并发布的Java编程最佳实践，旨在提升代码质量、可读性、可维护性和团队协作效率。该规范已被广泛应用于国内众多企业和开发团队，并被集成到IDE工具中（如AlibabaJavaCodingGuidelines插件）。以下是该规范的核心内容分类目录，适合作为技术分享的结构：阿里巴巴Java代码规范1.编程规约命名风格类名使用UpperCamelCase（大驼峰）方法名、参数名、变量名使用lowerCamelCase（小驼峰）常量名全部大写，用下划线分隔杜绝使用l、O等易混淆字母命名变量包名统一使用小写字母，单数形式常量定义不允许出现魔法值（如直接写3600、”UTF-8”）常量必须定义在constant类或枚举中使用BigDecimal处理金额时，必须通过字符串构造代码格式大括号使用大括号换行（K&amp;R风格）方法参数过长时，换行对齐每行不超过120个字符空行与缩进规范，提升可读性OOP规约（面向对象）避免用Object的equals直接比较null覆盖equals时必须覆盖hashCode使用Optional时避免过度嵌套或用于方法参数/返回值泛滥接口方法默认不加public（接口方法天然public）集合处理初始化集合时指定初始容量，避免频繁扩容使用isEmpty()而非size()==0判断集合为空不要在foreach循环中进行元素增删操作使用Comparator链式调用排序（Java8+）并发处理禁止使用Executors创建线程池，推荐使用ThreadPoolExecutor显式创建必须使用ThreadLocal时，注意remove()防止内存泄漏高并发场景下注意SimpleDateFormat的线程安全问题，推荐使用DateTimeFormatter控制语句if/else、for、while等必须使用大括号switch必须有default分支避免多层嵌套，控制圈复杂度注释规约类、方法必须有Javadoc注释方法注释需说明用途、参数、返回值、异常注释与代码同步更新，禁止无意义注释异常处理不要捕获异常后“吞掉”不处理不要用异常控制流程（如用catch做条件判断）自定义异常应继承RuntimeException，并命名以Exception结尾finally块中不允许使用return日志规约使用SLF4J门面+Logback实现，禁止直接使用System.out或printStackTrace日志输出禁止拼接字符串，使用占位符{}示例：logger.info("User{}loggedinat{}",userId,time);生产环境禁止输出debug级别日志异常日志必须记录完整堆栈2.异常日志错误码统一定义规范日志文件滚动策略与敏感信息脱敏异常分层处理：DAO、Service、Controller各层异常职责使用MDC实现链路追踪上下文记录3.单元测试测试类命名：XXXTest，与被测类同名加后缀测试方法命名建议：方法名_场景_预期结果，如saveUser_nullName_throwException单元测试必须遵循AIR原则：Automatic（自动化）、Independent（独立运行）、Repeatable（可重复）禁止使用System.out验证测试结果，必须使用assert测试数据准备应可重复执行，避免依赖外部状态4.安全规约防止SQL注入：禁止字符串拼接SQL，优先使用预编译（PreparedStatement）或MyBatis参数化防止XSS、CSRF攻击（结合SpringSecurity）敏感数据加密存储（如密码、身份证号）不要在日志中打印密钥、密码等敏感信息5.数据库规约建表必须有创建时间和修改时间等时间字段禁止使用select*，必须明确指定字段IN操作的集合元素数不超过1000字段必须有注释，命名使用小写字母+下划线（如user_id）合理使用索引，避免在索引字段上做函数运算分页查询注意性能，避免深分页6.工程结构分层命名规范：controller、service、dao、dto、vo、entity等接口与实现分离，使用ServiceImpl命名实现类二方库变更必须通知所有依赖方应用分层领域模型规范（DO、DTO、BO、VO等的使用场景）7.设计规约（设计模式与架构）优先使用组合而非继承类成员访问控制“最小化”避免一个类承担过多职责（SRP原则）推荐使用工厂模式、策略模式解耦业务逻辑高并发场景下避免使用BigDecimal(double)构造方法8.IDE辅助与插件实践安装AlibabaJavaCodingGuidelines插件（IntelliJIDEA/Eclipse）开发中实时扫描违规代码代码提交前进行规范检查（可集成到CI流程）9.实战案例对比展示“不规范代码”vs“规范后代码”典型问题分析：如空指针、线程安全、日志泄露、异常处理不当结合SonarLint/Checkstyle实现自动化检测10.总结与建议规范不是约束，而是质量保障团队统一规范，提升协作效率持续集成中加入代码规范检查（如GitHook+插件扫描）推荐阅读：《阿里巴巴Java开发手册》最新版（如黄山版、龙井版等</li>
  <li>快速入门参考官方文档GitHubPages快速入门域名解析下面用阿里云域名解析为例，分别解析ipv4和ipv6参考如下：将@解析修改为你自己实际的&lt;username&gt;.github.io多博客部署一个GitHub账号怎么实现多个静态网站？上面是基础操作实现个人网站的搭建，但是我现在只有一个GitHub账号,我还想实现多个网站。在&lt;username&gt;.github.io仓库存在的前提下，我们再创建一个仓库，现在的名字可以随便取。往仓库里面放入index.html文件设置里面开启GitHubPage，选择实际分支并设置https即可此时就可以通过username.github.io/仓库名访问了(部署需要时间,需要稍等片刻哦)</li>
  <li>git初始化设置配置Git用户名和邮箱，无论你使用Windows的命令提示符、GitBash，还是macOS/Linux的终端，都需要先打开一个命令窗口设置用户名：在终端中输入以下命令，然后替换“你的用户名”为你想要设置的用户名gitconfig--globaluser.name"你的用户名"设置邮箱接着输入以下命令，将“你的邮箱地址”替换成你的邮箱。gitconfig--globaluser.email"你的邮箱地址"验证配置你可以使用查看所有全局配置命令检查是否配置成功gitconfig--global--list生成SSH密钥在终端中，运行以下命令来生成一个新的SSH密钥对。这里，你需要将your_email@example.com替换成你的GitHub邮箱地址，并将id_ed25519替换成你想要的密钥文件名（如果你想要使用RSA算法，可以将-ted25519改为-trsa-b4096）ssh-keygen-ted25519-C"your_email@example.com"设置密码(可选)在执行上述命令后，系统会询问是否需要为你的密钥设置密码。如果你想要每次使用密钥时都输入密码，可以设置一个密码。如果你不想每次都输入密码，可以直接按回车跳过这一步。看你的公钥生成的公钥位于~/.ssh/id_ed25519.pub（如果你使用的是默认的文件名和算法）。添加SSH密钥到GitHub复制你的公钥内容（通过cat~/.ssh/id_ed25519.pub命令得到），然后登录到你的GitHub账户，在“Settings”-&gt;“SSHandGPGkeys”-&gt;“NewSSHkey”页面中，将公钥粘贴到“Key”框中，并添加一个描述性的标题（例如“MyMacBookPro”），最后点击“AddSSHkey”。测试连接ssh-Tgit@github.com系统会要求你确认是否继续连接，输入yes即可。如果一切设置正确，你将看到类似“Hiusername!You’vesuccessfullyauthenticated,butGitHubdoesnotprovideshellaccess.”的欢迎信息。这样，你就成功地在Mac上生成了GitHub的SSH密钥，并配置好了与GitHub的SSH连接。</li>
  <li>在使用ubuntu系统的过程中，你是否遇到过磁盘明明有未分配空间，却不知如何利用起来的困扰？别担心，本文将手把手教你把这些闲置的未分配磁盘空间挂载到系统中，充分发挥磁盘潜力，让你的Ubuntu存储更加充裕。一、前期准备与基础认知查看磁盘现状在开始挂载操作前，我们要先摸清磁盘底细。打开终端，输入lsblk命令，这行指令会像一份详细的磁盘地图，清晰展示出系统里所有的块设备，包括硬盘、固态硬盘、U盘以及它们各自的分区状态，有没有挂载点一目了然。例如，你可能看到类似这样的输出：NAMEMAJ:MINRMSIZEROTYPEMOUNTPOINTsda8:00465.8G0disk├─sda18:10512M0part/boot/efi├─sda28:2073.2G0part/└─sda38:30392.1G0part这里的sda3就是我们说的未分配空间，它没有挂载点，静静等着被启用。另外，df-h命令也别落下，它专注于已挂载磁盘的使用详情，像容量多少、已用多少、还剩多少，让你对当前在用磁盘空间心里有数，不过它不会显示未挂载的部分。二、挂载未分配空间实战使用lvextend命令扩展当前逻辑卷，如下所示：sudolvextend-l+100%FREE/dev/mapper/sda3/dev/mapper/sda3换成实际已经挂载的硬盘，+100%FREE表示将所有未分配的空间都增加到/dev/mapper/sda3三、更新逻辑卷的实际容量sudoresize2fs/dev/mapper/sda3</li>
  <li>Homebrew官网macOS常规安装（推荐）$/bin/zsh-c"$(curl-fsSLhttps://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"极速安装（精简版）$/bin/zsh-c"$(curl-fsSLhttps://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"speed卸载$/bin/zsh-c"$(curl-fsSLhttps://gitee.com/cunkai/HomebrewCN/raw/master/HomebrewUninstall.sh)"常见错误解决方案Linux安装$rmHomebrew.sh;wgethttps://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh;bashHomebrew.sh卸载$rmHomebrewUninstall.sh;wgethttps://gitee.com/cunkai/HomebrewCN/raw/master/HomebrewUninstall.sh;bashHomebrewUninstall.sh</li>
  <li>编译环境系统内核$uname-a4.19.90-52.39.v2207.ky10.aarch64#4SMPWedJun515:52:50CST2024aarch64aarch64aarch64GNU/LinuxJava$java-versionopenjdkversion"1.8.0_312"OpenJDKRuntimeEnvironmentBisheng(build1.8.0_312-b07)OpenJDK64-BitServerVMBisheng(build25.312-b07,mixedmode)ant$ant-versionApacheAnt(TM)version1.10.15compiledonAugust252024Java编译安装需要安装并配置java环境变量，在这里跳过安装，讲下配置环境变量，以我的java安装目录为：/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.312.b07-10.ky10.aarch64为例子：在/etc/bash.bashrc中写入以下配置：exportJAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.312.b07-10.ky10.aarch64exportCLASSPATH=$:CLASSPATH:$JAVA_HOME/lib/exportPATH=$PATH:$JAVA_HOME/bin刷新配置source/etc/bash.bashrc配置文件说明sudovim~/.bashrc(这是各个用户目录下的配置)sudovim/etc/bash.bashrc(根用户的配置，我们需要把环境变量配置在这里，所有的用户都可以读取)sudovim/etc/profile(这是ubuntu下的配置，opencv的脚本读不到)提示：经过我的测试，麒麟系统在/etc/profile下配置同样生效，刷新配置记得使用:source/etc/profile验证在控制台输入：echo$JAVA_HOME正确输出你的java的安装环境即可ant编译同时需要依赖于ant环境，先通过ant-version检查有没有安装，没有安装的情况下通过一下方式安装ant下载下载地址:https://ant.apache.org/bindownload.cgi以下载apache-ant-1.10.15-bin.zip为例，上传到/usr/local/ant中并解压：unzipapache-ant-1.10.15-bin.zip安装解压后就安装好了，开箱即用配置ant环境变量在/etc/profile中加入以下代码：exportANT_HOME=/usr/local/antexportPATH=$JAVA_HOME/bin:$ANT_HOME/bin:$PATH刷新环境变量source/etc/profile验证在终端输入ant-version能正确打印ant版本即表示正确安装准备工作上面的java和ant配置好了以后现在开始进行编译opencv生成想关的共享库下载OpenCV源码wget-Ohttps://github.com/opencv/opencv/archive/4.1.2.zip将源码下载到/usr/local/opencv中，并通过unzipopencv-4.1.2.zip命令解压出来，进入到源码目录并创建build目录cdopencv-4.1.2mkdirbuildcdbuildcmake在build目录下运行以下命令进行编译以产生对应的opencv-*.jar和共享库libopencv_java.socmake\-DCMAKE_BUILD_TYPE=RELEASE\-DCMAKE_INSTALL_PREFIX=/usr/local/opencv\-DBUILD_TESTS=OFF\-DBUILD_opencv_java=ON\-DBUILD_JAVA=ON\-DWITH_JAVA=ON\..参数说明CMAKE_INSTALL_PREFIX：编译安装的目录，默认/usr/local，本次仅编译不安装运行以上的cmake命令将会出现以下输出：--OpenCVmodules:--Tobebuilt:calib3dcorednnfeatures2dflannhighguiimgcodecsimgprocjavamlobjdetectphotostitchingtsvideovideoio--Disabled:world--Disabledbydependency:---Unavailable:gapijspython2python3--Applications:perf_testsapps--Documentation:NO--Non-freealgorithms:NO--Java:--ant:/usr/local/ant/bin/ant(ver1.10.15)--JNI:$JAVA_HOME/include$JAVA_HOME/include/linux$JAVA_HOME/include--Javawrappers:YES--Javatests:NO----Installto:/usr/local/opencv如果在输出中看到java项出现在编译队列中且java编译选项中ant,JNI和Javawrappers能够被识别，那么恭喜你配置生效了，就可以进行下一步编译编译采用多线程编译，执行以下命令：make-j$(nproc)验证在编译完成后在build目录下的lib文件夹内有编译产物：libopencv_java412.so$findlibopencv_ja*libopencv_java412.so可以看到正确编译并产生了java共享库文件libopencv_java412.so</li>
</ul>