แคชของ Springboot ใช้ในการทำงานซึ่งค่อนข้างสะดวกในการใช้งาน มันแนะนำแพ็คเกจการพึ่งพาแคชโดยตรงเช่น Redis หรือ Ehcache และแพ็คเกจการพึ่งพาเริ่มต้นของแคชที่เกี่ยวข้องจากนั้นเพิ่มคำอธิบายประกอบ @EnableCaching ลงในคลาสเริ่มต้นและจากนั้นคุณสามารถใช้ @Cacheable และ @CacheVict เพื่อใช้และลบแคชที่จำเป็น ใช้งานง่ายมาก ฉันเชื่อว่าผู้ที่ใช้ Springboot Cache จะเล่นดังนั้นฉันจะไม่พูดเพิ่มเติมที่นี่ ข้อเสียเปรียบเพียงอย่างเดียวคือ Springboot ใช้การรวมปลั๊กอิน แม้ว่ามันจะสะดวกมากในการใช้งานเมื่อคุณรวม ehcache คุณจะใช้ ehcache และเมื่อคุณรวม Redis คุณจะใช้ Redis หากคุณต้องการใช้ทั้งสองอย่างร่วมกัน Ehcache จะใช้เป็นแคชระดับ 1 และ Redis ในท้องถิ่นจะใช้เป็นแคชระดับ 2 ในตัว เท่าที่ฉันรู้มันเป็นไปไม่ได้ที่จะบรรลุวิธีการเริ่มต้น (หากมีผู้เชี่ยวชาญที่สามารถนำไปใช้ได้โปรดให้คำแนะนำกับฉัน) ท้ายที่สุดบริการจำนวนมากต้องการการปรับใช้หลายจุด หากคุณเลือก Ehcache เพียงอย่างเดียวคุณสามารถตระหนักถึงแคชท้องถิ่นได้ดี อย่างไรก็ตามหากคุณแบ่งปันแคชระหว่างหลายเครื่องมันจะต้องใช้เวลาในการสร้างปัญหา หากคุณเลือกแคช REDIS แบบรวมศูนย์เพราะคุณต้องไปที่เครือข่ายทุกครั้งที่คุณได้รับข้อมูลคุณจะรู้สึกว่าประสิทธิภาพจะไม่ดีเกินไป หัวข้อนี้ส่วนใหญ่จะกล่าวถึงวิธีการรวม Ehcache และ Redis อย่างราบรื่นเป็นแคชระดับแรกและครั้งที่สองตาม Springboot และตระหนักถึงการซิงโครไนซ์แคช
เพื่อที่จะไม่บุกรุกวิธีแคชดั้งเดิมของ Springboot ฉันได้กำหนดคำอธิบายประกอบที่เกี่ยวข้องกับแคชสองที่นี่ดังนี้
@Target ({ElementType.Method}) @Retention (RESINGINTPOLICY.RUNTIME) สาธารณะ @Interface cacheable {ค่าสตริง () ค่าเริ่มต้น ""; คีย์สตริง () ค่าเริ่มต้น ""; // คลาสประเภทคลาสทั่วไป <?> type () ข้อยกเว้นเริ่มต้นคลาส; } @Target ({ElementType.Method}) @Retention (RETINGINTPOLICY.RUNTIME) สาธารณะ @Interface cacheeVict {ค่าสตริง () ค่าเริ่มต้น ""; คีย์สตริง () ค่าเริ่มต้น ""; -เนื่องจากคำอธิบายประกอบสองข้อข้างต้นนั้นเหมือนกันกับคำอธิบายประกอบแคชในฤดูใบไม้ผลิ แต่แอตทริบิวต์ที่ใช้ไม่บ่อยนักบางส่วนจะถูกลบออก เมื่อพูดถึงเรื่องนี้ฉันสงสัยว่าเพื่อนคนใดสังเกตเห็นว่าเมื่อคุณใช้ Redis Cache เพียงอย่างเดียวใน Springboot ค่าแอตทริบิวต์ค่าที่มีคำอธิบายประกอบโดย cacheable และ cacheevict จริง ๆ แล้วกลายเป็นปุ่ม zset value ใน Redis และ ZSET ยังคงว่างเปล่าเช่น @caceable (value = "cache1", key = "key1") ภายใต้สถานการณ์ปกติ CACHE1 -> MAP (KEY1, value1) ควรปรากฏใน REDIS โดยที่ CACHE1 ถูกใช้เป็นชื่อแคชแผนที่เป็นค่าแคชและคีย์เป็นคีย์ใน MAP ซึ่งสามารถแยกแคชภายใต้ชื่อแคชที่แตกต่างกันได้อย่างมีประสิทธิภาพ แต่ในความเป็นจริงมี CACHE1 -> ว่าง (ZSET) และ KEY1 -> value1 คู่คีย์ -ค่าสองคู่ การทดลองพบว่าแคชภายใต้ชื่อแคชที่แตกต่างกันนั้นถูกแชร์อย่างสมบูรณ์ หากคุณสนใจคุณสามารถลองได้ กล่าวคือแอตทริบิวต์ค่านี้จริง ๆ แล้วเป็นการตกแต่งและความเป็นเอกลักษณ์ของคีย์นั้นได้รับการรับประกันโดยแอตทริบิวต์คีย์เท่านั้น ฉันสามารถคิดได้ว่านี่เป็นข้อผิดพลาดในการใช้แคชของฤดูใบไม้ผลิหรือได้รับการออกแบบมาโดยเฉพาะ (ถ้าคุณรู้เหตุผลโปรดให้คำแนะนำกับฉัน)
กลับไปที่หัวข้อด้วยคำอธิบายประกอบนอกจากนี้ยังมีคลาสการประมวลผลคำอธิบายประกอบ ที่นี่ฉันใช้ส่วนของ AOP สำหรับการสกัดกั้นและการใช้งานดั้งเดิมนั้นคล้ายกันจริง ๆ คลาสการประมวลผลส่วนมีดังนี้:
นำเข้า com.xuanwu.apaas.core.multicache.annotation.cacheevict; นำเข้า com.xuanwu.apaas.core.multicache.annotation.caceable; นำเข้า com.xuanwu.apaas.core.utils.jsonutil; นำเข้า org.apache.commons.lang3.Stringutils; นำเข้า org.aspectj.lang.proceedingjoinpoint; นำเข้า org.aspectj.lang.annotation.around; นำเข้า org.aspectj.lang.annotation.around; นำเข้า org.aspectj.lang.annotation.aspect; นำเข้า org.aspectj.lang.annotation.pointcut; นำเข้า org.aspectj.lang.reflect.methodsignature; นำเข้า org.json.jsonarray; นำเข้า org.json.jsonObject; นำเข้า org.slf4j.logger; นำเข้า org.slf4j.loggerfactory; นำเข้า org.springframework.beans.factory.annotation.autowired; นำเข้า org.springframework.core.localvariabletableParameternamediscover; นำเข้า org.springframework.expression.expressionParser; นำเข้า org.springframework.expression.spel.standard.spelexpressionParser; นำเข้า org.springframework.expression.spel.support.standardevaluationContext; นำเข้า org.springframework.stereotype.component; นำเข้า java.lang.reflect.method; / *** ส่วนแคชหลายระดับ* @author rongdi*/ @aspect @component คลาสสาธารณะ MulticAcheaspect {ส่วนตัว Logger Logger สุดท้ายคงที่ = loggerFactory.getLogger (MulticAcheaspect.class); @autowired cachefactory cachefactory ส่วนตัว; // ที่นี่ผู้ฟังจะเริ่มต้นผ่านคอนเทนเนอร์และสวิตช์แคชจะถูกควบคุมตามที่กำหนดค่า @EnableCaching ennotation Private Boolean Cacheenable Cacheenable; @PointCut ("@Annotation (com.xuanwu.apaas.core.multicache.annotation.cacheable)") โมฆะสาธารณะ cacheableaspect () {} @pointcut ("@Annotation @Around ("CacheAbleAspect ()") แคชวัตถุสาธารณะ (ดำเนินการ jointingpoint pointpoint) {// รับรายการพารามิเตอร์ของวิธีการที่แก้ไขโดยวัตถุ facet [] args = joinpoint.getargs (); // ผลลัพธ์เป็นผลการส่งคืนสุดท้ายของผลลัพธ์ของเมธอดผลลัพธ์ = null; // หากไม่ได้เปิดใช้งานแคชให้เรียกใช้วิธีการประมวลผลโดยตรงเพื่อส่งคืนถ้า (! cacheenable) {ลอง {result = joinpoint.proceed (args); } catch (throwable e) {logger.error ("", e); } ผลตอบแทนผลลัพธ์; } // รับประเภทค่าส่งคืนของคลาสพร็อกซีเมธอด returnType = ((methodSignature) joinpoint.getSignature ()). getReturnType (); // รับวิธีการพร็อกซีวิธีการ = ((วิธีการออกแบบ) joinpoint.getSignature ()). getMethod (); // รับความคิดเห็นเกี่ยวกับวิธีพร็อกซี caceable ca = method.getannotation (cacheable.class); // รับคีย์ค่าที่แยกวิเคราะห์โดย EL String Key = Parsekey (ca.key (), วิธี, args); คลาส <?> elementClass = ca.type (); // รับชื่อแคชจากชื่อสตริงคำอธิบายประกอบ = Ca.Value (); ลอง {// ก่อนรับข้อมูลจาก Ehcache String cachevalue = cachefactory.ehget (ชื่อ, คีย์); if (stringutils.isempty (cachevalue)) {// หากไม่มีข้อมูลใน ehcache ให้รับข้อมูลจาก Redis cachevalue = cachefactory.redisget (ชื่อ, key); if (stringutils.isempty (cachevalue)) {// หากไม่มีข้อมูลใน ehcache ให้รับข้อมูลจาก Redis cachevalue = cachefactory.redisget (ชื่อ, key); if (stringutils.isempty (cachevalue)) {// หากไม่มีข้อมูลใน redis // เรียกวิธีการธุรกิจเพื่อรับผลลัพธ์ผลลัพธ์ = joinpoint.proceed (args); // เป็นอนุกรมผลลัพธ์และวางไว้ใน Redis cachefactory.redisput (ชื่อ, key, serialize (ผลลัพธ์)); } else {// ถ้าข้อมูลสามารถรับได้จาก redis // deserialize ข้อมูลที่ได้รับในแคชและส่งคืนถ้า (elementclass == exception.class) {result = deserialize (cachevalue, returnType); } else {result = deserialize (cachevalue, returnType, elementClass); }} // เป็นอนุกรมผลลัพธ์และวางไว้ใน ehcache cachefactory.ehput (ชื่อ, คีย์, serialize (ผลลัพธ์)); } else {// deserialize ข้อมูลที่ได้รับในแคชและส่งคืนถ้า (elementclass == exception.class) {result = deserialize (cachevalue, returnType); } else {result = deserialize (cachevalue, returnType, elementClass); }}} catch (throwable throwable) {logger.error ("", throwable); } ผลตอบแทนผลลัพธ์; } / ** * ล้างแคชก่อนที่จะเรียกวิธีการจากนั้นเรียกใช้วิธีการธุรกิจ * @param joinpoint * @return * @throws throwable * * / @around ("cacheevict ()") วัตถุสาธารณะ evictcache // รับรายการพารามิเตอร์ของวิธีการที่แก้ไขโดยวัตถุ facet [] args = joinpoint.getargs (); // รับคำอธิบายประกอบบนวิธีพร็อกซี cacheevict ce = method.getNanotation (cacheevict.class); // รับคีย์ค่าที่แยกวิเคราะห์โดย EL String Key = Parsekey (Ce.Key (), Method, args); // รับชื่อแคชจากชื่อสตริงคำอธิบายประกอบ = ce.value (); // ล้างแคชแคชที่สอดคล้องกัน cachefactory.cachedel (ชื่อ, คีย์); return joinpoint.proceed (args); } / ** * รับคีย์แคช * คีย์ที่กำหนดไว้ในคำอธิบายประกอบการสนับสนุนการแสดงออกของ Spel * @return * / Private String Parsekey (คีย์สตริง, เมธอดวิธี, วัตถุ [] args) {ถ้า (stringutils.isempty (คีย์)) ส่งคืนค่า null; // รับรายการชื่อพารามิเตอร์ของวิธีการสกัดกั้น (ใช้ไลบรารีคลาสสนับสนุนสปริง) localVariabletableParameterNamediscoverer u = ใหม่ localVariabletableParameternamediscover (); สตริง [] paranamearr = u.getParameternames (วิธีการ); // ใช้ spel สำหรับการแยกวิเคราะห์ที่สำคัญ parser parser = ใหม่ spelexpressionParser (); // บริบท Spel StandardEvaluationContext บริบท = ใหม่ StandardEvaluationContext (); // ใส่พารามิเตอร์เมธอดลงในบริบท Spel สำหรับ (int i = 0; i <paranamearr.length; i ++) {context.setVariable (paranamearr [i], args [i]); } return parser.parseExpression (คีย์) .getValue (บริบท, string.class); } // serialize string ส่วนตัว serialize (object obj) {string result = null; ลอง {result = jsonutil.serialize (obj); } catch (exception e) {result = obj.toString (); } ผลตอบแทนผลลัพธ์; } // deserialize วัตถุส่วนตัว deserialize (string str, clazz คลาส) {object result = null; ลอง {ถ้า (clazz == jsonobject.class) {result = new JsonObject (str); } อื่นถ้า (clazz == jsonarray.class) {result = new JsonArray (str); } else {result = jsonutil.deserialize (str, clazz); }} catch (exception e) {} ผลตอบแทน; } // deserialization, รายการสนับสนุน <xxx> วัตถุส่วนตัว deserialize (str str, class clazz, class elementClass) {object results = null; ลอง {ถ้า (clazz == jsonobject.class) {result = new JsonObject (str); } อื่นถ้า (clazz == jsonarray.class) {result = new JsonArray (str); } else {result = jsonutil.deserialize (str, clazz, elementclass); }} catch (exception e) {} ผลตอบแทน; } โมฆะสาธารณะ setCacheenable (บูลีน cacheenable) {this.cacheenable = cacheenable; -อินเทอร์เฟซด้านบนใช้ตัวแปร cacheenable เพื่อควบคุมว่าจะใช้แคชหรือไม่ เพื่อให้การเข้าถึง Springboot ราบรื่นมีความจำเป็นที่จะต้องควบคุมโดยคำอธิบายประกอบ @enablecaching ดั้งเดิม ที่นี่ฉันใช้ผู้ฟังที่โหลดโดยคอนเทนเนอร์สปริงแล้วค้นหาในผู้ฟังว่ามีคลาสที่แก้ไขโดยคำอธิบายประกอบ @EnableCaching หรือไม่ ถ้าเป็นเช่นนั้นรับวัตถุ multicacheaspect จากภาชนะสปริงแล้วตั้งค่า cacheenable เป็นจริง สิ่งนี้จะช่วยให้การเข้าถึง Springboot ได้อย่างราบรื่น ฉันสงสัยว่ามีวิธีที่หรูหราสำหรับเพื่อนหรือไม่? ยินดีต้อนรับสู่การสื่อสาร! คลาสผู้ฟังมีดังนี้
นำเข้า com.xuanwu.apaas.core.multicache.cachefactory; นำเข้า com.xuanwu.apaas.core.multicache.multicacheaspect; นำเข้า org.springframework.cache.annotation.enablecaching; นำเข้า org.springframework.context.applicationListener; นำเข้า org.springframework.context.event.contextrefreshedEvent; นำเข้า org.springframework.stereotype.component; นำเข้า java.util.map; / ** * ใช้เพื่อค้นหาว่ามีคำอธิบายประกอบเพื่อเปิดใช้งานแคชในโครงการหลังจากการโหลดฤดูใบไม้ผลิเสร็จสมบูรณ์ @EnableCaching * @author rongdi */ @component คลาสสาธารณะ contextrefreshedListener โดยใช้แอปพลิเคชัน เป็นคอนเทนเนอร์สปริงเพื่อป้องกันการเกิดขึ้นของการโทรสองครั้ง (การโหลด MVC จะทริกเกอร์หนึ่งครั้ง) ถ้า (Event.getApplicationContext (). getParent () == null) {// รับคลาสทั้งหมดที่แก้ไขโดย @enableCaching map <string, beans> event.getApplicationContext () if (ถั่ว! = null &&! beans.isempty ()) {multicacheaspect multicache = (multicachepect) event.getApplicationContext (). getBean ("MulticAcheaspect"); multicache.setcacheenable (จริง); -เพื่อให้บรรลุการเข้าถึงที่ราบรื่นเรายังต้องพิจารณาว่า Ehcache หลายจุดสอดคล้องกับ Redis Cache อย่างไรเมื่อปรับใช้ Ehcache หลายจุด ในการใช้งานปกติ Redis มักจะเหมาะสำหรับแคชรวมศูนย์ในระยะยาวและ Ehcache เหมาะสำหรับแคชท้องถิ่นระยะสั้น สมมติว่าขณะนี้มีเซิร์ฟเวอร์ A, B และ C, A และ B ปรับใช้บริการธุรกิจและ C ปรับใช้บริการ Redis เมื่อมีการร้องขอเข้ามารายการส่วนหน้าไม่ว่าจะเป็นซอฟต์แวร์โหลดเช่น LVS หรือ NGINX จะส่งต่อการร้องขอไปยังเซิร์ฟเวอร์เฉพาะ สมมติว่ามันถูกส่งต่อไปยังเซิร์ฟเวอร์ A และเนื้อหาบางอย่างได้รับการแก้ไขและเนื้อหานี้มีอยู่ใน Redis และ Ehcache ในเวลานี้แคช EHCache ของเซิร์ฟเวอร์ A และ REDIS ของเซิร์ฟเวอร์ C นั้นง่ายต่อการควบคุมว่าแคชนั้นไม่ถูกต้องหรือถูกลบ แต่จะควบคุม ehcache ของเซิร์ฟเวอร์ B ในเวลานี้ได้อย่างไร? วิธีที่ใช้กันทั่วไปคือการใช้โหมดการสมัครสมาชิก เมื่อคุณต้องการลบแคชคุณเผยแพร่ข้อความในช่องคงที่ จากนั้นเซิร์ฟเวอร์ธุรกิจแต่ละแห่งสมัครสมาชิกช่องนี้ หลังจากได้รับข้อความคุณจะลบหรือหมดอายุแคช ehcache ในท้องถิ่น (เป็นการดีที่สุดที่จะใช้หมดอายุ แต่ปัจจุบัน Redis รองรับการดำเนินการที่หมดอายุในกุญแจไม่มีวิธีการใช้งานการหมดอายุของสมาชิกในแผนที่ภายใต้กุญแจ เขียนน้อยลงที่นี่เพื่อความสะดวกพวกเขาจะลบแคชโดยตรง) โดยสรุปกระบวนการคือการอัปเดตข้อมูลบางชิ้นก่อนอื่นลบแคชที่เกี่ยวข้องใน Redis จากนั้นเผยแพร่ข้อความด้วยแคชที่ไม่ถูกต้องในช่องทางหนึ่งของ Redis บริการธุรกิจในพื้นที่สมัครรับข้อความของช่องทางนี้ เมื่อบริการธุรกิจได้รับข้อความนี้จะลบแคช ehcache ในท้องถิ่น การกำหนดค่าต่างๆของ Redis มีดังนี้
นำเข้า com.fasterxml.jackson.annotation.jsonautodetect; นำเข้า com.fasterxml.jackson.annotation.propertyaccessor; นำเข้า com.fasterxml.jackson.databind.objectmapper; นำเข้า com.xuanwu.apaas.core.multicache.subscriber.messagesubscriber; นำเข้า org.springframework.cache.cachemanager; นำเข้า org.springframework.context.annotation.bean; นำเข้า org.springframework.context.annotation.configuration; นำเข้า org.springframework.data.redis.cache.rediscachemanager; นำเข้า org.springframework.data.redis.connection.redisconnectionfactory; นำเข้า org.springframework.data.redis.core.redistemplate; นำเข้า org.springframework.data.redis.core.stringredistemplate; นำเข้า org.springframework.data.redis.listener.patterntopic; นำเข้า org.springframework.data.redis.listener.redismessagelistenercontainer; นำเข้า org.springframework.data.redis.listener.adapter.messagelisteneradapter; นำเข้า org.springframework.data.redis.serializer.jackson2jsonredisserializer; @Configuration คลาสสาธารณะ redisconfig {@bean public cachemanager cachemanager (redistemplate redistemplate) {rediscachemanager rcm = ใหม่ rediscachemanager (redistemplate); // ตั้งค่าเวลาหมดอายุแคช (วินาที) RCM.SetDefaultExpiration (600); คืน RCM; } @Bean Public Redistemplate <String, String> redistemplate (โรงงาน RedisconnectionFactory) {template Stringredistemplate = new Stringredistemplate (โรงงาน); jackson2jsonredisserializer Jackson2jsonredisserializer = new Jackson2jsonredisserializer (Object.class); ObjectMapper OM = New ObjectMapper (); OM.SetVisibility (PropertyAccessor.All, jsonautodetect.visibility.any); OM.EnabledefaultTyping (ObjectMapper.defaultTyping.non_final); jackson2jsonredisserializer.setObjectmapper (OM); template.setValueserializer (Jackson2jsonredisserializer); Template.AfterPropertIesset (); เทมเพลตกลับ; } /*** คอนเทนเนอร์ข้อความ Redis Message* คุณสามารถเพิ่มผู้ฟัง Redis หลายรายการที่ฟังหัวข้อต่าง ๆ คุณจะต้องผูกมัดผู้ฟังข้อความและโปรเซสเซอร์การสมัครสมาชิกข้อความที่เกี่ยวข้องและผู้ฟังข้อความ * การเรียกวิธีการที่เกี่ยวข้องของโปรเซสเซอร์การสมัครสมาชิกข้อความผ่านเทคโนโลยีการสะท้อนสำหรับการประมวลผลธุรกิจบางอย่าง * @param ConnectionFactory * @param ListenerAdapter * @return */ @bean Redismasagelistenercontainerer REDISMESSAGELISTENERCONTANER Container = ใหม่ REDISMESSAGELISTENERCONTAINER (); container.SetConnectionFactory (ConnectionFactory); // สมัครสมาชิกช่องคอนเทนเนอร์. // คอนเทนเนอร์นี้สามารถเพิ่มคอนเทนเนอร์ส่งคืน MessageListener หลายรายการ } /** * อะแดปเตอร์ผู้ฟังข้อความผูกโปรเซสเซอร์ข้อความและใช้เทคโนโลยีการสะท้อนเพื่อเรียกวิธีการธุรกิจของโปรเซสเซอร์ข้อความ * @param ตัวรับสัญญาณ * @return * /@bean MessageListenerAdapter ListenerAdapter MessageListenerAdapter (ตัวรับสัญญาณ, "จัดการ"); -คลาสการเผยแพร่ข้อความมีดังนี้:
นำเข้า com.xuanwu.apaas.core.multicache.cachefactory; นำเข้า org.apache.commons.lang3.Stringutils; นำเข้า org.slf4j.logger; นำเข้า org.slf4j.loggerfactory; นำเข้า org.springframework.beans.factory.annotation.autowired; นำเข้า org.springframework.stereotype.component; @Component คลาสสาธารณะ MessagesUbsCriber {ส่วนตัว Logger สุดท้ายคงที่ logger = loggerFactory.getLogger (MessagesUbsCriber.class); @autowired cachefactory cachefactory ส่วนตัว; / *** หลังจากได้รับข้อความของการสมัครสมาชิก REDIS แคชของ ehcache จะไม่ถูกต้อง* รูปแบบข้อความ @param คือ name_key*/ มือจับโมฆะสาธารณะ (ข้อความสตริง) {logger.debug ("redis.ehcache:"+ข้อความ); if (stringutils.isempty (ข้อความ)) {return; } string [] strs = message.split ("#"); ชื่อสตริง = strs [0]; คีย์สตริง = null; if (strs.length == 2) {key = strs [1]; } cachefactory.ehdel (ชื่อ, คีย์); -คลาสการดำเนินการแคชเฉพาะมีดังนี้:
นำเข้า com.xuanwu.apaas.core.multicache.publisher.messagepublisher; นำเข้า net.sf.ehcache.cache; นำเข้า net.sf.ehcache.cachemanager; นำเข้า net.sf.ehcache.element; นำเข้า org.apache.commons.lang3.Stringutils; นำเข้า org.slf4j.logger; นำเข้า org.slf4j.loggerfactory; นำเข้า org.springframework.beans.factory.annotation.autowired; นำเข้า org.springframework.data.redis.redisconnectionfailureexception; นำเข้า org.springframework.data.redis.core.hashoperations; นำเข้า org.springframework.data.redis.core.redistemplate; นำเข้า org.springframework.stereotype.component; นำเข้า Java.io.InputStream; / *** ส่วนแคชหลายระดับ* @author rongdi*/ @component คลาสสาธารณะ cachefactory {ส่วนตัว logger logger สุดท้ายคงที่ = loggerFactory.getLogger (cachefactory.class); @autowired Redistemplate ส่วนตัว redistemplate; @autowired Messagepublisher Messagepublisher; cachemanager ส่วนตัว cachemanager; public cachefactory () {inputstream คือ = this.getClass (). getResourceasstream ("/ehcache.xml"); ถ้า (คือ! = null) {cacheManager = cacheManager.create (IS); }} โมฆะสาธารณะ Cachedel (ชื่อสตริง, คีย์สตริง) {// ลบแคชที่สอดคล้องกับ Redis; // ลบแคช ehcache ในเครื่องซึ่งไม่จำเป็นและผู้สมัครสมาชิกจะลบ // ehdel (ชื่อ, คีย์); if (cacheManager! = null) {// เผยแพร่ข้อความที่บอกบริการที่สมัครรับว่าแคชนั้นไม่ถูกต้อง MessagePublisher.publish (ชื่อ, key); }} สตริงสาธารณะ ehget (ชื่อสตริง, คีย์สตริง) {ถ้า (cachemanager == null) ส่งคืน null; แคชแคช = cachemanager.getCache (ชื่อ); if (cache == null) return null; CACHE.ACQUIREREADLOCKONKEY (กุญแจ); ลอง {Element Ele = cache.get (คีย์); ถ้า (ele == null) ส่งคืน null; return (string) ele.getObjectValue (); } ในที่สุด {cache.releasereadlockonkey (คีย์); }} สตริงสาธารณะ redisget (ชื่อสตริง, คีย์สตริง) {hashoperations <string, string, string> opera = redistemplate.opsforhash (); ลอง {return opera.get (ชื่อ, คีย์); } catch (redisconnectionfailureexception e) {// การเชื่อมต่อล้มเหลวไม่มีข้อผิดพลาดถูกโยนทิ้งและ logger.error ("เชื่อมต่อข้อผิดพลาด Redis", e); คืนค่า null; }} โมฆะสาธารณะ ehput (ชื่อสตริง, คีย์สตริง, ค่าสตริง) {ถ้า (cacheManager == null) คืน; if (! cachemanager.cacheexists (ชื่อ)) {cachemanager.addcache (ชื่อ); } แคชแคช = cacheManager.getCache (ชื่อ); // รับการล็อคการเขียนบนคีย์คีย์ที่แตกต่างกันจะไม่ส่งผลต่อกันและกันคล้ายกับซิงโครไนซ์ (key.intern ()) {} cache.acquirewriteLockonkey (คีย์); ลอง {cache.put (องค์ประกอบใหม่ (คีย์, ค่า)); } ในที่สุด {// ปล่อยแคชล็อคเขียน releasewriteLockonkey (คีย์); }} โมฆะสาธารณะ redisput (ชื่อสตริง, คีย์สตริง, ค่าสตริง) {hashoperations <สตริง, สตริง, สตริง> opera = redistemplate.opsforhash (); ลอง {operator.put (ชื่อ, คีย์, ค่า); } catch (redisconnectionfailureexception e) {// การเชื่อมต่อล้มเหลวไม่มีข้อผิดพลาดถูกโยนทิ้งและ logger.error ("เชื่อมต่อข้อผิดพลาด Redis", e); }} โมฆะสาธารณะ ehdel (ชื่อสตริง, คีย์สตริง) {if (cacheManager == null) return; if (cacheManager.cacheexists (ชื่อ)) {// ถ้าคีย์ว่างเปล่าให้ลบโดยตรงตามชื่อแคชถ้า (stringUtils.isEmpty (คีย์)) {cacheManager.removeCache (ชื่อ); } else {cache cache = cacheManager.getCache (ชื่อ); CACHE.REMOVE (กุญแจ); }}} โมฆะสาธารณะ redisdel (ชื่อสตริง, คีย์สตริง) {hashoperations <สตริง, สตริง, สตริง> opera = redistemplate.opsforhash (); ลอง {// ถ้าคีย์ว่างเปล่าให้ลบถ้า (stringUtils.isEmpty (คีย์)) {redistemplate.delete (ชื่อ); } else {opera.delete (ชื่อ, key); }} catch (redisconnectionfailureexception e) {// การเชื่อมต่อล้มเหลวไม่มีข้อผิดพลาดถูกโยนและ logger.error ("เชื่อมต่อข้อผิดพลาด Redis", e); -คลาสเครื่องมือมีดังนี้
นำเข้า com.fasterxml.jackson.core.type.typereference; นำเข้า com.fasterxml.jackson.databind.deserializationfeature; นำเข้า com.fasterxml.jackson.databind.javatype; นำเข้า com.fasterxml.jackson.databind.objectmapper; นำเข้า org.apache.commons.lang3.Stringutils; นำเข้า org.json.jsonarray; นำเข้า org.json.jsonObject; นำเข้า Java.util.*; ระดับสาธารณะ Jsonutil {Mapper ObjectMapper ส่วนตัว คงที่ {mapper = new ObjectMapper (); mapper.configure (deserializationfeature.fail_on_unknown_properties, false); } / ** * serialize วัตถุลงใน JSON * * @param obj object ที่จะเป็นอนุกรม * @return * @Throws Exception * / String String แบบคงที่สาธารณะ (Object OBJ) โยนข้อยกเว้น {ถ้า (OBJ == NULL) } return mapper.writevalueasstring (OBJ); } / ** deserialization ด้วยยาชื่อสามัญเช่น deserialization ของ jsonarray ลงในรายการ <suser>* / สาธารณะคงที่ <t> t deserialize (สตริง jsonstr, คลาส <?> คอลเลกชันคลาส, คลาส <?> ... elementClasses) ElementClasses); return mapper.readValue (JSONSTR, JAVATYPE); } / *** deserialize สตริง JSON ลงในวัตถุ* @param src สตริง JSON ที่จะถูก deserialized* @param t ประเภทคลาสของวัตถุที่ deserialized เป็น* @return* @throws Exception* / สาธารณะคงที่ <t> t deserialize (String src, class <t> t) เป็นโมฆะ "); } if ("{}". เท่ากับ (src.trim ()))) {return null; } return mappper.readValue (src, t); -หากต้องการใช้แคชโดยเฉพาะเพียงให้ความสนใจกับคำอธิบายประกอบ @Cacheable และ @CacheEvict และยังสนับสนุนการแสดงออกของฤดูใบไม้ผลิ EL ยิ่งไปกว่านั้นชื่อแคชที่แสดงโดยแอตทริบิวต์ค่าที่นี่ไม่มีปัญหาดังกล่าวข้างต้น แคชที่แตกต่างกันสามารถแยกได้โดยใช้ค่า ตัวอย่างมีดังนี้
@Cacheable (value = "bo", key = "#session.productVersionCode+''+#session.tenantCode+''+#objectCode")@cacheeVict (value = "bo", key = "#session.productersionCode+'+#เซสชัน
แนบแพ็คเกจการพึ่งพาหลัก
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น