学习 Spring Boot:(二十九)Spring Boot Junit 单元测试

From: https://blog.wuwii.com/springboot-test.html

前言

JUnit 是一个回归测试框架,被开发者用于实施对应用程序的单元测试,加快程序编制速度,同时提高编码的质量。

JUnit 测试框架具有以下重要特性:

  • 测试工具
  • 测试套件
  • 测试运行器
  • 测试分类

了解 Junit 基础方法

加入依赖

pom.xml 中加入依赖:

 

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<scope>test</scope>

<version>4.12</version>

</dependency>

创建测试类和测试方法

  1. 测试类的的命名规则一般是 xxxTest.java
  2. 测试类中测试的方法可以有前缀,这个看统一标准,所以有时候会发现别人的测试方法上有test前缀;
  3. 并且测试方法上加上注解 @Test

使用 IDEA 中,选中当前类名,使用快捷键 ALT + ENTER(WIN),向下选则 Create Test 回车,即可进入生成测试类的选项中,再次回车,就快速的生成测试类。

img

OK 完你会发现,生成的测试类在 src/test 目录下,测试类和源代码的包名 是一致的。生成后结果(注意下生成的方法名是不加 test):

 

public class HelloServiceImplTest {

@Before

public void setUp() throws Exception {

}

@After

public void tearDown() throws Exception {

}

@Test

public void say() {

}

}

JUnit中的注解

  • @BeforeClass:针对所有测试,只执行一次,且必须为static void
  • @Before:初始化方法,执行当前测试类的每个测试方法前执行。
  • @Test:测试方法,在这里可以测试期望异常和超时时间
  • @After:释放资源,执行当前测试类的每个测试方法后执行
  • @AfterClass:针对所有测试,只执行一次,且必须为static void
  • @Ignore:忽略的测试方法(只在测试类的时候生效,单独执行该测试方法无效)
  • @RunWith:可以更改测试运行器 ,缺省值 org.junit.runner.Runner

一个单元测试类执行顺序为:

@BeforeClass –> @Before –> @Test –> @After –> @AfterClass

每一个测试方法的调用顺序为:

@Before –> @Test –> @After

超时测试

如果一个测试用例比起指定的毫秒数花费了更多的时间,那么 Junit 将自动将它标记为失败。timeout 参数和 @Test注释一起使用。现在让我们看看活动中的 @test(timeout)

 

@Test(timeout = 1000)

public void testTimeout() throws InterruptedException {

TimeUnit.SECONDS.sleep(2);

System.out.println("Complete");

}

上面测试会失败,在一秒后会抛出异常 org.junit.runners.model.TestTimedOutException: test timed out after 1000 milliseconds

异常测试

你可以测试代码是否它抛出了想要得到的异常。expected 参数和 @Test 注释一起使用。现在让我们看看活动中的 @Test(expected)

 

@Test(expected = NullPointerException.class)

public void testNullException() {

throw new NullPointerException();

}

上面代码会测试成功。

套件测试

 

public class TaskOneTest {

@Test

public void test() {

System.out.println("Task one do.");

}

}

public class TaskTwoTest {

@Test

public void test() {

System.out.println("Task two do.");

}

}

public class TaskThreeTest {

@Test

public void test() {

System.out.println("Task Three.");

}

}

@RunWith(Suite.class) // 1. 更改测试运行方式为 Suite

// 2. 将测试类传入进来

@Suite.SuiteClasses({TaskOneTest.class, TaskTwoTest.class, TaskThreeTest.class})

public class SuitTest {

/**

* 测试套件的入口类只是组织测试类一起进行测试,无任何测试方法,

*/

}

参数化测试

Junit 4 引入了一个新的功能参数化测试。参数化测试允许开发人员使用不同的值反复运行同一个测试。你将遵循 5 个步骤来创建参数化测试

  • @RunWith(Parameterized.class)来注释 test 类。
  • 创建一个由 @Parameters 注释的公共的静态方法,它返回一个对象的集合(数组)来作为测试数据集合。
  • 创建一个公共的构造函数,它接受和一行测试数据相等同的东西。
  • 为每一列测试数据创建一个实例变量。
  • 用实例变量作为测试数据的来源来创建你的测试用例。
 

