Spring Boot 实践

#### 一、Spring Boot重要特性

  1. 独立的Spring应用程序,嵌入式Tomcat/Jetty容器,无需部署War包
  2. 尽可能使用自动化配置,Spring Auto Configuration
  3. 提供一批'starter' POM 简化Maven及Gradle配置
  4. 提供一系列可以用到生产环境的应用度量、健康检查等特性(Actuator)

二、Spring Boot 快速上手

访问http://start.spring.io/,使用SPRING INITIALIZR选择需要的模块快速初始化

![spring start](images/start.spring.io.jpg "start.spring.io")

选择Web模块,快速创建项目

```xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>

1<groupId>com.example</groupId> 2<artifactId>myproject</artifactId> 3<version>0.0.1-SNAPSHOT</version> 4 5<!-- Inherit defaults from Spring Boot --> 6<parent> 7 <groupId>org.springframework.boot</groupId> 8 <artifactId>spring-boot-starter-parent</artifactId> 9 <version>1.5.3.RELEASE</version> 10</parent> 11 12<!-- Add typical dependencies for a web application --> 13<dependencies> 14 <dependency> 15 <groupId>org.springframework.boot</groupId> 16 <artifactId>spring-boot-starter-web</artifactId> 17 </dependency>x 18</dependencies> 19 20<!-- Package as an executable jar --> 21<build> 22 <plugins> 23 <plugin> 24 <groupId>org.springframework.boot</groupId> 25 <artifactId>spring-boot-maven-plugin</artifactId> 26 </plugin> 27 </plugins> 28</build>
</project>

``` 生成的项目结构

```

myproject +- pom.xml +- src +- main +- java | +- com.example.myproject | +- Application.Java |
+- resources | +- application.properties | +- test +- java | +- com.example.myproject | +- ApplicationTests.java

```

三、项目分层结构及模块划分方式

![项目分层](images/springbootlayer.jpg "项目分层")

1. 名称规范
  • **包名规范**

``` com.pingan.haofang.${产品}.${模块}.${层次}.${className}.java ```

  • **特殊类名规范**

