工作目录第一次备份

master
RENCHAO 3 years ago
commit eb9908d993

@ -0,0 +1,67 @@
<?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>
<groupId>com.renchao</groupId>
<artifactId>Drools</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<!-- SpringBoot Web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 规则引擎 -->
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>7.6.0.Final</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,11 @@
package com.renchao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

@ -0,0 +1,31 @@
package com.renchao.bean;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@Accessors(chain = true)
public class Order {
/**
* 订单原价金额
*/
private int price;
/**
*下单人
*/
private User user;
/**
*积分
*/
private int score;
/**
* 下单日期
*/
private Date bookingDate;
}

@ -0,0 +1,9 @@
package com.renchao.bean;
import lombok.Data;
@Data
public class User {
private String name;
private Integer level;
}

@ -0,0 +1,67 @@
package com.renchao.config;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.*;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.io.IOException;
/**
* <p> 规则引擎自动配置类 </p>
*/
@Configuration
public class DroolsAutoConfiguration {
private static final String RULES_PATH = "rules/";
private KieServices getKieServices() {
return KieServices.Factory.get();
}
@Bean
@ConditionalOnMissingBean(KieFileSystem.class)
public KieFileSystem kieFileSystem() throws IOException {
KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
for (Resource file : getRuleFiles()) {
kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
}
return kieFileSystem;
}
private Resource[] getRuleFiles() throws IOException {
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
return resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*");
}
@Bean
@ConditionalOnMissingBean(KieContainer.class)
public KieContainer kieContainer() throws IOException {
final KieRepository kieRepository = getKieServices().getRepository();
kieRepository.addKieModule(() -> kieRepository.getDefaultReleaseId());
KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
kieBuilder.buildAll();
KieContainer kieContainer = getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
return kieContainer;
}
@Bean
@ConditionalOnMissingBean(KieBase.class)
public KieBase kieBase() throws IOException {
return kieContainer().getKieBase();
}
}

@ -0,0 +1,47 @@
package rules
import com.renchao.bean.Order
rule "zero"
no-loop true
lock-on-active true
salience 1
when
$s : Order(price <= 100)
then
$s.setScore(0);
update($s);
end
rule "add100"
no-loop true
lock-on-active true
salience 1
when
$s : Order(price > 100 && price <= 500)
then
$s.setScore(100);
update($s);
end
rule "add500"
no-loop true
lock-on-active true
salience 1
when
$s : Order(price > 500 && price <= 1000)
then
$s.setScore(500);
update($s);
end
rule "add1000"
no-loop true
lock-on-active true
salience 1
when
$s : Order(price > 1000)
then
$s.setScore(1000);
update($s);
end

@ -0,0 +1,96 @@
package com.renchao;
import com.renchao.bean.Order;
import com.renchao.bean.User;
import org.junit.jupiter.api.Test;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
/**
* 需求
* 计算额外积分金额 规则如下: 订单原价金额
* 100以下, 不加分
* 100-500 加100分
* 500-1000 加500分
* 1000 以上 加1000分
*/
@SpringBootTest
public class DroolsOrderTests {
@Resource
private KieContainer kieContainer;
@Test
public void droolsOrderTest() throws Exception {
KieSession kieSession = kieContainer.newKieSession();
List<Order> orderList = getInitData();
for (Order order: orderList) {
// 1-规则引擎处理逻辑
kieSession.insert(order);
kieSession.fireAllRules();
// 2-执行完规则后, 执行相关的逻辑
addScore(order);
}
kieSession.dispose();
}
private static void addScore(Order o){
System.out.println("用户" + o.getUser().getName() + "享受额外增加积分: " + o.getScore());
}
private static List<Order> getInitData() throws Exception {
List<Order> orderList = new ArrayList<>();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
{
Order order = new Order();
order.setPrice(80);
order.setBookingDate(df.parse("2015-07-01"));
User user = new User();
user.setLevel(1);
user.setName("Name1");
order.setUser(user);
order.setScore(111);
orderList.add(order);
}
{
Order order = new Order();
order.setPrice(200);
order.setBookingDate(df.parse("2015-07-02"));
User user = new User();
user.setLevel(2);
user.setName("Name2");
order.setUser(user);
orderList.add(order);
}
{
Order order = new Order();
order.setPrice(800);
order.setBookingDate(df.parse("2015-07-03"));
User user = new User();
user.setLevel(3);
user.setName("Name3");
order.setUser(user);
orderList.add(order);
}
{
Order order = new Order();
order.setPrice(1500);
order.setBookingDate(df.parse("2015-07-04"));
User user = new User();
user.setLevel(4);
user.setName("Name4");
order.setUser(user);
orderList.add(order);
}
return orderList;
}
}

@ -0,0 +1,136 @@
<?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>
<groupId>org.example</groupId>
<artifactId>MyMaven</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<!-- EasyExcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.6</version>
</dependency>
<!--常用工具库-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.26</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.9</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>compile</scope>
</dependency>
<!-- drools规则引擎 -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.6.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.20</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.14</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.9.Final</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.apache.poi</groupId>-->
<!-- <artifactId>poi</artifactId>-->
<!-- <version>4.1.1</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.apache.poi</groupId>-->
<!-- <artifactId>poi-ooxml</artifactId>-->
<!-- <version>4.1.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.36</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-generator-annprocess -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.36</version>
</dependency>
</dependencies>
</project>

@ -0,0 +1,42 @@
package com.renchao.chemical;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
@Data
public class DangerChemicalInfo {
@ExcelProperty(value = "序号")
private String number;
@ExcelProperty(value = "日期")
private String date;
@ExcelProperty(value = "样品名称及型号")
private String nameAndModel;
@ExcelProperty(value = "进口国别/地区")
private String country;
@ExcelProperty(value = "进口收货人/消费使用单位")
private String receiver;
@ExcelProperty(value = "生产商/发货人")
private String consignor;
@ExcelProperty(value = "成分/组分")
private String composition;
@ExcelProperty(value = "危险特性")
private String hazardousProperties;
@ExcelProperty(value = "危险类别")
private String hazardCategory;
@ExcelProperty(value = "联合国编号")
private String unNumber;
@ExcelProperty(value = "建议包装类别")
private String packingCategory;
}

@ -0,0 +1,113 @@
package com.renchao.chemical;
import com.alibaba.excel.EasyExcel;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DataProcessing {
/**
* 危险类别正则
*/
private static final Pattern HAZARD_CATEGORY_PATTERN1 = Pattern.compile("类别[::]((\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?)([((]((\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?))[))])?)");
private static final Pattern HAZARD_CATEGORY_PATTERN2 = Pattern.compile("(\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?)([((]((\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?))[))])?");
/**
* 联合国编号正则
*/
private static final Pattern UN_NUMBER_PATTERN = Pattern.compile("([Uu][Nn])?(\\d{4})");
/**
* 建议包装类别正则
*/
private static final Pattern PACKING_CATEGORY_PATTERN = Pattern.compile("[,,::;;/](I{1,3}|([ⅠⅡⅢ]))|\\d{4}[,,::;;/]([123])");
public static void main(String[] args) {
String filePath = "C:/Users/RENCHAO/Desktop/gggg.xlsx";
List<DangerChemicalInfo> list = EasyExcel.read(filePath).head(DangerChemicalInfo.class).sheet("第二次Mysql插入数据").doReadSync();
for (DangerChemicalInfo info : list) {
String properties = info.getHazardousProperties();
if (properties == null) {
continue;
}
info.setHazardCategory(processHazardCategory(properties));
info.setUnNumber(processUnNumber(properties));
info.setPackingCategory(processPackingCategory(properties));
}
EasyExcel.write("C:/Users/RENCHAO/Desktop/资料/bbr.xlsx", DangerChemicalInfo.class).sheet("Sheet1").doWrite(list);
System.out.println("完成!!!!");
}
/**
* 处理 危险类别
*/
private static String processHazardCategory(String properties) {
String category = processHazardCategory(properties, HAZARD_CATEGORY_PATTERN1);
if (category != null) {
return category;
}
return processHazardCategory(properties, HAZARD_CATEGORY_PATTERN2);
}
private static String processHazardCategory(String properties, Pattern pattern) {
int index = 0;
if (pattern == HAZARD_CATEGORY_PATTERN1) {
index = 1;
}
Matcher matcher = pattern.matcher(properties);
String category;
if (matcher.find()) {
// 后面是否又括号
String brackets = matcher.group(index + 6);
if (brackets == null) {
category = matcher.group(index);
} else {
category = matcher.group(index + 3) + "+" + brackets;
}
return category.replace("/", "+");
}
return null;
}
/**
* 处理 联合国编号
*/
private static String processUnNumber(String properties) {
Matcher matcher = UN_NUMBER_PATTERN.matcher(properties);
if (matcher.find()) {
return matcher.group(2);
}
return null;
}
/**
* 建议包装类别
*/
private static String processPackingCategory(String properties) {
Matcher matcher = PACKING_CATEGORY_PATTERN.matcher(properties);
if (matcher.find()) {
String category;
if (matcher.group(3) != null) {
category = matcher.group(3);
} else {
category = matcher.group(1);
}
if ("III".equals(category) || "3".equals(category)) {
return "Ⅲ";
}
if ("II".equals(category) || "2".equals(category)) {
return "Ⅱ";
}
if ("I".equals(category) || "1".equals(category)) {
return "Ⅰ";
}
return category;
}
return null;
}
}

@ -0,0 +1,106 @@
package com.renchao.drools;
import org.junit.Test;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class DroolsTest {
KieContainer kieContainer;
public DroolsTest() {
KieServices kieServices = KieServices.Factory.get();
// 获取Kie容器对象(默认容器对象
this.kieContainer = kieServices.newKieClasspathContainer();
kieContainer.newKieSession().dispose();
}
@Test
public void test02() {
KieSession kieSession1 = kieContainer.newKieSession();
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
kieSession1.insert(list);
System.out.println("第一个====");
kieSession1.fireAllRules();
// kieSession1.fireAllRules(match -> "book_discount_8".equals(match.getRule().getName()));
System.out.println("第二个====");
kieSession1.insert(new ArrayList<>());
kieSession1.fireAllRules();
//
// kieSession1.fireAllRules();
}
@Test
public void test03() {
KieSession kieSession = kieContainer.newKieSession();
Order order = new Order();
// order.setStringList(null);
kieSession.insert(order);
kieSession.fireAllRules();
}
@Test
public void test01() {
extracted();
extracted();
extracted();
extracted();
}
private void extracted() {
long l = System.currentTimeMillis();
KieSession kieSession = kieContainer.newKieSession();
Integer[] integers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
Set<Integer> set = new HashSet<>();
List<Integer> list = new ArrayList<>();
kieSession.insert(set);
kieSession.insert(list);
for (Integer integer : integers) {
kieSession.insert(integer);
kieSession.fireAllRules(match -> "book_discount_7".equals(match.getRule().getName()));
kieSession.fireAllRules();
}
kieSession.dispose();
System.out.println(set);
System.out.println(list);
System.out.println("耗时:" + (System.currentTimeMillis() - l));
}
@Test
public void test() {
KieSession kieSession = kieContainer.newKieSession();
Order order = new Order();
order.setOriginalPrice(60d);
// 将order对象插入工作内存
kieSession.insert(order);
String str = "我试一试的。。。。。";
kieSession.insert(str);
System.out.println("匹配规则前优惠后价格:" + order.getRealPrice());
// 匹配对象
// 激活规则,由drools框架自动进行规则匹配。若匹配成功,则执行
kieSession.fireAllRules();
// 关闭会话
kieSession.dispose();
System.out.println("优惠前价格:" + order.getOriginalPrice() + "\n优惠后价格:" + order.getRealPrice());
}
}

@ -0,0 +1,16 @@
package com.renchao.drools;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 订单
*/
@Data
public class Order {
private Double originalPrice; // 订单原始价格,即优惠前的价格
private Double realPrice; // 订单真实价格,即优惠后的价格
private List<String> stringList = new ArrayList<>();
}

@ -0,0 +1,104 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
/**
* Copyright (c) 2014, Oracle America, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Oracle nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
public class JMHSample_01_HelloWorld {
/**
* This is our first benchmark method.
*
* JMH works as follows: users annotate the methods with @Benchmark, and
* then JMH produces the generated code to run this particular benchmark as
* reliably as possible. In general one might think about @Benchmark methods
* as the benchmark "payload", the things we want to measure. The
* surrounding infrastructure is provided by the harness itself.
*
* Read the Javadoc for @Benchmark annotation for complete semantics and
* restrictions. At this point we only note that the methods names are
* non-essential, and it only matters that the methods are marked with
* @Benchmark. You can have multiple benchmark methods within the same
* class.
*
* Note: if the benchmark method never finishes, then JMH run never finishes
* as well. If you throw an exception from the method body the JMH run ends
* abruptly for this benchmark and JMH will run the next benchmark down the
* list.
*
* Although this benchmark measures "nothing" it is a good showcase for the
* overheads the infrastructure bear on the code you measure in the method.
* There are no magical infrastructures which incur no overhead, and it is
* important to know what are the infra overheads you are dealing with. You
* might find this thought unfolded in future examples by having the
* "baseline" measurements to compare against.
*/
@Benchmark
public void wellHelloThere() {
// this method was intentionally left blank.
}
/**
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You are expected to see the run with large number of iterations, and
* very large throughput numbers. You can see that as the estimate of the
* harness overheads per method call. In most of our measurements, it is
* down to several cycles per call.
*
* a) Via command-line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_01
*
* JMH generates self-contained JARs, bundling JMH together with it.
* The runtime options for the JMH are available with "-h":
* $ java -jar target/benchmarks.jar -h
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_01_HelloWorld.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,183 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* 基准测试-模式:
*
* @see Mode
*
* @author childe
* @date 2018/9/19 11:29
**/
public class JMHSample_02_BenchmarkModes {
public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(JMHSample_02_BenchmarkModes.class.getSimpleName())
.exclude(JMHSample_02_BenchmarkModes.class.getSimpleName() + ".measureAll")
.exclude(JMHSample_02_BenchmarkModes.class.getSimpleName() + ".measureMultiple")
.output("JMHSample_02_BenchmarkModes_result.sampleLog")
.forks(1)
.build();
new Runner(options).run();
}
/*
* JMH generates lots of synthetic code for the benchmarks for you during
* the benchmark compilation. JMH can measure the benchmark methods in lots
* of modes. Users may select the default benchmark mode with a special
* annotation, or select/override the mode via the runtime options.
*
* With this scenario, we start to measure something useful. Note that our
* payload code potentially throws exceptions, and we can just declare them
* to be thrown. If the code throws the actual exception, the benchmark
* execution will stop with an error.
*
* When you are puzzled with some particular behavior, it usually helps to
* look into the generated code. You might see the code is doing not
* something you intend it to do. Good experiments always follow up on the
* experimental setup, and cross-checking the generated code is an important
* part of that follow up.
*
* The generated code for this particular sample is somewhere at
* target/generated-sources/annotations/.../JMHSample_02_BenchmarkModes.java
*/
/**
* <p>Throughput: 每单位时间的操作 ops/time</p>
*
* <p>持续调用标记有{@link Benchmark}的方法,计算所有工作线程的总吞吐量。
* 这个模式基于时间,一致运行到迭代时间结束</p>
*
* Mode.Throughput, as stated in its Javadoc, measures the raw throughput by
* continuously calling the benchmark method in a time-bound iteration, and
* counting how many times we executed the method.
*
* We are using the special annotation to select the units to measure in,
* although you can use the default.
*/
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public static void measureThroughput() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(100);
}
/*
* Mode.AverageTime measures the average execution time, and it does it
* in the way similar to Mode.Throughput.
*
* Some might say it is the reciprocal throughput, and it really is.
* There are workloads where measuring times is more convenient though.
*/
/**
* <p>Average time: 每次操作的平均时间 time/op</p>
*
* <p>持续调用标记有{@link Benchmark}的方法,计算调用所有工作线程的平均时间。这个是{@link Mode#Throughput}的倒数,
* 采用不同的聚合政策。这个模式基于时间,一直运行到迭代时间结束</p>
*/
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public static void measureAverageTime() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(100);
}
/*
* Mode.SampleTime samples the execution time. With this mode, we are
* still running the method in a time-bound iteration, but instead of
* measuring the total time, we measure the time spent in *some* of
* the benchmark method calls.
*
* This allows us to infer the distributions, percentiles, etc.
*
* JMH also tries to auto-adjust sampling frequency: if the method
* is long enough, you will end up capturing all the samples.
*/
/**
* <p>Sample time: 对每次操作的时间进行采样 Sampling time</p>
*
* <p>This mode automatically adjusts the sampling
* frequency, but may omit some pauses which missed the sampling measurement. </p>
* <p>
* 持续调用标记有{@link Benchmark}的方法,随机采集调用需要的时间。这个模式自动调整采样频率,
* 但可能会忽略一些错过采样测量的暂停??(但是可能会因为测量的暂停遗漏一些采样)
* 这个模式基于时间,一直运行到迭代时间结束
* </p>
*/
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public static void measureSampleTime() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(100);
}
/*
* Mode.SingleShotTime measures the single method invocation time. As the Javadoc
* suggests, we do only the single benchmark method invocation. The iteration
* time is meaningless in this mode: as soon as benchmark method stops, the
* iteration is over.
*
* This mode is useful to do cold startup tests, when you specifically
* do not want to call the benchmark method continuously.
*/
/**
*
* <p>Single shot time: 测量单次操作的时间。</p>
* <p>
* 运行一次{@link Benchmark}的调用,并且测量它的时间。
* 这种模式对下面几种情况很有用:当你不想隐藏热身调用时来检测"冷"性能;你想看到调用的进展;你想记录每一个样本。
* 这个模式是基于工作的,仅在单次调用{@link Benchmark}时运行。
* </p>
* 这种模式的注意事项包括:
* <ul>
* <li>通常需要更多的预热/测量迭代</li>
* <li>如果基准测试很小,定时器开销可能很大;如果这是一个问题,切换到{@link Mode#SampleTime} 模式</li>
* </ul>
*/
@Benchmark
@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public static void measureSingleShotTime() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(100);
}
/*
* We can also ask for multiple benchmark modes at once. All the tests
* above can be replaced with just a single test like this:
*/
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime, Mode.SingleShotTime})
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void measureMultiple() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(100);
}
/**
* Meta-mode: all the benchmark modes.
* This is mostly useful for internal JMH testing.
*/
@Benchmark
@BenchmarkMode(Mode.All)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void measureAll() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(100);
}
}

@ -0,0 +1,85 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
/**
*
* @see Scope
*
* @author childe
* @date 2018/9/20 16:02
**/
public class JMHSample_03_States {
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_03_States.class.getSimpleName())
.output("JMHSample_03_States_result.sampleLog")
.threads(4)
.forks(1)
.build();
new Runner(opt).run();
}
/*
* 有些情况下,你需要在基准测试执行时维护一些状态。因为JHM常常用来构建并发基准测试,
* 我们选择了一个明确的state-bearing(状态支撑)对象概念。
*
* 下面是两个状态对象。他们的类名不是必需的,只是我们需要在类上标记@State。
* <b>这些对象在需要时被创建,在整个基准试验期间重复使用。</b>
*
* 这个重要的状态属性始终由其中一个基准线程实例化,然后可以访问该状态。
* 这意味着你可以初始化字段,就像在工作线程中那样(ThreadLocals是你的,等等)
*
*/
@State(Scope.Benchmark)
public static class BenchmarkState {
{
System.out.println("swwwws");
}
volatile double x = Math.PI;
}
@State(Scope.Thread)
public static class ThreadState {
{
System.out.println("ss");
}
volatile double x = Math.PI;
}
/*
* Benchmark方法可以引用这个状态,JHM在调用这些方法时会注入适当的状态。
* 你可以没有状态,或者只有一个,又或者引用多个。
* 这使你构建一个多线程的基准测试轻而易举。
*/
@Benchmark
public void measureUnshared(ThreadState state) {
// All benchmark threads will call in this method.
//
// However, since ThreadState is the Scope.Thread, each thread
// will have it's own copy of the state, and this benchmark
// will measure unshared case.
state.x++;
}
@Benchmark
public void measureShared(BenchmarkState state) {
// All benchmark threads will call in this method.
//
// Since BenchmarkState is the Scope.Benchmark, all threads
// will share the state instance, and we will end up measuring
// shared case.
state.x++;
}
}

@ -0,0 +1,37 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
/**
* 幸运的是,大多情况下你只需要一个单独的对象。
* 这种情况下,我们可以用benchmark的实例来标记@State。
* 然后,我们就可以像其他任何Java程序那样来引用他。
*
* @author childe
* @date 2018/9/20 17:08
**/
@State(Scope.Thread)
public class JMHSample_04_DefaultState {
private double x = Math.PI;
public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(JMHSample_04_DefaultState.class.getSimpleName())
.output("JMHSample_04_DefaultState_result.sampleLog")
.threads(1)
.forks(1)
.build();
new Runner(options).run();
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public void measureDefaultState() {
x++;
}
}