//1.更改默认的测试运行器为RunWith(Parameterized.class)

@RunWith(Parameterized.class)

public class ParameterTest {

// 2.声明变量存放预期值和测试数据

private String firstName;

private String lastName;

//3.声明一个返回值 为Collection的公共静态方法,并使用@Parameters进行修饰

@Parameterized.Parameters //

public static List<Object[]> param() {

// 这里我给出两个测试用例

return Arrays.asList(new Object[][]{{"Mike", "Black"}, {"Cilcln", "Smith"}});

}

//4.为测试类声明一个带有参数的公共构造函数,并在其中为之声明变量赋值

public ParameterTest(String firstName, String lastName) {

this.firstName = firstName;

this.lastName = lastName;

}

// 5. 进行测试,发现它会将所有的测试用例测试一遍

@Test

public void test() {

String name = firstName + " " + lastName;

assertThat("Mike Black", is(name));

}

}

Hamcrest

JUnit 4.4 结合 Hamcrest 提供了一个全新的断言语法——assertThat。

语法:

 

assertThat( [actual], [matcher expected] );

assertThat 使用了 Hamcrest 的 Matcher 匹配符,用户可以使用匹配符规定的匹配准则精确的指定一些想设定满足的条件,具有很强的易读性,而且使用起来更加灵活。

具体使用的一些匹配规则可以查看源码。

Spring Boot 中使用 JUnit

Spring 框架提供了一个专门的测试模块(spring-test),用于应用程序的集成测试。 在 Spring Boot 中,你可以通过spring-boot-starter-test启动器快速开启和使用它。

加入依赖

 

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

Spring Boot 测试

 

// 获取启动类,加载配置,确定装载 Spring 程序的装载方法,它回去寻找 主配置启动类(被 @SpringBootApplication 注解的)

@SpringBootTest

// 让 JUnit 运行 Spring 的测试环境, 获得 Spring 环境的上下文的支持

@RunWith(SpringRunner.class)

public class EmployeeServiceImplTest {

// do

}

Spring MVC 测试

当你想对 Spring MVC 控制器编写单元测试代码时,可以使用@WebMvcTest注解。它提供了自配置的 MockMvc,可以不需要完整启动 HTTP 服务器就可以快速测试 MVC 控制器

  1. 需要测试的 Controller:

  •  

    @RestController

    @RequestMapping(value = "/emp", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)

    public class EmployeeController {

    private final EmployeeService employeeService;

    @Autowired

    public EmployeeController(EmployeeService employeeService) {

    this.employeeService = employeeService;

    }

    @GetMapping

    public ResponseEntity<List<EmployeeResult>> listAll() {

    return ResponseEntity.ok(employeeService.findEmployee());

    }

    }

  • 编写 MockMvc 的测试类:

  •  

    @RunWith(SpringRunner.class)

    @WebMvcTest(EmployeeController.class)

    public class EmployeeController2Test {

    @Autowired

    private MockMvc mvc;

    @MockBean

    private EmployeeService employeeService;

    public void setUp() {

    // 数据打桩,设置该方法返回的 body一直 是空的

    Mockito.when(employeeService.findEmployee()).thenReturn(new ArrayList<>());

    }

    @Test

    public void listAll() throws Exception {

    mvc.perform(MockMvcRequestBuilders.get("/emp"))

    .andExpect(status().isOk()) // 期待返回状态吗码200

    // JsonPath expression https://github.com/jayway/JsonPath

    //.andExpect(jsonPath("$[1].name").exists()) // 这里是期待返回值是数组,并且第二个值的 name 存在,所以这里测试是失败的

    .andDo(print()); // 打印返回的 http response 信息

    }

    }

    使用@WebMvcTest注解时,只有一部分的 Bean 能够被扫描得到,它们分别是:

    • @Controller
    • @ControllerAdvice
    • @JsonComponent
    • Filter
    • WebMvcConfigurer
    • HandlerMethodArgumentResolver
      其他常规的@Component(包括@Service@Repository等)Bean 则不会被加载到 Spring 测试环境上下文中。
      所以我在上面使用了数据打桩,Mockito 在这篇文章最后一节。
  • 我们也可以注入Spring 上下文的环境到 MockMvc 中,如下编写 MockMvc 的测试类:

  1.  

    @RunWith(SpringRunner.class)

    @SpringBootTest

    public class EmployeeControllerTest {

    /**

    * Interface to provide configuration for a web application.

    */

    @Autowired

    private WebApplicationContext ctx;

    private MockMvc mockMvc;

    /**

    * 初始化 MVC 的环境

    */

    @Before

    public void before() {

    mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();

    }

    @Test

    public void listAll() throws Exception {

    mockMvc

    .perform(get("/emp") // 测试的相对地址

    .accept(MediaType.APPLICATION_JSON_UTF8) // accept response content type

    )

    .andExpect(status().isOk()) // 期待返回状态吗码200

    // JsonPath expression https://github.com/jayway/JsonPath

    .andExpect(jsonPath("$[1].name").exists()) // 这里是期待返回值是数组,并且第二个值的 name 存在

    .andDo(print()); // 打印返回的 http response 信息

    }

    }

    值得注意的是需要首先使用 WebApplicationContext 构建 MockMvc