Spring配置类: configuration/*Configuration,例如WebConfiguration/DatasourceConfiguration

Properties类: properties/*Properties,例如FtpProperties

dao类:dao/*Dao或者dao/*Repository

service类:service/*Service

  • ** 分层命名规范 **

domain: 数据库PO

dao/repository:数据库访问层

service:业务逻辑层

dto:数据传输对象

constants:枚举常量

controller:web控制器

form:web请求对象

vo:web响应对象

validator:校验器

batch:批处理Job类

2. 项目划分

对于每个项目可以按照如下方式进行划分为4个模块,独立为4个maven模块

  • **Parent**

负责依赖管理,公用的maven依赖

  • **Service (lib)**

包含整个项目的业务逻辑/数据访问代码

```

com.pingan.haofang +- myproject +- customer +- domain | +- Customer.java |
+- dao | +- CustomerDao.java | +- service | +- CustomerService.java | +- impl | +- CustomerServiceImpl.java | +- dto | +- CustomerDto.java | +- constants | +- CustomerConstants.java | +- CustomerStatus.java

```

  • **Exportapi (lib 或 app)**

项目对外API提供,RPC等。依赖Service,可单独部署也可打进web包进行部署。

  • **Web (app)**

项目web接口暴露代码,包括前后端接口暴露,文档,拦截器。依赖service、exportapi

``` com.pingan.haofang +- myproject +- WebApplication.java +- customer +- controller | +- CustomerController.java |
+- form | +- CustomerForm.java +- vo | +- CustomerVo.java | +- validator | +- CustomerValidator.java

```

  • **Batch (app)**

项目批处理任务,常驻进程任务或者定时任务,依赖service

``` com.pingan.haofang +- myproject +- BatchApplication.java +- customer +- batch | +- CustomerExportTask.java | +- CustomerExportRunner.java

```

四、Spring常用模块应用

1. Spring MVC
  • **接口定义规范**

``` Http Method

GET:读取数据,不允许有数据的修改等操作 列表URL设计:GET:/web/custmer 单条数据URL设计:GET:/web/customer/{custmerId} POST:新建数据 POST:/web/custmer PUT:修改数据 PUT:/web/custmer/{custormId} PUT:/web/custmer/status/{custormId} DELTE:删除数据 DELETE:/web/custmer/{custormId}

请求体与响应体

请求与响应除QueryString及PathVariable外,其余数据交互应以Json格式进行交互

Controller配置为@RestController, 前后端ContentType:application/json; charset=UTF8

```

  • **Swagger应用**

所有controller都用swagger annotation进行注解,springfox嵌入以提供接口文档及try out调试功能

  • **TraceFilter**

添加TraceFilter,对于每个请求随机生成RequestID并放入MDC进行日志打印,便于排查

  • **异常消息定义及ExceptionHandler**

自定义完善的异常处理器,按照和前端定义好的接口产生异常消息体。通过HTTP CODE定义各类状态

``` 200 成功 409 校验失败,例如非空、长度、格式等 400 客户端请求格式错误,例如不是合法的Json 401 未授权即未登录 403 无权限访问 404 不存在,未找到响应对象 500 服务器内部错误

```

异常消息体定义

```json { "errorCode": 1, // 保留错误码字段 "message": "全局异常消息", "fieldErrors": [ { "name": "名称不允许重复" }, { "desc": "描述太短" } ] }

```

2. JPA用法
  • **数据源及数据库连接池配置**

建议使用Alibaba Druid连接池配置,见 com.pingan.haofang.myproject.common.configuration.DataSourceConfiguration,

同时建议配置DruidStat,即可通过web管理数据源监测数据,见

```java

1@Bean 2public ServletRegistrationBean druidServlet(DruidStatProperties druidStatProperties) { 3 ServletRegistrationBean reg = new ServletRegistrationBean(); 4 reg.setServlet(new StatViewServlet()); 5 reg.addUrlMappings("/druid/\*"); 6 reg.addInitParameter("loginUsername", druidStatProperties.getUsername()); 7 reg.addInitParameter("loginPassword", druidStatProperties.getPassword()); 8 return reg; 9}

```

  • **继承Repository接口**

    查询可以使用QueryMethod/@Query/Example/Specification四种方式,前面三种适合做简单查询时用,Specification(即CriteriaQuery)建议在复杂查询,例如分业列表有较多筛选条件时使用

    demo见com.pingan.haofang.myproject.customer.service.impl#CustomerServiceImpl#queryList,CustomerSpecs.pageListSpec(dto)

  • **合理使用实体关联**

  • **使用Auditing**

    Auditing提供了如下四个annotation可以方便设置创建人、最后修改人、创建时间、最后修改时间。需要和@EnableJpaAuditing,AuditingEntityListener配合使用

    @CreatedBy,@LastModifiedBy,@CreatedDate,@LastModifiedDate

```java

@Bean public AuditorAware<Long> auditorProvider() { return () -> Optional.ofNullable(SearchThreadContext.getSessionVisitor()) .map(Visitor::getUserId) .orElse(0L); }

@MappedSuperclass public abstract class BaseDomain {

1@Column(name = "create\_time") 2@CreatedDate 3private Date createTime; 4 5@Column(name = "create\_by") 6@CreatedBy 7private Long createBy; 8 9@Column(name = "update\_time") 10@LastModifiedDate 11private Date updateTime; 12 13@Column(name = "update\_by") 14@LastModifiedBy 15private Long updateBy;

```

3. 使用Spring Boot Actuator/Spring Boot Admin

Spring Boot actuator可以帮助我们提供方便的健康页面、jmx等监控和排查功能。故期望所有项目的Actuator满足如下规范。

  • **Spring Boot Actuator的Context-path为/actuator**

context-path配置为统一前缀,方便未来配置内部管理域名时proxy的统一配置。

```properties

endpoints.sensitive=false endpoints.enabled=true endpoints.actuator.enabled=true endpoints.shutdown.enabled=true endpoints.shutdown.sensitive=false

management.security.enabled=false management.context-path=/actuator management.address=127.0.0.1

```

```xml

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>

```

  • **为便于管理和排查Spring Boot App,每个APP都配置spring boot admin**

引入Jar

```xml

<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>1.5.0</version> </dependency> <!--如下plugin也建议配置,主要用于生成buildinfo,方便/actuator/info返回项目的基本信息--> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <!--生成build-info文件--> <goal>build-info</goal> </goals> </execution> </executions> </plugin>
1 <!--如下插件生成git信息,包括构建的git分支,最后提交人及注释,版本号--> 2 <plugin> 3 <groupId>pl.project13.maven</groupId> 4 <artifactId>git-commit-id-plugin</artifactId> 5 <executions> 6 <execution> 7 <goals> 8 <goal>revision</goal> 9 </goals> 10 </execution> 11 </executions> 12 <configuration> 13 <!--日期格式;默认值:dd.MM.yyyy '@' HH:mm:ss z;--> 14 <dateFormat>yyyyMMddHHmmss</dateFormat> 15 <!--,构建过程中,是否打印详细信息;默认值:false;--> 16 <verbose>true</verbose> 17 <!-- ".git"文件路径;默认值:${project.basedir}/.git; --> 18 <dotGitDirectory>${project.basedir}/.git</dotGitDirectory> 19 <!--若项目打包类型为pom,是否取消构建;默认值:true;--> 20 <skipPoms>false</skipPoms> 21 <!--是否生成"git.properties"文件;默认值:false;--> 22 <generateGitPropertiesFile>true</generateGitPropertiesFile> 23 <!--指定"git.properties"文件的存放路径(相对于${project.basedir}的一个路径);--> 24 <generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties 25 </generateGitPropertiesFilename> 26 <!--".git"文件夹未找到时,构建是否失败;若设置true,则构建失败;若设置false,则跳过执行该目标;默认值:true;--> 27 <failOnNoGitDirectory>true</failOnNoGitDirectory> 28 </configuration> 29 </plugin> 30</plugins>
</build>

```

配置注册admin server地址

```properties

#spring admin ##目前st/ci将注册到ci环境的admin,ga将注册到ga环境的admin spring.boot.admin.url=http://actuator.a.pa.com/ ##如果dev及其他开发机可能网络不通,请使用下面配置 #spring.boot.admin.url=http://10.59.72.187:9596

##下面配置是spring-boot的name,配置后才能在admin有漂亮的名称,请自行取名 spring.application.name=${applicationName}

##默认如果hosts中配置了当前IP的hostname可能无法访问,所以可以加上如下设置 spring.boot.admin.client.prefer-ip=true

management.info.git.mode=full

```

  • **访问SpringAdminServer进行管理**

```

st/ci/开发: http://actuator.anhouse.com.cn/ 用户名:admin 密码:admin-st

anhouse http://actuator.an2.ipo.com/

ga: http://actuator.proxy.ipo.com/

```

![spring boot admin ui](images/springbootadminui.png "spring boot admin ui") ![spring boot admin ui](images/springbootadminui-logging.png "spring boot admin ui")

4. 校验

jsr303

functional validation

fail fast/ fail over

5. 单测
  • **内存数据库H2**

请使用内存数据库模拟数据库,初始化脚本请添加SchemaSQL,初始化数据可使用对应的data.sql,或者使用testEntityManager

详见myproject-service/src/test

  • **Mock/MockBean**

需要配置MockitoTestExecutionListener,见BaseTest

当单测测试逻辑类有依赖其他的逻辑类,这个时候如果只想测试自己的逻辑,可以使用@MockBean,mock掉依赖的逻辑类,这里mock的对象如果没有使用mockito指定 相应逻辑,则都会返回null

见com.pingan.haofang.myproject.customer.service.impl.CustomerServiceImplTest#isCustomerBuyProduct

  • **Spy/SpyBean**

需要配置MockitoTestExecutionListener,见BaseTest

与Mock和MockBean不同之处在于,mock的对象如果没有对方法使用mockito指定相应逻辑,则会执行真实代码,但是@Spy中如果先when,再ThenReturn则还是会先执行 一次mock方法的真实逻辑,可能会因为不可预知的错误而失败。

```java

// when去设置模拟返回值时,里面的方法object.callMethod()会先执行一次 when(object.callMethod()).thenReturn("result");

// 使用doReturn则不会产生上面的问题 doReturn("result").when(object).callMethod();

```

更多请参照com.pingan.haofang.myproject.customer.service.impl.CustomerServiceImplTest#getOne

6. Spring Session

@EnableRedisHttpSession

为避免redis共享时出现问题,建议设置redisNamespace区分key, 否则默认都是spring:session,不好区分, 同时按照需要设置session过期时间

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 43200, redisNamespace = "search_cloud")

``` 配置redisNamespace后的rediskey "spring:session:search_cloud:sessions:5259b7fb-c882-4f57-8d32-d967148b1338"

未配置namespace后的rediskey "spring:session:sessions:expires:c1698de0-618b-455d-a63b-b4809decb1fd"

```

7. ThreadContext

线程上下文,请扩展使用com.pingan.haofang.module.common.ThreadContext

8. 日志规范

日志请使用Spring-Boot默认提供的模板,springboot默认提供的模板已经预定义了变量,可以进行赋值扩展,主要分console-pattern和file-pattern, 两者格式相同,console-pattern还包含颜色美化,便于阅读

如果无额外appender配置,可以直接在application.properties中配置,SpringBoot提供的日志级别自定义 可按照如下配置示例扩展

``` logging.pattern.level=%X{REQUEST_ID} %p

```

但如果日志比较复杂,可使用SpringBoot提供的logback base.xml,defaults.xml进行组合配置,spring boot base提供的fileAppender不支持按时间滚动, 这块可以自己写

```xml

<?xml version="1.0" encoding="UTF-8"?> <configuration>
1<jmxConfigurator/> 2<property name="LOG\_FILE" value="${LOG\_PATH}/myproject.log"/> 3 4<property name="ADDITIONAL" value="%X{REQUEST\_ID} %X{TRACE\_ID}"/> 5<property name="LOG\_LEVEL\_PATTERN" value="${ADDITIONAL} %5p"/> 6 7<include resource="org/springframework/boot/logging/logback/defaults.xml"/> 8<include resource="org/springframework/boot/logging/logback/console-appender.xml"/> 9 10<appender name="FILE" 11 class="ch.qos.logback.core.rolling.RollingFileAppender"> 12 <encoder> 13 <pattern>${FILE\_LOG\_PATTERN}</pattern> 14 </encoder> 15 <file>${LOG\_FILE}</file> 16 <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 17 <fileNamePattern>${LOG\_FILE}.%d{yyyy-MM-dd}</fileNamePattern> 18 </rollingPolicy> 19</appender> 20 21<!--customer专用appender--> 22<appender name="customerAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> 23 <file>${LOG\_PATH}/customer.log</file> 24 <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 25 <fileNamePattern>${LOG\_PATH}/customer-%d{yyyy-MM-dd}.log</fileNamePattern> 26 </rollingPolicy> 27 <encoder> 28 <pattern>${ADDITIONAL} %d{HH:mm:ss.SSS} - %msg%n</pattern> 29 </encoder> 30</appender> 31 32<logger name="com.pingan.haofang.myproject.customer.controller.CustomerController" level="INFO" additivity="false"> 33 <appender-ref ref="customerAppender"/> 34</logger> 35 36<root level="INFO"> 37 <appender-ref ref="CONSOLE"/> 38 <appender-ref ref="FILE"/> 39</root>
</configuration>

```

日志文件路径指定,对于主程序日志,请通过配置文件或者Jvm参数指定logging.path和logging.file

五、好房Spring Boot Starter

基于各种开发场景,好房framework模块开发了若干开发组件,例如历史操作记录,批处理框架,校验工具等。需要使用请先引入如下pom

```xml

<dependencyManagement> <dependencies> <dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-framework-dependencies</artifactId> <version>1.0.0-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

```

1. pinganfang-common-module,通用工具模块

该模块主要封装了各种常用UTIL类库,Exception定义,例如StringUtils,ThreadContext,BaseException等。

```mvn

<dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-common-module</artifactId> </dependency>

```

2. pinganfang-rpc-starter, RPC封装

该模块封装了好房各业务模块通信的RPC,包括服务端开放RPC服务,以及RPC客户端调用。

```mvn

<dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-rpc-starter</artifactId> </dependency>

```

要启用RPC,请在Application.java,或者配置类上面添加@EnableHaofangRPC,并定义如下Filter

```java

@Bean @Order(Ordered.HIGHEST_PRECEDENCE) public FilterRegistrationBean rpcFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new RPCFilter()); filterRegistrationBean.addUrlPatterns("/rpc"); filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); return filterRegistrationBean; }

```

**声明RPC服务**,详见com.pingan.haofang.myproject.demo.rpc.DemoRPCExportService

```java

@RPCExporter(value = "findByIds", defaultErrorMessage = "rpc error", successCode = "0") public List<DemoDTO> findByIds(List<Integer> ids, int type) { List<DemoDTO> list = new ArrayList<DemoDTO>(); list.add(new DemoDTO(101, "demo1", Arrays.asList(1, 2, 3, 4, 5))); list.add(new DemoDTO(102, "demo2", Arrays.asList(1, 2, 3, 4, 5))); list.add(new DemoDTO(103, "demo3", Arrays.asList(1, 2, 3, 4, 5))); return list; }

```

**声明RPC客户端**,详见com.pingan.haofang.myproject.demo.rpc.DemoRPCService

```java

@RPCClient(value = "User\\User.getMobileByUserIDs", config = "rpc.user", successCode = "0000") public Map<Integer, String> getUserInfo(List<Integer> ids);

```

3. pinganfang-validator-starter, 校验器封装

该模块封装了jsr303校验器,扩展了现有的校验器,既支持hibernate validator,同时校验器可以配置fail over/fail fast等高级特性

```xml

<dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-validator-starter</artifactId> </dependency>

```

若要使用,请先在Application.java上或者配置类配置@EnableHaofangValidator

在需要校验的controller方法上配置如下注解

```java

@Valid(CustomerValidator.class) public List<CustomerVO> queryList(CustomerQueryForm form) {

```

同时写好validator

```java

@Component public class CustomerValidator {

1@ValidHandler 2public void queryList(ValidationResult result, CustomerQueryForm form) { 3 4 /\*\* 5 \* countryId为40的时候cityId不能>20 6 \*/ 7 if (form.getCountryId() == 40 && form.getCityId() > 20) { 8 result.addError(ValidationError.of("cityId", "cityId > 20")); 9 } 10}

}

