Browse Source

Merge branch 'dev' into master

pull/1/head
Yangkai.Shen 3 years ago
parent
commit
90de2f4935
100 changed files with 4570 additions and 761 deletions
  1. +86
    -316
      README.en.md
  2. +89
    -311
      README.md
  3. +67
    -67
      TODO.en.md
  4. +67
    -67
      TODO.md
  5. +0
    -0
      demo-activiti/.gitignore
  6. +71
    -0
      demo-activiti/pom.xml
  7. +22
    -0
      demo-activiti/src/main/java/com/xkcoding/activiti/SpringBootDemoActivitiApplication.java
  8. +56
    -0
      demo-activiti/src/main/java/com/xkcoding/activiti/config/SecurityConfiguration.java
  9. +74
    -0
      demo-activiti/src/main/java/com/xkcoding/activiti/util/SecurityUtil.java
  10. +0
    -0
      demo-activiti/src/main/resources/application.yml
  11. +0
    -0
      demo-activiti/src/main/resources/processes/team01.bpmn
  12. +0
    -0
      demo-activiti/src/test/java/com/xkcoding/activiti/SpringBootDemoActivitiApplicationTests.java
  13. +0
    -0
      demo-actuator/.gitignore
  14. +0
    -0
      demo-actuator/README.md
  15. +64
    -0
      demo-actuator/pom.xml
  16. +20
    -0
      demo-actuator/src/main/java/com/xkcoding/actuator/SpringBootDemoActuatorApplication.java
  17. +0
    -0
      demo-actuator/src/main/resources/application.yml
  18. +16
    -0
      demo-actuator/src/test/java/com/xkcoding/actuator/SpringBootDemoActuatorApplicationTests.java
  19. +0
    -0
      demo-admin/README.md
  20. +0
    -0
      demo-admin/admin-client/.gitignore
  21. +0
    -0
      demo-admin/admin-client/README.md
  22. +57
    -0
      demo-admin/admin-client/pom.xml
  23. +20
    -0
      demo-admin/admin-client/src/main/java/com/xkcoding/admin/client/SpringBootDemoAdminClientApplication.java
  24. +20
    -0
      demo-admin/admin-client/src/main/java/com/xkcoding/admin/client/controller/IndexController.java
  25. +0
    -0
      demo-admin/admin-client/src/main/resources/application.yml
  26. +16
    -0
      demo-admin/admin-client/src/test/java/com/xkcoding/admin/client/SpringBootDemoAdminClientApplicationTests.java
  27. +0
    -0
      demo-admin/admin-server/.gitignore
  28. +90
    -0
      demo-admin/admin-server/README.md
  29. +52
    -0
      demo-admin/admin-server/pom.xml
  30. +22
    -0
      demo-admin/admin-server/src/main/java/com/xkcoding/admin/server/SpringBootDemoAdminServerApplication.java
  31. +0
    -0
      demo-admin/admin-server/src/main/resources/application.yml
  32. +16
    -0
      demo-admin/admin-server/src/test/java/com/xkcoding/admin/server/SpringBootDemoAdminServerApplicationTests.java
  33. +36
    -0
      demo-admin/pom.xml
  34. +0
    -0
      demo-async/.gitignore
  35. +257
    -0
      demo-async/README.md
  36. +54
    -0
      demo-async/pom.xml
  37. +24
    -0
      demo-async/src/main/java/com/xkcoding/async/SpringBootDemoAsyncApplication.java
  38. +76
    -0
      demo-async/src/main/java/com/xkcoding/async/task/TaskFactory.java
  39. +0
    -0
      demo-async/src/main/resources/application.yml
  40. +0
    -0
      demo-async/src/test/java/com/xkcoding/async/SpringBootDemoAsyncApplicationTests.java
  41. +56
    -0
      demo-async/src/test/java/com/xkcoding/async/task/TaskFactoryTest.java
  42. +0
    -0
      demo-cache-ehcache/.gitignore
  43. +286
    -0
      demo-cache-ehcache/README.md
  44. +69
    -0
      demo-cache-ehcache/pom.xml
  45. +22
    -0
      demo-cache-ehcache/src/main/java/com/xkcoding/cache/ehcache/SpringBootDemoCacheEhcacheApplication.java
  46. +30
    -0
      demo-cache-ehcache/src/main/java/com/xkcoding/cache/ehcache/entity/User.java
  47. +36
    -0
      demo-cache-ehcache/src/main/java/com/xkcoding/cache/ehcache/service/UserService.java
  48. +78
    -0
      demo-cache-ehcache/src/main/java/com/xkcoding/cache/ehcache/service/impl/UserServiceImpl.java
  49. +0
    -0
      demo-cache-ehcache/src/main/resources/application.yml
  50. +0
    -0
      demo-cache-ehcache/src/main/resources/ehcache.xml
  51. +0
    -0
      demo-cache-ehcache/src/test/java/com/xkcoding/cache/ehcache/SpringBootDemoCacheEhcacheApplicationTests.java
  52. +60
    -0
      demo-cache-ehcache/src/test/java/com/xkcoding/cache/ehcache/service/UserServiceTest.java
  53. +0
    -0
      demo-cache-redis/.gitignore
  54. +347
    -0
      demo-cache-redis/README.md
  55. +81
    -0
      demo-cache-redis/pom.xml
  56. +0
    -0
      demo-cache-redis/src/main/java/com/xkcoding/cache/redis/SpringBootDemoCacheRedisApplication.java
  57. +56
    -0
      demo-cache-redis/src/main/java/com/xkcoding/cache/redis/config/RedisConfig.java
  58. +30
    -0
      demo-cache-redis/src/main/java/com/xkcoding/cache/redis/entity/User.java
  59. +36
    -0
      demo-cache-redis/src/main/java/com/xkcoding/cache/redis/service/UserService.java
  60. +78
    -0
      demo-cache-redis/src/main/java/com/xkcoding/cache/redis/service/impl/UserServiceImpl.java
  61. +0
    -0
      demo-cache-redis/src/main/resources/application.yml
  62. +52
    -0
      demo-cache-redis/src/test/java/com/xkcoding/cache/redis/RedisTest.java
  63. +0
    -0
      demo-cache-redis/src/test/java/com/xkcoding/cache/redis/SpringBootDemoCacheRedisApplicationTests.java
  64. +60
    -0
      demo-cache-redis/src/test/java/com/xkcoding/cache/redis/service/UserServiceTest.java
  65. +0
    -0
      demo-codegen/.gitignore
  66. +410
    -0
      demo-codegen/README.md
  67. +98
    -0
      demo-codegen/pom.xml
  68. +21
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/SpringBootDemoCodegenApplication.java
  69. +25
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/common/IResultCode.java
  70. +38
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/common/PageResult.java
  71. +90
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/common/R.java
  72. +39
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/common/ResultCode.java
  73. +16
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/constants/GenConstants.java
  74. +55
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/controller/CodeGenController.java
  75. +47
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/entity/ColumnEntity.java
  76. +43
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/entity/GenConfig.java
  77. +41
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/entity/TableEntity.java
  78. +43
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/entity/TableRequest.java
  79. +32
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/service/CodeGenService.java
  80. +130
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/service/impl/CodeGenServiceImpl.java
  81. +264
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/utils/CodeGenUtil.java
  82. +27
    -0
      demo-codegen/src/main/java/com/xkcoding/codegen/utils/DbUtil.java
  83. +0
    -0
      demo-codegen/src/main/resources/application.yml
  84. +0
    -0
      demo-codegen/src/main/resources/generator.properties
  85. +0
    -0
      demo-codegen/src/main/resources/jdbc_type.properties
  86. +79
    -0
      demo-codegen/src/main/resources/logback-spring.xml
  87. +0
    -0
      demo-codegen/src/main/resources/static/index.html
  88. +0
    -0
      demo-codegen/src/main/resources/static/libs/axios/axios.min.js
  89. +145
    -0
      demo-codegen/src/main/resources/static/libs/datejs/date-zh-CN.js
  90. +0
    -0
      demo-codegen/src/main/resources/static/libs/iview/fonts/ionicons.svg
  91. +0
    -0
      demo-codegen/src/main/resources/static/libs/iview/fonts/ionicons.ttf
  92. +0
    -0
      demo-codegen/src/main/resources/static/libs/iview/fonts/ionicons.woff
  93. +0
    -0
      demo-codegen/src/main/resources/static/libs/iview/iview.css
  94. +0
    -0
      demo-codegen/src/main/resources/static/libs/iview/iview.min.js
  95. +0
    -0
      demo-codegen/src/main/resources/static/libs/vue/vue.min.js
  96. +102
    -0
      demo-codegen/src/main/resources/template/Controller.java.vm
  97. +42
    -0
      demo-codegen/src/main/resources/template/Entity.java.vm
  98. +18
    -0
      demo-codegen/src/main/resources/template/Mapper.java.vm
  99. +0
    -0
      demo-codegen/src/main/resources/template/Mapper.xml.vm
  100. +16
    -0
      demo-codegen/src/main/resources/template/Service.java.vm

+ 86
- 316
README.en.md View File

@@ -5,7 +5,7 @@
<a href="https://xkcoding.com"><img alt="author" src="https://img.shields.io/badge/author-Yangkai.Shen-blue.svg"/></a>
<a href="https://www.oracle.com/technetwork/java/javase/downloads/index.html"><img alt="JDK" src="https://img.shields.io/badge/JDK-1.8.0_162-orange.svg"/></a>
<a href="https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/html/"><img alt="Spring Boot" src="https://img.shields.io/badge/Spring Boot-2.1.0.RELEASE-brightgreen.svg"/></a>
<a href="https://github.com/xkcoding/spring-boot-demo/blob/master/LICENSE"><img alt="LICENSE" src="https://img.shields.io/github/license/xkcoding/spring-boot-demo.svg"/></a>
<a href="https://github.com/xkcoding/spring-boot-demo/blob/master/LICENSE"><img alt="LICENSE" src="https://img.shields.io/github/license/xkcoding/spring-boot-demo.svg"/></a>
</p>

<p align="center">
@@ -20,17 +20,12 @@

## Introduction

`spring boot demo` is a project for learning and practicing `spring boot`, including `66` demos, and `54` of them have been done.
`spring boot demo` is a project for learning and practicing `spring boot`, including `66` demos, and `55` of them have been done.

This project has integrated actuator (`monitoring`), admin (`visual monitoring`), logback (`log`), aopLog (`recording web request logs through AOP`), global exception handling (`json level and page level` ), freemarker (`template engine`), thymeleaf (`template engine`), Beetl (`template engine`), Enjoy (`template engine`), JdbcTemplate (`general JDBC operate database`), JPA (`powerful ORM framework `), mybatis (`powerful ORM framework`), Generic Mapper (`mybatis quick operation `), PageHelper (`powerful mybatis pagination plugin`), mybatis-plus (`mybatis quick operation`), BeetlSQL (`powerful ORM framework `), upload (`local file upload and qiniu cloud file upload`), redis (`cache`), ehcache (`cache`), email (`send various types of mail`), task (`basic scheduled tasks`), quartz (`dynamic management scheduled tasks`), xxl-job (`distributed scheduled tasks`), swagger (`API interface management and tests`), security (`RBAC-based Dynamic Rights Authentication`), SpringSession (`session sharing`), Zookeeper (`implement distributed locks by AOP`), RabbitMQ (`message queue`), Kafka (`message queue`), websocket (` server pushes the monitoring server status to front end `), socket.io (`chat room`), ureport2 (`Chinese-style report`), packaged into a `war` file, integrate ElasticSearch (`basic operations and advanced queries`), Async ( `asynchronous tasks`), integrated Dubbo (`with official starter`), MongoDB (`document database`), neo4j (`graph database`), docker (`container`), `JPA Multi-Datasource`, `Mybatis Multi-Datasource`, `code generator`', GrayLog (`log collection`), JustAuth (`third-party login`), LDAP(`CURD`), `Dynamically add/switch datasources`, Standalone RateLimiting(`AOP + Guava RateLimiter`), Distributed Ratelimiting(`AOP + Redis + Lua`), ElasticSearch 7.x(`use official Rest High Level Client`), HTTPS, Flyway(`initialize databases`).
This project has integrated actuator (`monitoring`), admin (`visual monitoring`), logback (`log`), aopLog (`recording web request logs through AOP`), global exception handling (`json level and page level` ), freemarker (`template engine`), thymeleaf (`template engine`), Beetl (`template engine`), Enjoy (`template engine`), JdbcTemplate (`general JDBC operate database`), JPA (`powerful ORM framework `), mybatis (`powerful ORM framework`), Generic Mapper (`mybatis quick operation `), PageHelper (`powerful mybatis pagination plugin`), mybatis-plus (`mybatis quick operation`), BeetlSQL (`powerful ORM framework `), upload (`local file upload and qiniu cloud file upload`), redis (`cache`), ehcache (`cache`), email (`send various types of mail`), task (`basic scheduled tasks`), quartz (`dynamic management scheduled tasks`), xxl-job (`distributed scheduled tasks`), swagger (`API interface management and tests`), security (`RBAC-based Dynamic Rights Authentication`), SpringSession (`session sharing`), Zookeeper (`implement distributed locks by AOP`), RabbitMQ (`message queue`), Kafka (`message queue`), websocket (` server pushes the monitoring server status to front end `), socket.io (`chat room`), ureport2 (`Chinese-style report`), packaged into a `war` file, integrate ElasticSearch (`basic operations and advanced queries`), Async ( `asynchronous tasks`), integrated Dubbo (`with official starter`), MongoDB (`document database`), neo4j (`graph database`), docker (`container`), `JPA Multi-Datasource`, `Mybatis Multi-Datasource`, `code generator`', GrayLog (`log collection`), JustAuth (`third-party login`), LDAP(`CURD`), `Dynamically add/switch datasources`, Standalone RateLimiting(`AOP + Guava RateLimiter`), Distributed Ratelimiting(`AOP + Redis + Lua`), ElasticSearch 7.x(`use official Rest High Level Client`), HTTPS, Flyway(`initialize databases`),UReport2(`Chinese complex report `).

