搜索词>>Junit 耗时0.0060
  • junit5 新特性与使用

    在Java编程测试中junit5 新特性与使用,Java编程,junit5<h2>一、摘要说明</h2>     本JUnit 5教程讲解了如何使JUnit 5改编成java 8编码风格和其他几个特性。了解JUnit5与JUnit 3或4的不同之处。<br />     JUnit 5是Java应用程序中使用最广泛的测试框架。 很久以来,JUnit一直在做得很好。 在之间,JDK 8在java中引起了非常令人兴奋的功能,最引人注目的是lambda表达式。 JUnit 5旨在适应java 8风格的编码和几个其他功能,这就是为什么需要java 8在JUnit 5中创建和执行测试(尽管可以执行用JUnit 3或JUnit 4编写的测试用于向下兼容)。<br />     本文将讲解以下内容: <ol> <li>架构体系</li> <li>环境搭建</li> <li>注解</li> <li>编写动态测试</li> <li>测试套件</li> <li>断言</li> <li>假设</li> <li>向下兼容性</li> <li>回顾总结</li> </ol> <h2>二、JUnit 5架构体系</h2>   与JUnit 4不同,JUnit 5不再是单个库,而是模块化结构的集合,JUnit 5由三个不同的子项目组成<br /> <br /> JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage<br /> <br /> <strong>1.JUnit Platform</strong><br /> JUnit平台作为在JVM上启动测试框架的基础。 它还定义了用于开发在平台上运行的测试框架的TestEngine API。 此外,该平台提供了一个控制台启动器,从命令行启动平台,为Gradle和Maven构建插件以及基于JUnit 4的Runner,用于在平台上运行任何TestEngine。<br /> <strong>2.JUnit Jupiter</strong><br /> JUnit Jupiter是用于在JUnit 5中编写测试和扩展的新编程模型和扩展模型的组合.Jupiter子项目提供了一个用于在平台上运行基于Jupiter的测试的TestEngine。<br /> <strong>3.JUnit Vintage</strong><br /> JUnit Vintage提供了一个用于在平台上运行JUnit 3和JUnit 4的测试的TestEngine。 <h2>三、环境搭建</h2> 引入2个最基础的依赖(即Jupiter引擎依赖关系和平台运行程序依赖关系)在您的maven或gradle项目中使用JUnit 5。<br /> <br /> //pom.xml <pre> <code class="language-xml"><dependency>     <groupId>org.junit.jupiter</groupId>     <artifactId>junit-jupiter-engine</artifactId>     <version>${junit.jupiter.version}</version> </dependency> <dependency>     <groupId>org.junit.platform</groupId>     <artifactId>junit-platform-runner</artifactId>     <version>${junit.platform.version}</version>     <scope>test</scope> </dependency></code></pre> //build.gradle <pre> <code>testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0-M4") testRuntime("org.junit.platform:junit-platform-runner:1.0.0-M4")</code></pre> 如果有疑问,请参照官方的build.gradle和pom.xml: <h2>四、JUnit 5注解</h2> JUnit 5提供了以下注释来编写测试。<br /> ANNOTATION    DESCRIPTION<br /> @BeforeEach    注释方法将在测试类中的每个测试方法之前运行。<br /> @AfterEach    注释方法将在测试类中的每个测试方法之后运行。<br /> @BeforeAll    注释方法将在测试类中的所有测试方法之前运行。 此方法必须是静态的。<br /> @AfterAll    注释方法将在测试类中的所有测试方法之后运行。 此方法必须是静态的。<br /> @Test    它用于将方法标记为junit测试<br /> @DisplayName    用于为测试类或测试方法提供任何自定义显示名称<br /> @Disable    它用于禁用或忽略来自测试套件的测试类或方法。<br /> @Nested    用于创建嵌套测试类<br /> @Tag    使用用于测试发现和过滤的标签来标记测试方法或测试类<br /> @TestFactory    标记方法是动态测试的测试工厂 <h2>五、使用JUnit 5中编写动态测试</h2> JUnit 4和JUnit 5在测试写作风格中没有太大变化。 这里是他们的生命周期方法的样本测试。 <pre> <code class="language-java">import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import com.howtodoinjava.junit5.examples.Calculator; public class AppTest { @BeforeAll static void setup(){ System.out.println("@BeforeAll executed"); } @BeforeEach void setupThis(){ System.out.println("@BeforeEach executed"); } @Tag("DEV") @Test void testCalcOne() { System.out.println("======TEST ONE EXECUTED======="); Assertions.assertEquals( 4 , Calculator.add(2, 2)); } @Tag("PROD") @Disabled @Test void testCalcTwo() { System.out.println("======TEST TWO EXECUTED======="); Assertions.assertEquals( 6 , Calculator.add(2, 4)); } @AfterEach void tearThis(){ System.out.println("@AfterEach executed"); } @AfterAll static void tear(){ System.out.println("@AfterAll executed"); } } </code></pre> <h2>六、测试套件</h2> 使用JUnit 5测试套件,您可以将测试扩展到多个测试类和不同的软件包。 JUnit 5提供了两个注解:@SelectPackages和@SelectClasses来创建测试套件。<br /> 要执行该套件,您将使用@RunWith(JUnitPlatform.class)。 <pre> <code class="language-java">​​​​​​​@RunWith(JUnitPlatform.class) @SelectPackages("com.howtodoinjava.junit5.examples") public class JUnit5TestSuiteExample  { }</code></pre> <br /> 此外,您可以使用以下注解来过滤测试包,类甚至测试方法。<br /> 1.@IncludePackages 和 @ExcludePackages 过滤测试包<br /> 2.@IncludeClassNamePatterns 和 @ExcludeClassNamePatterns 过滤测试类<br /> 3.@IncludeTags 和 @ExcludeTags 过滤测试方法 <pre> <code class="language-java">@RunWith(JUnitPlatform.class) @SelectPackages("com.howtodoinjava.junit5.examples") @IncludePackages("com.howtodoinjava.junit5.examples.packageC") @ExcludeTags("PROD") public class JUnit5TestSuiteExample  { }</code></pre> <h2>七、Junit5中的断言</h2>   断言有助于使用测试用例的实际输出验证预期输出。 为了保持简单,所有JUnit Jupiter断言是org.junit.jupiter.Assertions类中的静态方法,例如 assertEquals(),assertNotEquals()。 <pre> <code class="language-java">void testCase() { //Test will pass Assertions.assertNotEquals(3, Calculator.add(2, 2)); //Test will fail Assertions.assertNotEquals(4, Calculator.add(2, 2), "Calculator.add(2, 2) test failed"); //Test will fail Supplier<String> messageSupplier = ()-> "Calculator.add(2, 2) test failed"; Assertions.assertNotEquals(4, Calculator.add(2, 2), messageSupplier); }</code></pre> <h2>八、假设</h2>     Assumptions类提供了静态方法来支持基于假设的条件测试执行。 失败的假设导致测试被中止。 无论何时继续执行给定的测试方法没有意义,通常使用假设。 在测试报告中,这些测试将被标记为已通过。<br />     JUnit jupiter Assumptions类有两个这样的方法:putsFalse(),putsTrue()。 <pre> <code class="language-java">public class AppTest { @Test void testOnDev() { System.setProperty("ENV", "DEV"); Assumptions.assumeTrue("DEV".equals(System.getProperty("ENV")), AppTest::message); } @Test void testOnProd() { System.setProperty("ENV", "PROD"); Assumptions.assumeFalse("DEV".equals(System.getProperty("ENV"))); } private static String message () { return "TEST Execution Failed :: "; } }</code></pre> <h2>九、兼容性</h2> JUnit 3或JUnit 4的向下兼容性<br /> <br /> JUnit 4已经在这里了很长时间,并且有许多测试以junit 4写成.JUnit Jupiter还需要支持这些测试。 为此,开发了JUnit Vintage子项目。<br /> <br /> JUnit Vintage提供了一个TestEngine实现,用于在JUnit 5平台上运行基于JUnit 3和JUnit 4的测试。 <h2>十、总结</h2>     JUnit 5还在开发中。 看起来如此令人兴奋,功能丰富。 而现在它被第三方工具和API扩展开放。 作为测试作者,您可能不会觉得有什么不同,但是当您要扩展或尝试开发任何IDE插件时,您会赞美它。<br /> <br />     作为开发人员,您还可以考虑将测试模板添加到eclipse IDE中以提高开发速度。
  • maven package打包项目跳过Junit测试

    maven package打包项目跳过Junit测试maven package打包项目跳过Junit测试 <pre> <code class="language-xml"><build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.18.1</version> <configuration> <skipTests>true</skipTests> </configuration> </plugin> </plugins> </bulid></code></pre>  
  • Java编程中Parameterized Test-Junit

    Java编程中Parameterized Test-Junit的使用<h2>一、Junit传参到单元测试的方式</h2> <p> 在JUnit中,您可以通过以下方法将参数传递到单元测试方法:</p> <ol> <li>构造函数</li> <li>通过注解@Parameter注入</li> </ol> <p><em>P.S 本教程测试使用Junit版本为4.12</em><br />  </p> <h2>二、多个参数的测试</h2> 创建类MatchUtils,里面有个简单的加法运算,代码如下:<br />   <pre> <code class="language-java">public class MathUtils { public static int add(int a, int b) { return a + b; } }</code></pre> <h2>三、Parameterized via Constructor</h2> 通过构造函数传递参数<br /> <strong>ParameterizedTest.java</strong> <pre> <code class="language-java">import com.mkyong.examples.MathUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import java.util.Arrays; import java.util.Collection; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @RunWith(value = Parameterized.class) public class ParameterizedTest { private int numberA; private int numberB; private int expected; // Inject via constructor // for {8, 2, 10}, numberA = 8, numberB = 2, expected = 10 public ParameterizedTest(int numberA, int numberB, int expected) { this.numberA = numberA; this.numberB = numberB; this.expected = expected; } // name attribute is optional, provide an unique name for test // multiple parameters, uses Collection<Object[]> @Parameters(name = "{index}: testAdd({0}+{1}) = {2}") public static Collection<Object[]> data() { return Arrays.asList(new Object[][]{ {1, 1, 2}, {2, 2, 4}, {8, 2, 10}, {4, 5, 9}, {5, 5, 10} }); } @Test public void test_addTwoNumbes() { assertThat(MathUtils.add(numberA, numberB), is(expected)); } }</code></pre> <h2>四、Parameterized via Field Injection</h2> 通过字段注入方式传参<br /> <strong>ParameterizedTest.java</strong> <pre> <code class="language-java">import com.mkyong.examples.MathUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.junit.runners.Parameterized.Parameter; import java.util.Arrays; import java.util.Collection; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @RunWith(value = Parameterized.class) public class ParameterizedTest { //default value = 0 @Parameter(value = 0) public int numberA; @Parameter(value = 1) public int numberB; @Parameter(value = 2) public int expected; @Parameters(name = "{index}: testAdd({0}+{1}) = {2}") public static Collection<Object[]> data() { return Arrays.asList(new Object[][]{ {1, 1, 2}, {2, 2, 4}, {8, 2, 10}, {4, 5, 9}, {5, 5, 10} }); } @Test public void test_addTwoNumbes() { assertThat(MathUtils.add(numberA, numberB), is(expected)); } }</code></pre> <strong>注意:</strong><br /> <em>注解@Parameters中, name属性是可选的, 可以帮助您通过提供唯一的名称来标识单个测试用例。<br /> 什么是 {0}, {1} and {2}?<br /> 如果参数是 “{ 3, 4, 7 }”, 则 {0} = 3, {1} = 4, {2} = 7.</em><br /> 输出:<br /> <img alt="输出" class="img-thumbnail" src="/assist/images/blog/a3251fffb52b429a93adbaef6d3c2541.png" /> <h2><br /> 五、DomainUtils – 测试一个参数</h2> 另一个简单的类,验证一个域名。<br /> <strong>DomainUtils.java</strong> <pre> <code class="language-java">import java.util.regex.Pattern; public class DomainUtils { private static final String DOMAIN_NAME_PATTERN = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,6}$"; private static Pattern pDomainName = Pattern.compile(DOMAIN_NAME_PATTERN); public static boolean isValid(String domainName) { return pDomainName.matcher(domainName).find(); } }</code></pre> <h2>六、DomainUtils Parameterized 测试</h2> 方法的参数都是通过字段的注入进来<br /> <strong>ParameterizedTest.java</strong> <pre> <code class="language-java">import com.mkyong.examples.DomainUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @RunWith(value = Parameterized.class) public class Parameterized2Test { //default value = 0 @Parameter public String domainName; //Single parameter, use Object[] @Parameters(name = "{index}: testDomain - {0}") public static Object[] data() { return new Object[]{ "google.com", "mkyong.com", "twitter.com" }; } @Test public void test_valid_domain() { assertThat(DomainUtils.isValid(domainName), is(true)); } }</code></pre> 测试输出:<br /> <img alt="测试输出" class="img-thumbnail" src="/assist/images/blog/c10014f87ab24e4a8bb1a69e10f204bb.png" /><br /> <strong>Note</strong><br /> TestNG is more flexible in the way of passing the parameters into unit tests, read this TestNG parameter test.<br /> <br /> <br />  
  • spring boot Junit单元测试_spring 普通项目单元测试

    spring boot Junit单元测试_spring 普通项目单元测试首先,讲解的是spring 普通项目的Junit单元测试。<br /> <br /> 举例maven项目。<br /> 首先引入spring 项目Junit单元测试需要的必须依赖 <pre> <code class="language-xml"><dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.0.8.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency></code></pre> <br /> 编写一个测试的基础类 <pre> <code class="language-java">import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.Log4jConfigurer; import java.io.FileNotFoundException; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:spring-*.xml"}) public class BaseTest { } </code></pre> 其他类只要继承该类就可以不用每次配置spring环境了<br /> <br /> 常见问题: <pre> <code class="language-html">log4j:WARN No appenders could be found for logger (org.springframework.test.context.junit4.SpringJUnit4ClassRunner). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. </code></pre> 解决spring Junit测试log4j环境初始化,在基础测试类中配置log4j的配置文件 <pre> <code class="language-java">import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.Log4jConfigurer; import java.io.FileNotFoundException; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:/springConfig/*.xml"}) public class BaseTest { /*** * 初始化log4j配置 */ static { try { Log4jConfigurer.initLogging("classpath:systemConfig/log4j.properties"); } catch (FileNotFoundException ex) { System.err.println("Cannot Initialize log4j"); } } } </code></pre>
  • Spring框架纯注解方式的junit整合测试

    一般spring框架与junit的整合测试都是通过注解@ContextConfiguration,配置其中的localtions加载的xml配置Spring框架纯注解方式的junit整合测试如下: <pre> <code class="language-java">package common.test.base; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.xqlee.dev.project.server.config.SpringContextConfig; import com.xqlee.dev.project.server.config.SpringMvcConfig; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes={SpringContextConfig.class,SpringMvcConfig.class}) public class BaseTest { } </code></pre> <br /> 可以看出,基于注解的方式的spring框架整合junit测试,就是将localtions载入xml的方式改成classes的方式载入spring的配置类<br /> <br /> <br /> 实现具体的测试,只需要继承上面的BaseTest,在子类需要测试的方法上使用@Test注解即可<br /> <br />  
  • spring boot 2.0入门之spring boot 2.0 Junit单元测试多线程问题解决

    spring boot 2.0 入门之单元测试多线程。spring boot 2.0 项目含多线程异步处理业务单元测试执行主线程结束不等待子线程结束。问题描述测试的业务将部分数据添加入一个公开队列中,有另外一个业务类进行多线程轮训等待处理。业务场景中涉及到了多线程异步任务。当使用spring boot 2.0自带的Junit整合测试业务时候,端点等待子线程无效。子线程会随主线程断点而断点执行。简单的说就是你在测试的业务中添加断点来等待子线程(异步多线程业务处理)时候,你会发现主线断点走一步,子线程也就动一下。从而子线程的异步任务无法得到正确的测试结果。主线程结束子线程就算任务没完成也会被强制结束。问题解决由于Junit的运行机制,目前临时的解决方式是在写的单元测试方法中调用测试的业务方法后面添加线程睡眠,也就是Thread.sleep(time);睡眠的时间根据子线程处理业务的速度估计个2倍。
  • Could not load TestContextBootstrapper [null].

    spring boot项目运行单元测试失败,报错Could not load TestContextBootstrapper [null]. Specify @BootstrapWith's 'value' attribute or make the default bootstrapper class available.错误日志:java.lang.IllegalStateException: Could not load TestContextBootstrapper [null]. Specify @BootstrapWith's 'value' attribute or make the default bootstrapper class available. at org.springframework.test.context.BootstrapUtils.resolveTestContextBootstrapper(BootstrapUtils.java:145) at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:124) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTestContextManager(SpringJUnit4ClassRunner.java:151) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.<init>(SpringJUnit4ClassRunner.java:142) at org.springframework.test.context.junit4.SpringRunner.<init>(SpringRunner.java:49) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104) at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86) at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59) at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26) at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59) at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33) at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:36) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:49) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) Caused by: java.lang.NoSuchMethodError: org.springframework.util.Assert.state(ZLjava/util/function/Supplier;)V at org.springframework.test.context.BootstrapUtils.resolveExplicitTestContextBootstrapper(BootstrapUtils.java:157) at org.springframework.test.context.BootstrapUtils.resolveTestContextBootstrapper(BootstrapUtils.java:128) ... 19 more代码段 小部件
  • spring boot mybatis 整合_spring boot mybatis3 事物配置

    引言    通过之前spring boot mybatis 整合的讲解: spring boot mybaties整合  (spring boot mybaties 整合 基于Java注解方式写sql,无需任何得mapper xml文件)s引言    通过之前spring boot mybatis 整合的讲解: spring boot mybaties整合  (spring boot mybaties 整合 基于Java注解方式写sql,无需任何得mapper xml文件)spring boot mybatis 整合_spring boot与mybaties的使用  (spring boot mybaties 整合 xml mapper方式,也是实际应用最多得方式) 我们对于spring boot mybaties 整合有了一个基础的认知。这里主要正对上面得两篇文章中spring boot mybaties整合讲解得一个扩展学习,事物的配置,整合到spring 的事物控制中。一.环境准备 本博客讲沿用上面的项目进行进一步讲解二.实战编码2.1 spring boot 核心配置文件application.properties#==================DataSource Config Start================== #默认采用Tomcat-jdbc-pool性能和并发最好,注意查看maven依赖中是否有tomcat-jdbc #name #spring.datasource.name=test #url #spring.datasource.url=jdbc:sqlserver://192.168.xxx.xxx;instanceName=sql_03;DatabaseName=edu;integratedSecurity=false spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8 #DriverClass #spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver spring.datasource.tomcat.driver-class-name=com.mysql.jdbc.Driver #DB username spring.datasource.tomcat.username=root #DB password spring.datasource.tomcat.password=root #最大连接数量 spring.datasource.tomcat.max-active=150 #最大闲置连接数量 spring.datasource.tomcat.max-idle=20 #最大等待时间 #spring.datasource.tomcat.max-wait=5000 #==================DataSource Config End================== #==================mybaties Config Start================== #ORM Bean Package mybatis.type-aliases-package=com.example.pojo mybatis.mapper-locations=classpath:/mapper/*.xml #打印mybatiesSql语句 logging.level.com.example.mapper=DEBUG #==================mybaties Config End ================== #模板引擎配置缓存为FALSE。开发调试用 spring.thymeleaf.cache=false 这里注意关注数据连接配置和mybaties的xml mapper文件配置。2.2spring boot mybaties 整合 事物关键配置 MyBatiesConfig.javapackage com.example.config; import javax.sql.DataSource; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * mybaties配置扫描mapper路径 * * @author leftso * */ @Configuration @MapperScan(basePackages = { "com.example.mapper" }) /** 注意,这个注解是扫描mapper接口不是xml文件,使用xml模式必须在配置文件中添加xml的配置 **/ @EnableTransactionManagement /** * 启用事物管理 ,在需要事物管理的service类或者方法上使用注解@Transactional **/ public class MyBatiesConfig { @Autowired private DataSource dataSource; /** * 配合注解完成事物管理 * * @return */ @Bean public PlatformTransactionManager annotationDrivenTransactionManager() { return new DataSourceTransactionManager(dataSource); } } 注意必须把当前的数据源配置进入spring的注解事物管理器。否则通过spring框架的注解标签@Transactional是不会有事物作用的。提示:spring boot 2.1.4.RELEASE 版本无需配置PlatformTransactionManager 也能起作用,也就说仅需要一个注解@EnableTransactionManagementSpring boot 2.x (Spring 5.0为基础的情况)无需使用@EnableTransactionManagement注解,spring boot 项目内部已经启用三.事物演示3.1编写测试代码package com.example.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.example.mapper.UserMapper; import com.example.pojo.User; @RunWith(SpringRunner.class) @SpringBootTest public class TransactionalTest { @Autowired private UserMapper userMapper; @Test public void name() { User user=new User("leftso", "男", 1); userMapper.insert(user); int t=1/0; System.out.println(t); } } 执行前查询数据库:​执行测试代码并观察eclipse的控制台和数据库的数据查询结果:​​很明显在报错的情况下,数据还是插入进了数据库。这并不是我们正常业务想要的结果。3.2编辑测试代码,添加spring框架的事物注解package com.example.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; import com.example.mapper.UserMapper; import com.example.pojo.User; @RunWith(SpringRunner.class) @SpringBootTest public class TransactionalTest { @Autowired private UserMapper userMapper; @Test @Transactional public void name() { User user=new User("测试哈哈", "女", 2); userMapper.insert(user); int t=1/0; System.out.println(t); } } 执行代码并观察eclipse和数据库:​​这次的操作姿势似乎对了。在报错的情况下数据并没有插入数据库。我们仔细观察spring 控制台输出的日志可以发现事物已经在spring的控制下回滚了。​从上图也可以看到回滚的日志
  • Spring Boot Redis 秒杀实现

    简述本文主要通过一个简单的例子模拟实现秒杀情景,其中主要使用Redis事物进行实现spring boot为提供方便的环境简述本文主要通过一个简单的例子模拟实现秒杀情景,其中主要使用Redis事物进行实现spring boot为提供方便的环境。首先导入redis依赖pom.xml文件<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.leftso.demo</groupId> <artifactId>demo-redis-seckill</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo-redis-seckill</name> <description>Redis 实现产品秒杀</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.0.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>Redispool配置配置redis的连接池,这个根据自己需求改。这里测试用。package com.leftso.demo.demoredisseckill; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; @Configuration public class JedisConfig { @Bean public JedisPool jedisPool(){ JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); // 设置配置 jedisPoolConfig.setMaxTotal(1024); jedisPoolConfig.setMaxIdle(100); jedisPoolConfig.setMaxWaitMillis(100); jedisPoolConfig.setTestOnBorrow(false);//jedis 第一次启动时,会报错 jedisPoolConfig.setTestOnReturn(true); JedisPool pool=new JedisPool(jedisPoolConfig,"127.0.0.1",6379); return pool; } } 写一个秒杀的业务处理实现package com.leftso.demo.demoredisseckill.service; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Transaction; import java.util.List; public class Seckill implements Runnable { private JedisPool jedisPool; private String userName; private String productKey; public Seckill(JedisPool jedisPool, String userName, String productKey) { this.jedisPool = jedisPool; this.userName = userName; this.productKey = productKey; } @Override public void run() { Jedis jedis=jedisPool.getResource(); try { jedis.watch(productKey); String val=jedis.get(productKey); int valInt=Integer.valueOf(val); if (valInt>=1){ Transaction tx=jedis.multi(); tx.incrBy(productKey,-1);//原子操作 List<Object> list=tx.exec(); if (list==null||list.isEmpty()){ System.out.println("用户:"+userName+" 抢购失败。"); this.run();//再抢 }else{ System.out.println("用户:"+userName+ " 抢购成功!!!"); // jedis.setnx(productKey,) jedis.rpush(productKey+"user",userName);//成功用户添加入队列 } }else{ System.out.println("商品已抢购完毕-------"); } }catch (Exception e){ e.printStackTrace(); } } } 最后模拟访问package com.leftso.demo.demoredisseckill; import com.leftso.demo.demoredisseckill.service.Seckill; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import redis.clients.jedis.JedisPool; import java.util.ArrayList; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest public class RedisSeckillTest { private final static Logger logger = LoggerFactory.getLogger(RedisSeckillTest.class); @Autowired JedisPool jedisPool; String productKey="SSSSSSKEY"; int productNum=10; @Before public void before(){ jedisPool.getResource().set(productKey,10+"");//设置产品默认库存数量 while (jedisPool.getResource().lpop(productKey+"user")!=null){ }//清空秒杀成功人用户列表 //end } @After public void after(){ String num=jedisPool.getResource().get(productKey); System.out.println("剩余库存:"+num); } @Test public void contextLoads() { try { for (int i = 0; i < 100; i++) { //每个用户件数 Thread t = new Thread(new Seckill(jedisPool,"用户"+i,productKey)); t.start(); } long size=jedisPool.getResource().llen(productKey+"user"); while (true){ if (size==productNum){ break; }else{ size=jedisPool.getResource().llen(productKey+"user"); } } List<String> successUsers=new ArrayList<>(); String user=jedisPool.getResource().lpop(productKey+"user"); while (user!=null){ successUsers.add(user); user=jedisPool.getResource().lpop(productKey+"user"); } System.out.println("活动结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>活动结束"); System.out.println("获奖名单:"+successUsers); Thread.currentThread().sleep(2000); } catch (Exception e) { e.printStackTrace(); } } } 执行单元测试,查看结果:.............................. 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 活动结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>活动结束 获奖名单:[用户66, 用户2, 用户8, 用户10, 用户56, 用户78, 用户33, 用户58, 用户16, 用户87] 剩余库存:0 Process finished with exit code 0您有任何想法欢迎评论区留言讨论,谢谢
  • Java编程之通过eclipse创建maven自定义项目原型模板(Archetype)

    Java编程之通过eclipse创建maven自定义项目原型模板(Archetype),Java编程,maven自定义项目模板<h2>一、简介</h2> <p>    Maven自定义原型有助于创建特定类型的项目结构,在任何标准的Maven原型中都是不可用的。在这个maven教程中,我们将学习从eclipse项目创建自定义原型。</p> <p>    它在某些场景中非常有用,特别是当我们需要在生成的maven项目中定制内容时,maven不提供这个选项。<br /> 本文将讲解以下内容:</p> <ul> <li>为什么需要定制原型?</li> <li>创建自定义原型</li> <li>导入Eclipse项目</li> <li>创建模板文件</li> <li>创建具有自定义原型的新项目</li> </ul> <h2>二、为什么需要定制原型?</h2>     一般来说,maven和一些第三方提供程序提供一些原型,这些原型在快速启动maven项目时非常有用。但是在我们的日常工作中,我们可能面临一些场景,我们需要为以下原因创建自定义的项目结构: <ul> <li>需要在组织内引入/实施特定的项目结构(包括包和骨架类)。</li> <li>在建立项目结构的过程中减少了大量的时间,通过快速地开始实际的工作来增加开发人员的生产力。</li> <li>减少代码审查工作,确保项目结构和预期的工件已经就位。</li> </ul> <h2>三、创建自定义原型</h2> Maven已经提供了一个创建原型的<code>maven-archetype-archetype去创建新的原型模板</code> <pre> <code>mvn archetype:generate -B -DarchetypeArtifactId=maven-archetype-archetype -DgroupId=com.howtodoinjava.archetype -DartifactId=maven-howtodoinjava-archetype -Dversion=1.0-SNAPSHOT</code></pre> 让我来理解上面的命令: <ul> <li><code>-DarchetypeArtifactId=maven-archetype-archetype</code>是maven提供的原型来创建新的自定义原型。</li> <li><code>-DgroupId=com.howtodoinjava.archetype</code> 是我们现在要创建的原型的组id。</li> <li><code>-DartifactId=maven-howtodoinjava-archetype</code> 是我们现在所创造的原型的名称。</li> <li><code>-Dversion=1.0-SNAPSHOT </code>是maven原型的版本。</li> </ul> <br /> 这里的所有参数都是不言自明的。我们仍然可以在任何时候遵循官方的maven文档了解更多细节。<br /> <br /> <em><strong>注意:<br /> 我们现在需要从命令提示符运行此命令,然后确保设置maven类路径。</strong></em><br /> 因此,当您从命令提示符运行这个命令时,您将在启动mvn命令的同一目录中生成一个maven项目。 <h2>四、将项目导入eclipse中</h2> 接下来的工作是在eclipse中导入这个项目,以进一步细化以满足我们的需求。在导入后,eclipse项目结构将是这样的。<br /> <img alt="项目结构" class="img-thumbnail" src="/assist/images/blog/ae2f47adecd5455facdb229592439928.jpg" /><br /> 一旦在eclipse中导入,我们需要执行以下步骤。 <ul> <li>删除的内容/ src / main /资源/ archetype-resources / src / main / java & / src / main /资源/ archetype-resources / src /测试/ java——主要是App.java和AppTest.java</li> <li>我们需要重新命名文件/ src / main / resources / meta - inf / maven /原型。archetype-metadata xml。xml,稍后我们需要更改这个文件的内容,我们稍后会看到。</li> </ul> 在上面的步骤之后,文件夹结构将会是这样,<br /> <img alt="项目结构2" class="img-thumbnail" src="/assist/images/blog/6775305f4d8040ee80fff9edaaa09e41.jpg" /> <h2>五、创建模板文件</h2> 现在,我们将为原型应该生成的类和资源创建一些模板文件。 <ul> <li>创建一个模板java文件\src\main\resources\archetype-resources\src\main\java\__project-name__.java和内容:    </li> </ul> <pre> <code class="language-java">package ${package}; public class ${project-name} { public static void ${project-name}SampleMethod() { System.out.println("Sample method generated by maven Archetype.."); } }</code></pre>     这个模板文件将根据运行时提供的占位符的值生成java文件。文件名中的位置必须被__NAME__包围,占位符逻辑名分隔符应该是连字符(-)。 <ul> <li>为属性文件创建一个模板;具有属性键值模板 <pre> <code>${project-name}.key=This is ${project-name} test property and the file name will be \src\main\resources\archetype-resources\src\main\resources\__property-file-name__.properties</code></pre> </li> </ul>     因为我们想在运行时生成文件名,所以我们在文件名中放置了一个占位符。 <ul> <li>您可以自由地按照您的需求来做模板文件。</li> <li>现在我们需要修改archetype-metadata.xml像下面:</li> </ul>     —requiredProperties部分将在生成这个原型的项目时声明所需的所有属性和它们的默认值。<br />     -文件集将声明将要生成的最终项目中的文件。<br />      <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <archetype-descriptor name="basic"> <requiredProperties> <requiredProperty key="project-name" /> <requiredProperty key="property-file-name"> <defaultValue>Resource-default</defaultValue> </requiredProperty> <!--JUnit version to use in generated project --> <requiredProperty key="junit-version"> <defaultValue>4.10</defaultValue> </requiredProperty> </requiredProperties> <!--Add new fileset for resources --> <!--Set filtered="true" to process files in that directory as templates --> <!--Set packaged="false" to remove package folder structure from resource directory --> <fileSets> <fileSet filtered="true"> <directory>src/main/resources</directory> <!--Filters example --> <includes> <include>*.txt</include> <include>*.properties</include> </includes> <excludes> <exclude>**/*.xml</exclude> </excludes> </fileSet> <fileSet filtered="true" packaged="true"> <directory>src/main/java</directory> </fileSet> <fileSet filtered="true" packaged="true"> <directory>src/test/java</directory> </fileSet> </fileSets> </archetype-descriptor></code></pre> <p>我们还需要更改pom.xml文件archetype-resources文件夹下接受运行时GAV(GroupId:ArtifactId:版本)坐标。我们需要将它更改为占位符,以接受运行时值,因为我们将使用这个原型生成项目。</p> <p><code>pom.xml<span style="font-family:sans-serif,Arial,Verdana,Trebuchet MS">在</span></code><code>archetype-resources下面应该是这样的:</code><br />  </p> <pre> <code class="language-xml"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>${groupId}</groupId> <artifactId>${artifactId}</artifactId> <version>${version}</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit-version}</version> <scope>test</scope> </dependency> </dependencies> </project></code></pre> 因此,我们主要完成了与原型更改相关的更改,现在我们应该通过命令mvn clean安装构建原型maven项目。项目应该构建良好,我们已经准备好使用这个原型来创建新的maven项目。 <h2>六、创建具有自定义原型的新项目</h2> <br /> 一旦我们成功地构建了原型项目,并且这个项目成功地安装在本地,我们将运行下面的命令来创建maven项目。 <pre> <code>mvn archetype:generate -DarchetypeCatalog=local -DarchetypeArtifactId=maven-howtodoinjava-archetype -DarchetypeGroupId=com.howtodoinjava.archetype -DarchetypeVersion=1.0-SNAPSHOT</code></pre> Maven将启动交互模式,并询问有关新Maven项目所需的所有属性。跳过默认值的属性,但是如果不确定最后一步中的配置,则可以重写默认值。<br /> <br /> 定制原型输入,例如:<br /> <img alt="原型" class="img-thumbnail" src="/assist/images/blog/6331413850e74436bae47a60a97c947c.jpg" /> <p>一旦这个步骤完成,我们就应该有一个基于我们开发的模板创建的新项目。</p> <p>因此,如果您在任何这样的场景中,默认maven原型是不够的,您可以使用maven的隐藏力量创建您自己的自定义原型。<br />  </p>