Spring Boot Web 测试

当你想启动一个完整的 HTTP 服务器对 Spring Boot 的 Web 应用编写测试代码时,可以使用@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)注解开启一个随机的可用端口。Spring Boot 针对 REST 调用的测试提供了一个 TestRestTemplate 模板,它可以解析链接服务器的相对地址。

 

@RunWith(SpringRunner.class)

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

public class EmployeeController1Test {

@Autowired

private TestRestTemplate restTemplate;

@Test

public void listAll() {

ResponseEntity<List> result = restTemplate.getForEntity("/emp", List.class);

Assert.assertThat(result.getBody(), Matchers.notNullValue());

}

}

其实之前上面的测试返回结果不是很正确,只能接收个List,给测试代码添加了不少麻烦,还好最终找到了解决办法:

 

@Test

public void listAll() {

// 由于我返回的是 List 类型的,一直想不到办法解决,网上给出了解决办法,使用 exchange 函数代替

//public <T> ResponseEntity<T> exchange(String url, HttpMethod method,

// HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType,

// Object... urlVariables) throws RestClientException {

ParameterizedTypeReference<List<EmployeeResult>> type = new ParameterizedTypeReference<List<EmployeeResult>>() {};

ResponseEntity<List<EmployeeResult>> result = restTemplate.exchange("/emp", HttpMethod.GET, null, type);

Assert.assertThat(result.getBody().get(0).getName(), Matchers.notNullValue());

}

Spring Data JPA 测试

我们可以使用 @DataJpaTest注解表示只对 JPA 测试;@DataJpaTest注解它只扫描@EntityBean 和装配 Spring Data JPA 存储库,其他常规的@Component(包括@Service@Repository等)Bean 则不会被加载到 Spring 测试环境上下文。

@DataJpaTest 还提供两种测试方式:

  1. 使用内存数据库 h2database,Spring Data Jpa 测试默认采取的是这种方式;
  2. 使用真实环境的数据库。

使用内存数据库测试

  1. 默认情况下,@DataJpaTest使用的是内存数据库进行测试,你无需配置和启用真实的数据库。只需要在 pom.xml 配置文件中声明如下依赖即可:

 

<dependency>

<groupId>com.h2database</groupId>

<artifactId>h2</artifactId>

</dependency>

gradle file:

  •  

    testCompile('com.h2database:h2')

  • 编写测试方法:

  1.  

    @RunWith(SpringRunner.class)

    @DataJpaTest

    public class EmployeeDaoTest {

    @Autowired

    private EmployeeDao employeeDao;

    @Test

    public void testSave() {

    Employee employee = new Employee();

    EmployeeDetail detail = new EmployeeDetail();

    detail.setName("kronchan");

    detail.setAge(24);

    employee.setDetail(detail);

    assertThat(detail.getName(), Matchers.is(employeeDao.save(employee).getDetail().getName()));;

    }

    }

使用真实数据库测试

如要需要使用真实环境中的数据库进行测试,需要替换掉默认规则,使用@AutoConfigureTestDatabase(replace = Replace.NONE)注解:

 

@RunWith(SpringRunner.class)

@DataJpaTest

// 加入 AutoConfigureTestDatabase 注解

@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)

public class EmployeeDaoTest {

@Autowired

private EmployeeDao employeeDao;

@Test

public void testSave() {

Employee employee = new Employee();

EmployeeDetail detail = new EmployeeDetail();

detail.setName("kronchan");

detail.setAge(24);

employee.setDetail(detail);

assertThat(detail.getName(), Matchers.is(employeeDao.save(employee).getDetail().getName()));;

}

}

事务控制

执行上面的新增数据的测试,发现测试通过,但是数据库却并没有新增数据。默认情况下,在每个 JPA 测试结束时,事务会发生回滚。这在一定程度上可以防止测试数据污染数据库。

如果你不希望事务发生回滚,你可以使用@Rollback(false)注解,该注解可以标注在类级别做全局的控制,也可以标注在某个特定不需要执行事务回滚的方法级别上。

也可以显式的使用注解 @Transactional 设置事务和事务的控制级别,放大事务的范围。

Mockito

这部分参考 使用Mockito和SpringTest进行单元测试

JUnitSpringTest,基本上可以满足绝大多数的单元测试了,但是,由于现在的系统越来越复杂,相互之间的依赖越来越多。特别是微服务化以后的系统,往往一个模块的代码需要依赖几个其他模块的东西。因此,在做单元测试的时候,往往很难构造出需要的依赖。一个单元测试,我们只关心一个小的功能,但是为了这个小的功能能跑起来,可能需要依赖一堆其他的东西,这就导致了单元测试无法进行。所以,我们就需要再测试过程中引入Mock测试。

所谓的Mock测试就是在测试过程中,对于一些不容易构造的、或者和这次单元测试无关但是上下文又有依赖的对象,用一个虚拟的对象(Mock对象)来模拟,以便单元测试能够进行。

比如有一段代码的依赖为:
img
当我们要进行单元测试的时候,就需要给A注入BC,但是C又依赖了DD又依赖了E。这就导致了,A的单元测试很难得进行。
但是,当我们使用了Mock来进行模拟对象后,我们就可以把这种依赖解耦,只关心A本身的测试,它所依赖的B和C,全部使用Mock出来的对象,并且给MockBMockC指定一个明确的行为。就像这样:
img

因此,当我们使用Mock后,对于那些难以构建的对象,就变成了个模拟对象,只需要提前的做Stubbing(桩)即可,所谓做桩数据,也就是告诉Mock对象,当与之交互时执行何种行为过程。比如当调用B对象的b()方法时,我们期望返回一个true,这就是一个设置桩数据的预期。

基础

Mockito 简明教程

Spring Boot 中使用

上面的 Spring MVC 测试 中也使用到了 Mockito

spring-boot-starter-test 自带了 mockito-core

基础业务

 

@Entity

@Data

@NoArgsConstructor

public class User implements Serializable {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

@Column(unique = true, nullable = false, length = 50)

private String username;

private String password;

@CreationTimestamp

private Date createDate;

public User(Long id, String username) {

this.id = id;

this.username = username;

}

}

public interface IUserRepository extends JpaRepository<User, Long> {

boolean updateUser(User user);

}

@Service

@RequiredArgsConstructor(onConstructor = @__(@Autowired))

public class UserServiceImpl implements IUserService {

private final IUserRepository userRepository;

@Override

public User findOne(Long id) {

return userRepository.getOne(id);

}

@Override

public boolean updateUsername(Long id, String username) {

User user = findOne(id);

if (user == null) {

return false;

}

user.setUsername(username);

return userRepository.updateUser(user);

}

}

测试类

 

public class IUserServiceTest {

private IUserService userService;

//@Mock

private IUserRepository userRepository;

@Before

public void setUp() throws Exception {

// 对所有注解了@Mock的对象进行模拟

// MockitoAnnotations.initMocks(this);

// 不使用注解,可以对单个对象进行 mock

userRepository = Mockito.mock(IUserRepository.class);

// 构造被测试对象

userService = new UserServiceImpl(userRepository);

// 打桩,构建当 userRepository的 getOne 函数执行参数为 1的时候,设置返回的结果 User

Mockito.when(userRepository.getOne(1L)).thenReturn(new User(1L, "kronchan"));

// 打桩,构建当 userRepository的 getOne 函数执行参数为 2的时候,设置返回的结果 null

Mockito.when(userRepository.getOne(2L)).thenReturn(null);

// 打桩,构建当 userRepository的 getOne 函数执行参数为 3的时候,设置结果抛出异常

Mockito.when(userRepository.getOne(3L)).thenThrow(new IllegalArgumentException("The id is not support"));

// 打桩,当 userRepository.updateUser 执行任何User类型的参数,返回的结果都是true

Mockito.when(userRepository.updateUser(Mockito.any(User.class))).thenReturn(true);

}

@Test

public void testUpdateUsernameSuccess() {

long userId = 1L;

String newUsername = "new kronchan";

// 测试某个 service 的方法

boolean updated = userService.updateUsername(userId, newUsername);

// 检查结果

Assert.assertThat(updated, Matchers.is(true));

// Verifies certain behavior <b>happened once</b>.

// mock对象一旦创建,就会自动记录自己的交互行为。通过verify(mock).someMethod()方法,来验证方法是否被调用。

// 验证调用上面的service 方法后是否 userRepository.getOne(1L) 调用过,

Mockito.verify(userRepository).getOne(userId);

// 有条件可以测试下没有被调用过的方法:

// Mockito.verify(userRepository).deleteById(userId);

// 则会测试失败:

// Wanted but not invoked:

// userRepository.deleteById(1L);

// However, there were exactly 2 interactions with this mock:

// userRepository.getOne(1L);

// userRepository.updateUser(

// User(id=1, username=new kronchan, password=null, createDate=null)

// );

// updateUsername 函数中我们调用了已经打桩了的其他的函数,现在我们来验证进入其他函数中的参数

//构造参数捕获器,用于捕获方法参数进行验证

ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);

// 验证updateUser方法是否被调用过,并且捕获入参

Mockito.verify(userRepository).updateUser(userCaptor.capture());

// 获取参数 updatedUser

User updatedUser = userCaptor.getValue();

// 验证入参是否是预期的

Assert.assertThat(updatedUser.getUsername(), Matchers.is(newUsername));

//保证这个测试用例中所有被Mock的对象的相关方法都已经被Verify过了

Mockito.verifyNoMoreInteractions(userRepository);

// 如果有一个交互,但是我们没有verify ,则会报错,

// org.mockito.exceptions.verification.NoInteractionsWanted:

// No interactions wanted here:

// -> at com.wuwii.service.IUserServiceTest.testUpdateUsernameSuccess(IUserServiceTest.java:74)

// But found this interaction on mock 'iUserRepository':

// -> at com.wuwii.service.impl.UserServiceImpl.findOne(UserServiceImpl.java:21)

// ***

}

@Test

public void testUpdateUsernameFailed() {

long userId = 2L;

String newUsername = "new kronchan";

// 没有经过 mock 的 updateUser 方法,它返回的是 false

boolean updated = userService.updateUsername(userId, newUsername);

Assert.assertThat(updated, Matchers.not(true));

//验证userRepository的getOne(2L)这个方法是否被调用过,(这个是被测试过的,此步骤通过)

Mockito.verify(userRepository).getOne(2L);

// 验证 userRepository 的 updateUser(null)这个方法是否被调用过,(这个没有被测试过,此步骤不通过)

//Mockito.verify(userRepository).updateUser(null);

Mockito.verifyNoMoreInteractions(userRepository);

}

}

分析

创建MOCK的对象

我需要对 userService 进行测定,就需要模拟 userRepository 对象。

我在 setUp() 方法中,模拟对象并打桩。

模拟对象有两种方式:

  1. 对注解了@Mock的对象进行模拟 MockitoAnnotations.initMocks(this);
  2. 对单个对象手动 mock :userRepository = Mockito.mock(IUserRepository.class);

数据打桩,除了上面我代码上用的几个方法,还有非常多的方法,具体可以在使用的时候看到,主要分下面几种:

  1. 最基本的用法就是调用 when以及thenReturn方法了。他们的作用就是指定当我们调用被代理的对象的某一个方法以及参数的时候,返回什么值。

  2. 提供参数匹配器,灵活匹配参数。any()any(Class<T> type)anyBoolean()anyByte()anyChar()anyInt()anyLong()等等,它支持复杂的过滤,可以使用正则 Mockito.matches(".*User$")),开头结尾验证endsWith(String suffix)startsWith(String prefix)、判空验证isNotNull() isNull()
    也还可以使用 argThat(ArgumentMatchermatcher),如:ArgumentMatcher只有一个方法boolean matches(T argument);传入入参,返回一个boolean表示是否匹配。

    Mockito.argThat(argument -> argument.getUsername.length() > 6;

  3. Mockito还提供了两个表示行为的方法:thenAnswer(Answer<?> answer);thenCallRealMethod();,分别表示自定义处理调用后的行为,以及调用真实的方法。这两个方法在有些测试用例中还是很有用的。

  4. 对于同一个方法,Mockito可以是顺序与次数关心的。也就是说可以实现同一个方法,第一次调用返回一个值,第二次调用返回一个值,甚至第三次调用抛出异常等等。只需要连续的调用thenXXXX即可。

  5. 如果为一个返回为Void的方法设置桩数据。上面的方法都是表示的是有返回值的方法,而由于一个方法没有返回值,因此我们不能调用when方法(编译器不允许)。因此,对于无返回值的方法,Mockito提供了一些列的doXXXXX方法,比如:doAnswer(Answer answer)doNothing()doReturn(Object toBeReturned)doThrow(Class<? extends Throwable> toBeThrown)doCallRealMethod()。他们的使用方法其实和上面的thenXXXX是一样的,但是when方法传入的是Mock的对象

 

/*对void的方法设置模拟*/

Mockito.doAnswer(invocationOnMock -> {

System.out.println("进入了Mock");

return null;

}).when(fileRecordDao).insert(Mockito.any());

当 Mockito 监视一个真实的对象的时候,我们也可以模拟这个对象的方法返回我们设置的期望值,

  1.  

    List spy = spy(new LinkedList());

    List spy = spy(new LinkedList());

    // IndexOutOfBoundsException (the list is yet empty)

    when(spy.get(0)).thenReturn("foo");

    // You have to use doReturn() for stubbing

    doReturn("foo").when(spy).get(0);

    when方法参数中spy.get(0),调用的是真实list对象的get(0),这会产生 IndexOutOfBoundsException异常,所以这时需要用到 doReturn 方法来设置返回值。

验证测试方法的结果

使用断言语句检查结果。

验证MOCK对象的调用

其实,在这里我们如果只是验证方法结果的正确的话,就非常简单,但是,在复杂的方法调用堆栈中,往往可能出现结果正确,但是过程不正确的情况。比如,updateUserName方法返回false是有两种可能的,一种可能是用户没有找到,还有一种可能就是userRepository.updateUser(userPO)返回false。因此,如果我们只是使用Assert.assertFalse(updated);来验证结果,可能就会忽略某些错误。

  1. 因此我在上面的测试中还需要验证指定的方法 userRepository).getOne(userId);是否运行过,而且我还使用了参数捕获器,抓取中间的方法参数,用来验证。

  2. 提供了verify(T mock, VerificationMode mode)方法。VerificationMode 有很多作用,

  •  

    // 验证指定方法 get(3) 没有被调用

    verify(mock, never()).get(3);

  • verifyZeroInteractionsverifyNoMoreInteractions 验证所有 mock 的方法是否都调用过了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402699.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何通过Maven的Tomcat插件运行Web工程