> If you have demos to contribute or needs to meet, it is very welcome to submit a [issue](https://github.com/xkcoding/spring-boot-demo/issues/new) and I will add it to my [TODO](./TODO.en.md) list.

## Thanks

- <a href="https://www.jetbrains.com/?from=spring-boot-demo"><img src="http://static.xkcoding.com/spring-boot-demo/064312.jpg" width="100px" alt="jetbrains">**Thanks JetBrains Offer Open Source Free License**</a>
- [Thanks MyBatisCodeHelper-Pro(The Best Code Generator Plugin) Offer Permanent Activation Code](https://gejun123456.github.io/MyBatisCodeHelper-Pro/#/?id=mybatiscodehelper-pro)

## Branch Introduction

- branch master: Based on Spring Boot version `2.1.0.RELEASE`. Every module's parent dependency is the pom.xml at root directory in convenience of managing common dependencies and learning spring boot.
@@ -55,318 +50,12 @@ This project has integrated actuator (`monitoring`), admin (`visual monitoring`)
6. **`Note: Each demo has a detailed README file. Remember to check it before running the demo~`**
7. **`Note: In some condition you have to execute sql to prepare data before running demo, don't forget it~`**

## TODO

View the [TODO](./TODO.en.md) file

## Introduction of each Module

| Module Name | Module Description |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| [spring-boot-demo-helloworld](./spring-boot-demo-helloworld) | a helloworld demo. |
| [spring-boot-demo-properties](./spring-boot-demo-properties) | a demo to read the contents of configuration file. |
| [spring-boot-demo-actuator](./spring-boot-demo-actuator) | a demo to integrate spring-boot-starter-actuator for monitoring the starting status and the running status of application. |
| [spring-boot-demo-admin-client](./spring-boot-demo-admin/spring-boot-demo-admin-client) | a client demo to integrate spring-boot-admin for visually monitoring the running status of application, it can be used with spring-boot-starter-actuator. |
| [spring-boot-demo-admin-server](./spring-boot-demo-admin/spring-boot-demo-admin-server) | a server demo to integrate spring-boot-admin for visually monitoring the running status of the spring-boot program, it can be used with spring-boot-starter-actuator. |
| [spring-boot-demo-logback](./spring-boot-demo-logback) | a demo to integrate the logback for logging. |
| [spring-boot-demo-log-aop](./spring-boot-demo-log-aop) | a demo to record web request logs using AOP aspect. |
| [spring-boot-demo-exception-handler](./spring-boot-demo-exception-handler) | a demo to demonstrate global exception handling, including 2 types, the first one returns json data, and the second one jumps to error page. |
| [spring-boot-demo-template-freemarker](./spring-boot-demo-template-freemarker) | a demo to integrate Freemarker template engine. |
| [spring-boot-demo-template-thymeleaf](./spring-boot-demo-template-thymeleaf) | a demo to integrate Thymeleaf template engine. |
| [spring-boot-demo-template-beetl](./spring-boot-demo-template-beetl) | a demo to integrate Beetl template engine. |
| [spring-boot-demo-template-enjoy](./spring-boot-demo-template-enjoy) | a demo to integrate Enjoy template engine. |
| [spring-boot-demo-orm-jdbctemplate](./spring-boot-demo-orm-jdbctemplate) | a demo to integrate the Jdbc Template for operating database and easily encapsulate the generic Dao layer. |
| [spring-boot-demo-orm-jpa](./spring-boot-demo-orm-jpa) | a demo to integrate spring-boot-starter-data-jpa for operating database. |
| [spring-boot-demo-orm-mybatis](./spring-boot-demo-orm-mybatis) | a demo to integrate native mybatis by using [mybatis-spring-boot-starter](https://github.com/mybatis/spring-boot-starter) dependency. |
| [spring-boot-demo-orm-mybatis-mapper-page](./spring-boot-demo-orm-mybatis-mapper-page) | a demo to integrate [Mapper](https://github.com/abel533/Mapper) and [PageHelper](https://github.com/pagehelper/Mybatis-PageHelper) by using [mapper-spring-boot-starter](https://github.com/abel533/Mapper/tree/master/spring-boot-starter) and [pagehelper-spring-boot-starter](https://github.com/pagehelper/pagehelper-spring-boot) dependencies. |
| [spring-boot-demo-orm-mybatis-plus](./spring-boot-demo-orm-mybatis-plus) | a demo to integrate [mybatis-plus](https://mybatis.plus/en/) by using [mybatis-plus-boot-starter](http://mp.baomidou.com/) dependency, integrate BaseMapper / BaseService / ActiveRecord to operate database. |
| [spring-boot-demo-orm-beetlsql](./spring-boot-demo-orm-beetlsql) | a demo to integrate [beetl-sql](http://ibeetl.com/guide/#beetlsql) by using [beetl-framework-starter](http://ibeetl.com/guide/#beetlsql) dependency. |
| [spring-boot-demo-upload](./spring-boot-demo-upload) | a file upload demo, including local file upload and qiniu cloud file upload. |
| [spring-boot-demo-cache-redis](./spring-boot-demo-cache-redis) | a demo to integrate redis, operate data in redis, and use redis to cache data. |
| [spring-boot-demo-cache-ehcache](./spring-boot-demo-cache-ehcache) | a demo to integrate ehcache, and use ehcache to cache data. |
| [spring-boot-demo-email](./spring-boot-demo-email) | a demo to integrate email, including sending simple text email, HTML email (including template HTML email), attachment email, and static resource email. |
| [spring-boot-demo-task](./spring-boot-demo-task) | a demo to show easy to use scheduled task. |
| [spring-boot-demo-task-quartz](./spring-boot-demo-task-quartz) | a demo to integrate quartz for managing scheduled tasks, including adding new scheduled tasks, deleting scheduled tasks, suspending scheduled tasks, restoring scheduled tasks, modifying scheduled task startup times, and timing task list queries, and `providing front-end pages`. |
| [spring-boot-demo-task-xxl-job](./spring-boot-demo-task-xxl-job) | a demo to integrate [xxl-job](http://www.xuxueli.com/xxl-job/en/#/) for distributed scheduled tasks and provide methods to manage scheduled tasks bypass `xxl-job-admin`, including scheduled task lists, trigger lists, new scheduled tasks, deleted scheduled tasks, stopped scheduled tasks, and started scheduled tasks. Modify the scheduled task and manually trigger the scheduled task. |
| [spring-boot-demo-swagger](./spring-boot-demo-swagger) | a demo to integrate native `swagger` to manage and test API interfaces. |
| [spring-boot-demo-swagger-beauty](./spring-boot-demo-swagger-beauty) | a demo to integrate third part of swagger dependency [swagger-bootstrap-ui](https://github.com/xiaoymin/Swagger-Bootstrap-UI) to beautify document style and manage and test API interfaces. |
| [spring-boot-demo-rbac-security](./spring-boot-demo-rbac-security) | a demo to integrate spring security implement privilege management based on RBAC privilege model, supports custom filtering request, dynamic privilege authentication, uses JWT security authentication, supports online population statistics, manually kicks out users, etc. |
| [spring-boot-demo-rbac-shiro](./spring-boot-demo-rbac-shiro) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate shiro for authentication management. |
| [spring-boot-demo-session](./spring-boot-demo-session) | a demo to integrate Spring Session to implement Session sharing, restart program Session does not expire. |
| [spring-boot-demo-oauth](./spring-boot-demo-oauth) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to implement the oauth server and to implement oauth2 protocol such as the authorization code, access token. |
| [spring-boot-demo-social](./spring-boot-demo-social) | a demo to integrate third-party login by using `justauth-spring-boot-starter` dependency to achieve QQ login, GitHub login, WeChat login, Google login, Microsoft login, Xiaomi login, enterprise WeChat login. |
| [spring-boot-demo-zookeeper](./spring-boot-demo-zookeeper) | a demo to integrate Zookeeper and AOP to implement distributed lock. |
| [spring-boot-demo-mq-rabbitmq](./spring-boot-demo-mq-rabbitmq) | a demo to integrate RabbitMQ implementation for message delivery and reception based on direct queue mode, fanout mode, topic mode, delay queue. |
| [spring-boot-demo-mq-rocketmq](./spring-boot-demo-mq-rocketmq) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate RocketMQ implementation for message delivery and reception. |
| [spring-boot-demo-mq-kafka](./spring-boot-demo-mq-kafka) | a demo to integrate Kafka implementation for message delivery and reception. |
| [spring-boot-demo-websocket](./spring-boot-demo-websocket) | a demo to integrate websocket, the backend actively pushes the server running status to front end. |
| [spring-boot-demo-websocket-socketio](./spring-boot-demo-websocket-socketio) | a demo to integrate websocket by using `netty-socketio`, implement a simple chat room. |
| [spring-boot-demo-ureport2](./spring-boot-demo-ureport2) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate [ureport2](https://github.com/youseries/ureport) to implement complex, customized Chinese-style reports. |
| [spring-boot-demo-uflo](./spring-boot-demo-uflo) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate [uflo](https://github.com/youseries/uflo)(process engine like Activiti and Flowable) to quickly implement a lightweight process engine. |
| [spring-boot-demo-urule](./spring-boot-demo-urule) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate [urule](https://github.com/youseries/urule)(rule engine like drools) fast implementation rule engine. |
| [spring-boot-demo-activiti](./spring-boot-demo-activiti) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate Activiti 7 process engine. |
| [spring-boot-demo-async](./spring-boot-demo-async) | asynchronous execution of tasks by using natively provided asynchronous task support. |
| [spring-boot-demo-war](./spring-boot-demo-war) | packaged into a war format configuration |
| [spring-boot-demo-elasticsearch](./spring-boot-demo-elasticsearch) | a demo to integrate ElasticSearch by using `spring-boot-starter-data-elasticsearch` to implement advanced techniques for using ElasticSearch, including creating indexes, configuring mappings, deleting indexes, adding and deleting basic operations, complex queries, advanced queries, aggregate queries, etc. |
| [spring-boot-demo-dubbo](./spring-boot-demo-dubbo) | a demo to integrate Dubbo, common module `spring-boot-demo-dubbo-common`, service provider `spring-boot-demo-dubbo-provider`, service consumer `spring-boot-demo-dubbo-consumer`. |
| [spring-boot-demo-mongodb](./spring-boot-demo-mongodb) | a demo to integrate MongoDB and use the official starter to CRUD. |
| [spring-boot-demo-neo4j](./spring-boot-demo-neo4j) | a demo to integrate Neo4j graph database to implement a campus character relationship network. |
| [spring-boot-demo-docker](./spring-boot-demo-docker) | docker container. |
| [spring-boot-demo-multi-datasource-jpa](./spring-boot-demo-multi-datasource-jpa) | a demo to implement JPA multi-datasource. |
| [spring-boot-demo-multi-datasource-mybatis](./spring-boot-demo-multi-datasource-mybatis) | a demo to implement Mybatis multi-datasource by using an open source solution from Mybatis-Plus. |
| [spring-boot-demo-sharding-jdbc](./spring-boot-demo-sharding-jdbc) | a demo to use `sharding-jdbc` to implement sub-database and sub-tables, while ORM uses Mybatis-Plus. |
| [spring-boot-demo-tio](./spring-boot-demo-tio) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate t-io(a network programming framework like netty). |
| [spring-boot-demo-grpc](./spring-boot-demo-grpc) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate Google grpc, need to be configure tls/ssl, see [ISSUE#5](https://github.com/xkcoding/spring-boot-demo/issues/5). |
| [spring-boot-demo-codegen](./spring-boot-demo-codegen) | a demo to integrate velocity template engine to implement code generator, improve development efficiency. |
| [spring-boot-demo-graylog](./spring-boot-demo-graylog) | a demo to integrate graylog for unified log collection. |
| spring-boot-demo-sso | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate Single Sign On, see [ISSUE#12](https://github.com/xkcoding/spring-boot-demo/issues/12). |
| [spring-boot-demo-ldap](./spring-boot-demo-ldap) | a demo to integrate LDAP to use `spring-boot-starter-data-ldap` to implement CURD operations and give the login demo, see [ISSUE#23](https://github.com/xkcoding/spring-boot-demo/issues/23), thanks [@fxbin](https://github.com/fxbin). |
| [spring-boot-demo-dynamic-datasource](./spring-boot-demo-dynamic-datasource) | a demo to add datasource dynamically, switch datasource dynamically. |
| [spring-boot-demo-ratelimit-guava](./spring-boot-demo-ratelimit-guava) | a demo to use use Guava RateLimiter to protect API by standalone rate limiting. |
| [spring-boot-demo-ratelimit-redis](./spring-boot-demo-ratelimit-redis) | a demo to use Redis and Lua script implementation to protect API by distributed rate limiting. |
| [spring-boot-demo-https](./spring-boot-demo-https) | a demo to integrate HTTPS. |
| [spring-boot-demo-elasticsearch-rest-high-level-client](./spring-boot-demo-elasticsearch-rest-high-level-client) | a demo to integrate ElasticSearch 7.x version by using official Rest High Level Client to operate ES data. |
| [spring-boot-demo-flyway](./spring-boot-flyway) | a demo to integrate Flyway to initialize tables and data in database, Flyway also support the sql script version control. |

## License

[MIT](http://opensource.org/licenses/MIT)

Copyright (c) 2018 Yangkai.Shen

## Stargazers over time

[![Stargazers over time](https://starchart.cc/xkcoding/spring-boot-demo.svg)](https://starchart.cc/xkcoding/spring-boot-demo)

## Appendix

### Pom.xml in the root directory

```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>

<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<modules>
<module>spring-boot-demo-helloworld</module>
<module>spring-boot-demo-properties</module>
<module>spring-boot-demo-actuator</module>
<module>spring-boot-demo-admin</module>
<module>spring-boot-demo-logback</module>
<module>spring-boot-demo-log-aop</module>
<module>spring-boot-demo-exception-handler</module>
<module>spring-boot-demo-template-freemarker</module>
<module>spring-boot-demo-template-thymeleaf</module>
<module>spring-boot-demo-template-beetl</module>
<module>spring-boot-demo-template-enjoy</module>
<module>spring-boot-demo-orm-jdbctemplate</module>
<module>spring-boot-demo-orm-jpa</module>
<module>spring-boot-demo-orm-mybatis</module>
<module>spring-boot-demo-orm-mybatis-mapper-page</module>
<module>spring-boot-demo-orm-mybatis-plus</module>
<module>spring-boot-demo-orm-beetlsql</module>
<module>spring-boot-demo-upload</module>
<module>spring-boot-demo-cache-redis</module>
<module>spring-boot-demo-cache-ehcache</module>
<module>spring-boot-demo-email</module>
<module>spring-boot-demo-task</module>
<module>spring-boot-demo-task-quartz</module>
<module>spring-boot-demo-task-xxl-job</module>
<module>spring-boot-demo-swagger</module>
<module>spring-boot-demo-swagger-beauty</module>
<module>spring-boot-demo-rbac-security</module>
<module>spring-boot-demo-rbac-shiro</module>
<module>spring-boot-demo-session</module>
<module>spring-boot-demo-oauth</module>
<module>spring-boot-demo-social</module>
<module>spring-boot-demo-zookeeper</module>
<module>spring-boot-demo-mq-rabbitmq</module>
<module>spring-boot-demo-mq-rocketmq</module>
<module>spring-boot-demo-mq-kafka</module>
<module>spring-boot-demo-websocket</module>
<module>spring-boot-demo-websocket-socketio</module>
<module>spring-boot-demo-ureport2</module>
<module>spring-boot-demo-uflo</module>
<module>spring-boot-demo-urule</module>
<module>spring-boot-demo-activiti</module>
<module>spring-boot-demo-async</module>
<module>spring-boot-demo-dubbo</module>
<module>spring-boot-demo-war</module>
<module>spring-boot-demo-elasticsearch</module>
<module>spring-boot-demo-mongodb</module>
<module>spring-boot-demo-neo4j</module>
<module>spring-boot-demo-docker</module>
<module>spring-boot-demo-multi-datasource-jpa</module>
<module>spring-boot-demo-multi-datasource-mybatis</module>
<module>spring-boot-demo-sharding-jdbc</module>
<module>spring-boot-demo-tio</module>
<module>spring-boot-demo-codegen</module>
<module>spring-boot-demo-graylog</module>
<module>spring-boot-demo-ldap</module>
<module>spring-boot-demo-dynamic-datasource</module>
<module>spring-boot-demo-ratelimit-guava</module>
<module>spring-boot-demo-ratelimit-redis</module>
<module>spring-boot-demo-elasticsearch-rest-high-level-client</module>
<module>spring-boot-demo-https</module>
<module>spring-boot-demo-flyway</module>
</modules>
<packaging>pom</packaging>

<name>spring-boot-demo</name>
<url>http://xkcoding.com</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.boot.version>2.1.0.RELEASE</spring.boot.version>
<mysql.version>8.0.12</mysql.version>
<hutool.version>5.0.0</hutool.version>
<guava.version>28.1-jre</guava.version>
<user.agent.version>1.20</user.agent.version>
</properties>

<repositories>
<repository>
<id>aliyun</id>
<name>aliyun</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- hutool工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- guava工具类 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<!-- 解析 UserAgent 信息 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>${user.agent.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
```

### Official starter introduction

| Name | Description |
| :------------------------------------- | :----------------------------------------------------------- |
| spring-boot-starter | The core Spring Boot starter, including auto-configuration support, logging and YAML. |
| spring-boot-starter-actuator | Production ready features to help you monitor and manage your application. |
| spring-boot-starter-amqp | Support for RabbitMQ messages |
| spring-boot-starter-aop | Support for aspect-oriented programming including spring-aop and AspectJ. |
| spring-boot-starter-batch | Support for “Spring Batch” including HSQLDB database. |
| spring-boot-starter-cache | Support for Spring’s Cache abstraction. |
| spring-boot-starter-data-elasticsearch | Support for the Elasticsearch search and analytics engine including spring-data-elasticsearch. |
| spring-boot-starter-data-jpa | Support for the “Java Persistence API” including spring-data-jpa, spring-orm and Hibernate. |
| spring-boot-starter-data-mongodb | Support for the MongoDB NoSQL Database, including spring-data-mongodb. |
| spring-boot-starter-data-rest | Support for exposing Spring Data repositories over REST via spring-data-rest-webmvc. |
| spring-boot-starter-data-solr | Support for the Apache Solr search platform, including spring-data-solr. |
| spring-boot-starter-freemarker | Support for the FreeMarker templating engine. |
| spring-boot-starter-groovy-templates | Support for the Groovy templating engine. |
| spring-boot-starter-integration | Support for common spring-integration modules. |
| spring-boot-starter-jdbc | Support for JDBC databases. |
| spring-boot-starter-jersey | Support for the Jersey RESTful Web Services framework. |
| spring-boot-starter-jta-atomikos | Support for JTA distributed transactions via Atomikos. |
| spring-boot-starter-jta-bitronix | Support for JTA distributed transactions via Bitronix. |
| spring-boot-starter-mail | Support for javax.mail. |
| spring-boot-starter-mustache | Support for the Mustache templating engine. |
| spring-boot-starter-redis | Support for the REDIS key-value data store, including spring-redis. |
| spring-boot-starter-security | Support for spring-security. |
| spring-boot-starter-social-facebook | Support for spring-social-facebook. |
| spring-boot-starter-social-linkedin | Support for spring-social-linkedin. |
| spring-boot-starter-social-twitter | Support for spring-social-twitter. |
| spring-boot-starter-test | Support for common test dependencies, including JUnit, Hamcrest and Mockito along with the spring-test module. |
| spring-boot-starter-thymeleaf | Support for the Thymeleaf templating engine, including integration with Spring. |
| spring-boot-starter-velocity | Support for the Velocity templating engine. |
| spring-boot-starter-web | Support for full-stack web development, including Tomcat and spring-webmvc. |
| spring-boot-starter-websocket | Support for WebSocket development. |
| spring-boot-starter-ws | Support for Spring Web Services. |

### Recommended Open source

- `JustAuth`:The most comprehensive open source library for third-party logins in history,https://github.com/justauth/JustAuth
@@ -375,6 +64,87 @@ Copyright (c) 2018 Yangkai.Shen
- `SpringBlade`:Complete micro-service online solution (required for enterprise development),https://github.com/chillzhuang/SpringBlade
- `Pig`:The universe's strongest micro-service certification authorized scaffolding (architect necessary),https://github.com/pigxcloud/pig

### Advertisement
### TODO

View the [TODO](./TODO.en.md) file

### Introduction of each Module

[![JD_CLOUD](assets/jdcloud.jpg)](https://re.jdcloud.com/cps?returnUrl=aHR0cHM6Ly93d3cuamRjbG91ZC5jb20vY24vYWN0aXZpdHkveWVhci1lbmQ_bUlkPTE4JmNwc0tleT1iMjg2Y2Q0ZmExMWM0ODZhODU2NmUwNjc5MGQ0MzY4MA==)
| Module Name | Module Description |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| [demo-helloworld](./demo-helloworld) | a helloworld demo. |
| [demo-properties](./demo-properties) | a demo to read the contents of configuration file. |
| [demo-actuator](./demo-actuator) | a demo to integrate spring-boot-starter-actuator for monitoring the starting status and the running status of application. |
| [demo-admin-client](./demo-admin/admin-client) | a client demo to integrate spring-boot-admin for visually monitoring the running status of application, it can be used with spring-boot-starter-actuator. |
| [demo-admin-server](./demo-admin/admin-server) | a server demo to integrate spring-boot-admin for visually monitoring the running status of the spring-boot program, it can be used with spring-boot-starter-actuator. |
| [demo-logback](./demo-logback) | a demo to integrate the logback for logging. |
| [demo-log-aop](./demo-log-aop) | a demo to record web request logs using AOP aspect. |
| [demo-exception-handler](./demo-exception-handler) | a demo to demonstrate global exception handling, including 2 types, the first one returns json data, and the second one jumps to error page. |
| [demo-template-freemarker](./demo-template-freemarker) | a demo to integrate Freemarker template engine. |
| [demo-template-thymeleaf](./demo-template-thymeleaf) | a demo to integrate Thymeleaf template engine. |
| [demo-template-beetl](./demo-template-beetl) | a demo to integrate Beetl template engine. |
| [demo-template-enjoy](./demo-template-enjoy) | a demo to integrate Enjoy template engine. |
| [demo-orm-jdbctemplate](./demo-orm-jdbctemplate) | a demo to integrate the Jdbc Template for operating database and easily encapsulate the generic Dao layer. |
| [demo-orm-jpa](./demo-orm-jpa) | a demo to integrate spring-boot-starter-data-jpa for operating database. |
| [demo-orm-mybatis](./demo-orm-mybatis) | a demo to integrate native mybatis by using [mybatis-spring-boot-starter](https://github.com/mybatis/spring-boot-starter) dependency. |
| [demo-orm-mybatis-mapper-page](./demo-orm-mybatis-mapper-page) | a demo to integrate [Mapper](https://github.com/abel533/Mapper) and [PageHelper](https://github.com/pagehelper/Mybatis-PageHelper) by using [mapper-spring-boot-starter](https://github.com/abel533/Mapper/tree/master/spring-boot-starter) and [pagehelper-spring-boot-starter](https://github.com/pagehelper/pagehelper-spring-boot) dependencies. |
| [demo-orm-mybatis-plus](./demo-orm-mybatis-plus) | a demo to integrate [mybatis-plus](https://mybatis.plus/en/) by using [mybatis-plus-boot-starter](http://mp.baomidou.com/) dependency, integrate BaseMapper / BaseService / ActiveRecord to operate database. |
| [demo-orm-beetlsql](./demo-orm-beetlsql) | a demo to integrate [beetl-sql](http://ibeetl.com/guide/#beetlsql) by using [beetl-framework-starter](http://ibeetl.com/guide/#beetlsql) dependency. |
| [demo-upload](./demo-upload) | a file upload demo, including local file upload and qiniu cloud file upload. |
| [demo-cache-redis](./demo-cache-redis) | a demo to integrate redis, operate data in redis, and use redis to cache data. |
| [demo-cache-ehcache](./demo-cache-ehcache) | a demo to integrate ehcache, and use ehcache to cache data. |
| [demo-email](./demo-email) | a demo to integrate email, including sending simple text email, HTML email (including template HTML email), attachment email, and static resource email. |
| [demo-task](./demo-task) | a demo to show easy to use scheduled task. |
| [demo-task-quartz](./demo-task-quartz) | a demo to integrate quartz for managing scheduled tasks, including adding new scheduled tasks, deleting scheduled tasks, suspending scheduled tasks, restoring scheduled tasks, modifying scheduled task startup times, and timing task list queries, and `providing front-end pages`. |
| [demo-task-xxl-job](./demo-task-xxl-job) | a demo to integrate [xxl-job](http://www.xuxueli.com/xxl-job/en/#/) for distributed scheduled tasks and provide methods to manage scheduled tasks bypass `xxl-job-admin`, including scheduled task lists, trigger lists, new scheduled tasks, deleted scheduled tasks, stopped scheduled tasks, and started scheduled tasks. Modify the scheduled task and manually trigger the scheduled task. |
| [demo-swagger](./demo-swagger) | a demo to integrate native `swagger` to manage and test API interfaces. |
| [demo-swagger-beauty](./demo-swagger-beauty) | a demo to integrate third part of swagger dependency [swagger-bootstrap-ui](https://github.com/xiaoymin/Swagger-Bootstrap-UI) to beautify document style and manage and test API interfaces. |
| [demo-rbac-security](./demo-rbac-security) | a demo to integrate spring security implement privilege management based on RBAC privilege model, supports custom filtering request, dynamic privilege authentication, uses JWT security authentication, supports online population statistics, manually kicks out users, etc. |
| [demo-rbac-shiro](./demo-rbac-shiro) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate shiro for authentication management. |
| [demo-session](./demo-session) | a demo to integrate Spring Session to implement Session sharing, restart program Session does not expire. |
| [demo-oauth](./demo-oauth) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to implement the oauth server and to implement oauth2 protocol such as the authorization code, access token. |
| [demo-social](./demo-social) | a demo to integrate third-party login by using `justauth-spring-boot-starter` dependency to achieve QQ login, GitHub login, WeChat login, Google login, Microsoft login, Xiaomi login, enterprise WeChat login. |
| [demo-zookeeper](./demo-zookeeper) | a demo to integrate Zookeeper and AOP to implement distributed lock. |
| [demo-mq-rabbitmq](./demo-mq-rabbitmq) | a demo to integrate RabbitMQ implementation for message delivery and reception based on direct queue mode, fanout mode, topic mode, delay queue. |
| [demo-mq-rocketmq](./demo-mq-rocketmq) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate RocketMQ implementation for message delivery and reception. |
| [demo-mq-kafka](./demo-mq-kafka) | a demo to integrate Kafka implementation for message delivery and reception. |
| [demo-websocket](./demo-websocket) | a demo to integrate websocket, the backend actively pushes the server running status to front end. |
| [demo-websocket-socketio](./demo-websocket-socketio) | a demo to integrate websocket by using `netty-socketio`, implement a simple chat room. |
| [demo-ureport2](./demo-ureport2) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate [ureport2](https://github.com/youseries/ureport) to implement complex, customized Chinese-style reports. |
| [demo-uflo](./demo-uflo) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate [uflo](https://github.com/youseries/uflo)(process engine like Activiti and Flowable) to quickly implement a lightweight process engine. |
| [demo-urule](./demo-urule) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate [urule](https://github.com/youseries/urule)(rule engine like drools) fast implementation rule engine. |
| [demo-activiti](./demo-activiti) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate Activiti 7 process engine. |
| [demo-async](./demo-async) | asynchronous execution of tasks by using natively provided asynchronous task support. |
| [demo-war](./demo-war) | packaged into a war format configuration |
| [demo-elasticsearch](./demo-elasticsearch) | a demo to integrate ElasticSearch by using `spring-boot-starter-data-elasticsearch` to implement advanced techniques for using ElasticSearch, including creating indexes, configuring mappings, deleting indexes, adding and deleting basic operations, complex queries, advanced queries, aggregate queries, etc. |
| [demo-dubbo](./demo-dubbo) | a demo to integrate Dubbo, common module `spring-boot-demo-dubbo-common`, service provider `spring-boot-demo-dubbo-provider`, service consumer `spring-boot-demo-dubbo-consumer`. |
| [demo-mongodb](./demo-mongodb) | a demo to integrate MongoDB and use the official starter to CRUD. |
| [demo-neo4j](./demo-neo4j) | a demo to integrate Neo4j graph database to implement a campus character relationship network. |
| [demo-docker](./demo-docker) | docker container. |
| [demo-multi-datasource-jpa](./demo-multi-datasource-jpa) | a demo to implement JPA multi-datasource. |
| [demo-multi-datasource-mybatis](./demo-multi-datasource-mybatis) | a demo to implement Mybatis multi-datasource by using an open source solution from Mybatis-Plus. |
| [demo-sharding-jdbc](./demo-sharding-jdbc) | a demo to use `sharding-jdbc` to implement sub-database and sub-tables, while ORM uses Mybatis-Plus. |
| [demo-tio](./demo-tio) | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate t-io(a network programming framework like netty). |
| demo-grpc | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate Google grpc, need to be configure tls/ssl, see [ISSUE#5](https://github.com/xkcoding/spring-boot-demo/issues/5). |
| [demo-codegen](./demo-codegen) | a demo to integrate velocity template engine to implement code generator, improve development efficiency. |
| [demo-graylog](./demo-graylog) | a demo to integrate graylog for unified log collection. |
| demo-sso | <span style="color:pink;">NOT FINISHED YET!</span> <br />a demo to integrate Single Sign On, see [ISSUE#12](https://github.com/xkcoding/spring-boot-demo/issues/12). |
| [demo-ldap](./demo-ldap) | a demo to integrate LDAP to use `spring-boot-starter-data-ldap` to implement CURD operations and give the login demo, see [ISSUE#23](https://github.com/xkcoding/spring-boot-demo/issues/23), thanks [@fxbin](https://github.com/fxbin). |
| [demo-dynamic-datasource](./demo-dynamic-datasource) | a demo to add datasource dynamically, switch datasource dynamically. |
| [demo-ratelimit-guava](./demo-ratelimit-guava) | a demo to use use Guava RateLimiter to protect API by standalone rate limiting. |
| [demo-ratelimit-redis](./demo-ratelimit-redis) | a demo to use Redis and Lua script implementation to protect API by distributed rate limiting. |
| [demo-https](./demo-https) | a demo to integrate HTTPS. |
| [demo-elasticsearch-rest-high-level-client](./demo-elasticsearch-rest-high-level-client) | a demo to integrate ElasticSearch 7.x version by using official Rest High Level Client to operate ES data. |
| [demo-flyway](./demo-flyway) | a demo to integrate Flyway to initialize tables and data in database, Flyway also support the sql script version control. |
| [demo-ureport2](./demo-ureport2) | a demo to integrate Ureport2 to design the Chinese complex report file. |

### Thanks

- <a href="https://www.jetbrains.com/?from=spring-boot-demo"><img src="http://static.xkcoding.com/spring-boot-demo/064312.jpg" width="100px" alt="jetbrains">**Thanks JetBrains Offer Open Source Free License**</a>
- [Thanks MyBatisCodeHelper-Pro(The Best Code Generator Plugin) Offer Permanent Activation Code](https://gejun123456.github.io/MyBatisCodeHelper-Pro/#/?id=mybatiscodehelper-pro)

### License

[MIT](http://opensource.org/licenses/MIT)

Copyright (c) 2018 Yangkai.Shen

+ 89
- 311
README.md View File

@@ -5,7 +5,7 @@
<a href="https://xkcoding.com"><img alt="author" src="https://img.shields.io/badge/author-Yangkai.Shen-blue.svg"/></a>
<a href="https://www.oracle.com/technetwork/java/javase/downloads/index.html"><img alt="JDK" src="https://img.shields.io/badge/JDK-1.8.0_162-orange.svg"/></a>
<a href="https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/html/"><img alt="Spring Boot" src="https://img.shields.io/badge/Spring Boot-2.1.0.RELEASE-brightgreen.svg"/></a>
<a href="https://github.com/xkcoding/spring-boot-demo/blob/master/LICENSE"><img alt="LICENSE" src="https://img.shields.io/github/license/xkcoding/spring-boot-demo.svg"/></a>
<a href="https://github.com/xkcoding/spring-boot-demo/blob/master/LICENSE"><img alt="LICENSE" src="https://img.shields.io/github/license/xkcoding/spring-boot-demo.svg"/></a>
</p>

<p align="center">
@@ -20,18 +20,12 @@

## 项目简介

`spring boot demo` 是一个用来深度学习并实战 `spring boot` 的项目,目前总共包含 **`66`** 个集成demo,已经完成 **`54`** 个。
`spring boot demo` 是一个用来深度学习并实战 `spring boot` 的项目,目前总共包含 **`66`** 个集成demo,已经完成 **`55`** 个。

该项目已成功集成 actuator(`监控`)、admin(`可视化监控`)、logback(`日志`)、aopLog(`通过AOP记录web请求日志`)、统一异常处理(`json级别和页面级别`)、freemarker(`模板引擎`)、thymeleaf(`模板引擎`)、Beetl(`模板引擎`)、Enjoy(`模板引擎`)、JdbcTemplate(`通用JDBC操作数据库`)、JPA(`强大的ORM框架`)、mybatis(`强大的ORM框架`)、通用Mapper(`快速操作Mybatis`)、PageHelper(`通用的Mybatis分页插件`)、mybatis-plus(`快速操作Mybatis`)、BeetlSQL(`强大的ORM框架`)、upload(`本地文件上传和七牛云文件上传`)、redis(`缓存`)、ehcache(`缓存`)、email(`发送各种类型邮件`)、task(`基础定时任务`)、quartz(`动态管理定时任务`)、xxl-job(`分布式定时任务`)、swagger(`API接口管理测试`)、security(`基于RBAC的动态权限认证`)、SpringSession(`Session共享`)、Zookeeper(`结合AOP实现分布式锁`)、RabbitMQ(`消息队列`)、Kafka(`消息队列`)、websocket(`服务端推送监控服务器运行信息`)、socket.io(`聊天室`)、ureport2(`中国式报表`)、打包成`war`文件、集成 ElasticSearch(`基本操作和高级查询`)、Async(`异步任务`)、集成Dubbo(`采用官方的starter`)、MongoDB(`文档数据库`)、neo4j(`图数据库`)、docker(`容器化`)、`JPA多数据源`、`Mybatis多数据源`、`代码生成器`、GrayLog(`日志收集`)、JustAuth(`第三方登录`)、LDAP(`增删改查`)、`动态添加/切换数据源`、单机限流(`AOP + Guava RateLimiter`)、分布式限流(`AOP + Redis + Lua`)、ElasticSearch 7.x(`使用官方 Rest High Level Client`)、HTTPS、Flyway(`数据库初始化`)。
该项目已成功集成 actuator(`监控`)、admin(`可视化监控`)、logback(`日志`)、aopLog(`通过AOP记录web请求日志`)、统一异常处理(`json级别和页面级别`)、freemarker(`模板引擎`)、thymeleaf(`模板引擎`)、Beetl(`模板引擎`)、Enjoy(`模板引擎`)、JdbcTemplate(`通用JDBC操作数据库`)、JPA(`强大的ORM框架`)、mybatis(`强大的ORM框架`)、通用Mapper(`快速操作Mybatis`)、PageHelper(`通用的Mybatis分页插件`)、mybatis-plus(`快速操作Mybatis`)、BeetlSQL(`强大的ORM框架`)、upload(`本地文件上传和七牛云文件上传`)、redis(`缓存`)、ehcache(`缓存`)、email(`发送各种类型邮件`)、task(`基础定时任务`)、quartz(`动态管理定时任务`)、xxl-job(`分布式定时任务`)、swagger(`API接口管理测试`)、security(`基于RBAC的动态权限认证`)、SpringSession(`Session共享`)、Zookeeper(`结合AOP实现分布式锁`)、RabbitMQ(`消息队列`)、Kafka(`消息队列`)、websocket(`服务端推送监控服务器运行信息`)、socket.io(`聊天室`)、ureport2(`中国式报表`)、打包成`war`文件、集成 ElasticSearch(`基本操作和高级查询`)、Async(`异步任务`)、集成Dubbo(`采用官方的starter`)、MongoDB(`文档数据库`)、neo4j(`图数据库`)、docker(`容器化`)、`JPA多数据源`、`Mybatis多数据源`、`代码生成器`、GrayLog(`日志收集`)、JustAuth(`第三方登录`)、LDAP(`增删改查`)、`动态添加/切换数据源`、单机限流(`AOP + Guava RateLimiter`)、分布式限流(`AOP + Redis + Lua`)、ElasticSearch 7.x(`使用官方 Rest High Level Client`)、HTTPS、Flyway(`数据库初始化`)、UReport2(`中国式复杂报表`)

> 如果大家还有想要集成的demo,也可在 [issue](https://github.com/xkcoding/spring-boot-demo/issues/new) 里提需求。我会额外添加在 [TODO](./TODO.md) 列表里。✊

## 感谢

- <a href="https://www.jetbrains.com/?from=spring-boot-demo"><img src="http://static.xkcoding.com/spring-boot-demo/064312.jpg" width="100px" alt="jetbrains">**感谢 JetBrains 提供的免费开源 License**</a>

- [感谢史上最牛的代码生成插件 MyBatisCodeHelper-Pro 提供的永久激活码](https://gejun123456.github.io/MyBatisCodeHelper-Pro/#/?id=mybatiscodehelper-pro)

## 分支介绍

- master 分支:基于 Spring Boot 版本 `2.1.0.RELEASE`,每个 Module 的 parent 依赖根目录下的 pom.xml,主要用于管理每个 Module 的通用依赖版本,方便大家学习。
@@ -56,322 +50,106 @@
6. **`注意:每个 demo 均有详细的 README 配套,食用 demo 前记得先看看哦~`**
7. **`注意:运行各个 demo 之前,有些是需要事先初始化数据库数据的,亲们别忘记了哦~`**

## 开发计划

查看 [TODO](./TODO.md) 文件

## 各 Module 介绍

| Module 名称 | Module 介绍 |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| [spring-boot-demo-helloworld](./spring-boot-demo-helloworld) | spring-boot 的一个 helloworld |
| [spring-boot-demo-properties](./spring-boot-demo-properties) | spring-boot 读取配置文件中的内容 |
| [spring-boot-demo-actuator](./spring-boot-demo-actuator) | spring-boot 集成 spring-boot-starter-actuator 用于监控 spring-boot 的启动和运行状态 |
| [spring-boot-demo-admin-client](./spring-boot-demo-admin/spring-boot-demo-admin-client) | spring-boot 集成 spring-boot-admin 来可视化的监控 spring-boot 程序的运行状态,可以与 actuator 互相搭配使用,客户端示例 |
| [spring-boot-demo-admin-server](./spring-boot-demo-admin/spring-boot-demo-admin-server) | spring-boot 集成 spring-boot-admin 来可视化的监控 spring-boot 程序的运行状态,可以与 actuator 互相搭配使用,服务端示例 |
| [spring-boot-demo-logback](./spring-boot-demo-logback) | spring-boot 集成 logback 日志 |
| [spring-boot-demo-log-aop](./spring-boot-demo-log-aop) | spring-boot 使用 AOP 切面的方式记录 web 请求日志 |
| [spring-boot-demo-exception-handler](./spring-boot-demo-exception-handler) | spring-boot 统一异常处理,包括2种,第一种返回统一的 json 格式,第二种统一跳转到异常页面 |
| [spring-boot-demo-template-freemarker](./spring-boot-demo-template-freemarker) | spring-boot 集成 Freemarker 模板引擎 |
| [spring-boot-demo-template-thymeleaf](./spring-boot-demo-template-thymeleaf) | spring-boot 集成 Thymeleaf 模板引擎 |
| [spring-boot-demo-template-beetl](./spring-boot-demo-template-beetl) | spring-boot 集成 Beetl 模板引擎 |
| [spring-boot-demo-template-enjoy](./spring-boot-demo-template-enjoy) | spring-boot 集成 Enjoy 模板引擎 |
| [spring-boot-demo-orm-jdbctemplate](./spring-boot-demo-orm-jdbctemplate) | spring-boot 集成 Jdbc Template 操作数据库,并简易封装通用 Dao 层 |
| [spring-boot-demo-orm-jpa](./spring-boot-demo-orm-jpa) | spring-boot 集成 spring-boot-starter-data-jpa 操作数据库 |
| [spring-boot-demo-orm-mybatis](./spring-boot-demo-orm-mybatis) | spring-boot 集成原生mybatis,使用 [mybatis-spring-boot-starter](https://github.com/mybatis/spring-boot-starter) 集成 |
| [spring-boot-demo-orm-mybatis-mapper-page](./spring-boot-demo-orm-mybatis-mapper-page) | spring-boot 集成[通用Mapper](https://github.com/abel533/Mapper)和[PageHelper](https://github.com/pagehelper/Mybatis-PageHelper),使用 [mapper-spring-boot-starter](https://github.com/abel533/Mapper/tree/master/spring-boot-starter) 和 [pagehelper-spring-boot-starter](https://github.com/pagehelper/pagehelper-spring-boot) 集成 |
| [spring-boot-demo-orm-mybatis-plus](./spring-boot-demo-orm-mybatis-plus) | spring-boot 集成 [mybatis-plus](https://mybatis.plus/),使用 [mybatis-plus-boot-starter](http://mp.baomidou.com/) 集成,集成 BaseMapper、BaseService、ActiveRecord 操作数据库 |
| [spring-boot-demo-orm-beetlsql](./spring-boot-demo-orm-beetlsql) | spring-boot 集成 [beetl-sql](http://ibeetl.com/guide/#beetlsql),使用 [beetl-framework-starter](http://ibeetl.com/guide/#beetlsql) 集成 |
| [spring-boot-demo-upload](./spring-boot-demo-upload) | spring-boot 文件上传示例,包含本地文件上传以及七牛云文件上传 |
| [spring-boot-demo-cache-redis](./spring-boot-demo-cache-redis) | spring-boot 整合 redis,操作redis中的数据,并使用redis缓存数据 |
| [spring-boot-demo-cache-ehcache](./spring-boot-demo-cache-ehcache) | spring-boot 整合 ehcache,使用 ehcache 缓存数据 |
| [spring-boot-demo-email](./spring-boot-demo-email) | spring-boot 整合 email,包括发送简单文本邮件、HTML邮件(包括模板HTML邮件)、附件邮件、静态资源邮件 |
| [spring-boot-demo-task](./spring-boot-demo-task) | spring-boot 快速实现定时任务 |
| [spring-boot-demo-task-quartz](./spring-boot-demo-task-quartz) | spring-boot 整合 quartz,并实现对定时任务的管理,包括新增定时任务,删除定时任务,暂停定时任务,恢复定时任务,修改定时任务启动时间,以及定时任务列表查询,`提供前端页面` |
| [spring-boot-demo-task-xxl-job](./spring-boot-demo-task-xxl-job) | spring-boot 整合[xxl-job](http://www.xuxueli.com/xxl-job/en/#/),并提供绕过 `xxl-job-admin` 对定时任务的管理的方法,包括定时任务列表,触发器列表,新增定时任务,删除定时任务,停止定时任务,启动定时任务,修改定时任务,手动触发定时任务 |
| [spring-boot-demo-swagger](./spring-boot-demo-swagger) | spring-boot 集成原生的 `swagger` 用于统一管理、测试 API 接口 |
| [spring-boot-demo-swagger-beauty](./spring-boot-demo-swagger-beauty) | spring-boot 集成第三方 `swagger` [swagger-bootstrap-ui](https://github.com/xiaoymin/Swagger-Bootstrap-UI) 美化API文档样式,用于统一管理、测试 API 接口 |
| [spring-boot-demo-rbac-security](./spring-boot-demo-rbac-security) | spring-boot 集成 spring security 完成基于RBAC权限模型的权限管理,支持自定义过滤请求,动态权限认证,使用 JWT 安全认证,支持在线人数统计,手动踢出用户等操作 |
| [spring-boot-demo-rbac-shiro](./spring-boot-demo-rbac-shiro) | spring-boot 集成 shiro 实现权限管理<br /> <span style="color:pink;">待完成</span> |
| [spring-boot-demo-session](./spring-boot-demo-session) | spring-boot 集成 Spring Session 实现Session共享、重启程序Session不失效 |
| [spring-boot-demo-oauth](./spring-boot-demo-oauth) | spring-boot 实现 oauth 服务器功能,实现授权码机制<br /> <span style="color:pink;">待完成</span> |
| [spring-boot-demo-social](./spring-boot-demo-social) | spring-boot 集成第三方登录,集成 `justauth-spring-boot-starter` 实现QQ登录、GitHub登录、微信登录、谷歌登录、微软登录、小米登录、企业微信登录。 |
| [spring-boot-demo-zookeeper](./spring-boot-demo-zookeeper) | spring-boot 集成 Zookeeper 结合AOP实现分布式锁 |
| [spring-boot-demo-mq-rabbitmq](./spring-boot-demo-mq-rabbitmq) | spring-boot 集成 RabbitMQ 实现基于直接队列模式、分列模式、主题模式、延迟队列的消息发送和接收 |
| [spring-boot-demo-mq-rocketmq](./spring-boot-demo-mq-rocketmq) | spring-boot 集成 RocketMQ,实现消息的发送和接收<br /> <span style="color:pink;">待完成</span> |
| [spring-boot-demo-mq-kafka](./spring-boot-demo-mq-kafka) | spring-boot 集成 kafka,实现消息的发送和接收 |
| [spring-boot-demo-websocket](./spring-boot-demo-websocket) | spring-boot 集成 websocket,后端主动推送前端服务器运行信息 |
| [spring-boot-demo-websocket-socketio](./spring-boot-demo-websocket-socketio) | spring-boot 使用 netty-socketio 集成 websocket,实现一个简单的聊天室 |
| [spring-boot-demo-ureport2](./spring-boot-demo-ureport2) | spring-boot 集成 ureport2 实现复杂的自定义的中国式报表<br /> <span style="color:pink;">待完成</span> |
| [spring-boot-demo-uflo](./spring-boot-demo-uflo) | spring-boot 集成 uflo 快速实现轻量级流程引擎<br /> <span style="color:pink;">待完成</span> |
| [spring-boot-demo-urule](./spring-boot-demo-urule) | spring-boot 集成 urule 快速实现规则引擎<br /> <span style="color:pink;">待完成</span> |
| [spring-boot-demo-activiti](./spring-boot-demo-activiti) | spring-boot 集成 activiti 7 流程引擎<br /> <span style="color:pink;">待完成</span> |
| [spring-boot-demo-async](./spring-boot-demo-async) | spring-boot 使用原生提供的异步任务支持,实现异步执行任务 |
| [spring-boot-demo-war](./spring-boot-demo-war) | spring-boot 打成 war 包的配置 |
| [spring-boot-demo-elasticsearch](./spring-boot-demo-elasticsearch) | spring-boot 集成 ElasticSearch,集成 `spring-boot-starter-data-elasticsearch` 完成对 ElasticSearch 的高级使用技巧,包括创建索引、配置映射、删除索引、增删改查基本操作、复杂查询、高级查询、聚合查询等 |
| [spring-boot-demo-dubbo](./spring-boot-demo-dubbo) | spring-boot 集成 Dubbo,分别为公共模块 `spring-boot-demo-dubbo-common`、服务提供方`spring-boot-demo-dubbo-provider`、服务调用方`spring-boot-demo-dubbo-consumer` |
| [spring-boot-demo-mongodb](./spring-boot-demo-mongodb) | spring-boot 集成 MongoDB,使用官方的 starter 实现增删改查 |
| [spring-boot-demo-neo4j](./spring-boot-demo-neo4j) | spring-boot 集成 Neo4j 图数据库,实现一个校园人物关系网的demo |
| [spring-boot-demo-docker](./spring-boot-demo-docker) | spring-boot 容器化 |
| [spring-boot-demo-multi-datasource-jpa](./spring-boot-demo-multi-datasource-jpa) | spring-boot 使用JPA集成多数据源 |
| [spring-boot-demo-multi-datasource-mybatis](./spring-boot-demo-multi-datasource-mybatis) | spring-boot 使用Mybatis集成多数据源,使用 Mybatis-Plus 提供的开源解决方案实现 |
| [spring-boot-demo-sharding-jdbc](./spring-boot-demo-sharding-jdbc) | spring-boot 使用 `sharding-jdbc` 实现分库分表,同时ORM采用 Mybatis-Plus |
| [spring-boot-demo-tio](./spring-boot-demo-tio) | spring-boot 集成 tio 网络编程框架<br /> <span style="color:pink;">待完成</span> |
| [spring-boot-demo-grpc](./spring-boot-demo-grpc) | spring-boot 集成grpc,配置tls/ssl,参见[ISSUE#5](https://github.com/xkcoding/spring-boot-demo/issues/5)<br /> <span style="color:pink;">待完成</span> |
| [spring-boot-demo-codegen](./spring-boot-demo-codegen) | spring-boot 集成 velocity 模板技术实现的代码生成器,简化开发 |
| [spring-boot-demo-graylog](./spring-boot-demo-graylog) | spring-boot 集成 graylog 实现日志统一收集 |
| spring-boot-demo-sso | spring-boot 集成 SSO 单点登录,参见 [ISSUE#12](https://github.com/xkcoding/spring-boot-demo/issues/12)<br /> <span style="color:pink;">待完成</span> |
| [spring-boot-demo-ldap](./spring-boot-demo-ldap) | spring-boot 集成 LDAP,集成 `spring-boot-starter-data-ldap` 完成对 Ldap 的基本 CURD操作, 并给出以登录为实战的 API 示例,参见 [ISSUE#23](https://github.com/xkcoding/spring-boot-demo/issues/23),感谢 [@fxbin](https://github.com/fxbin) |
| [spring-boot-demo-dynamic-datasource](./spring-boot-demo-dynamic-datasource) | spring-boot 动态添加数据源、动态切换数据源 |
| [spring-boot-demo-ratelimit-guava](./spring-boot-demo-ratelimit-guava) | spring-boot 使用 Guava RateLimiter 实现单机版限流,保护 API |
| [spring-boot-demo-ratelimit-redis](./spring-boot-demo-ratelimit-redis) | spring-boot 使用 Redis + Lua 脚本实现分布式限流,保护 API |
| [spring-boot-demo-https](./spring-boot-demo-https) | spring-boot 集成 HTTPS |
| [spring-boot-demo-elasticsearch-rest-high-level-client](./spring-boot-demo-elasticsearch-rest-high-level-client) | spring boot 集成 ElasticSearch 7.x 版本,使用官方 Rest High Level Client 操作 ES 数据 |
| [spring-boot-demo-flyway](./spring-boot-demo-flyway) | spring boot 集成 Flyway,项目启动时初始化数据库表结构,同时支持数据库脚本版本控制 |

## License

[MIT](http://opensource.org/licenses/MIT)

Copyright (c) 2018 Yangkai.Shen

## 项目趋势

[![Stargazers over time](https://starchart.cc/xkcoding/spring-boot-demo.svg)](https://starchart.cc/xkcoding/spring-boot-demo)

## 附录
## 其他

### 根目录下的 pom.xml
### 团队纳新

```xml
<?xml version="1.0" encoding="UTF-8"?>
组内招人啦,HC 巨多,Base 杭州,感兴趣的小伙伴,查看 [岗位详情](./jd.md)

<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.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<modules>
<module>spring-boot-demo-helloworld</module>
<module>spring-boot-demo-properties</module>
<module>spring-boot-demo-actuator</module>
<module>spring-boot-demo-admin</module>
<module>spring-boot-demo-logback</module>
<module>spring-boot-demo-log-aop</module>
<module>spring-boot-demo-exception-handler</module>
<module>spring-boot-demo-template-freemarker</module>
<module>spring-boot-demo-template-thymeleaf</module>
<module>spring-boot-demo-template-beetl</module>
<module>spring-boot-demo-template-enjoy</module>
<module>spring-boot-demo-orm-jdbctemplate</module>
<module>spring-boot-demo-orm-jpa</module>
<module>spring-boot-demo-orm-mybatis</module>
<module>spring-boot-demo-orm-mybatis-mapper-page</module>
<module>spring-boot-demo-orm-mybatis-plus</module>
<module>spring-boot-demo-orm-beetlsql</module>
<module>spring-boot-demo-upload</module>
<module>spring-boot-demo-cache-redis</module>
<module>spring-boot-demo-cache-ehcache</module>
<module>spring-boot-demo-email</module>
<module>spring-boot-demo-task</module>
<module>spring-boot-demo-task-quartz</module>
<module>spring-boot-demo-task-xxl-job</module>
<module>spring-boot-demo-swagger</module>
<module>spring-boot-demo-swagger-beauty</module>
<module>spring-boot-demo-rbac-security</module>
<module>spring-boot-demo-rbac-shiro</module>
<module>spring-boot-demo-session</module>
<module>spring-boot-demo-oauth</module>
<module>spring-boot-demo-social</module>
<module>spring-boot-demo-zookeeper</module>
<module>spring-boot-demo-mq-rabbitmq</module>
<module>spring-boot-demo-mq-rocketmq</module>
<module>spring-boot-demo-mq-kafka</module>
<module>spring-boot-demo-websocket</module>
<module>spring-boot-demo-websocket-socketio</module>
<module>spring-boot-demo-ureport2</module>
<module>spring-boot-demo-uflo</module>
<module>spring-boot-demo-urule</module>
<module>spring-boot-demo-activiti</module>
<module>spring-boot-demo-async</module>
<module>spring-boot-demo-dubbo</module>
<module>spring-boot-demo-war</module>
<module>spring-boot-demo-elasticsearch</module>
<module>spring-boot-demo-mongodb</module>
<module>spring-boot-demo-neo4j</module>
<module>spring-boot-demo-docker</module>
<module>spring-boot-demo-multi-datasource-jpa</module>
<module>spring-boot-demo-multi-datasource-mybatis</module>
<module>spring-boot-demo-sharding-jdbc</module>
<module>spring-boot-demo-tio</module>
<module>spring-boot-demo-codegen</module>
<module>spring-boot-demo-graylog</module>
<module>spring-boot-demo-ldap</module>
<module>spring-boot-demo-dynamic-datasource</module>
<module>spring-boot-demo-ratelimit-guava</module>
<module>spring-boot-demo-ratelimit-redis</module>
<module>spring-boot-demo-elasticsearch-rest-high-level-client</module>
<module>spring-boot-demo-https</module>
<module>spring-boot-demo-flyway</module>
</modules>
<packaging>pom</packaging>
- `JustAuth`:史上最全的整合第三方登录的开源库,https://github.com/justauth/JustAuth
- `Mica`:SpringBoot 微服务高效开发工具集,https://github.com/lets-mica/mica
- `awesome-collector`:https://github.com/P-P-X/awesome-collector
- `SpringBlade`:完整的线上解决方案(企业开发必备),https://github.com/chillzhuang/SpringBlade
- `Pig`:宇宙最强微服务认证授权脚手架(架构师必备),https://github.com/pigxcloud/pig

<name>spring-boot-demo</name>
<url>http://xkcoding.com</url>
### 开发计划

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.boot.version>2.1.0.RELEASE</spring.boot.version>
<mysql.version>8.0.12</mysql.version>
<hutool.version>5.0.0</hutool.version>
<guava.version>28.1-jre</guava.version>
<user.agent.version>1.20</user.agent.version>
</properties>
查看 [TODO](./TODO.md) 文件

<repositories>
<repository>
<id>aliyun</id>
<name>aliyun</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
### 各 Module 介绍

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- hutool工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- guava工具类 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<!-- 解析 UserAgent 信息 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>${user.agent.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
| Module 名称 | Module 介绍 |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| [demo-helloworld](./demo-helloworld) | spring-boot 的一个 helloworld |
| [demo-properties](./demo-properties) | spring-boot 读取配置文件中的内容 |
| [demo-actuator](./demo-actuator) | spring-boot 集成 spring-boot-starter-actuator 用于监控 spring-boot 的启动和运行状态 |
| [demo-admin-client](./demo-admin/admin-client) | spring-boot 集成 spring-boot-admin 来可视化的监控 spring-boot 程序的运行状态,可以与 actuator 互相搭配使用,客户端示例 |
| [demo-admin-server](./demo-admin/admin-server) | spring-boot 集成 spring-boot-admin 来可视化的监控 spring-boot 程序的运行状态,可以与 actuator 互相搭配使用,服务端示例 |
| [demo-logback](./demo-logback) | spring-boot 集成 logback 日志 |
| [demo-log-aop](./demo-log-aop) | spring-boot 使用 AOP 切面的方式记录 web 请求日志 |
| [demo-exception-handler](./demo-exception-handler) | spring-boot 统一异常处理,包括2种,第一种返回统一的 json 格式,第二种统一跳转到异常页面 |
| [demo-template-freemarker](./demo-template-freemarker) | spring-boot 集成 Freemarker 模板引擎 |
| [demo-template-thymeleaf](./demo-template-thymeleaf) | spring-boot 集成 Thymeleaf 模板引擎 |
| [demo-template-beetl](./demo-template-beetl) | spring-boot 集成 Beetl 模板引擎 |
| [demo-template-enjoy](./demo-template-enjoy) | spring-boot 集成 Enjoy 模板引擎 |
| [demo-orm-jdbctemplate](./demo-orm-jdbctemplate) | spring-boot 集成 Jdbc Template 操作数据库,并简易封装通用 Dao 层 |
| [demo-orm-jpa](./demo-orm-jpa) | spring-boot 集成 spring-boot-starter-data-jpa 操作数据库 |
| [demo-orm-mybatis](./demo-orm-mybatis) | spring-boot 集成原生mybatis,使用 [mybatis-spring-boot-starter](https://github.com/mybatis/spring-boot-starter) 集成 |
| [demo-orm-mybatis-mapper-page](./demo-orm-mybatis-mapper-page) | spring-boot 集成[通用Mapper](https://github.com/abel533/Mapper)和[PageHelper](https://github.com/pagehelper/Mybatis-PageHelper),使用 [mapper-spring-boot-starter](https://github.com/abel533/Mapper/tree/master/spring-boot-starter) 和 [pagehelper-spring-boot-starter](https://github.com/pagehelper/pagehelper-spring-boot) 集成 |
| [demo-orm-mybatis-plus](./demo-orm-mybatis-plus) | spring-boot 集成 [mybatis-plus](https://mybatis.plus/),使用 [mybatis-plus-boot-starter](http://mp.baomidou.com/) 集成,集成 BaseMapper、BaseService、ActiveRecord 操作数据库 |
| [demo-orm-beetlsql](./demo-orm-beetlsql) | spring-boot 集成 [beetl-sql](http://ibeetl.com/guide/#beetlsql),使用 [beetl-framework-starter](http://ibeetl.com/guide/#beetlsql) 集成 |
| [demo-upload](./demo-upload) | spring-boot 文件上传示例,包含本地文件上传以及七牛云文件上传 |
| [demo-cache-redis](./demo-cache-redis) | spring-boot 整合 redis,操作redis中的数据,并使用redis缓存数据 |
| [demo-cache-ehcache](./demo-cache-ehcache) | spring-boot 整合 ehcache,使用 ehcache 缓存数据 |
| [demo-email](./demo-email) | spring-boot 整合 email,包括发送简单文本邮件、HTML邮件(包括模板HTML邮件)、附件邮件、静态资源邮件 |
| [demo-task](./demo-task) | spring-boot 快速实现定时任务 |
| [demo-task-quartz](./demo-task-quartz) | spring-boot 整合 quartz,并实现对定时任务的管理,包括新增定时任务,删除定时任务,暂停定时任务,恢复定时任务,修改定时任务启动时间,以及定时任务列表查询,`提供前端页面` |
| [demo-task-xxl-job](./demo-task-xxl-job) | spring-boot 整合[xxl-job](http://www.xuxueli.com/xxl-job/en/#/),并提供绕过 `xxl-job-admin` 对定时任务的管理的方法,包括定时任务列表,触发器列表,新增定时任务,删除定时任务,停止定时任务,启动定时任务,修改定时任务,手动触发定时任务 |
| [demo-swagger](./demo-swagger) | spring-boot 集成原生的 `swagger` 用于统一管理、测试 API 接口 |
| [demo-swagger-beauty](./demo-swagger-beauty) | spring-boot 集成第三方 `swagger` [swagger-bootstrap-ui](https://github.com/xiaoymin/Swagger-Bootstrap-UI) 美化API文档样式,用于统一管理、测试 API 接口 |
| [demo-rbac-security](./demo-rbac-security) | spring-boot 集成 spring security 完成基于RBAC权限模型的权限管理,支持自定义过滤请求,动态权限认证,使用 JWT 安全认证,支持在线人数统计,手动踢出用户等操作 |
| [demo-rbac-shiro](./demo-rbac-shiro) | spring-boot 集成 shiro 实现权限管理<br /> <span style="color:pink;">待完成</span> |
| [demo-session](./demo-session) | spring-boot 集成 Spring Session 实现Session共享、重启程序Session不失效 |
| [demo-oauth](./demo-oauth) | spring-boot 实现 oauth 服务器功能,实现授权码机制<br /> <span style="color:pink;">待完成</span> |
| [demo-social](./demo-social) | spring-boot 集成第三方登录,集成 `justauth-spring-boot-starter` 实现QQ登录、GitHub登录、微信登录、谷歌登录、微软登录、小米登录、企业微信登录。 |
| [demo-zookeeper](./demo-zookeeper) | spring-boot 集成 Zookeeper 结合AOP实现分布式锁 |
| [demo-mq-rabbitmq](./demo-mq-rabbitmq) | spring-boot 集成 RabbitMQ 实现基于直接队列模式、分列模式、主题模式、延迟队列的消息发送和接收 |
| [demo-mq-rocketmq](./demo-mq-rocketmq) | spring-boot 集成 RocketMQ,实现消息的发送和接收<br /> <span style="color:pink;">待完成</span> |
| [demo-mq-kafka](./demo-mq-kafka) | spring-boot 集成 kafka,实现消息的发送和接收 |
| [demo-websocket](./demo-websocket) | spring-boot 集成 websocket,后端主动推送前端服务器运行信息 |
| [demo-websocket-socketio](./demo-websocket-socketio) | spring-boot 使用 netty-socketio 集成 websocket,实现一个简单的聊天室 |
| [demo-ureport2](./demo-ureport2) | spring-boot 集成 ureport2 实现复杂的自定义的中国式报表<br /> <span style="color:pink;">待完成</span> |
| [demo-uflo](./demo-uflo) | spring-boot 集成 uflo 快速实现轻量级流程引擎<br /> <span style="color:pink;">待完成</span> |
| [demo-urule](./demo-urule) | spring-boot 集成 urule 快速实现规则引擎<br /> <span style="color:pink;">待完成</span> |
| [demo-activiti](./demo-activiti) | spring-boot 集成 activiti 7 流程引擎<br /> <span style="color:pink;">待完成</span> |
| [demo-async](./demo-async) | spring-boot 使用原生提供的异步任务支持,实现异步执行任务 |
| [demo-war](./demo-war) | spring-boot 打成 war 包的配置 |
| [demo-elasticsearch](./demo-elasticsearch) | spring-boot 集成 ElasticSearch,集成 `spring-boot-starter-data-elasticsearch` 完成对 ElasticSearch 的高级使用技巧,包括创建索引、配置映射、删除索引、增删改查基本操作、复杂查询、高级查询、聚合查询等 |
| [demo-dubbo](./demo-dubbo) | spring-boot 集成 Dubbo,分别为公共模块 `spring-boot-demo-dubbo-common`、服务提供方`spring-boot-demo-dubbo-provider`、服务调用方`spring-boot-demo-dubbo-consumer` |
| [demo-mongodb](./demo-mongodb) | spring-boot 集成 MongoDB,使用官方的 starter 实现增删改查 |
| [demo-neo4j](./demo-neo4j) | spring-boot 集成 Neo4j 图数据库,实现一个校园人物关系网的demo |
| [demo-docker](./demo-docker) | spring-boot 容器化 |
| [demo-multi-datasource-jpa](./demo-multi-datasource-jpa) | spring-boot 使用JPA集成多数据源 |
| [demo-multi-datasource-mybatis](./demo-multi-datasource-mybatis) | spring-boot 使用Mybatis集成多数据源,使用 Mybatis-Plus 提供的开源解决方案实现 |
| [demo-sharding-jdbc](./demo-sharding-jdbc) | spring-boot 使用 `sharding-jdbc` 实现分库分表,同时ORM采用 Mybatis-Plus |
| [demo-tio](./demo-tio) | spring-boot 集成 tio 网络编程框架<br /> <span style="color:pink;">待完成</span> |
| demo-grpc | spring-boot 集成grpc,配置tls/ssl,参见[ISSUE#5](https://github.com/xkcoding/spring-boot-demo/issues/5)<br /> <span style="color:pink;">待完成</span> |
| [demo-codegen](./demo-codegen) | spring-boot 集成 velocity 模板技术实现的代码生成器,简化开发 |
| [demo-graylog](./demo-graylog) | spring-boot 集成 graylog 实现日志统一收集 |
| demo-sso | spring-boot 集成 SSO 单点登录,参见 [ISSUE#12](https://github.com/xkcoding/spring-boot-demo/issues/12)<br /> <span style="color:pink;">待完成</span> |
| [demo-ldap](./demo-ldap) | spring-boot 集成 LDAP,集成 `spring-boot-starter-data-ldap` 完成对 Ldap 的基本 CURD操作, 并给出以登录为实战的 API 示例,参见 [ISSUE#23](https://github.com/xkcoding/spring-boot-demo/issues/23),感谢 [@fxbin](https://github.com/fxbin) |
| [demo-dynamic-datasource](./demo-dynamic-datasource) | spring-boot 动态添加数据源、动态切换数据源 |
| [demo-ratelimit-guava](./demo-ratelimit-guava) | spring-boot 使用 Guava RateLimiter 实现单机版限流,保护 API |
| [demo-ratelimit-redis](./demo-ratelimit-redis) | spring-boot 使用 Redis + Lua 脚本实现分布式限流,保护 API |
| [demo-https](./demo-https) | spring-boot 集成 HTTPS |
| [demo-elasticsearch-rest-high-level-client](./demo-elasticsearch-rest-high-level-client) | spring boot 集成 ElasticSearch 7.x 版本,使用官方 Rest High Level Client 操作 ES 数据 |
| [demo-flyway](./demo-flyway) | spring boot 集成 Flyway,项目启动时初始化数据库表结构,同时支持数据库脚本版本控制 |
| [demo-ureport2](./demo-ureport2) | spring boot 集成 Ureport2,实现中国式复杂报表设计 |

### 感谢

<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
```
- <a href="https://www.jetbrains.com/?from=spring-boot-demo"><img src="http://static.xkcoding.com/spring-boot-demo/064312.jpg" width="100px" alt="jetbrains">**感谢 JetBrains 提供的免费开源 License**</a>

### 官方提供的 starter 介绍
- [感谢史上最牛的代码生成插件 MyBatisCodeHelper-Pro 提供的永久激活码](https://gejun123456.github.io/MyBatisCodeHelper-Pro/#/?id=mybatiscodehelper-pro)

| 名称 | 描述 |
| :------------------------------------- | :---------------------------------------------------------- |
| spring-boot-starter | Spring Boot 核心包,包括自动装配,日志,以及YAML文件解析 |
| spring-boot-starter-actuator | 帮助在生产环境下监控和管理 Spring Boot 应用 |
| spring-boot-starter-amqp | Spring Boot 快速集成 RabbitMQ |
| spring-boot-starter-aop | 提供切面编程特性,包含 spring-aop 和 AspectJ 依赖 |
| spring-boot-starter-batch | 快速集成 Spring Batch 批处理框架,包括操作 HSQLDB 数据库 |
| spring-boot-starter-cache | Support for Spring’s Cache abstraction. |
| spring-boot-starter-data-elasticsearch | Spring Boot 快速集成 ElasticSearch 查询分析引擎 |
| spring-boot-starter-data-jpa | Spring Boot 快速集成 JPA 操作数据库 |
| spring-boot-starter-data-mongodb | Spring Boot 快速集成 MongoDB 非关系型数据库 |
| spring-boot-starter-data-rest | Spring Boot 暴露数据库查询端点为 REST 服务 |
| spring-boot-starter-data-solr | Spring Boot 快速集成 Solr 实现全文索引 |
| spring-boot-starter-freemarker | 提供 FreeMarker 模板引擎 |
| spring-boot-starter-groovy-templates | 提供 Groovy 模板引擎 |
| spring-boot-starter-integration | 提供通用的集成 spring-integration 模块 |
| spring-boot-starter-jdbc | 快速集成 JDBC 操作数据库 |
| spring-boot-starter-jersey | 提供 Jersey 提供 RESTful 服务 |
| spring-boot-starter-jta-atomikos | 集成 JTA Atomikos 实现分布式事务 |
| spring-boot-starter-jta-bitronix | 集成 JTA Bitronix 实现分布式事务 |
| spring-boot-starter-mail | 快速邮件集成 |
| spring-boot-starter-mustache | 提供 Mustache 模板引擎 |
| spring-boot-starter-redis | Spring Boot 快速集成 Redis |
| spring-boot-starter-security | Support for spring-security. |
| spring-boot-starter-social-facebook | Support for spring-social-facebook. |
| spring-boot-starter-social-linkedin | Support for spring-social-linkedin. |
| spring-boot-starter-social-twitter | Support for spring-social-twitter. |
| spring-boot-starter-test | 提供通用单元测试依赖,包括 JUnit, Hamcrest , Mockito |
| spring-boot-starter-thymeleaf | 提供 Thymeleaf 模板引擎,包括 Thymeleaf 的自动装配等 |
| spring-boot-starter-velocity | 提供 Velocity 模板引擎 |
| spring-boot-starter-web | 提供全栈的 web 开发特性,包括 Spring MVC 依赖和 Tomcat 容器 |
| spring-boot-starter-websocket | Spring Boot 集成 WebSocket 功能 |
| spring-boot-starter-ws | Spring Boot 集成 WebService 功能 |
### License

### 开源推荐
[MIT](http://opensource.org/licenses/MIT)

- `JustAuth`:史上最全的整合第三方登录的开源库,https://github.com/justauth/JustAuth
- `Mica`:SpringBoot 微服务高效开发工具集,https://github.com/lets-mica/mica
- `awesome-collector`:https://github.com/P-P-X/awesome-collector
- `SpringBlade`:完整的线上解决方案(企业开发必备),https://github.com/chillzhuang/SpringBlade
- `Pig`:宇宙最强微服务认证授权脚手架(架构师必备),https://github.com/pigxcloud/pig
Copyright (c) 2018 Yangkai.Shen

+ 67
- 67
TODO.en.md View File

@@ -1,73 +1,73 @@
# spring-boot-demo Project TODO List

## Module plan (completed: 54 / 66)
## Module plan (completed: 55 / 66)

- [x] ~~spring-boot-demo-helloworld(helloworld example)~~
- [x] ~~spring-boot-demo-properties (read configuration file information)~~
- [x] ~~spring-boot-demo-actuator (endpoint monitoring for Spring boot)~~
- [x] ~~spring-boot-demo-admin-client (for Spring boot visual control client)~~
- [x] ~~spring-boot-demo-admin-server (for Spring boot visual control server)~~
- [x] ~~spring-boot-demo-logback (integrated logback log)~~
- [x] ~~spring-boot-demo-log-aop (use AOP to intercept request log information)~~
- [x] ~~spring-boot-demo-exception-handler (unified exception handling)~~
- [x] ~~spring-boot-demo-template-freemarker (using template engine - Freemarker)~~
- [x] ~~spring-boot-demo-template-thymeleaf (using template engine - thymeleaf)~~
- [x] ~~spring-boot-demo-template-beetl (using template engine - beetl)~~
- [x] ~~spring-boot-demo-template-enjoy (using template engine - JFinal-Enjoy)~~
- [x] ~~spring-boot-demo-upload (upload - integrated local upload and seven cattle cloud upload)~~
- [x] ~~spring-boot-demo-orm-jdbctemplate (operating SQL relational database - JdbcTemplate)~~
- [x] ~~spring-boot-demo-orm-jpa (operating SQL Relational Database - JPA)~~
- [x] ~~spring-boot-demo-orm-mybatis (operating SQL relational database - mybatis)~~
- [x] ~~spring-boot-demo-orm-mybatis-mapper-page (operating SQL relational database - integrating mybatis generic Mapper, PageHelper)~~
- [x] ~~spring-boot-demo-orm-mybatis-plus (operating SQL relational database - integrating mybatis-plus, Mapper, ActiveRecord)~~
- [x] ~~spring-boot-demo-orm-beetlsql (operating SQL relational database - beetlSQL)~~
- [x] ~~spring-boot-demo-cache-redis (using redis for caching)~~
- [x] ~~spring-boot-demo-cache-ehcache (using Ehcache for caching)~~
- [x] ~~spring-boot-demo-email (integrated mail service)~~
- [x] ~~spring-boot-demo-task (scheduled task - Task implementation)~~
- [x] ~~spring-boot-demo-task-quartz (scheduled task - Quartz implementation)~~
- [x] ~~spring-boot-demo-task-xxl-job (scheduled task - XXL-JOB for Distributed Scheduling)~~
- [x] ~~spring-boot-demo-swagger (integrated Swagger for API interface test management)~~
- [x] ~~spring-boot-demo-swagger-beauty (integrated custom and more beautiful Swagger test management of API interface)~~
- [x] ~~spring-boot-demo-rbac-security (implementing RBAC-based permission model - Spring Security)~~
- [ ] spring-boot-demo-rbac-shiro (implementing RBAC-based permission model - shiro)
- [x] ~~spring-boot-demo-session(unified Session Management)~~
- [ ] spring-boot-demo-oauth (OAuth2 certification)
- [x] ~~spring-boot-demo-social (integrated JustAuth implements third-party authorization verification, and implements third-party logins such as QQ, WeChat, GitHub, Google, Xiaomi, etc.)~~
- [x] ~~spring-boot-demo-zookeeper (use zookeeper to implement distributed locks with AOP)~~
- [x] ~~spring-boot-demo-mq-rabbitmq (integrated messaging middleware - RabbitMQ)~~
- [ ] spring-boot-demo-mq-rocketmq (integrated messaging middleware - RocketMQ)
- [x] ~~spring-boot-demo-mq-kafka (integrated message middleware - Kafka)~~
- [x] ~~spring-boot-demo-websocket (integrated websocket service)~~
- [x] ~~spring-boot-demo-websocket-socketio (integrated socketio implements websocket service)~~
- [ ] spring-boot-demo-ureport2 (integrated ureport2 implements a custom complex Chinese-style reporting engine)
- [ ] spring-boot-demo-uflo (integrated uflo implementation process control engine)
- [ ] spring-boot-demo-urule (integrated urule implementation rules engine)
- [ ] spring-boot-demo-activiti (integrated of Activiti to implement process control engine)
- [x] ~~spring-boot-demo-async (Spring boot implements asynchronous calls)~~
- [x] ~~spring-boot-demo-dubbo (integrated dubbo)~~
- [x] ~~spring-boot-demo-war (packaged into a war package)~~
- [x] ~~spring-boot-demo-elasticsearch (integrated ElasticSearch)~~
- [x] ~~spring-boot-demo-mongodb (integrated MongoDb)~~
- [x] ~~spring-boot-demo-neo4j (integrated neo4j graph database)~~
- [x] ~~spring-boot-demo-docker (packaged into docker image)~~
- [x] ~~spring-boot-demo-multi-datasource-jpa (integrated JPA multi data source)~~
- [x] ~~spring-boot-demo-multi-datasource-mybatis (integrated with mybatis multi-data source)~~
- [x] ~~spring-boot-demo-sharding-jdbc (integrated sharding-jdbc implementation sub-library table)~~
- [ ] spring-boot-demo-tio (integrated t-io)
- [ ] spring-boot-demo-grpc (integrated grpc, configure tls/ssl) see [ISSUE#5](https://github.com/xkcoding/spring-boot-demo/issues/5)
- [x] ~~spring-boot-demo-codegen (integrated velocity auto-generated code)~~
- [x] ~~spring-boot-demo-graylog (integrated gralog log management)~~
- [ ] spring-boot-demo-sso (integrated single sign on) see [ISSUE#12](https://github.com/xkcoding/spring-boot-demo/issues/12)
- [x] ~~spring-boot-demo-ldap (integrated ldap)see [ISSUE#23](https://github.com/xkcoding/spring-boot-demo/issues/23)~~
- [x] ~~spring-boot-demo-dynamic-datasource(add datasource dynamically, switch datasource dynamically)~~
- [x] ~~spring-boot-demo-ratelimit-guava(use Guava RateLimiter to protect API by standalone rate limiting)~~
- [x] ~~spring-boot-demo-ratelimit-redis(use Redis and Lua script implementation to protect API by distributed rate limiting)~~
- [x] ~~spring-boot-demo-https(integrated HTTPS)~~
- [x] ~~spring-boot-demo-elasticsearch-rest-high-level-client(integrated Elasticsearch 7.x version,use official Rest High Level Client to operate ES data)~~
- [ ] spring-boot-demo-springbatch(data process)
- [ ] spring-boot-demo-security-justauth(use JustAuth to login GitHub,and use Spring-Security to manage login state)
- [x] ~~spring-boot-demo-flyway(integrated Flyway to initialize tables and data in database, Flyway also support the sql script version control)~~
- [x] ~~demo-helloworld(helloworld example)~~
- [x] ~~demo-properties (read configuration file information)~~
- [x] ~~demo-actuator (endpoint monitoring for Spring boot)~~
- [x] ~~demo-admin-client (for Spring boot visual control client)~~
- [x] ~~demo-admin-server (for Spring boot visual control server)~~
- [x] ~~demo-logback (integrated logback log)~~
- [x] ~~demo-log-aop (use AOP to intercept request log information)~~
- [x] ~~demo-exception-handler (unified exception handling)~~
- [x] ~~demo-template-freemarker (using template engine - Freemarker)~~
- [x] ~~demo-template-thymeleaf (using template engine - thymeleaf)~~
- [x] ~~demo-template-beetl (using template engine - beetl)~~
- [x] ~~demo-template-enjoy (using template engine - JFinal-Enjoy)~~
- [x] ~~demo-upload (upload - integrated local upload and seven cattle cloud upload)~~
- [x] ~~demo-orm-jdbctemplate (operating SQL relational database - JdbcTemplate)~~
- [x] ~~demo-orm-jpa (operating SQL Relational Database - JPA)~~
- [x] ~~demo-orm-mybatis (operating SQL relational database - mybatis)~~
- [x] ~~demo-orm-mybatis-mapper-page (operating SQL relational database - integrating mybatis generic Mapper, PageHelper)~~
- [x] ~~demo-orm-mybatis-plus (operating SQL relational database - integrating mybatis-plus, Mapper, ActiveRecord)~~
- [x] ~~demo-orm-beetlsql (operating SQL relational database - beetlSQL)~~
- [x] ~~demo-cache-redis (using redis for caching)~~
- [x] ~~demo-cache-ehcache (using Ehcache for caching)~~
- [x] ~~demo-email (integrated mail service)~~
- [x] ~~demo-task (scheduled task - Task implementation)~~
- [x] ~~demo-task-quartz (scheduled task - Quartz implementation)~~
- [x] ~~demo-task-xxl-job (scheduled task - XXL-JOB for Distributed Scheduling)~~
- [x] ~~demo-swagger (integrated Swagger for API interface test management)~~
- [x] ~~demo-swagger-beauty (integrated custom and more beautiful Swagger test management of API interface)~~
- [x] ~~demo-rbac-security (implementing RBAC-based permission model - Spring Security)~~
- [ ] demo-rbac-shiro (implementing RBAC-based permission model - shiro)
- [x] ~~demo-session(unified Session Management)~~
- [ ] demo-oauth (OAuth2 certification)
- [x] ~~demo-social (integrated JustAuth implements third-party authorization verification, and implements third-party logins such as QQ, WeChat, GitHub, Google, Xiaomi, etc.)~~
- [x] ~~demo-zookeeper (use zookeeper to implement distributed locks with AOP)~~
- [x] ~~demo-mq-rabbitmq (integrated messaging middleware - RabbitMQ)~~
- [ ] demo-mq-rocketmq (integrated messaging middleware - RocketMQ)
- [x] ~~demo-mq-kafka (integrated message middleware - Kafka)~~
- [x] ~~demo-websocket (integrated websocket service)~~
- [x] ~~demo-websocket-socketio (integrated socketio implements websocket service)~~
- [x] ~~demo-ureport2 (integrated ureport2 implements a custom complex Chinese-style reporting engine)~~
- [ ] demo-uflo (integrated uflo implementation process control engine)
- [ ] demo-urule (integrated urule implementation rules engine)
- [ ] demo-activiti (integrated of Activiti to implement process control engine)
- [x] ~~demo-async (Spring boot implements asynchronous calls)~~
- [x] ~~demo-dubbo (integrated dubbo)~~
- [x] ~~demo-war (packaged into a war package)~~
- [x] ~~demo-elasticsearch (integrated ElasticSearch)~~
- [x] ~~demo-mongodb (integrated MongoDb)~~
- [x] ~~demo-neo4j (integrated neo4j graph database)~~
- [x] ~~demo-docker (packaged into docker image)~~
- [x] ~~demo-multi-datasource-jpa (integrated JPA multi data source)~~
- [x] ~~demo-multi-datasource-mybatis (integrated with mybatis multi-data source)~~
- [x] ~~demo-sharding-jdbc (integrated sharding-jdbc implementation sub-library table)~~
- [ ] demo-tio (integrated t-io)
- [ ] demo-grpc (integrated grpc, configure tls/ssl) see [ISSUE#5](https://github.com/xkcoding/spring-boot-demo/issues/5)
- [x] ~~demo-codegen (integrated velocity auto-generated code)~~
- [x] ~~demo-graylog (integrated gralog log management)~~
- [ ] demo-sso (integrated single sign on) see [ISSUE#12](https://github.com/xkcoding/spring-boot-demo/issues/12)
- [x] ~~demo-ldap (integrated ldap)see [ISSUE#23](https://github.com/xkcoding/spring-boot-demo/issues/23)~~
- [x] ~~demo-dynamic-datasource(add datasource dynamically, switch datasource dynamically)~~
- [x] ~~demo-ratelimit-guava(use Guava RateLimiter to protect API by standalone rate limiting)~~
- [x] ~~demo-ratelimit-redis(use Redis and Lua script implementation to protect API by distributed rate limiting)~~
- [x] ~~demo-https(integrated HTTPS)~~
- [x] ~~demo-elasticsearch-rest-high-level-client(integrated Elasticsearch 7.x version,use official Rest High Level Client to operate ES data)~~
- [ ] demo-springbatch(data process)
- [ ] demo-security-justauth(use JustAuth to login GitHub,and use Spring-Security to manage login state)
- [x] ~~demo-flyway(integrated Flyway to initialize tables and data in database, Flyway also support the sql script version control)~~

## Remarks



+ 67
- 67
TODO.md View File

@@ -1,73 +1,73 @@
# spring-boot-demo 项目待办列表

## 模块计划(已完成:54 / 66)
## 模块计划(已完成:55 / 66)

- [x] ~~spring-boot-demo-helloworld(Helloworld 示例)~~
- [x] ~~spring-boot-demo-properties(读取配置文件信息)~~
- [x] ~~spring-boot-demo-actuator(对 Spring boot 的端点监控)~~
- [x] ~~spring-boot-demo-admin-client(对 Spring boot 可视化管控 客户端)~~
- [x] ~~spring-boot-demo-admin-server(对 Spring boot 可视化管控 服务端)~~
- [x] ~~spring-boot-demo-logback(集成 logback 日志)~~
- [x] ~~spring-boot-demo-log-aop(使用 AOP 拦截请求日志信息)~~
- [x] ~~spring-boot-demo-exception-handler(统一异常处理)~~
- [x] ~~spring-boot-demo-template-freemarker(使用模板引擎 - Freemarker)~~
- [x] ~~spring-boot-demo-template-thymeleaf(使用模板引擎 - thymeleaf)~~
- [x] ~~spring-boot-demo-template-beetl(使用模板引擎 - beetl)~~
- [x] ~~spring-boot-demo-template-enjoy(使用模板引擎 - JFinal-Enjoy)~~
- [x] ~~spring-boot-demo-upload(上传 - 集成本地上传和七牛云上传)~~
- [x] ~~spring-boot-demo-orm-jdbctemplate(操作 SQL 关系型数据库 - JdbcTemplate)~~
- [x] ~~spring-boot-demo-orm-jpa(操作 SQL 关系型数据库 - JPA)~~
- [x] ~~spring-boot-demo-orm-mybatis(操作 SQL 关系型数据库 - mybatis)~~
- [x] ~~spring-boot-demo-orm-mybatis-mapper-page(操作 SQL 关系型数据库 - 集成mybatis通用Mapper,PageHelper)~~
- [x] ~~spring-boot-demo-orm-mybatis-plus(操作 SQL 关系型数据库 - 集成mybatis-plus,Mapper操作、ActiveRecord操作)~~
- [x] ~~spring-boot-demo-orm-beetlsql(操作 SQL 关系型数据库 - beetlSQL)~~
- [x] ~~spring-boot-demo-cache-redis(使用 redis 进行缓存)~~
- [x] ~~spring-boot-demo-cache-ehcache(使用 Ehcache 进行缓存)~~
- [x] ~~spring-boot-demo-email(集成邮件服务)~~
- [x] ~~spring-boot-demo-task(定时任务 - Task 实现)~~
- [x] ~~spring-boot-demo-task-quartz(定时任务 - Quartz 实现)~~
- [x] ~~spring-boot-demo-task-xxl-job(定时任务 - XXL-JOB 实现分布式调度)~~
- [x] ~~spring-boot-demo-swagger(集成 Swagger 对 API 接口进行测试管理)~~
- [x] ~~spring-boot-demo-swagger-beauty(集成自定义且更加美观的 Swagger 对 API 接口进行测试管理)~~
- [x] ~~spring-boot-demo-rbac-security(实现基于 RBAC 的权限模型 - Spring Security)~~
- [ ] spring-boot-demo-rbac-shiro(实现基于 RBAC 的权限模型 - shiro)
- [x] ~~spring-boot-demo-session(统一 Session 管理)~~
- [ ] spring-boot-demo-oauth(OAuth2 认证)
- [x] ~~spring-boot-demo-social(集成 JustAuth 实现第三方授权验证,实现 QQ、微信、GitHub、谷歌、小米等第三方登录)~~
- [x] ~~spring-boot-demo-zookeeper(使用 zookeeper 结合AOP实现分布式锁)~~
- [x] ~~spring-boot-demo-mq-rabbitmq(集成消息中间件 - RabbitMQ)~~
- [ ] spring-boot-demo-mq-rocketmq(集成消息中间件 - RocketMQ)
- [x] ~~spring-boot-demo-mq-kafka(集成消息中间件 - Kafka)~~
- [x] ~~spring-boot-demo-websocket(集成 websocket 服务)~~
- [x] ~~spring-boot-demo-websocket-socketio(集成 socketio 实现 websocket 服务)~~
- [ ] spring-boot-demo-ureport2 (集成 ureport2 实现自定义的复杂中国式报表引擎)
- [ ] spring-boot-demo-uflo(集成 uflo 实现流程控制引擎)
- [ ] spring-boot-demo-urule(集成 urule 实现规则引擎)
- [ ] spring-boot-demo-activiti(集成 Activiti 实现流程控制引擎)
- [x] ~~spring-boot-demo-async(Spring boot 实现异步调用)~~
- [x] ~~spring-boot-demo-dubbo(集成 dubbo)~~
- [x] ~~spring-boot-demo-war(打包成war包)~~
- [x] ~~spring-boot-demo-elasticsearch(集成 ElasticSearch)~~
- [x] ~~spring-boot-demo-mongodb(集成 MongoDb)~~
- [x] ~~spring-boot-demo-neo4j(集成 neo4j 图数据库)~~
- [x] ~~spring-boot-demo-docker(打包成 docker 镜像)~~
- [x] ~~spring-boot-demo-multi-datasource-jpa(集成JPA多数据源)~~
- [x] ~~spring-boot-demo-multi-datasource-mybatis(集成mybatis多数据源)~~
- [x] ~~spring-boot-demo-sharding-jdbc(集成 sharding-jdbc 实现分库分表)~~
- [ ] spring-boot-demo-tio(集成 tio)
- [ ] spring-boot-demo-grpc(集成grpc,配置tls/ssl)参见[ISSUE#5](https://github.com/xkcoding/spring-boot-demo/issues/5)
- [x] ~~spring-boot-demo-codegen(集成 velocity 自动生成代码)~~
- [x] ~~spring-boot-demo-graylog(集成 gralog 日志管理)~~
- [ ] spring-boot-demo-sso(集成单点登录)参见 [ISSUE#12](https://github.com/xkcoding/spring-boot-demo/issues/12)
- [x] ~~spring-boot-demo-ldap (集成 ldap)参见 [ISSUE#23](https://github.com/xkcoding/spring-boot-demo/issues/23)~~
- [x] ~~spring-boot-demo-dynamic-datasource(动态添加数据源,切换数据源)~~
- [x] ~~spring-boot-demo-ratelimit-guava(单机限流保护API,集成 Guava 的 RateLimiter)~~
- [x] ~~spring-boot-demo-ratelimit-redis(分布式限流保护API,使用 Redis + lua 脚本实现)~~
- [x] ~~spring-boot-demo-https(集成 HTTPS)~~
- [x] ~~spring-boot-demo-elasticsearch-rest-high-level-client(集成 Elasticsearch 7.x 版本,使用官方 rest high level client操作 ES 数据)~~
- [ ] spring-boot-demo-springbatch(数据处理)
- [ ] spring-boot-demo-security-justauth(使用 JustAuth 登录 GitHub,使用 Security 管理登录状态)
- [x] ~~spring-boot-demo-flyway(集成 Flyway,项目启动时初始化数据库表结构,同时支持数据库脚本版本控制)~~
- [x] ~~demo-helloworld(Helloworld 示例)~~
- [x] ~~demo-properties(读取配置文件信息)~~
- [x] ~~demo-actuator(对 Spring boot 的端点监控)~~
- [x] ~~demo-admin-client(对 Spring boot 可视化管控 客户端)~~
- [x] ~~demo-admin-server(对 Spring boot 可视化管控 服务端)~~
- [x] ~~demo-logback(集成 logback 日志)~~
- [x] ~~demo-log-aop(使用 AOP 拦截请求日志信息)~~
- [x] ~~demo-exception-handler(统一异常处理)~~
- [x] ~~demo-template-freemarker(使用模板引擎 - Freemarker)~~
- [x] ~~demo-template-thymeleaf(使用模板引擎 - thymeleaf)~~
- [x] ~~demo-template-beetl(使用模板引擎 - beetl)~~
- [x] ~~demo-template-enjoy(使用模板引擎 - JFinal-Enjoy)~~
- [x] ~~demo-upload(上传 - 集成本地上传和七牛云上传)~~
- [x] ~~demo-orm-jdbctemplate(操作 SQL 关系型数据库 - JdbcTemplate)~~
- [x] ~~demo-orm-jpa(操作 SQL 关系型数据库 - JPA)~~
- [x] ~~demo-orm-mybatis(操作 SQL 关系型数据库 - mybatis)~~
- [x] ~~demo-orm-mybatis-mapper-page(操作 SQL 关系型数据库 - 集成mybatis通用Mapper,PageHelper)~~
- [x] ~~demo-orm-mybatis-plus(操作 SQL 关系型数据库 - 集成mybatis-plus,Mapper操作、ActiveRecord操作)~~
- [x] ~~demo-orm-beetlsql(操作 SQL 关系型数据库 - beetlSQL)~~
- [x] ~~demo-cache-redis(使用 redis 进行缓存)~~
- [x] ~~demo-cache-ehcache(使用 Ehcache 进行缓存)~~
- [x] ~~demo-email(集成邮件服务)~~
- [x] ~~demo-task(定时任务 - Task 实现)~~
- [x] ~~demo-task-quartz(定时任务 - Quartz 实现)~~
- [x] ~~demo-task-xxl-job(定时任务 - XXL-JOB 实现分布式调度)~~
- [x] ~~demo-swagger(集成 Swagger 对 API 接口进行测试管理)~~
- [x] ~~demo-swagger-beauty(集成自定义且更加美观的 Swagger 对 API 接口进行测试管理)~~
- [x] ~~demo-rbac-security(实现基于 RBAC 的权限模型 - Spring Security)~~
- [ ] demo-rbac-shiro(实现基于 RBAC 的权限模型 - shiro)
- [x] ~~demo-session(统一 Session 管理)~~
- [ ] demo-oauth(OAuth2 认证)
- [x] ~~demo-social(集成 JustAuth 实现第三方授权验证,实现 QQ、微信、GitHub、谷歌、小米等第三方登录)~~
- [x] ~~demo-zookeeper(使用 zookeeper 结合AOP实现分布式锁)~~
- [x] ~~demo-mq-rabbitmq(集成消息中间件 - RabbitMQ)~~
- [ ] demo-mq-rocketmq(集成消息中间件 - RocketMQ)
- [x] ~~demo-mq-kafka(集成消息中间件 - Kafka)~~
- [x] ~~demo-websocket(集成 websocket 服务)~~
- [x] ~~demo-websocket-socketio(集成 socketio 实现 websocket 服务)~~
- [x] ~~demo-ureport2 (集成 ureport2 实现自定义的复杂中国式报表引擎)~~
- [ ] demo-uflo(集成 uflo 实现流程控制引擎)
- [ ] demo-urule(集成 urule 实现规则引擎)
- [ ] demo-activiti(集成 Activiti 实现流程控制引擎)
- [x] ~~demo-async(Spring boot 实现异步调用)~~
- [x] ~~demo-dubbo(集成 dubbo)~~
- [x] ~~demo-war(打包成war包)~~
- [x] ~~demo-elasticsearch(集成 ElasticSearch)~~
- [x] ~~demo-mongodb(集成 MongoDb)~~
- [x] ~~demo-neo4j(集成 neo4j 图数据库)~~
- [x] ~~demo-docker(打包成 docker 镜像)~~
- [x] ~~demo-multi-datasource-jpa(集成JPA多数据源)~~
- [x] ~~demo-multi-datasource-mybatis(集成mybatis多数据源)~~
- [x] ~~demo-sharding-jdbc(集成 sharding-jdbc 实现分库分表)~~
- [ ] demo-tio(集成 tio)
- [ ] demo-grpc(集成grpc,配置tls/ssl)参见[ISSUE#5](https://github.com/xkcoding/spring-boot-demo/issues/5)
- [x] ~~demo-codegen(集成 velocity 自动生成代码)~~
- [x] ~~demo-graylog(集成 gralog 日志管理)~~
- [ ] demo-sso(集成单点登录)参见 [ISSUE#12](https://github.com/xkcoding/spring-boot-demo/issues/12)
- [x] ~~demo-ldap (集成 ldap)参见 [ISSUE#23](https://github.com/xkcoding/spring-boot-demo/issues/23)~~
- [x] ~~demo-dynamic-datasource(动态添加数据源,切换数据源)~~
- [x] ~~demo-ratelimit-guava(单机限流保护API,集成 Guava 的 RateLimiter)~~
- [x] ~~demo-ratelimit-redis(分布式限流保护API,使用 Redis + lua 脚本实现)~~
- [x] ~~demo-https(集成 HTTPS)~~
- [x] ~~demo-elasticsearch-rest-high-level-client(集成 Elasticsearch 7.x 版本,使用官方 rest high level client操作 ES 数据)~~
- [ ] demo-springbatch(数据处理)
- [ ] demo-security-justauth(使用 JustAuth 登录 GitHub,使用 Security 管理登录状态)
- [x] ~~demo-flyway(集成 Flyway,项目启动时初始化数据库表结构,同时支持数据库脚本版本控制)~~

## 备注



spring-boot-demo-activiti/.gitignore → demo-activiti/.gitignore View File


+ 71
- 0
demo-activiti/pom.xml View File

@@ -0,0 +1,71 @@
<?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>

<artifactId>demo-activiti</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demo-activiti</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

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

<!-- activiti 自动建表 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M2</version>
</dependency>

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

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<finalName>demo-activiti</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

+ 22
- 0
demo-activiti/src/main/java/com/xkcoding/activiti/SpringBootDemoActivitiApplication.java View File

@@ -0,0 +1,22 @@
package com.xkcoding.activiti;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* <p>
* 启动器
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-31 22:24
*/
@SpringBootApplication
public class SpringBootDemoActivitiApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoActivitiApplication.class, args);
}

}


+ 56
- 0
demo-activiti/src/main/java/com/xkcoding/activiti/config/SecurityConfiguration.java View File

@@ -0,0 +1,56 @@
package com.xkcoding.activiti.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
* <p>
* 安全配置类
* </p>
*
* @author yangkai.shen
* @date Created in 2019-07-01 18:40
*/
@Slf4j
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}

@Bean
protected UserDetailsService myUserDetailsService() {
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();

String[][] usersGroupsAndRoles = {{"salaboy", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {"ryandawsonuk", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {"erdemedeiros", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"}, {"admin", "password", "ROLE_ACTIVITI_ADMIN"}};

for (String[] user : usersGroupsAndRoles) {
List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
log.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]");
inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]), authoritiesStrings.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())));
}


return inMemoryUserDetailsManager;
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

+ 74
- 0
demo-activiti/src/main/java/com/xkcoding/activiti/util/SecurityUtil.java View File

@@ -0,0 +1,74 @@
package com.xkcoding.activiti.util;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
* <p>
* 认证工具
* </p>
*
* @author yangkai.shen
* @date Created in 2019-07-01 18:38
*/
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class SecurityUtil {

private final UserDetailsService userDetailsService;

public void logInAs(String username) {

UserDetails user = userDetailsService.loadUserByUsername(username);
if (user == null) {
throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user");
}

SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}

@Override
public Object getCredentials() {
return user.getPassword();
}

@Override
public Object getDetails() {
return user;
}

@Override
public Object getPrincipal() {
return user;
}

@Override
public boolean isAuthenticated() {
return true;
}

@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {

}

@Override
public String getName() {
return user.getUsername();
}
}));
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
}
}

spring-boot-demo-activiti/src/main/resources/application.yml → demo-activiti/src/main/resources/application.yml View File


spring-boot-demo-activiti/src/main/resources/processes/team01.bpmn → demo-activiti/src/main/resources/processes/team01.bpmn View File


spring-boot-demo-activiti/src/test/java/com/xkcoding/activiti/SpringBootDemoActivitiApplicationTests.java → demo-activiti/src/test/java/com/xkcoding/activiti/SpringBootDemoActivitiApplicationTests.java View File


spring-boot-demo-actuator/.gitignore → demo-actuator/.gitignore View File


spring-boot-demo-actuator/README.md → demo-actuator/README.md View File


+ 64
- 0
demo-actuator/pom.xml View File

@@ -0,0 +1,64 @@
<?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>

<artifactId>demo-actuator</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demo-actuator</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

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

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

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

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

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>demo-actuator</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

+ 20
- 0
demo-actuator/src/main/java/com/xkcoding/actuator/SpringBootDemoActuatorApplication.java View File

@@ -0,0 +1,20 @@
package com.xkcoding.actuator;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* <p>
* 启动类
* </p>
*
* @author yangkai.shen
* @date Created in 2018-9-29 14:27
*/
@SpringBootApplication
public class SpringBootDemoActuatorApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoActuatorApplication.class, args);
}
}

spring-boot-demo-actuator/src/main/resources/application.yml → demo-actuator/src/main/resources/application.yml View File


+ 16
- 0
demo-actuator/src/test/java/com/xkcoding/actuator/SpringBootDemoActuatorApplicationTests.java View File

@@ -0,0 +1,16 @@
package com.xkcoding.actuator;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootDemoActuatorApplicationTests {

@Test
public void contextLoads() {
}

}

spring-boot-demo-admin/README.md → demo-admin/README.md View File


spring-boot-demo-admin/spring-boot-demo-admin-client/.gitignore → demo-admin/admin-client/.gitignore View File


spring-boot-demo-admin/spring-boot-demo-admin-client/README.md → demo-admin/admin-client/README.md View File


+ 57
- 0
demo-admin/admin-client/pom.xml View File

@@ -0,0 +1,57 @@
<?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>
<groupId>com.xkcoding</groupId>
<artifactId>demo-admin</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>admin-client</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>admin-client</name>
<description>Demo project for Spring Boot</description>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

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

<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>

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

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>admin-client</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

+ 20
- 0
demo-admin/admin-client/src/main/java/com/xkcoding/admin/client/SpringBootDemoAdminClientApplication.java View File

@@ -0,0 +1,20 @@
package com.xkcoding.admin.client;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* <p>
* 启动类
* </p>
*
* @author yangkai.shen
* @date Created in 2018-10-8 14:16
*/
@SpringBootApplication
public class SpringBootDemoAdminClientApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoAdminClientApplication.class, args);
}
}

+ 20
- 0
demo-admin/admin-client/src/main/java/com/xkcoding/admin/client/controller/IndexController.java View File

@@ -0,0 +1,20 @@
package com.xkcoding.admin.client.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* <p>
* 首页
* </p>
*
* @author yangkai.shen
* @date Created in 2018-10-08 14:15
*/
@RestController
public class IndexController {
@GetMapping(value = {"", "/"})
public String index() {
return "This is a Spring Boot Admin Client.";
}
}

spring-boot-demo-admin/spring-boot-demo-admin-client/src/main/resources/application.yml → demo-admin/admin-client/src/main/resources/application.yml View File


+ 16
- 0
demo-admin/admin-client/src/test/java/com/xkcoding/admin/client/SpringBootDemoAdminClientApplicationTests.java View File

@@ -0,0 +1,16 @@
package com.xkcoding.admin.client;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootDemoAdminClientApplicationTests {

@Test
public void contextLoads() {
}

}

spring-boot-demo-admin/spring-boot-demo-admin-server/.gitignore → demo-admin/admin-server/.gitignore View File


+ 90
- 0
demo-admin/admin-server/README.md View File

@@ -0,0 +1,90 @@
# spring-boot-demo-admin-server

> 本 demo 主要演示了如何搭建一个 Spring Boot Admin 的服务端项目,可视化展示自己客户端项目的运行状态。

## pom.xml

```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>

<artifactId>spring-boot-demo-admin-server</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-boot-demo-admin-server</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo-admin</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

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

<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>spring-boot-demo-admin-server</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
```

## SpringBootDemoAdminServerApplication.java

```java
/**
* <p>
* 启动类
* </p>
*
* @author yangkai.shen
* @date Created in 2018-10-08 14:08
*/
@EnableAdminServer
@SpringBootApplication
public class SpringBootDemoAdminServerApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoAdminServerApplication.class, args);
}
}
```

## application.yml

```yaml
server:
port: 8000
```


+ 52
- 0
demo-admin/admin-server/pom.xml View File

@@ -0,0 +1,52 @@
<?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>
<groupId>com.xkcoding</groupId>
<artifactId>demo-admin</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>demo-admin-server</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>admin-server</name>
<description>Demo project for Spring Boot</description>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

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

<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>admin-server</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

+ 22
- 0
demo-admin/admin-server/src/main/java/com/xkcoding/admin/server/SpringBootDemoAdminServerApplication.java View File

@@ -0,0 +1,22 @@
package com.xkcoding.admin.server;

import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* <p>
* 启动类
* </p>
*
* @author yangkai.shen
* @date Created in 2018-10-08 14:08
*/
@EnableAdminServer
@SpringBootApplication
public class SpringBootDemoAdminServerApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoAdminServerApplication.class, args);
}
}

spring-boot-demo-admin/spring-boot-demo-admin-server/src/main/resources/application.yml → demo-admin/admin-server/src/main/resources/application.yml View File


+ 16
- 0
demo-admin/admin-server/src/test/java/com/xkcoding/admin/server/SpringBootDemoAdminServerApplicationTests.java View File

@@ -0,0 +1,16 @@
package com.xkcoding.admin.server;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootDemoAdminServerApplicationTests {

@Test
public void contextLoads() {
}

}

+ 36
- 0
demo-admin/pom.xml View File

@@ -0,0 +1,36 @@
<?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>spring-boot-demo</artifactId>
<groupId>com.xkcoding</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>demo-admin</artifactId>
<packaging>pom</packaging>

<properties>
<spring-boot-admin.version>2.1.0</spring-boot-admin.version>
</properties>

<modules>
<module>admin-client</module>
<module>admin-server</module>
</modules>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-dependencies</artifactId>
<version>${spring-boot-admin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

</project>

spring-boot-demo-async/.gitignore → demo-async/.gitignore View File


+ 257
- 0
demo-async/README.md View File

@@ -0,0 +1,257 @@
# spring-boot-demo-async

> 此 demo 主要演示了 Spring Boot 如何使用原生提供的异步任务支持,实现异步执行任务。

## pom.xml

```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>

<artifactId>spring-boot-demo-async</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-boot-demo-async</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

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

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

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<finalName>spring-boot-demo-async</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
```

## application.yml

```yaml
spring:
task:
execution:
pool:
# 最大线程数
max-size: 16
# 核心线程数
core-size: 16
# 存活时间
keep-alive: 10s
# 队列大小
queue-capacity: 100
# 是否允许核心线程超时
allow-core-thread-timeout: true
# 线程名称前缀
thread-name-prefix: async-task-
```

## SpringBootDemoAsyncApplication.java

```java
/**
* <p>
* 启动器
* </p>
*
* @author yangkai.shen
* @date Created in 2018-12-29 10:28
*/
@EnableAsync
@SpringBootApplication
public class SpringBootDemoAsyncApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoAsyncApplication.class, args);
}

}
```

## TaskFactory.java

```java
/**
* <p>
* 任务工厂
* </p>
*
* @author yangkai.shen
* @date Created in 2018-12-29 10:37
*/
@Component
@Slf4j
public class TaskFactory {

/**
* 模拟5秒的异步任务
*/
@Async
public Future<Boolean> asyncTask1() throws InterruptedException {
doTask("asyncTask1", 5);
return new AsyncResult<>(Boolean.TRUE);
}

/**
* 模拟2秒的异步任务
*/
@Async
public Future<Boolean> asyncTask2() throws InterruptedException {
doTask("asyncTask2", 2);
return new AsyncResult<>(Boolean.TRUE);
}

/**
* 模拟3秒的异步任务
*/
@Async
public Future<Boolean> asyncTask3() throws InterruptedException {
doTask("asyncTask3", 3);
return new AsyncResult<>(Boolean.TRUE);
}

/**
* 模拟5秒的同步任务
*/
public void task1() throws InterruptedException {
doTask("task1", 5);
}

/**
* 模拟2秒的同步任务
*/
public void task2() throws InterruptedException {
doTask("task2", 2);
}

/**
* 模拟3秒的同步任务
*/
public void task3() throws InterruptedException {
doTask("task3", 3);
}

private void doTask(String taskName, Integer time) throws InterruptedException {
log.info("{}开始执行,当前线程名称【{}】", taskName, Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(time);
log.info("{}执行成功,当前线程名称【{}】", taskName, Thread.currentThread().getName());
}
}
```

## TaskFactoryTest.java

```java
/**
* <p>
* 测试任务
* </p>
*
* @author yangkai.shen
* @date Created in 2018-12-29 10:49
*/
@Slf4j
public class TaskFactoryTest extends SpringBootDemoAsyncApplicationTests {
@Autowired
private TaskFactory task;

/**
* 测试异步任务
*/
@Test
public void asyncTaskTest() throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
Future<Boolean> asyncTask1 = task.asyncTask1();
Future<Boolean> asyncTask2 = task.asyncTask2();
Future<Boolean> asyncTask3 = task.asyncTask3();

// 调用 get() 阻塞主线程
asyncTask1.get();
asyncTask2.get();
asyncTask3.get();
long end = System.currentTimeMillis();

log.info("异步任务全部执行结束,总耗时:{} 毫秒", (end - start));
}

/**
* 测试同步任务
*/
@Test
public void taskTest() throws InterruptedException {
long start = System.currentTimeMillis();
task.task1();
task.task2();
task.task3();
long end = System.currentTimeMillis();

log.info("同步任务全部执行结束,总耗时:{} 毫秒", (end - start));
}
}
```

## 运行结果

### 异步任务

```bash
2018-12-29 10:57:28.511 INFO 3134 --- [ async-task-3] com.xkcoding.async.task.TaskFactory : asyncTask3开始执行,当前线程名称【async-task-3】
2018-12-29 10:57:28.511 INFO 3134 --- [ async-task-1] com.xkcoding.async.task.TaskFactory : asyncTask1开始执行,当前线程名称【async-task-1】
2018-12-29 10:57:28.511 INFO 3134 --- [ async-task-2] com.xkcoding.async.task.TaskFactory : asyncTask2开始执行,当前线程名称【async-task-2】
2018-12-29 10:57:30.514 INFO 3134 --- [ async-task-2] com.xkcoding.async.task.TaskFactory : asyncTask2执行成功,当前线程名称【async-task-2】
2018-12-29 10:57:31.516 INFO 3134 --- [ async-task-3] com.xkcoding.async.task.TaskFactory : asyncTask3执行成功,当前线程名称【async-task-3】
2018-12-29 10:57:33.517 INFO 3134 --- [ async-task-1] com.xkcoding.async.task.TaskFactory : asyncTask1执行成功,当前线程名称【async-task-1】
2018-12-29 10:57:33.517 INFO 3134 --- [ main] com.xkcoding.async.task.TaskFactoryTest : 异步任务全部执行结束,总耗时:5015 毫秒
```

### 同步任务

```bash
2018-12-29 10:55:49.830 INFO 3079 --- [ main] com.xkcoding.async.task.TaskFactory : task1开始执行,当前线程名称【main】
2018-12-29 10:55:54.834 INFO 3079 --- [ main] com.xkcoding.async.task.TaskFactory : task1执行成功,当前线程名称【main】
2018-12-29 10:55:54.835 INFO 3079 --- [ main] com.xkcoding.async.task.TaskFactory : task2开始执行,当前线程名称【main】
2018-12-29 10:55:56.839 INFO 3079 --- [ main] com.xkcoding.async.task.TaskFactory : task2执行成功,当前线程名称【main】
2018-12-29 10:55:56.839 INFO 3079 --- [ main] com.xkcoding.async.task.TaskFactory : task3开始执行,当前线程名称【main】
2018-12-29 10:55:59.843 INFO 3079 --- [ main] com.xkcoding.async.task.TaskFactory : task3执行成功,当前线程名称【main】
2018-12-29 10:55:59.843 INFO 3079 --- [ main] com.xkcoding.async.task.TaskFactoryTest : 同步任务全部执行结束,总耗时:10023 毫秒
```

## 参考

- Spring Boot 异步任务线程池的配置 参考官方文档:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-task-execution-scheduling

+ 54
- 0
demo-async/pom.xml View File

@@ -0,0 +1,54 @@
<?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>

<artifactId>demo-async</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demo-async</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

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

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

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<finalName>demo-async</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

+ 24
- 0
demo-async/src/main/java/com/xkcoding/async/SpringBootDemoAsyncApplication.java View File

@@ -0,0 +1,24 @@
package com.xkcoding.async;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

/**
* <p>
* 启动器
* </p>
*
* @author yangkai.shen
* @date Created in 2018-12-29 10:28
*/
@EnableAsync
@SpringBootApplication
public class SpringBootDemoAsyncApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoAsyncApplication.class, args);
}

}


+ 76
- 0
demo-async/src/main/java/com/xkcoding/async/task/TaskFactory.java View File

@@ -0,0 +1,76 @@
package com.xkcoding.async.task;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
* <p>
* 任务工厂
* </p>
*
* @author yangkai.shen
* @date Created in 2018-12-29 10:37
*/
@Component
@Slf4j
public class TaskFactory {

/**
* 模拟5秒的异步任务
*/
@Async
public Future<Boolean> asyncTask1() throws InterruptedException {
doTask("asyncTask1", 5);
return new AsyncResult<>(Boolean.TRUE);
}

/**
* 模拟2秒的异步任务
*/
@Async
public Future<Boolean> asyncTask2() throws InterruptedException {
doTask("asyncTask2", 2);
return new AsyncResult<>(Boolean.TRUE);
}

/**
* 模拟3秒的异步任务
*/
@Async
public Future<Boolean> asyncTask3() throws InterruptedException {
doTask("asyncTask3", 3);
return new AsyncResult<>(Boolean.TRUE);
}

/**
* 模拟5秒的同步任务
*/
public void task1() throws InterruptedException {
doTask("task1", 5);
}

/**
* 模拟2秒的同步任务
*/
public void task2() throws InterruptedException {
doTask("task2", 2);
}

/**
* 模拟3秒的同步任务
*/
public void task3() throws InterruptedException {
doTask("task3", 3);
}

private void doTask(String taskName, Integer time) throws InterruptedException {
log.info("{}开始执行,当前线程名称【{}】", taskName, Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(time);
log.info("{}执行成功,当前线程名称【{}】", taskName, Thread.currentThread().getName());
}
}

spring-boot-demo-async/src/main/resources/application.yml → demo-async/src/main/resources/application.yml View File


spring-boot-demo-async/src/test/java/com/xkcoding/async/SpringBootDemoAsyncApplicationTests.java → demo-async/src/test/java/com/xkcoding/async/SpringBootDemoAsyncApplicationTests.java View File


+ 56
- 0
demo-async/src/test/java/com/xkcoding/async/task/TaskFactoryTest.java View File

@@ -0,0 +1,56 @@
package com.xkcoding.async.task;

import com.xkcoding.async.SpringBootDemoAsyncApplicationTests;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
* <p>
* 测试任务
* </p>
*
* @author yangkai.shen
* @date Created in 2018-12-29 10:49
*/
@Slf4j
public class TaskFactoryTest extends SpringBootDemoAsyncApplicationTests {
@Autowired
private TaskFactory task;

/**
* 测试异步任务
*/
@Test
public void asyncTaskTest() throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
Future<Boolean> asyncTask1 = task.asyncTask1();
Future<Boolean> asyncTask2 = task.asyncTask2();
Future<Boolean> asyncTask3 = task.asyncTask3();

// 调用 get() 阻塞主线程
asyncTask1.get();
asyncTask2.get();
asyncTask3.get();
long end = System.currentTimeMillis();

log.info("异步任务全部执行结束,总耗时:{} 毫秒", (end - start));
}

/**
* 测试同步任务
*/
@Test
public void taskTest() throws InterruptedException {
long start = System.currentTimeMillis();
task.task1();
task.task2();
task.task3();
long end = System.currentTimeMillis();

log.info("同步任务全部执行结束,总耗时:{} 毫秒", (end - start));
}
}

spring-boot-demo-cache-ehcache/.gitignore → demo-cache-ehcache/.gitignore View File


+ 286
- 0
demo-cache-ehcache/README.md View File

@@ -0,0 +1,286 @@
# spring-boot-demo-cache-ehcache

> 此 demo 主要演示了 Spring Boot 如何集成 ehcache 使用缓存。

## pom.xml

```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>

<artifactId>spring-boot-demo-cache-ehcache</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-boot-demo-cache-ehcache</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

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

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

<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>spring-boot-demo-cache-ehcache</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
```

## SpringBootDemoCacheEhcacheApplication.java

```java
/**
* <p>
* 启动类
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-16 17:02
*/
@SpringBootApplication
@EnableCaching
public class SpringBootDemoCacheEhcacheApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoCacheEhcacheApplication.class, args);
}
}
```

## application.yml

```yaml
spring:
cache:
type: ehcache
ehcache:
config: classpath:ehcache.xml
logging:
level:
com.xkcoding: debug
```

## ehcache.xml

```xml
<!-- ehcache配置 -->
<ehcache
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--缓存路径,用户目录下的base_ehcache目录-->
<diskStore path="user.home/base_ehcache"/>

<defaultCache
maxElementsInMemory="20000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>

<!--
缓存文件名:user,同样的可以配置多个缓存
maxElementsInMemory:内存中最多存储
eternal:外部存储
overflowToDisk:超出缓存到磁盘
diskPersistent:磁盘持久化
timeToLiveSeconds:缓存时间
diskExpiryThreadIntervalSeconds:磁盘过期时间
-->
<cache name="user"
maxElementsInMemory="20000"
eternal="true"
overflowToDisk="true"
diskPersistent="false"
timeToLiveSeconds="0"
diskExpiryThreadIntervalSeconds="120"/>

</ehcache>
```

## UserServiceImpl.java

```java
/**
* <p>
* UserService
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-16 16:54
*/
@Service
@Slf4j
public class UserServiceImpl implements UserService {
/**
* 模拟数据库
*/
private static final Map<Long, User> DATABASES = Maps.newConcurrentMap();

/**
* 初始化数据
*/
static {
DATABASES.put(1L, new User(1L, "user1"));
DATABASES.put(2L, new User(2L, "user2"));
DATABASES.put(3L, new User(3L, "user3"));
}

/**
* 保存或修改用户
*
* @param user 用户对象
* @return 操作结果
*/
@CachePut(value = "user", key = "#user.id")
@Override
public User saveOrUpdate(User user) {
DATABASES.put(user.getId(), user);
log.info("保存用户【user】= {}", user);
return user;
}

/**
* 获取用户
*
* @param id key值
* @return 返回结果
*/
@Cacheable(value = "user", key = "#id")
@Override
public User get(Long id) {
// 我们假设从数据库读取
log.info("查询用户【id】= {}", id);
return DATABASES.get(id);
}

/**
* 删除
*
* @param id key值
*/
@CacheEvict(value = "user", key = "#id")
@Override
public void delete(Long id) {
DATABASES.remove(id);
log.info("删除用户【id】= {}", id);
}
}
```

## UserServiceTest.java

```java
/**
* <p>
* ehcache缓存测试
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-16 16:58
*/
@Slf4j
public class UserServiceTest extends SpringBootDemoCacheEhcacheApplicationTests {

@Autowired
private UserService userService;

/**
* 获取两次,查看日志验证缓存
*/
@Test
public void getTwice() {
// 模拟查询id为1的用户
User user1 = userService.get(1L);
log.debug("【user1】= {}", user1);

// 再次查询
User user2 = userService.get(1L);
log.debug("【user2】= {}", user2);
// 查看日志,只打印一次日志,证明缓存生效
}

/**
* 先存,再查询,查看日志验证缓存
*/
@Test
public void getAfterSave() {
userService.saveOrUpdate(new User(4L, "user4"));

User user = userService.get(4L);
log.debug("【user】= {}", user);
// 查看日志,只打印保存用户的日志,查询是未触发查询日志,因此缓存生效
}

/**
* 测试删除,查看redis是否存在缓存数据
*/
@Test
public void deleteUser() {
// 查询一次,使ehcache中存在缓存数据
userService.get(1L);
// 删除,查看ehcache是否存在缓存数据
userService.delete(1L);
}
}
```

## 参考

- Ehcache 官网:http://www.ehcache.org/documentation/
- Spring Boot 官方文档:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-caching-provider-ehcache2
- 博客:https://juejin.im/post/5b308de9518825748b56ae1d

+ 69
- 0
demo-cache-ehcache/pom.xml View File

@@ -0,0 +1,69 @@
<?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>

<artifactId>demo-cache-ehcache</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demo-cache-ehcache</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

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

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

<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>demo-cache-ehcache</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

+ 22
- 0
demo-cache-ehcache/src/main/java/com/xkcoding/cache/ehcache/SpringBootDemoCacheEhcacheApplication.java View File

@@ -0,0 +1,22 @@
package com.xkcoding.cache.ehcache;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

/**
* <p>
* 启动类
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-16 17:02
*/
@SpringBootApplication
@EnableCaching
public class SpringBootDemoCacheEhcacheApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoCacheEhcacheApplication.class, args);
}
}

+ 30
- 0
demo-cache-ehcache/src/main/java/com/xkcoding/cache/ehcache/entity/User.java View File

@@ -0,0 +1,30 @@
package com.xkcoding.cache.ehcache.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
* <p>
* 用户实体
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-16 16:53
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 2892248514883451461L;
/**
* 主键id
*/
private Long id;
/**
* 姓名
*/
private String name;
}

+ 36
- 0
demo-cache-ehcache/src/main/java/com/xkcoding/cache/ehcache/service/UserService.java View File

@@ -0,0 +1,36 @@
package com.xkcoding.cache.ehcache.service;

import com.xkcoding.cache.ehcache.entity.User;

/**
* <p>
* UserService
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-16 16:53
*/
public interface UserService {
/**
* 保存或修改用户
*
* @param user 用户对象
* @return 操作结果
*/
User saveOrUpdate(User user);

/**
* 获取用户
*
* @param id key值
* @return 返回结果
*/
User get(Long id);

/**
* 删除
*
* @param id key值
*/
void delete(Long id);
}

+ 78
- 0
demo-cache-ehcache/src/main/java/com/xkcoding/cache/ehcache/service/impl/UserServiceImpl.java View File

@@ -0,0 +1,78 @@
package com.xkcoding.cache.ehcache.service.impl;

import com.google.common.collect.Maps;
import com.xkcoding.cache.ehcache.entity.User;
import com.xkcoding.cache.ehcache.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Map;

/**
* <p>
* UserService
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-16 16:54
*/
@Service
@Slf4j
public class UserServiceImpl implements UserService {
/**
* 模拟数据库
*/
private static final Map<Long, User> DATABASES = Maps.newConcurrentMap();

/**
* 初始化数据
*/
static {
DATABASES.put(1L, new User(1L, "user1"));
DATABASES.put(2L, new User(2L, "user2"));
DATABASES.put(3L, new User(3L, "user3"));
}

/**
* 保存或修改用户
*
* @param user 用户对象
* @return 操作结果
*/
@CachePut(value = "user", key = "#user.id")
@Override
public User saveOrUpdate(User user) {
DATABASES.put(user.getId(), user);
log.info("保存用户【user】= {}", user);
return user;
}

/**
* 获取用户
*
* @param id key值
* @return 返回结果
*/
@Cacheable(value = "user", key = "#id")
@Override
public User get(Long id) {
// 我们假设从数据库读取
log.info("查询用户【id】= {}", id);
return DATABASES.get(id);
}

/**
* 删除
*
* @param id key值
*/
@CacheEvict(value = "user", key = "#id")
@Override
public void delete(Long id) {
DATABASES.remove(id);
log.info("删除用户【id】= {}", id);
}
}

spring-boot-demo-cache-ehcache/src/main/resources/application.yml → demo-cache-ehcache/src/main/resources/application.yml View File


spring-boot-demo-cache-ehcache/src/main/resources/ehcache.xml → demo-cache-ehcache/src/main/resources/ehcache.xml View File


spring-boot-demo-cache-ehcache/src/test/java/com/xkcoding/cache/ehcache/SpringBootDemoCacheEhcacheApplicationTests.java → demo-cache-ehcache/src/test/java/com/xkcoding/cache/ehcache/SpringBootDemoCacheEhcacheApplicationTests.java View File


+ 60
- 0
demo-cache-ehcache/src/test/java/com/xkcoding/cache/ehcache/service/UserServiceTest.java View File

@@ -0,0 +1,60 @@
package com.xkcoding.cache.ehcache.service;

import com.xkcoding.cache.ehcache.SpringBootDemoCacheEhcacheApplicationTests;
import com.xkcoding.cache.ehcache.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

/**
* <p>
* ehcache缓存测试
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-16 16:58
*/
@Slf4j
public class UserServiceTest extends SpringBootDemoCacheEhcacheApplicationTests {

@Autowired
private UserService userService;

/**
* 获取两次,查看日志验证缓存
*/
@Test
public void getTwice() {
// 模拟查询id为1的用户
User user1 = userService.get(1L);
log.debug("【user1】= {}", user1);

// 再次查询
User user2 = userService.get(1L);
log.debug("【user2】= {}", user2);
// 查看日志,只打印一次日志,证明缓存生效
}

/**
* 先存,再查询,查看日志验证缓存
*/
@Test
public void getAfterSave() {
userService.saveOrUpdate(new User(4L, "user4"));

User user = userService.get(4L);
log.debug("【user】= {}", user);
// 查看日志,只打印保存用户的日志,查询是未触发查询日志,因此缓存生效
}

/**
* 测试删除,查看redis是否存在缓存数据
*/
@Test
public void deleteUser() {
// 查询一次,使ehcache中存在缓存数据
userService.get(1L);
// 删除,查看ehcache是否存在缓存数据
userService.delete(1L);
}
}

spring-boot-demo-cache-redis/.gitignore → demo-cache-redis/.gitignore View File


+ 347
- 0
demo-cache-redis/README.md View File

@@ -0,0 +1,347 @@
# spring-boot-demo-cache-redis

> 此 demo 主要演示了 Spring Boot 如何整合 redis,操作redis中的数据,并使用redis缓存数据。连接池使用 Lettuce。

## pom.xml

```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>

<artifactId>spring-boot-demo-cache-redis</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-boot-demo-cache-redis</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

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

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

<!-- 对象池,使用redis时必须引入 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

<!-- 引入 jackson 对象json转换 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>

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

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<finalName>spring-boot-demo-cache-redis</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
```

## application.yml

```yaml
spring:
redis:
host: localhost
# 连接超时时间(记得添加单位,Duration)
timeout: 10000ms
# Redis默认情况下有16个分片,这里配置具体使用的分片
# database: 0
lettuce:
pool:
# 连接池最大连接数(使用负值表示没有限制) 默认 8
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
max-wait: -1ms
# 连接池中的最大空闲连接 默认 8
max-idle: 8
# 连接池中的最小空闲连接 默认 0
min-idle: 0
cache:
# 一般来说是不用配置的,Spring Cache 会根据依赖的包自行装配
type: redis
logging:
level:
com.xkcoding: debug
```

## RedisConfig.java

```java
/**
* <p>
* redis配置
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-15 16:41
*/
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
@EnableCaching
public class RedisConfig {

/**
* 默认情况下的模板只能支持RedisTemplate<String, String>,也就是只能存入字符串,因此支持序列化
*/
@Bean
public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}

/**
* 配置使用注解的时候缓存配置,默认是序列化反序列化的形式,加上此配置则为 json 形式
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
// 配置序列化
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

return RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration).build();
}
}
```

## UserServiceImpl.java

```java
/**
* <p>
* UserService
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-15 16:45
*/
@Service
@Slf4j
public class UserServiceImpl implements UserService {
/**
* 模拟数据库
*/
private static final Map<Long, User> DATABASES = Maps.newConcurrentMap();

/**
* 初始化数据
*/
static {
DATABASES.put(1L, new User(1L, "user1"));
DATABASES.put(2L, new User(2L, "user2"));
DATABASES.put(3L, new User(3L, "user3"));
}

/**
* 保存或修改用户
*
* @param user 用户对象
* @return 操作结果
*/
@CachePut(value = "user", key = "#user.id")
@Override
public User saveOrUpdate(User user) {
DATABASES.put(user.getId(), user);
log.info("保存用户【user】= {}", user);
return user;
}

/**
* 获取用户
*
* @param id key值
* @return 返回结果
*/
@Cacheable(value = "user", key = "#id")
@Override
public User get(Long id) {
// 我们假设从数据库读取
log.info("查询用户【id】= {}", id);
return DATABASES.get(id);
}

/**
* 删除
*
* @param id key值
*/
@CacheEvict(value = "user", key = "#id")
@Override
public void delete(Long id) {
DATABASES.remove(id);
log.info("删除用户【id】= {}", id);
}
}
```

## RedisTest.java

> 主要测试使用 `RedisTemplate` 操作 `Redis` 中的数据:
>
> - opsForValue:对应 String(字符串)
> - opsForZSet:对应 ZSet(有序集合)
> - opsForHash:对应 Hash(哈希)
> - opsForList:对应 List(列表)
> - opsForSet:对应 Set(集合)
> - opsForGeo:** 对应 GEO(地理位置)

```java
/**
* <p>
* Redis测试
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-15 17:17
*/
@Slf4j
public class RedisTest extends SpringBootDemoCacheRedisApplicationTests {

@Autowired
private StringRedisTemplate stringRedisTemplate;

@Autowired
private RedisTemplate<String, Serializable> redisCacheTemplate;

/**
* 测试 Redis 操作
*/
@Test
public void get() {
// 测试线程安全,程序结束查看redis中count的值是否为1000
ExecutorService executorService = Executors.newFixedThreadPool(1000);
IntStream.range(0, 1000).forEach(i -> executorService.execute(() -> stringRedisTemplate.opsForValue().increment("count", 1)));

stringRedisTemplate.opsForValue().set("k1", "v1");
String k1 = stringRedisTemplate.opsForValue().get("k1");
log.debug("【k1】= {}", k1);

// 以下演示整合,具体Redis命令可以参考官方文档
String key = "xkcoding:user:1";
redisCacheTemplate.opsForValue().set(key, new User(1L, "user1"));
// 对应 String(字符串)
User user = (User) redisCacheTemplate.opsForValue().get(key);
log.debug("【user】= {}", user);
}
}

```

## UserServiceTest.java

> 主要测试使用Redis缓存是否起效

```java
/**
* <p>
* Redis - 缓存测试
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-15 16:53
*/
@Slf4j
public class UserServiceTest extends SpringBootDemoCacheRedisApplicationTests {
@Autowired
private UserService userService;

/**
* 获取两次,查看日志验证缓存
*/
@Test
public void getTwice() {
// 模拟查询id为1的用户
User user1 = userService.get(1L);
log.debug("【user1】= {}", user1);

// 再次查询
User user2 = userService.get(1L);
log.debug("【user2】= {}", user2);
// 查看日志,只打印一次日志,证明缓存生效
}

/**
* 先存,再查询,查看日志验证缓存
*/
@Test
public void getAfterSave() {
userService.saveOrUpdate(new User(4L, "测试中文"));

User user = userService.get(4L);
log.debug("【user】= {}", user);
// 查看日志,只打印保存用户的日志,查询是未触发查询日志,因此缓存生效
}

/**
* 测试删除,查看redis是否存在缓存数据
*/
@Test
public void deleteUser() {
// 查询一次,使redis中存在缓存数据
userService.get(1L);
// 删除,查看redis是否存在缓存数据
userService.delete(1L);
}

}
```

## 参考

- spring-data-redis 官方文档:https://docs.spring.io/spring-data/redis/docs/2.0.1.RELEASE/reference/html/
- redis 文档:https://redis.io/documentation
- redis 中文文档:http://www.redis.cn/commands.html

+ 81
- 0
demo-cache-redis/pom.xml View File

@@ -0,0 +1,81 @@
<?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>

<artifactId>demo-cache-redis</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demo-cache-redis</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

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

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

<!-- 对象池,使用redis时必须引入 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

<!-- 引入 jackson 对象json转换 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>

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

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<finalName>demo-cache-redis</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

spring-boot-demo-cache-redis/src/main/java/com/xkcoding/cache/redis/SpringBootDemoCacheRedisApplication.java → demo-cache-redis/src/main/java/com/xkcoding/cache/redis/SpringBootDemoCacheRedisApplication.java View File


+ 56
- 0
demo-cache-redis/src/main/java/com/xkcoding/cache/redis/config/RedisConfig.java View File

@@ -0,0 +1,56 @@
package com.xkcoding.cache.redis.config;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.io.Serializable;

/**
* <p>
* redis配置
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-15 16:41
*/
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
@EnableCaching
public class RedisConfig {

/**
* 默认情况下的模板只能支持RedisTemplate<String, String>,也就是只能存入字符串,因此支持序列化
*/
@Bean
public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}

/**
* 配置使用注解的时候缓存配置,默认是序列化反序列化的形式,加上此配置则为 json 形式
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
// 配置序列化
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

return RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration).build();
}
}

+ 30
- 0
demo-cache-redis/src/main/java/com/xkcoding/cache/redis/entity/User.java View File

@@ -0,0 +1,30 @@
package com.xkcoding.cache.redis.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
* <p>
* 用户实体
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-15 16:39
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 2892248514883451461L;
/**
* 主键id
*/
private Long id;
/**
* 姓名
*/
private String name;
}

+ 36
- 0
demo-cache-redis/src/main/java/com/xkcoding/cache/redis/service/UserService.java View File

@@ -0,0 +1,36 @@
package com.xkcoding.cache.redis.service;

import com.xkcoding.cache.redis.entity.User;

/**
* <p>
* UserService
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-15 16:45
*/
public interface UserService {
/**
* 保存或修改用户
*
* @param user 用户对象
* @return 操作结果
*/
User saveOrUpdate(User user);

/**
* 获取用户
*
* @param id key值
* @return 返回结果
*/
User get(Long id);

/**
* 删除
*
* @param id key值
*/
void delete(Long id);
}

+ 78
- 0
demo-cache-redis/src/main/java/com/xkcoding/cache/redis/service/impl/UserServiceImpl.java View File

@@ -0,0 +1,78 @@
package com.xkcoding.cache.redis.service.impl;

import com.google.common.collect.Maps;
import com.xkcoding.cache.redis.entity.User;
import com.xkcoding.cache.redis.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Map;

/**
* <p>
* UserService
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-15 16:45
*/
@Service
@Slf4j
public class UserServiceImpl implements UserService {
/**
* 模拟数据库
*/
private static final Map<Long, User> DATABASES = Maps.newConcurrentMap();

/**
* 初始化数据
*/
static {
DATABASES.put(1L, new User(1L, "user1"));
DATABASES.put(2L, new User(2L, "user2"));
DATABASES.put(3L, new User(3L, "user3"));
}

/**
* 保存或修改用户
*
* @param user 用户对象
* @return 操作结果
*/
@CachePut(value = "user", key = "#user.id")
@Override
public User saveOrUpdate(User user) {
DATABASES.put(user.getId(), user);
log.info("保存用户【user】= {}", user);
return user;
}

/**
* 获取用户
*
* @param id key值
* @return 返回结果
*/
@Cacheable(value = "user", key = "#id")
@Override
public User get(Long id) {
// 我们假设从数据库读取
log.info("查询用户【id】= {}", id);
return DATABASES.get(id);
}

/**
* 删除
*
* @param id key值
*/
@CacheEvict(value = "user", key = "#id")
@Override
public void delete(Long id) {
DATABASES.remove(id);
log.info("删除用户【id】= {}", id);
}
}

spring-boot-demo-cache-redis/src/main/resources/application.yml → demo-cache-redis/src/main/resources/application.yml View File


+ 52
- 0
demo-cache-redis/src/test/java/com/xkcoding/cache/redis/RedisTest.java View File

@@ -0,0 +1,52 @@
package com.xkcoding.cache.redis;

import com.xkcoding.cache.redis.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.io.Serializable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

/**
* <p>
* Redis测试
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-15 17:17
*/
@Slf4j
public class RedisTest extends SpringBootDemoCacheRedisApplicationTests {

@Autowired
private StringRedisTemplate stringRedisTemplate;

@Autowired
private RedisTemplate<String, Serializable> redisCacheTemplate;

/**
* 测试 Redis 操作
*/
@Test
public void get() {
// 测试线程安全,程序结束查看redis中count的值是否为1000
ExecutorService executorService = Executors.newFixedThreadPool(1000);
IntStream.range(0, 1000).forEach(i -> executorService.execute(() -> stringRedisTemplate.opsForValue().increment("count", 1)));

stringRedisTemplate.opsForValue().set("k1", "v1");
String k1 = stringRedisTemplate.opsForValue().get("k1");
log.debug("【k1】= {}", k1);

// 以下演示整合,具体Redis命令可以参考官方文档
String key = "xkcoding:user:1";
redisCacheTemplate.opsForValue().set(key, new User(1L, "user1"));
// 对应 String(字符串)
User user = (User) redisCacheTemplate.opsForValue().get(key);
log.debug("【user】= {}", user);
}
}

spring-boot-demo-cache-redis/src/test/java/com/xkcoding/cache/redis/SpringBootDemoCacheRedisApplicationTests.java → demo-cache-redis/src/test/java/com/xkcoding/cache/redis/SpringBootDemoCacheRedisApplicationTests.java View File


+ 60
- 0
demo-cache-redis/src/test/java/com/xkcoding/cache/redis/service/UserServiceTest.java View File

@@ -0,0 +1,60 @@
package com.xkcoding.cache.redis.service;

import com.xkcoding.cache.redis.SpringBootDemoCacheRedisApplicationTests;
import com.xkcoding.cache.redis.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

/**
* <p>
* Redis - 缓存测试
* </p>
*
* @author yangkai.shen
* @date Created in 2018-11-15 16:53
*/
@Slf4j
public class UserServiceTest extends SpringBootDemoCacheRedisApplicationTests {
@Autowired
private UserService userService;

/**
* 获取两次,查看日志验证缓存
*/
@Test
public void getTwice() {
// 模拟查询id为1的用户
User user1 = userService.get(1L);
log.debug("【user1】= {}", user1);

// 再次查询
User user2 = userService.get(1L);
log.debug("【user2】= {}", user2);
// 查看日志,只打印一次日志,证明缓存生效
}

/**
* 先存,再查询,查看日志验证缓存
*/
@Test
public void getAfterSave() {
userService.saveOrUpdate(new User(4L, "测试中文"));

User user = userService.get(4L);
log.debug("【user】= {}", user);
// 查看日志,只打印保存用户的日志,查询是未触发查询日志,因此缓存生效
}

/**
* 测试删除,查看redis是否存在缓存数据
*/
@Test
public void deleteUser() {
// 查询一次,使redis中存在缓存数据
userService.get(1L);
// 删除,查看redis是否存在缓存数据
userService.delete(1L);
}

}

spring-boot-demo-codegen/.gitignore → demo-codegen/.gitignore View File


+ 410
- 0
demo-codegen/README.md View File

@@ -0,0 +1,410 @@
# spring-boot-demo-codegen

> 此 demo 主要演示了 Spring Boot 使用**模板技术**生成代码,并提供前端页面,可生成 Entity/Mapper/Service/Controller 等代码。

## 1. 主要功能

1. 使用 `velocity` 代码生成
2. 暂时支持mysql数据库的代码生成
3. 提供前端页面展示,并下载代码压缩包

> 注意:① Entity里使用lombok,简化代码 ② Mapper 和 Service 层集成 Mybatis-Plus 简化代码

## 2. 运行

1. 运行 `SpringBootDemoCodegenApplication` 启动项目
2. 打开浏览器,输入 http://localhost:8080/demo/index.html
3. 输入查询条件,生成代码

## 3. 关键代码

### 3.1. pom.xml

```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>

<artifactId>spring-boot-demo-codegen</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-boot-demo-codegen</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>

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

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

<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>

<!--velocity代码生成使用模板 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.6</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<finalName>spring-boot-demo-codegen</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
```

### 3.2. 代码生成器配置

```properties
#代码生成器,配置信息
mainPath=com.xkcoding
#包名
package=com.xkcoding
moduleName=generator
#作者
author=Yangkai.Shen
#表前缀(类名不会包含表前缀)
tablePrefix=tb_
#类型转换,配置信息
tinyint=Integer
smallint=Integer
mediumint=Integer
int=Integer
integer=Integer
bigint=Long
float=Float
double=Double
decimal=BigDecimal
bit=Boolean
char=String
varchar=String
tinytext=String
text=String
mediumtext=String
longtext=String
date=LocalDateTime
datetime=LocalDateTime
timestamp=LocalDateTime
```

### 3.3. CodeGenUtil.java

```java
/**
* <p>
* 代码生成器 工具类
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 09:27
*/
@Slf4j
@UtilityClass
public class CodeGenUtil {

private final String ENTITY_JAVA_VM = "Entity.java.vm";
private final String MAPPER_JAVA_VM = "Mapper.java.vm";
private final String SERVICE_JAVA_VM = "Service.java.vm";
private final String SERVICE_IMPL_JAVA_VM = "ServiceImpl.java.vm";
private final String CONTROLLER_JAVA_VM = "Controller.java.vm";
private final String MAPPER_XML_VM = "Mapper.xml.vm";
private final String API_JS_VM = "api.js.vm";

private List<String> getTemplates() {
List<String> templates = new ArrayList<>();
templates.add("template/Entity.java.vm");
templates.add("template/Mapper.java.vm");
templates.add("template/Mapper.xml.vm");
templates.add("template/Service.java.vm");
templates.add("template/ServiceImpl.java.vm");
templates.add("template/Controller.java.vm");

templates.add("template/api.js.vm");
return templates;
}

/**
* 生成代码
*/
public void generatorCode(GenConfig genConfig, Entity table, List<Entity> columns, ZipOutputStream zip) {
//配置信息
Props props = getConfig();
boolean hasBigDecimal = false;
//表信息
TableEntity tableEntity = new TableEntity();
tableEntity.setTableName(table.getStr("tableName"));

if (StrUtil.isNotBlank(genConfig.getComments())) {
tableEntity.setComments(genConfig.getComments());
} else {
tableEntity.setComments(table.getStr("tableComment"));
}

String tablePrefix;
if (StrUtil.isNotBlank(genConfig.getTablePrefix())) {
tablePrefix = genConfig.getTablePrefix();
} else {
tablePrefix = props.getStr("tablePrefix");
}

//表名转换成Java类名
String className = tableToJava(tableEntity.getTableName(), tablePrefix);
tableEntity.setCaseClassName(className);
tableEntity.setLowerClassName(StrUtil.lowerFirst(className));

//列信息
List<ColumnEntity> columnList = Lists.newArrayList();
for (Entity column : columns) {
ColumnEntity columnEntity = new ColumnEntity();
columnEntity.setColumnName(column.getStr("columnName"));
columnEntity.setDataType(column.getStr("dataType"));
columnEntity.setComments(column.getStr("columnComment"));
columnEntity.setExtra(column.getStr("extra"));

//列名转换成Java属性名
String attrName = columnToJava(columnEntity.getColumnName());
columnEntity.setCaseAttrName(attrName);
columnEntity.setLowerAttrName(StrUtil.lowerFirst(attrName));

//列的数据类型,转换成Java类型
String attrType = props.getStr(columnEntity.getDataType(), "unknownType");
columnEntity.setAttrType(attrType);
if (!hasBigDecimal && "BigDecimal".equals(attrType)) {
hasBigDecimal = true;
}
//是否主键
if ("PRI".equalsIgnoreCase(column.getStr("columnKey")) && tableEntity.getPk() == null) {
tableEntity.setPk(columnEntity);
}

columnList.add(columnEntity);
}
tableEntity.setColumns(columnList);

//没主键,则第一个字段为主键
if (tableEntity.getPk() == null) {
tableEntity.setPk(tableEntity.getColumns().get(0));
}

//设置velocity资源加载器
Properties prop = new Properties();
prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
Velocity.init(prop);
//封装模板数据
Map<String, Object> map = new HashMap<>(16);
map.put("tableName", tableEntity.getTableName());
map.put("pk", tableEntity.getPk());
map.put("className", tableEntity.getCaseClassName());
map.put("classname", tableEntity.getLowerClassName());
map.put("pathName", tableEntity.getLowerClassName().toLowerCase());
map.put("columns", tableEntity.getColumns());
map.put("hasBigDecimal", hasBigDecimal);
map.put("datetime", DateUtil.now());
map.put("year", DateUtil.year(new Date()));

if (StrUtil.isNotBlank(genConfig.getComments())) {
map.put("comments", genConfig.getComments());
} else {
map.put("comments", tableEntity.getComments());
}

if (StrUtil.isNotBlank(genConfig.getAuthor())) {
map.put("author", genConfig.getAuthor());
} else {
map.put("author", props.getStr("author"));
}

if (StrUtil.isNotBlank(genConfig.getModuleName())) {
map.put("moduleName", genConfig.getModuleName());
} else {
map.put("moduleName", props.getStr("moduleName"));
}

if (StrUtil.isNotBlank(genConfig.getPackageName())) {
map.put("package", genConfig.getPackageName());
map.put("mainPath", genConfig.getPackageName());
} else {
map.put("package", props.getStr("package"));
map.put("mainPath", props.getStr("mainPath"));
}
VelocityContext context = new VelocityContext(map);

//获取模板列表
List<String> templates = getTemplates();
for (String template : templates) {
//渲染模板
StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, CharsetUtil.UTF_8);
tpl.merge(context, sw);

try {
//添加到zip
zip.putNextEntry(new ZipEntry(Objects.requireNonNull(getFileName(template, tableEntity.getCaseClassName(), map
.get("package")
.toString(), map.get("moduleName").toString()))));
IoUtil.write(zip, StandardCharsets.UTF_8, false, sw.toString());
IoUtil.close(sw);
zip.closeEntry();
} catch (IOException e) {
throw new RuntimeException("渲染模板失败,表名:" + tableEntity.getTableName(), e);
}
}
}


/**
* 列名转换成Java属性名
*/
private String columnToJava(String columnName) {
return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
}

/**
* 表名转换成Java类名
*/
private String tableToJava(String tableName, String tablePrefix) {
if (StrUtil.isNotBlank(tablePrefix)) {
tableName = tableName.replaceFirst(tablePrefix, "");
}
return columnToJava(tableName);
}

/**
* 获取配置信息
*/
private Props getConfig() {
Props props = new Props("generator.properties");
props.autoLoad(true);
return props;
}

/**
* 获取文件名
*/
private String getFileName(String template, String className, String packageName, String moduleName) {
// 包路径
String packagePath = GenConstants.SIGNATURE + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator;
// 资源路径
String resourcePath = GenConstants.SIGNATURE + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator;
// api路径
String apiPath = GenConstants.SIGNATURE + File.separator + "api" + File.separator;

if (StrUtil.isNotBlank(packageName)) {
packagePath += packageName.replace(".", File.separator) + File.separator + moduleName + File.separator;
}

if (template.contains(ENTITY_JAVA_VM)) {
return packagePath + "entity" + File.separator + className + ".java";
}

if (template.contains(MAPPER_JAVA_VM)) {
return packagePath + "mapper" + File.separator + className + "Mapper.java";
}

if (template.contains(SERVICE_JAVA_VM)) {
return packagePath + "service" + File.separator + className + "Service.java";
}

if (template.contains(SERVICE_IMPL_JAVA_VM)) {
return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
}

if (template.contains(CONTROLLER_JAVA_VM)) {
return packagePath + "controller" + File.separator + className + "Controller.java";
}

if (template.contains(MAPPER_XML_VM)) {
return resourcePath + "mapper" + File.separator + className + "Mapper.xml";
}

if (template.contains(API_JS_VM)) {
return apiPath + className.toLowerCase() + ".js";
}

return null;
}
}
```

### 3.4. 其余代码参见demo

## 4. 演示

<video id="video" controls="" preload="none">
<source id="mp4" src="https://static.xkcoding.com/code/spring-boot-demo/codegen/codegen.mp4" type="video/mp4">
<p>您的浏览器版本过低,不支持播放视频演示,可下载演示视频观看,https://static.xkcoding.com/code/spring-boot-demo/codegen/codegen.mp4</p>
</video>

## 5. 参考

- [基于人人开源 自动构建项目_V1](https://qq343509740.gitee.io/2018/12/20/%E7%AC%94%E8%AE%B0/%E8%87%AA%E5%8A%A8%E6%9E%84%E5%BB%BA%E9%A1%B9%E7%9B%AE/%E5%9F%BA%E4%BA%8E%E4%BA%BA%E4%BA%BA%E5%BC%80%E6%BA%90%20%E8%87%AA%E5%8A%A8%E6%9E%84%E5%BB%BA%E9%A1%B9%E7%9B%AE_V1/)

- [Mybatis-Plus代码生成器](https://mybatis.plus/guide/generator.html#%E6%B7%BB%E5%8A%A0%E4%BE%9D%E8%B5%96)

+ 98
- 0
demo-codegen/pom.xml View File

@@ -0,0 +1,98 @@
<?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>

<artifactId>demo-codegen</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demo-codegen</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>

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

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

<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>

<!--velocity代码生成使用模板 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.1</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.6</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<finalName>demo-codegen</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

+ 21
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/SpringBootDemoCodegenApplication.java View File

@@ -0,0 +1,21 @@
package com.xkcoding.codegen;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* <p>
* 启动器
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 09:10
*/
@SpringBootApplication
public class SpringBootDemoCodegenApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoCodegenApplication.class, args);
}

}

+ 25
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/common/IResultCode.java View File

@@ -0,0 +1,25 @@
package com.xkcoding.codegen.common;

/**
* <p>
* 统一状态码接口
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-21 16:28
*/
public interface IResultCode {
/**
* 获取状态码
*
* @return 状态码
*/
Integer getCode();

/**
* 获取返回消息
*
* @return 返回消息
*/
String getMessage();
}

+ 38
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/common/PageResult.java View File

@@ -0,0 +1,38 @@
package com.xkcoding.codegen.common;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.List;

/**
* <p>
* 分页结果集
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 11:24
*/
@Data
@AllArgsConstructor
public class PageResult<T> {
/**
* 总条数
*/
private Long total;

/**
* 页码
*/
private int pageNumber;

/**
* 每页结果数
*/
private int pageSize;

/**
* 结果集
*/
private List<T> list;
}

+ 90
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/common/R.java View File

@@ -0,0 +1,90 @@
package com.xkcoding.codegen.common;

import lombok.Data;
import lombok.NoArgsConstructor;

/**
* <p>
* 统一API对象返回
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 10:13
*/
@Data
@NoArgsConstructor
public class R<T> {
/**
* 状态码
*/
private Integer code;

/**
* 返回消息
*/
private String message;

/**
* 状态
*/
private boolean status;

/**
* 返回数据
*/
private T data;

public R(Integer code, String message, boolean status, T data) {
this.code = code;
this.message = message;
this.status = status;
this.data = data;
}

public R(IResultCode resultCode, boolean status, T data) {
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
this.status = status;
this.data = data;
}

public R(IResultCode resultCode, boolean status) {
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
this.status = status;
this.data = null;
}

public static <T> R success() {
return new R<>(ResultCode.OK, true);
}

public static <T> R message(String message) {
return new R<>(ResultCode.OK.getCode(), message, true, null);
}

public static <T> R success(T data) {
return new R<>(ResultCode.OK, true, data);
}

public static <T> R fail() {
return new R<>(ResultCode.ERROR, false);
}

public static <T> R fail(IResultCode resultCode) {
return new R<>(resultCode, false);
}

public static <T> R fail(Integer code, String message) {
return new R<>(code, message, false, null);
}

public static <T> R fail(IResultCode resultCode, T data) {
return new R<>(resultCode, false, data);
}

public static <T> R fail(Integer code, String message, T data) {
return new R<>(code, message, false, data);
}

}

+ 39
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/common/ResultCode.java View File

@@ -0,0 +1,39 @@
package com.xkcoding.codegen.common;

import lombok.Getter;

/**
* <p>
* 通用状态枚举
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 10:13
*/
@Getter
public enum ResultCode implements IResultCode {
/**
* 成功
*/
OK(200, "成功"),
/**
* 失败
*/
ERROR(500, "失败");

/**
* 返回码
*/
private Integer code;

/**
* 返回消息
*/
private String message;

ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}

}

+ 16
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/constants/GenConstants.java View File

@@ -0,0 +1,16 @@
package com.xkcoding.codegen.constants;

/**
* <p>
* 常量池
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 10:04
*/
public interface GenConstants {
/**
* 签名
*/
String SIGNATURE = "xkcoding代码生成";
}

+ 55
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/controller/CodeGenController.java View File

@@ -0,0 +1,55 @@
package com.xkcoding.codegen.controller;

import cn.hutool.core.io.IoUtil;
import com.xkcoding.codegen.common.R;
import com.xkcoding.codegen.entity.GenConfig;
import com.xkcoding.codegen.entity.TableRequest;
import com.xkcoding.codegen.service.CodeGenService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;

/**
* <p>
* 代码生成器
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 10:11
*/
@RestController
@AllArgsConstructor
@RequestMapping("/generator")
public class CodeGenController {
private final CodeGenService codeGenService;

/**
* 列表
*
* @param request 参数集
* @return 数据库表
*/
@GetMapping("/table")
public R listTables(TableRequest request) {
return R.success(codeGenService.listTables(request));
}

/**
* 生成代码
*/
@SneakyThrows
@PostMapping("")
public void generatorCode(@RequestBody GenConfig genConfig, HttpServletResponse response) {
byte[] data = codeGenService.generatorCode(genConfig);

response.reset();
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s.zip", genConfig.getTableName()));
response.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(data.length));
response.setContentType("application/octet-stream; charset=UTF-8");

IoUtil.write(response.getOutputStream(), Boolean.TRUE, data);
}
}

+ 47
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/entity/ColumnEntity.java View File

@@ -0,0 +1,47 @@
package com.xkcoding.codegen.entity;

import lombok.Data;

/**
* <p>
* 列属性: https://blog.csdn.net/lkforce/article/details/79557482
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 09:46
*/
@Data
public class ColumnEntity {
/**
* 列表
*/
private String columnName;
/**
* 数据类型
*/
private String dataType;
/**
* 备注
*/
private String comments;
/**
* 驼峰属性
*/
private String caseAttrName;
/**
* 普通属性
*/
private String lowerAttrName;
/**
* 属性类型
*/
private String attrType;
/**
* jdbc类型
*/
private String jdbcType;
/**
* 其他信息
*/
private String extra;
}

+ 43
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/entity/GenConfig.java View File

@@ -0,0 +1,43 @@
package com.xkcoding.codegen.entity;

import lombok.Data;

/**
* <p>
* 生成配置
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 09:47
*/
@Data
public class GenConfig {
/**
* 请求参数
*/
private TableRequest request;
/**
* 包名
*/
private String packageName;
/**
* 作者
*/
private String author;
/**
* 模块名称
*/
private String moduleName;
/**
* 表前缀
*/
private String tablePrefix;
/**
* 表名称
*/
private String tableName;
/**
* 表备注
*/
private String comments;
}

+ 41
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/entity/TableEntity.java View File

@@ -0,0 +1,41 @@
package com.xkcoding.codegen.entity;

import lombok.Data;

import java.util.List;

/**
* <p>
* 表属性: https://blog.csdn.net/lkforce/article/details/79557482
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 09:47
*/
@Data
public class TableEntity {
/**
* 名称
*/
private String tableName;
/**
* 备注
*/
private String comments;
/**
* 主键
*/
private ColumnEntity pk;
/**
* 列名
*/
private List<ColumnEntity> columns;
/**
* 驼峰类型
*/
private String caseClassName;
/**
* 普通类型
*/
private String lowerClassName;
}

+ 43
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/entity/TableRequest.java View File

@@ -0,0 +1,43 @@
package com.xkcoding.codegen.entity;

import lombok.Data;

/**
* <p>
* 表格请求参数
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 10:24
*/
@Data
public class TableRequest {
/**
* 当前页
*/
private Integer currentPage;
/**
* 每页条数
*/
private Integer pageSize;
/**
* jdbc-前缀
*/
private String prepend;
/**
* jdbc-url
*/
private String url;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 表名
*/
private String tableName;
}

+ 32
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/service/CodeGenService.java View File

@@ -0,0 +1,32 @@
package com.xkcoding.codegen.service;

import cn.hutool.db.Entity;
import com.xkcoding.codegen.common.PageResult;
import com.xkcoding.codegen.entity.GenConfig;
import com.xkcoding.codegen.entity.TableRequest;

/**
* <p>
* 代码生成器
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 10:15
*/
public interface CodeGenService {
/**
* 生成代码
*
* @param genConfig 生成配置
* @return 代码压缩文件
*/
byte[] generatorCode(GenConfig genConfig);

/**
* 分页查询表信息
*
* @param request 请求参数
* @return 表名分页信息
*/
PageResult<Entity> listTables(TableRequest request);
}

+ 130
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/service/impl/CodeGenServiceImpl.java View File

@@ -0,0 +1,130 @@
package com.xkcoding.codegen.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.Page;
import com.xkcoding.codegen.common.PageResult;
import com.xkcoding.codegen.entity.GenConfig;
import com.xkcoding.codegen.entity.TableRequest;
import com.xkcoding.codegen.service.CodeGenService;
import com.xkcoding.codegen.utils.CodeGenUtil;
import com.xkcoding.codegen.utils.DbUtil;
import com.zaxxer.hikari.HikariDataSource;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.util.List;
import java.util.zip.ZipOutputStream;

/**
* <p>
* 代码生成器
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 10:15
*/
@Service
@AllArgsConstructor
public class CodeGenServiceImpl implements CodeGenService {
private final String TABLE_SQL_TEMPLATE = "select table_name tableName, engine, table_comment tableComment, create_time createTime from information_schema.tables where table_schema = (select database()) %s order by create_time desc";

private final String COLUMN_SQL_TEMPLATE = "select column_name columnName, data_type dataType, column_comment columnComment, column_key columnKey, extra from information_schema.columns where table_name = ? and table_schema = (select database()) order by ordinal_position";

private final String COUNT_SQL_TEMPLATE = "select count(1) from (%s)tmp";

private final String PAGE_SQL_TEMPLATE = " limit ?,?";

/**
* 分页查询表信息
*
* @param request 请求参数
* @return 表名分页信息
*/
@Override
@SneakyThrows
public PageResult<Entity> listTables(TableRequest request) {
HikariDataSource dataSource = DbUtil.buildFromTableRequest(request);
Db db = new Db(dataSource);

Page page = new Page(request.getCurrentPage(), request.getPageSize());
int start = page.getStartPosition();
int pageSize = page.getPageSize();

String paramSql = StrUtil.EMPTY;
if (StrUtil.isNotBlank(request.getTableName())) {
paramSql = "and table_name like concat('%', ?, '%')";
}
String sql = String.format(TABLE_SQL_TEMPLATE, paramSql);
String countSql = String.format(COUNT_SQL_TEMPLATE, sql);

List<Entity> query;
BigDecimal count;
if (StrUtil.isNotBlank(request.getTableName())) {
query = db.query(sql + PAGE_SQL_TEMPLATE, request.getTableName(), start, pageSize);
count = (BigDecimal) db.queryNumber(countSql, request.getTableName());
} else {
query = db.query(sql + PAGE_SQL_TEMPLATE, start, pageSize);
count = (BigDecimal) db.queryNumber(countSql);
}

PageResult<Entity> pageResult = new PageResult<>(count.longValue(), page.getPageNumber(), page.getPageSize(), query);

dataSource.close();
return pageResult;
}

/**
* 生成代码
*
* @param genConfig 生成配置
* @return 代码压缩文件
*/
@Override
public byte[] generatorCode(GenConfig genConfig) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(outputStream);

//查询表信息
Entity table = queryTable(genConfig.getRequest());
//查询列信息
List<Entity> columns = queryColumns(genConfig.getRequest());
//生成代码
CodeGenUtil.generatorCode(genConfig, table, columns, zip);
IoUtil.close(zip);
return outputStream.toByteArray();
}

@SneakyThrows
private Entity queryTable(TableRequest request) {
HikariDataSource dataSource = DbUtil.buildFromTableRequest(request);
Db db = new Db(dataSource);

String paramSql = StrUtil.EMPTY;
if (StrUtil.isNotBlank(request.getTableName())) {
paramSql = "and table_name = ?";
}
String sql = String.format(TABLE_SQL_TEMPLATE, paramSql);
Entity entity = db.queryOne(sql, request.getTableName());

dataSource.close();
return entity;
}

@SneakyThrows
private List<Entity> queryColumns(TableRequest request) {
HikariDataSource dataSource = DbUtil.buildFromTableRequest(request);
Db db = new Db(dataSource);

List<Entity> query = db.query(COLUMN_SQL_TEMPLATE, request.getTableName());

dataSource.close();
return query;
}

}

+ 264
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/utils/CodeGenUtil.java View File

@@ -0,0 +1,264 @@
package com.xkcoding.codegen.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import cn.hutool.setting.dialect.Props;
import com.google.common.collect.Lists;
import com.xkcoding.codegen.constants.GenConstants;
import com.xkcoding.codegen.entity.ColumnEntity;
import com.xkcoding.codegen.entity.GenConfig;
import com.xkcoding.codegen.entity.TableEntity;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.WordUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
* <p>
* 代码生成器 工具类
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 09:27
*/
@Slf4j
@UtilityClass
public class CodeGenUtil {

private final String ENTITY_JAVA_VM = "Entity.java.vm";
private final String MAPPER_JAVA_VM = "Mapper.java.vm";
private final String SERVICE_JAVA_VM = "Service.java.vm";
private final String SERVICE_IMPL_JAVA_VM = "ServiceImpl.java.vm";
private final String CONTROLLER_JAVA_VM = "Controller.java.vm";
private final String MAPPER_XML_VM = "Mapper.xml.vm";
private final String API_JS_VM = "api.js.vm";

private List<String> getTemplates() {
List<String> templates = new ArrayList<>();
templates.add("template/Entity.java.vm");
templates.add("template/Mapper.java.vm");
templates.add("template/Mapper.xml.vm");
templates.add("template/Service.java.vm");
templates.add("template/ServiceImpl.java.vm");
templates.add("template/Controller.java.vm");

templates.add("template/api.js.vm");
return templates;
}

/**
* 生成代码
*/
public void generatorCode(GenConfig genConfig, Entity table, List<Entity> columns, ZipOutputStream zip) {
//配置信息
Props propsDB2Java = getConfig("generator.properties");
Props propsDB2Jdbc = getConfig("jdbc_type.properties");

boolean hasBigDecimal = false;
//表信息
TableEntity tableEntity = new TableEntity();
tableEntity.setTableName(table.getStr("tableName"));

if (StrUtil.isNotBlank(genConfig.getComments())) {
tableEntity.setComments(genConfig.getComments());
} else {
tableEntity.setComments(table.getStr("tableComment"));
}

String tablePrefix;
if (StrUtil.isNotBlank(genConfig.getTablePrefix())) {
tablePrefix = genConfig.getTablePrefix();
} else {
tablePrefix = propsDB2Java.getStr("tablePrefix");
}

//表名转换成Java类名
String className = tableToJava(tableEntity.getTableName(), tablePrefix);
tableEntity.setCaseClassName(className);
tableEntity.setLowerClassName(StrUtil.lowerFirst(className));

//列信息
List<ColumnEntity> columnList = Lists.newArrayList();
for (Entity column : columns) {
ColumnEntity columnEntity = new ColumnEntity();
columnEntity.setColumnName(column.getStr("columnName"));
columnEntity.setDataType(column.getStr("dataType"));
columnEntity.setComments(column.getStr("columnComment"));
columnEntity.setExtra(column.getStr("extra"));

//列名转换成Java属性名
String attrName = columnToJava(columnEntity.getColumnName());
columnEntity.setCaseAttrName(attrName);
columnEntity.setLowerAttrName(StrUtil.lowerFirst(attrName));

//列的数据类型,转换成Java类型
String attrType = propsDB2Java.getStr(columnEntity.getDataType(), "unknownType");
columnEntity.setAttrType(attrType);
String jdbcType = propsDB2Jdbc.getStr(columnEntity.getDataType(), "unknownType");
columnEntity.setJdbcType(jdbcType);
if (!hasBigDecimal && "BigDecimal".equals(attrType)) {
hasBigDecimal = true;
}
//是否主键
if ("PRI".equalsIgnoreCase(column.getStr("columnKey")) && tableEntity.getPk() == null) {
tableEntity.setPk(columnEntity);
}

columnList.add(columnEntity);
}
tableEntity.setColumns(columnList);

//没主键,则第一个字段为主键
if (tableEntity.getPk() == null) {
tableEntity.setPk(tableEntity.getColumns().get(0));
}

//设置velocity资源加载器
Properties prop = new Properties();
prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
Velocity.init(prop);
//封装模板数据
Map<String, Object> map = new HashMap<>(16);
map.put("tableName", tableEntity.getTableName());
map.put("pk", tableEntity.getPk());
map.put("className", tableEntity.getCaseClassName());
map.put("classname", tableEntity.getLowerClassName());
map.put("pathName", tableEntity.getLowerClassName().toLowerCase());
map.put("columns", tableEntity.getColumns());
map.put("hasBigDecimal", hasBigDecimal);
map.put("datetime", DateUtil.now());
map.put("year", DateUtil.year(new Date()));

if (StrUtil.isNotBlank(genConfig.getComments())) {
map.put("comments", genConfig.getComments());
} else {
map.put("comments", tableEntity.getComments());
}

if (StrUtil.isNotBlank(genConfig.getAuthor())) {
map.put("author", genConfig.getAuthor());
} else {
map.put("author", propsDB2Java.getStr("author"));
}

if (StrUtil.isNotBlank(genConfig.getModuleName())) {
map.put("moduleName", genConfig.getModuleName());
} else {
map.put("moduleName", propsDB2Java.getStr("moduleName"));
}

if (StrUtil.isNotBlank(genConfig.getPackageName())) {
map.put("package", genConfig.getPackageName());
map.put("mainPath", genConfig.getPackageName());
} else {
map.put("package", propsDB2Java.getStr("package"));
map.put("mainPath", propsDB2Java.getStr("mainPath"));
}
VelocityContext context = new VelocityContext(map);

//获取模板列表
List<String> templates = getTemplates();
for (String template : templates) {
//渲染模板
StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, CharsetUtil.UTF_8);
tpl.merge(context, sw);

try {
//添加到zip
zip.putNextEntry(new ZipEntry(Objects.requireNonNull(getFileName(template, tableEntity.getCaseClassName(), map.get("package").toString(), map.get("moduleName").toString()))));
IoUtil.write(zip, StandardCharsets.UTF_8, false, sw.toString());
IoUtil.close(sw);
zip.closeEntry();
} catch (IOException e) {
throw new RuntimeException("渲染模板失败,表名:" + tableEntity.getTableName(), e);
}
}
}


/**
* 列名转换成Java属性名
*/
private String columnToJava(String columnName) {
return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
}

/**
* 表名转换成Java类名
*/
private String tableToJava(String tableName, String tablePrefix) {
if (StrUtil.isNotBlank(tablePrefix)) {
tableName = tableName.replaceFirst(tablePrefix, "");
}
return columnToJava(tableName);
}

/**
* 获取配置信息
*/
private Props getConfig(String fileName) {
Props props = new Props(fileName);
props.autoLoad(true);
return props;
}

/**
* 获取文件名
*/
private String getFileName(String template, String className, String packageName, String moduleName) {
// 包路径
String packagePath = GenConstants.SIGNATURE + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator;
// 资源路径
String resourcePath = GenConstants.SIGNATURE + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator;
// api路径
String apiPath = GenConstants.SIGNATURE + File.separator + "api" + File.separator;

if (StrUtil.isNotBlank(packageName)) {
packagePath += packageName.replace(".", File.separator) + File.separator + moduleName + File.separator;
}

if (template.contains(ENTITY_JAVA_VM)) {
return packagePath + "entity" + File.separator + className + ".java";
}

if (template.contains(MAPPER_JAVA_VM)) {
return packagePath + "mapper" + File.separator + className + "Mapper.java";
}

if (template.contains(SERVICE_JAVA_VM)) {
return packagePath + "service" + File.separator + className + "Service.java";
}

if (template.contains(SERVICE_IMPL_JAVA_VM)) {
return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
}

if (template.contains(CONTROLLER_JAVA_VM)) {
return packagePath + "controller" + File.separator + className + "Controller.java";
}

if (template.contains(MAPPER_XML_VM)) {
return resourcePath + "mapper" + File.separator + className + "Mapper.xml";
}

if (template.contains(API_JS_VM)) {
return apiPath + className.toLowerCase() + ".js";
}

return null;
}
}

+ 27
- 0
demo-codegen/src/main/java/com/xkcoding/codegen/utils/DbUtil.java View File

@@ -0,0 +1,27 @@
package com.xkcoding.codegen.utils;

import com.xkcoding.codegen.entity.TableRequest;
import com.zaxxer.hikari.HikariDataSource;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

/**
* <p>
* 数据库工具类
* </p>
*
* @author yangkai.shen
* @date Created in 2019-03-22 10:26
*/
@Slf4j
@UtilityClass
public class DbUtil {
public HikariDataSource buildFromTableRequest(TableRequest request) {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(request.getPrepend() + request.getUrl());
dataSource.setUsername(request.getUsername());
dataSource.setPassword(request.getPassword());
return dataSource;
}

}

spring-boot-demo-codegen/src/main/resources/application.yml → demo-codegen/src/main/resources/application.yml View File


spring-boot-demo-codegen/src/main/resources/generator.properties → demo-codegen/src/main/resources/generator.properties View File


spring-boot-demo-codegen/src/main/resources/jdbc_type.properties → demo-codegen/src/main/resources/jdbc_type.properties View File


+ 79
- 0
demo-codegen/src/main/resources/logback-spring.xml View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="FILE_ERROR_PATTERN"
value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} %file:%line: %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>

<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--如果只是想要 Info 级别的日志,只是过滤 info 还是会输出 Error 日志,因为 Error 的级别高, 所以我们使用下面的策略,可以避免输出 Error 的日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!--过滤 Error-->
<level>ERROR</level>
<!--匹配到就禁止-->
<onMatch>DENY</onMatch>
<!--没有匹配到就允许-->
<onMismatch>ACCEPT</onMismatch>
</filter>
<!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。-->
<!--<File>logs/info.demo-logback.log</File>-->
<!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
<FileNamePattern>logs/demo-logback/info.created_on_%d{yyyy-MM-dd}.part_%i.log</FileNamePattern>
<!--只保留最近90天的日志-->
<maxHistory>90</maxHistory>
<!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
<!--<totalSizeCap>1GB</totalSizeCap>-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- maxFileSize:这是活动文件的大小,默认值是10MB,本篇设置为1KB,只是为了演示 -->
<maxFileSize>2MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!--<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">-->
<!--<maxFileSize>1KB</maxFileSize>-->
<!--</triggeringPolicy>-->
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
</appender>

<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--如果只是想要 Error 级别的日志,那么需要过滤一下,默认是 info 级别的,ThresholdFilter-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>Error</level>
</filter>
<!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。-->
<!--<File>logs/error.demo-logback.log</File>-->
<!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
<FileNamePattern>logs/demo-logback/error.created_on_%d{yyyy-MM-dd}.part_%i.log</FileNamePattern>
<!--只保留最近90天的日志-->
<maxHistory>90</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- maxFileSize:这是活动文件的大小,默认值是10MB,本篇设置为1KB,只是为了演示 -->
<maxFileSize>2MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>${FILE_ERROR_PATTERN}</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
</appender>

<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE_INFO"/>
<appender-ref ref="FILE_ERROR"/>
</root>
</configuration>

spring-boot-demo-codegen/src/main/resources/static/index.html → demo-codegen/src/main/resources/static/index.html View File


spring-boot-demo-codegen/src/main/resources/static/libs/axios/axios.min.js → demo-codegen/src/main/resources/static/libs/axios/axios.min.js View File


+ 145
- 0
demo-codegen/src/main/resources/static/libs/datejs/date-zh-CN.js View File

@@ -0,0 +1,145 @@
/**
* @version: 1.0 Alpha-1
* @author Coolite Inc. http://www.coolite.com/
* @date: 2008-05-13
* @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved.
* @license: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/.
* @website: http://www.datejs.com/
*/
Date.CultureInfo={name:"zh-CN",englishName:"Chinese (People's Republic of China)",nativeName:"中文(中华人民共和国)",dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],abbreviatedDayNames:["日","一","二","三","四","五","六"],shortestDayNames:["日","一","二","三","四","五","六"],firstLetterDayNames:["日","一","二","三","四","五","六"],monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],abbreviatedMonthNames:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],amDesignator:"上午",pmDesignator:"下午",firstDayOfWeek:0,twoDigitYearMax:2029,dateElementOrder:"ymd",formatPatterns:{shortDate:"yyyy/M/d",longDate:"yyyy'年'M'月'd'日'",shortTime:"H:mm",longTime:"H:mm:ss",fullDateTime:"yyyy'年'M'月'd'日' H:mm:ss",sortableDateTime:"yyyy-MM-ddTHH:mm:ss",universalSortableDateTime:"yyyy-MM-dd HH:mm:ssZ",rfc1123:"ddd, dd MMM yyyy HH:mm:ss GMT",monthDay:"M'月'd'日'",yearMonth:"yyyy'年'M'月'"},regexPatterns:{jan:/^一月/i,feb:/^二月/i,mar:/^三月/i,apr:/^四月/i,may:/^五月/i,jun:/^六月/i,jul:/^七月/i,aug:/^八月/i,sep:/^九月/i,oct:/^十月/i,nov:/^十一月/i,dec:/^十二月/i,sun:/^星期日/i,mon:/^星期一/i,tue:/^星期二/i,wed:/^星期三/i,thu:/^星期四/i,fri:/^星期五/i,sat:/^星期六/i,future:/^next/i,past:/^last|past|prev(ious)?/i,add:/^(\+|aft(er)?|from|hence)/i,subtract:/^(\-|bef(ore)?|ago)/i,yesterday:/^yes(terday)?/i,today:/^t(od(ay)?)?/i,tomorrow:/^tom(orrow)?/i,now:/^n(ow)?/i,millisecond:/^ms|milli(second)?s?/i,second:/^sec(ond)?s?/i,minute:/^mn|min(ute)?s?/i,hour:/^h(our)?s?/i,week:/^w(eek)?s?/i,month:/^m(onth)?s?/i,day:/^d(ay)?s?/i,year:/^y(ear)?s?/i,shortMeridian:/^(a|p)/i,longMeridian:/^(a\.?m?\.?|p\.?m?\.?)/i,timezone:/^((e(s|d)t|c(s|d)t|m(s|d)t|p(s|d)t)|((gmt)?\s*(\+|\-)\s*\d\d\d\d?)|gmt|utc)/i,ordinalSuffix:/^\s*(st|nd|rd|th)/i,timeContext:/^\s*(\:|a(?!u|p)|p)/i},timezones:[{name:"UTC",offset:"-000"},{name:"GMT",offset:"-000"},{name:"EST",offset:"-0500"},{name:"EDT",offset:"-0400"},{name:"CST",offset:"-0600"},{name:"CDT",offset:"-0500"},{name:"MST",offset:"-0700"},{name:"MDT",offset:"-0600"},{name:"PST",offset:"-0800"},{name:"PDT",offset:"-0700"}]};
(function(){var $D=Date,$P=$D.prototype,$C=$D.CultureInfo,p=function(s,l){if(!l){l=2;}
return("000"+s).slice(l*-1);};$P.clearTime=function(){this.setHours(0);this.setMinutes(0);this.setSeconds(0);this.setMilliseconds(0);return this;};$P.setTimeToNow=function(){var n=new Date();this.setHours(n.getHours());this.setMinutes(n.getMinutes());this.setSeconds(n.getSeconds());this.setMilliseconds(n.getMilliseconds());return this;};$D.today=function(){return new Date().clearTime();};$D.compare=function(date1,date2){if(isNaN(date1)||isNaN(date2)){throw new Error(date1+" - "+date2);}else if(date1 instanceof Date&&date2 instanceof Date){return(date1<date2)?-1:(date1>date2)?1:0;}else{throw new TypeError(date1+" - "+date2);}};$D.equals=function(date1,date2){return(date1.compareTo(date2)===0);};$D.getDayNumberFromName=function(name){var n=$C.dayNames,m=$C.abbreviatedDayNames,o=$C.shortestDayNames,s=name.toLowerCase();for(var i=0;i<n.length;i++){if(n[i].toLowerCase()==s||m[i].toLowerCase()==s||o[i].toLowerCase()==s){return i;}}
return-1;};$D.getMonthNumberFromName=function(name){var n=$C.monthNames,m=$C.abbreviatedMonthNames,s=name.toLowerCase();for(var i=0;i<n.length;i++){if(n[i].toLowerCase()==s||m[i].toLowerCase()==s){return i;}}
return-1;};$D.isLeapYear=function(year){return((year%4===0&&year%100!==0)||year%400===0);};$D.getDaysInMonth=function(year,month){return[31,($D.isLeapYear(year)?29:28),31,30,31,30,31,31,30,31,30,31][month];};$D.getTimezoneAbbreviation=function(offset){var z=$C.timezones,p;for(var i=0;i<z.length;i++){if(z[i].offset===offset){return z[i].name;}}
return null;};$D.getTimezoneOffset=function(name){var z=$C.timezones,p;for(var i=0;i<z.length;i++){if(z[i].name===name.toUpperCase()){return z[i].offset;}}
return null;};$P.clone=function(){return new Date(this.getTime());};$P.compareTo=function(date){return Date.compare(this,date);};$P.equals=function(date){return Date.equals(this,date||new Date());};$P.between=function(start,end){return this.getTime()>=start.getTime()&&this.getTime()<=end.getTime();};$P.isAfter=function(date){return this.compareTo(date||new Date())===1;};$P.isBefore=function(date){return(this.compareTo(date||new Date())===-1);};$P.isToday=function(){return this.isSameDay(new Date());};$P.isSameDay=function(date){return this.clone().clearTime().equals(date.clone().clearTime());};$P.addMilliseconds=function(value){this.setMilliseconds(this.getMilliseconds()+value);return this;};$P.addSeconds=function(value){return this.addMilliseconds(value*1000);};$P.addMinutes=function(value){return this.addMilliseconds(value*60000);};$P.addHours=function(value){return this.addMilliseconds(value*3600000);};$P.addDays=function(value){this.setDate(this.getDate()+value);return this;};$P.addWeeks=function(value){return this.addDays(value*7);};$P.addMonths=function(value){var n=this.getDate();this.setDate(1);this.setMonth(this.getMonth()+value);this.setDate(Math.min(n,$D.getDaysInMonth(this.getFullYear(),this.getMonth())));return this;};$P.addYears=function(value){return this.addMonths(value*12);};$P.add=function(config){if(typeof config=="number"){this._orient=config;return this;}
var x=config;if(x.milliseconds){this.addMilliseconds(x.milliseconds);}
if(x.seconds){this.addSeconds(x.seconds);}
if(x.minutes){this.addMinutes(x.minutes);}
if(x.hours){this.addHours(x.hours);}
if(x.weeks){this.addWeeks(x.weeks);}
if(x.months){this.addMonths(x.months);}
if(x.years){this.addYears(x.years);}
if(x.days){this.addDays(x.days);}
return this;};var $y,$m,$d;$P.getWeek=function(){var a,b,c,d,e,f,g,n,s,w;$y=(!$y)?this.getFullYear():$y;$m=(!$m)?this.getMonth()+1:$m;$d=(!$d)?this.getDate():$d;if($m<=2){a=$y-1;b=(a/4|0)-(a/100|0)+(a/400|0);c=((a-1)/4|0)-((a-1)/100|0)+((a-1)/400|0);s=b-c;e=0;f=$d-1+(31*($m-1));}else{a=$y;b=(a/4|0)-(a/100|0)+(a/400|0);c=((a-1)/4|0)-((a-1)/100|0)+((a-1)/400|0);s=b-c;e=s+1;f=$d+((153*($m-3)+2)/5)+58+s;}
g=(a+b)%7;d=(f+g-e)%7;n=(f+3-d)|0;if(n<0){w=53-((g-s)/5|0);}else if(n>364+s){w=1;}else{w=(n/7|0)+1;}
$y=$m=$d=null;return w;};$P.getISOWeek=function(){$y=this.getUTCFullYear();$m=this.getUTCMonth()+1;$d=this.getUTCDate();return p(this.getWeek());};$P.setWeek=function(n){return this.moveToDayOfWeek(1).addWeeks(n-this.getWeek());};$D._validate=function(n,min,max,name){if(typeof n=="undefined"){return false;}else if(typeof n!="number"){throw new TypeError(n+" is not a Number.");}else if(n<min||n>max){throw new RangeError(n+" is not a valid value for "+name+".");}
return true;};$D.validateMillisecond=function(value){return $D._validate(value,0,999,"millisecond");};$D.validateSecond=function(value){return $D._validate(value,0,59,"second");};$D.validateMinute=function(value){return $D._validate(value,0,59,"minute");};$D.validateHour=function(value){return $D._validate(value,0,23,"hour");};$D.validateDay=function(value,year,month){return $D._validate(value,1,$D.getDaysInMonth(year,month),"day");};$D.validateMonth=function(value){return $D._validate(value,0,11,"month");};$D.validateYear=function(value){return $D._validate(value,0,9999,"year");};$P.set=function(config){if($D.validateMillisecond(config.millisecond)){this.addMilliseconds(config.millisecond-this.getMilliseconds());}
if($D.validateSecond(config.second)){this.addSeconds(config.second-this.getSeconds());}
if($D.validateMinute(config.minute)){this.addMinutes(config.minute-this.getMinutes());}
if($D.validateHour(config.hour)){this.addHours(config.hour-this.getHours());}
if($D.validateMonth(config.month)){this.addMonths(config.month-this.getMonth());}
if($D.validateYear(config.year)){this.addYears(config.year-this.getFullYear());}
if($D.validateDay(config.day,this.getFullYear(),this.getMonth())){this.addDays(config.day-this.getDate());}
if(config.timezone){this.setTimezone(config.timezone);}
if(config.timezoneOffset){this.setTimezoneOffset(config.timezoneOffset);}
if(config.week&&$D._validate(config.week,0,53,"week")){this.setWeek(config.week);}
return this;};$P.moveToFirstDayOfMonth=function(){return this.set({day:1});};$P.moveToLastDayOfMonth=function(){return this.set({day:$D.getDaysInMonth(this.getFullYear(),this.getMonth())});};$P.moveToNthOccurrence=function(dayOfWeek,occurrence){var shift=0;if(occurrence>0){shift=occurrence-1;}
else if(occurrence===-1){this.moveToLastDayOfMonth();if(this.getDay()!==dayOfWeek){this.moveToDayOfWeek(dayOfWeek,-1);}
return this;}
return this.moveToFirstDayOfMonth().addDays(-1).moveToDayOfWeek(dayOfWeek,+1).addWeeks(shift);};$P.moveToDayOfWeek=function(dayOfWeek,orient){var diff=(dayOfWeek-this.getDay()+7*(orient||+1))%7;return this.addDays((diff===0)?diff+=7*(orient||+1):diff);};$P.moveToMonth=function(month,orient){var diff=(month-this.getMonth()+12*(orient||+1))%12;return this.addMonths((diff===0)?diff+=12*(orient||+1):diff);};$P.getOrdinalNumber=function(){return Math.ceil((this.clone().clearTime()-new Date(this.getFullYear(),0,1))/86400000)+1;};$P.getTimezone=function(){return $D.getTimezoneAbbreviation(this.getUTCOffset());};$P.setTimezoneOffset=function(offset){var here=this.getTimezoneOffset(),there=Number(offset)*-6/10;return this.addMinutes(there-here);};$P.setTimezone=function(offset){return this.setTimezoneOffset($D.getTimezoneOffset(offset));};$P.hasDaylightSavingTime=function(){return(Date.today().set({month:0,day:1}).getTimezoneOffset()!==Date.today().set({month:6,day:1}).getTimezoneOffset());};$P.isDaylightSavingTime=function(){return(this.hasDaylightSavingTime()&&new Date().getTimezoneOffset()===Date.today().set({month:6,day:1}).getTimezoneOffset());};$P.getUTCOffset=function(){var n=this.getTimezoneOffset()*-10/6,r;if(n<0){r=(n-10000).toString();return r.charAt(0)+r.substr(2);}else{r=(n+10000).toString();return"+"+r.substr(1);}};$P.getElapsed=function(date){return(date||new Date())-this;};if(!$P.toISOString){$P.toISOString=function(){function f(n){return n<10?'0'+n:n;}
return'"'+this.getUTCFullYear()+'-'+
f(this.getUTCMonth()+1)+'-'+
f(this.getUTCDate())+'T'+
f(this.getUTCHours())+':'+
f(this.getUTCMinutes())+':'+
f(this.getUTCSeconds())+'Z"';};}
$P._toString=$P.toString;$P.toString=function(format){var x=this;if(format&&format.length==1){var c=$C.formatPatterns;x.t=x.toString;switch(format){case"d":return x.t(c.shortDate);case"D":return x.t(c.longDate);case"F":return x.t(c.fullDateTime);case"m":return x.t(c.monthDay);case"r":return x.t(c.rfc1123);case"s":return x.t(c.sortableDateTime);case"t":return x.t(c.shortTime);case"T":return x.t(c.longTime);case"u":return x.t(c.universalSortableDateTime);case"y":return x.t(c.yearMonth);}}
var ord=function(n){switch(n*1){case 1:case 21:case 31:return"st";case 2:case 22:return"nd";case 3:case 23:return"rd";default:return"th";}};return format?format.replace(/(\\)?(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|S)/g,function(m){if(m.charAt(0)==="\\"){return m.replace("\\","");}
x.h=x.getHours;switch(m){case"hh":return p(x.h()<13?(x.h()===0?12:x.h()):(x.h()-12));case"h":return x.h()<13?(x.h()===0?12:x.h()):(x.h()-12);case"HH":return p(x.h());case"H":return x.h();case"mm":return p(x.getMinutes());case"m":return x.getMinutes();case"ss":return p(x.getSeconds());case"s":return x.getSeconds();case"yyyy":return p(x.getFullYear(),4);case"yy":return p(x.getFullYear());case"dddd":return $C.dayNames[x.getDay()];case"ddd":return $C.abbreviatedDayNames[x.getDay()];case"dd":return p(x.getDate());case"d":return x.getDate();case"MMMM":return $C.monthNames[x.getMonth()];case"MMM":return $C.abbreviatedMonthNames[x.getMonth()];case"MM":return p((x.getMonth()+1));case"M":return x.getMonth()+1;case"t":return x.h()<12?$C.amDesignator.substring(0,1):$C.pmDesignator.substring(0,1);case"tt":return x.h()<12?$C.amDesignator:$C.pmDesignator;case"S":return ord(x.getDate());default:return m;}}):this._toString();};}());
(function(){var $D=Date,$P=$D.prototype,$C=$D.CultureInfo,$N=Number.prototype;$P._orient=+1;$P._nth=null;$P._is=false;$P._same=false;$P._isSecond=false;$N._dateElement="day";$P.next=function(){this._orient=+1;return this;};$D.next=function(){return $D.today().next();};$P.last=$P.prev=$P.previous=function(){this._orient=-1;return this;};$D.last=$D.prev=$D.previous=function(){return $D.today().last();};$P.is=function(){this._is=true;return this;};$P.same=function(){this._same=true;this._isSecond=false;return this;};$P.today=function(){return this.same().day();};$P.weekday=function(){if(this._is){this._is=false;return(!this.is().sat()&&!this.is().sun());}
return false;};$P.at=function(time){return(typeof time==="string")?$D.parse(this.toString("d")+" "+time):this.set(time);};$N.fromNow=$N.after=function(date){var c={};c[this._dateElement]=this;return((!date)?new Date():date.clone()).add(c);};$N.ago=$N.before=function(date){var c={};c[this._dateElement]=this*-1;return((!date)?new Date():date.clone()).add(c);};var dx=("sunday monday tuesday wednesday thursday friday saturday").split(/\s/),mx=("january february march april may june july august september october november december").split(/\s/),px=("Millisecond Second Minute Hour Day Week Month Year").split(/\s/),pxf=("Milliseconds Seconds Minutes Hours Date Week Month FullYear").split(/\s/),nth=("final first second third fourth fifth").split(/\s/),de;$P.toObject=function(){var o={};for(var i=0;i<px.length;i++){o[px[i].toLowerCase()]=this["get"+pxf[i]]();}
return o;};$D.fromObject=function(config){config.week=null;return Date.today().set(config);};var df=function(n){return function(){if(this._is){this._is=false;return this.getDay()==n;}
if(this._nth!==null){if(this._isSecond){this.addSeconds(this._orient*-1);}
this._isSecond=false;var ntemp=this._nth;this._nth=null;var temp=this.clone().moveToLastDayOfMonth();this.moveToNthOccurrence(n,ntemp);if(this>temp){throw new RangeError($D.getDayName(n)+" does not occur "+ntemp+" times in the month of "+$D.getMonthName(temp.getMonth())+" "+temp.getFullYear()+".");}
return this;}
return this.moveToDayOfWeek(n,this._orient);};};var sdf=function(n){return function(){var t=$D.today(),shift=n-t.getDay();if(n===0&&$C.firstDayOfWeek===1&&t.getDay()!==0){shift=shift+7;}
return t.addDays(shift);};};for(var i=0;i<dx.length;i++){$D[dx[i].toUpperCase()]=$D[dx[i].toUpperCase().substring(0,3)]=i;$D[dx[i]]=$D[dx[i].substring(0,3)]=sdf(i);$P[dx[i]]=$P[dx[i].substring(0,3)]=df(i);}
var mf=function(n){return function(){if(this._is){this._is=false;return this.getMonth()===n;}
return this.moveToMonth(n,this._orient);};};var smf=function(n){return function(){return $D.today().set({month:n,day:1});};};for(var j=0;j<mx.length;j++){$D[mx[j].toUpperCase()]=$D[mx[j].toUpperCase().substring(0,3)]=j;$D[mx[j]]=$D[mx[j].substring(0,3)]=smf(j);$P[mx[j]]=$P[mx[j].substring(0,3)]=mf(j);}
var ef=function(j){return function(){if(this._isSecond){this._isSecond=false;return this;}
if(this._same){this._same=this._is=false;var o1=this.toObject(),o2=(arguments[0]||new Date()).toObject(),v="",k=j.toLowerCase();for(var m=(px.length-1);m>-1;m--){v=px[m].toLowerCase();if(o1[v]!=o2[v]){return false;}
if(k==v){break;}}
return true;}
if(j.substring(j.length-1)!="s"){j+="s";}
return this["add"+j](this._orient);};};var nf=function(n){return function(){this._dateElement=n;return this;};};for(var k=0;k<px.length;k++){de=px[k].toLowerCase();$P[de]=$P[de+"s"]=ef(px[k]);$N[de]=$N[de+"s"]=nf(de);}
$P._ss=ef("Second");var nthfn=function(n){return function(dayOfWeek){if(this._same){return this._ss(arguments[0]);}
if(dayOfWeek||dayOfWeek===0){return this.moveToNthOccurrence(dayOfWeek,n);}
this._nth=n;if(n===2&&(dayOfWeek===undefined||dayOfWeek===null)){this._isSecond=true;return this.addSeconds(this._orient);}
return this;};};for(var l=0;l<nth.length;l++){$P[nth[l]]=(l===0)?nthfn(-1):nthfn(l);}}());
(function(){Date.Parsing={Exception:function(s){this.message="Parse error at '"+s.substring(0,10)+" ...'";}};var $P=Date.Parsing;var _=$P.Operators={rtoken:function(r){return function(s){var mx=s.match(r);if(mx){return([mx[0],s.substring(mx[0].length)]);}else{throw new $P.Exception(s);}};},token:function(s){return function(s){return _.rtoken(new RegExp("^\s*"+s+"\s*"))(s);};},stoken:function(s){return _.rtoken(new RegExp("^"+s));},until:function(p){return function(s){var qx=[],rx=null;while(s.length){try{rx=p.call(this,s);}catch(e){qx.push(rx[0]);s=rx[1];continue;}
break;}
return[qx,s];};},many:function(p){return function(s){var rx=[],r=null;while(s.length){try{r=p.call(this,s);}catch(e){return[rx,s];}
rx.push(r[0]);s=r[1];}
return[rx,s];};},optional:function(p){return function(s){var r=null;try{r=p.call(this,s);}catch(e){return[null,s];}
return[r[0],r[1]];};},not:function(p){return function(s){try{p.call(this,s);}catch(e){return[null,s];}
throw new $P.Exception(s);};},ignore:function(p){return p?function(s){var r=null;r=p.call(this,s);return[null,r[1]];}:null;},product:function(){var px=arguments[0],qx=Array.prototype.slice.call(arguments,1),rx=[];for(var i=0;i<px.length;i++){rx.push(_.each(px[i],qx));}
return rx;},cache:function(rule){var cache={},r=null;return function(s){try{r=cache[s]=(cache[s]||rule.call(this,s));}catch(e){r=cache[s]=e;}
if(r instanceof $P.Exception){throw r;}else{return r;}};},any:function(){var px=arguments;return function(s){var r=null;for(var i=0;i<px.length;i++){if(px[i]==null){continue;}
try{r=(px[i].call(this,s));}catch(e){r=null;}
if(r){return r;}}
throw new $P.Exception(s);};},each:function(){var px=arguments;return function(s){var rx=[],r=null;for(var i=0;i<px.length;i++){if(px[i]==null){continue;}
try{r=(px[i].call(this,s));}catch(e){throw new $P.Exception(s);}
rx.push(r[0]);s=r[1];}
return[rx,s];};},all:function(){var px=arguments,_=_;return _.each(_.optional(px));},sequence:function(px,d,c){d=d||_.rtoken(/^\s*/);c=c||null;if(px.length==1){return px[0];}
return function(s){var r=null,q=null;var rx=[];for(var i=0;i<px.length;i++){try{r=px[i].call(this,s);}catch(e){break;}
rx.push(r[0]);try{q=d.call(this,r[1]);}catch(ex){q=null;break;}
s=q[1];}
if(!r){throw new $P.Exception(s);}
if(q){throw new $P.Exception(q[1]);}
if(c){try{r=c.call(this,r[1]);}catch(ey){throw new $P.Exception(r[1]);}}
return[rx,(r?r[1]:s)];};},between:function(d1,p,d2){d2=d2||d1;var _fn=_.each(_.ignore(d1),p,_.ignore(d2));return function(s){var rx=_fn.call(this,s);return[[rx[0][0],r[0][2]],rx[1]];};},list:function(p,d,c){d=d||_.rtoken(/^\s*/);c=c||null;return(p instanceof Array?_.each(_.product(p.slice(0,-1),_.ignore(d)),p.slice(-1),_.ignore(c)):_.each(_.many(_.each(p,_.ignore(d))),px,_.ignore(c)));},set:function(px,d,c){d=d||_.rtoken(/^\s*/);c=c||null;return function(s){var r=null,p=null,q=null,rx=null,best=[[],s],last=false;for(var i=0;i<px.length;i++){q=null;p=null;r=null;last=(px.length==1);try{r=px[i].call(this,s);}catch(e){continue;}
rx=[[r[0]],r[1]];if(r[1].length>0&&!last){try{q=d.call(this,r[1]);}catch(ex){last=true;}}else{last=true;}
if(!last&&q[1].length===0){last=true;}
if(!last){var qx=[];for(var j=0;j<px.length;j++){if(i!=j){qx.push(px[j]);}}
p=_.set(qx,d).call(this,q[1]);if(p[0].length>0){rx[0]=rx[0].concat(p[0]);rx[1]=p[1];}}
if(rx[1].length<best[1].length){best=rx;}
if(best[1].length===0){break;}}
if(best[0].length===0){return best;}
if(c){try{q=c.call(this,best[1]);}catch(ey){throw new $P.Exception(best[1]);}
best[1]=q[1];}
return best;};},forward:function(gr,fname){return function(s){return gr[fname].call(this,s);};},replace:function(rule,repl){return function(s){var r=rule.call(this,s);return[repl,r[1]];};},process:function(rule,fn){return function(s){var r=rule.call(this,s);return[fn.call(this,r[0]),r[1]];};},min:function(min,rule){return function(s){var rx=rule.call(this,s);if(rx[0].length<min){throw new $P.Exception(s);}
return rx;};}};var _generator=function(op){return function(){var args=null,rx=[];if(arguments.length>1){args=Array.prototype.slice.call(arguments);}else if(arguments[0]instanceof Array){args=arguments[0];}
if(args){for(var i=0,px=args.shift();i<px.length;i++){args.unshift(px[i]);rx.push(op.apply(null,args));args.shift();return rx;}}else{return op.apply(null,arguments);}};};var gx="optional not ignore cache".split(/\s/);for(var i=0;i<gx.length;i++){_[gx[i]]=_generator(_[gx[i]]);}
var _vector=function(op){return function(){if(arguments[0]instanceof Array){return op.apply(null,arguments[0]);}else{return op.apply(null,arguments);}};};var vx="each any all".split(/\s/);for(var j=0;j<vx.length;j++){_[vx[j]]=_vector(_[vx[j]]);}}());(function(){var $D=Date,$P=$D.prototype,$C=$D.CultureInfo;var flattenAndCompact=function(ax){var rx=[];for(var i=0;i<ax.length;i++){if(ax[i]instanceof Array){rx=rx.concat(flattenAndCompact(ax[i]));}else{if(ax[i]){rx.push(ax[i]);}}}
return rx;};$D.Grammar={};$D.Translator={hour:function(s){return function(){this.hour=Number(s);};},minute:function(s){return function(){this.minute=Number(s);};},second:function(s){return function(){this.second=Number(s);};},meridian:function(s){return function(){this.meridian=s.slice(0,1).toLowerCase();};},timezone:function(s){return function(){var n=s.replace(/[^\d\+\-]/g,"");if(n.length){this.timezoneOffset=Number(n);}else{this.timezone=s.toLowerCase();}};},day:function(x){var s=x[0];return function(){this.day=Number(s.match(/\d+/)[0]);};},month:function(s){return function(){this.month=(s.length==3)?"jan feb mar apr may jun jul aug sep oct nov dec".indexOf(s)/4:Number(s)-1;};},year:function(s){return function(){var n=Number(s);this.year=((s.length>2)?n:(n+(((n+2000)<$C.twoDigitYearMax)?2000:1900)));};},rday:function(s){return function(){switch(s){case"yesterday":this.days=-1;break;case"tomorrow":this.days=1;break;case"today":this.days=0;break;case"now":this.days=0;this.now=true;break;}};},finishExact:function(x){x=(x instanceof Array)?x:[x];for(var i=0;i<x.length;i++){if(x[i]){x[i].call(this);}}
var now=new Date();if((this.hour||this.minute)&&(!this.month&&!this.year&&!this.day)){this.day=now.getDate();}
if(!this.year){this.year=now.getFullYear();}
if(!this.month&&this.month!==0){this.month=now.getMonth();}
if(!this.day){this.day=1;}
if(!this.hour){this.hour=0;}
if(!this.minute){this.minute=0;}
if(!this.second){this.second=0;}
if(this.meridian&&this.hour){if(this.meridian=="p"&&this.hour<12){this.hour=this.hour+12;}else if(this.meridian=="a"&&this.hour==12){this.hour=0;}}
if(this.day>$D.getDaysInMonth(this.year,this.month)){throw new RangeError(this.day+" is not a valid value for days.");}
var r=new Date(this.year,this.month,this.day,this.hour,this.minute,this.second);if(this.timezone){r.set({timezone:this.timezone});}else if(this.timezoneOffset){r.set({timezoneOffset:this.timezoneOffset});}
return r;},finish:function(x){x=(x instanceof Array)?flattenAndCompact(x):[x];if(x.length===0){return null;}
for(var i=0;i<x.length;i++){if(typeof x[i]=="function"){x[i].call(this);}}
var today=$D.today();if(this.now&&!this.unit&&!this.operator){return new Date();}else if(this.now){today=new Date();}
var expression=!!(this.days&&this.days!==null||this.orient||this.operator);var gap,mod,orient;orient=((this.orient=="past"||this.operator=="subtract")?-1:1);if(!this.now&&"hour minute second".indexOf(this.unit)!=-1){today.setTimeToNow();}
if(this.month||this.month===0){if("year day hour minute second".indexOf(this.unit)!=-1){this.value=this.month+1;this.month=null;expression=true;}}
if(!expression&&this.weekday&&!this.day&&!this.days){var temp=Date[this.weekday]();this.day=temp.getDate();if(!this.month){this.month=temp.getMonth();}
this.year=temp.getFullYear();}
if(expression&&this.weekday&&this.unit!="month"){this.unit="day";gap=($D.getDayNumberFromName(this.weekday)-today.getDay());mod=7;this.days=gap?((gap+(orient*mod))%mod):(orient*mod);}
if(this.month&&this.unit=="day"&&this.operator){this.value=(this.month+1);this.month=null;}
if(this.value!=null&&this.month!=null&&this.year!=null){this.day=this.value*1;}
if(this.month&&!this.day&&this.value){today.set({day:this.value*1});if(!expression){this.day=this.value*1;}}
if(!this.month&&this.value&&this.unit=="month"&&!this.now){this.month=this.value;expression=true;}
if(expression&&(this.month||this.month===0)&&this.unit!="year"){this.unit="month";gap=(this.month-today.getMonth());mod=12;this.months=gap?((gap+(orient*mod))%mod):(orient*mod);this.month=null;}
if(!this.unit){this.unit="day";}
if(!this.value&&this.operator&&this.operator!==null&&this[this.unit+"s"]&&this[this.unit+"s"]!==null){this[this.unit+"s"]=this[this.unit+"s"]+((this.operator=="add")?1:-1)+(this.value||0)*orient;}else if(this[this.unit+"s"]==null||this.operator!=null){if(!this.value){this.value=1;}
this[this.unit+"s"]=this.value*orient;}
if(this.meridian&&this.hour){if(this.meridian=="p"&&this.hour<12){this.hour=this.hour+12;}else if(this.meridian=="a"&&this.hour==12){this.hour=0;}}
if(this.weekday&&!this.day&&!this.days){var temp=Date[this.weekday]();this.day=temp.getDate();if(temp.getMonth()!==today.getMonth()){this.month=temp.getMonth();}}
if((this.month||this.month===0)&&!this.day){this.day=1;}
if(!this.orient&&!this.operator&&this.unit=="week"&&this.value&&!this.day&&!this.month){return Date.today().setWeek(this.value);}
if(expression&&this.timezone&&this.day&&this.days){this.day=this.days;}
return(expression)?today.add(this):today.set(this);}};var _=$D.Parsing.Operators,g=$D.Grammar,t=$D.Translator,_fn;g.datePartDelimiter=_.rtoken(/^([\s\-\.\,\/\x27]+)/);g.timePartDelimiter=_.stoken(":");g.whiteSpace=_.rtoken(/^\s*/);g.generalDelimiter=_.rtoken(/^(([\s\,]|at|@|on)+)/);var _C={};g.ctoken=function(keys){var fn=_C[keys];if(!fn){var c=$C.regexPatterns;var kx=keys.split(/\s+/),px=[];for(var i=0;i<kx.length;i++){px.push(_.replace(_.rtoken(c[kx[i]]),kx[i]));}
fn=_C[keys]=_.any.apply(null,px);}
return fn;};g.ctoken2=function(key){return _.rtoken($C.regexPatterns[key]);};g.h=_.cache(_.process(_.rtoken(/^(0[0-9]|1[0-2]|[1-9])/),t.hour));g.hh=_.cache(_.process(_.rtoken(/^(0[0-9]|1[0-2])/),t.hour));g.H=_.cache(_.process(_.rtoken(/^([0-1][0-9]|2[0-3]|[0-9])/),t.hour));g.HH=_.cache(_.process(_.rtoken(/^([0-1][0-9]|2[0-3])/),t.hour));g.m=_.cache(_.process(_.rtoken(/^([0-5][0-9]|[0-9])/),t.minute));g.mm=_.cache(_.process(_.rtoken(/^[0-5][0-9]/),t.minute));g.s=_.cache(_.process(_.rtoken(/^([0-5][0-9]|[0-9])/),t.second));g.ss=_.cache(_.process(_.rtoken(/^[0-5][0-9]/),t.second));g.hms=_.cache(_.sequence([g.H,g.m,g.s],g.timePartDelimiter));g.t=_.cache(_.process(g.ctoken2("shortMeridian"),t.meridian));g.tt=_.cache(_.process(g.ctoken2("longMeridian"),t.meridian));g.z=_.cache(_.process(_.rtoken(/^((\+|\-)\s*\d\d\d\d)|((\+|\-)\d\d\:?\d\d)/),t.timezone));g.zz=_.cache(_.process(_.rtoken(/^((\+|\-)\s*\d\d\d\d)|((\+|\-)\d\d\:?\d\d)/),t.timezone));g.zzz=_.cache(_.process(g.ctoken2("timezone"),t.timezone));g.timeSuffix=_.each(_.ignore(g.whiteSpace),_.set([g.tt,g.zzz]));g.time=_.each(_.optional(_.ignore(_.stoken("T"))),g.hms,g.timeSuffix);g.d=_.cache(_.process(_.each(_.rtoken(/^([0-2]\d|3[0-1]|\d)/),_.optional(g.ctoken2("ordinalSuffix"))),t.day));g.dd=_.cache(_.process(_.each(_.rtoken(/^([0-2]\d|3[0-1])/),_.optional(g.ctoken2("ordinalSuffix"))),t.day));g.ddd=g.dddd=_.cache(_.process(g.ctoken("sun mon tue wed thu fri sat"),function(s){return function(){this.weekday=s;};}));g.M=_.cache(_.process(_.rtoken(/^(1[0-2]|0\d|\d)/),t.month));g.MM=_.cache(_.process(_.rtoken(/^(1[0-2]|0\d)/),t.month));g.MMM=g.MMMM=_.cache(_.process(g.ctoken("jan feb mar apr may jun jul aug sep oct nov dec"),t.month));g.y=_.cache(_.process(_.rtoken(/^(\d\d?)/),t.year));g.yy=_.cache(_.process(_.rtoken(/^(\d\d)/),t.year));g.yyy=_.cache(_.process(_.rtoken(/^(\d\d?\d?\d?)/),t.year));g.yyyy=_.cache(_.process(_.rtoken(/^(\d\d\d\d)/),t.year));_fn=function(){return _.each(_.any.apply(null,arguments),_.not(g.ctoken2("timeContext")));};g.day=_fn(g.d,g.dd);g.month=_fn(g.M,g.MMM);g.year=_fn(g.yyyy,g.yy);g.orientation=_.process(g.ctoken("past future"),function(s){return function(){this.orient=s;};});g.operator=_.process(g.ctoken("add subtract"),function(s){return function(){this.operator=s;};});g.rday=_.process(g.ctoken("yesterday tomorrow today now"),t.rday);g.unit=_.process(g.ctoken("second minute hour day week month year"),function(s){return function(){this.unit=s;};});g.value=_.process(_.rtoken(/^\d\d?(st|nd|rd|th)?/),function(s){return function(){this.value=s.replace(/\D/g,"");};});g.expression=_.set([g.rday,g.operator,g.value,g.unit,g.orientation,g.ddd,g.MMM]);_fn=function(){return _.set(arguments,g.datePartDelimiter);};g.mdy=_fn(g.ddd,g.month,g.day,g.year);g.ymd=_fn(g.ddd,g.year,g.month,g.day);g.dmy=_fn(g.ddd,g.day,g.month,g.year);g.date=function(s){return((g[$C.dateElementOrder]||g.mdy).call(this,s));};g.format=_.process(_.many(_.any(_.process(_.rtoken(/^(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|zz?z?)/),function(fmt){if(g[fmt]){return g[fmt];}else{throw $D.Parsing.Exception(fmt);}}),_.process(_.rtoken(/^[^dMyhHmstz]+/),function(s){return _.ignore(_.stoken(s));}))),function(rules){return _.process(_.each.apply(null,rules),t.finishExact);});var _F={};var _get=function(f){return _F[f]=(_F[f]||g.format(f)[0]);};g.formats=function(fx){if(fx instanceof Array){var rx=[];for(var i=0;i<fx.length;i++){rx.push(_get(fx[i]));}
return _.any.apply(null,rx);}else{return _get(fx);}};g._formats=g.formats(["\"yyyy-MM-ddTHH:mm:ssZ\"","yyyy-MM-ddTHH:mm:ssZ","yyyy-MM-ddTHH:mm:ssz","yyyy-MM-ddTHH:mm:ss","yyyy-MM-ddTHH:mmZ","yyyy-MM-ddTHH:mmz","yyyy-MM-ddTHH:mm","ddd, MMM dd, yyyy H:mm:ss tt","ddd MMM d yyyy HH:mm:ss zzz","MMddyyyy","ddMMyyyy","Mddyyyy","ddMyyyy","Mdyyyy","dMyyyy","yyyy","Mdyy","dMyy","d"]);g._start=_.process(_.set([g.date,g.time,g.expression],g.generalDelimiter,g.whiteSpace),t.finish);g.start=function(s){try{var r=g._formats.call({},s);if(r[1].length===0){return r;}}catch(e){}
return g._start.call({},s);};$D._parse=$D.parse;$D.parse=function(s){var r=null;if(!s){return null;}
if(s instanceof Date){return s;}
try{r=$D.Grammar.start.call({},s.replace(/^\s*(\S*(\s+\S+)*)\s*$/,"$1"));}catch(e){return null;}
return((r[1].length===0)?r[0]:null);};$D.getParseFunction=function(fx){var fn=$D.Grammar.formats(fx);return function(s){var r=null;try{r=fn.call({},s);}catch(e){return null;}
return((r[1].length===0)?r[0]:null);};};$D.parseExact=function(s,fx){return $D.getParseFunction(fx)(s);};}());

spring-boot-demo-codegen/src/main/resources/static/libs/iview/fonts/ionicons.svg → demo-codegen/src/main/resources/static/libs/iview/fonts/ionicons.svg View File


spring-boot-demo-codegen/src/main/resources/static/libs/iview/fonts/ionicons.ttf → demo-codegen/src/main/resources/static/libs/iview/fonts/ionicons.ttf View File


spring-boot-demo-codegen/src/main/resources/static/libs/iview/fonts/ionicons.woff → demo-codegen/src/main/resources/static/libs/iview/fonts/ionicons.woff View File


spring-boot-demo-codegen/src/main/resources/static/libs/iview/iview.css → demo-codegen/src/main/resources/static/libs/iview/iview.css View File


spring-boot-demo-codegen/src/main/resources/static/libs/iview/iview.min.js → demo-codegen/src/main/resources/static/libs/iview/iview.min.js View File


spring-boot-demo-codegen/src/main/resources/static/libs/vue/vue.min.js → demo-codegen/src/main/resources/static/libs/vue/vue.min.js View File


+ 102
- 0
demo-codegen/src/main/resources/template/Controller.java.vm View File

@@ -0,0 +1,102 @@
package ${package}.${moduleName}.controller;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import ${package}.${moduleName}.common.R;
import ${package}.${moduleName}.entity.${className};
import ${package}.${moduleName}.service.${className}Service;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import lombok.extern.slf4j.Slf4j;
/**
* <p>
* ${comments}
* </p>
*
* @author ${author}
* @date Created in ${datetime}
*/
@Slf4j
@RestController
@RequestMapping("/${pathName}")
@Api(description = "${className}Controller", tags = {"${comments}"})
public class ${className}Controller {
@Autowired
private ${className}Service ${classname}Service;

/**
* 分页查询${comments}
* @param page 分页对象
* @param ${classname} ${comments}
* @return R
*/
@GetMapping("")
@ApiOperation(value = "分页查询${comments}", notes = "分页查询${comments}")
@ApiImplicitParams({
@ApiImplicitParam(name = "page", value = "分页参数", required = true),
@ApiImplicitParam(name = "${classname}", value = "查询条件", required = true)
})
public R list${className}(Page page, ${className} ${classname}) {
return R.success(${classname}Service.page(page,Wrappers.query(${classname})));
}


/**
* 通过id查询${comments}
* @param ${pk.lowerAttrName} id
* @return R
*/
@GetMapping("/{${pk.lowerAttrName}}")
@ApiOperation(value = "通过id查询${comments}", notes = "通过id查询${comments}")
@ApiImplicitParams({
@ApiImplicitParam(name = "${pk.lowerAttrName}", value = "主键id", required = true)
})
public R get${className}(@PathVariable("${pk.lowerAttrName}") ${pk.attrType} ${pk.lowerAttrName}){
return R.success(${classname}Service.getById(${pk.lowerAttrName}));
}

/**
* 新增${comments}
* @param ${classname} ${comments}
* @return R
*/
@PostMapping
@ApiOperation(value = "新增${comments}", notes = "新增${comments}")
public R save${className}(@RequestBody ${className} ${classname}){
return R.success(${classname}Service.save(${classname}));
}

/**
* 修改${comments}
* @param ${pk.lowerAttrName} id
* @param ${classname} ${comments}
* @return R
*/
@PutMapping("/{${pk.lowerAttrName}}")
@ApiOperation(value = "修改${comments}", notes = "修改${comments}")
@ApiImplicitParams({
@ApiImplicitParam(name = "${pk.lowerAttrName}", value = "主键id", required = true)
})
public R update${className}(@PathVariable ${pk.attrType} ${pk.lowerAttrName}, @RequestBody ${className} ${classname}){
return R.success(${classname}Service.updateById(${classname}));
}

/**
* 通过id删除${comments}
* @param ${pk.lowerAttrName} id
* @return R
*/
@DeleteMapping("/{${pk.lowerAttrName}}")
@ApiOperation(value = "删除${comments}", notes = "删除${comments}")
@ApiImplicitParams({
@ApiImplicitParam(name = "${pk.lowerAttrName}", value = "主键id", required = true)
})
public R delete${className}(@PathVariable ${pk.attrType} ${pk.lowerAttrName}){
return R.success(${classname}Service.removeById(${pk.lowerAttrName}));
}

}

+ 42
- 0
demo-codegen/src/main/resources/template/Entity.java.vm View File

@@ -0,0 +1,42 @@
package ${package}.${moduleName}.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
#if(${hasBigDecimal})
import java.math.BigDecimal;
#end
import java.time.LocalDateTime;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.NoArgsConstructor;
/**
* <p>
* ${comments}
* </p>
*
* @author ${author}
* @date Created in ${datetime}
*/
@Data
@NoArgsConstructor
@TableName("${tableName}")
@ApiModel(description = "${comments}")
@EqualsAndHashCode(callSuper = true)
public class ${className} extends Model<${className}> {
private static final long serialVersionUID = 1L;

#foreach ($column in $columns)
/**
* $column.comments
*/
#if($column.columnName == $pk.columnName)
@TableId
#end
@ApiModelProperty(value = "$column.comments")
private $column.attrType $column.lowerAttrName;
#end

}

+ 18
- 0
demo-codegen/src/main/resources/template/Mapper.java.vm View File

@@ -0,0 +1,18 @@
package ${package}.${moduleName}.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Component;
import ${package}.${moduleName}.entity.${className};

/**
* <p>
* ${comments}
* </p>
*
* @author ${author}
* @date Created in ${datetime}
*/
@Component
public interface ${className}Mapper extends BaseMapper<${className}> {

}

spring-boot-demo-codegen/src/main/resources/template/Mapper.xml.vm → demo-codegen/src/main/resources/template/Mapper.xml.vm View File


+ 16
- 0
demo-codegen/src/main/resources/template/Service.java.vm View File

@@ -0,0 +1,16 @@
package ${package}.${moduleName}.service;

import com.baomidou.mybatisplus.extension.service.IService;
import ${package}.${moduleName}.entity.${className};

/**
* <p>
* ${comments}
* </p>
*
* @author ${author}
* @date Created in ${datetime}
*/
public interface ${className}Service extends IService<${className}> {

}

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

Loading…
Cancel
Save