Spring Boot 緩存 Cache 入門詳解
一、概述
(一)緩存的必要性
隨著系統(tǒng)訪問(wèn)量的增加,數(shù)據(jù)庫(kù)往往成為性能瓶頸。為了減少數(shù)據(jù)庫(kù)的壓力,提高系統(tǒng)的響應(yīng)速度,我們可以通過(guò)緩存來(lái)優(yōu)化系統(tǒng)性能。
(二)緩存策略
常見的緩存策略包括:
- 讀寫分離:將讀操作分流到從節(jié)點(diǎn),避免主節(jié)點(diǎn)壓力過(guò)多。
- 分庫(kù)分表:將讀寫操作分?jǐn)偟蕉鄠€(gè)節(jié)點(diǎn),避免單節(jié)點(diǎn)壓力過(guò)多。
- 緩存系統(tǒng):使用緩存系統(tǒng)(如 Redis、Ehcache)來(lái)存儲(chǔ)經(jīng)常訪問(wèn)的數(shù)據(jù),減少對(duì)數(shù)據(jù)庫(kù)的直接訪問(wèn)。
(三)Spring Cache
Spring 3.1 引入了基于注解的緩存技術(shù),通過(guò) @Cacheable
等注解簡(jiǎn)化緩存邏輯,支持多種緩存實(shí)現(xiàn)。Spring Cache 的特點(diǎn)包括:
- 通過(guò)少量的配置注解即可使得既有代碼支持緩存。
- 支持開箱即用,無(wú)需安裝和部署額外第三方組件即可使用緩存。
- 支持 Spring 表達(dá)式語(yǔ)言(SpEL),能使用對(duì)象的任何屬性或者方法來(lái)定義緩存的 key 和 condition。
- 支持 AspectJ,并通過(guò)其實(shí)現(xiàn)任何方法的緩存支持。
- 支持自定義 key 和自定義緩存管理者,具有相當(dāng)?shù)撵`活性和擴(kuò)展性。
二、注解
(一)@Cacheable
@Cacheable
注解用于方法上,緩存方法的執(zhí)行結(jié)果。執(zhí)行過(guò)程如下:
- 判斷方法執(zhí)行結(jié)果的緩存。如果有,則直接返回該緩存結(jié)果。
- 執(zhí)行方法,獲得方法結(jié)果。
- 根據(jù)是否滿足緩存的條件。如果滿足,則緩存方法結(jié)果到緩存。
- 返回方法結(jié)果。
常用屬性:
cacheNames
:緩存名。必填。[]
數(shù)組,可以填寫多個(gè)緩存名。key
:緩存的 key 。允許空。如果為空,則默認(rèn)方法的所有參數(shù)進(jìn)行組合。如果非空,則需要按照 SpEL 配置。例如,@Cacheable(value = "users", key = "#id")
,使用方法參數(shù)id
的值作為緩存的 key 。condition
:基于方法入?yún)?,判斷要緩存的條件。允許空。如果非空,則需要按照 SpEL 配置。例如,@Cacheable(condition="#id > 0")
,需要傳入的id
大于零。unless
:基于方法返回,判斷不緩存的條件。允許空。如果非空,則需要按照 SpEL 配置。例如,@Cacheable(unless="#result == null")
,如果返回結(jié)果為null
,則不進(jìn)行緩存。
不常用屬性:
keyGenerator
:自定義 key 生成器 KeyGenerator Bean 的名字。允許空。如果設(shè)置,則key
失效。cacheManager
:自定義緩存管理器 CacheManager Bean 的名字。允許空。一般不填寫,除非有多個(gè) CacheManager Bean 的情況下。cacheResolver
:自定義緩存解析器 CacheResolver Bean 的名字。允許空。sync
:在獲得不到緩存的情況下,是否同步執(zhí)行方法。默認(rèn)為false
,表示無(wú)需同步。如果設(shè)置為true
,則執(zhí)行方法時(shí),會(huì)進(jìn)行加鎖,保證同一時(shí)刻,有且僅有一個(gè)方法在執(zhí)行,其它線程阻塞等待。
(二)@CachePut
@CachePut
注解用于方法上,緩存方法的執(zhí)行結(jié)果。與 @Cacheable
不同,它的執(zhí)行過(guò)程如下:
- 執(zhí)行方法,獲得方法結(jié)果。也就是說(shuō),無(wú)論是否有緩存,都會(huì)執(zhí)行方法。
- 根據(jù)是否滿足緩存的條件。如果滿足,則緩存方法結(jié)果到緩存。
- 返回方法結(jié)果。
一般來(lái)說(shuō),@Cacheable
搭配讀操作,實(shí)現(xiàn)緩存的被動(dòng)寫;@CachePut
配置寫操作,實(shí)現(xiàn)緩存的主動(dòng)寫。
(三)@CacheEvict
@CacheEvict
注解用于方法上,刪除緩存。相比 @CachePut
,它額外多了兩個(gè)屬性:
allEntries
:是否刪除緩存名(cacheNames
)下,所有 key 對(duì)應(yīng)的緩存。默認(rèn)為false
,只刪除指定 key 的緩存。beforeInvocation
:是否在方法執(zhí)行前刪除緩存。默認(rèn)為false
,在方法執(zhí)行后刪除緩存。
(四)@Caching
@Caching
注解用于方法上,可以組合使用多個(gè) @Cacheable
、@CachePut
、@CacheEvict
注解。不太常用,可以暫時(shí)忽略。
(五)@CacheConfig
@CacheConfig
注解用于類上,共享如下四個(gè)屬性的配置:
cacheNames
keyGenerator
cacheManager
cacheResolver
(六)@EnableCaching
@EnableCaching
注解用于標(biāo)記開啟 Spring Cache 功能,所以一定要添加。
三、Spring Boot 集成
(一)依賴
在 Spring Boot 里,提供了 spring-boot-starter-cache
庫(kù),實(shí)現(xiàn) Spring Cache 的自動(dòng)化配置,通過(guò) CacheAutoConfiguration
配置類。
(二)緩存工具和框架
在 Java 后端開發(fā)中,常見的緩存工具和框架列舉如下:
- 本地緩存:Guava LocalCache、Ehcache、Caffeine 。Ehcache 的功能更加豐富,Caffeine 的性能要比 Guava LocalCache 好。
- 分布式緩存:Redis、Memcached、Tair 。Redis 最為主流和常用。
(三)自動(dòng)配置
在這些緩存方案當(dāng)中,spring-boot-starter-cache
怎么知道使用哪種呢?在默認(rèn)情況下,Spring Boot 會(huì)按照如下順序,自動(dòng)判斷使用哪種緩存方案,創(chuàng)建對(duì)應(yīng)的 CacheManager 緩存管理器。
private static final Map<CacheType, Class<?>> MAPPINGS; static { Map<CacheType, Class<?>> mappings = new EnumMap<>(CacheType.class); mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class); mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class); mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class); mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class); mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class); mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class); mappings.put(CacheType.REDIS, RedisCacheConfiguration.class); mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class); mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class); mappings.put(CacheType.NONE, NoOpCacheConfiguration.class); MAPPINGS = Collections.unmodifiableMap(mappings); }
最差的情況下,會(huì)使用 SimpleCacheConfiguration
。因?yàn)樽詣?dòng)判斷可能和我們希望使用的緩存方案不同,此時(shí)我們可以手動(dòng)配置 spring.cache.type
指定類型。
四、Ehcache 示例
(一)引入依賴
在 pom.xml
文件中,引入相關(guān)依賴。
<dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>
(二)應(yīng)用配置文件
在 resources
目錄下,創(chuàng)建 application.yaml
配置文件。配置如下:
spring: cache: type: ehcache
(三)Ehcache 配置文件
在 resources
目錄下,創(chuàng)建 ehcache.xml
配置文件。配置如下:
<ehcache> <cache name="users" maxElementsInMemory="1000" timeToLiveSeconds="60" memoryStoreEvictionPolicy="LRU"/> </ehcache>
(四)Application
創(chuàng)建 Application.java
類,代碼如下:
@SpringBootApplication @EnableCaching public class Application { }
(五)UserDO
在 cn.iocoder.springboot.lab21.cache.dataobject
包路徑下,創(chuàng)建 UserDO.java
類,用戶 DO 。代碼如下:
@TableName(value = "users") public class UserDO { private Integer id; private String username; private String password; private Date createTime; @TableLogic private Integer deleted; }
(六)UserMapper
在 cn.iocoder.springboot.lab21.cache.mapper
包路徑下,創(chuàng)建 UserMapper
接口。代碼如下:
@Repository @CacheConfig(cacheNames = "users") public interface UserMapper extends BaseMapper<UserDO> { @Cacheable(key = "#id") UserDO selectById(Integer id); @CachePut(key = "#user.id") default UserDO insert0(UserDO user) { this.insert(user); return user; } @CacheEvict(key = "#id") int deleteById(Integer id); }
(七)UserMapperTest
創(chuàng)建 UserMapperTest
測(cè)試類,我們來(lái)測(cè)試一下簡(jiǎn)單的 UserMapper
的每個(gè)操作。核心代碼如下:
@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserMapperTest { private static final String CACHE_NAME_USER = "users"; @Autowired private UserMapper userMapper; @Autowired private CacheManager cacheManager; @Test public void testCacheManager() { System.out.println(cacheManager); } @Test public void testSelectById() { Integer id = 1; UserDO user = userMapper.selectById(id); System.out.println("user:" + user); Assert.assertNotNull("緩存為空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class)); user = userMapper.selectById(id); System.out.println("user:" + user); } @Test public void testInsert() { UserDO user = new UserDO(); user.setUsername(UUID.randomUUID().toString()); user.setPassword("nicai"); user.setCreateTime(new Date()); user.setDeleted(0); userMapper.insert0(user); Assert.assertNotNull("緩存為空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class)); } @Test public void testDeleteById() { UserDO user = new UserDO(); user.setUsername(UUID.randomUUID().toString()); user.setPassword("nicai"); user.setCreateTime(new Date()); user.setDeleted(0); userMapper.insert0(user); Assert.assertNotNull("緩存為空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class)); userMapper.deleteById(user.getId()); Assert.assertNull("緩存不為空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class)); } }
五、Redis 示例
(一)引入依賴
在 pom.xml
文件中,引入相關(guān)依賴。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
(二)應(yīng)用配置文件
在 resources
目錄下,創(chuàng)建 application.yaml
配置文件。配置如下:
spring: redis: host: 127.0.0.1 port: 6379 password: database: 0 timeout: 0 jedis: pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: -1 cache: type: redis
(三)Application
與 4.4 Application
一致。
(四)UserDO
與 4.5 UserDO
一致。差別在于,需要讓 UserDO
實(shí)現(xiàn) Serializable
接口。
(五)UserMapper
與 4.6 UserMapper
一致。
(六)UserMapperTest
與 4.7 UserMapperTest
基本一致。
六、面試回答思路和答案
(一)緩存的必要性
問(wèn)題:為什么需要使用緩存?
回答:隨著系統(tǒng)訪問(wèn)量的增加,數(shù)據(jù)庫(kù)往往成為性能瓶頸。緩存可以減少數(shù)據(jù)庫(kù)的壓力,提高系統(tǒng)的響應(yīng)速度。緩存系統(tǒng)(如 Redis、Ehcache)可以存儲(chǔ)經(jīng)常訪問(wèn)的數(shù)據(jù),減少對(duì)數(shù)據(jù)庫(kù)的直接訪問(wèn)。
(二)Spring Cache
問(wèn)題:什么是 Spring Cache?
回答:Spring Cache 是 Spring 3.1 引入的基于注解的緩存技術(shù),通過(guò) @Cacheable
等注解簡(jiǎn)化緩存邏輯,支持多種緩存實(shí)現(xiàn)。Spring Cache 的特點(diǎn)包括:通過(guò)少量的配置注解即可使得既有代碼支持緩存;支持開箱即用;支持 Spring 表達(dá)式語(yǔ)言(SpEL);支持 AspectJ;支持自定義 key 和自定義緩存管理者。
(三)@Cacheable
問(wèn)題:@Cacheable
注解的使用方法和屬性有哪些?
回答:@Cacheable
注解用于方法上,緩存方法的執(zhí)行結(jié)果。常用屬性包括:cacheNames
(緩存名,必填),key
(緩存的 key,允許空),condition
(基于方法入?yún)ⅲ袛嘁彺娴臈l件),unless
(基于方法返回,判斷不緩存的條件)。
(四)@CachePut
問(wèn)題:@CachePut
注解的作用是什么?
回答:@CachePut
注解用于方法上,緩存方法的執(zhí)行結(jié)果。與 @Cacheable
不同,它的執(zhí)行過(guò)程如下:執(zhí)行方法,獲得方法結(jié)果;根據(jù)是否滿足緩存的條件,如果滿足,則緩存方法結(jié)果到緩存;返回方法結(jié)果。
(五)@CacheEvict
問(wèn)題:@CacheEvict
注解的作用是什么?
回答:@CacheEvict
注解用于方法上,刪除緩存。相比 @CachePut
,它額外多了兩個(gè)屬性:allEntries
(是否刪除緩存名下所有 key 對(duì)應(yīng)的緩存),beforeInvocation
(是否在方法執(zhí)行前刪除緩存)。
(六)Spring Boot 緩存集成
問(wèn)題:如何在 Spring Boot 中集成緩存?
回答:在 Spring Boot 中,可以通過(guò)引入 spring-boot-starter-cache
依賴來(lái)集成緩存。Spring Boot 會(huì)自動(dòng)判斷使用哪種緩存方案,也可以通過(guò) spring.cache.type
手動(dòng)指定。
(七)Ehcache 和 Redis
問(wèn)題:Ehcache 和 Redis 的區(qū)別是什么?
回答:Ehcache 是一個(gè)純 Java 的進(jìn)程內(nèi)緩存框架,具有快速、精干等特點(diǎn),是 Hibernate 中默認(rèn)的 CacheProvider 。Redis 是一個(gè)基于鍵值對(duì)的內(nèi)存數(shù)據(jù)庫(kù),支持多種數(shù)據(jù)類型,如字符串、哈希、列表、集合、有序集合等。Ehcache 適合用于本地緩存,Redis 適合用于分布式緩存。
(八)緩存策略
問(wèn)題:常見的緩存策略有哪些?
回答:常見的緩存策略包括:讀寫分離(將讀操作分流到從節(jié)點(diǎn),避免主節(jié)點(diǎn)壓力過(guò)多),分庫(kù)分表(將讀寫操作分?jǐn)偟蕉鄠€(gè)節(jié)點(diǎn),避免單節(jié)點(diǎn)壓力過(guò)多),緩存系統(tǒng)(使用緩存系統(tǒng)(如 Redis、Ehcache)來(lái)存儲(chǔ)經(jīng)常訪問(wèn)的數(shù)據(jù),減少對(duì)數(shù)據(jù)庫(kù)的直接訪問(wèn))。
(九)緩存擊穿
問(wèn)題:什么是緩存擊穿?如何解決?
回答:緩存擊穿是指多個(gè)線程同時(shí)訪問(wèn)同一個(gè)緩存鍵,導(dǎo)致緩存失效,多個(gè)線程同時(shí)查詢數(shù)據(jù)庫(kù),造成數(shù)據(jù)庫(kù)壓力。解決方法包括:使用互斥鎖(如 Redis 的 SETNX 命令),保證同一時(shí)刻只有一個(gè)線程查詢數(shù)據(jù)庫(kù);使用緩存預(yù)熱,在系統(tǒng)啟動(dòng)時(shí)預(yù)先加載緩存。
(十)緩存穿透
問(wèn)題:什么是緩存穿透?如何解決?
回答:緩存穿透是指查詢一個(gè)不存在的數(shù)據(jù),導(dǎo)致緩存和數(shù)據(jù)庫(kù)都沒(méi)有命中,直接查詢數(shù)據(jù)庫(kù)。解決方法包括:使用布隆過(guò)濾器,快速判斷數(shù)據(jù)是否存在;對(duì)不存在的數(shù)據(jù)也進(jìn)行緩存,設(shè)置較短的過(guò)期時(shí)間。
(十一)緩存雪崩
問(wèn)題:什么是緩存雪崩?如何解決?
回答:緩存雪崩是指大量緩存同時(shí)失效,導(dǎo)致大量請(qǐng)求直接查詢數(shù)據(jù)庫(kù),造成數(shù)據(jù)庫(kù)壓力。解決方法包括:使用緩存預(yù)熱,在系統(tǒng)啟動(dòng)時(shí)預(yù)先加載緩存;設(shè)置不同的過(guò)期時(shí)間,避免大量緩存同時(shí)失效。
(十二)緩存的過(guò)期策略
問(wèn)題:緩存的過(guò)期策略有哪些?
回答:緩存的過(guò)期策略包括:設(shè)置固定過(guò)期時(shí)間(如 Redis 的 EXPIRE 命令),根據(jù)訪問(wèn)頻率動(dòng)態(tài)調(diào)整過(guò)期時(shí)間,根據(jù)數(shù)據(jù)的重要性動(dòng)態(tài)調(diào)整過(guò)期時(shí)間。
(十三)緩存的淘汰策略
問(wèn)題:緩存的淘汰策略有哪些?
回答:緩存的淘汰策略包括:先進(jìn)先出(FIFO),最近最少使用(LRU),最不經(jīng)常使用(LFU),隨機(jī)淘汰。
(十四)緩存的序列化
問(wèn)題:緩存的序列化有哪些方式?
回答:緩存的序列化方式包括:Java 序列化,JSON 序列化,Protobuf 序列化。Java 序列化簡(jiǎn)單,但性能較差;JSON 序列化性能較好,但需要手動(dòng)實(shí)現(xiàn)序列化和反序列化;Protobuf 序列化性能最好,但需要定義.proto 文件。
(十五)緩存的分布式鎖
問(wèn)題:如何在分布式環(huán)境下實(shí)現(xiàn)緩存的鎖?
回答:在分布式環(huán)境下,可以使用 Redis 的 SETNX 命令實(shí)現(xiàn)分布式鎖。SETNX 命令可以保證同一時(shí)刻只有一個(gè)線程獲取到鎖,從而避免緩存擊穿等問(wèn)題。
(十六)緩存的事務(wù)
問(wèn)題:緩存是否支持事務(wù)?
回答:緩存本身不支持事務(wù),但可以通過(guò)與數(shù)據(jù)庫(kù)事務(wù)結(jié)合來(lái)實(shí)現(xiàn)。例如,在數(shù)據(jù)庫(kù)事務(wù)提交后,更新緩存;在數(shù)據(jù)庫(kù)事務(wù)回滾后,回滾緩存。
(十七)緩存的監(jiān)控
問(wèn)題:如何監(jiān)控緩存的使用情況?
回答:可以使用 Redis 的 INFO 命令監(jiān)控緩存的使用情況,包括內(nèi)存使用、命中率、過(guò)期鍵數(shù)等。也可以使用第三方監(jiān)控工具,如 Prometheus 和 Grafana。
(十八)緩存的優(yōu)化
問(wèn)題:如何優(yōu)化緩存的性能?
回答:優(yōu)化緩存性能的方法包括:選擇合適的緩存策略,使用高效的序列化方式,合理設(shè)置緩存的過(guò)期時(shí)間和淘汰策略,使用分布式鎖避免緩存擊穿,監(jiān)控緩存的使用情況并及時(shí)調(diào)整。
(十九)緩存的命中率
問(wèn)題:如何計(jì)算緩存的命中率?
回答:緩存的命中率可以通過(guò)以下公式計(jì)算:命中率 = 命中次數(shù) /(命中次數(shù) + 未命中次數(shù))??梢酝ㄟ^(guò)監(jiān)控工具獲取命中次數(shù)和未命中次數(shù)。
(二十)緩存的場(chǎng)景
問(wèn)題:緩存適用于哪些場(chǎng)景?
回答:緩存適用于以下場(chǎng)景:經(jīng)常訪問(wèn)的數(shù)據(jù),如用戶信息、商品信息等;數(shù)據(jù)變化不頻繁,如配置信息;對(duì)實(shí)時(shí)性要求不高的數(shù)據(jù),如統(tǒng)計(jì)信息。
(二十一)緩存的局限性
問(wèn)題:緩存有哪些局限性?
回答:緩存的局限性包括:緩存數(shù)據(jù)與數(shù)據(jù)庫(kù)數(shù)據(jù)不一致,緩存擊穿、緩存穿透、緩存雪崩等問(wèn)題,緩存的內(nèi)存有限,緩存的序列化和反序列化可能影響性能,緩存的分布式鎖實(shí)現(xiàn)復(fù)雜。
(二十二)緩存的未來(lái)發(fā)展趨勢(shì)
問(wèn)題:緩存技術(shù)未來(lái)的發(fā)展趨勢(shì)是什么?
回答:緩存技術(shù)未來(lái)的發(fā)展趨勢(shì)包括:與數(shù)據(jù)庫(kù)的深度融合,支持事務(wù)和一致性;支持更多的數(shù)據(jù)類型和查詢方式;提供更好的性能和擴(kuò)展性;提供更便捷的監(jiān)控和管理工具。
以上就是對(duì) Spring Boot 緩存 Cache 入門的詳細(xì)講解和面試回答思路及答案。希望對(duì)初學(xué)者有所幫助,祝大家學(xué)習(xí)愉快!
到此這篇關(guān)于Spring Boot 緩存 Cache 入門的文章就介紹到這了,更多相關(guān)Spring Boot 緩存 Cache內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 在SpringBoot項(xiàng)目中使用JetCache緩存的詳細(xì)教程
- Springboot集成Ehcache3實(shí)現(xiàn)本地緩存的配置方法
- 詳解Springboot @Cacheable 注解(指定緩存位置)
- springboot整合ehcache和redis實(shí)現(xiàn)多級(jí)緩存實(shí)戰(zhàn)案例
- SpringBoot?Cache?二級(jí)緩存的使用
- SpringBoot使用@Cacheable注解實(shí)現(xiàn)緩存功能流程詳解
- SpringBoot如何使用@Cacheable進(jìn)行緩存與取值
- SpringBoot使用@Cacheable時(shí)設(shè)置部分緩存的過(guò)期時(shí)間方式
- 詳解SpringBoot2.0的@Cacheable(Redis)緩存失效時(shí)間解決方案
相關(guān)文章
Java實(shí)現(xiàn)bmp和jpeg圖片格式互轉(zhuǎn)
本文主要介紹了Java實(shí)現(xiàn)bmp和jpeg圖片格式互轉(zhuǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04java實(shí)現(xiàn)上傳文件到服務(wù)器和客戶端
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)上傳文件到服務(wù)器和客戶端,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01Java多線程中的CountDownLatch詳細(xì)解讀
這篇文章主要介紹了Java多線程中的CountDownLatch詳細(xì)解讀,一個(gè)同步輔助類,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個(gè)或多個(gè)線程一直等待,用給定的計(jì)數(shù) 初始化 CountDownLatch,需要的朋友可以參考下2023-11-11java基于servlet實(shí)現(xiàn)文件上傳功能
這篇文章主要為大家詳細(xì)介紹了java基于servlet實(shí)現(xiàn)文件上傳功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09Java將word文件轉(zhuǎn)成pdf文件的操作方法
這篇文章主要介紹了Java將word文件轉(zhuǎn)成pdf文件的操作方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09Java spring webmvc如何實(shí)現(xiàn)控制反轉(zhuǎn)
這篇文章主要介紹了Java spring webmvc如何實(shí)現(xiàn)控制反轉(zhuǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08