前言

首先声明笔者并不是一个TDD开发的拥趸,我倾向于实用主义。TDD有他的用武之地,但不是银弹,同理BDD(行为驱动开发)也是如此。笔者坚持写测试的原因只是为了更加方便的重构,当笔者意识到一个模块会被大范围使用且会面临多次迭代的时候,笔者就会认证写测试,并且每次出bug都会用测试先复现。如果一个项目只是一次性的demo那些个啥的测试,一把梭哈得了。

什么是TDD

TDD是测试驱动开发(Test-Driven Development)的英文简称,是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD虽是敏捷方法的核心实践,但不只适用于XP(Extreme Programming),同样可以适用于其他开发方法和过程。

为什么要使用mockito

Mockito is a mocking framework that tastes really good. It lets you write beautiful tests with a clean & simple API. Mockito doesn’t give you hangover because the tests are very readable and they produce clean verification errors.

Mockito 是非常不错框架。它使您可以使用干净简单的 API 编写漂亮的测试。 Mockito 不会给你带来宿醉,因为测试非常易读并且会产生干净的验证错误

在开发程序的时候我们需要测试的类不可能都是简单的类,很多复杂的逻辑往往需要多个前置条件才能测试到。如果为了测试去满足这些前置条件未免显得太繁琐。比如有的逻辑需要进行HTTP请求某个特定服务,我不想在测试的时候去单独启动这个服务。这时候我们就可以mock这个http请求,让其返回一个特定值,以此简化测试流程。

如何使用mockito

前期准备

引包

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>${mockito.version}</version>
    <scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>${mockito.version}</version>
    <scope>test</scope>
</dependency>

静态导入

import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
import static org.mockito.Mockito.*;

包装你要模拟的类

使用mock方法

LinkedList mockedList = mock(LinkedList.class);

使用spy方法

PDStateMachineNode mock = spy(new PDStateMachineNode());

mockspy的区别

mock方法和spy方法都可以对对象进行mock。但是前者是接管了对象的全部方法,而后者只是将有桩实现(stubbing)的调用进行mock,其余方法仍然是实际调用。大家先这样理解后面会具体举例子。

什么插桩

其实就是对要模拟方法进行包装,设定返回值或者抛异常之类,直接看官方例子。这里就是对get(0)get(1) 进行插桩了

 //You can mock concrete classes, not just interfaces
 LinkedList mockedList = mock(LinkedList.class);
 //插桩
 when(mockedList.get(0)).thenReturn("first");
 when(mockedList.get(1)).thenThrow(new RuntimeException());
 //following prints "first"
 System.out.println(mockedList.get(0));
 //following throws runtime exception
 System.out.println(mockedList.get(1));
 //following prints "null" because get(999) was not stubbed
 System.out.println(mockedList.get(999));
 //Although it is possible to verify a stubbed invocation, usually it's just redundant
 //If your code cares what get(0) returns, then something else breaks (often even before verify() gets executed).
 //If your code doesn't care what get(0) returns, then it should not be stubbed.
 verify(mockedList).get(0);

验证行为

可以用来验证某个方法是否调用了

这里使用了官网例子,大致用途就是你可以用它来验证某个方法被调用了没有。可以很明显在的看到他在验证是否调用了add和clear

 //Let's import Mockito statically so that the code looks clearer
 import static org.mockito.Mockito.*;
 //mock creation
 List mockedList = mock(List.class);
 //using mock object
 mockedList.add("one");
 mockedList.clear();
 //verification
 verify(mockedList).add("one");
 verify(mockedList).clear();

下面看看我自己项目里面得例子。这里可以发现我在传入参数得时候直接是传入了any(),这也是mockito提供得,与此对应得还有anyString(),anyInt()等等。

大致意思就是验证runAviatorScript这个方法被调用的了应该有一次。

@Test
void testOnEntry2() throws NodeExecuteTimeoutException {
    PDStateMachineNode mock = spy(PDStateMachineNode.class);
    mock.onEntry(any());
    // 默认第二个参数就是times(1),因此这里可以不写
    verify(mock,times(1)).runAviatorScript(any(),any(),anyString());
}

参数匹配

方便得模拟一次方法得参数

直接上官方例子,上述代码也有使用

//stubbing using built-in anyInt() argument matcher
 when(mockedList.get(anyInt())).thenReturn("element");
 //stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
 when(mockedList.contains(argThat(isValid()))).thenReturn(true);
 //following prints "element"
 System.out.println(mockedList.get(999));
 //you can also verify using an argument matcher
 verify(mockedList).get(anyInt());
 //argument matchers can also be written as Java 8 Lambdas
 verify(mockedList).add(argThat(someString -> someString.length() > 5));