From: https://blog.wuwii.com/maven-tomcat.html Tomcat 服务器是一个免费的开放源代码的Web 应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发和调试JSP 程序的首选。对于一个初学者来说&…

DUMPE2FS(8)

为什么80%的码农都做不了架构师&#xff1f;>>> DUMPE2FS(8) DUMPE2FS(8) NAME dumpe2fs - dump ext2/ext3/ext4 filesystem information SYNOPSIS dumpe2fs [ -bfhixV ] [ -o superblocksuperblock ] [ -o blocksizeblock- size ] …

Maven多模块,Dubbo分布式服务框架,SpringMVC,前后端分离项目,基础搭建,搭建过程出现的问题...

现互联网公司后端架构常用到SpringSpringMVCMyBatis&#xff0c;通过Maven来构建。通过学习&#xff0c;我已经掌握了基本的搭建过程&#xff0c;写下基础文章为而后的深入学习奠定基础。 首先说一下这篇文章的主要内容分为&#xff1a; 1、Maven多模块项目的创建&#xff1b; …

基于struts2,hibernate的小javaweb项目

19:47:49 这是截图 闲话不说 就开始了 web-xml&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <web-app version"2.5" xmlns"http://java.sun.com/xml/ns/javaee"xmlns:xsi"http://www.w3.org/2001/XMLSchema-in…

MySql的用户权限

用户管理 MySQL数据库中的表与其他任何关系表没有区别&#xff0c;都可以通过典型的SQL命令修改其结构和数据。可以使用GRANT和REVOKE命令。通过这些命令&#xff0c;可以创建和禁用用户&#xff0c;可以在线授予和撤回用户访问权限。在5.0版本中增加了两个新命令&#xff1a;C…

.yaml 文件格式简介

From: https://www.cnblogs.com/wxmdevelop/p/7341292.html YAML 的意思其实是&#xff1a;"Yet Another Markup Language"&#xff08;仍是一种置标语言&#xff09;的缩写。 功能 YAML的语法和其他高阶语言类似&#xff0c;并且可以简单表达清单、散列表&#x…

SnakeYaml快速入门

From: https://www.jianshu.com/p/d8136c913e52 在YAML快速入门[https://www.jianshu.com/p/97222440cd08]中&#xff0c;我们已经简单介绍了YAML的语法&#xff0c;本节中主要介绍YAML的配置读取。 目前有很多可以生成和解析YAML的第三方工具&#xff0c;常见的&#xff0c;…

有源代码的iphone项目

2019独角兽企业重金招聘Python工程师标准>>> http://blog.joomla.org.tw/iphone-ipad/104-iphone.html 學習和利用現成的資源是很重要的&#xff0c;以下列出有原始碼可下載的iPhone/iPod程式&#xff0c;這邊收集的是以已經放到App Store上的程式為主&#xff0c;…

Content Security Policy 入门教程

From: http://www.ruanyifeng.com/blog/2016/09/csp.html 跨域脚本攻击 XSS 是最常见、危害最大的网页安全漏洞。 为了防止它们&#xff0c;要采取很多编程措施&#xff0c;非常麻烦。很多人提出&#xff0c;能不能根本上解决问题&#xff0c;浏览器自动禁止外部注入恶意脚本&…

springboot跨域配置

From: https://www.cnblogs.com/nananana/p/8492185.html 前言&#xff1a; 当它请求的一个资源是从一个与它本身提供的第一个资源的不同的域名时&#xff0c;一个资源会发起一个跨域HTTP请求(Cross-site HTTP request)。 比如说&#xff0c;域名A ( http://domaina.example …