```

demo见com.pingan.haofang.myproject.customer.controller.CustomerController#queryList

4. pinganfang-jpa-starter, JPA封装

该模块封装了BaseDomain, BaseRepository,对Spring Data Jpa 进行了进一步扩展

```xml

<dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-validator-starter</artifactId> </dependency>

```

  • BaseDomain封装了createTime,createBy,updateTime,updateBy,推荐在定义domain时继承

  • 对于Repository,可以继承BaseRepository,提供了众多新的数据库操作方法,例如返回Map, listMap, Java 8支持等

  • 提供了PageQueryDTO等基础类

5. pinganfang-history-starter, 历史操作记录封装

history封装了历史操作记录handler bean注册,相应切面等逻辑,但是按照何种格式记录日志,则由具体业务而定,在HistoryOpHandler中实现即可。

```xml

<dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-history-starter</artifactId> </dependency>

```

  • 1.在项目中配置注解@EnableHaofangHistory,启用history功能

  • 2.定义HistoryOpHandler

  • 3.在需要记录日志的地方配置注解HistoryOpLog,这里注解配置参数如下,需要注意

```

value: 对应的处理器方法名称 beanName: 处理器在spring中的BeanName errMessage: 异常消息 ignoreError: 如果为true,则写入日志时失败会抛出异常,否则会忽略继续执行主体流程 force: 如果为true,则不论请求是否成功均会记录日志,否则只会在正常返回时才记录日志