如果你使用的参数匹配器,那么所有参数都必须提供参数匹配器,否则会抛异常。

//正确   
verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
//错误
verify(mock).someMethod(anyInt(), anyString(), "third argument");

验证调用次数

就是验证某个方法调用了多少次

 //using mock
 mockedList.add("once");
 mockedList.add("twice");
 mockedList.add("twice");
 mockedList.add("three times");
 mockedList.add("three times");
 mockedList.add("three times");
 //following two verifications work exactly the same - times(1) is used by default
 verify(mockedList).add("once");
 verify(mockedList, times(1)).add("once");
 //exact number of invocations verification
 verify(mockedList, times(2)).add("twice");
 verify(mockedList, times(3)).add("three times");
 //verification using never(). never() is an alias to times(0)
 verify(mockedList, never()).add("never happened");
 //verification using atLeast()/atMost()
 verify(mockedList, atMostOnce()).add("once");
 verify(mockedList, atLeastOnce()).add("three times");
 verify(mockedList, atLeast(2)).add("three times");
 verify(mockedList, atMost(5)).add("three times");

笔者项目中得例子

@Test
void testDELETE() {
    Assertions.assertTimeout(Duration.ofSeconds(10), () -> {
        mockStatic.when(() -> HttpRequest.delete("test").execute().body()).thenReturn("success");
        String execute = (String) AviatorEvaluator.execute("return DELETE("test");");
        Assertions.assertTrue(execute.contains("success"));
        //在这里
        mockStatic.verify(() -> HttpRequest.delete(anyString()), times(2));
    });
}

模拟void方法

项目中的例子

当调用onExit的时候啥也不干

@Test
void onExit() throws NodeExecuteTimeoutException {
    StateMachineInterpreter interpreter = new StateMachineInterpreter();
    StateMachineNode spy = spy(new StateMachineNode());
    //啥也不干
    doNothing().when(spy).onExit(any());
}

验证调用顺序

有时候在需要验证某个方法内部调用其他方法的顺序。笔者例子如下:

这段测试意思就是模拟 PDStateMachineNode这个类调用verify()方法得逻辑。分两种情况