l2正则化

在机器学习中&#xff0c;无论是分类还是回归&#xff0c;都可能存在由于特征过多而导致的过拟合问题。当然解决的办法有 &#xff08;1&#xff09;减少特征&#xff0c;留取最重要的特征。 &#xff08;2&#xff09;惩罚不重要的特征的权重。 但是通常情况下&#xff0c;我们…

机房收费系统的合作版

概述 机房收费系统的合作版自己负责的是B层和Facade层&#xff0c;在做这块的时候有很多的感触&#xff1a;动态SQL语句&#xff1b;设计模式&#xff1b;合作开发应该注意的点。其中动态SQL语句的理解已经在上一篇博客中写了&#xff0c;如果你有意向的话&#xff0c;可以看一…

ajax跨域,这应该是最全的解决方案了

From: https://segmentfault.com/a/1190000012469713 前言 从刚接触前端开发起&#xff0c;跨域这个词就一直以很高的频率在身边重复出现&#xff0c;一直到现在&#xff0c;已经调试过N个跨域相关的问题了&#xff0c;16年时也整理过一篇相关文章&#xff0c;但是感觉还是差…

如何导入ShareSDK的sample

由于项目需要&#xff0c;最近需要做10几个平台的分享&#xff0c;如果自己去集成&#xff0c;浪费很多时间&#xff0c;而且还很难成功。最后发现Sharesdk,可以满足项目需求。 首先&#xff0c;需要到他们的官网http://sharesdk.cn/下载android版本的SDK。 然后玩了一下他们的…

EF5.x Code First 一对多关联条件查询,Contains,Any,All

背景 通过多个部门id获取所有用户&#xff0c;部门和用户是多对多。 已知部门id&#xff0c;获取该部门包括该部门下的所有子部门的所有用户。 关系如下&#xff1a; public class Entity:IEntity{public Guid Id { get; set; }public string CreateUser { get; set; }public D…

Spring5:@Autowired注解、@Resource注解和@Service注解

From: https://www.cnblogs.com/szlbm/p/5512931.html 什么是注解 传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop、事物&#xff0c;这么做有两个缺点&#xff1a; 1、如果所有的内容都配置在.xml文件中&#xff0c;那么.xml文件将会十分庞大&#xff1b;如…

CCNA实验(8) -- PPP HDLC

HDLC帧格式与以太帧格式有很大差别&#xff0c;HDLC帧没有源MAC和目的MAC地址。HDLC不能提供验证&#xff0c;缺少对链路保护。Cisco设备与Cisco设备连接&#xff0c;可用HDLC封装。Cisco设备与非Cisco设备连接&#xff0c;应使用PPP协议。PPP经过4个过程在点到点链路上建立连接…

不使用中间变量交换2个数据

2019独角兽企业重金招聘Python工程师标准>>> 第一种方法&#xff1a; <!-- lang: cpp -->aab;ba-b;aa-b;可能产生越界和溢出。 第二种方法&#xff1a; <!-- lang: cpp -->aa^b;ba^b;aa^b;这种方法只适用整形数。 写成宏的形式 <!-- lang: cpp -->…

slf4j的简单用法以及与log4j的区别

From: https://www.cnblogs.com/qlqwjy/p/9275415.html 之前在项目中用的日志记录器都是log4j的日志记录器&#xff0c;可是到了新公司发现都是slf4j&#xff0c;于是想着研究一下slf4j的用法。 注意:每次引入Logger的时候注意引入的jar包&#xff0c;因为有Logger的包太多了。…

JdbcType类型和Java类型的对应关系

From: https://www.cnblogs.com/tongxuping/p/7134113.html 在Oracle中有些字段不是必填时在用户使用的时候会出现数据null的情况。这个时候在Oracle中是无法进行插入的。 1 JDBC Type Java Type 2 CHAR String 3 VARCHAR String 4 L…