java單元測(cè)試JUnit框架原理與用法實(shí)例教程
本文實(shí)例講述了java單元測(cè)試JUnit框架原理與用法。分享給大家供大家參考,具體如下:
1 簡(jiǎn)介
JUnit是一個(gè)Java語(yǔ)言的單元測(cè)試框架,它由 Kent Beck 和 Erich Gamma 建立,逐漸成為 xUnit 家族中最為成功的一個(gè)。 JUnit有它自己的JUnit擴(kuò)展生態(tài)圈,多數(shù)Java的開(kāi)發(fā)環(huán)境都已經(jīng)集成了JUnit作為單元測(cè)試的工具。在這里,一個(gè)單元可以是一個(gè)方法、類(lèi)、包或者子系統(tǒng)。因此,單元測(cè)試是指對(duì)代碼中的最小可測(cè)試單元進(jìn)行檢查和驗(yàn)證,以便確保它們正常工作。例如,我們可以給予一定的輸入測(cè)試輸出是否是所希望得到的結(jié)果。在本篇博客中,作者將著重介紹 JUnit 4.X 版本的特性,這也是我們?cè)谌粘i_(kāi)發(fā)中使用最多的版本。
2 特點(diǎn)
JUnit提供了注釋以及確定的測(cè)試方法;
JUnit提供了斷言用于測(cè)試預(yù)期的結(jié)果;
JUnit測(cè)試優(yōu)雅簡(jiǎn)潔不需要花費(fèi)太多的時(shí)間;
JUnit測(cè)試讓大家可以更快地編寫(xiě)代碼并且提高質(zhì)量;
JUnit測(cè)試可以組織成測(cè)試套件包含測(cè)試案例,甚至其他測(cè)試套件;
Junit顯示測(cè)試進(jìn)度,如果測(cè)試是沒(méi)有問(wèn)題條形是綠色的,測(cè)試失敗則會(huì)變成紅色;
JUnit測(cè)試可以自動(dòng)運(yùn)行,檢查自己的結(jié)果,并提供即時(shí)反饋,沒(méi)有必要通過(guò)測(cè)試結(jié)果報(bào)告來(lái)手動(dòng)梳理。
3 內(nèi)容
3.1 注解
@Test :該注釋表示,用其附著的公共無(wú)效方法(即用public修飾的void類(lèi)型的方法 )可以作為一個(gè)測(cè)試用例;
@Before :該注釋表示,用其附著的方法必須在類(lèi)中的每個(gè)測(cè)試之前執(zhí)行,以便執(zhí)行測(cè)試某些必要的先決條件;
@BeforeClass :該注釋表示,用其附著的靜態(tài)方法必須執(zhí)行一次并在類(lèi)的所有測(cè)試之前,發(fā)生這種情況時(shí)一般是測(cè)試計(jì)算共享配置方法,如連接到數(shù)據(jù)庫(kù);
@After :該注釋表示,用其附著的方法在執(zhí)行每項(xiàng)測(cè)試后執(zhí)行,如執(zhí)行每一個(gè)測(cè)試后重置某些變量,刪除臨時(shí)變量等;
@AfterClass :該注釋表示,當(dāng)需要執(zhí)行所有的測(cè)試在JUnit測(cè)試用例類(lèi)后執(zhí)行,AfterClass注解可以使用以清理建立方法,如斷開(kāi)數(shù)據(jù)庫(kù)連接,注意:附有此批注(類(lèi)似于BeforeClass)的方法必須定義為靜態(tài);
@Ignore :該注釋表示,當(dāng)想暫時(shí)禁用特定的測(cè)試執(zhí)行可以使用忽略注釋?zhuān)總€(gè)被注解為@Ignore的方法將不被執(zhí)行。
/** * JUnit 注解示例 */ @Test public void testYeepay(){ Syetem.out.println("用@Test標(biāo)示測(cè)試方法!"); } @AfterClass public static void paylus(){ Syetem.out.println("用@AfterClass標(biāo)示的方法在測(cè)試用例類(lèi)執(zhí)行完之后!"); }
3.2 斷言
在這里,作者將介紹一些斷言方法,所有這些方法都來(lái)自 org.junit.Assert 類(lèi),其擴(kuò)展了 java.lang.Object 類(lèi)并為它們提供編寫(xiě)測(cè)試,以便檢測(cè)故障。簡(jiǎn)而言之,我們就是通過(guò)斷言方法來(lái)判斷實(shí)際結(jié)果與我們預(yù)期的結(jié)果是否相同,如果相同,則測(cè)試成功,反之,則測(cè)試失敗。
void assertEquals([String message], expected value, actual value) :斷言?xún)蓚€(gè)值相等,值的類(lèi)型可以為int、short、long、byte、char 或者
java.lang.Object,其中第一個(gè)參數(shù)是一個(gè)可選的字符串消息;
void assertTrue([String message], boolean condition) :斷言一個(gè)條件為真;
void assertFalse([String message],boolean condition) :斷言一個(gè)條件為假;
void assertNotNull([String message], java.lang.Object object) :斷言一個(gè)對(duì)象不為空(null);
void assertNull([String message], java.lang.Object object) :斷言一個(gè)對(duì)象為空(null);
void assertSame([String message], java.lang.Object expected, java.lang.Object actual) :斷言?xún)蓚€(gè)對(duì)象引用相同的對(duì)象;
void assertNotSame([String message], java.lang.Object unexpected, java.lang.Object actual) :斷言?xún)蓚€(gè)對(duì)象不是引用同一個(gè)對(duì)象;
void assertArrayEquals([String message], expectedArray, resultArray) :斷言預(yù)期數(shù)組和結(jié)果數(shù)組相等,數(shù)組的類(lèi)型可以為int、long、short、char、byte 或者 java.lang.Object
4 JUnit 3.X 和 JUnit 4.X 的區(qū)別
4.1 JUnit 3.X
(1)使用 JUnit 3.X 版本進(jìn)行單元測(cè)試時(shí),測(cè)試類(lèi)必須要繼承于 TestCase 父類(lèi);
(2)測(cè)試方法需要遵循的原則:
① public的;
② void的;
③ 無(wú)方法參數(shù);
④方法名稱(chēng)必須以 test 開(kāi)頭;
(3)不同的測(cè)試用例之間一定要保持完全的獨(dú)立性,不能有任何的關(guān)聯(lián);
(4)要掌握好測(cè)試方法的順序,不能依賴(lài)于測(cè)試方法自己的執(zhí)行順序。
/** * 用 JUnit 3.X 進(jìn)行測(cè)試 */ import junit.framework.Assert; import junit.framework.TestCase; public class TestOperation extends TestCase { private Operation operation; public TestOperation(String name) { // 構(gòu)造函數(shù) super(name); } @Override public void setUp() throws Exception { // 在每個(gè)測(cè)試方法執(zhí)行 [之前] 都會(huì)被調(diào)用,多用于初始化 System.out.println("歡迎使用Junit進(jìn)行單元測(cè)試..."); operation = new Operation(); } @Override public void tearDown() throws Exception { // 在每個(gè)測(cè)試方法執(zhí)行 [之后] 都會(huì)被調(diào)用,多用于釋放資源 System.out.println("Junit單元測(cè)試結(jié)束..."); } public void testDivideByZero() { Throwable te = null; try { operation.divide(6, 0); Assert.fail("測(cè)試失敗"); //斷言失敗 } catch (Exception e) { e.printStackTrace(); te = e; } Assert.assertEquals(Exception.class, te.getClass()); Assert.assertEquals("除數(shù)不能為 0 ", te.getMessage()); } }
4.2 JUnit 4.X
(1)使用 JUnit 4.X 版本進(jìn)行單元測(cè)試時(shí),不用測(cè)試類(lèi)繼承TestCase父類(lèi);
(2)JUnit 4.X 版本,引用了注解的方式進(jìn)行單元測(cè)試;
(3)JUnit 4.X 版本我們常用的注解包括:
@Before 注解:與JUnit 3.X 中的 setUp() 方法功能一樣,在每個(gè)測(cè)試方法之前執(zhí)行,多用于初始化;
@After 注解:與 JUnit 3.X 中的 tearDown() 方法功能一樣,在每個(gè)測(cè)試方法之后執(zhí)行,多用于釋放資源;
@Test(timeout = xxx) 注解:設(shè)置當(dāng)前測(cè)試方法在一定時(shí)間內(nèi)運(yùn)行完,否則返回錯(cuò)誤;
@Test(expected = Exception.class) 注解:設(shè)置被測(cè)試的方法是否有異常拋出。拋出異常類(lèi)型為:Exception.class;
此外,我們可以通過(guò)閱讀上面的第二部分“2 注解”了解更多的注解。
/** * 用 JUnit 4.X 進(jìn)行測(cè)試 */ import static org.junit.Assert.*; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class TestOperation { private Operation operation; @BeforeClass public static void globalInit() { // 在所有方法執(zhí)行之前執(zhí)行 System.out.println("@BeforeClass標(biāo)注的方法,在所有方法執(zhí)行之前執(zhí)行..."); } @AfterClass public static void globalDestory() { // 在所有方法執(zhí)行之后執(zhí)行 System.out.println("@AfterClass標(biāo)注的方法,在所有方法執(zhí)行之后執(zhí)行..."); } @Before public void setUp() { // 在每個(gè)測(cè)試方法之前執(zhí)行 System.out.println("@Before標(biāo)注的方法,在每個(gè)測(cè)試方法之前執(zhí)行..."); operation = new Operation(); } @After public void tearDown() { // 在每個(gè)測(cè)試方法之后執(zhí)行 System.out.println("@After標(biāo)注的方法,在每個(gè)測(cè)試方法之后執(zhí)行..."); } @Test(timeout=600) public void testAdd() { // 設(shè)置限定測(cè)試方法的運(yùn)行時(shí)間 如果超出則返回錯(cuò)誤 System.out.println("測(cè)試 add 方法..."); int result = operation.add(2, 3); assertEquals(5, result); } @Test public void testSubtract() { System.out.println("測(cè)試 subtract 方法..."); int result = operation.subtract(1, 2); assertEquals(-1, result); } @Test public void testMultiply() { System.out.println("測(cè)試 multiply 方法..."); int result = operation.multiply(2, 3); assertEquals(6, result); } @Test public void testDivide() { System.out.println("測(cè)試 divide 方法..."); int result = 0; try { result = operation.divide(6, 2); } catch (Exception e) { fail(); } assertEquals(3, result); } @Test(expected = Exception.class) public void testDivideAgain() throws Exception { System.out.println("測(cè)試 divide 方法,除數(shù)為 0 的情況..."); operation.divide(6, 0); fail("test Error"); } public static void main(String[] args) { } }
4.3 特別提醒
通過(guò)以上兩個(gè)例子,我們已經(jīng)可以大致知道 JUnit 3.X 和 JUnit 4.X 兩個(gè)版本的區(qū)別啦!首先,如果我們使用 JUnit 3.X,那么在我們寫(xiě)的測(cè)試類(lèi)的時(shí)候,一定要繼承 TestCase 類(lèi),但是如果我們使用 JUnit 4.X,則不需繼承 TestCase 類(lèi),直接使用注解就可以啦!在 JUnit 3.X 中,還強(qiáng)制要求測(cè)試方法的命名為“ testXxxx ”這種格式;在 JUnit 4.X 中,則不要求測(cè)試方法的命名格式,但作者還是建議測(cè)試方法統(tǒng)一命名為“ testXxxx ”這種格式,簡(jiǎn)潔明了。
此外,在上面的兩個(gè)示例中,我們只給出了測(cè)試類(lèi),但是在這之前,還應(yīng)該有一個(gè)被測(cè)試類(lèi),也就是我們真正要實(shí)現(xiàn)功能的類(lèi)。現(xiàn)在,作者將給出上面示例中被測(cè)試的類(lèi),即 Operation 類(lèi):
/** * 定義了加減乘除的法則 */ public class Operation { public static void main(String[] args) { System.out.println("a + b = " + add(1,2)); System.out.println("a - b = " + subtract(1,2)); System.out.println("a * b = " + multiply(1,2)); System.out.println("a / b = " + divide(4,2)); System.out.println("a / b = " + divide(1,0)); } public static int add(int a, int b) { return a + b; } public static int subtract(int a, int b) { return a - b; } public static int multiply(int a, int b) { return a * b; } public static int divide(int a, int b) { return a / b; } }
5 測(cè)試示例
5.1 示例一:簡(jiǎn)單的 JUnit 3.X 測(cè)試
import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import java.util.ArrayList; import java.util.Collection; /** * 1、創(chuàng)建一個(gè)測(cè)試類(lèi),繼承TestCase類(lèi) */ public class SimpleTestDemo extends TestCase { public SimpleTestDemo(String name) { super(name); } /** * 2、寫(xiě)一個(gè)測(cè)試方法,斷言期望的結(jié)果 */ public void testEmptyCollection(){ Collection collection = new ArrayList(); assertTrue(collection.isEmpty()); } /** * 3、寫(xiě)一個(gè)suite()方法,它會(huì)使用反射動(dòng)態(tài)的創(chuàng)建一個(gè)包含所有的testXxxx方法的測(cè)試套件 */ public static Test suit(){ return new TestSuite(SimpleTestDemo.class); } /** * 4、寫(xiě)一個(gè)main()方法,以文本運(yùn)行器的方式方便的運(yùn)行測(cè)試 */ public static void main(String[] args) { junit.textui.TestRunner.run(suit()); } }
5.2 示例二:套件測(cè)試
首先,介紹一下套件測(cè)試,簡(jiǎn)單來(lái)講,測(cè)試套件是指:一些測(cè)試不同類(lèi)的用例,可以使用 @RunWith 和 @Suite 注解把所有的測(cè)試類(lèi)套在一起,從而形成測(cè)試套件。如果有很多測(cè)試類(lèi),想讓它們都運(yùn)行在同一時(shí)間,而不是單一地運(yùn)行每個(gè)測(cè)試,套件測(cè)試是非常有用的。當(dāng)一個(gè)類(lèi)被注解為 @RunWith, JUnit 將調(diào)用其中的注解,以便運(yùn)行測(cè)試類(lèi),而不使用內(nèi)置的 JUnit 運(yùn)行方法。
/** * 待測(cè)試類(lèi) */ import java.util.Arrays; public class GotoWork { public String[] prepareSkills() { String[] skill = { "Java", "MySQL", "JSP" }; System.out.println("My skills include : " + Arrays.toString(skill)); return skill; } public String[] addSkills() { String[] skill = { "Java", "MySQL", "JSP", "JUnit" }; System.out.println("Look, my skills include : " + Arrays.toString(skill)); return skill; } }
/** * 測(cè)試類(lèi) 1 */ import org.junit.Test; import static org.junit.Assert.*; public class PrepareSkillsTest { GotoWork gotoWork = new GotoWork(); String[] skill = { "Java", "MySQL", "JSP" }; @Test public void testPrepareSkills() { System.out.println("Inside testPrepareSkills()"); assertArrayEquals(skill, gotoWork.prepareSkills()); } }
/** * 測(cè)試類(lèi) 2 */ import org.junit.Test; import static org.junit.Assert.*; public class AddSkillsTest { GotoWork gotoWork = new GotoWork(); String[] skill = { "Java", "MySQL", "JSP", "JUnit" }; @Test public void testAddSkills() { System.out.println("Inside testAddPencils()"); assertArrayEquals(skill, gotoWork.addSkills()); } }
/** * 套件測(cè)試 */ import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ PrepareSkillsTest.class, AddSkillsTest.class }) public class SuitTest { }
使用 @Suite.SuiteClasses 注解,可以定義測(cè)試類(lèi),將被列入執(zhí)行,并且執(zhí)行的順序就是在 @Suite.SuiteClasses 注解中定義的順序。
5.3 示例三:參數(shù)化測(cè)試
首先介紹一下參數(shù)化測(cè)試,一個(gè)測(cè)試類(lèi)可以被看作是一個(gè)參數(shù)化測(cè)試類(lèi),當(dāng)其滿(mǎn)足下列所有要求:
① 該類(lèi)被注解為 @RunWith(Parameterized.class);
② 該類(lèi)有一個(gè)構(gòu)造函數(shù),存儲(chǔ)測(cè)試數(shù)據(jù);
③ 該類(lèi)有一個(gè)靜態(tài)方法生成并返回測(cè)試數(shù)據(jù),并標(biāo)注 @Parameters 注解;
④ 該類(lèi)有一個(gè)測(cè)試方法,即用注解 @Test 標(biāo)注的方法。
/** * 待測(cè)試類(lèi) */ public class Calculate { public int sum(int var1, int var2) { System.out.println("此方法的參數(shù)值分別為 : " + var1 + " + " + var2); return var1 + var2; } }
/** * 參數(shù)化測(cè)試類(lèi) */ import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.Collection; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class CalculateTest { private int expected; private int first; private int second; public CalculateTest(int expectedResult, int firstNumber, int secondNumber) { this.expected = expectedResult; this.first = firstNumber; this.second = secondNumber; } @Parameters public static Collection addedNumbers() { return Arrays.asList(new Integer[][] { { 3, 1, 2 }, { 5, 2, 3 }, { 7, 3, 4 }, { 9, 4, 5 }, }); } @Test public void testSum() { Calculate add = new Calculate(); System.out.println("Addition with parameters : " + first + " and " + second); assertEquals(expected, add.sum(first, second)); } }
觀察 CalculateTest 類(lèi),它滿(mǎn)足上述所有的要求,因此它就可以稱(chēng)為一個(gè)參數(shù)化測(cè)試類(lèi)。addedNumbers 方法使用注釋 @Parameters 返回?cái)?shù)組的集合,每個(gè)數(shù)組包括每個(gè)測(cè)試執(zhí)行輸入和輸出數(shù)字,每個(gè)數(shù)組中的元素?cái)?shù)必須相同好與構(gòu)造參數(shù)的個(gè)數(shù)相匹配。所以,在這種特定的情況下,每個(gè)數(shù)組包括三個(gè)元素,即表示要加入的兩個(gè)元素和一個(gè)結(jié)果元素。
6 個(gè)人建議
有些童鞋可能會(huì)有一些誤解,認(rèn)為寫(xiě)測(cè)試代碼沒(méi)有用,而且還會(huì)增大自己的壓力,浪費(fèi)時(shí)間。但事實(shí)上,寫(xiě)測(cè)試代碼與否,還是有很大區(qū)別的,如果是在小的項(xiàng)目中,或許這種區(qū)別還不太明顯,但如果在大型項(xiàng)目中,一旦出現(xiàn)錯(cuò)誤或異常,用人力去排查的話(huà),那將會(huì)浪費(fèi)很多時(shí)間,而且還不一定排查的出來(lái),但是如果用測(cè)試代碼的話(huà),JUnit 就是自動(dòng)幫我們判斷一些代碼的結(jié)果正確與否,從而節(jié)省的時(shí)間將會(huì)遠(yuǎn)遠(yuǎn)超過(guò)你寫(xiě)測(cè)試代碼的時(shí)間。
因此,個(gè)人建議:要養(yǎng)成編寫(xiě)測(cè)試代碼的習(xí)慣,碼一點(diǎn)、測(cè)一點(diǎn);再碼一點(diǎn),再測(cè)一點(diǎn),如此循環(huán)。在我們不斷編寫(xiě)與測(cè)試代碼的過(guò)程中,我們將會(huì)對(duì)類(lèi)的行為有一個(gè)更為深入的了解,從而可以有效的提高我們的工作效率。下面,作者就給出一些具體的編寫(xiě)測(cè)試代碼的技巧和較好的實(shí)踐方法:
1. 不要用 TestCase 的構(gòu)造函數(shù)初始化 Fixture,而要用 setUp() 和 tearDown() 方法;
2. 不要依賴(lài)或假定測(cè)試運(yùn)行的順序,因?yàn)?JUnit 會(huì)利用 Vector 保存測(cè)試方法,所以不同的平臺(tái)會(huì)按不同的順序從 Vector 中取出測(cè)試方法;
3. 避免編寫(xiě)有副作用的 TestCase,例如:如果隨后的測(cè)試依賴(lài)于某些特定的交易數(shù)據(jù),就不要提交交易數(shù)據(jù),只需要簡(jiǎn)單的回滾就可以了;
4. 當(dāng)繼承一個(gè)測(cè)試類(lèi)時(shí),記得調(diào)用父類(lèi)的 setUp() 和 tearDown() 方法;
5. 將測(cè)試代碼和工作代碼放在一起,同步編譯和更新;
6. 測(cè)試類(lèi)和測(cè)試方法應(yīng)該有一致的命名方案,如在工作類(lèi)名前加上 test 從而形成測(cè)試類(lèi)名;
7. 確保測(cè)試與時(shí)間無(wú)關(guān),不要使用過(guò)期的數(shù)據(jù)進(jìn)行測(cè)試,以至于導(dǎo)致在隨后的維護(hù)過(guò)程中很難重現(xiàn)測(cè)試;
8. 如果編寫(xiě)的軟件面向國(guó)際市場(chǎng),那么編寫(xiě)測(cè)試時(shí)一定要考慮國(guó)際化的因素;
9. 盡可能地利用 JUnit 提供地 assert 和 fail 方法以及異常處理的方法,其可以使代碼更為簡(jiǎn)潔;
10. 測(cè)試要盡可能地小,執(zhí)行速度快;
11. 不要硬性規(guī)定數(shù)據(jù)文件的路徑;
12. 使用文檔生成器做測(cè)試文檔。
事實(shí)上,在 Junit 中使用 try catch 來(lái)捕獲異常是沒(méi)有必要的,因?yàn)?Junit 會(huì)自動(dòng)捕獲異常,那些沒(méi)有被捕獲的異常就會(huì)被當(dāng)成錯(cuò)誤處理啦!
更多關(guān)于java相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java字符與字符串操作技巧總結(jié)》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對(duì)大家java程序設(shè)計(jì)有所幫助。
- 解決java junit單元測(cè)試@Test報(bào)錯(cuò)的問(wèn)題
- java實(shí)現(xiàn)連接mysql數(shù)據(jù)庫(kù)單元測(cè)試查詢(xún)數(shù)據(jù)的實(shí)例代碼
- 詳解Java單元測(cè)試之JUnit篇
- Java Junit單元測(cè)試實(shí)例詳解
- JAVA中單元測(cè)試的常用方式(小結(jié))
- 詳解Java單元測(cè)試Junit框架實(shí)例
- 詳解Java單元測(cè)試之Junit框架使用教程
- 淺談Java 中的單元測(cè)試
- Java通過(guò)PowerMockito和Mokito進(jìn)行單元測(cè)試的實(shí)現(xiàn)
相關(guān)文章
JDBC使用游標(biāo)實(shí)現(xiàn)分頁(yè)查詢(xún)的方法
這篇文章主要介紹了JDBC使用游標(biāo)實(shí)現(xiàn)分頁(yè)查詢(xún)的方法,實(shí)例分析了jdbc查詢(xún)過(guò)程中游標(biāo)的使用及查詢(xún)分頁(yè)相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-08-08springcloud?gateway實(shí)現(xiàn)簡(jiǎn)易版灰度路由步驟詳解
這篇文章主要為大家介紹了springcloud?gateway實(shí)現(xiàn)簡(jiǎn)易版灰度路由步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11Java并發(fā)編程多線(xiàn)程間的同步控制和通信詳解
這篇文章主要為大家介紹了Java并發(fā)編程多線(xiàn)程間的同步控制和通信詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Java list.remove( )方法注意事項(xiàng)
這篇文章主要介紹了Java list.remove( )方法注意事項(xiàng),非常簡(jiǎn)單易懂,需要的朋友可以參考下2018-08-08

Spring事務(wù)相關(guān)問(wèn)題解決方案

單機(jī)redis分布式鎖實(shí)現(xiàn)原理解析

Spring三種方法的注解自動(dòng)注入問(wèn)題