onCheck方法返回true(doReturn(true).when(mock).onCheck(any())

此种情况下,方法调用顺序应为:onEntry,onCheck,onMatch,onExit

onCheck返回false(doReturn(false).when(mock).onCheck(any())

此种情况下,方法调用顺序应为:onEntry,onCheck,onFail,onExit

PDStateMachineNode mock = spy(new PDStateMachineNode());
doReturn(true).when(mock).onCheck(any());
mock.verify(any());
InOrder inOrder = inOrder(mock);
inOrder.verify(mock).onEntry(any());
inOrder.verify(mock).onCheck(any());
inOrder.verify(mock).onMatch(any());
inOrder.verify(mock).onExit(any());
doReturn(false).when(mock).onCheck(any());
mock.verify(any());
InOrder inOrder2 = inOrder(mock);
inOrder2.verify(mock).onEntry(any());
inOrder2.verify(mock).onCheck(any());
inOrder2.verify(mock).onFail(any());
inOrder2.verify(mock).onExit(any());

doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod()

官方建议大部分情况下你应该使用when(),二者区别后文再说。

doReturn()

List list = new LinkedList();
List spy = spy(list);
//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo", "bar", "qix");
//You have to use doReturn() for stubbing:
doReturn("foo", "bar", "qix").when(spy).get(0);
   List list = new LinkedList();
   List spy = spy(list);
   //Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
   when(spy.get(0)).thenReturn("foo", "bar", "qix");
   //You have to use doReturn() for stubbing:
   doReturn("foo", "bar", "qix").when(spy).get(0);

doThrow()

doThrow(new RuntimeException()).when(mock).someVoidMethod();
doThrow(RuntimeException.class).when(mock).someVoidMethod();

doAnswer()

doAnswer(new Answer() {
    public Object answer(InvocationOnMock invocation) {
        Object[] args = invocation.getArguments();
        Mock mock = invocation.getMock();
        return null;
    }})
    .when(mock).someMethod();

doNothing()

doNothing().
    doThrow(new RuntimeException())
    .when(mock).someVoidMethod();
//does nothing the first time:
mock.someVoidMethod();
//throws RuntimeException the next time:
mock.someVoidMethod();

doCallRealMethod()

Foo mock = mock(Foo.class);
doCallRealMethod().when(mock).someVoidMethod();
// this will call the real implementation of Foo.someVoidMethod()
mock.someVoidMethod();

笔者项目中的使用的例子

@Test
void step() throws NodeExecuteTimeoutException, NextNodesNotExistException {
    PDStateMachineNode spy = spy(new PDStateMachineNode());
    PDStateMachineNode node = new PDStateMachineNode();
    PDStateMachineNode subSpy = spy(node);
    doReturn(true).when(subSpy).verify(any());
    doReturn(List.of(subSpy)).when(spy).getNextNodes();
    PDStateMachineNode step = spy.step(any(PointData.class));
    Assertions.assertEquals(subSpy, step);
    when(spy.getNextNodes()).thenReturn(new ArrayList<>());
    doReturn(true).when(spy).isTerminalNode();
    Assertions.assertThrows(NextNodesNotExistException.class, () -> spy.step(any(PointData.class)));
    doReturn(new ArrayList<>()).when(spy).getNextNodes();
    doReturn(false).when(spy).isTerminalNode();
    Assertions.assertEquals(spy, spy.step(any(PointData.class)));
}

静态方法模拟

静态有返回值且存在链式调用

直接看笔者项目中的例子,注意这里下面的例子有些许不同,RETURNS_DEEP_STUBS其实是用来进行嵌套模拟的。因为HttpRequest.get("test").execute().body() 是一个链式的调用,实际上涉及了多个类的模拟。如果你没有这个需求就可以不加。

@BeforeAll
void init() {
    mockStatic = mockStatic(HttpRequest.class, RETURNS_DEEP_STUBS);
}
@Test
void testGET() {
    Assertions.assertTimeout(Duration.ofSeconds(10), () -> {
        mockStatic.when(() -> HttpRequest.get("test").execute().body()).thenReturn("success");
        String execute = (String) AviatorEvaluator.execute("return GET("test");");
        Assertions.assertTrue(execute.contains("success"));
        mockStatic.verify(() -> HttpRequest.get(anyString()), times(2));
    });
}

静态无返回值

someMock.when(() -> Files.delete(fileToDelete)).thenAnswer((Answer<Void>) invocation -> null);
// 也可以是下面这个
// someMock.when(() -> Files.delete(fileToDelete)).thenAnswer(Answers.RETURNS_DEFAULTS);

进阶

mock和spy的区别

mock方法和spy方法都可以对对象进行mock。但是前者是接管了对象的全部方法,而后者只是将有桩实现(stubbing)的调用进行mock,其余方法仍然是实际调用。

使用mock

PDStateMachineNode mock = mock(PDStateMachineNode.class);
mock.onEntry(any());
verify(mock, times(1)).runAviatorScript(any(), any(), anyString());

抛错如下,意思就是runAviatorScript没有被调用,因此验证失败。实际上笔者在onEntry内部是调用了runAviatorScript方法的

Wanted but not invoked:
pDStateMachineNode.runAviatorScript(
    <any>,
    <any>,
    <any string>
);
-> at core.state.GenericStateMachineNode.runAviatorScript(GenericStateMachineNode.java:78)

使用spy,则无任务错误。

@Test
void testOnEntry2() throws NodeExecuteTimeoutException {
    PDStateMachineNode mock = spy(PDStateMachineNode.class);
    mock.onEntry(any());
    verify(mock, times(1)).runAviatorScript(any(), any(), anyString());
}

从上述对比就可以理解mock和spy的区别,对于未指定mock的方法,spy默认会调用真实的方法,有返回值的返回真实的返回值,而mock默认不执行,有返回值的,默认返回null。具体细节笔者也没有深究比如实际上mock也能做到类似psy的效果

when(...).thenReturn(...)和doReturn(...).when(...)的区别

● when(...) thenReturn(...)会调用真实的方法,如果你不想调用真实的方法而是想要mock的话,就不要使用这个方法。

● doReturn(...) when(...) 不会调用真实方法

因此针对区别一般情况下如果时第三方库得代码在需要测试得方法则可以使用 do...return进行略过,自己调用自己得方法则建议使用 when...return。但是有时候调用得方法需要一些特殊得环境才能起作用,那么也能使用 do..return,亦或者被调用得方法已经测试过了也可以使用 do..return。下面看二者区别得例子。

例子:

@Override
public void onEntry(T event) throws NodeExecuteTimeoutException {
    System.out.println("hello");
    this.runAviatorScript(this.onEntry, event, "onEntry");
}

测试when..return...:

    @Test
    void testOnEntry2() throws NodeExecuteTimeoutException {
        PDStateMachineNode mock = spy(PDStateMachineNode.class);
        when(mock.onCheck(any())).thenReturn(true);
        mock.onEntry(any());
        verify(mock, times(1)).runAviatorScript(any(), any(), anyString());
    }

结果可以看到输出得hello

测试do...return...

@Test
void testOnEntry2() throws NodeExecuteTimeoutException {
    PDStateMachineNode mock = spy(PDStateMachineNode.class);
    doNothing().when(mock).onEntry(any());
    mock.onEntry(any());
    verify(mock, times(1)).runAviatorScript(any(), any(), anyString());
}

结果可以看到不仅没输出还报错了,为什么呢?因为 do..return实际上不执行包装得方法,也就没有执行onEntry方法,自然里面 runAviatorScript也就没有执行,因此就会导致验证错误。

BDDMockito(行为驱动测试)

什么是BDD

行为驱动开发(英语:Behavior-driven development,缩写BDD)是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作。BDD最初是由Dan North在2003年命名,它包括验收测试和客户测试驱动等的极限编程的实践,作为对测试驱动开发的回应。在过去数年里,它得到了很大的发展。行为驱动测试的开发风格使用//given//when//then 作为测试方法的基本部分。

其实还是比较简单的,粗浅的理解就是换了几个API。

举个例子

直接看几个官方的例子

 import static org.mockito.BDDMockito.*;
 Seller seller = mock(Seller.class);
 Shop shop = new Shop(seller);
 public void shouldBuyBread() throws Exception {
   //given
   given(seller.askForBread()).willReturn(new Bread());
   //when
   Goods goods = shop.buyBread();
   //then
   assertThat(goods, containBread());
 }

如何模拟异常

可以发现willThrow就像之前的doThrow差不多

   //given
   willThrow(new RuntimeException("boo")).given(mock).foo();
   //when
   Result result = systemUnderTest.perform();
   //then
   assertEquals(failure, result);

验证调用次数

person.ride(bike);
person.ride(bike);
then(person).should(times(2)).ride(bike);
then(person).shouldHaveNoMoreInteractions();
then(police).shouldHaveZeroInteractions();

验证调用顺序

   InOrder inOrder = inOrder(person);
   person.drive(car);
   person.ride(bike);
   person.ride(bike);
   then(person).should(inOrder).drive(car);
   then(person).should(inOrder, times(2)).ride(bike);

实战中使用

这里不仅模拟了方法的返回值,还模拟了springbootcontroller的调用

@Test
void shouldNotListRoles() throws Exception {
    given(roleService.findAll()).willReturn(new ArrayList<>());
    ResultActions actions = this.mvc.perform(get("/api/role/getRoles"));
    actions.andExpect(status().isOk()).andReturn().getResponse().setCharacterEncoding("UTF-8");
    actions.andDo(print()).andExpect(jsonPath("$.data.length()").value(Matchers.is(0)));
}
@Test
void shouldCreateRole() throws Exception {
    objectMapper.registerModule(new JavaTimeModule());
    objectMapper.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
    Role role = new Role(null, "mfine",
                         LocalDateTime.now(), "", 0, null, "admin");
    // 这里 也使用了参数匹配器
    given(roleService.insertSelective(BDDMockito.any())).willReturn(1);
    ResultActions actions = this.mvc.perform(post("/api/role/createRole").content(objectMapper.writeValueAsString(role))
                                             .accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON));
    actions.andExpect(status().isOk()).andReturn().getResponse().setCharacterEncoding("UTF-8");
    actions.andExpect(ResultMatcher.matchAll(result -> {
        Assert.assertTrue(result.getResponse().getContentAsString().contains("success"));
    }));
}

总结

完整的测试时重构得底气,当没有测试得时候一切重构都是扯淡。程序出bug之后第一件事应该是复现bug,增补测试然后再是修复bug,如果是线上紧急情况那也应该在时候补充测试。但是测试也不是银弹,所谓的TDD和BDD也要看情况使用,没有万能方案只有适合方法。

以上就是使用mockito编写测试用例教程的详细内容,更多关于mockito测试用例教程的资料请关注Devmax其它相关文章!

使用mockito编写测试用例教程的更多相关文章

  1. iOS – 开始iOS教程 – 变量之前的下划线?

    这是正确的还是我做错了什么?

  2. Swift开发快速上手系列教程目录-陆续完善,由浅入深

    Swift开发教程一.Swift基础Swift教程01-使用switfc终端命令编译运行swift程序Swift教程02-抓住下一个浪潮之巅Swift教程03-playground剖析swift语言Swift教程04-定义声明变量重要原则Swift教程05-基本数据类型(一)整型浮点型Swift教程06-基本数据类型(二)Bool布尔类型对比Java-boolean,Objc-BOOLSwift教

  3. Swift2.0不深入只浅出入门教程-01-The Basic

    本套视频教程是Swift2.0的入门教程,如果你看过其他的教程,可以不看这套教程,录制这套教程的目的,一个是为了自学,一个是为之后自己的另一套教程打基础。本期教程主要介绍Swift语言的一些基础知识。由于CSDN学院的课程审核还没有通过,所以暂时就放到了百度网盘。

  4. Swift教程-视频拍摄教程

    在此教程中,我们将拍摄一段保存到手机相册的视频。教程运行在iOS8.4和Xcode6.4下。打开Xcode并创建一个newSingleViewApplication,项目名称为IOS8SwiftTakeVideoPlayerTutorial,接着填上你的OrganizationName和OrganizationIdentifier,选择Swift语言,在设备一栏只选择iPhone。ImagePickerController的数据可以是Camera或Movie两种类型。视频的maximum长度设置为10秒。

  5. 详解 SiriKit - SiriKit 教程Part 2

    此文章是SiriKit教程系列的第二篇,建议先去阅读第一篇。处理SiriKit请求为了让集成的Siri更有用,可以使用INSendMessageIntentHandling协议的回调方法增加信息内容。协议有以下可选方法:只要实现这些方法,就可以给SiriKit提供更多信息,例如接收者、内容、小组名字、服务名字或者发送者。后面会详细说明区别,现在先介绍如何使用Siri提供的数据。Siri传入的intent对象包含文字版的消息内容。现在当我们尝试发送消息时,SiriKit就明白必须要提供内容值。

  6. swift 学习资源 - Swift 语言指南

    对于精选项目及文章,可直接访问《Swift项目精选》和《Swift文章精选》。对于Swift开源及跨平台开发的同学,可以关注swift.org教程文章开源项目推荐网站苹果官方Swift:Swift概述、博客以及开发资源。swift.org:开源后独立出来的Swift开源社区。ksm/SwiftInFlux:作者将AppleDeveloperForums上有关Swift特性、缺陷及变更讨论分类汇总并更新到GitHub,具有很好的可读性。从中可以一窥Swift缺陷及未来潜在地变化。近期第一时间出了三本Swif

  7. [译] NSCollectionView 入门教程

    Mac中自带的Finder和Photos就是使用了它:通过一个CollectionView来展示所有的文件和图片。NSCollectionView最早在OSX10.5被推出,它可以非常方便地布局一组具有相同大小的item,并把它们展示在一个可以滑动的ScrollView中。在OSX10.11ElCapitan中,参照iOS上的UICollectionView,NSCollectionView被全面进行了升级。在这个NSCollectionView的入门教程中,你将会创造一个叫SlideMagic的app,

  8. unity3d – Android游戏开发使用统一3D教程

    我是团结3D的新手.我打算用Unity3D开发一个安卓游戏,我已经在网上搜索了Unity3D中的android相关教程,但是找不到一个好的开发人员教我一个应用程序启动,所以大家请建议我一个网站我可以启动.我需要android教程,而不是独立的平台教程.提前致谢.解决方法你可以使用Google或Unity3Dofficialdocumentation.

  9. 解析Android中的LinkedList时的java.lang.AbstractMethodError

    我想窥探android中的Linkedlist.但是,发生了异常.libs的依赖关系是甚至我将mockito-core-1.10.19.jar更新为mockito-core-2.0.31-beta.jar,问题依然存在.但是Mockito.mock还可以,我对这个问题没有任何想法.谢谢.解决方法我刚刚找到了解决问题的另一种方法.对于dexmaker1.2来说这是一个问题,我们应该升级到dexmaker1.4,dexmaker-mockito1.4并包含dexmaker-dx-1.4.所以依赖是

  10. Arduino和Android的“Hello World”教程

    我当然知道Arduino网站和Android开发者文档,但它们过于复杂……解决方法你问两个问题:我如何编程Arduino?

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部