@ -0,0 +1,113 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
/**
* @see Level
* @see Setup
* @see TearDown
*
* @author childe
* @date 2018/10/8 11:23
**/
@State(Scope.Thread)
public class JMHSample_05_StateFixtures {
private double x;
/*
* Since @State objects are kept around during the lifetime of the
* benchmark, it helps to have the methods which do state housekeeping.
* These are usual fixture methods, you are probably familiar with them from
* JUnit and TestNG.
*
* Fixture methods make sense only on @State objects, and JMH will fail to
* compile the test otherwise.
*
* As with the State, fixture methods are only called by those benchmark
* threads which are using the state. That means you can operate in the
* thread-local context, and (not) use synchronization as if you are
* executing in the context of benchmark thread.
*
* Note: fixture methods can also work with static fields, although the
* semantics of these operations fall back out of State scope, and obey
* usual Java rules (i.e. one static field per class).
*
* 因为@State对象在benchmark的生命周期中保持不变,它可以帮助我们使用状态管理的方法。
* 这些fixture方法看起来和JUnit和TestNG很像
* @see org.openjdk.jmh.annotations.Level。
*
* fixture方法只对@State对象有意义,否则,JHM在编译时会报错。
*
* 与State一样,fixture方法仅被那些使用state的benchmark线程调用。 这意味着你可以在线程本地上下文中操作,
* 并且(不)使用同步,就像你在benchmark线程的上下文中执行一样。
*
* 注意:fixture方法也可以使用静态字段,尽管这些操作的语义从State范围中退出,并遵守通常的Java规则(即每个类一个静态字段)。
*/
/*
* Level默认每个@Benchmark前执行
* Ok, let's prepare our benchmark:
*/
@Setup
public void prepare() {
x = Math.PI;
}
/*
* Level默认每个@TearDown后执行
* And, check the benchmark went fine afterwards:
*/
@TearDown
public void check() {
assert x > Math.PI : "Nothing changed?";
}
/*
* This method obviously does the right thing, incrementing the field x
* in the benchmark state. check() will never fail this way, because
* we are always guaranteed to have at least one benchmark call.
*/
@Benchmark
public void measureRight() {
x++;
}
/*
* This method, however, will fail the check(), because we deliberately
* have the "typo", and increment only the local variable. This should
* not pass the check, and JMH will fail the run.
*/
@Benchmark
public void measureWrong() {
double x = 0;
x++;
}
/*
* java -ea //启动断言
* java -ea:pkname... //在包pkname及其子包下起用断言
* java -ea:pkname.classname //对类 pkname.classname启用断言
* 停用断言与启用设置类似
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_05_StateFixtures.class.getSimpleName())
.forks(1)
.jvmArgs("-ea")
.output("JMHSample_05_StateFixtures.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,79 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
/**
* @see Level
* @see Setup
* @see TearDown
*
* @author childe
* @date 2018/10/8 15:36
**/
@State(Scope.Thread)
public class JMHSample_06_FixtureLevel {
private double x;
/*
* Fixture methods have different levels to control when they should be run.
* There are at least three Levels available to the user. These are, from
* top to bottom:
*
* Level.Trial: before or after the entire benchmark run (the sequence of iterations)
* Level.Iteration: before or after the benchmark iteration (the sequence of invocations)
* Level.Invocation; before or after the benchmark method invocation (WARNING: read the Javadoc before using)
*
* Time spent in fixture methods does not count into the performance
* metrics, so you can use this to do some heavy-lifting.
*
* fixture方法在运行时有几个不同的等级。
* 一共有三个等级供我们使用:
*
* Level.Trial:整个基准测试(一个@Benchmark注解为一个基准测试)运行之前或之后(迭代序列)
* Level.Iteration:在基准迭代之前或之后(调用序列)
* Level.Invocation:该等级表现比较复杂 @see org.openjdk.jmh.annotations.Level
*
* fixture方法的耗时不会被统计进性能指标,所以可以用它来做一些比较重的操作。
*/
@TearDown(Level.Iteration)
public void check() {
assert x > Math.PI : "Nothing changed?";
}
@Benchmark
public void measureRight() {
x++;
}
@Benchmark
public void measureWrong() {
double x = 0;
x++;
}
/*
* java -ea //启动断言
* java -ea:pkname... //在包pkname及其子包下起用断言
* java -ea:pkname.classname //对类 pkname.classname启用断言
* 停用断言与启用设置类似
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_06_FixtureLevel.class.getSimpleName())
.forks(1)
.jvmArgs("-ea")
// switch to "true" to fail the complete run
.shouldFailOnError(false)
.output("JMHSample_06_FixtureLevel.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,137 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.*;
/**
* Fixtures have different Levels to control when they are about to run.
* Level.Invocation is useful sometimes to do some per-invocation work
* which should not count as payload (e.g. sleep for some time to emulate
* think time)
*
* 在他们运行的时候fixture有不同的等级进行控制。
* Level.Invocation一些情况下很有用,比如:每次调用前做一些工作,并且这些工作不会被统计为有效载荷。
*
* @author childe
* @date 2018/10/8 17:13
**/
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class JMHSample_07_FixtureLevelInvocation {
/*
* Fixtures have different Levels to control when they are about to run.
* Level.Invocation is useful sometimes to do some per-invocation work,
* which should not count as payload. PLEASE NOTE the timestamping and
* synchronization for Level.Invocation helpers might significantly offset
* the measurement, use with care. See Level.Invocation javadoc for further
* discussion.
*
* 在他们运行的时候fixture有不同的等级进行控制。
* Level.Invocation一些情况下很有用,比如:每次调用前做一些工作,并且这些工作不会被统计为有效载荷。
* 请注意Level.Invocation助手的时间戳和同步可能会显着抵消测量,请谨慎使用。
*
* Consider this sample:
* 看下面的例子:
*/
/*
* This state handles the executor.
* Note we create and shutdown executor with Level.Trial, so
* it is kept around the same across all iterations.
*/
@State(Scope.Benchmark)
public static class NormalState {
ExecutorService service;
@Setup(Level.Trial)
public void up() {
System.out.println("up");
service = Executors.newCachedThreadPool();
}
@TearDown(Level.Trial)
public void down() {
service.shutdown();
}
}
/*
* This is the *extension* of the basic state, which also
* has the Level.Invocation fixture method, sleeping for some time.
*
* @State 是可继承的
*/
public static class LaggingState extends NormalState {
static final int SLEEP_TIME = Integer.getInteger("sleepTime", 10);
@Setup(Level.Invocation)
public void lag() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(SLEEP_TIME);
}
}
/*
* This allows us to formulate the task: measure the task turnaround in
* "hot" mode when we are not sleeping between the submits, and "cold" mode,
* when we are sleeping.
*/
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public double measureHot(NormalState e, final Scratch s) throws ExecutionException, InterruptedException {
return e.service.submit(new Task(s)).get();
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public double measureCold(LaggingState e, final Scratch s) throws ExecutionException, InterruptedException {
return e.service.submit(new Task(s)).get();
}
/*
* This is our scratch state which will handle the work.
*/
@State(Scope.Thread)
public static class Scratch {
private double p;
private double doWork() {
p = Math.log(p);
return p;
}
}
public static class Task implements Callable<Double> {
private Scratch s;
private Task(Scratch s) {
this.s = s;
}
@Override
public Double call() {
return s.doWork();
}
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_07_FixtureLevelInvocation.class.getSimpleName())
.forks(1)
.output("JMHSample_07_FixtureLevelInvocation.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,74 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* Dead-Code Elimination (DCE)
* 死代码消除对基准测试造成的影响
*
* @author childe
* @date 2018/10/9 11:18
**/
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JMHSample_08_DeadCode {
/*
* The downfall of many benchmarks is Dead-Code Elimination (DCE): compilers
* are smart enough to deduce some computations are redundant and eliminate
* them completely. If the eliminated part was our benchmarked code, we are
* in trouble.
*
* Fortunately, JMH provides the essential infrastructure to fight this
* where appropriate: returning the result of the computation will ask JMH
* to deal with the result to limit dead-code elimination (returned results
* are implicitly consumed by Blackholes, see JMHSample_09_Blackholes).
*
* 许多基准测试的失败是死代码消除(DCE):
* 编译器非常聪明,可以推断出一些冗余的计算并完全消除它们。
* 如果被淘汰的部分是我们的基准代码,我们就遇到了麻烦。
*
* 幸运的是JMH提供了必要的基础设施,以便在适当的时候来避免这种情况:返回计算结果将要求JMH进行处理以限制死码消除(
* 返回的结果可以饮食被Blackholes消费,参见 JMHSample_09_Blackholes
* )
*/
private double x = Math.PI;
@Benchmark
public void baseline() {
// do nothing, this is a baseline
// 基准线
}
@Benchmark
public void measureWrong() {
// This is wrong: result is not used and the entire computation is optimized away.
// 这是错误的:结果没有被使用,整个计算将会被编译器优化。
Math.log(x);
}
@Benchmark
public double measureRight() {
// This is correct: the result is being used.
return Math.log(x);
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_08_DeadCode.class.getSimpleName())
.output("JMHSample_08_DeadCode.sampleLog")
.forks(1)
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,101 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* 死代码消除解决
*
* @author childe
* @date 2018/10/9 11:44
**/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class JMHSample_09_Blackholes {
/*
* Should your benchmark require returning multiple results, you have to
* consider two options (detailed below).
*
* NOTE: If you are only producing a single result, it is more readable to
* use the implicit return, as in JMHSample_08_DeadCode. Do not make your benchmark
* code less readable with explicit Blackholes!
*
* 你的基准测试是否需要返回付哦个测试结果,你需要考虑下民两个问题。
* 注意:如果你只会产生一个结果,应该使用更易读的明确return,就像JMHSample_08_DeadCode。
* 不要使用明确的Blackholes来降低您的基准代码的可读性!
*/
double x1 = Math.PI;
double x2 = Math.PI * 2;
/*
* Baseline measurement: how much single Math.sampleLog costs.
*/
@Benchmark
public double baseline() {
return Math.log(x1);
}
/*
* While the Math.sampleLog(x2) computation is intact(完好), Math.sampleLog(x1)
* is redundant and optimized out.
*/
@Benchmark
public double measureWrong() {
Math.log(x1);
return Math.log(x2);
}
/*
* This demonstrates(演示) Option A:
*
* Merge multiple results into one and return it.
* This is OK when is computation is relatively heavyweight, and merging
* the results does not offset the results much.
*
* 演示选项A:
* 合并多个结果并返回。
* 这种方式行得通,但相对较重,并且这个结果不会对结果有太大影响。
*/
@Benchmark
public double measureRight_1() {
return Math.log(x1) + Math.log(x2);
}
/*
* This demonstrates Option B:
*
* Use explicit Blackhole objects, and sink the values there.
* (Background: Blackhole is just another @State object, bundled with JMH).
*
* 演示选项B:
* 明确的使用Blackhole对象,并且把值汇入。
* (背后:Blackhole就像另一个@State对象,绑定在JMH)。
*/
@Benchmark
public void measureRight_2(Blackhole bh) {
bh.consume(Math.log(x1));
bh.consume(Math.log(x2));
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_09_Blackholes.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,84 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* DCE的常量折叠(constant-folding)
*
* @author childe
* @date 2018/10/9 14:27
**/
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JMHSample_10_ConstantFold {
/*
* The flip side of dead-code elimination is constant-folding.
*
* If JVM realizes the result of the computation is the same no matter what,
* it can cleverly optimize it. In our case, that means we can move the
* computation outside of the internal JMH loop.
*
* This can be prevented by always reading the inputs from non-final
* instance fields of @State objects, computing the result based on those
* values, and follow the rules to prevent DCE.
*
* 死代码消除的另一面是常量折叠。
* 如果JVM意识到计算结果不管怎样计算都是不变的,他便会巧妙的优化掉。
* 在我们的例子中,这意味着我们可以把计算移到JMH内部循环之外。
*
* 这可以通过始终从@State对象的non-final字段读取输入,根据这些值计算结果,并遵循规则来防止DCE。
*/
// IDEs will say "Oh, you can convert this field to local variable". Don't. Trust. Them.
// (While this is normally fine advice, it does not work in the context of measuring correctly.)
// 哈哈,竟然言中
private double x = Math.PI;
// IDEs will probably also say "Look, it could be final". Don't. Trust. Them. Either.
// (While this is normally fine advice, it does not work in the context of measuring correctly.)
// 哈哈,原因说错了
private final double wrongX = Math.PI;
@Benchmark
public double baseline() {
// simply return the value, this is a baseline
return Math.PI;
}
@Benchmark
public double measureWrong_1() {
// This is wrong: the source is predictable, and computation is foldable.
return Math.log(Math.PI);
}
@Benchmark
public double measureWrong_2() {
// This is wrong: the source is predictable, and computation is foldable.
return Math.log(wrongX);
}
@Benchmark
public double measureRight() {
// This is correct: the source is not predictable.
return Math.log(x);
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_10_ConstantFold.class.getSimpleName())
.forks(1)
.output("JMHSample_10_ConstantFold.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,134 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* 基准测试方法中进行循环是坏做法
*
* @author childe
* @date 2018/10/9 15:51
**/
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JMHSample_11_Loops {
/*
* It would be tempting for users to do loops within the benchmarked method.
* (This is the bad thing Caliper taught everyone). These tests explain why
* this is a bad idea.
*
* Looping is done in the hope of minimizing the overhead of calling the
* test method, by doing the operations inside the loop instead of inside
* the method call. Don't buy this argument; you will see there is more
* magic happening when we allow optimizers to merge the loop iterations.
*
* 在基准测试方法中做循环是很诱人的。这是Caliper教给大家的坏事情。以下测试告诉你原因。
*
* 循环是为了通过在循环内而不是在方法调用内部进行操作来最小化调用测试方法的开销。???
* 不要买这个论点;当我们允许优化器合并循环迭代时,你会发现有更多的魔法发生。
*/
/*
* Suppose we want to measure how much it takes to sum two integers:
*/
int x = 1;
int y = 2;
/*
* This is what you do with JMH.
*/
@Benchmark
public int measureRight() {
return (x + y);
}
/*
* The following tests emulate the naive looping.
* This is the Caliper-style benchmark.
*
* Caliper风格的基准测试
*/
private int reps(int reps) {
int s = 0;
for (int i = 0; i < reps; i++) {
s += (x + y);
}
return s;
}
/*
* We would like to measure this with different repetitions count.
* Special annotation is used to get the individual operation cost.
*/
@Benchmark
@OperationsPerInvocation(1)
public int measureWrong_1() {
return reps(1);
}
@Benchmark
@OperationsPerInvocation(10)
public int measureWrong_10() {
return reps(10);
}
@Benchmark
@OperationsPerInvocation(100)
public int measureWrong_100() {
return reps(100);
}
@Benchmark
@OperationsPerInvocation(1000)
public int measureWrong_1000() {
return reps(1000);
}
@Benchmark
@OperationsPerInvocation(10000)
public int measureWrong_10000() {
return reps(10000);
}
@Benchmark
@OperationsPerInvocation(100000)
public int measureWrong_100000() {
return reps(100000);
}
/*
* You might notice the larger the repetitions count, the lower the "perceived"
* cost of the operation being measured. Up to the point we do each addition with 1/20 ns,
* well beyond what hardware can actually do.
*
* This happens because the loop is heavily unrolled/pipelined, and the operation
* to be measured is hoisted from the loop. Morale: don't overuse loops, rely on JMH
* to get the measurement right.
*
* 你可能已经注意到,循环次数阅读,统计出来的时间越短。到目前位置,每次操作的时间已经是1/20 ns,远远超过硬件的实际能力。
*
* 发生这种情况是因为循环被大量unrolled/pipelined,并且要从循环中提升要测量的操作。规则:不要过度使用循环,依靠JMH来正确测量。
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_11_Loops.class.getSimpleName())
.forks(1)
.output("JMHSample_11_Loops.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,162 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* Use non-forked runs only for debugging purposes, not for actual performance runs
* 使用non-forked运行仅用于调试目的,而不是用于实际性能运行
*
* @author childe
* @date 2018/10/9 17:17
**/
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JMHSample_12_Forking {
/*
* JVMs are notoriously good at profile-guided optimizations. This is bad
* for benchmarks, because different tests can mix their profiles together,
* and then render the "uniformly bad" code for every test. Forking (running
* in a separate process) each test can help to evade this issue.
*
* JMH will fork the tests by default.
*
* JVM擅长profile-guided优化。但这对基准不友好,因为不同的测试可以把他们的profiles混合在一起,
* 然后为每一个测试渲染"uniformly bad"代码???。forking(运行在不同的进程)每个测试可以避免这个问题。
*
* JMH默认fork所有test。
*/
/*
* Suppose we have this simple counter interface, and two implementations.
* Even though those are semantically the same, from the JVM standpoint,
* those are distinct classes.
*
* 假设我们又一个简单的统计接受,并且有两个实现。
* 即使他们逻辑相同,站在JVM角度看他们是不同的类。
*/
public interface Counter {
int inc();
}
public class Counter1 implements Counter {
private int x;
@Override
public int inc() {
return x++;
}
}
public class Counter2 implements Counter {
private int x;
@Override
public int inc() {
return x++;
}
}
/*
* And this is how we measure it.
* Note this is susceptible for same issue with loops we mention in previous examples.
*/
public int measure(Counter c) {
int s = 0;
for (int i = 0; i < 10; i++) {
s += c.inc();
}
return s;
}
/*
* These are two counters.
*/
Counter c1 = new Counter1();
Counter c2 = new Counter2();
/*
* We first measure the Counter1 alone...
* Fork(0) helps to run in the same JVM.
*/
@Benchmark
@Fork(0)
public int measure_1_c1() {
return measure(c1);
}
/*
* Then Counter2...
*/
@Benchmark
@Fork(0)
public int measure_2_c2() {
return measure(c2);
}
/*
* Then Counter1 again...
*/
@Benchmark
@Fork(0)
public int measure_3_c1_again() {
return measure(c1);
}
/*
* These two tests have explicit @Fork annotation.
* JMH takes this annotation as the request to run the test in the forked JVM.
* It's even simpler to force this behavior for all the tests via the command
* line option "-f". The forking is default, but we still use the annotation
* for the consistency.
*
* This is the test for Counter1.
*/
@Benchmark
@Fork(1)
public int measure_4_forked_c1() {
return measure(c1);
}
/*
* ...and this is the test for Counter2.
*/
@Benchmark
@Fork(1)
public int measure_5_forked_c2() {
return measure(c2);
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* Note that C1 is faster, C2 is slower, but the C1 is slow again! This is because
* the profiles for C1 and C2 had merged together.
*
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_12_Forking.class.getSimpleName())
.output("JMHSample_12_Forking.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,118 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* run-to-run variance
* 运行间差异
*
* @author childe
* @date 2018/10/10 09:16
**/
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class JMHSample_13_RunToRun {
/*
* Forking also allows to estimate run-to-run variance.
*
* JVMs are complex systems, and the non-determinism is inherent for them.
* This requires us to always account the run-to-run variance as the one
* of the effects in our experiments.
*
* Luckily, forking aggregates the results across several JVM launches.
*
* fork还允许估计运行间的差异。
* JVM是复杂的系统,非决定论是他们固有的。
* 这要求我们始终将运行间差异视为我们实验中的影响之一。
*
* 幸运的是,fork聚合了多个JVM启动的结果。
*/
/*
* In order to introduce readily measurable run-to-run variance, we build
* the workload which performance differs from run to run. Note that many workloads
* will have the similar behavior, but we do that artificially to make a point.
*
* 为了引入易于测量的逐次运行差异,我们构建了性能因运行而异的工作负载。
* 许多工作负载都会有类似的行为,但我们会人为地指出这一点。
*/
@State(Scope.Thread)
public static class SleepyState {
public long sleepTime;
@Setup
public void setup() {
// 每次fork都会执行,fork相当于重复多次某个被标记@Benchmark注解的方法,但它会合并结果。
sleepTime = (long) (Math.random() * 1000);
}
}
/*
* Now, we will run this different number of times.
*/
@Benchmark
@Fork(1)
public void baseline(SleepyState s) throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(s.sleepTime);
}
@Benchmark
@Fork(5)
public void fork_1(SleepyState s) throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(s.sleepTime);
}
@Benchmark
@Fork(20)
public void fork_2(SleepyState s) throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(s.sleepTime);
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* Note the baseline is random within [0..1000] msec; and both forked runs
* are estimating the average 500 msec with some confidence.
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_13 -wi 0 -i 3
* (we requested no warmup, 3 measurement iterations; there are also other options, see -h)
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_13_RunToRun.class.getSimpleName())
.warmupIterations(0)
.measurementIterations(3)
.output("JMHSample_13_RunToRun.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,122 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Group;
import org.openjdk.jmh.annotations.GroupThreads;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 非对称分组执行
*
* 例子场景:生产者/消费者
*
* @author childe
* @date 2018/10/10 10:40
**/
@State(Scope.Group)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JMHSample_15_Asymmetric {
/*
* So far all the tests were symmetric: the same code was executed in all the threads.
* At times, you need the asymmetric test. JMH provides this with the notion of @Group,
* which can bind several methods together, and all the threads are distributed among
* the test methods.
*
* Each execution group contains of one or more threads. Each thread within a particular
* execution group executes one of @Group-annotated @Benchmark methods. Multiple execution
* groups may participate in the run. The total thread count in the run is rounded to the
* execution group size, which will only allow the full execution groups.
*
* Note that two state scopes: Scope.Benchmark and Scope.Thread are not covering all
* the use cases here -- you either share everything in the state, or share nothing.
* To break this, we have the middle ground Scope.Group, which marks the state to be
* shared within the execution group, but not among the execution groups.
*
* Putting this all together, the example below means:
* a) define the execution group "g", with 3 threads executing inc(), and 1 thread
* executing get(), 4 threads per group in total;
* b) if we run this test case with 4 threads, then we will have a single execution
* group. Generally, running with 4*N threads will create N execution groups, etc.;
* c) each execution group has one @State instance to share: that is, execution groups
* share the counter within the group, but not across the groups.
*
* 到目前位置,我们的测试都是对称的:所有的线程都运行相同的代码。
* 是时候了解非对称测试了。JMH提供了@Group注解来把几个方法绑定到一起,所有线程都分布在测试方法中。
*
* 每个执行组包含一个或者多个线程。特定执行组中的每个线程执行@Group-annotated @Benchmark方法之一。
* 多个执行组可以参与运行。运行中的总线程数将四舍五入为执行组大小,这将只允许完整的执行组。???
*
* 注意那两个状态的作用域:Scope.Benchmark 和 Scope.Thread没有在这个用例中覆盖 -- 你要么在状态中共享每个东西,要么不共享。
* 我们使用Scope.Group状态用来表明在执行组内共享,而不在组间共享。
*
* 以下事例含义:
* a)定义执行组"g",它有3个线程来执行inc(),1个线程来执行get(),每个分组共有4个线程;
* b)如果我们用4个线程来运行这个测试用例,我们将会有一个单独的执行组。通常,用4*N个线程来创建N个执行组。
* c)每个执行组内共享一个@State实例:也就是执行组内共享counter,而不是跨组共享。
*/
private AtomicInteger counter;
@Setup
public void up() {
counter = new AtomicInteger();
}
@Benchmark
@Group("g")
@GroupThreads(3)
public int inc() {
return counter.incrementAndGet();
}
@Benchmark
@Group("g")
@GroupThreads(1)
public int get() {
return counter.get();
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You will have the distinct metrics for inc() and get() from this run.
*
* 在此次运行中我们讲分别获得inc()和get()的指标。
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_15 -f 1
* (we requested single fork; there are also other options, see -h)
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_15_Asymmetric.class.getSimpleName())
.forks(1)
.output("JMHSample_15_Asymmetric.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,161 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* Method Inlining
* 方法内敛,是JVM对代码的编译优化,常见的编译优化 @see http://www.importnew.com/2009.html
*
* 官方白皮书
* From http://www.oracle.com/technetwork/java/whitepaper-135217.html#method:
*
* The frequency of virtual method invocations in the Java programming language is an important optimization bottleneck.
* Once the Java HotSpot adaptive optimizer has gathered information during execution about program hot spots,
* it not only compiles the hot spot into native code, but also performs extensive method inlining on that code.
*
* Inlining has important benefits.
* It dramatically reduces the dynamic frequency of method invocations, which saves the time needed to perform those method invocations.
* But even more importantly, inlining produces much larger blocks of code for the optimizer to work on.
* This creates a situation that significantly increases the effectiveness of traditional compiler optimizations,
* overcoming a major obstacle to increased Java programming language performance.
*
* Inlining is synergistic with other code optimizations, because it makes them more effective.
* As the Java HotSpot compiler matures, the ability to operate on large, inlined blocks of code will open the door to a host of even more advanced optimizations in the future.
*
* Java编程语言中虚拟方法调用的频率是一个重要的优化瓶颈。
* 一旦Java HotSpot自适应优化器在执行期间收集有关程序热点的信息,它不仅将热点编译为本机代码,而且还对该代码执行大量方法内联。
*
* 内联具有重要的好处。
* 它大大降低了方法调用的动态频率,从而节省了执行这些方法调用所需的时间。
* 但更重要的是,内联会为优化程序生成更大的代码块。
* 这创造了一种能够显着提高传统编译器优化效率的情况,克服了增加Java编程语言性能的主要障碍。
*
* 内联与其他代码优化具有协同作用,因为它使它们更有效。
* 随着Java HotSpot编译器的成熟,对大型内联代码块进行操作的能力将为未来的一系列更高级的优化打开大门。
*
* @author childe
* @date 2018/10/10 14:16
**/
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JMHSample_16_CompilerControl {
/*
* We can use HotSpot-specific functionality to tell the compiler what
* do we want to do with particular methods. To demonstrate the effects,
* we end up with 3 methods in this sample.
*
* 我们使用HotSpot特定的功能来告诉编译器我们想对特定的方法做怎么。
* 为了证明这种效果,我们在这个例子中写了三个测试方法。
*/
/**
* These are our targets:
* - first method is prohibited from inlining
* - second method is forced to inline
* - third method is prohibited from compiling
*
* We might even place the annotations directly to the benchmarked
* methods, but this expresses the intent more clearly.
*
* 这是我们的目标:
* - 第一个方法禁止内敛
* - 第二个方法强制内敛
* - 第三个方法禁止编译
*
* 我们甚至可以将注释直接放在基准测试方法中,但这更清楚地表达了意图。
*/
public void target_blank() {
// this method was intentionally left blank
// 方法故意留空
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public void target_dontInline() {
// this method was intentionally left blank
}
@CompilerControl(CompilerControl.Mode.INLINE)
public void target_inline() {
// this method was intentionally left blank
}
/**
* Exclude the method from the compilation.
*/
@CompilerControl(CompilerControl.Mode.EXCLUDE)
public void target_exclude() {
// this method was intentionally left blank
}
/*
* These method measures the calls performance.
*
* 这些方法来测量调用性能。
*/
@Benchmark
public void baseline() {
// this method was intentionally left blank
}
@Benchmark
public void blank() {
target_blank();
}
@Benchmark
public void dontinline() {
target_dontInline();
}
@Benchmark
public void inline() {
target_inline();
}
@Benchmark
public void exclude() {
target_exclude();
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* Note the performance of the baseline, blank, and inline methods are the same.
* dontinline differs a bit, because we are making the proper call.
* exclude is severely slower, becase we are not compiling it at all.
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_16 -wi 0 -i 3 -f 1
* (we requested no warmup iterations, 3 iterations, single fork; there are also other options, see -h)
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_16_CompilerControl.class.getSimpleName())
.warmupIterations(0)
.measurementIterations(3)
.forks(1)
.output("JMHSample_16_CompilerControl.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,117 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;
import java.util.concurrent.TimeUnit;
/**
* desc
*
* @author childe
* @date 2018/10/11 17:01
**/
@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class JMHSample_17_SyncIterations {
/*
* This is the another thing that is enabled in JMH by default.
*
* Suppose we have this simple benchmark.
*/
private double src;
@Benchmark
public double test() {
double s = src;
for (int i = 0; i < 1000; i++) {
s = Math.sin(s);
}
return s;
}
/*
* It turns out if you run the benchmark with multiple threads,
* the way you start and stop the worker threads seriously affects
* performance.
*
* The natural way would be to park all the threads on some sort
* of barrier, and the let them go "at once". However, that does
* not work: there are no guarantees the worker threads will start
* at the same time, meaning other worker threads are working
* in better conditions, skewing the result.
*
* The better solution would be to introduce bogus iterations,
* ramp up the threads executing the iterations, and then atomically
* shift the system to measuring stuff. The same thing can be done
* during the rampdown. This sounds complicated, but JMH already
* handles that for you.
*
* 事实证明如果你用多线程来跑benchmark,你启动和停止工作线程的方式会严重影响性能。
*
* 通常的做法是,让所有的线程都挂起在一些有序的屏障上,然后让他们一起开始。
* 然而,这种做法是不奏效的:没有谁能够保证工作线程在同一时间开始,这就意味着其他工作线程在更好的条件下运行,从而扭曲了结果。
*
* 更好的解决方案是引入虚假迭代,增加执行迭代的线程,然后将系统原子地转换为测量内容。在减速期间可以做同样的事情。
* 这听起来很复杂,但是JMH已经帮你处理好了。
*/
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You will need to oversubscribe the system to make this effect
* clearly visible; however, this effect can also be shown on the
* unsaturated systems.*
*
* 您需要超额预订系统才能使此效果清晰可见;然而,这种效应也可以在不饱和系统上显示出来。
*
* Note the performance of -si false version is more flaky, even
* though it is "better". This is the false improvement, granted by
* some of the threads executing in solo. The -si true version more stable
* and coherent.
*
* -si false版本的性能更加不稳定,即使它“更好”。 -si true版本更稳定,更连贯。
*
* -si true is enabled by default.
*
* Say, $CPU is the number of CPUs on your machine.
*
* You can run this test with:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_17 \
* -w 1s -r 1s -f 1 -t ${CPU*16} -si {true|false}
* (we requested shorter warmup/measurement iterations, single fork,
* lots of threads, and changeable "synchronize iterations" option)
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_17_SyncIterations.class.getSimpleName())
.warmupTime(TimeValue.seconds(1))
.measurementTime(TimeValue.seconds(1))
.threads(Runtime.getRuntime().availableProcessors()*16)
.forks(1)
// try to switch to "false", default is true
.syncIterations(false)
.output("JMHSample_17_SyncIterations_false.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,91 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Group;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Control;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 同benchmark交互
* @see Control
* @author childe
* @date 2018/10/12 15:12
**/
@State(Scope.Group)
public class JMHSample_18_Control {
/*
* Sometimes you need the tap into the harness mind to get the info
* on the transition change. For this, we have the experimental state object,
* Control, which is updated by JMH as we go.
*
* 有时您需要点击线束头脑以获取有关转换更改的信息。为此,我们有实验状态对象Control,它由JMH随时更新。
*
* WARNING: The API for Contro class is considered unstable, and can be changed without notice.
*/
/*
* In this example, we want to estimate the ping-pong speed for the simple
* AtomicBoolean. Unfortunately, doing that in naive manner will livelock
* one of the threads, because the executions of ping/pong are not paired
* perfectly. We need the escape hatch to terminate the loop if threads
* are about to leave the measurement.
*
* 在这个例子中,我们想要估计简单的AtomicBoolean的ping / pong速度。
* 不幸的是,以天真的方式执行此操作将会锁定其中一个线程,因为ping / pong的执行不能完美配对。
* 如果线程即将离开测量,我们需要"逃生舱口"来终止循环。
*/
public final AtomicBoolean flag = new AtomicBoolean();
@Benchmark
@Group("pingpong")
public void ping(Control cnt) {
// 业务在迭代时间未结束前,一直运行。
while (!cnt.stopMeasurement && !flag.compareAndSet(false, true)) {
// this body is intentionally left blank
}
}
@Benchmark
@Group("pingpong")
public void pong(Control cnt) {
while (!cnt.stopMeasurement && !flag.compareAndSet(true, false)) {
// this body is intentionally left blank
}
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_18 -t 2 -f 1
* (we requested 2 threads and single fork; there are also other options, see -h)
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_18_Control.class.getSimpleName())
.threads(2)
.forks(1)
.output("JMHSample_18_Control.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,79 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* 基于注解的基准测试
*
* @author childe
* @date 2018/10/12 16:28
**/
@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(1)
public class JMHSample_20_Annotations {
double x1 = Math.PI;
/*
* In addition to all the command line options usable at run time,
* we have the annotations which can provide the reasonable defaults
* for the some of the benchmarks. This is very useful when you are
* dealing with lots of benchmarks, and some of them require
* special treatment.
*
* Annotation can also be placed on class, to have the effect over
* all the benchmark methods in the same class. The rule is, the
* annotation in the closest scope takes the precedence: i.e.
* the method-based annotation overrides class-based annotation,
* etc.
*
* 除了运行时所有的命令行选项外,我们还可以通过注解给一些基准测试提供默认值。
* 在你处理大量基准测试时这个很有用,其中一些需要特别处理。
*
* 注解可以放在class上,来影响这个class中所有的基准测试方法。规则是,靠近作用域的注解有优先权:
* 比如,方法上的注解可以覆盖类上的注解。
*
* 注意:命令行优先级最高。
*/
@Benchmark
@Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS)
public double measure() {
return Math.log(x1);
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* Note JMH honors the default annotation settings. You can always override
* the defaults via the command line or API.
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_20
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_20_Annotations.class.getSimpleName())
.output("JMHSample_20_Annotations.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,139 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* Consume some amount of time tokens.
*
* 消费一些时间令牌。
*
* 有时测试消耗一定CPU周期。通过静态的BlackHole.consumeCPU(tokens)方法来实现。
* Token是一些CPU指令。这样编写方法代码就可以达到运行时间依赖于该参数的目的(不被任何JIT/CPU优化)。
*
* @author childe
* @date 2018/10/13 15:03
**/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JMHSample_21_ConsumeCPU {
/*
* At times you require the test to burn some of the cycles doing nothing.
* In many cases, you *do* want to burn the cycles instead of waiting.
*
* For these occasions, we have the infrastructure support. Blackholes
* can not only consume the values, but also the time! Run this test
* to get familiar with this part of JMH.
*
* (Note we use static method because most of the use cases are deep
* within the testing code, and propagating blackholes is tedious).
*
* 有时你需要测试来烧掉一些无所事事的循环。
* 在许多情况下,你想要燃烧周期而不是等待。
*
* 在这些场合,我们有基础设施支持。
* Blackhole不仅可以消耗数值,还可以消耗时间!运行此测试以熟悉JMH的这一部分。
*
* (注意:我们使用静态方法,因为大多数用例都在测试代码的深处,传播Blackhole很繁琐)
*/
@Benchmark
public void consume_0000() {
Blackhole.consumeCPU(0);
}
@Benchmark
public void consume_0001() {
Blackhole.consumeCPU(1);
}
@Benchmark
public void consume_0002() {
Blackhole.consumeCPU(2);
}
@Benchmark
public void consume_0004() {
Blackhole.consumeCPU(4);
}
@Benchmark
public void consume_0008() {
Blackhole.consumeCPU(8);
}
@Benchmark
public void consume_0016() {
Blackhole.consumeCPU(16);
}
@Benchmark
public void consume_0032() {
Blackhole.consumeCPU(32);
}
@Benchmark
public void consume_0064() {
Blackhole.consumeCPU(64);
}
@Benchmark
public void consume_0128() {
Blackhole.consumeCPU(128);
}
@Benchmark
public void consume_0256() {
Blackhole.consumeCPU(256);
}
@Benchmark
public void consume_0512() {
Blackhole.consumeCPU(512);
}
@Benchmark
public void consume_1024() {
Blackhole.consumeCPU(1024);
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* Note the single token is just a few cycles, and the more tokens
* you request, then more work is spent (almost linearly)
*
* 注意:单个token只有几个周期,请求的令牌越多则消耗更多的工作(近乎线性)
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_21 -f 1
* (we requested single fork; there are also other options, see -h)
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_21_ConsumeCPU.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,104 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* AuxCounters是实验类,慎用!未来可能删除。
* CAVEAT: THIS IS AN EXPERIMENTAL API, it may be changed or removed in future without prior warning.
*
* 所以不深入了解了。大意是作为@State的辅助类,可以分别统计操作对象的每个字段值。
* @author childe
* @date 2018/10/16 15:20
**/
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
public class JMHSample_23_AuxCounters {
/*
* In some weird cases you need to get the separate throughput/time
* metrics for the benchmarked code depending on the outcome of the
* current code. Trying to accommodate the cases like this, JMH optionally
* provides the special annotation which treats @State objects
* as the object bearing user counters. See @AuxCounters javadoc for
* the limitations.
*/
@State(Scope.Thread)
@AuxCounters(AuxCounters.Type.OPERATIONS)
public static class OpCounters {
// These fields would be counted as metrics
public int case1;
public int case2;
// This accessor will also produce a metric
public int total() {
return case1 + case2;
}
}
@State(Scope.Thread)
// 计算“事件”,即工作量生命周期中的一次性事件。此计数器不会按时间标准化。
@AuxCounters(AuxCounters.Type.EVENTS)
public static class EventCounters {
// This field would be counted as metric
public int wows;
}
/*
* This code measures the "throughput" in two parts of the branch.
* The @AuxCounters state above holds the counters which we increment
* ourselves, and then let JMH to use their values in the performance
* calculations.
*/
@Benchmark
public void splitBranch(OpCounters counters) {
if (Math.random() < 0.1) {
counters.case1++;
} else {
counters.case2++;
}
}
@Benchmark
public void runSETI(EventCounters counters) {
float random = (float) Math.random();
float wowSignal = (float) Math.PI / 4;
if (random == wowSignal) {
// WOW, that's unusual.
counters.wows++;
}
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_23
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_23_AuxCounters.class.getSimpleName())
.output("JMHSample_23_AuxCounters.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,109 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* desc
*
* @author childe
* @date 2018/10/16 17:17
**/
public class JMHSample_24_Inheritance {
/*
* In very special circumstances, you might want to provide the benchmark
* body in the (abstract) superclass, and specialize it with the concrete
* pieces in the subclasses.
*
* The rule of thumb is: if some class has @Benchmark method, then all the subclasses
* are also having the "synthetic" @Benchmark method. The caveat is, because we only
* know the type hierarchy during the compilation, it is only possible during
* the same compilation session. That is, mixing in the subclass extending your
* benchmark class *after* the JMH compilation would have no effect.
*
* Note how annotations now have two possible places. The closest annotation
* in the hierarchy wins.
*
* 我们可以使用模版模式通过抽象方法来分离实现。
* 经验法则是:如果一些类有@Benchmark方法,那么它所有的子类都继承@Benchmark方法。
* 注意,因为我们只知道编译期间的类型层次结构,所以只能在同一个编译会话期间使用。
* 也就是说,在JMH编译之后混合扩展benchmark类的子类将不起作用。
*
* 注释现在有两个可能的地方。靠近的注视将被采纳。
*/
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public static abstract class AbstractBenchmark {
int x;
@Setup
public void setup() {
x = 42;
}
@Benchmark
@Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS)
public double bench() {
return doWork() * doWork();
}
protected abstract double doWork();
}
@BenchmarkMode(Mode.Throughput)
public static class BenchmarkLog extends AbstractBenchmark {
@Override
protected double doWork() {
return Math.log(x);
}
}
public static class BenchmarkSin extends AbstractBenchmark {
@Override
protected double doWork() {
return Math.sin(x);
}
}
public static class BenchmarkCos extends AbstractBenchmark {
@Override
protected double doWork() {
return Math.cos(x);
}
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You can run this test, and observe the three distinct benchmarks running the squares
* of Math.sampleLog, Math.sin, and Math.cos, accordingly.
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_24
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_24_Inheritance.class.getSimpleName())
.output("JMHSample_24_Inheritance.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,121 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.LinkedList;
import java.util.List;
/**
* desc
*
* @author childe
* @date 2018/10/17 10:05
**/
@State(Scope.Thread)
public class JMHSample_26_BatchSize {
/*
* Sometimes you need to evaluate operation which doesn't have
* the steady state. The cost of a benchmarked operation may
* significantly vary from invocation to invocation.
*
* In this case, using the timed measurements is not a good idea,
* and the only acceptable benchmark mode is a single shot. On the
* other hand, the operation may be too small for reliable single
* shot measurement.
*
* We can use "batch size" parameter to describe the number of
* benchmark calls to do per one invocation without looping the method
* manually and protect from problems described in JMHSample_11_Loops.
*
* 有时你需要评估没有稳定状态的操作。基准操作的成本可能因调用而异。
*
* 这种情况下,使用时间检测不是一个好主意,唯一可以接受的基准模型是single shot(单次测量)。
* 另一方面,对于可靠的单次测量,操作可能太小。
*
* 我们可以使用“batch size”参数来描述每次调用执行的基准调用次数,而无需手动循环方法并防止JMHSample_11_Loops中描述的问题。
*/
/*
* Suppose we want to measure insertion in the middle of the list.
*/
List<String> list = new LinkedList<>();
@Benchmark
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@BenchmarkMode(Mode.AverageTime)
public List<String> measureWrong_1() {
list.add(list.size() / 2, "something");
return list;
}
@Benchmark
@Warmup(iterations = 5, time = 5)
@Measurement(iterations = 5, time = 5)
@BenchmarkMode(Mode.AverageTime)
public List<String> measureWrong_5() {
list.add(list.size() / 2, "something");
return list;
}
/*
* This is what you do with JMH.
*/
@Benchmark
@Warmup(iterations = 5, batchSize = 5000)
@Measurement(iterations = 5, batchSize = 5000)
@BenchmarkMode(Mode.SingleShotTime)
public List<String> measureRight() {
list.add(list.size() / 2, "something");
return list;
}
@Setup(Level.Iteration)
public void setup(){
list.clear();
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You can see completely different results for measureWrong_1 and measureWrong_5; this
* is because the workload has no steady state. The result of the workload is dependent
* on the measurement time. measureRight does not have this drawback, because it measures
* the N invocations of the test method and measures it's time.
*
* We measure batch of 5000 invocations and consider the batch as the single operation.
*
* 您可以看到measureWrong_1和measureWrong_5的完全不同的结果;这是因为工作负载没有稳定状态。工作量的结果取决于测量时间。
* measureRight没有这个缺点,因为它测量测试方法的N次调用并测量它的时间。
*
* 我们测量5000次调用批次并将批次视为单个操作。
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_26 -f 1
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_26_BatchSize.class.getSimpleName())
.forks(1)
.output("JMHSample_26_BatchSize.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,76 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.math.BigInteger;
import java.util.concurrent.TimeUnit;
/**
* desc
*
* @author childe
* @date 2018/10/17 14:30
**/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@State(Scope.Benchmark)
public class JMHSample_27_Params {
/**
* In many cases, the experiments require walking the configuration space
* for a benchmark. This is needed for additional control, or investigating
* how the workload performance changes with different settings.
*
* 大多情况下,一个基准需要跑在不同的配置下。这需要额外的控制,或者调查工作性能如何随着不同设定改变。
*
* 如下每个arg参数会完全组合一遍certainty。
*/
@Param({"1", "31", "65", "101", "103"})
public int arg;
@Param({"0", "1", "2", "4", "8", "16", "32"})
public int certainty;
@Benchmark
public boolean bench() {
// 该计算的时常随着certainty的变大而变长
return BigInteger.valueOf(arg).isProbablePrime(certainty);
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* Note the performance is different with different parameters.
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_27
*
* You can juggle parameters through the command line, e.g. with "-p arg=41,42"
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_27_Params.class.getSimpleName())
// .param("arg", "41", "42") // Use this to selectively constrain/override parameters
.output("JMHSample_27_Params.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,126 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* desc
*
* @author childe
* @date 2018/10/17 15:14
**/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@State(Scope.Thread)
public class JMHSample_28_BlackholeHelpers {
/**
* Sometimes you need the black hole not in @Benchmark method, but in
* helper methods, because you want to pass it through to the concrete
* implementation which is instantiated in helper methods. In this case,
* you can request the black hole straight in the helper method signature.
* This applies to both @Setup and @TearDown methods, and also to other
* JMH infrastructure objects, like Control.
*
* Below is the variant of {@link com.cxd.benchmark.JMHSample_08_DeadCode}
* test, but wrapped in the anonymous classes.
*
* 有时你不需要Blackhole在@Benchmark方法中,而是在helper方法中,因为你想把它传递给在helper方法中的具体实现的实例。
* 在这种清凉夏,你可以通过helper方法签名获得Blackhole。
* 这可以应用在被标注为@Setup和@TearDown的方法上,也包括其他JMH脚手架对象,比如Control。
*
* {@link com.cxd.benchmark.JMHSample_08_DeadCode}是它的变种,但是他被包装在匿名类中。
*/
public interface Worker {
void work();
}
private Worker workerBaseline;
private Worker workerRight;
private Worker workerWrong;
@Setup
public void setup(final Blackhole bh) {
workerBaseline = new Worker() {
double x;
@Override
public void work() {
// do nothing
}
};
workerWrong = new Worker() {
double x;
@Override
public void work() {
Math.log(x);
}
};
workerRight = new Worker() {
double x;
@Override
public void work() {
bh.consume(Math.log(x));
}
};
}
@Benchmark
public void baseline() {
workerBaseline.work();
}
@Benchmark
public void measureWrong() {
workerWrong.work();
}
@Benchmark
public void measureRight() {
workerRight.work();
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You will see measureWrong() running on-par with baseline().
* Both measureRight() are measuring twice the baseline, so the logs are intact.
*
* measureWrong()的测量结果和baseline()很相近。
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_28
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_28_BlackholeHelpers.class.getSimpleName())
.output("JMHSample_28_BlackholeHelpers.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,106 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* desc
*
* @author childe
* @date 2018/10/17 15:31
**/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Group)
@Timeout(time = 10)
public class JMHSample_30_Interrupts {
/*
* JMH can also detect when threads are stuck in the benchmarks, and try
* to forcefully interrupt the benchmark thread. JMH tries to do that
* when it is arguably sure it would not affect the measurement.
*
* JMH还可以检测线程何时卡在基准测试中,并尝试强制中断基准线程。
* 在可以确定它不会影响测量时,JMH会尝试这样做。
*/
/*
* In this example, we want to measure the simple performance characteristics
* of the ArrayBlockingQueue. Unfortunately, doing that without a harness
* support will deadlock one of the threads, because the executions of
* take/put are not paired perfectly. Fortunately for us, both methods react
* to interrupts well, and therefore we can rely on JMH to terminate the
* measurement for us. JMH will notify users about the interrupt actions
* nevertheless, so users can see if those interrupts affected the measurement.
* JMH will start issuing interrupts after the default or user-specified timeout
* had been reached.
*
* This is a variant of org.openjdk.jmh.samples.JMHSample_18_Control, but without
* the explicit control objects. This example is suitable for the methods which
* react to interrupts gracefully.
*
* 在这个例子中,我们想测量ArrayBlockingQueue的简单性能特征。
* 不幸的是,在没有工具支持的情况下执行此操作会使其中一个线程死锁,因为take / put的执行不能完美配对。
* 幸运的是,这两种方法都能很好地应对中断,因此我们可以依赖JMH来中断测量。
* JMH将通知用户有关中断操作的信息,因此用户可以查看这些中断是否会影响测量。
* 在达到默认或用户指定的超时后,JMH将开始发出中断。
*
* 这是 {@link com.cxd.benchmark.JMHSample_18_Control}的一个变种,但是没有明确的控制对象。
* 这个例子很适合那些需要优雅应对中断的方法。
*/
private BlockingQueue<Integer> q;
@Setup
public void setup() {
q = new ArrayBlockingQueue<>(1);
}
@Group("Q")
@Benchmark
public Integer take() throws InterruptedException {
return q.take();
}
@Group("Q")
@Benchmark
public void put() throws InterruptedException {
q.put(42);
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_30 -t 2 -f 5 -to 10
* (we requested 2 threads, 5 forks, and 10 sec timeout)
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_30_Interrupts.class.getSimpleName())
.threads(2)
.forks(5)
// .timeout(TimeValue.seconds(10))
.output("JMHSample_30_Interrupts_annotation.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,124 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.ThreadParams;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* 通过JMH提供的脚手架获取JMH的一些运行信息
*
* @author childe
* @date 2018/10/17 16:35
**/
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class JMHSample_31_InfraParams {
/*
* There is a way to query JMH about the current running mode. This is
* possible with three infrastructure objects we can request to be injected:
* - BenchmarkParams: covers the benchmark-global configuration
* - IterationParams: covers the current iteration configuration
* - ThreadParams: covers the specifics about threading
*
* Suppose we want to check how the ConcurrentHashMap scales under different
* parallelism levels. We can put concurrencyLevel in @Param, but it sometimes
* inconvenient if, say, we want it to follow the @Threads count. Here is
* how we can query JMH about how many threads was requested for the current run,
* and put that into concurrencyLevel argument for CHM constructor.
*
* 有一种方式用来查JMH并发运行的模型。通过请求注入以下三个脚手架对象我们就可以做到:
* - BenchmarkParams: 涵盖了benchmark的全局配置
* - IterationParams: 涵盖了当前迭代的配置
* - ThreadParams: 涵盖了指定线程的配置
*
* 假设我们想检查ConcurrentHashMap如何在不同的并行级别下扩展。我们可以可以把concurrencyLevel通过@Param传入,
* 但有时不方便,比如,我们想让他和@Threads一致。以下是我们如何查询JMH关于当前运行请求的线程数,
* 并将其放入ConcurrentHashMap构造函数的concurrencyLevel参数中。
*/
static final int THREAD_SLICE = 1000;
private ConcurrentHashMap<String, String> mapSingle;
private ConcurrentHashMap<String, String> mapFollowThreads;
@Setup
public void setup(BenchmarkParams params) {
int capacity = 16 * THREAD_SLICE * params.getThreads();
// 并发级别数量似乎只会影响initcapacity(仅在initcapacity小于并发数量时)。这么测试好像没什么意义。
mapSingle = new ConcurrentHashMap<>(capacity, 0.75f, 1);
mapFollowThreads = new ConcurrentHashMap<>(capacity, 0.75f, params.getThreads());
}
/*
* Here is another neat trick. Generate the distinct set of keys for all threads:
*
* 这是另一个巧妙的伎俩。为所有线程生成不同的密钥集:
*/
@State(Scope.Thread)
public static class Ids {
private List<String> ids;
@Setup
public void setup(ThreadParams threads) {
ids = new ArrayList<>();
for (int c = 0; c < THREAD_SLICE; c++) {
ids.add("ID" + (THREAD_SLICE * threads.getThreadIndex() + c));
}
}
}
@Benchmark
public void measureDefault(Ids ids) {
for (String s : ids.ids) {
mapSingle.remove(s);
mapSingle.put(s, s);
}
}
@Benchmark
public void measureFollowThreads(Ids ids) {
for (String s : ids.ids) {
mapFollowThreads.remove(s);
mapFollowThreads.put(s, s);
}
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_31 -t 4 -f 5
* (we requested 4 threads, and 5 forks; there are also other options, see -h)
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_31_InfraParams.class.getSimpleName())
.threads(4)
.forks(5)
.output("JMHSample_31_InfraParams.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,131 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.WarmupMode;
import java.util.concurrent.TimeUnit;
/**
* 预热方式不同测量的结果大不一样。
*
* @author childe
* @date 2018/10/17 17:09
**/
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JMHSample_32_BulkWarmup {
/*
* This is an addendum to JMHSample_12_Forking test.
*
* Sometimes you want an opposite configuration: instead of separating the profiles
* for different benchmarks, you want to mix them together to test the worst-case
* scenario.
*
* JMH has a bulk warmup feature for that: it does the warmups for all the tests
* first, and then measures them. JMH still forks the JVM for each test, but once the
* new JVM has started, all the warmups are being run there, before running the
* measurement. This helps to dodge the type profile skews, as each test is still
* executed in a different JVM, and we only "mix" the warmup code we want.
*
* 这是JMHSample_12_Forking测试的附录。
*
* 有时你想要一个相反的配置:您可以将它们混合在一起以测试最坏情况,而不是分离不同基准的配置文件。
*
* JMH有一个批量预热特性:它首先预热所有测试,然后测量他们。JMH仍然为每个测试fork出一个JVM,但当新的JVM启动,
* 所有的预热在测量开始前都会执行。
* 这有助于避免类型配置文件偏差???,因为每个测试仍然在不同的JVM中执行,我们只“混合”我们想要的预热代码。
*/
/*
* These test classes are borrowed verbatim from JMHSample_12_Forking.
*/
public interface Counter {
int inc();
}
public class Counter1 implements Counter {
private int x;
@Override
public int inc() {
return x++;
}
}
public class Counter2 implements Counter {
private int x;
@Override
public int inc() {
return x++;
}
}
Counter c1 = new Counter1();
Counter c2 = new Counter2();
/*
* And this is our test payload. Notice we have to break the inlining of the payload,
* so that in could not be inlined in either measure_c1() or measure_c2() below, and
* specialized for that only call.
*/
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public int measure(Counter c) {
int s = 0;
for (int i = 0; i < 10; i++) {
s += c.inc();
}
return s;
}
@Benchmark
public int measure_c1() {
return measure(c1);
}
@Benchmark
public int measure_c2() {
return measure(c2);
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* Note how JMH runs the warmups first, and only then a given test. Note how JMH re-warmups
* the JVM for each test. The scores for C1 and C2 cases are equally bad, compare them to
* the scores from JMHSample_12_Forking.
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_32 -f 1 -wm BULK
* (we requested a single fork, and bulk warmup mode; there are also other options, see -h)
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_32_BulkWarmup.class.getSimpleName())
// .includeWarmup(...) <-- this may include other benchmarks into warmup
// .warmupMode(WarmupMode.BULK) // see other WarmupMode.* as well
.warmupMode(WarmupMode.INDI) // see other WarmupMode.* as well
.forks(1)
.output("JMHSample_32_BulkWarmup_indi.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,199 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* 如何避免编译器的优化,按照预想跑循环
*
* @author childe
* @date 2018/10/17 17:37
**/
@State(Scope.Thread)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JMHSample_34_SafeLooping {
/*
* JMHSample_11_Loops warns about the dangers of using loops in @Benchmark methods.
* Sometimes, however, one needs to traverse through several elements in a dataset.
* This is hard to do without loops, and therefore we need to devise a scheme for
* safe looping.
*
* JMHSample_11_Loops警告我们在@Benchmark方法中使用循环很危险。
* 然而,有时我们需要便利数据集中的元素。很难避免循环,因此我们需要为安全循环设计一种方式。
*/
/*
* Suppose we want to measure how much it takes to execute work() with different
* arguments. This mimics a frequent use case when multiple instances with the same
* implementation, but different data, is measured.
*
* 假设我们想要测量以不同参数运行work()时的性能。当测量具有相同实现但数据不同的多个实例时,这模仿了一个常见的用例。
*/
static final int BASE = 42;
static int work(int x) {
return BASE + x;
}
/*
* Every benchmark requires control. We do a trivial control for our benchmarks
* by checking the benchmark costs are growing linearly with increased task size.
* If it doesn't, then something wrong is happening.
*
* 每个基准都需要控制。我们对的基准进行了微不足道的控制,通过检查基准成本随着任务规模的增加而线性增长。
* 如果没有,那么就出现了问题。
*/
@Param({"1", "10", "100", "1000"})
int size;
int[] xs;
@Setup
public void setup() {
xs = new int[size];
for (int c = 0; c < size; c++) {
xs[c] = c;
}
}
/*
* First, the obviously wrong way: "saving" the result into a local variable would not
* work. A sufficiently smart compiler will inline work(), and figure out only the last
* work() call needs to be evaluated. Indeed, if you run it with varying $size, the score
* will stay the same!
*
* 首先,这个错误很明显:把结果保存在了局部变量中,这是达不到效果。一个足够只能的编译器会把work()进行内敛优化,
* 并指出只有最后一次work()调用需要被操作。确实,如果你用一系列$size(x数组的长度)来测试,会发现他们的结果是一样的。
*/
@Benchmark
public int measureWrong_1() {
int acc = 0;
for (int x : xs) {
acc = work(x);
}
return acc;
}
/*
* Second, another wrong way: "accumulating" the result into a local variable. While
* it would force the computation of each work() method, there are software pipelining
* effects in action, that can merge the operations between two otherwise distinct work()
* bodies. This will obliterate the benchmark setup.
*
* In this example, HotSpot does the unrolled loop, merges the $BASE operands into a single
* addition to $acc, and then does a bunch of very tight stores of $x-s. The final performance
* depends on how much of the loop unrolling happened *and* how much data is available to make
* the large strides.
*
* 然后,另外一个错误:把计算结果保存在局部变量中。
* 虽然它会强制计算每个work()方法,但是有一些软件的pipelining可以合并两个不同的work()体之间的操作。这将消除基准设置。
*
* HotSpot执行unrolled循环,将$BASE操作数合并为$acc的单个添加,然后执行一堆非常紧凑的$x-s存储。
* 最终的性能取决于循环unrolled的次数以及可用于实现大幅度跨越的数据量。
*/
@Benchmark
public int measureWrong_2() {
int acc = 0;
for (int x : xs) {
acc += work(x);
}
return acc;
}
/*
* Now, let's see how to measure these things properly. A very straight-forward way to
* break the merging is to sink each result to Blackhole. This will force runtime to compute
* every work() call in full. (We would normally like to care about several concurrent work()
* computations at once, but the memory effects from Blackhole.consume() prevent those optimization
* on most runtimes).
*
* 现在,让我们看下如何正确测量。一个很直接打断合并的方法就是把每个结果都下沉到Blackhole。
* 这会强制在运行时为每次调用work()方法都会进行计算。我们通常喜欢同时关注几个并发的work()计算,但是Blackhole.consume()的内存效果阻止了大多数运行时的优化)
*/
@Benchmark
public void measureRight_1(Blackhole bh) {
for (int x : xs) {
bh.consume(work(x));
}
}
/*
* DANGEROUS AREA, PLEASE READ THE DESCRIPTION BELOW.
*
* Sometimes, the cost of sinking the value into a Blackhole is dominating the nano-benchmark score.
* In these cases, one may try to do a make-shift "sinker" with non-inlineable method. This trick is
* *very* VM-specific, and can only be used if you are verifying the generated code (that's a good
* strategy when dealing with nano-benchmarks anyway).
*
* You SHOULD NOT use this trick in most cases. Apply only where needed.
*
* 危险区域,请阅读以下解释。
*
* 有时,将结果下沉到Blackhole的花费会影响nano-benchmark的结果。
* 在这些情况下,我们或许可以尝试用non-inlineable的方法来做切换。?这个技巧非常特定于VM?,只有在验证生成的代码?时才能使用(这在处理nano-benchmarks时是一个很好的策略)。
*
* 大多情况下不要使用该技巧。仅在需要时使用。
*/
@Benchmark
public void measureRight_2() {
for (int x : xs) {
sink(work(x));
}
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static void sink(int v) {
// IT IS VERY IMPORTANT TO MATCH THE SIGNATURE TO AVOID AUTOBOXING.
// The method intentionally does nothing.
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You might notice measureWrong_1 does not depend on $size, measureWrong_2 has troubles with
* linearity, and otherwise much faster than both measureRight_*. You can also see measureRight_2
* is marginally faster than measureRight_1.
*
* 你会注意到measureWrong_1结果不受$size影响,measureWrong_2不是线性增长并且比measureRight_*要快的多。
* 你也会看到measureRight_2比measureRight_1稍快。
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_34
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_34_SafeLooping.class.getSimpleName())
.forks(3)
.output("JMHSample_34_SafeLooping.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,612 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.profile.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* 全部配置 {@link org.openjdk.jmh.profile}
*
* @author childe
* @date 2018/10/18 19:21
**/
public class JMHSample_35_Profilers {
/*
* This sample serves as the profiler overview.
*
* JMH has a few very handy profilers that help to understand your benchmarks. While
* these profilers are not the substitute for full-fledged external profilers, in many
* cases, these are handy to quickly dig into the benchmark behavior. When you are
* doing many cycles of tuning up the benchmark code itself, it is important to have
* a quick turnaround for the results.
*
* Use -lprof to list the profilers. There are quite a few profilers, and this sample
* would expand on a handful of most useful ones. Many profilers have their own options,
* usually accessible via -prof <profiler-name>:help.
*
* Since profilers are reporting on different things, it is hard to construct a single
* benchmark sample that will show all profilers in action. Therefore, we have a couple
* of benchmarks in this sample.
*
* 这个例子是profiler(分析器)的概览。
*
* JMH有一些很便利的profiler来帮助我们理解benchmark。
* 虽然这些分析器不能替代成熟的外部分析器,但在许多情况下,这些分析器很容易快速挖掘基准行为。
*
* 使用-lprof列出所有profiler。本例中只展示常用的一些。许多profiler有他们自己的选项,通过-prof <profiler-name>:help获取。
*
* 因为profiler报告不同的事情,很难在一个基准测试中展示所有profiler的行为。因为有好几个例子。
*
* */
/*
* ================================ MAPS BENCHMARK ================================
*/
@State(Scope.Thread)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public static class Maps {
private Map<Integer, Integer> map;
@Param({"hashmap", "treemap"})
private String type;
private int begin;
private int end;
@Setup
public void setup() {
switch (type) {
case "hashmap":
map = new HashMap<>();
break;
case "treemap":
map = new TreeMap<>();
break;
default:
throw new IllegalStateException("Unknown type: " + type);
}
begin = 1;
end = 256;
for (int i = begin; i < end; i++) {
map.put(i, i);
}
}
@Benchmark
public void test(Blackhole bh) {
for (int i = begin; i < end; i++) {
bh.consume(map.get(i));
}
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_35.*Maps -prof stack
* $ java -jar target/benchmarks.jar JMHSample_35.*Maps -prof gc
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(Maps.class.getSimpleName())
// .addProfiler(StackProfiler.class)
.addProfiler(GCProfiler.class)
.build();
new Runner(opt).run();
}
/*
Running this benchmark will yield something like:
Benchmark (type) Mode Cnt Score Error Units
JMHSample_35_Profilers.Maps.test hashmap avgt 5 1553.201 ± 6.199 ns/op
JMHSample_35_Profilers.Maps.test treemap avgt 5 5177.065 ± 361.278 ns/op
Running with -prof stack will yield:
....[Thread state: RUNNABLE]........................................................................
99.0% 99.0% org.openjdk.jmh.samples.JMHSample_35_Profilers$Maps.test
0.4% 0.4% org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Maps_test.test_avgt_jmhStub
0.2% 0.2% sun.reflect.NativeMethodAccessorImpl.invoke0
0.2% 0.2% java.lang.Integer.valueOf
0.2% 0.2% sun.misc.Unsafe.compareAndSwapInt
....[Thread state: RUNNABLE]........................................................................
78.0% 78.0% java.util.TreeMap.getEntry
21.2% 21.2% org.openjdk.jmh.samples.JMHSample_35_Profilers$Maps.test
0.4% 0.4% java.lang.Integer.valueOf
0.2% 0.2% sun.reflect.NativeMethodAccessorImpl.invoke0
0.2% 0.2% org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Maps_test.test_avgt_jmhStub
Stack profiler is useful to quickly see if the code we are stressing actually executes. As many other
sampling profilers, it is susceptible for sampling bias: it can fail to notice quickly executing methods,
for example. In the benchmark above, it does not notice HashMap.get.
Stack profiler可以快速查看我们所强调的代码是否实际执行。
和其他许采样分析器一样,它容易受到采样偏差的影响:它无法快速发现执行方法,例如:在上面的基准测试中,它没有注意到HashMap.get。
Next up, GC profiler. Running with -prof gc will yield:
Benchmark (type) Mode Cnt Score Error Units
JMHSample_35_Profilers.Maps.test hashmap avgt 5 1553.201 ± 6.199 ns/op
JMHSample_35_Profilers.Maps.test:·gc.alloc.rate hashmap avgt 5 1257.046 ± 5.675 MB/sec
JMHSample_35_Profilers.Maps.test:·gc.alloc.rate.norm hashmap avgt 5 2048.001 ± 0.001 B/op
JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Eden_Space hashmap avgt 5 1259.148 ± 315.277 MB/sec
JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Eden_Space.norm hashmap avgt 5 2051.519 ± 520.324 B/op
JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Survivor_Space hashmap avgt 5 0.175 ± 0.386 MB/sec
JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Survivor_Space.norm hashmap avgt 5 0.285 ± 0.629 B/op
JMHSample_35_Profilers.Maps.test:·gc.count hashmap avgt 5 29.000 counts
JMHSample_35_Profilers.Maps.test:·gc.time hashmap avgt 5 16.000 ms
JMHSample_35_Profilers.Maps.test treemap avgt 5 5177.065 ± 361.278 ns/op
JMHSample_35_Profilers.Maps.test:·gc.alloc.rate treemap avgt 5 377.251 ± 26.188 MB/sec
JMHSample_35_Profilers.Maps.test:·gc.alloc.rate.norm treemap avgt 5 2048.003 ± 0.001 B/op
JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Eden_Space treemap avgt 5 392.743 ± 174.156 MB/sec
JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Eden_Space.norm treemap avgt 5 2131.767 ± 913.941 B/op
JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Survivor_Space treemap avgt 5 0.131 ± 0.215 MB/sec
JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Survivor_Space.norm treemap avgt 5 0.709 ± 1.125 B/op
JMHSample_35_Profilers.Maps.test:·gc.count treemap avgt 5 25.000 counts
JMHSample_35_Profilers.Maps.test:·gc.time treemap avgt 5 26.000 ms
There, we can see that the tests are producing quite some garbage. "gc.alloc" would say we are allocating 1257
and 377 MB of objects per second, or 2048 bytes per benchmark operation. "gc.churn" would say that GC removes
the same amount of garbage from Eden space every second. In other words, we are producing 2048 bytes of garbage per
benchmark operation.
我们可以看到测试产生了相当多的垃圾。"gc.alloc"表示我们每秒分配1257和277MB内存,或者为每个基准测试操作分配了2048字节。
"gc.churn"表示GC每秒从Eden区域删除与GC相同量的内存。换句话说,我们每个基准测试操作产生了2048字节的垃圾。
If you look closely at the test, you can get a (correct) hypothesis this is due to Integer autoboxing.
如果仔细观察测试,可以得到一个(正确的)假设,这是由于Integer自动装箱造成的。为什么???
Note that "gc.alloc" counters generally produce more accurate data, but they can also fail when threads come and
go over the course of the benchmark. "gc.churn" values are updated on each GC event, and so if you want a more accurate
data, running longer and/or with small heap would help. But anyhow, always cross-reference "gc.alloc" and "gc.churn"
values with each other to get a complete picture.
请注意,"gc.alloc"计数器通常会产生更准确的数据,但是当线程来回顾基准测试的过程,它们也会失败。
"gc.churn"值会在每个GC事件上更新,因此如果您想要更准确的数据,运行更长时间和/或使用小堆会有所帮助。
但无论如何,总是互相引用“gc.alloc”和“gc.churn”值来获得完整的图片。
It is also worth noticing that non-normalized counters are dependent on benchmark performance! Here, "treemap"
tests are 3x slower, and thus both allocation and churn rates are also comparably lower. It is often useful to look
into non-normalized counters to see if the test is allocation/GC-bound (figure the allocation pressure "ceiling"
for your configuration!), and normalized counters to see the more precise benchmark behavior.
值得注意的是,非标准化计数器依赖于benchmark的性能!
"treemap"测试速度慢了3倍,因此分配和回收速率也相对较低。
查看 非规范化计数器以查看测试是否为allocation/GC-bound(计算配置的分配压力“上限”)和 规范化计数器以查看更精确的benchmark行为 通常很有用。
As most profilers, both "stack" and "gc" profile are able to aggregate samples from multiple forks. It is a good
idea to run multiple forks with the profilers enabled, as it improves results error estimates.
与大多数分析器一样,"stack"和"gc"配置文件都能够聚合来自多个fork的样本。在启用分析器的情况下运行多个fork是个好主意,因为它可以改善结果误差估计。
*/
}
/*
* ================================ CLASSLOADER BENCHMARK ================================
*/
@State(Scope.Thread)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public static class Classy {
/**
* Our own crippled classloader, that can only load a simple class over and over again.
*/
public static class XLoader extends URLClassLoader {
private static final byte[] X_BYTECODE = new byte[]{
(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE, 0x00, 0x00, 0x00, 0x34, 0x00, 0x0D, 0x0A, 0x00, 0x03, 0x00,
0x0A, 0x07, 0x00, 0x0B, 0x07, 0x00, 0x0C, 0x01, 0x00, 0x06, 0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x03,
0x28, 0x29, 0x56, 0x01, 0x00, 0x04, 0x43, 0x6F, 0x64, 0x65, 0x01, 0x00, 0x0F, 0x4C, 0x69, 0x6E, 0x65, 0x4E, 0x75,
0x6D, 0x62, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x01, 0x00, 0x0A, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x46,
0x69, 0x6C, 0x65, 0x01, 0x00, 0x06, 0x58, 0x2E, 0x6A, 0x61, 0x76, 0x61, 0x0C, 0x00, 0x04, 0x00, 0x05, 0x01, 0x00,
0x01, 0x58, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65,
0x63, 0x74, 0x00, 0x20, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00,
0x05, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x2A,
(byte) 0xB7, 0x00, 0x01, (byte) 0xB1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09,
};
public XLoader() {
super(new URL[0], ClassLoader.getSystemClassLoader());
}
@Override
protected Class<?> findClass(final String name) {
return defineClass(name, X_BYTECODE, 0, X_BYTECODE.length);
}
}
@Benchmark
public Class<?> load() throws ClassNotFoundException {
return Class.forName("X", true, new XLoader());
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_35.*Classy -prof cl
* $ java -jar target/benchmarks.jar JMHSample_35.*Classy -prof comp
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(Classy.class.getSimpleName())
.addProfiler(ClassloaderProfiler.class)
// .addProfiler(CompilerProfiler.class)
.build();
new Runner(opt).run();
}
/*
Running with -prof cl will yield:
Benchmark Mode Cnt Score Error Units
JMHSample_35_Profilers.Classy.load avgt 15 34215.363 ± 545.892 ns/op
JMHSample_35_Profilers.Classy.load:·class.load avgt 15 29374.097 ± 716.743 classes/sec
JMHSample_35_Profilers.Classy.load:·class.load.norm avgt 15 1.000 ± 0.001 classes/op
JMHSample_35_Profilers.Classy.load:·class.unload avgt 15 29598.233 ± 3420.181 classes/sec
JMHSample_35_Profilers.Classy.load:·class.unload.norm avgt 15 1.008 ± 0.119 classes/op
Here, we can see the benchmark indeed load class per benchmark op, and this adds up to more than 29K classloads
per second. We can also see the runtime is able to successfully keep the number of loaded classes at bay,
since the class unloading happens at the same rate.
这里,我们可以看到基准在每个基准操作的确有加载类,这种操作有每秒超过29K的类加载量。
我们看到运行时能够成功地保持加载的类的数量,因为类的卸载以同样速率在发生。
This profiler is handy when doing the classloading performance work, because it says if the classes
were actually loaded, and not reused across the Class.forName calls. It also helps to see if the benchmark
performs any classloading in the measurement phase. For example, if you have non-classloading benchmark,
you would expect these metrics be zero.
在进行类加载性能工作时,此分析器很方便,因为它表示类是否实际加载,而不是在Class.forName调用中重用。
它还有助于查看基准测试是否在测量阶段执行任何类加载。例如,如果您有非类加载基准,那么您可能希望这些指标为零。
Another useful profiler that could tell if compiler is doing a heavy work in background, and thus interfering
with measurement, -prof comp:
另一个有用的profiler可以告诉我们编译器会否在后台做了一些较重的操作,这些操作会影响我们的检测,-prof comp:
Benchmark Mode Cnt Score Error Units
JMHSample_35_Profilers.Classy.load avgt 5 33523.875 ± 3026.025 ns/op
JMHSample_35_Profilers.Classy.load:·compiler.time.profiled avgt 5 5.000 ms
JMHSample_35_Profilers.Classy.load:·compiler.time.total avgt 5 479.000 ms
We seem to be at proper steady state: out of 479 ms of total compiler work, only 5 ms happen during the
measurement window. It is expected to have some level of background compilation even at steady state.
我们似乎看到了稳定状态:在超过479ms的编译器工作重,仅有5ms发生在检测窗口期。
即使在稳定状态下,也会有一定程度的后台编译。
As most profilers, both "cl" and "comp" are able to aggregate samples from multiple forks. It is a good
idea to run multiple forks with the profilers enabled, as it improves results error estimates.
像其他profilers一样,"cl"和"comp"能够聚合多个fork的采样。这对运行多个开启了profiler的fork是件好事,改善了错误的误差。
*/
}
/*
* ================================ ATOMIC LONG BENCHMARK ================================
*/
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public static class Atomic {
private AtomicLong n;
@Setup
public void setup() {
n = new AtomicLong();
}
@Benchmark
public long test() {
return n.incrementAndGet();
}
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof perf -f 1 (Linux)
* $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof perfnorm -f 3 (Linux)
* $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof perfasm -f 1 (Linux)
* $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof xperfasm -f 1 (Windows)
* $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof dtraceasm -f 1 (Mac OS X)
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(Atomic.class.getSimpleName())
// .addProfiler(LinuxPerfProfiler.class)
// .addProfiler(LinuxPerfNormProfiler.class)
// .addProfiler(LinuxPerfAsmProfiler.class)
// .addProfiler(WinPerfAsmProfiler.class)
// 需要开启用户sudo免密
.addProfiler(DTraceAsmProfiler.class)
.build();
new Runner(opt).run();
}
/*
Dealing with nanobenchmarks like these requires looking into the abyss of runtime, hardware, and
generated code. Luckily, JMH has a few handy tools that ease the pain. If you are running Linux,
then perf_events are probably available as standard package. This kernel facility taps into
hardware counters, and provides the data for user space programs like JMH. Windows has less
sophisticated facilities, but also usable, see below.
One can simply run "perf stat java -jar ..." to get the first idea how the workload behaves. In
JMH case, however, this will cause perf to profile both host and forked JVMs.
-prof perf avoids that: JMH invokes perf for the forked VM alone. For the benchmark above, it
would print something like:
Perf stats:
--------------------------------------------------
4172.776137 task-clock (msec) # 0.411 CPUs utilized
612 context-switches # 0.147 K/sec
31 cpu-migrations # 0.007 K/sec
195 page-faults # 0.047 K/sec
16,599,643,026 cycles # 3.978 GHz [30.80%]
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
17,815,084,879 instructions # 1.07 insns per cycle [38.49%]
3,813,373,583 branches # 913.870 M/sec [38.56%]
1,212,788 branch-misses # 0.03% of all branches [38.91%]
7,582,256,427 L1-dcache-loads # 1817.077 M/sec [39.07%]
312,913 L1-dcache-load-misses # 0.00% of all L1-dcache hits [38.66%]
35,688 LLC-loads # 0.009 M/sec [32.58%]
<not supported> LLC-load-misses:HG
<not supported> L1-icache-loads:HG
161,436 L1-icache-load-misses:HG # 0.00% of all L1-icache hits [32.81%]
7,200,981,198 dTLB-loads:HG # 1725.705 M/sec [32.68%]
3,360 dTLB-load-misses:HG # 0.00% of all dTLB cache hits [32.65%]
193,874 iTLB-loads:HG # 0.046 M/sec [32.56%]
4,193 iTLB-load-misses:HG # 2.16% of all iTLB cache hits [32.44%]
<not supported> L1-dcache-prefetches:HG
0 L1-dcache-prefetch-misses:HG # 0.000 K/sec [32.33%]
10.159432892 seconds time elapsed
We can already see this benchmark goes with good IPC, does lots of loads and lots of stores,
all of them are more or less fulfilled without misses. The data like this is not handy though:
you would like to normalize the counters per benchmark op.
This is exactly what -prof perfnorm does:
Benchmark Mode Cnt Score Error Units
JMHSample_35_Profilers.Atomic.test avgt 15 6.551 ± 0.023 ns/op
JMHSample_35_Profilers.Atomic.test:·CPI avgt 3 0.933 ± 0.026 #/op
JMHSample_35_Profilers.Atomic.test:·L1-dcache-load-misses avgt 3 0.001 ± 0.022 #/op
JMHSample_35_Profilers.Atomic.test:·L1-dcache-loads avgt 3 12.267 ± 1.324 #/op
JMHSample_35_Profilers.Atomic.test:·L1-dcache-store-misses avgt 3 0.001 ± 0.006 #/op
JMHSample_35_Profilers.Atomic.test:·L1-dcache-stores avgt 3 4.090 ± 0.402 #/op
JMHSample_35_Profilers.Atomic.test:·L1-icache-load-misses avgt 3 0.001 ± 0.011 #/op
JMHSample_35_Profilers.Atomic.test:·LLC-loads avgt 3 0.001 ± 0.004 #/op
JMHSample_35_Profilers.Atomic.test:·LLC-stores avgt 3 ≈ 10⁻⁴ #/op
JMHSample_35_Profilers.Atomic.test:·branch-misses avgt 3 ≈ 10⁻⁴ #/op
JMHSample_35_Profilers.Atomic.test:·branches avgt 3 6.152 ± 0.385 #/op
JMHSample_35_Profilers.Atomic.test:·bus-cycles avgt 3 0.670 ± 0.048 #/op
JMHSample_35_Profilers.Atomic.test:·context-switches avgt 3 ≈ 10⁻⁠#/op
JMHSample_35_Profilers.Atomic.test:·cpu-migrations avgt 3 ≈ 10⁻⁷ #/op
JMHSample_35_Profilers.Atomic.test:·cycles avgt 3 26.790 ± 1.393 #/op
JMHSample_35_Profilers.Atomic.test:·dTLB-load-misses avgt 3 ≈ 10⁻⁴ #/op
JMHSample_35_Profilers.Atomic.test:·dTLB-loads avgt 3 12.278 ± 0.277 #/op
JMHSample_35_Profilers.Atomic.test:·dTLB-store-misses avgt 3 ≈ 10⁻⁵ #/op
JMHSample_35_Profilers.Atomic.test:·dTLB-stores avgt 3 4.113 ± 0.437 #/op
JMHSample_35_Profilers.Atomic.test:·iTLB-load-misses avgt 3 ≈ 10⁻⁵ #/op
JMHSample_35_Profilers.Atomic.test:·iTLB-loads avgt 3 0.001 ± 0.034 #/op
JMHSample_35_Profilers.Atomic.test:·instructions avgt 3 28.729 ± 1.297 #/op
JMHSample_35_Profilers.Atomic.test:·minor-faults avgt 3 ≈ 10⁻⁷ #/op
JMHSample_35_Profilers.Atomic.test:·page-faults avgt 3 ≈ 10⁻⁷ #/op
JMHSample_35_Profilers.Atomic.test:·ref-cycles avgt 3 26.734 ± 2.081 #/op
It is customary to trim the lines irrelevant to the particular benchmark. We show all of them here for
completeness.
We can see that the benchmark does ~12 loads per benchmark op, and about ~4 stores per op, most of
them fitting in the cache. There are also ~6 branches per benchmark op, all are predicted as well.
It is also easy to see the benchmark op takes ~28 instructions executed in ~27 cycles.
The output would get more interesting when we run with more threads, say, -t 8:
Benchmark Mode Cnt Score Error Units
JMHSample_35_Profilers.Atomic.test avgt 15 143.595 ± 1.968 ns/op
JMHSample_35_Profilers.Atomic.test:·CPI avgt 3 17.741 ± 28.761 #/op
JMHSample_35_Profilers.Atomic.test:·L1-dcache-load-misses avgt 3 0.175 ± 0.406 #/op
JMHSample_35_Profilers.Atomic.test:·L1-dcache-loads avgt 3 11.872 ± 0.786 #/op
JMHSample_35_Profilers.Atomic.test:·L1-dcache-store-misses avgt 3 0.184 ± 0.505 #/op
JMHSample_35_Profilers.Atomic.test:·L1-dcache-stores avgt 3 4.422 ± 0.561 #/op
JMHSample_35_Profilers.Atomic.test:·L1-icache-load-misses avgt 3 0.015 ± 0.083 #/op
JMHSample_35_Profilers.Atomic.test:·LLC-loads avgt 3 0.015 ± 0.128 #/op
JMHSample_35_Profilers.Atomic.test:·LLC-stores avgt 3 1.036 ± 0.045 #/op
JMHSample_35_Profilers.Atomic.test:·branch-misses avgt 3 0.224 ± 0.492 #/op
JMHSample_35_Profilers.Atomic.test:·branches avgt 3 6.524 ± 2.873 #/op
JMHSample_35_Profilers.Atomic.test:·bus-cycles avgt 3 13.475 ± 14.502 #/op
JMHSample_35_Profilers.Atomic.test:·context-switches avgt 3 ≈ 10⁻⁴ #/op
JMHSample_35_Profilers.Atomic.test:·cpu-migrations avgt 3 ≈ 10⁻⁠#/op
JMHSample_35_Profilers.Atomic.test:·cycles avgt 3 537.874 ± 595.723 #/op
JMHSample_35_Profilers.Atomic.test:·dTLB-load-misses avgt 3 0.001 ± 0.006 #/op
JMHSample_35_Profilers.Atomic.test:·dTLB-loads avgt 3 12.032 ± 2.430 #/op
JMHSample_35_Profilers.Atomic.test:·dTLB-store-misses avgt 3 ≈ 10⁻⁴ #/op
JMHSample_35_Profilers.Atomic.test:·dTLB-stores avgt 3 4.557 ± 0.948 #/op
JMHSample_35_Profilers.Atomic.test:·iTLB-load-misses avgt 3 ≈ 10⁻³ #/op
JMHSample_35_Profilers.Atomic.test:·iTLB-loads avgt 3 0.016 ± 0.052 #/op
JMHSample_35_Profilers.Atomic.test:·instructions avgt 3 30.367 ± 15.052 #/op
JMHSample_35_Profilers.Atomic.test:·minor-faults avgt 3 ≈ 10⁻⁵ #/op
JMHSample_35_Profilers.Atomic.test:·page-faults avgt 3 ≈ 10⁻⁵ #/op
JMHSample_35_Profilers.Atomic.test:·ref-cycles avgt 3 538.697 ± 590.183 #/op
Note how this time the CPI is awfully high: 17 cycles per instruction! Indeed, we are making almost the
same ~30 instructions, but now they take >530 cycles. Other counters highlight why: we now have cache
misses on both loads and stores, on all levels of cache hierarchy. With a simple constant-footprint
like ours, that's an indication of sharing problems. Indeed, our AtomicLong is heavily-contended
with 8 threads.
"perfnorm", again, can (and should!) be used with multiple forks, to properly estimate the metrics.
The last, but not the least player on our field is -prof perfasm. It is important to follow up on
generated code when dealing with fine-grained benchmarks. We could employ PrintAssembly to dump the
generated code, but it will dump *all* the generated code, and figuring out what is related to our
benchmark is a daunting task. But we have "perf" that can tell what program addresses are really hot!
This enables us to contrast the assembly output.
-prof perfasm would indeed contrast out the hottest loop in the generated code! It will also point
fingers at "lock xadd" as the hottest instruction in our code. Hardware counters are not very precise
about the instruction addresses, so sometimes they attribute the events to the adjacent code lines.
Hottest code regions (>10.00% "cycles" events):
....[Hottest Region 1]..............................................................................
[0x7f1824f87c45:0x7f1824f87c79] in org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub
; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@29 (line 201)
; implicit exception: dispatches to 0x00007f1824f87d21
0x00007f1824f87c25: test %r11d,%r11d
0x00007f1824f87c28: jne 0x00007f1824f87cbd ;*ifeq
; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@32 (line 201)
0x00007f1824f87c2e: mov $0x1,%ebp
0x00007f1824f87c33: nopw 0x0(%rax,%rax,1)
0x00007f1824f87c3c: xchg %ax,%ax ;*aload
; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@13 (line 199)
0x00007f1824f87c40: mov 0x8(%rsp),%r10
0.00% 0x00007f1824f87c45: mov 0xc(%r10),%r11d ;*getfield n
; - org.openjdk.jmh.samples.JMHSample_35_Profilers$Atomic::test@1 (line 280)
; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@16 (line 199)
0.19% 0.02% 0x00007f1824f87c49: test %r11d,%r11d
0x00007f1824f87c4c: je 0x00007f1824f87cad
0x00007f1824f87c4e: mov $0x1,%edx
0x00007f1824f87c53: lock xadd %rdx,0x10(%r12,%r11,8)
;*invokevirtual getAndAddLong
; - java.util.concurrent.atomic.AtomicLong::incrementAndGet@8 (line 200)
; - org.openjdk.jmh.samples.JMHSample_35_Profilers$Atomic::test@4 (line 280)
; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@16 (line 199)
95.20% 95.06% 0x00007f1824f87c5a: add $0x1,%rdx ;*ladd
; - java.util.concurrent.atomic.AtomicLong::incrementAndGet@12 (line 200)
; - org.openjdk.jmh.samples.JMHSample_35_Profilers$Atomic::test@4 (line 280)
; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@16 (line 199)
0.24% 0.00% 0x00007f1824f87c5e: mov 0x10(%rsp),%rsi
0x00007f1824f87c63: callq 0x00007f1824e2b020 ; OopMap{[0]=Oop [8]=Oop [16]=Oop [24]=Oop off=232}
;*invokevirtual consume
; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@19 (line 199)
; {optimized virtual_call}
0.20% 0.01% 0x00007f1824f87c68: mov 0x18(%rsp),%r10
0x00007f1824f87c6d: movzbl 0x94(%r10),%r11d ;*getfield isDone
; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@29 (line 201)
0.00% 0x00007f1824f87c75: add $0x1,%rbp ; OopMap{r10=Oop [0]=Oop [8]=Oop [16]=Oop [24]=Oop off=249}
;*ifeq
; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@32 (line 201)
0.20% 0.01% 0x00007f1824f87c79: test %eax,0x15f36381(%rip) # 0x00007f183aebe000
; {poll}
0x00007f1824f87c7f: test %r11d,%r11d
0x00007f1824f87c82: je 0x00007f1824f87c40 ;*aload_2
; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@35 (line 202)
0x00007f1824f87c84: mov $0x7f1839be4220,%r10
0x00007f1824f87c8e: callq *%r10 ;*invokestatic nanoTime
; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@36 (line 202)
0x00007f1824f87c91: mov (%rsp),%r10
....................................................................................................
96.03% 95.10% <total for region 1>
perfasm would also print the hottest methods to show if we indeed spending time in our benchmark. Most of the time,
it can demangle VM and kernel symbols as well:
....[Hottest Methods (after inlining)]..............................................................
96.03% 95.10% org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub
0.73% 0.78% org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_AverageTime
0.63% 0.00% org.openjdk.jmh.infra.Blackhole::consume
0.23% 0.25% native_write_msr_safe ([kernel.kallsyms])
0.09% 0.05% _raw_spin_unlock ([kernel.kallsyms])
0.09% 0.00% [unknown] (libpthread-2.19.so)
0.06% 0.07% _raw_spin_lock ([kernel.kallsyms])
0.06% 0.04% _raw_spin_unlock_irqrestore ([kernel.kallsyms])
0.06% 0.05% _IO_fwrite (libc-2.19.so)
0.05% 0.03% __srcu_read_lock; __srcu_read_unlock ([kernel.kallsyms])
0.04% 0.05% _raw_spin_lock_irqsave ([kernel.kallsyms])
0.04% 0.06% vfprintf (libc-2.19.so)
0.04% 0.01% mutex_unlock ([kernel.kallsyms])
0.04% 0.01% _nv014306rm ([nvidia])
0.04% 0.04% rcu_eqs_enter_common.isra.47 ([kernel.kallsyms])
0.04% 0.02% mutex_lock ([kernel.kallsyms])
0.03% 0.07% __acct_update_integrals ([kernel.kallsyms])
0.03% 0.02% fget_light ([kernel.kallsyms])
0.03% 0.01% fput ([kernel.kallsyms])
0.03% 0.04% rcu_eqs_exit_common.isra.48 ([kernel.kallsyms])
1.63% 2.26% <...other 319 warm methods...>
....................................................................................................
100.00% 98.97% <totals>
....[Distribution by Area]..........................................................................
97.44% 95.99% <generated code>
1.60% 2.42% <native code in ([kernel.kallsyms])>
0.47% 0.78% <native code in (libjvm.so)>
0.22% 0.29% <native code in (libc-2.19.so)>
0.15% 0.07% <native code in (libpthread-2.19.so)>
0.07% 0.38% <native code in ([nvidia])>
0.05% 0.06% <native code in (libhsdis-amd64.so)>
0.00% 0.00% <native code in (nf_conntrack.ko)>
0.00% 0.00% <native code in (hid.ko)>
....................................................................................................
100.00% 100.00% <totals>
Since program addresses change from fork to fork, it does not make sense to run perfasm with more than
a single fork.
*/
}
}

@ -0,0 +1,138 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.profile.DTraceAsmProfiler;
import org.openjdk.jmh.profile.LinuxPerfNormProfiler;
import org.openjdk.jmh.profile.LinuxPerfProfiler;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
* 分支预测,底层做的一件很牛逼的运行优化
* @author childe
* @date 2018-10-20
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(5)
@State(Scope.Benchmark)
public class JMHSample_36_BranchPrediction {
/*
* This sample serves as a warning against regular data sets.
*
* It is very tempting to present a regular data set to benchmark, either due to
* naive generation strategy, or just from feeling better about regular data sets.
* Unfortunately, it frequently backfires: the regular datasets are known to be
* optimized well by software and hardware. This example exploits one of these
* optimizations: branch prediction.
*
* Imagine our benchmark selects the branch based on the array contents, as
* we are streaming through it:
*
* 这个例子用作对常规数据集的警告。
*
* 由于天真的生成策略,或者只是感觉对常规数据集更好,将常规数据集呈现为基准测试是非常诱人的。
* 不幸的是,它经常事与愿违:已知常规数据集可以通过软件和硬件很好地进行优化。
* 此示例利用了以下优化之一:分支预测。
*
* 假设我们的基准测试通过数组的内容来选择分支,因为我们正在通过它进行流式传输:
*/
private static final int COUNT = 1024 * 1024;
private byte[] sorted;
private byte[] unsorted;
@Setup
public void setup() {
sorted = new byte[COUNT];
unsorted = new byte[COUNT];
Random random = new Random(1234);
random.nextBytes(sorted);
random.nextBytes(unsorted);
Arrays.sort(sorted);
}
@Benchmark
@OperationsPerInvocation(COUNT)
public void sorted(Blackhole bh1, Blackhole bh2) {
for (byte v : sorted) {
if (v > 0) {
bh1.consume(v);
} else {
bh2.consume(v);
}
}
}
@Benchmark
@OperationsPerInvocation(COUNT)
public void unsorted(Blackhole bh1, Blackhole bh2) {
for (byte v : unsorted) {
if (v > 0) {
bh1.consume(v);
} else {
bh2.consume(v);
}
}
}
/*
There is a substantial difference in performance for these benchmarks!
It is explained by good branch prediction in "sorted" case, and branch mispredicts in "unsorted"
case. -prof perfnorm conveniently highlights that, with larger "branch-misses", and larger "CPI"
for "unsorted" case:
Benchmark Mode Cnt Score Error Units
JMHSample_36_BranchPrediction.sorted avgt 25 2.160 ± 0.049 ns/op
JMHSample_36_BranchPrediction.sorted:·CPI avgt 5 0.286 ± 0.025 #/op
JMHSample_36_BranchPrediction.sorted:·branch-misses avgt 5 ≈ 10⁻⁴ #/op
JMHSample_36_BranchPrediction.sorted:·branches avgt 5 7.606 ± 1.742 #/op
JMHSample_36_BranchPrediction.sorted:·cycles avgt 5 8.998 ± 1.081 #/op
JMHSample_36_BranchPrediction.sorted:·instructions avgt 5 31.442 ± 4.899 #/op
JMHSample_36_BranchPrediction.unsorted avgt 25 5.943 ± 0.018 ns/op
JMHSample_36_BranchPrediction.unsorted:·CPI avgt 5 0.775 ± 0.052 #/op
JMHSample_36_BranchPrediction.unsorted:·branch-misses avgt 5 0.529 ± 0.026 #/op <--- OOPS
JMHSample_36_BranchPrediction.unsorted:·branches avgt 5 7.841 ± 0.046 #/op
JMHSample_36_BranchPrediction.unsorted:·cycles avgt 5 24.793 ± 0.434 #/op
JMHSample_36_BranchPrediction.unsorted:·instructions avgt 5 31.994 ± 2.342 #/op
It is an open question if you want to measure only one of these tests. In many cases, you have to measure
both to get the proper best-case and worst-case estimate!
*/
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_36
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(".*" + JMHSample_36_BranchPrediction.class.getSimpleName() + ".*")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,126 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
* desc
*
* @author childe
* @date 2018/10/22 17:10
**/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(5)
@State(Scope.Benchmark)
public class JMHSample_37_CacheAccess {
/*
* This sample serves as a warning against subtle differences in cache access patterns.
*
* Many performance differences may be explained by the way tests are accessing memory.
* In the example below, we walLevelk the matrix either row-first, or col-first:
*/
private final static int COUNT = 4096;
private final static int MATRIX_SIZE = COUNT * COUNT;
private int[][] matrix;
@Setup
public void setup() {
matrix = new int[COUNT][COUNT];
Random random = new Random(1234);
for (int i = 0; i < COUNT; i++) {
for (int j = 0; j < COUNT; j++) {
matrix[i][j] = random.nextInt();
}
}
}
@Benchmark
@OperationsPerInvocation(MATRIX_SIZE)
public void colFirst(Blackhole bh) {
for (int c = 0; c < COUNT; c++) {
for (int r = 0; r < COUNT; r++) {
bh.consume(matrix[r][c]);
}
}
}
@Benchmark
@OperationsPerInvocation(MATRIX_SIZE)
public void rowFirst(Blackhole bh) {
for (int r = 0; r < COUNT; r++) {
for (int c = 0; c < COUNT; c++) {
bh.consume(matrix[r][c]);
}
}
}
/*
Notably, colFirst accesses are much slower, and that's not a surprise: Java's multidimensional
arrays are actually rigged, being one-dimensional arrays of one-dimensional arrays. Therefore,
pulling n-th element from each of the inner array induces more cache misses, when matrix is large.
-prof perfnorm conveniently highlights that, with >2 cache misses per one benchmark op:
值得注意的是,colFirst访问速度要慢得多,这并不奇怪:
Java的多维数组实际上是被操纵的,是一维数组的一维数组。
因此,当矩阵很大时,从每个内部阵列中拉出第n个元素会引起更多的高速缓存未命中。
-prof perfnorm清晰的展示出,每个基准操作有2个缓存未命中:
Benchmark Mode Cnt Score Error Units
JMHSample_37_MatrixCopy.colFirst avgt 25 5.306 ± 0.020 ns/op
JMHSample_37_MatrixCopy.colFirst:·CPI avgt 5 0.621 ± 0.011 #/op
JMHSample_37_MatrixCopy.colFirst:·L1-dcache-load-misses avgt 5 2.177 ± 0.044 #/op <-- OOPS
JMHSample_37_MatrixCopy.colFirst:·L1-dcache-loads avgt 5 14.804 ± 0.261 #/op
JMHSample_37_MatrixCopy.colFirst:·LLC-loads avgt 5 2.165 ± 0.091 #/op
JMHSample_37_MatrixCopy.colFirst:·cycles avgt 5 22.272 ± 0.372 #/op
JMHSample_37_MatrixCopy.colFirst:·instructions avgt 5 35.888 ± 1.215 #/op
JMHSample_37_MatrixCopy.rowFirst avgt 25 2.662 ± 0.003 ns/op
JMHSample_37_MatrixCopy.rowFirst:·CPI avgt 5 0.312 ± 0.003 #/op
JMHSample_37_MatrixCopy.rowFirst:·L1-dcache-load-misses avgt 5 0.066 ± 0.001 #/op
JMHSample_37_MatrixCopy.rowFirst:·L1-dcache-loads avgt 5 14.570 ± 0.400 #/op
JMHSample_37_MatrixCopy.rowFirst:·LLC-loads avgt 5 0.002 ± 0.001 #/op
JMHSample_37_MatrixCopy.rowFirst:·cycles avgt 5 11.046 ± 0.343 #/op
JMHSample_37_MatrixCopy.rowFirst:·instructions avgt 5 35.416 ± 1.248 #/op
So, when comparing two different benchmarks, you have to follow up if the difference is caused
by the memory locality issues.
所以,在比较两个不同的基准时,如果差异是由内存局部性问题引起的,则必须跟进。
*/
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_37
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(".*" + JMHSample_37_CacheAccess.class.getSimpleName() + ".*")
.output("JMHSample_37_CacheAccess.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,177 @@
package com.renchao.jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
* desc
*
* @author childe
* @date 2018/10/22 19:47
**/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(5)
public class JMHSample_38_PerInvokeSetup {
/*
* This example highlights the usual mistake in non-steady-state benchmarks.
*
* Suppose we want to test how long it takes to bubble sort an array. Naively,
* we could make the test that populates an array with random (unsorted) values,
* and calls sort on it over and over again:
*
* 此示例突显非稳态基准测试(non-steady-state benchmarks)中的常见错误。
*
* 假设我们要测试对数组进行冒泡排序的耗时。
* 天真地,我们可以使用随机(未排序)值填充数组,并一遍又一遍地调用sort测试:
*/
private void bubbleSort(byte[] b) {
boolean changed = true;
while (changed) {
changed = false;
for (int c = 0; c < b.length - 1; c++) {
if (b[c] > b[c + 1]) {
byte t = b[c];
b[c] = b[c + 1];
b[c + 1] = t;
changed = true;
}
}
}
}
// Could be an implicit State instead, but we are going to use it
// as the dependency in one of the tests below
// 可能是一个隐式状态,但我们将在下面的一个测试中依赖它
@State(Scope.Benchmark)
public static class Data {
@Param({"1", "16", "256"})
int count;
byte[] arr;
@Setup
public void setup() {
arr = new byte[count];
Random random = new Random(1234);
random.nextBytes(arr);
}
}
@Benchmark
public byte[] measureWrong(Data d) {
bubbleSort(d.arr);
return d.arr;
}
/*
* The method above is subtly wrong: it sorts the random array on the first invocation
* only. Every subsequent call will "sort" the already sorted array. With bubble sort,
* that operation would be significantly faster!
*
* This is how we might *try* to measure it right by making a copy in Level.Invocation
* setup. However, this is susceptible to the problems described in Level.Invocation
* Javadocs, READ AND UNDERSTAND THOSE DOCS BEFORE USING THIS APPROACH.
*
* 上面的方法是巧妙的错误:它只在第一次调用时对随机数组进行排序。
* 后续每个调用都将"排序"已排序的数组。
* 通过冒泡排序,操作速度会明显加快!
*
* 我们可以尝试通过在Level.Invocation中制作数组的副本来正确测量它。
* 在使用Level.Invocation这些方法之前请阅读并理解这些文档。
*/
@State(Scope.Thread)
public static class DataCopy {
byte[] copy;
@Setup(Level.Invocation)
public void setup2(Data d) {
copy = Arrays.copyOf(d.arr, d.arr.length);
}
}
@Benchmark
public byte[] measureNeutral(DataCopy d) {
bubbleSort(d.copy);
return d.copy;
}
/*
* In an overwhelming majority of cases, the only sensible thing to do is to suck up
* the per-invocation setup costs into a benchmark itself. This work well in practice,
* especially when the payload costs dominate the setup costs.
*
* 在绝大多数情况下,唯一明智的做法是将每次调用设置成本吸收到基准测试本身。
*
* 这在实践中很有效,特别是当有效载荷成本主导设置成本时
* (即:测试本身耗时远远大于准备数据时,准备数据时间对测试本身的影响可以忽略)。
*/
@Benchmark
public byte[] measureRight(Data d) {
byte[] c = Arrays.copyOf(d.arr, d.arr.length);
bubbleSort(c);
return c;
}
/*
Benchmark (count) Mode Cnt Score Error Units
JMHSample_38_PerInvokeSetup.measureWrong 1 avgt 25 2.408 ± 0.011 ns/op
JMHSample_38_PerInvokeSetup.measureWrong 16 avgt 25 8.286 ± 0.023 ns/op
JMHSample_38_PerInvokeSetup.measureWrong 256 avgt 25 73.405 ± 0.018 ns/op
JMHSample_38_PerInvokeSetup.measureNeutral 1 avgt 25 15.835 ± 0.470 ns/op
JMHSample_38_PerInvokeSetup.measureNeutral 16 avgt 25 112.552 ± 0.787 ns/op
JMHSample_38_PerInvokeSetup.measureNeutral 256 avgt 25 58343.848 ± 991.202 ns/op
JMHSample_38_PerInvokeSetup.measureRight 1 avgt 25 6.075 ± 0.018 ns/op
JMHSample_38_PerInvokeSetup.measureRight 16 avgt 25 102.390 ± 0.676 ns/op
JMHSample_38_PerInvokeSetup.measureRight 256 avgt 25 58812.411 ± 997.951 ns/op
We can clearly see that "measureWrong" provides a very weird result: it "sorts" way too fast.
"measureNeutral" is neither good or bad: while it prepares the data for each invocation correctly,
the timing overheads are clearly visible. These overheads can be overwhelming, depending on
the thread count and/or OS flavor.
明显可以看出"measureWrong"跑出来一个奇怪的结果:它的排序太快了。
"measureNeutral"一般般:它正确地为每个调用准备数据,时间开销清晰可见。在不同的线程数 和/或 OS风格下,这些开销可能会非常大。
*/
/*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_38
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(".*" + JMHSample_38_PerInvokeSetup.class.getSimpleName() + ".*")
.output("JMHSample_38_PerInvokeSetup.sampleLog")
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,14 @@
package com.renchao.spring;
import com.renchao.spring.bean.Anonymous;
import com.renchao.spring.bean.TestController;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
public class AnnotationUtilsTest {
public static void main(String[] args) {
Anonymous annotation = AnnotationUtils.findAnnotation(TestController.class, Anonymous.class);
System.out.println(annotation);
System.out.println(AnnotatedElementUtils.isAnnotated(TestController.class, Anonymous.class));
}
}

@ -0,0 +1,19 @@
package com.renchao.spring.bean;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 匿名访问不鉴权注解
*
* @author admin
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Anonymous
{
}

@ -0,0 +1,5 @@
package com.renchao.spring.bean;
@Anonymous
public class BaseController {
}

@ -0,0 +1,15 @@
package com.renchao.spring.bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Anonymous
public class TestController extends BaseController {
@RequestMapping({"/cc","dd"})
@Anonymous
public String testF() {
return "testFeign.test01()";
}
}

@ -0,0 +1,70 @@
package com.renchao.test;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import org.hibernate.validator.HibernateValidator;
import org.junit.Test;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.validation.annotation.Validated;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.Set;
public class DateDemo {
public static void main(String[] args) throws JsonProcessingException {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
SimpleModule simpleModule = new SimpleModule()
// .addSerializer(Long.class, ToStringSerializer.instance)
.addSerializer(Long.TYPE, ToStringSerializer.instance);
builder.modules(simpleModule);
ObjectMapper build = builder.build();
String str = "{\"id\":22,\"name\":\"aaa\",\"age\":55}";
Bean bean1 = build.readValue(str, Bean.class);
Bean bean = new Bean();
bean.setId(22L);
bean.setName("aaa");
String s = build.writeValueAsString(bean);
System.out.println(s);
}
@Data
static class Bean {
@NotBlank(message = "aabb")
private String name;
@NotNull(message = "唯一标识不能为空")
private Long id;
@NotNull(message = "cc")
private String age;
}
@Test
public void test01() {
Bean bean = new Bean();
Validator validator = Validation.byProvider(HibernateValidator.class)
.configure().failFast(false).buildValidatorFactory().getValidator();
Set<ConstraintViolation<Bean>> validate = validator.validate(bean);
for (ConstraintViolation<Bean> violation : validate) {
System.out.println(violation.getLeafBean());
System.out.println(violation.getMessage());
System.out.println(violation.getMessageTemplate());
System.out.println(violation.getPropertyPath());
System.out.println("======================");
}
}
}

@ -0,0 +1,38 @@
package com.renchao.test;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class DemoData {
public static void main(String[] args) {
String filePath = "C:/Users/RENCHAO/Desktop/资料/easyexcel-user1.xls";
List<UserEntity> list = EasyExcel.read(filePath).head(UserEntity.class).sheet().doReadSync();
System.out.println(list);
}
@Test
public void test01() {
String str = "[{\"component\":\"聚酯树脂\",\"content\":\"48%\"},{\"component\":\"乙酸乙酯\",\"content\":\"52%\"}]";
// String str = "ssss";
System.out.println(JSON.isValid(str));
List<Map<String, String>> list = JSON.parseObject(str, new TypeReference<List<Map<String, String>>>() {});
for (Map<String, String> map : list) {
System.out.println(map.get("component") + "::" + map.get("content"));
}
ArrayList<String> strings = new ArrayList<>();
strings.add("cc");
strings.add("dd");
System.out.println(String.join("||", strings));
}
}

@ -0,0 +1,80 @@
package com.renchao.test;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
//@BenchmarkMode(Mode.AverageTime)
//@OutputTimeUnit(TimeUnit.MICROSECONDS)
//@State(Scope.Thread)
public class JMHTest {
/**
* 测试数据
*/
private final static String DATA = "大白有点菜";
// private List<String> arrayList = new ArrayList<>();
// private List<String> linkedList = new LinkedList<>();
// /**
// * 初始化 ArrayList 和 LinkedList
// */
// @Setup(Level.Iteration)
// public void setUp()
// {
// this.arrayList = new ArrayList<>();
// this.linkedList = new LinkedList<>();
// }
// /**
// * ArrayList的add方法
// * @return
// */
// @Benchmark
// public List<String> arrayListAdd() {
// this.arrayList.add(DATA);
// return arrayList;
// }
// /**
// * LinkedList的add方法
// * @return
// */
// @Benchmark
// public List<String> linkedListAdd() {
// this.linkedList.add(DATA);
// return this.linkedList;
// }
@Benchmark
public int Test() {
int i = 5;
int k = i * 8;
return k;
}
public static void main(String[] args) throws RunnerException {
final Options opts = new OptionsBuilder()
.include(JMHTest.class.getSimpleName())
.forks(1)
// .output("Benchmark1.log")
.build();
new Runner(opts).run();
}
}

@ -0,0 +1,96 @@
package com.renchao.test;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
public class JMHTest2 {
/**
* Most of the time, you need to maintain some state while the benchmark is
* running. Since JMH is heavily used to build concurrent benchmarks, we
* opted for an explicit notion of state-bearing objects.
*
* Below are two state objects. Their class names are not essential, it
* matters they are marked with @State. These objects will be instantiated
* on demand, and reused during the entire benchmark trial.
*
* The important property is that state is always instantiated by one of
* those benchmark threads which will then have the access to that state.
* That means you can initialize the fields as if you do that in worker
* threads (ThreadLocals are yours, etc).
*/
@State(Scope.Benchmark)
public static class BenchmarkState {
volatile double x = Math.PI;
}
@State(Scope.Thread)
public static class ThreadState {
volatile double x = Math.PI;
}
/*
* Benchmark methods can reference the states, and JMH will inject the
* appropriate states while calling these methods. You can have no states at
* all, or have only one state, or have multiple states referenced. This
* makes building multi-threaded benchmark a breeze.
*
* For this exercise, we have two methods.
*/
@Benchmark
public void measureUnshared(ThreadState state) {
// All benchmark threads will call in this method.
//
// However, since ThreadState is the Scope.Thread, each thread
// will have it's own copy of the state, and this benchmark
// will measure unshared case.
state.x++;
}
@Benchmark
public void measureShared(BenchmarkState state) {
// All benchmark threads will call in this method.
//
// Since BenchmarkState is the Scope.Benchmark, all threads
// will share the state instance, and we will end up measuring
// shared case.
state.x++;
}
/**
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You are expected to see the drastic difference in shared and unshared cases,
* because you either contend for single memory location, or not. This effect
* is more articulated on large machines.
*
* You can run this test:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_03 -t 4 -f 1
* (we requested 4 threads, single fork; there are also other options, see -h)
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHTest2.class.getSimpleName())
.threads(4)
.forks(1)
.build();
new Runner(opt).run();
}
}

@ -0,0 +1,179 @@
package com.renchao.test;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.CryptoException;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.renchao.utils.RsaUtil;
import org.apache.commons.lang3.time.DateUtils;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.TimeZone;
import java.util.regex.Pattern;
public class MyTest {
private static final Pattern pattern = Pattern.compile("[+/]");
public static void main(String[] args) throws JsonProcessingException {
String str = "ee, tt,,cc ";
List<String> split = StrUtil.split(str, ",", true, true);
split.forEach(System.out::println);
}
@Test
public void test01() {
String str = "I:X,II:X.Y,III:X.Y.Z";
String[] split = str.split(",");
HashMap<String, List<String>> map = new HashMap<>();
for (String s : split) {
String[] m = s.split(":");
map.put(m[0], CharSequenceUtil.split(m[1], "."));
}
System.out.println();
}
@Test
public void test03() throws IOException {
String str = "任超";
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
byte[] bytes1 = str.getBytes(StandardCharsets.ISO_8859_1);
System.out.println(new String(bytes,StandardCharsets.UTF_8));
System.out.println(new String(bytes1,StandardCharsets.ISO_8859_1));
}
@Test
public void test04() {
String str = "2bce7d71-d60b-4858-a852-15380eba6c84";
String s = RsaUtil.decryptStr(str);
System.out.println(s);
}
@Test
public void test05() {
String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDG03gR1w6i3E6h6+N9F2///BnRrkzPc7RT4qZKKl2b/rolym0EYl3QZTsIV5oQngT93TLtld7EK5svdwUabX6kzqd8yDDChZXS/E7/FrufN6Hwf9S3O3ZzkhEyd45HmRHV4aNRFsS/NviEZx83D6FR94l0SPnomvPkVqM8UnafnQIDAQAB";
String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbTeBHXDqLcTqHr430Xb//8GdGuTM9ztFPipkoqXZv+uiXKbQRiXdBlOwhXmhCeBP3dMu2V3sQrmy93BRptfqTOp3zIMMKFldL8Tv8Wu583ofB/1Lc7dnOSETJ3jkeZEdXho1EWxL82+IRnHzcPoVH3iXRI+eia8+RWozxSdp+dAgMBAAECgYAJjtfqT6LR/HJBQXQ9qrdFIIrjNBRYMrE8CRzCWvgGDEBJmcoU2F+3KW6lj4SGAPqvc4dDuZ0sZAZBSWDy7MmWL+Zz2z44sulxsOsb3DJqIyBSAr5D6mhrRmu7MJA5AGgDHo/2gn+9Cji2JQBHBFe18BzJdr2tIM4uAYTVB6EW8QJBAPCrnHohSDtgLSmHrbORP/cIS8OOF/M3PsYfHZ3cpdrKk2zs1rXAHJq80GlmhSQx8tezx6wt63Cph0reiHbOMRkCQQDTfYqahFR0NTFFfTBfSJKQEqoiRYMnOrjkkOOgFv6cBwYd16pnqTfNISSYkBsOcDO09qiMILW96MoJONCV458lAkEAmMrqueK9X+zMX0xjK9hwOp5Ks2lXrTKKqO+CNwGpTkFD3WhzW8oOnvJ2giPzLSqE2QqrHpW8nrcSTKcBDiQTqQJABORmjGR7P6TrWtwmfk3Ddim4XcqV2hZ1qHPhkBZ4FUvkTFRs0LENZWVa31yWA6N8zrbV90fabGYyJjx2NsFpMQJARtRflzJjWc/49nzu+om41bz9Ngg07/S8Rxe8AlZbSlCxggmp/KUBcoVgNJCa5qGsX2AvTOCXaHngp+YLtHHPBQ==";
RSA rsa = new RSA(privateKey,publicKey);
//获得私钥
System.out.println("获得私钥:" + rsa.getPrivateKey());
System.out.println("获得私钥:" + rsa.getPrivateKeyBase64());
//获得公钥
System.out.println("获得公钥:" + rsa.getPublicKey());
System.out.println("获得公钥:" + rsa.getPublicKeyBase64());
//公钥加密,私钥解密
byte[] encrypt = rsa.encrypt(StrUtil.bytes("我是一段测试aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);
System.out.println("加密后的串:" + rsa.encryptBase64("我是一段测试aaaa",KeyType.PublicKey));
byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);
System.out.println("公钥加密,私钥解密:" + new String(decrypt));
//Junit单元测试
//Assert.assertEquals("我是一段测试aaaa", StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));
//私钥加密,公钥解密
byte[] encrypt2 = rsa.encrypt(StrUtil.bytes("我是一段测试aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);
System.out.println("加密后的串:" + rsa.encryptBase64("url=/checkInInfo/reviewResult&token=2bce7d71-d60b-4858-a852-15380eba6c84&timestamp=1574821902392",KeyType.PrivateKey));
byte[] decrypt2 = rsa.decrypt(encrypt2, KeyType.PublicKey);
System.out.println("私钥加密,公钥解密:" + new String(decrypt2));
}
@Test
public void test06() {
String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDG03gR1w6i3E6h6+N9F2///BnRrkzPc7RT4qZKKl2b/rolym0EYl3QZTsIV5oQngT93TLtld7EK5svdwUabX6kzqd8yDDChZXS/E7/FrufN6Hwf9S3O3ZzkhEyd45HmRHV4aNRFsS/NviEZx83D6FR94l0SPnomvPkVqM8UnafnQIDAQAB";
String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbTeBHXDqLcTqHr430Xb//8GdGuTM9ztFPipkoqXZv+uiXKbQRiXdBlOwhXmhCeBP3dMu2V3sQrmy93BRptfqTOp3zIMMKFldL8Tv8Wu583ofB/1Lc7dnOSETJ3jkeZEdXho1EWxL82+IRnHzcPoVH3iXRI+eia8+RWozxSdp+dAgMBAAECgYAJjtfqT6LR/HJBQXQ9qrdFIIrjNBRYMrE8CRzCWvgGDEBJmcoU2F+3KW6lj4SGAPqvc4dDuZ0sZAZBSWDy7MmWL+Zz2z44sulxsOsb3DJqIyBSAr5D6mhrRmu7MJA5AGgDHo/2gn+9Cji2JQBHBFe18BzJdr2tIM4uAYTVB6EW8QJBAPCrnHohSDtgLSmHrbORP/cIS8OOF/M3PsYfHZ3cpdrKk2zs1rXAHJq80GlmhSQx8tezx6wt63Cph0reiHbOMRkCQQDTfYqahFR0NTFFfTBfSJKQEqoiRYMnOrjkkOOgFv6cBwYd16pnqTfNISSYkBsOcDO09qiMILW96MoJONCV458lAkEAmMrqueK9X+zMX0xjK9hwOp5Ks2lXrTKKqO+CNwGpTkFD3WhzW8oOnvJ2giPzLSqE2QqrHpW8nrcSTKcBDiQTqQJABORmjGR7P6TrWtwmfk3Ddim4XcqV2hZ1qHPhkBZ4FUvkTFRs0LENZWVa31yWA6N8zrbV90fabGYyJjx2NsFpMQJARtRflzJjWc/49nzu+om41bz9Ngg07/S8Rxe8AlZbSlCxggmp/KUBcoVgNJCa5qGsX2AvTOCXaHngp+YLtHHPBQ==";
RSA rsa = new RSA(null,publicKey);
String str = "mlVdU379GcmXAFJvtdJ5rhoLrTOg89/efKmQvCtcYnEFUdZYuivURy0T62S0jN+KCKJ70LpfX7uoBMitBixAK8sUNq9YF9GF4GmnhSJHHklsgEujz+ULwPIG3kbhYpjKCl0RQXwdjyz0htNgLn6MR6M7JQ2fAbtR6nDL1Z2W8Ig=";
String decrypt = rsa.decryptStr(str, KeyType.PublicKey);
System.out.println(decrypt);
}
/**
* 私钥加密
*/
@Test
public void test07() throws UnsupportedEncodingException {
String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDG03gR1w6i3E6h6+N9F2///BnRrkzPc7RT4qZKKl2b/rolym0EYl3QZTsIV5oQngT93TLtld7EK5svdwUabX6kzqd8yDDChZXS/E7/FrufN6Hwf9S3O3ZzkhEyd45HmRHV4aNRFsS/NviEZx83D6FR94l0SPnomvPkVqM8UnafnQIDAQAB";
String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbTeBHXDqLcTqHr430Xb//8GdGuTM9ztFPipkoqXZv+uiXKbQRiXdBlOwhXmhCeBP3dMu2V3sQrmy93BRptfqTOp3zIMMKFldL8Tv8Wu583ofB/1Lc7dnOSETJ3jkeZEdXho1EWxL82+IRnHzcPoVH3iXRI+eia8+RWozxSdp+dAgMBAAECgYAJjtfqT6LR/HJBQXQ9qrdFIIrjNBRYMrE8CRzCWvgGDEBJmcoU2F+3KW6lj4SGAPqvc4dDuZ0sZAZBSWDy7MmWL+Zz2z44sulxsOsb3DJqIyBSAr5D6mhrRmu7MJA5AGgDHo/2gn+9Cji2JQBHBFe18BzJdr2tIM4uAYTVB6EW8QJBAPCrnHohSDtgLSmHrbORP/cIS8OOF/M3PsYfHZ3cpdrKk2zs1rXAHJq80GlmhSQx8tezx6wt63Cph0reiHbOMRkCQQDTfYqahFR0NTFFfTBfSJKQEqoiRYMnOrjkkOOgFv6cBwYd16pnqTfNISSYkBsOcDO09qiMILW96MoJONCV458lAkEAmMrqueK9X+zMX0xjK9hwOp5Ks2lXrTKKqO+CNwGpTkFD3WhzW8oOnvJ2giPzLSqE2QqrHpW8nrcSTKcBDiQTqQJABORmjGR7P6TrWtwmfk3Ddim4XcqV2hZ1qHPhkBZ4FUvkTFRs0LENZWVa31yWA6N8zrbV90fabGYyJjx2NsFpMQJARtRflzJjWc/49nzu+om41bz9Ngg07/S8Rxe8AlZbSlCxggmp/KUBcoVgNJCa5qGsX2AvTOCXaHngp+YLtHHPBQ==";
RSA rsa = new RSA(privateKey,null);
String str = "url=/checkInInfo/reviewResult&token=5bce7d71-d60b-4858-a852-15380eba6c84&timestamp=1681440129945";
String base64 = rsa.encryptBase64(str, KeyType.PrivateKey);
System.out.println(base64);
System.out.println(URLEncoder.encode(base64,"UTF-8"));
System.out.println("==========PublicKey解密=============");
RSA rsa2 = new RSA(null, publicKey);
try {
System.out.println(rsa2.decryptStr(base64 + "ss", KeyType.PublicKey));
} catch (CryptoException e) {
System.out.println("发生异常===================" + e);
}
}
@Test
public void test() {
List<String> list = new ArrayList<>();
list.add("聚酯树脂");
list.add("聚酯树脂33");
list.add("4,4'-(1-甲基亚乙基)双苯酚与(氯甲基)环氧乙烷的聚合物(平均分子量≤700)");
System.out.println(JSONUtil.toJsonStr(list));
System.out.println(this.getClass().getResource("/"));
}
@Test
public void test00() throws IOException {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("template/declarationInformationExport.docx");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
byte[] byteArray = outputStream.toByteArray();
long l = System.currentTimeMillis();
for (int i = 0; i < 50000; i++) {
// InputStream inputStream2 = getClass().getClassLoader().getResourceAsStream("template/declarationInformationExport.docx");
// InputStream inputStream2 = new FileInputStream("C:\\Users\\RENCHAO\\IdeaProjects\\Test\\MyMaven\\src\\main\\resources\\template\\declarationInformationExport.docx");
InputStream inputStream2 = new ByteArrayInputStream(byteArray);
byte[] bytes = new byte[1024 * 1024];
inputStream2.read(bytes);
}
System.out.println(System.currentTimeMillis() - l);
}
@Test
public void test08() {
System.out.println(new Date());
System.out.println(Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()));
System.out.println(TimeZone.getDefault());
}
}

@ -0,0 +1,38 @@
package com.renchao.test;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserEntity {
@ExcelProperty(value = "姓名")
private String name;
@ExcelProperty(value = "年龄")
private int age;
@ExcelProperty(value = "操作时间")
private Date time;
public static void main(String[] args) {
List<UserEntity> dataList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
UserEntity userEntity = new UserEntity();
userEntity.setName("张三" + i);
userEntity.setAge(20 + i);
userEntity.setTime(new Date(System.currentTimeMillis() + i));
dataList.add(userEntity);
}
EasyExcel.write("C:/Users/RENCHAO/Desktop/资料/easyexcel-user1.xls", UserEntity.class).sheet("用户信息").doWrite(dataList);
}
}

@ -0,0 +1,123 @@
package com.renchao.test;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import lombok.Data;
import org.hibernate.validator.HibernateValidator;
public class ValidationUtil {
/**
* 开启快速结束模式 failFast (true)
*/
private static final Validator validator = Validation.byProvider(HibernateValidator.class).configure().failFast(false).buildValidatorFactory().getValidator();
/**
* 校验对象
*
* @param t bean
* @param groups 校验组
* @return ValidResult
*/
public static <T> ValidResult validateBean(T t,Class<?>...groups) {
ValidResult result = new ValidResult();
Set<ConstraintViolation<T>> violationSet = validator.validate(t,groups);
boolean hasError = violationSet != null && violationSet.size() > 0;
result.setHasErrors(hasError);
if (hasError) {
for (ConstraintViolation<T> violation : violationSet) {
result.addError(violation.getPropertyPath().toString(), violation.getMessage());
}
}
return result;
}
/**
* 校验bean的某一个属性
*
* @param obj bean
* @param propertyName 属性名称
* @return ValidResult
*/
public static <T> ValidResult validateProperty(T obj, String propertyName) {
ValidResult result = new ValidResult();
Set<ConstraintViolation<T>> violationSet = validator.validateProperty(obj, propertyName);
boolean hasError = violationSet != null && violationSet.size() > 0;
result.setHasErrors(hasError);
if (hasError) {
for (ConstraintViolation<T> violation : violationSet) {
result.addError(propertyName, violation.getMessage());
}
}
return result;
}
/**
* 校验结果类
*/
@Data
static class ValidResult {
/**
* 是否有错误
*/
private boolean hasErrors;
/**
* 错误信息
*/
private List<ErrorMessage> errors;
public ValidResult() {
this.errors = new ArrayList<>();
}
public boolean hasErrors() {
return hasErrors;
}
public void setHasErrors(boolean hasErrors) {
this.hasErrors = hasErrors;
}
/**
* 获取所有验证信息
* @return 集合形式
*/
public List<ErrorMessage> getAllErrors() {
return errors;
}
/**
* 获取所有验证信息
* @return 字符串形式
*/
public String getErrors(){
StringBuilder sb = new StringBuilder();
for (ErrorMessage error : errors) {
sb.append(error.getPropertyPath()).append(":").append(error.getMessage()).append(" \n");
}
return sb.toString();
}
public void addError(String propertyName, String message) {
this.errors.add(new ErrorMessage(propertyName, message));
}
}
@Data
static class ErrorMessage {
private String propertyPath;
private String message;
public ErrorMessage() {
}
public ErrorMessage(String propertyPath, String message) {
this.propertyPath = propertyPath;
this.message = message;
}
}
}

@ -0,0 +1,15 @@
package com.renchao.utils;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
public class RsaUtil {
private static final RSA PUB_RSA = new RSA(null, "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMdrstRnZe65DiLQtGUuA33JKdwf4P4oBSd0itbUYmn/8N4Z9kVKap/yLZhXU8JRoI7yJLizR0iszlllbrRor+HBhB56NaHnZ1zTshELII/QXLCbqfevVDOcfvMs5OGa7pThxn3r08H7RCdko9xVWYDZvf3xJzrGe9Zj//zbe5ywIDAQAB");
public static String decryptStr(String content) {
return PUB_RSA.decryptStr(content, KeyType.PublicKey);
}
}

@ -0,0 +1,226 @@
package com.renchao.word;
import com.microsoft.schemas.office.office.CTLock;
import com.microsoft.schemas.vml.CTGroup;
import com.microsoft.schemas.vml.CTShape;
import com.microsoft.schemas.vml.CTShapetype;
import com.microsoft.schemas.vml.CTTextPath;
import com.microsoft.schemas.vml.STExt;
import com.microsoft.schemas.vml.STTrueFalse;
import org.apache.poi.util.IOUtils;
import org.apache.poi.wp.usermodel.HeaderFooterType;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.junit.Test;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPicture;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class XWPFDocumentDemo {
private static byte[] b = null;
public static void main(String[] args) throws IOException {
// File file = new File("C:\\Users\\RENCHAO\\Desktop\\新建文件夹\\allReviewInfoTemplate.docx");
FileOutputStream outputStream = new FileOutputStream("C:\\Users\\RENCHAO\\Desktop\\新建文件夹\\abcd.zip");
// b 22530
long l = System.currentTimeMillis();
ZipOutputStream out = new ZipOutputStream(outputStream);
for (int i = 0; i < 1000; i++) {
extracted(out, i);
}
out.close();
outputStream.close();
System.out.println(System.currentTimeMillis() - l);
}
private static byte[] getByte() throws IOException {
if (b != null) {
return b;
}
InputStream is = new FileInputStream("C:\\Users\\RENCHAO\\Desktop\\aaa.docx");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
b = outputStream.toByteArray();
return b;
}
private static void extracted(ZipOutputStream out, int i) throws IOException {
XWPFDocument doc = new XWPFDocument(new FileInputStream("C:\\Users\\RENCHAO\\Desktop\\aaa.docx"));
// XWPFDocument doc = new XWPFDocument(new ByteArrayInputStream(getByte()));
List<XWPFTable> tables = doc.getTables();
List<XWPFTableCell> cells;
List<XWPFTableRow> rows;
// 委托单位
rows = tables.get(0).getRows();
cells = rows.get(0).getTableCells();
cells.get(1).setText("My测试的委托单位");
// 违规货物信息
rows = tables.get(1).getRows();
cells = rows.get(0).getTableCells();
cells.get(1).setText("inOutType");
cells.get(3).setText("goodsName");
cells.get(5).setText("goodsId");
cells = rows.get(1).getTableCells();
cells.get(1).setText("unNo");
cells.get(3).setText("composition");
cells.get(5).setText("transportNameCn");
cells = rows.get(2).getTableCells();
cells.get(1).setText("dangerType");
cells.get(3).setText("packageClass");
cells.get(5).setText("chemicalName");
cells = rows.get(3).getTableCells();
cells.get(1).setText("remarks");
// 危规货物属性
rows = tables.get(2).getRows();
cells = rows.get(0).getTableCells();
cells.get(1).setText("hsCode");
cells.get(3).setText("ciqName");
cells.get(5).setText("ciqNo");
cells = rows.get(1).getTableCells();
cells.get(1).setText("goodsAttr");
cells.get(3).setText("packSign");
// 输出到本地目录
// FileOutputStream fos = new FileOutputStream("C:\\Users\\RENCHAO\\Desktop\\新建文件夹\\aaa\\AAA.docx");
// fos.flush();
// fos.close();
out.putNextEntry(new ZipEntry("aaaaa" + i + ".docx"));
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
doc.write(arrayOutputStream);
out.write(arrayOutputStream.toByteArray());
doc.close();
}
@Test
public void test02() throws IOException {
XWPFDocument doc = new XWPFDocument(new FileInputStream("C:\\Users\\RENCHAO\\Desktop\\DeclarationInformationExport.docx"));
waterMarkDocxDocument(doc, "CCIC 2023-06");
doc.write(new FileOutputStream("C:\\Users\\RENCHAO\\Desktop\\DIE.docx"));
doc.close();
}
/**
* 为文档添加水印<br />
* 实现参考了{@link org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy#createWatermark(String)}
*
* @param doc 需要被处理的docx文档对象
* @param waterMark 水印内容
*/
private void waterMarkDocxDocument(XWPFDocument doc, String waterMark) {
// 获取文档的所有页眉,如果文档没有页眉,则新建一个默认页眉
List<XWPFHeader> headerList = new ArrayList<>(doc.getHeaderList());
if (headerList.isEmpty()) {
headerList.add(doc.createHeader(HeaderFooterType.DEFAULT));
}
// 遍历所有的header,这里包含所有的分页
for (XWPFHeader header : headerList) {
int size = header.getParagraphs().size();
if (size == 0) {
header.createParagraph();
}
XWPFParagraph paragraph = header.getParagraphArray(0);
CTP ctp = paragraph.getCTP();
byte[] rsidR = doc.getDocument().getBody().getPArray(0).getRsidR();
byte[] rsidRDefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault();
ctp.setRsidP(rsidR);
ctp.setRsidRDefault(rsidRDefault);
CTPPr ppr = ctp.addNewPPr();
ppr.addNewPStyle().setVal("Header");
// 开始加水印
CTR ctr = ctp.addNewR();
CTRPr ctrpr = ctr.addNewRPr();
ctrpr.addNewNoProof();
CTGroup group = CTGroup.Factory.newInstance();
CTShapetype shapeType = group.addNewShapetype();
CTTextPath shapeTypeTextPath = shapeType.addNewTextpath();
shapeTypeTextPath.setOn(STTrueFalse.T);
shapeTypeTextPath.setFitshape(STTrueFalse.T);
CTLock lock = shapeType.addNewLock();
lock.setExt(STExt.VIEW);
CTShape shape = group.addNewShape();
shape.setId("PowerPlusWaterMarkObject");
shape.setSpid("_x0000_s102");
shape.setType("#_x0000_t136");
// 设置形状样式(旋转,位置,相对路径等参数)
shape.setStyle(getShapeStyle());
shape.setFillcolor("#d8d8d8");
// 字体设置为实心
shape.setStroked(STTrueFalse.FALSE);
// 绘制文本的路径
CTTextPath shapeTextPath = shape.addNewTextpath();
// 设置文本字体与大小
shapeTextPath.setStyle("font-family:Arial;font-size:1pt");
shapeTextPath.setString(waterMark);
CTPicture pict = ctr.addNewPict();
pict.set(group);
}
}
/**
* 构建Shape的样式参数
*/
private String getShapeStyle() {
// 文本path绘制的定位方式
return "position: " + "absolute" +
";margin-top: " + 0 +
";width: " + "415pt" +
";height: " + "207.5pt" +
";z-index: " + "-251654144" +
";mso-wrap-edited: " + "f" +
";mso-position-horizontal: " + "center" +
";mso-position-horizontal-relative: " + "margin" +
";mso-position-vertical-relative: " + "margin" +
";mso-position-vertical: " + "center" +
";rotation: " + 315;
}
}

@ -0,0 +1,550 @@
package com.jiuyv.business.domain;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.jiuyv.common.annotation.Excel;
import com.jiuyv.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 货物申报信息对象 tbl_goods_declare_info
*
* @author jiuyv
* @date 2023-02-07
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class TblGoodsDeclareInfo extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
private Long id;
/**
* 物料编号
*/
@Excel(name = "物料编号")
@NotBlank(message = "物料编号不能为空")
private String goodsId;
/**
* 版本
*/
@Excel(name = "版本")
private Long version;
/**
* 委托单位ID
*/
@Excel(name = "委托单位ID")
@NotBlank(message = "委托单位ID不能为空")
private String delegateId;
/**
* 委托单位名称
*/
@Excel(name = "委托单位名称")
@NotBlank(message = "委托单位名称不能为空")
private String delegateName;
/**
* 货物名称
*/
@Excel(name = "货物名称")
@NotBlank(message = "货物名称不能为空")
private String goodsName;
/**
* 出入境标识
*/
@Excel(name = "出入境标识")
@NotBlank(message = "出入境标识不能为空")
private String inOutType;
/**
* $column.columnComment
*/
@Excel(name = "联合国编号")
@NotBlank(message = "UN编号不能为空")
private String unNo;
/**
* 成分含量
*/
@Excel(name = "成分含量")
@NotBlank(message = "成分含量不能为空")
private String composition;
/**
* 正式运输名称
*/
@Excel(name = "正式运输名称")
private String transportNameCn;
/**
* 危险类别
*/
@Excel(name = "危险类别")
private String dangerType;
/**
* 包装类别
*/
@Excel(name = "包装类别")
private String packageClass;
/**
* 关联危险品货物ID
*/
@Excel(name = "关联危险品货物ID")
private Long goodsInfoId;
/**
* 化学品名称
*/
@Excel(name = "化学品名称")
@NotBlank(message = "化学品名称不能为空")
private String chemicalName;
/**
* 关联危化品目录ID
*/
@Excel(name = "关联危化品目录ID")
private Long catalogueInfoId;
/**
* 备注
*/
@Excel(name = "备注")
private String remarks;
/**
* HS编码
*/
@Excel(name = "HS编码")
@NotBlank(message = "HS编码不能为空")
private String hsCode;
/**
* 检验检疫名称
*/
@Excel(name = "检验检疫名称")
private String ciqName;
/**
* CIQ代码
*/
@Excel(name = "CIQ代码")
private String ciqNo;
/**
* 关联CIQ危险化学品ID
*/
@Excel(name = "关联CIQ危险化学品ID")
@NotNull(message = "关联CIQ危险化学品ID不能为空")
private Long ciqGoodsInfoId;
/**
* 货物属性
*/
@Excel(name = "货物属性")
private String goodsAttr;
/**
* 包装UN标记
*/
@Excel(name = "包装UN标记")
private String packSign;
/**
* 状态(0正常 1废除)
*/
@Excel(name = "状态", readConverterExp = "0=正常,1=废除")
private String status;
/**
* 数据状态(0存在 2删除)
*/
@Excel(name = "数据状态", readConverterExp = "0=存在,2=删除")
private String dataStatus;
/**
* rectoken
*/
@Excel(name = "rectoken")
private String recToken;
/**
* 创建人id
*/
@Excel(name = "创建人id")
private Long createById;
/**
* 修改人id
*/
@Excel(name = "修改人id")
private Long updateById;
/**
* $column.columnComment
*/
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private String rsv1;
/**
* $column.columnComment
*/
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private String rsv2;
/**
* $column.columnComment
*/
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private String rsv3;
/**
* 关联分类鉴定信息ID
*/
@Excel(name = "关联分类鉴定信息ID")
private Long dangerIdentifyInfoId;
/**
* 货代公司ID
*/
@Excel(name = "货代公司ID")
private Long agentId;
/**
* 货代公司
*/
@Excel(name = "货代公司")
private String agentName;
private String recTokenEt;
/**
* 随附文件
*/
private List<MultipartFile> files;
/**
* 文件信息
*/
private List<TblAttachFileInfo> attachFileInfos;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setGoodsId(String goodsId) {
this.goodsId = goodsId;
}
public String getGoodsId() {
return goodsId;
}
public void setVersion(Long version) {
this.version = version;
}
public Long getVersion() {
return version;
}
public void setDelegateId(String delegateId) {
this.delegateId = delegateId;
}
public String getDelegateId() {
return delegateId;
}
public void setDelegateName(String delegateName) {
this.delegateName = delegateName;
}
public String getDelegateName() {
return delegateName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
public String getGoodsName() {
return goodsName;
}
public void setInOutType(String inOutType) {
this.inOutType = inOutType;
}
public String getInOutType() {
return inOutType;
}
public void setUnNo(String unNo) {
this.unNo = unNo;
}
public String getUnNo() {
return unNo;
}
public void setComposition(String composition) {
this.composition = composition;
}
public String getComposition() {
return composition;
}
public void setTransportNameCn(String transportNameCn) {
this.transportNameCn = transportNameCn;
}
public String getTransportNameCn() {
return transportNameCn;
}
public void setDangerType(String dangerType) {
this.dangerType = dangerType;
}
public String getDangerType() {
return dangerType;
}
public void setPackageClass(String packageClass) {
this.packageClass = packageClass;
}
public String getPackageClass() {
return packageClass;
}
public void setGoodsInfoId(Long goodsInfoId) {
this.goodsInfoId = goodsInfoId;
}
public Long getGoodsInfoId() {
return goodsInfoId;
}
public void setChemicalName(String chemicalName) {
this.chemicalName = chemicalName;
}
public String getChemicalName() {
return chemicalName;
}
public void setCatalogueInfoId(Long catalogueInfoId) {
this.catalogueInfoId = catalogueInfoId;
}
public Long getCatalogueInfoId() {
return catalogueInfoId;
}
public void setRemarks(String remarks) {
this.remarks = remarks;
}
public String getRemarks() {
return remarks;
}
public void setHsCode(String hsCode) {
this.hsCode = hsCode;
}
public String getHsCode() {
return hsCode;
}
public void setCiqName(String ciqName) {
this.ciqName = ciqName;
}
public String getCiqName() {
return ciqName;
}
public void setCiqNo(String ciqNo) {
this.ciqNo = ciqNo;
}
public String getCiqNo() {
return ciqNo;
}
public void setCiqGoodsInfoId(Long ciqGoodsInfoId) {
this.ciqGoodsInfoId = ciqGoodsInfoId;
}
public Long getCiqGoodsInfoId() {
return ciqGoodsInfoId;
}
public void setGoodsAttr(String goodsAttr) {
this.goodsAttr = goodsAttr;
}
public String getGoodsAttr() {
return goodsAttr;
}
public void setPackSign(String packSign) {
this.packSign = packSign;
}
public String getPackSign() {
return packSign;
}
public void setStatus(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
public void setDataStatus(String dataStatus) {
this.dataStatus = dataStatus;
}
public String getDataStatus() {
return dataStatus;
}
public void setRecToken(String recToken) {
this.recToken = recToken;
}
public String getRecToken() {
return recToken;
}
public void setCreateById(Long createById) {
this.createById = createById;
}
public Long getCreateById() {
return createById;
}
public void setUpdateById(Long updateById) {
this.updateById = updateById;
}
public Long getUpdateById() {
return updateById;
}
public void setRsv1(String rsv1) {
this.rsv1 = rsv1;
}
public String getRsv1() {
return rsv1;
}
public void setRsv2(String rsv2) {
this.rsv2 = rsv2;
}
public String getRsv2() {
return rsv2;
}
public void setRsv3(String rsv3) {
this.rsv3 = rsv3;
}
public String getRsv3() {
return rsv3;
}
public void setDangerIdentifyInfoId(Long dangerIdentifyInfoId) {
this.dangerIdentifyInfoId = dangerIdentifyInfoId;
}
public Long getDangerIdentifyInfoId() {
return dangerIdentifyInfoId;
}
public void setAgentId(Long agentId) {
this.agentId = agentId;
}
public Long getAgentId() {
return agentId;
}
public String getRecTokenEt() {
return recTokenEt;
}
public void setRecTokenEt(String recTokenEt) {
this.recTokenEt = recTokenEt;
}
public List<MultipartFile> getFiles() {
return files;
}
public void setFiles(List<MultipartFile> files) {
this.files = files;
}
public List<TblAttachFileInfo> getAttachFileInfos() {
return attachFileInfos;
}
public void setAttachFileInfos(List<TblAttachFileInfo> attachFileInfos) {
this.attachFileInfos = attachFileInfos;
}
public String getAgentName() {
return agentName;
}
public void setAgentName(String agentName) {
this.agentName = agentName;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE).append("id", getId())
.append("goodsId", getGoodsId()).append("version", getVersion()).append("delegateId", getDelegateId())
.append("delegateName", getDelegateName()).append("goodsName", getGoodsName())
.append("inOutType", getInOutType()).append("unNo", getUnNo()).append("composition", getComposition())
.append("transportNameCn", getTransportNameCn()).append("dangerType", getDangerType())
.append("packageClass", getPackageClass()).append("goodsInfoId", getGoodsInfoId())
.append("chemicalName", getChemicalName()).append("catalogueInfoId", getCatalogueInfoId())
.append("remarks", getRemarks()).append("hsCode", getHsCode()).append("ciqName", getCiqName())
.append("ciqNo", getCiqNo()).append("ciqGoodsInfoId", getCiqGoodsInfoId())
.append("goodsAttr", getGoodsAttr()).append("packSign", getPackSign()).append("status", getStatus())
.append("dataStatus", getDataStatus()).append("recToken", getRecToken())
.append("createById", getCreateById()).append("createBy", getCreateBy())
.append("createTime", getCreateTime()).append("updateById", getUpdateById())
.append("updateBy", getUpdateBy()).append("updateTime", getUpdateTime()).append("rsv1", getRsv1())
.append("rsv2", getRsv2()).append("rsv3", getRsv3())
.append("dangerIdentifyInfoId", getDangerIdentifyInfoId()).append("agentId", getAgentId()).toString();
}
}

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
<!--
name:指定kbase的名称,可以任意,但是需要唯一
packages:指定规则文件的目录,需要根据实际情况填写,否则无法加载到规则文件
default:指定当前kbase是否为默认
-->
<kbase name="myKbase1" packages="rules" default="true">
<!--
name:指定ksession的名称,可以任意,但需要唯一
default:指定当前session是否为默认
-->
<ksession name="ksession-rule" default="true"/>
</kbase>
</kmodule>

@ -0,0 +1,137 @@
// 图书优惠规则
package book.discount
import com.renchao.drools.Order
import java.util.List
import java.util.Set
import java.lang.Integer
import java.util.ArrayList
import org.springframework.util.CollectionUtils
// 规则一:所购图书总价在100元以下的没有优惠
rule "book_discount_1"
when
$order: Order(originalPrice < 100) // 匹配模式,到规则引擎中(工作内存)查找Order对象,命名为$order
$str: String(true)
then
$order.setRealPrice($order.getOriginalPrice());
System.out.println("成功匹配到规则一,所购图书总价在100元以下无优惠");
System.out.println(">>>>>" + $str);
List<String> list = $order.getStringList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
for (String s : list) {
if (s.equals("bbb")){
System.out.println(s);
}
}
insert(list);
end
// 规则二:所购图书总价在100~200的优惠20元
rule "book_discount_2"
when
$order: Order(originalPrice >= 100 && originalPrice < 200)
then
Double originalPrice = $order.getOriginalPrice();
System.out.println(">>>>>>>>" + originalPrice);
$order.setRealPrice($order.getOriginalPrice() - 20);
System.out.println("成功匹配到规则二,所购图书总价在100~200元之间");
end
// 规则三:所购图书总价在200~300元的优惠50元
rule "book_discount_3"
when
$order: Order(originalPrice >= 200 && originalPrice < 300)
then
$order.setRealPrice($order.getOriginalPrice() - 50);
System.out.println("成功匹配到规则三,所购图书总价在200~300元之间");
end
// 规则四:所购图书总价在300元及以上的优惠100元
rule "book_discount_4"
when
$order: Order(originalPrice >= 300)
then
$order.setRealPrice($order.getOriginalPrice() - 100);
System.out.println("成功匹配到规则四,所购图书总价在300元及以上");
end
// 规则5:所购图书总价在300元及以上的优惠100元
rule "book_discount_5"
when
$list: List(size > 0)
then
String join = String.join(",",$list);
System.out.println("list不是空的。。。。》》》》》" + join);
end
// 规则6
rule "book_discount_6"
when
$list: List(CollectionUtils.isEmpty($list))
then
System.out.println("list是空的。。。。》》》》》hehe");
end
// 规则7
rule "book_discount_7"
when
$set: Set(true)
$list: List(true)
$i: Integer($i > 6)
then
$set.add($i * 2);
$list.add($i);
end
// 规则8
rule "book_discount_8"
when
$list: List(size > 1)
then
System.out.println($list);
List<String> list = new ArrayList<>();
insert(list);
end
// 规则9
rule "book_discount_9"
salience 10
activation-group "test"
// no-loop true
when
$order: Order(stringList == null/* && stringList.size() == 0*/)
then
System.out.println("999 Order的list是空的。。。。》》》》》");
// update($order)
end
// 规则9
rule "book_discount_10"
salience 20
activation-group "test"
no-loop true
when
$order: Order(stringList != null && stringList.size() == 0)
then
System.out.println("10 10 Order的list是空的。。。。》》》》》");
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
$order.setStringList(list);
update($order)
end
// 规则11
rule "book_discount_11"
when
$order: Order(stringList != null && stringList.size() == 2)
then
System.out.println("11 11 Order的list是空的。。。。》》》》》");
// update($order)
end

@ -0,0 +1,70 @@
package com.renchao;
import org.junit.Test;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class RSADemo {
public static void main(String[] args) throws Exception {
// 生成密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
System.out.println(publicKey.getFormat());
PrivateKey privateKey = keyPair.getPrivate();
// 加密消息
String message = "Hello RSA";
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedMessage = cipher.doFinal(message.getBytes());
System.out.println("Encrypted message: " + Base64.getEncoder().encodeToString(encryptedMessage));
// 解密密文
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedMessage = cipher.doFinal(encryptedMessage);
System.out.println("Decrypted message: " + new String(decryptedMessage));
}
@Test
public void test01() throws Exception {
// String publicKeyStr = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDG03gR1w6i3E6h6+N9F2///BnRrkzPc7RT4qZKKl2b/rolym0EYl3QZTsIV5oQngT93TLtld7EK5svdwUabX6kzqd8yDDChZXS/E7/FrufN6Hwf9S3O3ZzkhEyd45HmRHV4aNRFsS/NviEZx83D6FR94l0SPnomvPkVqM8UnafnQIDAQAB";
String publicKeyStr = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCUAPS4EiEkyKAay4dRY7hWuyTSewj3X1g9NXj6832Eup0VE+xxGfsDiU5xlZBenFcLT8nn88q3mYit5DowuwxTCmem2TIAfkxdAnZ4vm7ndVbugQTu3TDB5R7LIGRjNF62lfwzYc7ywJFHVH/7dVfh4/uaijjQeDhznlBxM57NgwIDAQAB";
String privateKeyStr = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbTeBHXDqLcTqHr430Xb//8GdGuTM9ztFPipkoqXZv+uiXKbQRiXdBlOwhXmhCeBP3dMu2V3sQrmy93BRptfqTOp3zIMMKFldL8Tv8Wu583ofB/1Lc7dnOSETJ3jkeZEdXho1EWxL82+IRnHzcPoVH3iXRI+eia8+RWozxSdp+dAgMBAAECgYAJjtfqT6LR/HJBQXQ9qrdFIIrjNBRYMrE8CRzCWvgGDEBJmcoU2F+3KW6lj4SGAPqvc4dDuZ0sZAZBSWDy7MmWL+Zz2z44sulxsOsb3DJqIyBSAr5D6mhrRmu7MJA5AGgDHo/2gn+9Cji2JQBHBFe18BzJdr2tIM4uAYTVB6EW8QJBAPCrnHohSDtgLSmHrbORP/cIS8OOF/M3PsYfHZ3cpdrKk2zs1rXAHJq80GlmhSQx8tezx6wt63Cph0reiHbOMRkCQQDTfYqahFR0NTFFfTBfSJKQEqoiRYMnOrjkkOOgFv6cBwYd16pnqTfNISSYkBsOcDO09qiMILW96MoJONCV458lAkEAmMrqueK9X+zMX0xjK9hwOp5Ks2lXrTKKqO+CNwGpTkFD3WhzW8oOnvJ2giPzLSqE2QqrHpW8nrcSTKcBDiQTqQJABORmjGR7P6TrWtwmfk3Ddim4XcqV2hZ1qHPhkBZ4FUvkTFRs0LENZWVa31yWA6N8zrbV90fabGYyJjx2NsFpMQJARtRflzJjWc/49nzu+om41bz9Ngg07/S8Rxe8AlZbSlCxggmp/KUBcoVgNJCa5qGsX2AvTOCXaHngp+YLtHHPBQ==";
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Cipher rsa = Cipher.getInstance("RSA");
// 加密 Hello
KeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr));
PublicKey publicKey = keyFactory.generatePublic(keySpec);
System.out.println(publicKey);
System.out.println(System.currentTimeMillis());
rsa.init(Cipher.ENCRYPT_MODE, publicKey);
String str = "Admin1234," + System.currentTimeMillis();
byte[] bytes = rsa.doFinal(str.getBytes(StandardCharsets.UTF_8));
String s = Base64.getEncoder().encodeToString(bytes);
System.out.println(s);
// 解密
// KeySpec keySpec2 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr));
// PrivateKey privateKey = keyFactory.generatePrivate(keySpec2);
// rsa.init(Cipher.DECRYPT_MODE, privateKey);
// byte[] bytes1 = rsa.doFinal(Base64.getDecoder().decode(s));
// System.out.println("解密后:" + new String(bytes1, StandardCharsets.UTF_8));
}
}

@ -0,0 +1,141 @@
package com.renchao;
import org.junit.Test;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegExDemo {
public static void main(String[] args) {
String str = "2.3+5.1+8";
String[] split = str.split("\\+");
System.out.println(Arrays.toString(split));
}
@Test
public void test() {
String str = "2.3/5.1+8Dd./";
System.out.println(str.replaceAll("[+/a-zA-Z]", ""));
}
@Test
public void test02() {
// String str = "3,UN1263,Ⅲ";
// String str = "2.2/UN1956/P200";
// String str = "6.1/3/8,UN2740,Ⅰ";
String str = "2.3(8);UN1079;I";
// String str = "正式运输名称:丁醇类;联合国编号:UN1120;危险货物类别:3;包装类:II;海洋污染物:否。";
// String str = "正式运输名称:四氯化硅;联合国编号:UN1818;危险货物类别:2.3(8);包装类:II;海洋污染物:否。";
// String str = "正式运输名称:四氯化硅;联合国编号:UN1818;危险货物类别:8;包装类:II;海洋污染物:否。";
// Pattern pattern = Pattern.compile("类别[::]((\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?)([((]((\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?))[))])?)");
// Pattern pattern = Pattern.compile("(\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?)([((]((\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?))[))])?");
// Pattern pattern = Pattern.compile("([Uu][Nn])(\\d{4})");
// Pattern pattern = Pattern.compile("[IⅠⅡⅢ]{1,3}");
Pattern pattern = Pattern.compile("[,,::;;/](I{1,3}|([ⅠⅡⅢ]))|\\d{4}[,,::;;/]([123])");
Matcher matcher = pattern.matcher(str);
if (matcher.find()) {
System.out.println("0位置:" + matcher.group());
System.out.println("1位置:" + matcher.group(1));
System.out.println("2位置:" + matcher.group(2));
System.out.println("3位置:" + matcher.group(3));
System.out.println("4位置:" + matcher.group(4));
System.out.println("5位置:" + matcher.group(5));
System.out.println("6位置:" + matcher.group(6));
System.out.println("7位置:" + matcher.group(7));
System.out.println("8位置:" + matcher.group(8));
}
}
@Test
public void test04() {
String str = "url=/checkInInfo/reviewResult&token=2bce7d71-d60b-4858-a852-15380eba6c84&timestamp=1574821902392";
Pattern pattern = Pattern.compile("[0-9a-f]+(-[0-9a-f]+){4}");
Matcher matcher = pattern.matcher(str);
if (matcher.find()) {
System.out.println("0位置:" + matcher.group());
// System.out.println("1位置:" + matcher.group(1));
}
}
@Test
public void test05() {
// String str = "乙酸乙酯30-50%,2-乙基-2-(羟甲基)-1,3-丙二醇与1,3-二异氰酸根合甲基苯和2,2'-氧二(乙醇)的聚合物1-10%";
// String str = "2-乙基-2-(羟甲基)-1,3-丙二醇与1,3-二异氰酸根合甲基苯和2,2'-氧二(乙醇)的聚合物25-40%;甲乙酮10-20%;乙酸乙酯10-20%;2-乙基-2-(羟甲基)-1,3-丙二醇与1,3-二异氰酸根合甲基苯和2,2'-氧二(乙醇)的聚合物25-40%";
String str = "乙酸乙酯30-50%,甲乙酮10-20%,4,4'-(1-甲基亚乙基)二苯酚与2,2'-[(1-甲基亚乙基)二(4,1-亚苯基氧亚甲基)]二(环氧乙烷)的聚合物2.5-10%";
// Pattern pattern = Pattern.compile("([^%]+)%"); (?=[0-9>>≥<<≤-]+%)
// Pattern pattern = Pattern.compile("([^%]+)([0-9>>≥<<≤-]+%)");
// Pattern pattern = Pattern.compile("(-?\\d[,-]?\\d?\\D+)([0-9>>≥<<≤-]+%)");
Pattern pattern = Pattern.compile("([;;,,]?)(((-?\\d[,-]?\\d?['-]*)?[^%0-9>>≥.<<≤-]+)+)([0-9>>≥.<<≤-]+%)");
// Pattern pattern = Pattern.compile("-?\\d[,-]?\\d?['-]*");
Pattern pattern2 = Pattern.compile("(\\d+)%");
Matcher matcher = pattern.matcher(str);
Map<String, Integer> map = new HashMap<>();
while (matcher.find()) {
String group = matcher.group();
System.out.println("0===>>> " + group);
System.out.println("1===>>> " + matcher.group(1));
System.out.println("2===>>> " + matcher.group(2));
System.out.println("3===>>> " + matcher.group(3));
System.out.println("4===>>> " + matcher.group(4));
System.out.println("5===>>> " + matcher.group(5));
Matcher matcher2 = pattern2.matcher(group);
if (matcher2.find()) {
map.put(group, Integer.valueOf(matcher2.group(1)));
}
System.out.println("==============");
}
// System.out.println(map);
}
@Test
public void test055() {
String str = "乙酸乙酯30-50%,2-乙基-2-(羟甲基)-1,3-丙二醇与1,3-二异氰酸根合甲基苯和2,2'-氧二(乙醇)的聚合物1-10%";
Pattern pattern = Pattern.compile("[\\u4e00-\\u9fa5]+");
Matcher matcher = pattern.matcher(str);
while (matcher.find()) {
System.out.println(matcher.group());
}
}
@Test
public void test06() {
String content = "3.3+2.3+8";
String regStr = "(\\d(\\.\\d)?\\+)*(\\d(\\.\\d)?)";
// String content = "1A2/Y2.2/100/22 CN/C230409 PI:007";
// String regStr = "[0-9A-Z]{2,3}/[A-Z]\\d\\.\\d/.+";
// String regStr = "([^/]+/)*[^/]+";
boolean b = Pattern.matches(regStr, content);
System.out.println("整体匹配= " + b);
}
@Test
public void test07() {
String str = "a,b,c";
String[] split = str.split("[,,]");
for (String s : split) {
System.out.println(s);
}
}
@Test
public void test08() {
Pattern p = Pattern.compile("产品.*含");
String str = "产品中主要含1-甲氧基-2-乙酰";
String s = p.matcher(str).replaceAll("");
System.out.println(s);
}
}

@ -0,0 +1,37 @@
package com.renchao;
import org.junit.Test;
import java.io.IOException;
import java.util.UUID;
public class StringFormatDemo {
public static void main(String[] args) {
String format = String.format("aaa%s,bbb%s,ccc%s", "111", 222, "333");
System.out.println(format);
String s = String.format("%s/%s/%s/%s.pdf", "c:", "a", "b", UUID.randomUUID());
System.out.println(s);
}
@Test
public void test01() {
StringFormatDemo demo = new StringFormatDemo();
String t = demo.t();
System.out.println(t);
}
public String t() {
try {
// int i =4 / 0;
return "try";
} catch (Exception e) {
return "catch";
} finally {
// return "finally";
System.out.println("ccc");
}
}
}

@ -0,0 +1,52 @@
package com.renchao;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipDemo {
public static void main(String[] args) throws Exception {
// 创建一个ZipOutputStream类的对象
ZipOutputStream out = new ZipOutputStream(new FileOutputStream("C:\\Users\\RENCHAO\\Desktop\\新建文件夹\\output.zip"));
// 创建一个File对象,表示要压缩的文件夹
File file = new File("C:\\Users\\RENCHAO\\Desktop\\aa");
// 调用compress方法,将file对象压缩到out对象中
compress(out, file, "");
// 关闭out对象
out.close();
}
public static void compress(ZipOutputStream out, File file, String base) throws Exception {
if (file.isDirectory()) {
// 如果file对象是一个目录,则获取该目录下的所有文件和目录
File[] files = file.listFiles();
// 如果files数组不为空,则遍历files数组中的所有元素
if (files != null && files.length > 0) {
for (File f : files) {
// 递归调用compress方法,将f对象压缩到out对象中
compress(out, f, base + file.getName() + "/");
}
}
} else {
// 如果file对象是一个文件,则创建一个ZipEntry对象,表示该文件在压缩包中的路径和名称
ZipEntry entry = new ZipEntry(base + file.getName());
// 将entry对象添加到out对象中
out.putNextEntry(entry);
// 创建一个FileInputStream类的对象
FileInputStream in = new FileInputStream(file);
// 创建一个byte数组
byte[] buffer = new byte[1024];
int len;
// 读取in对象中的数据到buffer数组中,直到读取完毕
while ((len = in.read(buffer)) > 0) {
// 将buffer数组中的数据写入到out对象中
out.write(buffer, 0, len);
}
// 关闭in对象
in.close();
}
}
}

@ -0,0 +1,28 @@
package com.renchao;
import org.junit.Test;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class ZonedDateTimeDemo {
@Test
public void test09() throws InterruptedException {
// long timestamp = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
// System.out.println(timestamp);
//// Thread.sleep(5000);
// System.out.println(System.currentTimeMillis() - timestamp);
// System.out.println(30<<1<<2);
ZonedDateTime zonedDateTime = ZonedDateTime.of(2023, 4, 13, 12, 57, 0, 0, ZoneId.systemDefault());
long l = zonedDateTime.toInstant().toEpochMilli();
System.out.println(l);
System.out.println(System.currentTimeMillis());
System.out.println((System.currentTimeMillis() - 1681365120000L) /1000/60);
System.out.println(System.currentTimeMillis());
}
}

@ -0,0 +1,40 @@
<?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">
<parent>
<artifactId>agile-data</artifactId>
<groupId>com.jiuyv</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>agile-data-api</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,29 @@
package com.jiuyv.agile.data.api;
import com.jiuyv.agile.data.web.R;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
/**
* 敏捷API
*
* @author yulei
*/
public interface AgileApi {
/**
* 发布接口
* @param id
* @return
*/
@PostMapping(value = "/dataApis/{id}/release")
public R<Object> releaseDataApi(@PathVariable Long id);
/**
* 注销接口
* @param id
* @return
*/
@PostMapping(value = "/dataApis/{id}/cancel")
public R<Object> cancelDataApi(@PathVariable Long id);
}

@ -0,0 +1,37 @@
package com.jiuyv.agile.data.dto.enums;
public enum AlgorithmCrypto {
BASE64("1", "BASE64加密"),
MD5("2", "MD5加密"),
SHA_1("3", "SHA_1加密"),
SHA_256("4", "SHA_256加密"),
AES("5", "AES加密"),
DES("6", "DES加密");
private final String key;
private final String val;
AlgorithmCrypto(String key, String val) {
this.key = key;
this.val = val;
}
public String getKey() {
return key;
}
public String getVal() {
return val;
}
public static AlgorithmCrypto getAlgorithmCrypto(String algorithmCrypt) {
for (AlgorithmCrypto type : AlgorithmCrypto.values()) {
if (type.key.equals(algorithmCrypt)) {
return type;
}
}
return BASE64;
}
}

@ -0,0 +1,33 @@
package com.jiuyv.agile.data.dto.enums;
public enum CipherType {
REGEX("1", "正则替换"),
ALGORITHM("2", "加密算法");
private final String key;
private final String val;
CipherType(String key, String val) {
this.key = key;
this.val = val;
}
public String getKey() {
return key;
}
public String getVal() {
return val;
}
public static CipherType getCipherType(String cipherType) {
for (CipherType type : CipherType.values()) {
if (type.key.equals(cipherType)) {
return type;
}
}
return REGEX;
}
}

@ -0,0 +1,83 @@
package com.jiuyv.agile.data.dto.enums;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
public enum ParamType {
String("1", "字符串"),
Integer("2", "整型"),
Float("3", "浮点型"),
Date("4", "时间"),
List("5", "集合");
private final String key;
private final String val;
ParamType(String key, String val) {
this.key = key;
this.val = val;
}
public String getKey() {
return key;
}
public String getVal() {
return val;
}
public static Object parse(ParamType paramType, Object obj) {
if (obj == null) {
return null;
}
switch (paramType) {
case String:
try {
return (java.lang.String)obj;
} catch (Exception e) {
throw new RuntimeException("参数值[" + obj + "]不是" + String.getVal() + "数据类型]");
}
case Float:
try {
return new BigDecimal(obj.toString()).doubleValue();
} catch (Exception e) {
throw new RuntimeException("参数值[" + obj + "]不是" + Float.getVal() + "数据类型]");
}
case Integer:
try {
return (java.lang.Integer)obj;
} catch (Exception e) {
throw new RuntimeException("参数值[" + obj + "]不是" + Integer.getVal() + "数据类型]");
}
case List:
try {
return (java.util.List<?>)obj;
} catch (Exception e) {
throw new RuntimeException("参数值[" + obj + "]不是" + List.getVal() + "数据类型]");
}
case Date:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return sdf.parse(obj.toString());
} catch (Exception e) {
try {
return sdf.parse(obj.toString());
} catch (Exception ex) {
throw new RuntimeException("参数值[" + obj + "]不是" + Date.getVal() + "数据类型]");
}
}
}
return null;
}
public static ParamType getParamType(String paramType) {
for (ParamType type : ParamType.values()) {
if (type.key.equals(paramType)) {
return type;
}
}
return String;
}
}

@ -0,0 +1,39 @@
package com.jiuyv.agile.data.dto.enums;
public enum RegexCrypto {
CHINESE_NAME("1", "中文姓名"),
ID_CARD("2", "身份证号"),
FIXED_PHONE("3", "固定电话"),
MOBILE_PHONE("4", "手机号码"),
ADDRESS("5", "地址"),
EMAIL("6", "电子邮箱"),
BANK_CARD("7", "银行卡号"),
CNAPS_CODE("8", "公司开户银行联号");
private final String key;
private final String val;
RegexCrypto(String key, String val) {
this.key = key;
this.val = val;
}
public String getKey() {
return key;
}
public String getVal() {
return val;
}
public static RegexCrypto getRegexCrypto(String regexCrypt) {
for (RegexCrypto type : RegexCrypto.values()) {
if (type.key.equals(regexCrypt)) {
return type;
}
}
return CHINESE_NAME;
}
}

@ -0,0 +1,50 @@
package com.jiuyv.agile.data.dto.enums;
public enum WhereType {
EQUALS("1", "=", "等于"),
NOT_EQUALS("2", "!=", "不等于"),
LIKE("3", "LIKE", "全模糊查询"),
LIKE_LEFT("4", "LIKE", "左模糊查询"),
LIKE_RIGHT("5", "LIKE", "右模糊查询"),
GREATER_THAN("6", ">", "大于"),
GREATER_THAN_EQUALS("7", ">=", "大于等于"),
LESS_THAN("8", "<", "小于"),
LESS_THAN_EQUALS("9", "<=", "小于等于"),
NULL("10", "IS NULL", "是否为空"),
NOT_NULL("11", "IS NOT NULL", "是否不为空"),
IN("12", "IN", "IN");
private final String type;
private final String key;
private final String desc;
WhereType(String type, String key, String desc) {
this.type = type;
this.key = key;
this.desc = desc;
}
public String getType() {
return type;
}
public String getKey() {
return key;
}
public String getDesc() {
return desc;
}
public static WhereType getWhereType(String whereType) {
for (WhereType type : WhereType.values()) {
if (type.type.equals(whereType)) {
return type;
}
}
return EQUALS;
}
}

@ -0,0 +1,46 @@
package com.jiuyv.agile.data.dto.request;
import java.util.Objects;
/**
* <p>
* 数据API脱敏信息表 查询实体
* </p>
*/
public class ApiMaskQuery extends BaseQueryParams {
private static final long serialVersionUID=1L;
private String maskName;
private Long apiId;
public String getMaskName() {
return maskName;
}
public void setMaskName(String maskName) {
this.maskName = maskName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ApiMaskQuery that = (ApiMaskQuery) o;
return maskName.equals(that.maskName) && apiId.equals(that.apiId);
}
@Override
public int hashCode() {
return Objects.hash(maskName, apiId);
}
public Long getApiId() {
return apiId;
}
public void setApiId(Long apiId) {
this.apiId = apiId;
}
}

@ -0,0 +1,82 @@
package com.jiuyv.agile.data.dto.request;
import java.io.Serializable;
import java.util.List;
public class BaseQueryParams implements Serializable {
private static final long serialVersionUID = 1L;
// 关键字
private String keyword;
// 当前页码
private Integer pageNum = 1;
// 分页条数
private Integer pageSize = 20;
// 排序
private List<OrderItem> orderList;
// 数据权限
private String dataScope;
public class OrderItem{
private String column;
private boolean asc;
public String getColumn() {
return column;
}
public void setColumn(String column) {
this.column = column;
}
public boolean isAsc() {
return asc;
}
public void setAsc(boolean asc) {
this.asc = asc;
}
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public List<OrderItem> getOrderList() {
return orderList;
}
public void setOrderList(List<OrderItem> orderList) {
this.orderList = orderList;
}
public String getDataScope() {
return dataScope;
}
public void setDataScope(String dataScope) {
this.dataScope = dataScope;
}
}

@ -0,0 +1,96 @@
package com.jiuyv.agile.data.dto.request;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 数据源连接信息Model
*/
public class DbSchema implements Serializable {
private static final long serialVersionUID=1L;
/**
* 主机
*/
@NotBlank(message = "主机不能为空")
private String host;
/**
* 端口
*/
@NotNull(message = "端口不能为空")
private Integer port;
/**
* 用户名
*/
@NotBlank(message = "用户名不能为空")
private String username;
/**
* 密码
*/
@NotBlank(message = "密码不能为空")
private String password;
/**
* 数据库
*/
private String dbName;
/**
* 服务名
*/
private String sid;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDbName() {
return dbName;
}
public void setDbName(String dbName) {
this.dbName = dbName;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
}

@ -0,0 +1,97 @@
package com.jiuyv.agile.data.dto.request;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
/**
* 执行配置信息Model
*/
public class ExecuteConfig implements Serializable {
private static final long serialVersionUID=1L;
/**
* 数据源
*/
@NotBlank(message = "数据源不能为空")
private Long sourceId;
/**
* 配置方式
*/
@NotNull(message = "配置方式不能为空")
private String configType;
/**
* 数据库表主键
*/
private Long tableId;
/**
* 数据库表
*/
private String tableName;
/**
* 表字段列表
*/
@Valid
private List<FieldParam> fieldParams;
/**
* 解析SQL
*/
private String sqlText;
public Long getSourceId() {
return sourceId;
}
public void setSourceId(Long sourceId) {
this.sourceId = sourceId;
}
public String getConfigType() {
return configType;
}
public void setConfigType(String configType) {
this.configType = configType;
}
public Long getTableId() {
return tableId;
}
public void setTableId(Long tableId) {
this.tableId = tableId;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public List<FieldParam> getFieldParams() {
return fieldParams;
}
public void setFieldParams(List<FieldParam> fieldParams) {
this.fieldParams = fieldParams;
}
public String getSqlText() {
return sqlText;
}
public void setSqlText(String sqlText) {
this.sqlText = sqlText;
}
}

@ -0,0 +1,167 @@
package com.jiuyv.agile.data.dto.request;
import java.io.Serializable;
/**
* 字段信息Model
*/
public class FieldParam implements Serializable {
private static final long serialVersionUID=1L;
/**
* 列名
*/
private String columnName;
/**
* 数据类型
*/
private String dataType;
/**
* 数据长度
*/
private Integer dataLength;
/**
* 数据精度
*/
private Integer dataPrecision;
/**
* 数据小数位
*/
private Integer dataScale;
/**
* 是否主键
*/
private String columnKey;
/**
* 是否允许为空
*/
private String columnNullable;
/**
* 列的序号
*/
private Integer columnPosition;
/**
* 列默认值
*/
private String dataDefault;
/**
* 列注释
*/
private String columnComment;
/**
* 作为请求参数
*/
private String reqable;
/**
* 作为返回参数
*/
private String resable;
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public Integer getDataLength() {
return dataLength;
}
public void setDataLength(Integer dataLength) {
this.dataLength = dataLength;
}
public Integer getDataPrecision() {
return dataPrecision;
}
public void setDataPrecision(Integer dataPrecision) {
this.dataPrecision = dataPrecision;
}
public Integer getDataScale() {
return dataScale;
}
public void setDataScale(Integer dataScale) {
this.dataScale = dataScale;
}
public String getColumnKey() {
return columnKey;
}
public void setColumnKey(String columnKey) {
this.columnKey = columnKey;
}
public String getColumnNullable() {
return columnNullable;
}
public void setColumnNullable(String columnNullable) {
this.columnNullable = columnNullable;
}
public Integer getColumnPosition() {
return columnPosition;
}
public void setColumnPosition(Integer columnPosition) {
this.columnPosition = columnPosition;
}
public String getDataDefault() {
return dataDefault;
}
public void setDataDefault(String dataDefault) {
this.dataDefault = dataDefault;
}
public String getColumnComment() {
return columnComment;
}
public void setColumnComment(String columnComment) {
this.columnComment = columnComment;
}
public String getReqable() {
return reqable;
}
public void setReqable(String reqable) {
this.reqable = reqable;
}
public String getResable() {
return resable;
}
public void setResable(String resable) {
this.resable = resable;
}
}

@ -0,0 +1,53 @@
package com.jiuyv.agile.data.dto.request;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 字段信息Model
*/
public class FieldRule implements Serializable {
private static final long serialVersionUID=1L;
/**
* 字段名称
*/
@NotBlank(message = "字段名称不能为空")
private String fieldName;
/**
* 脱敏类型
*/
@NotNull(message = "脱敏类型不能为空")
private String cipherType;
/**
* 规则类型
*/
@NotNull(message = "规则类型不能为空")
private String cryptType;
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public String getCipherType() {
return cipherType;
}
public void setCipherType(String cipherType) {
this.cipherType = cipherType;
}
public String getCryptType() {
return cryptType;
}
public void setCryptType(String cryptType) {
this.cryptType = cryptType;
}
}

@ -0,0 +1,52 @@
package com.jiuyv.agile.data.dto.request;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 限流信息Model
*/
public class RateLimit implements Serializable {
private static final long serialVersionUID=1L;
/**
* 是否限流:0:否,1:是
*/
@NotNull(message = "是否限流不能为空")
private String enable;
/**
* 请求次数默认5次
*/
private Integer times = 5;
/**
* 请求时间范围默认60秒
*/
private Integer seconds = 60;
public String getEnable() {
return enable;
}
public void setEnable(String enable) {
this.enable = enable;
}
public Integer getTimes() {
return times;
}
public void setTimes(Integer times) {
this.times = times;
}
public Integer getSeconds() {
return seconds;
}
public void setSeconds(Integer seconds) {
this.seconds = seconds;
}
}

@ -0,0 +1,111 @@
package com.jiuyv.agile.data.dto.request;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 请求参数信息Model
*/
public class ReqParam implements Serializable {
private static final long serialVersionUID=1L;
/**
* 参数名称
*/
@NotBlank(message = "参数名称不能为空")
private String paramName;
/**
* 是否为空
*/
@NotNull(message = "是否为空不能为空")
private String nullable;
/**
* 描述
*/
@NotBlank(message = "描述不能为空")
private String paramComment;
/**
* 操作符
*/
@NotNull(message = "操作符不能为空")
private String whereType;
/**
* 参数类型
*/
@NotNull(message = "参数类型不能为空")
private String paramType;
/**
* 示例值
*/
@NotBlank(message = "示例值不能为空")
private String exampleValue;
/**
* 默认值
*/
@NotBlank(message = "默认值不能为空")
private String defaultValue;
public String getParamName() {
return paramName;
}
public void setParamName(String paramName) {
this.paramName = paramName;
}
public String getNullable() {
return nullable;
}
public void setNullable(String nullable) {
this.nullable = nullable;
}
public String getParamComment() {
return paramComment;
}
public void setParamComment(String paramComment) {
this.paramComment = paramComment;
}
public String getWhereType() {
return whereType;
}
public void setWhereType(String whereType) {
this.whereType = whereType;
}
public String getParamType() {
return paramType;
}
public void setParamType(String paramType) {
this.paramType = paramType;
}
public String getExampleValue() {
return exampleValue;
}
public void setExampleValue(String exampleValue) {
this.exampleValue = exampleValue;
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
}

@ -0,0 +1,34 @@
package com.jiuyv.agile.data.dto.request;
import java.io.Serializable;
/**
* API请求参数
*
* @author yulei
*/
public class ReqVo implements Serializable {
/** 报文内容 */
private String body;
/** 签名 */
private String sign;
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
}

@ -0,0 +1,82 @@
package com.jiuyv.agile.data.dto.response;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 返回参数信息Model
*/
public class ResParam implements Serializable {
private static final long serialVersionUID=1L;
/**
* 字段名称
*/
@NotBlank(message = "字段名称不能为空")
private String fieldName;
/**
* 描述
*/
@NotBlank(message = "描述不能为空")
private String fieldComment;
/**
* 数据类型
*/
@NotNull(message = "数据类型不能为空")
private String dataType;
/**
* 示例值
*/
@NotBlank(message = "示例值不能为空")
private String exampleValue;
/**
* 字段别名
*/
private String fieldAliasName;
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public String getFieldComment() {
return fieldComment;
}
public void setFieldComment(String fieldComment) {
this.fieldComment = fieldComment;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public String getExampleValue() {
return exampleValue;
}
public void setExampleValue(String exampleValue) {
this.exampleValue = exampleValue;
}
public String getFieldAliasName() {
return fieldAliasName;
}
public void setFieldAliasName(String fieldAliasName) {
this.fieldAliasName = fieldAliasName;
}
}

@ -0,0 +1,55 @@
package com.jiuyv.agile.data.dto.response;
import com.jiuyv.agile.data.web.ResultCode;
import java.io.Serializable;
/**
* API返回参数
*
* @author yulei
*/
public class RespVo implements Serializable {
/** 返回码*/
private Integer respCode;
/** 返回说明 */
private String respDesc;
public RespVo() {
}
public RespVo(Integer code, String message) {
this.respCode = code;
this.respDesc = message;
}
public static RespVo ok(){
return new RespVo(ResultCode.SUCCESS.getCode(),ResultCode.SUCCESS.getMessage());
}
public Integer getRespCode() {
return respCode;
}
public void setRespCode(Integer respCode) {
this.respCode = respCode;
}
public String getRespDesc() {
return respDesc;
}
public void setRespDesc(String respDesc) {
this.respDesc = respDesc;
}
@Override
public String toString() {
return "RespVo{" +
"respCode='" + respCode + '\'' +
", respDesc='" + respDesc + '\'' +
'}';
}
}

@ -0,0 +1,20 @@
package com.jiuyv.agile.data.web;
/**
* @ClassName : IErrorCode
* @Description : 常用API返回对接接口
* @Author : sky
* @Date: 2023-06-06 16:33
*/
public interface IErrorCode {
/**
* 返回码
*/
Integer getCode();
/**
* 返回信息
*/
String getMessage();
}

@ -0,0 +1,107 @@
package com.jiuyv.agile.data.web;
import com.jiuyv.agile.data.web.constant.HttpStatus;
import java.io.Serializable;
/**
* 响应信息主体
*
* @author admin
*/
public class R<T> implements Serializable
{
private static final long serialVersionUID = 1L;
/** 成功 */
public static final int SUCCESS = HttpStatus.SUCCESS;
/** 失败 */
public static final int FAIL = HttpStatus.ERROR;
private int code;
private String msg;
private T data;
public static <T> R<T> ok()
{
return restResult(null, SUCCESS, "操作成功");
}
public static <T> R<T> ok(T data)
{
return restResult(data, SUCCESS, "操作成功");
}
public static <T> R<T> ok(T data, String msg)
{
return restResult(data, SUCCESS, msg);
}
public static <T> R<T> fail()
{
return restResult(null, FAIL, "操作失败");
}
public static <T> R<T> fail(String msg)
{
return restResult(null, FAIL, msg);
}
public static <T> R<T> fail(T data)
{
return restResult(data, FAIL, "操作失败");
}
public static <T> R<T> fail(T data, String msg)
{
return restResult(data, FAIL, msg);
}
public static <T> R<T> fail(int code, String msg)
{
return restResult(null, code, msg);
}
private static <T> R<T> restResult(T data, int code, String msg)
{
R<T> apiResult = new R<>();
apiResult.setCode(code);
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}
public int getCode()
{
return code;
}
public void setCode(int code)
{
this.code = code;
}
public String getMsg()
{
return msg;
}
public void setMsg(String msg)
{
this.msg = msg;
}
public T getData()
{
return data;
}
public void setData(T data)
{
this.data = data;
}
}

@ -0,0 +1,70 @@
package com.jiuyv.agile.data.web;
/**
* 常用API返回对象
* Created by macro on 2019/4/19.
*/
public enum ResultCode implements IErrorCode {
/** success : 成功. */
SUCCESS(200, "操作成功"),
ACCEPTED(202, "请求已经被接受"),
NO_CONTENT(204, "操作已经执行成功,但是没有返回数据"),
MOVED_PERM(301, "资源已被移除"),
SEE_OTHER(303, "重定向"),
NOT_MODIFIED(304, "资源没有被修改"),
BAD_REQUEST(400, "参数列表错误(缺少,格式不匹配)"),
UNAUTHORIZED(401, "未授权"),
FORBIDDEN(403, "访问受限,授权过期"),
NOT_FOUND(404, "资源,服务未找到"),
BAD_METHOD(405, "不允许的http方法"),
CONFLICT(409, "资源冲突,或者资源被锁"),
ERROR(500, "系统内部错误"),
NOT_IMPLEMENTED(501, "接口未实现"),
//PROCESS("process", "订单处理中,请稍后查询"),
//FAILED("failed", "操作失败"),
//VALIDATE_FAILED("input_not_valid", "输入不合法"),
//REPEAT_SUBMIT("duplicate_req_id", "请勿重复提交"),
//AUTHORIZATION_LIMIT("authorization limit", "权限限制"),
//UNAUTHORIZED("401", "暂未登录或token已经过期"),
//
//ILLEGALLY("illegally request", "不合法请求"),
///** input_not_valid : 输入不合法. */
//INPUT_NOT_VALID("input_not_valid", "输入不合法"),
///** duplicate_req_id : 重复请求. */
//DUPLICATE_REQ_ID("duplicate_req_id","重复请求"),
///** duplicate_order : 订单重复. */
//DUPLICATE_ORDER("duplicate_order","订单重复"),
//
///** product_limit : 商品限购 */
//SYS_ERROR("sys_error","系统异常"),
///** business_error : 调用返回非成功 */
//BUSINESS_ERROR("business_error","调用返回非成功"),
///** sql_lock_exception : 乐观锁异常*/
//SQL_EXCEPTION("sql_lock_exception","乐观锁异常"),
//
///** file_not_exist,请输入正确的权益号 */
//FILE_NOT_EXIST("file_not_exist","文件不存在"),
///** file_no_data,文件内容为空 */
//FILE_NO_DATA("file_no_data","文件内容为空");
;
private Integer code;
private String message;
private ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}
@Override
public Integer getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}

@ -0,0 +1,89 @@
package com.jiuyv.agile.data.web.constant;
/**
* 返回状态码
*
* @author admin
*/
public class HttpStatus
{
/**
* 操作成功
*/
public static final int SUCCESS = 200;
/**
* 对象创建成功
*/
public static final int CREATED = 201;
/**
* 请求已经被接受
*/
public static final int ACCEPTED = 202;
/**
* 操作已经执行成功,但是没有返回数据
*/
public static final int NO_CONTENT = 204;
/**
* 资源已被移除
*/
public static final int MOVED_PERM = 301;
/**
* 重定向
*/
public static final int SEE_OTHER = 303;
/**
* 资源没有被修改
*/
public static final int NOT_MODIFIED = 304;
/**
* 参数列表错误(缺少,格式不匹配)
*/
public static final int BAD_REQUEST = 400;
/**
* 未授权
*/
public static final int UNAUTHORIZED = 401;
/**
* 访问受限,授权过期
*/
public static final int FORBIDDEN = 403;
/**
* 资源,服务未找到
*/
public static final int NOT_FOUND = 404;
/**
* 不允许的http方法
*/
public static final int BAD_METHOD = 405;
/**
* 资源冲突,或者资源被锁
*/
public static final int CONFLICT = 409;
/**
* 不支持的数据,媒体类型
*/
public static final int UNSUPPORTED_TYPE = 415;
/**
* 系统内部错误
*/
public static final int ERROR = 500;
/**
* 接口未实现
*/
public static final int NOT_IMPLEMENTED = 501;
}

@ -0,0 +1,76 @@
<?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">
<parent>
<artifactId>agile-data</artifactId>
<groupId>com.jiuyv</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>agile-data-service</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.jiuyv</groupId>
<artifactId>agile-data-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 表示依赖不会传递 -->
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- postgresql连接 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,30 @@
package com.jiuyv.agile.data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* @author yulei
*/
@SpringBootApplication
@EnableScheduling
public class AgileDataApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(AgileDataApplication.class);
public static void main(String[] args) {
SpringApplication.run(AgileDataApplication.class, args);
LOGGER.info("(♥◠‿◠)ノ゙ 模块启动成功゙ \n"+
" ___ ___ ___ \n"+
" |\\ \\ |\\ \\ / /| \n"+
" \\ \\ \\ \\ \\ \\/ / / \n"+
" __ \\ \\ \\ \\ \\ / / \n"+
" |\\ \\\\_\\ \\ \\/ / / \n"+
" \\ \\________\\ __/ / / \n"+
" \\|________| |\\___/ / \n"+
" \\|___|/ \n"
);
}
}

@ -0,0 +1,211 @@
package com.jiuyv.agile.data.common.constants;
public class DataConstant {
/**
* Oauth2安全相关常量
*/
public enum Security {
//请求头TOKEN名称
TOKENHEADER("gatewayToken"),
//请求头TOKEN值
TOKENVALUE("datax:gateway:123456"),
//OAUTH2请求头
AUTHORIZATION("Authorization"),
//OAUTH2令牌类型
TOKENTYPE("bearer "),
//security授权角色前缀
ROLEPREFIX("ROLE_");
Security(String val){
this.val = val;
}
private final String val;
public String getVal() {
return val;
}
}
/**
* 通用的是否
*/
public enum TrueOrFalse {
FALSE("0",false),
TRUE("1",true);
TrueOrFalse(String key, boolean val){
this.key = key;
this.val = val;
}
private final String key;
private final boolean val;
public String getKey() {
return key;
}
public boolean getVal() {
return val;
}
}
/**
* 用户认证返回额外信息
*/
public enum UserAdditionalInfo {
LICENSE("license", "datax"),
USER("user", "用户"),
USERID("user_id", "用户ID"),
USERNAME("username", "用户名"),
NICKNAME("nickname", "用户昵称"),
DEPT("user_dept", "用户部门"),
ROLE("user_role", "用户角色"),
POST("user_post", "用户岗位");
UserAdditionalInfo(String key, String val){
this.key = key;
this.val = val;
}
private final String key;
private final String val;
public String getKey() {
return key;
}
public String getVal() {
return val;
}
}
/**
* 通用的启用禁用状态
*/
public enum EnableState {
DISABLE("0","禁用"),
ENABLE("1","启用");
EnableState(String key, String val){
this.key = key;
this.val = val;
}
private final String key;
private final String val;
public String getKey() {
return key;
}
public String getVal() {
return val;
}
}
/**
* 流程审核状态
*/
public enum AuditState{
WAIT("1","待提交"),
BACK("2", "已退回"),
AUDIT("3","审核中"),
AGREE("4","通过"),
REJECT("5","不通过"),
CANCEL("6", "已撤销");
AuditState(String key, String val){
this.key = key;
this.val = val;
}
private final String key;
private final String val;
public String getKey() {
return key;
}
public String getVal() {
return val;
}
}
/**
* 菜单类型
*/
public enum MenuType{
MODULE("0","模块"),
MENU("1","菜单"),
BUTTON("2","按钮");
MenuType(String key, String val){
this.key = key;
this.val = val;
}
private final String key;
private final String val;
public String getKey() {
return key;
}
public String getVal() {
return val;
}
}
/**
* 数据范围
*/
public enum DataScope{
ALL("1","全部数据权限"),
CUSTOM("2","自定义数据权限"),
DEPT("3","本部门数据权限"),
DEPTANDCHILD("4","本部门及以下数据权限"),
SELF("5","仅本人数据权限");
DataScope(String key, String val){
this.key = key;
this.val = val;
}
private final String key;
private final String val;
public String getKey() {
return key;
}
public String getVal() {
return val;
}
}
/**
* Api状态
*/
public enum ApiState{
WAIT("1","待发布"),
RELEASE("2","已发布"),
CANCEL("3","已下线");
ApiState(String key, String val){
this.key = key;
this.val = val;
}
private final String key;
private final String val;
public String getKey() {
return key;
}
public String getVal() {
return val;
}
}
}

@ -0,0 +1,33 @@
package com.jiuyv.agile.data.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jiuyv.agile.data.handler.MappingHandlerMapping;
import com.jiuyv.agile.data.handler.RequestHandler;
import com.jiuyv.agile.data.handler.RequestInterceptor;
import com.jiuyv.agile.data.service.impl.ApiMappingEngine;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@Configuration
public class ApiMappingConfig {
@Bean
public MappingHandlerMapping mappingHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping,
ApiMappingEngine apiMappingEngine,
ObjectMapper objectMapper) {
MappingHandlerMapping mappingHandlerMapping = new MappingHandlerMapping();
mappingHandlerMapping.setHandler(requestHandler(apiMappingEngine, objectMapper));
mappingHandlerMapping.setRequestMappingHandlerMapping(requestMappingHandlerMapping);
return mappingHandlerMapping;
}
@Bean
public RequestHandler requestHandler(ApiMappingEngine apiMappingEngine, ObjectMapper objectMapper) {
RequestHandler handler = new RequestHandler();
handler.setApiMappingEngine(apiMappingEngine);
handler.setObjectMapper(objectMapper);
handler.setRequestInterceptor(new RequestInterceptor());
return handler;
}
}

@ -0,0 +1,43 @@
package com.jiuyv.agile.data.controller;
import com.jiuyv.agile.data.service.AgileApiService;
import com.jiuyv.agile.data.web.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 敏捷API控制
*
* @author yulei
*/
@RestController
public class AgileApiController {
@Autowired
private AgileApiService agileApiService;
/**
* 发布接口
* @param id
* @return
*/
@PostMapping(value = "/dataApis/{id}/release")
public R<Object> releaseDataApi(@PathVariable Long id){
agileApiService.releaseDataApi(id);
return R.ok();
}
/**
* 注销接口
* @param id
* @return
*/
@PostMapping(value = "/dataApis/{id}/cancel")
public R<Object> cancelDataApi(@PathVariable Long id){
agileApiService.cancelDataApi(id);
return R.ok();
}
}

@ -0,0 +1,7 @@
package com.jiuyv.agile.data.controller;
import com.jiuyv.agile.data.api.AgileApi;
public interface ts extends AgileApi {
}

@ -0,0 +1,31 @@
package com.jiuyv.agile.data.controller;
import com.jiuyv.agile.data.api.AgileApi;
import com.jiuyv.agile.data.service.AgileApiService;
import com.jiuyv.agile.data.web.R;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class tt implements AgileApi {
/**
* 发布接口
*
* @param id
* @return
*/
@Override
public R<Object> releaseDataApi(Long id) {
return null;
}
/**
* 注销接口
*
* @param id
* @return
*/
@Override
public R<Object> cancelDataApi(Long id) {
return null;
}
}

@ -0,0 +1,12 @@
package com.jiuyv.agile.data.database;
public interface DataSourceFactory {
/**
* 创建数据源实例
*
* @param property
* @return
*/
DbQuery createDbQuery(DbQueryProperty property);
}

@ -0,0 +1,134 @@
package com.jiuyv.agile.data.database;
public class DbColumn {
/**
* 列名
*/
private String colName;
/**
* 数据类型
*/
private String dataType;
/**
* 数据长度
*/
private String dataLength;
/**
* 数据精度
*/
private String dataPrecision;
/**
* 数据小数位
*/
private String dataScale;
/**
* 是否主键
*/
private Boolean colKey;
/**
* 是否允许为空
*/
private Boolean nullable;
/**
* 列的序号
*/
private Integer colPosition;
/**
* 列默认值
*/
private String dataDefault;
/**
* 列注释
*/
private String colComment;
public String getColName() {
return colName;
}
public void setColName(String colName) {
this.colName = colName;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public String getDataLength() {
return dataLength;
}
public void setDataLength(String dataLength) {
this.dataLength = dataLength;
}
public String getDataPrecision() {
return dataPrecision;
}
public void setDataPrecision(String dataPrecision) {
this.dataPrecision = dataPrecision;
}
public String getDataScale() {
return dataScale;
}
public void setDataScale(String dataScale) {
this.dataScale = dataScale;
}
public Boolean getColKey() {
return colKey;
}
public void setColKey(Boolean colKey) {
this.colKey = colKey;
}
public Boolean getNullable() {
return nullable;
}
public void setNullable(Boolean nullable) {
this.nullable = nullable;
}
public Integer getColPosition() {
return colPosition;
}
public void setColPosition(Integer colPosition) {
this.colPosition = colPosition;
}
public String getDataDefault() {
return dataDefault;
}
public void setDataDefault(String dataDefault) {
this.dataDefault = dataDefault;
}
public String getColComment() {
return colComment;
}
public void setColComment(String colComment) {
this.colComment = colComment;
}
}

@ -0,0 +1,48 @@
package com.jiuyv.agile.data.database;
import org.springframework.jdbc.core.RowMapper;
/**
* 表数据查询接口
*/
public interface DbDialect {
RowMapper<DbTable> tableMapper();
RowMapper<DbColumn> columnMapper();
/**
* 获取指定表的所有列
*
* @param dbName
* @param tableName
* @return
*/
String columns(String dbName, String tableName);
/**
* 获取数据库下的 所有表
*
* @param dbName
* @return
*/
String tables(String dbName);
/**
* 构建 分页 sql
*
* @param sql
* @param offset
* @param count
* @return
*/
String buildPaginationSql(String sql, long offset, long count);
/**
* 包装 count sql
*
* @param sql
* @return
*/
String count(String sql);
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save