```

示例见 com.pingan.haofang.myproject.customer.service.impl.CustomerLogService, com.pingan.haofang.myproject.customer.service.impl.CustomerServiceImpl

6. pinganfang-batch-starter, 批处理封装

batch封装了批处理定义基础类库,支持一次性任务,常驻任务类型两种

```xml

<dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-batch-starter</artifactId> </dependency>

```

如果使用batch,请配置@EnableHaofangBatch

batch 建议两种用法

  • 一次性任务

见com.pingan.haofang.myproject.demo.batch.DemoCronTaskRunner

  • 常驻进程任务

见com.pingan.haofang.myproject.demo.batch.DemoScheduleTaskRunner

如上任务启动类均为BatchMain,如要启动某作业,启动JVM参数为-DrunnerName=${batchName}

7. pinganfang-web-common, web基础工具类库

该模块主要提供web程序要的通用基础类库Utils等

```

<dependency> <groupId>com.pingan.haofang.framework</groupId> <artifactId>pinganfang-web-common</artifactId> </dependency>

```

目前提供的基础类有

  • ContextFilter,提供请求ID生成并写入MDC,可在logback中打印

六、项目构建及部署

打包方式

``` sh build.sh ${mvn_profile}

```

打包为

``` output/myproject-web.tar.gz output/myproject-batch.tar.gz ```

包结构为

```

tar.gz +- bin | app_control.bash +- conf | logback.xml | application.properties +- myproject-web.jar

```

App启动方式

``` bash app_control.bash start|shutdown|kill|force|restart|status

start 启动app shutdown 关闭app kill 杀掉app进程 force 强制杀掉app进程 restart 重启app status 查看app状态

```

war 包使用方式

见项目myproject-web-war,目前需要将配置文件打进war包,相关配置都配置在application.properties中

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )