优化阅读格式

This commit is contained in:
heibaiying
2019-07-31 17:39:13 +08:00
parent 3020ff8efa
commit 246e5cfca1
55 changed files with 10521 additions and 10482 deletions

View File

@ -2,7 +2,7 @@
![spring](https://img.shields.io/badge/spring-5.1.3.RELEASE-brightgreen.svg) ![springboot](https://img.shields.io/badge/springboot-2.1.1.RELEASE-brightgreen.svg) ![springcloud](https://img.shields.io/badge/springcloud-Finchley.SR2-brightgreen.svg) ![jdk](https://img.shields.io/badge/jdk->=1.8-blue.svg) ![author](https://img.shields.io/badge/author-heibaiying-orange.svg) ![spring](https://img.shields.io/badge/spring-5.1.3.RELEASE-brightgreen.svg) ![springboot](https://img.shields.io/badge/springboot-2.1.1.RELEASE-brightgreen.svg) ![springcloud](https://img.shields.io/badge/springcloud-Finchley.SR2-brightgreen.svg) ![jdk](https://img.shields.io/badge/jdk->=1.8-blue.svg) ![author](https://img.shields.io/badge/author-heibaiying-orange.svg)
本项目仓库提供spring、spring-boot、spring-cloud 的常用整合用例。**每个用例都提供详细的图文说明**并给出官方文档的具体链接作为参考。随着spring的迭代本仓库会持续更新升级版本和丰富用例。 本项目仓库提供 spring、spring-boot、spring-cloud 的常用整合用例。**每个用例都提供详细的图文说明**,并给出官方文档的具体链接作为参考。随着 spring 的迭代,本仓库会持续更新,升级版本和丰富用例。
**版本说明** **版本说明**
@ -17,21 +17,21 @@ spring-cloudFinchley.SR2
## 1. spring samples ## 1. spring samples
所有spring的项目我都会提供两个版本的sample 所有 spring 的项目我都会提供两个版本的 sample
- 一个版本是基于xml配置也就是最为常见的配置方式 - 一个版本是基于 xml 配置,也就是最为常见的配置方式;
- 另一个版本完全基于代码配置(项目以**annotation**结尾这也是目前spring官方推荐的更为灵活配置方法也方便更好的衔接spring boot 的配置。 - 另一个版本完全基于代码配置(项目以**annotation**结尾),这也是目前 spring 官方推荐的更为灵活配置方法,也方便更好的衔接 spring boot 的配置。
| samples | 描述 | 官方文档 | | samples | 描述 | 官方文档 |
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| [springmvc-base](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/springmvc-base)<br/>[springmvc-base-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/springmvc-base-annotation) | springmvc基础、参数绑定、格式转换、数据校验、<br/>异常处理、 文件上传下载、视图渲染 | [Spring Mvc ](https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/web.html#mvc) | | [springmvc-base](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/springmvc-base)<br/>[springmvc-base-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/springmvc-base-annotation) | springmvc 基础、参数绑定、格式转换、数据校验、<br/>异常处理、 文件上传下载、视图渲染 | [Spring Mvc ](https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/web.html#mvc) |
| [spring-aop](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-aop)<br/>[spring-aop-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-aop-annotation) | spring切面编程 | [Spring AOP](https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#aop) | | [spring-aop](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-aop)<br/>[spring-aop-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-aop-annotation) | spring 切面编程 | [Spring AOP](https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#aop) |
| [spring-jdbc](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-jdbc)<br/>[spring-jdbc-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-jdbc-annotation) | spring jdbc-template 的使用 | [Using JdbcTemplate](https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#jdbc-JdbcTemplate) | | [spring-jdbc](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-jdbc)<br/>[spring-jdbc-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-jdbc-annotation) | spring jdbc-template 的使用 | [Using JdbcTemplate](https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#jdbc-JdbcTemplate) |
| [spring-mybatis](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-mybatis)<br/>[spring-mybatis-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-mybatis-annotation) | spring 整合 mybatis | [Mybatis-Spring](http://www.mybatis.org/spring/zh/index.html) | | [spring-mybatis](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-mybatis)<br/>[spring-mybatis-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-mybatis-annotation) | spring 整合 mybatis | [Mybatis-Spring](http://www.mybatis.org/spring/zh/index.html) |
| [spring-druid-mybatis](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-druid-mybatis)<br/>[spring-druid-mybatis-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-druid-mybatis-annotation) | spring 整合druid、mybatis | [Alibaba druid](https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98) | | [spring-druid-mybatis](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-druid-mybatis)<br/>[spring-druid-mybatis-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-druid-mybatis-annotation) | spring 整合 druid、mybatis | [Alibaba druid](https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98) |
| [spring-redis](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-redis)<br/>[spring-redis-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-redis-annotation) | spring 整合 redis 单机+集群jedis客户端<br/>spring 整合 redis 单机+集群redisson客户端 | [Redisson](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | | [spring-redis](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-redis)<br/>[spring-redis-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-redis-annotation) | spring 整合 redis 单机 + 集群jedis 客户端)<br/>spring 整合 redis 单机 + 集群redisson 客户端) | [Redisson](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) |
| [spring-mongodb](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-mongodb)<br/>[spring-mongodb-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-mongodb-annotation) | spring 整合 mongodb | [Spring Data MongoDB](https://docs.spring.io/spring-data/mongodb/docs/2.1.3.RELEASE/reference/html/#mongo.mongo-db-factory-java) | | [spring-mongodb](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-mongodb)<br/>[spring-mongodb-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-mongodb-annotation) | spring 整合 mongodb | [Spring Data MongoDB](https://docs.spring.io/spring-data/mongodb/docs/2.1.3.RELEASE/reference/html/#mongo.mongo-db-factory-java) |
| [spring-memcached](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-memcached)<br/>[spring-memcached-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-memcached-annotation) | spring 整合 memcached(单机+集群) | [Xmemcached](https://github.com/killme2008/xmemcached/wiki/Xmemcached%20%E4%B8%AD%E6%96%87%E7%94%A8%E6%88%B7%E6%8C%87%E5%8D%97) | | [spring-memcached](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-memcached)<br/>[spring-memcached-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-memcached-annotation) | spring 整合 memcached(单机 + 集群) | [Xmemcached](https://github.com/killme2008/xmemcached/wiki/Xmemcached%20%E4%B8%AD%E6%96%87%E7%94%A8%E6%88%B7%E6%8C%87%E5%8D%97) |
| [spring-rabbitmq](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-rabbitmq)<br/>[spring-rabbitmq-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-rabbitmq-annotation) | spring 整合 rabbitmq、消息序列化与反序列化 | [Rabbitmq](http://www.rabbitmq.com/getstarted.html)<br>[Spring AMQP](https://docs.spring.io/spring-amqp/docs/2.1.3.BUILD-SNAPSHOT/reference/html/) | | [spring-rabbitmq](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-rabbitmq)<br/>[spring-rabbitmq-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-rabbitmq-annotation) | spring 整合 rabbitmq、消息序列化与反序列化 | [Rabbitmq](http://www.rabbitmq.com/getstarted.html)<br>[Spring AMQP](https://docs.spring.io/spring-amqp/docs/2.1.3.BUILD-SNAPSHOT/reference/html/) |
| [spring-dubbo](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-dubbo)<br/>[spring-dubbo-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-dubbo-annotation) | spring 整合 dubbo | [Dubbo ](http://dubbo.apache.org/zh-cn/docs/user/quick-start.html) | | [spring-dubbo](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-dubbo)<br/>[spring-dubbo-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-dubbo-annotation) | spring 整合 dubbo | [Dubbo ](http://dubbo.apache.org/zh-cn/docs/user/quick-start.html) |
| [spring-websocket](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-websocket)<br/>[spring-websocket-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-websocket-annotation) | spring 整合 websocket | [Spring Websocket](https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/web.html#websocket) | | [spring-websocket](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-websocket)<br/>[spring-websocket-annotation](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring/spring-websocket-annotation) | spring 整合 websocket | [Spring Websocket](https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/web.html#websocket) |
@ -47,11 +47,11 @@ spring-cloudFinchley.SR2
| [spring-boot-base](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-base) | spring-boot 基础 | [spring boot 官方文档](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/)<br>[spring boot 中文官方文档](https://www.breakyizhan.com/springboot/3028.html) | | [spring-boot-base](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-base) | spring-boot 基础 | [spring boot 官方文档](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/)<br>[spring boot 中文官方文档](https://www.breakyizhan.com/springboot/3028.html) |
| [spring-boot-yml-profile](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-yml-profile) | yml 语法和多配置切换 | [Using YAML Instead of Properties](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-external-config-yaml) | | [spring-boot-yml-profile](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-yml-profile) | yml 语法和多配置切换 | [Using YAML Instead of Properties](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-external-config-yaml) |
| [spring-boot-tomcat](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-tomcat) | spring-boot 整合外部容器tomcat | [Use Another Web Server](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#howto-use-another-web-server) | | [spring-boot-tomcat](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-tomcat) | spring-boot 整合外部容器tomcat | [Use Another Web Server](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#howto-use-another-web-server) |
| [spring-boot-servlet](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-servlet) | spring boot 整合servlet 3.0 | [Embedded Servlet Container Support](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-embedded-container) | | [spring-boot-servlet](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-servlet) | spring boot 整合 servlet 3.0 | [Embedded Servlet Container Support](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-embedded-container) |
| [spring-boot-jsp](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-jsp) | spring-boot 整合 jsp内置容器 | [JSP Limitations](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-jsp-limitations) | | [spring-boot-jsp](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-jsp) | spring-boot 整合 jsp内置容器 | [JSP Limitations](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-jsp-limitations) |
| [spring-boot-data-jpa](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-data-jpa) | spring-boot data jpa 的使用 | [Spring Data JPA](https://docs.spring.io/spring-data/jpa/docs/2.1.3.RELEASE/reference/html/) | | [spring-boot-data-jpa](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-data-jpa) | spring-boot data jpa 的使用 | [Spring Data JPA](https://docs.spring.io/spring-data/jpa/docs/2.1.3.RELEASE/reference/html/) |
| [spring-boot-mybatis](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-mybatis) | spring-boot+HikariDataSources 整合 mybatis | [Mybatis-Spring](http://www.mybatis.org/spring/zh/index.html)<br/>[Mybatis-Spring-Boot-Autoconfigure](http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/) | | [spring-boot-mybatis](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-mybatis) | spring-boot+HikariDataSources 整合 mybatis | [Mybatis-Spring](http://www.mybatis.org/spring/zh/index.html)<br/>[Mybatis-Spring-Boot-Autoconfigure](http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/) |
| [spring-boot-druid-mybtais](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-druid-mybatis) | spring-boot 整合druid、mybatis | [Alibaba druid](https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)<br/>[druid-spring-boot-starter](https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter) | | [spring-boot-druid-mybtais](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-druid-mybatis) | spring-boot 整合 druid、mybatis | [Alibaba druid](https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)<br/>[druid-spring-boot-starter](https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter) |
| [spring-boot-redis](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-redis) | spring-boot 整合 redis | [Working with NoSQL Technologies](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-nosql) | | [spring-boot-redis](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-redis) | spring-boot 整合 redis | [Working with NoSQL Technologies](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-nosql) |
| [spring-boot-mongodb](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-mongodb) | spring-boot 整合 mongodb | [Working with NoSQL Technologies](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-nosql) | | [spring-boot-mongodb](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-mongodb) | spring-boot 整合 mongodb | [Working with NoSQL Technologies](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-nosql) |
| [spring-boot-memcached](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-memcached) | spring-boot 整合 memcached | [Xmemcached](https://github.com/killme2008/xmemcached/wiki/Xmemcached%20%E4%B8%AD%E6%96%87%E7%94%A8%E6%88%B7%E6%8C%87%E5%8D%97) | | [spring-boot-memcached](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-memcached) | spring-boot 整合 memcached | [Xmemcached](https://github.com/killme2008/xmemcached/wiki/Xmemcached%20%E4%B8%AD%E6%96%87%E7%94%A8%E6%88%B7%E6%8C%87%E5%8D%97) |
@ -75,7 +75,7 @@ spring-cloudFinchley.SR2
| [spring-cloud-Hystrix](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-hystrix) | Hystix 服务容错保护<br/>hystrix dashboard 断路器监控<br>Turbine 断路器聚合监控 | [Circuit Breaker: Hystrix Clients](https://cloud.spring.io/spring-cloud-static/Finchley.SR2/multi/multi__circuit_breaker_hystrix_clients.html)<br/>[Hystrix metrics aggregation with Turbine](https://cloud.spring.io/spring-cloud-static/Finchley.SR2/multi/multi_spring-cloud-consul-turbine.html) | | [spring-cloud-Hystrix](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-hystrix) | Hystix 服务容错保护<br/>hystrix dashboard 断路器监控<br>Turbine 断路器聚合监控 | [Circuit Breaker: Hystrix Clients](https://cloud.spring.io/spring-cloud-static/Finchley.SR2/multi/multi__circuit_breaker_hystrix_clients.html)<br/>[Hystrix metrics aggregation with Turbine](https://cloud.spring.io/spring-cloud-static/Finchley.SR2/multi/multi_spring-cloud-consul-turbine.html) |
| [spring-cloud-Zuul](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-zuul) | Zuul 网关服务 | [Router and Filter: Zuul](https://cloud.spring.io/spring-cloud-static/Finchley.SR2/multi/multi__router_and_filter_zuul.html) | | [spring-cloud-Zuul](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-zuul) | Zuul 网关服务 | [Router and Filter: Zuul](https://cloud.spring.io/spring-cloud-static/Finchley.SR2/multi/multi__router_and_filter_zuul.html) |
| [spring-cloud-Sleuth-Zipkin](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-sleuth-zipkin) | Sleuth + Zipkin 服务链路追踪 | [Spring Cloud Sleuth](https://cloud.spring.io/spring-cloud-static/Finchley.SR2/multi/multi__introduction.html#sleuth-adding-project) | | [spring-cloud-Sleuth-Zipkin](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-sleuth-zipkin) | Sleuth + Zipkin 服务链路追踪 | [Spring Cloud Sleuth](https://cloud.spring.io/spring-cloud-static/Finchley.SR2/multi/multi__introduction.html#sleuth-adding-project) |
| [spring-cloud-Config-Bus](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-config) | Config 分布式配置中心 <br>集成Bus消息总线 实现配置热更新 | [Spring Cloud Config Client](https://cloud.spring.io/spring-cloud-static/Finchley.SR2/multi/multi__spring_cloud_config_client.html) | | [spring-cloud-Config-Bus](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-config) | Config 分布式配置中心 <br>集成 Bus 消息总线 实现配置热更新 | [Spring Cloud Config Client](https://cloud.spring.io/spring-cloud-static/Finchley.SR2/multi/multi__spring_cloud_config_client.html) |
<br/> <br/>
@ -85,13 +85,13 @@ spring-cloudFinchley.SR2
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| [spring-session](https://github.com/heibaiying/spring-samples-for-all/tree/master/distributed-solution/spring-session) | spring 实现分布式 session | [spring session](https://spring.io/projects/spring-session#learn) | | [spring-session](https://github.com/heibaiying/spring-samples-for-all/tree/master/distributed-solution/spring-session) | spring 实现分布式 session | [spring session](https://spring.io/projects/spring-session#learn) |
| [spring boot + spring session](https://github.com/heibaiying/spring-samples-for-all/tree/master/distributed-solution/spring-boot-session) | spring boot + spring session 实现分布式 session | [spring session](https://spring.io/projects/spring-session#learn) | | [spring boot + spring session](https://github.com/heibaiying/spring-samples-for-all/tree/master/distributed-solution/spring-boot-session) | spring boot + spring session 实现分布式 session | [spring session](https://spring.io/projects/spring-session#learn) |
| [springboot-druid-mybatis-atomikos](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/springboot-druid-mybatis-multi) | spring boot + druid + mybatis + atomikos<BR> 配置多数据源、支持分布式事务( JTA 方式实现) | [Distributed Transactions with JTA](https://docs.spring.io/spring-boot/docs/2.1.2.RELEASE/reference/htmlsingle/#boot-features-jta) | | [springboot-druid-mybatis-atomikos](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/springboot-druid-mybatis-multi) | spring boot + druid + mybatis + atomikos<BR> 配置多数据源、支持分布式事务 ( JTA 方式实现) | [Distributed Transactions with JTA](https://docs.spring.io/spring-boot/docs/2.1.2.RELEASE/reference/htmlsingle/#boot-features-jta) |
<br/> <br/>
## 5.参考资料 ## 5.参考资料
相关参考文档放在了仓库的referenced documents 目录下,文件目录如下: 相关参考文档放在了仓库的 referenced documents 目录下,文件目录如下:
- Servlet3.1规范(最终版).pdf - Servlet3.1 规范(最终版).pdf
- Thymeleaf中⽂参考⼿册.pdf - Thymeleaf 中⽂参考⼿册.pdf

View File

@ -1,5 +1,6 @@
# spring boot 实现分布式 session # spring boot 实现分布式 session
## 目录<br/>
<a href="#一项目结构">一、项目结构</a><br/> <a href="#一项目结构">一、项目结构</a><br/>
<a href="#二分布式session的配置">二、分布式session的配置</a><br/> <a href="#二分布式session的配置">二、分布式session的配置</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-引入依赖">2.1 引入依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-引入依赖">2.1 引入依赖</a><br/>
@ -51,7 +52,7 @@ spring:
min-idle: 1 min-idle: 1
timeout: 300ms timeout: 300ms
session: session:
session: # session 存储方式 支持 redis、mongo、jdbc、hazelcast
store-type: redis store-type: redis
# 如果是集群节点 采用如下配置指定节点 # 如果是集群节点 采用如下配置指定节点
@ -61,14 +62,14 @@ spring:
有两点需要特别说明: 有两点需要特别说明:
1. spring-session 不仅提供了 redis 作为公共 session 存储的方案,同时也支持 jdbc、mongodb、Hazelcast 等作为公共 session 的存储,可以用 session.store-type 指定;
1. spring-session 不仅提供了redis作为公共session存储方案同时也支持jdbc、mongodb、Hazelcast等作为公共session的存储可以用session.store-type 指定; 2. 对于 redis 存储方案而言,官方也提供了不止一种整合方式,这里我们选取的整合方案是 jedis 客户端作为连接,当然也可以使用 Lettuce 作为客户端连接。
#### 2.3 启动类上添加@EnableRedisHttpSession 注解开启 spring-session-redis 整合方案的自动配置 #### 2.3 启动类上添加@EnableRedisHttpSession 注解开启 spring-session-redis 整合方案的自动配置
```java ```java
@SpringBootApplication @SpringBootApplication
@SpringBootApplication @EnableRedisHttpSession(maxInactiveIntervalInSeconds= 1800) //开启 redis session 支持,并配置 session 过期时间
public class SpringBootSessionApplication { public class SpringBootSessionApplication {
public static void main(String[] args) { public static void main(String[] args) {
@ -100,9 +101,9 @@ public class LoginController {
@PostMapping("login") @PostMapping("login")
public String login(User user, HttpSession session) { public String login(User user, HttpSession session) {
public String login(User user, HttpSession session) { // 随机生成用户 id
user.setUserId(Math.round(Math.floor(Math.random() * 10 * 1000))); user.setUserId(Math.round(Math.floor(Math.random() * 10 * 1000)));
user.setUserId(Math.round(Math.floor(Math.random() * 10 * 1000))); // 将用户信息保存到 id 中
session.setAttribute("USER", user); session.setAttribute("USER", user);
return "home"; return "home";
} }
@ -110,7 +111,7 @@ public class LoginController {
} }
``` ```
登录页面 index.ftl
```jsp ```jsp
<!doctype html> <!doctype html>
@ -128,7 +129,7 @@ public class LoginController {
</html> </html>
``` ```
session 信息展示页面 home.ftl
```jsp ```jsp
<!doctype html> <!doctype html>
@ -145,13 +146,13 @@ session 信息展示页面home.ftl
#### 3.2 启动项目 #### 3.2 启动项目
由于我们这里采用的是 spring boot 的内置容器作为 web 容器,所以直接启动两个实例测试即可。
应用 1 启动配置:
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-session-app1.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-session-app1.png"/> </div>
应用 2 启动配置,需要用 `--server.port ` 指定不同的端口号:
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-session-app2.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-session-app2.png"/> </div>

View File

@ -1,5 +1,6 @@
# spring session 实现分布式 session # spring session 实现分布式 session
## 目录<br/>
<a href="#一项目结构">一、项目结构</a><br/> <a href="#一项目结构">一、项目结构</a><br/>
<a href="#二分布式session的配置">二、分布式session的配置</a><br/> <a href="#二分布式session的配置">二、分布式session的配置</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-引入依赖">2.1 引入依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-引入依赖">2.1 引入依赖</a><br/>
@ -12,7 +13,7 @@
## 一、项目结构 ## 一、项目结构
分布式 session 主要配置文件为 spring-session.xml 和 web.xml其他的配置为标准的 web 工程的配置。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-session.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-session.png"/> </div>
@ -58,8 +59,8 @@
有两点需要特别说明: 有两点需要特别说明:
1. spring-session 不仅提供了 redis 作为公共 session 存储的方案,同时也支持 jdbc、mongodb、Hazelcast 等作为公共 session 的存储;
1. spring-session 不仅提供了redis作为公共session存储的方案同时也支持jdbc、mongodb、Hazelcast等作为公共session的存储 2. 对于 redis 存储方案而言,官方也提供了不止一种整合方式,这里我们选取的整合方案是 jedis 客户端作为连接,当然也可以使用 Lettuce 作为客户端连接。
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -79,7 +80,7 @@
<property name="maxInactiveIntervalInSeconds" value="1800"/> <property name="maxInactiveIntervalInSeconds" value="1800"/>
</bean> </bean>
<!--单机版本配置 redis 配置-->
<bean id="redisStandaloneConfiguration" <bean id="redisStandaloneConfiguration"
class="org.springframework.data.redis.connection.RedisStandaloneConfiguration"> class="org.springframework.data.redis.connection.RedisStandaloneConfiguration">
<constructor-arg name="hostName" value="${redis.host}"/> <constructor-arg name="hostName" value="${redis.host}"/>
@ -135,9 +136,9 @@ public class LoginController {
@PostMapping("login") @PostMapping("login")
public String login(User user, HttpSession session, HttpServletRequest request, Model model){ public String login(User user, HttpSession session, HttpServletRequest request, Model model){
public String login(User user, HttpSession session, HttpServletRequest request, Model model){ // 随机生成用户 id
user.setUserId(Math.round(Math.floor(Math.random() *10*1000))); user.setUserId(Math.round(Math.floor(Math.random() *10*1000)));
user.setUserId(Math.round(Math.floor(Math.random() *10*1000))); // 将用户信息保存到 id 中
session.setAttribute("USER",user); session.setAttribute("USER",user);
return "redirect:home"; return "redirect:home";
} }
@ -164,7 +165,7 @@ public class LoginController {
</html> </html>
``` ```
session 信息展示页面 (home.jsp)
```jsp ```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %>
@ -182,7 +183,7 @@ session 信息展示页面(home.jsp)
#### 3.2 启动项目 #### 3.2 启动项目
这里我们采用两个 tomcat 分别启动项目,在第一个项目 index.jsp 页面进行登录,第二个项目不登录,直接访问 session 展示页home.jsp
tomcat 1 配置: tomcat 1 配置:

View File

@ -1,5 +1,6 @@
# spring boot actuator # spring boot actuator
## 目录<br/>
<a href="#一用例涉及到的概念综述">一、用例涉及到的概念综述</a><br/> <a href="#一用例涉及到的概念综述">一、用例涉及到的概念综述</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-端点">1.1 端点</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-端点">1.1 端点</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-启用端点">1.2 启用端点</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-启用端点">1.2 启用端点</a><br/>
@ -24,43 +25,43 @@
### 1.1 端点 ### 1.1 端点
执行器端点endpoints可用于监控应用及与应用进行交互Spring Boot 包含很多内置的端点你也可以添加自己的。例如health 端点提供了应用的基本健康信息。 端点暴露的方式取决于你采用的技术类型,大部分应用选择 HTTP 监控,端点的 ID 映射到一个 URL。例如health 端点默认映射到/health。
下面的端点都是可用的: 下面的端点都是可用的:
| ID | 描述 | 是否敏感 | | ID | 描述 | 是否敏感 |
| ----------- | ------------------------------------------------------------ | -------- | | ----------- | ------------------------------------------------------------ | -------- |
| ----------- | ------------------------------------------------------------ | -------- | | actuator | 为其他端点提供基于超文本的导航页面,需要添加 Spring HATEOAS 依赖 | true |
| autoconfig | 显示一个自动配置类的报告,该报告展示所有自动配置候选者及它们被应用或未被应用的原因 | true | | autoconfig | 显示一个自动配置类的报告,该报告展示所有自动配置候选者及它们被应用或未被应用的原因 | true |
| autoconfig | 显示一个自动配置类的报告,该报告展示所有自动配置候选者及它们被应用或未被应用的原因 | true | | beans | 显示一个应用中所有 Spring Beans 的完整列表 | true |
| beans | 显示一个应用中所有Spring Beans的完整列表 | true | | configprops | 显示一个所有@ConfigurationProperties 的集合列表 | true |
| dump | 执行一个线程转储 | true | | dump | 执行一个线程转储 | true |
| dump | 执行一个线程转储 | true | | env | 暴露来自 Spring ConfigurableEnvironment 的属性 | true |
| flyway | 显示数据库迁移路径,如果有的话 | true | | flyway | 显示数据库迁移路径,如果有的话 | true |
| health | 展示应用的健康信息(当使用一个未认证连接访问时显示一个简单的'status',使用认证连接访问则显示全部信息详情) | false | | health | 展示应用的健康信息(当使用一个未认证连接访问时显示一个简单的'status',使用认证连接访问则显示全部信息详情) | false |
| info | 显示任意的应用信息 | false | | info | 显示任意的应用信息 | false |
| info | 示任意的应用信息 | false | | liquibase | 示任何 Liquibase 数据库迁移路径,如果有的话 | true |
| metrics | 展示当前应用的'metrics'信息 | true | | metrics | 展示当前应用的'metrics'信息 | true |
| metrics | 展示当前应用的'metrics'信息 | true | | mappings | 显示一个所有@RequestMapping 路径的集合列表 | true |
| shutdown | 允许应用以优雅的方式关闭(默认情况下不启用) | true | | shutdown | 允许应用以优雅的方式关闭(默认情况下不启用) | true |
| shutdown | 允许应用以优雅的方式关闭(默认情况下不启用) | true | | trace | 显示 trace 信息(默认为最新的 100 条 HTTP 请求) | true |
如果使用 Spring MVC你还可以使用以下端点
| ID | 描述 | 是否敏感 | | ID | 描述 | 是否敏感 |
| -------- | ------------------------------------------------------------ | -------- | | -------- | ------------------------------------------------------------ | -------- |
| -------- | ------------------------------------------------------------ | -------- | | docs | 展示 Actuator 的文档,包括示例请求和响应,需添加 spring-boot-actuator-docs 依赖 | false |
| docs | 展示Actuator的文档包括示例请求和响应需添加spring-boot-actuator-docs依赖 | false | | heapdump | 返回一个 GZip 压缩的 hprof 堆转储文件 | true |
| heapdump | 返回一个GZip压缩的hprof堆转储文件 | true | | jolokia | 通过 HTTP 暴露 JMX beans依赖 Jolokia | true |
| jolokia | 通过HTTP暴露JMX beans依赖Jolokia | true | | logfile | 返回日志文件内容(如果设置 logging.file 或 logging.path 属性),支持使用 HTTP Range 头接收日志文件内容的部分信息 | |
根据端点暴露的方式sensitive 属性可用做安全提示,例如,在使用 HTTP 访问敏感sensitive端点时需要提供用户名/密码(如果没有启用 web 安全,可能会简化为禁止访问该端点)。
### 1.2 启用端点 ### 1.2 启用端点
默认情况下,除了以外的所有端点 shutdown 都已启用。要配置端点的启用,请使用其 management.endpoint.<id>.enabled 属性。以下示例启用 shutdown 端点:
```properties ```properties
management.endpoint.shutdown.enabled = true management.endpoint.shutdown.enabled = true
@ -109,7 +110,7 @@ management.endpoint.shutdown.enabled = true
### 1.4 健康检查信息 ### 1.4 健康检查信息
您可以使用健康信息来检查正在运行的应用程序的状态。health 端点公开的信息取决于 management.endpoint.health.show-details 可以使用以下值之一配置的属性:
| 名称 | 描述 | | 名称 | 描述 |
| --------------- | ------------------------------------------------------------ | | --------------- | ------------------------------------------------------------ |
@ -154,32 +155,32 @@ management:
health: health:
status: status:
http-mapping: http-mapping:
http-mapping: # 自定义健康检查返回状态码对应的 http 状态码
FATAL: 503 FATAL: 503
``` ```
#### 1.4 查看监控状态 #### 1.4 查看监控状态
导入 actuator 的 start 并进行配置后,访问 http://127.0.0.1:8080/actuator/health 就可以看到对应的项目监控状态。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/health.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/health.png"/> </div>
需要注意的是这里的监控状态根据实际项目所用到的技术不同而不同。因为以下 HealthIndicators 情况在适当时由 Spring Boot 自动配置的:
| 名称 | 描述 | | 名称 | 描述 |
| ------------------------------------------------------------ | -------------------------------- | | ------------------------------------------------------------ | -------------------------------- |
| ------------------------------------------------------------ | -------------------------------- | | [CassandraHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraHealthIndicator.java) | 检查 Cassandra 数据库是否启动。 |
| [DiskSpaceHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/DiskSpaceHealthIndicator.java) | 检查磁盘空间不足。 | | [DiskSpaceHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/DiskSpaceHealthIndicator.java) | 检查磁盘空间不足。 |
| [DiskSpaceHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/DiskSpaceHealthIndicator.java) | 检查磁盘空间不足。 | | [DataSourceHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jdbc/DataSourceHealthIndicator.java) | 检查是否可以获得连接 DataSource。 |
| [DataSourceHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jdbc/DataSourceHealthIndicator.java) | 检查是否可以获得连接DataSource。 | | [ElasticsearchHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchHealthIndicator.java) | 检查 Elasticsearch 集群是否启动。 |
| [ElasticsearchHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchHealthIndicator.java) | 检查Elasticsearch集群是否启动。 | | [InfluxDbHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/influx/InfluxDbHealthIndicator.java) | 检查 InfluxDB 服务器是否启动。 |
| [InfluxDbHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/influx/InfluxDbHealthIndicator.java) | 检查InfluxDB服务器是否启动。 | | [JmsHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jms/JmsHealthIndicator.java) | 检查 JMS 代理是否启动。 |
| [MailHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/mail/MailHealthIndicator.java) | 检查邮件服务器是否启动。 | | [MailHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/mail/MailHealthIndicator.java) | 检查邮件服务器是否启动。 |
| [MailHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/mail/MailHealthIndicator.java) | 检查邮件服务器是否启动。 | | [MongoHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/mongo/MongoHealthIndicator.java) | 检查 Mongo 数据库是否启动。 |
| [MongoHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/mongo/MongoHealthIndicator.java) | 检查Mongo数据库是否启动。 | | [Neo4jHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicator.java) | 检查 Neo4j 服务器是否启动。 |
| [Neo4jHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicator.java) | 检查Neo4j服务器是否启动。 | | [RabbitHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/amqp/RabbitHealthIndicator.java) | 检查 Rabbit 服务器是否启动。 |
| [RabbitHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/amqp/RabbitHealthIndicator.java) | 检查Rabbit服务器是否启动。 | | [RedisHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisHealthIndicator.java) | 检查 Redis 服务器是否启动。 |
| [RedisHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisHealthIndicator.java) | 检查Redis服务器是否启动。 | | [SolrHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/solr/SolrHealthIndicator.java) | 检查 Solr 服务器是否启动。 |
@ -217,7 +218,7 @@ public class CustomHealthIndicator implements HealthIndicator {
这里我们可以看到自定义检查不论是否通过都不会影响整体的 status,两种情况下都是 status 都是“up”。如果我们想通过自定义的检查检查去影响最终的检查结果比如我们健康检查针对的是支付业务在支付业务的不可用的情况下我们就认为整个服务是不可用的。这个时候就需要实现自定义实现健康状态的聚合。
@ -234,7 +235,7 @@ public class CustomHealthAggregator implements HealthAggregator {
@Override @Override
public Health aggregate(Map<String, Health> healths) { public Health aggregate(Map<String, Health> healths) {
for (Health health : healths.values()) { for (Health health : healths.values()) {
for (Health health : healths.values()) { // 聚合规则可以自定义,这里假设我们自定义的监控状态中有一项 FATAL,就认为整个服务都是不可用的,否则认为整个服务是可用的
if (health.getStatus().getCode().equals("FATAL")) { if (health.getStatus().getCode().equals("FATAL")) {
return Health.status("FATAL").withDetail("error code", "综合判断后服务宕机").build(); return Health.status("FATAL").withDetail("error code", "综合判断后服务宕机").build();
} }
@ -248,7 +249,7 @@ public class CustomHealthAggregator implements HealthAggregator {
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/actuator-heath-503.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/actuator-heath-503.png"/> </div>
这里需要注意的是返回我们自定义的聚合状态的时候,状态码也变成了 503,这是我们在配置文件中进行定义的:
```properties ```properties
management.health.status.http-mapping.FATAL = 503 management.health.status.http-mapping.FATAL = 503
@ -269,15 +270,15 @@ management.health.status.http-mapping.FATAL = 503
#### 5.1 自定义端点 #### 5.1 自定义端点
spring boot 支持使用@Endpoint 来自定义端点暴露应用信息。这里我们采用第三方 sigar 来暴露服务所在硬件的监控信息。
Sigar 是 Hyperic-hq 产品的基础包,是 Hyperic HQ 主要的数据收集组件。Sigar.jar 的底层是用 C 语言编写的,它通过本地方法来调用操作系统 API 来获取系统相关数据 [jar 包下载地址](https://sourceforge.net/projects/sigar/)。
Sigar 为不同平台提供了不同的库文件,下载后需要将库文件放到服务所在主机的对应位置:
- Windows 下配置:根据自己的操作系统版本选择 sigar-amd64-winnt.dll 或 sigar-x86-winnt.dll 拷贝到 C:\Windows\System32 中
- Linux 下配置:将 libsigar-amd64-linux.so 或 libsigar-x86-linux.so 拷贝到/usr/lib64 或/lib64 或/lib 或/usr/lib 目录下,如果不起作用,还需要 sudochmod 744 修改 libsigar-amd64-linux.so 文件权限
```java ```java
@Endpoint(id = "customEndPoint") @Endpoint(id = "customEndPoint")
@ -296,9 +297,9 @@ public class CustomEndPoint {
for (int i = 0; i < infoList.length; i++) { for (int i = 0; i < infoList.length; i++) {
CpuInfo info = infoList[i]; CpuInfo info = infoList[i];
CpuInfo info = infoList[i]; cupInfoMap.put("CPU " + i + " 的总量 MHz", info.getMhz()); // CPU 的总量 MHz
cupInfoMap.put("CPU " + i + " 的总量MHz", info.getMhz()); // CPU的总量MHz cupInfoMap.put("CPU " + i + " 生产商", info.getVendor()); // 获得 CPU 的生产商Intel
cupInfoMap.put("CPU " + i + " 生产商", info.getVendor()); // 获得CPU的生产商Intel cupInfoMap.put("CPU " + i + " 类别", info.getModel()); // 获得 CPU 的类别Core
cupInfoMap.put("CPU " + i + " 缓存数量", info.getCacheSize()); // 缓冲存储器数量 cupInfoMap.put("CPU " + i + " 缓存数量", info.getCacheSize()); // 缓冲存储器数量
cupInfoMap.put("CPU " + i + " 用户使用率", CpuPerc.format(cpuList[i].getUser())); // 用户使用率 cupInfoMap.put("CPU " + i + " 用户使用率", CpuPerc.format(cpuList[i].getUser())); // 用户使用率
cupInfoMap.put("CPU " + i + " 系统使用率", CpuPerc.format(cpuList[i].getSys())); // 系统使用率 cupInfoMap.put("CPU " + i + " 系统使用率", CpuPerc.format(cpuList[i].getSys())); // 系统使用率
@ -313,9 +314,9 @@ public class CustomEndPoint {
} }
``` ```
其中可用的方法注解由 http 操作决定:
| operation | HTTP 方法 |
| ---------------- | -------- | | ---------------- | -------- |
| @ReadOperation | GET | | @ReadOperation | GET |
| @WriteOperation | POST | | @WriteOperation | POST |
@ -327,7 +328,7 @@ public class CustomEndPoint {
关于 Sigar 的 更多监控参数可以参考博客:[java 读取计算机 CPU、内存等信息Sigar 使用)](https://blog.csdn.net/wudiazu/article/details/73829324)
Sigar 下载包中也提供了各种参数的参考用例: Sigar 下载包中也提供了各种参数的参考用例:

View File

@ -1,5 +1,6 @@
# spring-boot 基础 # spring-boot 基础
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目依赖">1.2 项目依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目依赖">1.2 项目依赖</a><br/>
@ -18,15 +19,15 @@
#### 1.1 项目结构说明 #### 1.1 项目结构说明
1. 本项目搭建一个简单的 hello spring 的 web 工程,简单说明 spring-boot 的开箱即用的特性;
1. 本项目搭建一个简单的hello spring 的 web工程简单说明spring-boot 的开箱即用的特性 2. 模板引擎采用 freemaker 和 thymeleaf 作为示例,分别对应模板文件 makershow.ftl 和 leafShow.html
2. 模板引擎采用freemaker 和 thymeleaf 作为示例分别对应模板文件makershow.ftl 和 leafShow.html 3. spring boot 2.x 默认是不支持 jsp 的,需要额外的配置,关于使用 jsp 的整合可以参考[spring-boot-jsp](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-jsp) 项目。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-base.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-base.png"/> </div>
#### 1.2 项目依赖 #### 1.2 项目依赖
导入相关的 starter(启动器)
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -78,7 +79,7 @@
</dependency> </dependency>
</dependencies> </dependencies>
<!--Spring Boot 的 Maven 插件Spring Boot Maven plugin能够以 Maven 的方式为应用提供 Spring Boot 的支持-->
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
@ -92,17 +93,17 @@
``` ```
1. spring boot 项目默认继承自 spring-boot-starter-parent而 spring-boot-starter-parent 继承自 spring-boot-dependencies, spring-boot-dependencies 中定义了关于 spring boot 依赖的各种 jar 包的版本,是 spring boot 的版本管理中心。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-dependencies.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-dependencies.png"/> </div>
2. 关于 spring boot 2.x 官方支持的所有 starter 可以参见官方文档 [Table 13.1. Spring Boot application starters](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#using-boot-starter)
## 二、spring boot 主启动类 ## 二、spring boot 主启动类
如果采用 IDEA 或者 Spring Tool Suite (STS) 等开发工具创建的 spring boot 工程,会默认创建启动类,如果没有创建,需要手动创建启动类
```java ```java
package com.heibaiying.springbootbase; package com.heibaiying.springbootbase;
@ -121,7 +122,7 @@ public class SpringBootBaseApplication {
} }
``` ```
@SpringBootApplication 注解是一个复合注解,里面包含了@ComponentScan 注解,默认开启包扫描,扫描与主程序所在包及其子包,对于本工程而言 默认扫描 com.heibaiying.springbootbase
```java ```java
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@ -142,7 +143,7 @@ public @interface SpringBootApplication {
## 三、开箱即用的web工程 ## 三、开箱即用的web工程
在 springbootBaseApplication.java 的同级目录创建 controller 文件夹,并在其中创建 RestfulController.java,启动项目访问 localhost:8080/restful/programmers 即可看到项目搭建成功。
```java ```java
/** /**
@ -163,20 +164,20 @@ public class RestfulController {
} }
``` ```
这里之所以能够开箱即用,是因为我们在项目中导入 spring-boot-starter-web 启动器,而@SpringBootApplication 复合注解中默认开启了@EnableAutoConfiguration 注解允许开启自动化配置spring 在检查导入 starter-web 的依赖后就会开启 web 的自动化配置。
## 四、模板引擎 ## 四、模板引擎
这里我们在一个项目中同时导入了 freemaker 和 thymeleaf 的 starter虽然并不推荐但是在同一个项目中是可以混用这两种模板引擎的
#### 4.1 freemarker #### 4.1 freemarker
```java ```java
/** /**
* @author : heibaiying * @author : heibaiying
* @author : heibaiying * @description : 跳转渲染模板引擎 默认模板的存放位置为 classpath:templates
*/ */
@Controller @Controller
@RequestMapping("freemarker") @RequestMapping("freemarker")
@ -199,7 +200,7 @@ public class FreeMarkerController {
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta charset="UTF-8"> <title>freemarker 模板引擎</title>
</head> </head>
<body> <body>
<ul> <ul>
@ -216,7 +217,7 @@ public class FreeMarkerController {
```java ```java
/** /**
* @author : heibaiying * @author : heibaiying
* @author : heibaiying * @description : 跳转渲染模板引擎 默认模板的存放位置为 classpath:templates
*/ */
@Controller @Controller
@RequestMapping("thymeleaf") @RequestMapping("thymeleaf")
@ -239,7 +240,7 @@ public class ThymeleafController {
<html lang="en" xmlns:th="http://www.thymeleaf.org"> <html lang="en" xmlns:th="http://www.thymeleaf.org">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta charset="UTF-8"> <title>thymeleaf 模板引擎</title>
</head> </head>
<body> <body>
<ul th:each="programmer:${programmers}"> <ul th:each="programmer:${programmers}">
@ -258,4 +259,4 @@ freemarker提供了完善的中文文档地址 http://freemarker.foofun.cn
thymeleaf官方英文文档地址[thymeleaf 3.0.11RELEASE](https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.pdf) thymeleaf官方英文文档地址[thymeleaf 3.0.11RELEASE](https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.pdf)
注:我在本仓库中也上传了一份[thymeleaf 中文文档gangzi828(刘明刚 译)](https://github.com/heibaiying/spring-samples-for-all/tree/master/referenced%20documents),翻译的版本为 3.0.5RELEASE

View File

@ -1,5 +1,6 @@
# spring boot data jpa # spring boot data jpa
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/>
@ -27,7 +28,7 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> </dependency>
</dependency> <!--引入 mysql 驱动-->
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
@ -56,7 +57,7 @@ spring:
jpa: jpa:
hibernate: hibernate:
ddl-auto: update ddl-auto: update
ddl-auto: update #Hibernate 默认创建的表是 myisam 引擎,可以用以下方式指定为使用 innodb 创建表
database-platform: org.hibernate.dialect.MySQL57Dialect database-platform: org.hibernate.dialect.MySQL57Dialect
show-sql: true show-sql: true
``` ```
@ -66,7 +67,7 @@ spring:
```java ```java
/** /**
* @author : heibaiying * @author : heibaiying
* @author : heibaiying * @description : 查询接口继承自 CrudRepository,CrudRepository 默认定义了部分增删改查方法
*/ */
public interface ProgRepository extends CrudRepository<Programmer, Integer> { public interface ProgRepository extends CrudRepository<Programmer, Integer> {
@ -172,7 +173,7 @@ public class DataJPATests {
List<Programmer> byCondition = repository.findByConditionAndOrder("pro03", 3321.34f, Sort.Order.asc("salary")); List<Programmer> byCondition = repository.findByConditionAndOrder("pro03", 3321.34f, Sort.Order.asc("salary"));
System.out.println("byCondition:" + byCondition); System.out.println("byCondition:" + byCondition);
//条件与分页查询 需要注意的是这里的页数是从第 0 页开始计算的
Page<Programmer> page = repository.findAll(PageRequest.of(0, 10, Sort.Direction.DESC, "salary")); Page<Programmer> page = repository.findAll(PageRequest.of(0, 10, Sort.Direction.DESC, "salary"));
page.get().forEach(System.out::println); page.get().forEach(System.out::println);
} }

View File

@ -1,5 +1,6 @@
# spring boot 整合 druid+mybatis # spring boot 整合 druid+mybatis
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/>
@ -17,20 +18,20 @@
#### 1.1 项目结构 #### 1.1 项目结构
1. 项目查询用的表对应的建表语句放置在 resources 的 sql 文件夹下;
2. 为了使用 druid 控制台的功能,项目以 web 的方式构建。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-druid-mybatis.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-druid-mybatis.png"/> </div>
#### 1.2 项目主要依赖 #### 1.2 项目主要依赖
需要说明的是按照 spring 官方对于自定义的 starter 命名规范的推荐:
- 官方的 starter 命名spring-boot-starter-XXXX
- 官方的starter命名spring-boot-starter-XXXX - 其他第三方 starter 命名:XXXX-spring-boot-starte
所以 mybatis 的 starter 命名为 mybatis-spring-boot-starter如果有自定义 starter 需求,也需要按照此命名规则进行命名。
```xml ```xml
<dependency> <dependency>
@ -67,7 +68,7 @@ spring boot 与 mybatis 版本的对应关系:
#### 2.1 在application.yml 中配置数据源 #### 2.1 在application.yml 中配置数据源
本用例采用 druid 作为数据库连接池,虽然 druid 性能略逊于 Hikari但是提供了更为全面的监控管理可以按照实际需求选用 druid 或者 Hikari。关于 Hikari 数据源的配置可以参考[spring-boot-mybatis 项目](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-mybatis)
```yaml ```yaml
spring: spring:
@ -80,50 +81,50 @@ spring:
# 使用 druid 作为连接池 更多配置的说明可以参见 druid starter 中文文档 https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter # 使用 druid 作为连接池 更多配置的说明可以参见 druid starter 中文文档 https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
type: com.alibaba.druid.pool.DruidDataSource type: com.alibaba.druid.pool.DruidDataSource
druid: druid:
druid: # 初始化时建立物理连接的个数。初始化发生在显示调用 init 方法,或者第一次 getConnection 时
initialSize: 5 initialSize: 5
# 最小连接池数量 # 最小连接池数量
minIdle: 5 minIdle: 5
# 最大连接池数量 # 最大连接池数量
maxActive: 10 maxActive: 10
maxActive: 10 # 获取连接时最大等待时间,单位毫秒。配置了 maxWait 之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 useUnfairLock 属性为 true 使用非公平锁。
maxWait: 60000 maxWait: 60000
maxWait: 60000 # Destroy 线程会检测连接的间隔时间,如果连接空闲时间大于等于 minEvictableIdleTimeMillis 则关闭物理连接。
timeBetweenEvictionRunsMillis: 60000 timeBetweenEvictionRunsMillis: 60000
# 连接保持空闲而不被驱逐的最小时间 # 连接保持空闲而不被驱逐的最小时间
minEvictableIdleTimeMillis: 300000 minEvictableIdleTimeMillis: 300000
minEvictableIdleTimeMillis: 300000 # 用来检测连接是否有效的 sql 因数据库方言而差, 例如 oracle 应该写成 SELECT 1 FROM DUAL
validationQuery: SELECT 1 validationQuery: SELECT 1
validationQuery: SELECT 1 # 建议配置为 true不影响性能并且保证安全性。申请连接的时候检测如果空闲时间大于 timeBetweenEvictionRunsMillis执行 validationQuery 检测连接是否有效。
testWhileIdle: true testWhileIdle: true
testWhileIdle: true # 申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
testOnBorrow: false testOnBorrow: false
testOnBorrow: false # 归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
testOnReturn: false testOnReturn: false
# 是否自动回收超时连接 # 是否自动回收超时连接
removeAbandoned: true removeAbandoned: true
removeAbandoned: true # 超时时间 (以秒数为单位)
remove-abandoned-timeout: 180 remove-abandoned-timeout: 180
# druid 监控的配置 如果不使用 druid 的监控功能的话 以下配置就不是必须的 # druid 监控的配置 如果不使用 druid 的监控功能的话 以下配置就不是必须的
# 本项目监控台访问地址: http://localhost:8080/druid/login.html # 本项目监控台访问地址: http://localhost:8080/druid/login.html
# WebStatFilter 用于采集 web-jdbc 关联监控的数据。
# 更多配置可参见: https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_%E9%85%8D%E7%BD%AEWebStatFilter # 更多配置可参见: https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_%E9%85%8D%E7%BD%AEWebStatFilter
web-stat-filter: web-stat-filter:
web-stat-filter: # 是否开启 WebStatFilter 默认是 true
enabled: true enabled: true
enabled: true # 需要拦截的 url
url-pattern: /* url-pattern: /*
# 排除静态资源的请求 # 排除静态资源的请求
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
# Druid 内置提供了一个 StatViewServlet 用于展示 Druid 的统计信息。
# 更多配置可参见:https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE # 更多配置可参见:https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE
stat-view-servlet: stat-view-servlet:
stat-view-servlet: #是否启用 StatViewServlet 默认值 true
enabled: true enabled: true
enabled: true # 需要拦截的 url
url-pattern: /druid/* url-pattern: /druid/*
# 允许清空统计数据 # 允许清空统计数据
reset-enable: true reset-enable: true
@ -136,9 +137,9 @@ spring:
mybatis: mybatis:
configuration: configuration:
# 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 # 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。
# 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 # oracle 数据库建议配置为 JdbcType.NULL, 默认是 Other
jdbc-type-for-null: 'null' jdbc-type-for-null: 'null'
jdbc-type-for-null: 'null' # 是否打印 sql 语句 调试的时候可以开启
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
``` ```
@ -185,7 +186,7 @@ public class ProgrammerController {
```java ```java
/** /**
* @author : heibaiying * @author : heibaiying
* @author : heibaiying * @description :在 Spring Boot 中可以通过 HTTP 接口将 Druid 监控数据以 JSON 的形式暴露出去,
* 实际使用中你可以根据你的需要自由地对监控数据、暴露方式进行扩展。 * 实际使用中你可以根据你的需要自由地对监控数据、暴露方式进行扩展。
*/ */

View File

@ -1,5 +1,6 @@
# spring boot 整合 dubbo # spring boot 整合 dubbo
## 目录<br/>
<a href="#一-项目结构说明">一、 项目结构说明</a><br/> <a href="#一-项目结构说明">一、 项目结构说明</a><br/>
<a href="#二关键依赖">二、关键依赖</a><br/> <a href="#二关键依赖">二、关键依赖</a><br/>
<a href="#三公共模块boot-dubbo-common">三、公共模块boot-dubbo-common</a><br/> <a href="#三公共模块boot-dubbo-common">三、公共模块boot-dubbo-common</a><br/>
@ -18,13 +19,13 @@
## 一、 项目结构说明 ## 一、 项目结构说明
1.1 按照 dubbo 文档推荐的服务最佳实践,建议将服务接口、服务模型、服务异常等均放在 API 包中,所以项目采用 maven 多模块的构建方式,在 spring-boot-dubbo 下构建三个子模块:
1. boot-dubbo-common 是公共模块,用于存放公共的接口和 bean,被 boot-dubbo-provider 和 boot-dubbo-consumer 在 pom.xml 中引用;
2. boot-dubbo-provider 是服务的提供者,提供商品的查询服务; 2. boot-dubbo-provider 是服务的提供者,提供商品的查询服务;
2. boot-dubbo-provider 是服务的提供者,提供商品的查询服务 3. boot-dubbo-consumer 是服务的消费者,调用 provider 提供的查询服务
1.2 本项目 dubbo 的搭建采用 zookeeper 作为注册中心, 关于 zookeeper 的安装和基本操作可以参见我的手记 [Zookeeper 基础命令与 Java 客户端](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/ZooKeeper/ZooKeeper%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA%E4%B8%8EJava%E5%AE%A2%E6%88%B7%E7%AB%AF.md)
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-dubbo.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-dubbo.png"/> </div>
@ -32,7 +33,7 @@
## 二、关键依赖 ## 二、关键依赖
在父工程的项目中统一导入依赖 dubbo 的 starter父工程的 pom.xml 如下
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -88,7 +89,7 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependency> <!--引入 dubbo start 依赖-->
<dependency> <dependency>
<groupId>com.alibaba.boot</groupId> <groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId> <artifactId>dubbo-spring-boot-starter</artifactId>
@ -118,7 +119,7 @@
dubbo: dubbo:
application: application:
name: boot-duboo-provider name: boot-duboo-provider
name: boot-duboo-provider # 指定注册协议和注册地址 dubbo 推荐使用 zookeeper 作为注册中心,并且在 start 依赖中引入了 zookeeper 的 java 客户端 Curator
registry: registry:
protocol: zookeeper protocol: zookeeper
address: 127.0.0.1:2181 address: 127.0.0.1:2181
@ -127,7 +128,7 @@ dubbo:
#### 4.2 使用注解@Service暴露服务 #### 4.2 使用注解@Service暴露服务
需要注意的是这里的@Service 注解不是 spring 的注解,而是 dubbo 的注解 com.alibaba.dubbo.config.annotation.Service
```java ```java
package com.heibaiying.dubboprovider.service; package com.heibaiying.dubboprovider.service;
@ -182,7 +183,7 @@ public class ProductService implements IProductService {
dubbo: dubbo:
application: application:
name: boot-duboo-provider name: boot-duboo-provider
name: boot-duboo-provider # 指定注册协议和注册地址 dubbo 推荐使用 zookeeper 作为注册中心,并且在 start 依赖中引入了 zookeeper 的 java 客户端 Curator
registry: registry:
protocol: zookeeper protocol: zookeeper
address: 127.0.0.1:2181 address: 127.0.0.1:2181
@ -213,7 +214,7 @@ import java.util.List;
@RequestMapping("sell") @RequestMapping("sell")
public class SellController { public class SellController {
// dubbo 远程引用注解
@Reference @Reference
private IProductService productService; private IProductService productService;
@ -235,13 +236,13 @@ public class SellController {
## 六、项目构建的说明 ## 六、项目构建的说明
因为在项目中consumer 和 provider 模块均依赖公共模块,所以在构建 consumer 和 provider 项目前需要将 common 模块安装到本地仓库,**依次**对**父工程**和**common 模块**执行:
```shell ```shell
mvn install -Dmaven.test.skip = true mvn install -Dmaven.test.skip = true
``` ```
consumer 中 pom.xml 如下
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -286,7 +287,7 @@ consumer中 pom.xml如下
</project> </project>
``` ```
provider 中 pom.xml 如下
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -347,7 +348,7 @@ mvn clean package
```sh ```sh
配置文件为: 配置文件为:
dubbo-admin-backend/src/main/resources/application.properties dubbo-admin-backend/src/main/resources/application.properties
dubbo-admin-backend/src/main/resources/application.properties 主要的配置有 默认的配置就是 127.0.0.1:2181
dubbo.registry.address=zookeeper://127.0.0.1:2181 dubbo.registry.address=zookeeper://127.0.0.1:2181
``` ```

View File

@ -1,5 +1,6 @@
# spring boot 内置容器 整合 jsp # spring boot 内置容器 整合 jsp
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/>

View File

@ -1,5 +1,6 @@
# spring boot 整合 kafka # spring boot 整合 kafka
## 目录<br/>
<a href="#一kafka的相关概念">一、kafka的相关概念</a><br/> <a href="#一kafka的相关概念">一、kafka的相关概念</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#1主题和分区">1.主题和分区</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#1主题和分区">1.主题和分区</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#2分区复制">2.分区复制</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#2分区复制">2.分区复制</a><br/>
@ -29,7 +30,7 @@
### 1.主题和分区 ### 1.主题和分区
kafka 的消息通过主题进行分类。一个主题可以被分为若干个分区一个分区就是一个提交日志。消息以追加的方式写入分区然后以先入先出的顺序读取。kafka 通过分区来实现数据的冗余和伸缩性,分区可以分布在不同的服务器上,也就是说一个主题可以横跨多个服务器,以此来提供比单个服务器更强大的性能(类比 HDFS 分布式文件系统)。
注意:由于一个主题包含多个分区,因此无法在整个主题范围内保证消息的顺序性,**但可以保证消息在单个分区内的顺序性**。 注意:由于一个主题包含多个分区,因此无法在整个主题范围内保证消息的顺序性,**但可以保证消息在单个分区内的顺序性**。
@ -46,7 +47,7 @@ kafka 的消息通过主题进行分类。一个主题可以被分为若干个
- 默认情况下生产者在把消息均衡地分布到在主题的所有分区上,而并不关心特定消息会被写到那个分区; - 默认情况下生产者在把消息均衡地分布到在主题的所有分区上,而并不关心特定消息会被写到那个分区;
- 如果指定消息键,则通过对消息键的散列来实现分区; - 如果指定消息键,则通过对消息键的散列来实现分区;
- 如果指定消息键,则通过消息键的散列来实现分区; - 也可以通过消息键和分区器来实现把消息直接写到指定的分区,这个需要自定义分区器,需要实现 Partitioner 接口,并重写其中的 partition 方法。
### 4. 消费者 ### 4. 消费者
@ -58,13 +59,13 @@ kafka 的消息通过主题进行分类。一个主题可以被分为若干个
### 5.broker和集群 ### 5.broker和集群
一个独立的 kafka 服务器被称为 broker。broker 接收来自生产者的消息为消息设置偏移量并提交消息到磁盘保存。broker 为消费者提供服务,对读取分区的请求做出响应,返回已经提交到磁盘的消息。
broker 是集群的组成部分。每一个集群都有一个 broker 同时充当了集群控制器的角色(自动从集群的活跃成员中选举出来)。控制器负责管理工作,包括将分区分配给 broker 和监控 broker。**在集群中,一个分区从属一个 broker,该 broker 被称为分区的首领**。一个分区可以分配给多个 broker,这个时候会发生分区复制。这种复制机制为分区提供了消息冗余,如果有一个 broker 失效,其他 broker 可以接管领导权。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/kafka集群复制.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/kafka集群复制.png"/> </div>
更多 kafka 的说明可以参考我的个人笔记:[《Kafka 权威指南》读书笔记](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/Kafka/%E3%80%8AKafka%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.md#53-%E5%A4%8D%E5%88%B6)
@ -72,7 +73,7 @@ broker是集群的组成部分。每一个集群都有一个broker同时充当
#### 1.1 项目结构说明 #### 1.1 项目结构说明
本项目提供 kafka 发送简单消息、对象消息、和多消费者组消费消息三种情况下的 sample。
1. kafkaSimpleConsumer 用于普通消息的监听; 1. kafkaSimpleConsumer 用于普通消息的监听;
2. kafkaBeanConsumer 用于对象消息监听; 2. kafkaBeanConsumer 用于对象消息监听;
@ -105,7 +106,7 @@ broker是集群的组成部分。每一个集群都有一个broker同时充当
```yaml ```yaml
spring: spring:
kafka: kafka:
kafka: # 以逗号分隔的地址列表,用于建立与 Kafka 集群的初始连接 (kafka 默认的端口号为 9092)
bootstrap-servers: 127.0.0.1:9092 bootstrap-servers: 127.0.0.1:9092
producer: producer:
# 发生错误后,消息重发的次数。 # 发生错误后,消息重发的次数。
@ -123,13 +124,13 @@ spring:
# acks=all :只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。 # acks=all :只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。
acks: 1 acks: 1
consumer: consumer:
consumer: # 自动提交的时间间隔 在 spring boot 2.X 版本中这里采用的是值的类型为 Duration 需要符合特定的格式,如 1S,1M,2H,5D
auto-commit-interval: 1S auto-commit-interval: 1S
# 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理: # 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理:
# latest默认值在偏移量无效的情况下消费者将从最新的记录开始读取数据在消费者启动之后生成的记录 # latest默认值在偏移量无效的情况下消费者将从最新的记录开始读取数据在消费者启动之后生成的记录
# earliest :在偏移量无效的情况下,消费者将从起始位置读取分区的记录 # earliest :在偏移量无效的情况下,消费者将从起始位置读取分区的记录
auto-offset-reset: earliest auto-offset-reset: earliest
auto-offset-reset: earliest # 是否自动提交偏移量,默认值是 true,为了避免出现重复数据和数据丢失,可以把它设置为 false,然后手动提交偏移量
enable-auto-commit: true enable-auto-commit: true
# 键的反序列化方式 # 键的反序列化方式
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
@ -143,7 +144,7 @@ spring:
这里需要说明的是: 这里需要说明的是:
在 spring boot 2.X 版本 auto-commit-interval自动提交的时间间隔采用的是值的类型为 Duration Duration 是 jdk 1.8 版本之后引入的类,在其源码中我们可以看到对于其字符串的表达需要符合一定的规范,即数字 + 单位,如下的写法 1s 1.5s 0s 0.001S 1h 2d 在 yaml 中都是有效的。如果传入无效的字符串,则 spring boot 在启动阶段解析配置文件的时候就会抛出异常。
```java ```java
public final class Duration public final class Duration
@ -174,9 +175,9 @@ public class KafKaCustomrProducer {
public void sendMessage(String topic, Object object) { public void sendMessage(String topic, Object object) {
/* /*
/* * 这里的 ListenableFuture 类是 spring 对 java 原生 Future 的扩展增强,是一个泛型接口,用于监听异步方法的回调
* 这里的ListenableFuture类是spring对java原生Future的扩展增强,是一个泛型接口,用于监听异步方法的回调 * 而对于 kafka send 方法返回值而言,这里的泛型所代表的实际类型就是 SendResult<K, V>,而这里 K,V 的泛型实际上
* 而对于kafka send 方法返回值而言,这里的泛型所代表的实际类型就是 SendResult<K, V>,而这里K,V的泛型实际上 * 被用于 ProducerRecord<K, V> producerRecord,即生产者发送消息的 key,value 类型
*/ */
ListenableFuture<SendResult<String, Object>> future = kafkaTemplate.send(topic, object); ListenableFuture<SendResult<String, Object>> future = kafkaTemplate.send(topic, object);
@ -246,7 +247,7 @@ public class SendMsgController {
```java ```java
/** /**
* @author : heibaiying * @author : heibaiying
* @author : heibaiying * @description : kafka 配置类
*/ */
@Configuration @Configuration
public class KafkaConfig { public class KafkaConfig {
@ -262,10 +263,10 @@ public class KafkaConfig {
#### 3.2 多消费者组对同一主题的监听 #### 3.2 多消费者组对同一主题的监听
1. 消费者 1-1 监听主题的 0、1 分区
1. 消费者1-1 监听主题的 0、1 分区 2. 消费者 1-2 监听主题的 2、3 分区
2. 消费者1-2 监听主题的 2、3 分区 3. 消费者 1-3 监听主题的 0、1 分区
3. 消费者1-3 监听主题的 0、1 分区 4. 消费者 2-1 监听主题的所有分区
```java ```java
/** /**
@ -278,7 +279,7 @@ public class KafkaConfig {
@Slf4j @Slf4j
public class KafkaGroupConsumer { public class KafkaGroupConsumer {
// 分组 1 中的消费者 1
@KafkaListener(id = "consumer1-1", groupId = "group1", topicPartitions = @KafkaListener(id = "consumer1-1", groupId = "group1", topicPartitions =
{@TopicPartition(topic = Topic.GROUP, partitions = {"0", "1"}) {@TopicPartition(topic = Topic.GROUP, partitions = {"0", "1"})
}) })
@ -286,7 +287,7 @@ public class KafkaGroupConsumer {
System.out.println("consumer1-1 收到消息:" + record.value()); System.out.println("consumer1-1 收到消息:" + record.value());
} }
// 分组 1 中的消费者 2
@KafkaListener(id = "consumer1-2", groupId = "group1", topicPartitions = @KafkaListener(id = "consumer1-2", groupId = "group1", topicPartitions =
{@TopicPartition(topic = Topic.GROUP, partitions = {"2", "3"}) {@TopicPartition(topic = Topic.GROUP, partitions = {"2", "3"})
}) })
@ -294,7 +295,7 @@ public class KafkaGroupConsumer {
System.out.println("consumer1-2 收到消息:" + record.value()); System.out.println("consumer1-2 收到消息:" + record.value());
} }
// 分组 1 中的消费者 3
@KafkaListener(id = "consumer1-3", groupId = "group1", topicPartitions = @KafkaListener(id = "consumer1-3", groupId = "group1", topicPartitions =
{@TopicPartition(topic = Topic.GROUP, partitions = {"0", "1"}) {@TopicPartition(topic = Topic.GROUP, partitions = {"0", "1"})
}) })
@ -302,7 +303,7 @@ public class KafkaGroupConsumer {
System.out.println("consumer1-3 收到消息:" + record.value()); System.out.println("consumer1-3 收到消息:" + record.value());
} }
// 分组 2 中的消费者
@KafkaListener(id = "consumer2-1", groupId = "group2", topics = Topic.GROUP) @KafkaListener(id = "consumer2-1", groupId = "group2", topics = Topic.GROUP)
public void consumer2_1(ConsumerRecord<String, Object> record) { public void consumer2_1(ConsumerRecord<String, Object> record) {
System.err.println("consumer2-1 收到消息:" + record.value()); System.err.println("consumer2-1 收到消息:" + record.value());
@ -363,18 +364,18 @@ consumer1-2 收到消息:hello group 2
#### 3.4 测试结果 #### 3.4 测试结果
1. 和 kafka 原本的机制一样,多消费者组之间对于同一个主题的消费彼此之间互不影响;
1. 和kafka 原本机制一样,多消费者组之间对于同一个主题的消费彼此之间互不影响; 2. kafka 原本机制一样的是,这里我们消费者 1-1 和消费 1-3 共同属于同一个消费者组,并且监听同样的分区,按照原本 kafka 的机制,群组保证每个分区只能被同一个消费者组的一个消费者使用,但是按照 spring 的声明方式实现的消息监听,这里被两个消费者都监听到了。
## 四、序列化与反序列化 ## 四、序列化与反序列化
用例采用的是第三方 fastjson 将实体类序列化为 json 后发送。实现如下:
```java ```java
/*** /***
/*** * 发送消息体为 bean 的消息
*/ */
@GetMapping("sendBean") @GetMapping("sendBean")
public void sendBean() { public void sendBean() {

View File

@ -1,5 +1,6 @@
# spring boot 整合 mecached # spring boot 整合 mecached
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--XMemcached客户端说明">1.1 XMemcached客户端说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--XMemcached客户端说明">1.1 XMemcached客户端说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目结构说明">1.2 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目结构说明">1.2 项目结构说明</a><br/>
@ -19,11 +20,11 @@
### 1.1 XMemcached客户端说明 ### 1.1 XMemcached客户端说明
spring boot 官方并没有提供关于 memcached 的 starter所以我们这里还是采用 XMemcached 作为客户端进行整合。 XMemcached 是基于 java nio 的 memcached 高性能客户端,支持完整的 memcached 协议,支持客户端分布并且提供了一致性哈希 (consistent hash) 算法的实现。
### 1.2 项目结构说明 ### 1.2 项目结构说明
memcached 的整合配置位于 config 文件夹下。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-memcached.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-memcached.png"/> </div>
@ -74,7 +75,7 @@ public MemcachedClient memcachedClientForCluster() {
builder.setConnectionPoolSize(10); builder.setConnectionPoolSize(10);
// 协议工厂 // 协议工厂
builder.setCommandFactory(new TextCommandFactory()); builder.setCommandFactory(new TextCommandFactory());
builder.setCommandFactory(new TextCommandFactory()); // 分布策略,一致性哈希 KetamaMemcachedSessionLocator 或者 ArraySessionLocator(默认)
builder.setSessionLocator(new KetamaMemcachedSessionLocator()); builder.setSessionLocator(new KetamaMemcachedSessionLocator());
// 设置序列化器 // 设置序列化器
builder.setTranscoder(new SerializingTranscoder()); builder.setTranscoder(new SerializingTranscoder());
@ -90,7 +91,7 @@ public MemcachedClient memcachedClientForCluster() {
#### 2.3 存储基本类型测试用例 #### 2.3 存储基本类型测试用例
xmemcached 单机版本和集群版本注入的实例是相同的。
```java ```java
/** /**
@ -150,14 +151,14 @@ public class MemObjectSamples {
| 命令 | 格式 | 说明 | | 命令 | 格式 | 说明 |
| --------------- | -------------------------------------------------- | ------------------------------------- | | --------------- | -------------------------------------------------- | ------------------------------------- |
| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 | | 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 |
| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 | | 新增 add | add key flags exTime length -> value | 只有当 key 不存在的情况下,才可以插入 |
| 新增 add | add key flags exTime length -> value | 只有当key不存在的情况下才可以插入 | | 替换 replace | replace key flags exTime length -> value | 只修改已存在 key 的 value 值 |
| 替换 replace | replace key flags exTime length -> value | 只修改已存在key的value值 | | 追加内容 append | append key flags exTime length -> value | length 表示追加的长度而不是总长度 |
| 追加内容append | append key flags exTime length -> value | length表示追加的长度而不是总长度 | | 前面追加 prepend | prepend key flags exTime length -> value | length 表示追加的长度而不是总长度 |
| 查询操作 get | get key | | | 查询操作 get | get key | |
| 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 | | 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 |
| 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 | | 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 |
| 删除 delete | delete key | 将数据打一个删除标记 | | 删除 delete | delete key | 将数据打一个删除标记 |
| 删除 delete | delete key | 将数据打一个删除标记 | | 自增 incr | incr key 增加偏移量 | incr 和 decr 只能操作能转换为数字的 Value |
| 自增 incr | incr key 增加偏移量 | incr和decr只能操作能转换为数字的Value | | 自减 decr | decr key 减少偏移量 | desr 不能将数字减少至 0 以下 |
| 清库 | flush_all | | | 清库 | flush_all | |

View File

@ -1,5 +1,6 @@
# spring boot 整合 mongodb # spring boot 整合 mongodb
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-用例结构">1.1 用例结构</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-用例结构">1.1 用例结构</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/>
@ -16,9 +17,9 @@
#### 1.1 用例结构 #### 1.1 用例结构
1. 本用例提供 mongdb 的简单整合用例;
1. 本用例提供mongdb的简单整合用例 2. 提供用 MongoTemplate 的方式操作 mongdb见测试用例 MongoOriginalTests.java
2. 提供用MongoTemplate的方式操作mongdb见测试用例MongoOriginalTests.java 3. 提供基于 spring data jpa 的方式操作 mongodb(推荐),见测试用例 MongoJPATests.java
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-mongodb.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-mongodb.png"/> </div>
@ -159,7 +160,7 @@ public class MongoJPATests {
} }
``` ```
查询方法支持的关键字如下,更多命名规范可以参见 Spring Data MongoDB 官方文档[Query Methods](https://docs.spring.io/spring-data/mongodb/docs/2.1.3.RELEASE/reference/html/#mongodb.repositories.queries)
| Keyword | Sample | Logical result | | Keyword | Sample | Logical result |
| ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |

View File

@ -1,5 +1,6 @@
# spring boot 整合 mybatis # spring boot 整合 mybatis
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/>
@ -16,28 +17,28 @@
#### 1.1 项目结构 #### 1.1 项目结构
1. 项目查询用的表对应的建表语句放置在 resources 的 sql 文件夹下;
2. 关于 mybatis sql 的写法提供两种方式:
xml 写法:对应的类为 ProgrammerMapper.java 和 programmerMapper.xml用 MybatisXmlTest 进行测试;
注解写法:对应的类为 Programmer.java ,用 MybatisAnnotationTest 进行测试。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-mybatis.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-mybatis.png"/> </div>
#### 1.2 项目主要依赖 #### 1.2 项目主要依赖
需要说明的是按照 spring 官方对应自定义的 starter 命名规范的推荐:
- 官方的 starter 命名spring-boot-starter-XXXX
- 官方的starter命名spring-boot-starter-XXXX - 其他第三方 starter 命名:XXXX-spring-boot-starte
所以 mybatis 的 starter 命名为 mybatis-spring-boot-starter如果有自定义 starter 需求,也需要按照此命名规则进行命名。
```xml ```xml
<!--spring 1.5 x 以上版本对应 mybatis 1.3.x (1.3.1) <!--spring 1.5 x 以上版本对应 mybatis 1.3.x (1.3.1)
<!--spring 1.5 x 以上版本对应 mybatis 1.3.x (1.3.1) 关于更多 spring-boot 与 mybatis 的版本对应可以参见 <a href="http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/">-->
<dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId> <groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId> <artifactId>mybatis-spring-boot-starter</artifactId>
@ -74,7 +75,7 @@ spring boot 与 mybatis 版本的对应关系:
#### 2.1 在application.yml 中配置数据源 #### 2.1 在application.yml 中配置数据源
spring boot 2.x 版本默认采用 Hikari 作为数据库连接池Hikari 是目前 java 平台性能最好的连接池,性能好于 druid。
```yaml ```yaml
spring: spring:
@ -92,17 +93,17 @@ spring:
minimum-idle: 10 minimum-idle: 10
# 池中最大连接数,包括闲置和使用中的连接 # 池中最大连接数,包括闲置和使用中的连接
maximum-pool-size: 20 maximum-pool-size: 20
maximum-pool-size: 20 # 此属性控制从池返回的连接的默认自动提交行为。默认为 true
auto-commit: true auto-commit: true
# 允许最长空闲时间 # 允许最长空闲时间
idle-timeout: 30000 idle-timeout: 30000
idle-timeout: 30000 # 此属性表示连接池的用户定义名称,主要显示在日志记录和 JMX 管理控制台中,以标识池和池配置。 默认值:自动生成
pool-name: custom-hikari pool-name: custom-hikari
pool-name: custom-hikari #此属性控制池中连接的最长生命周期,值 0 表示无限生命周期,默认 1800000 即 30 分钟
max-lifetime: 1800000 max-lifetime: 1800000
max-lifetime: 1800000 # 数据库连接超时时间,默认 30 秒,即 30000
connection-timeout: 30000 connection-timeout: 30000
connection-timeout: 30000 # 连接测试 sql 这个地方需要根据数据库方言差异而配置 例如 oracle 就应该写成 select 1 from dual
connection-test-query: SELECT 1 connection-test-query: SELECT 1
# mybatis 相关配置 # mybatis 相关配置
@ -111,9 +112,9 @@ mybatis:
mapper-locations: classpath*:mappers/*.xml mapper-locations: classpath*:mappers/*.xml
configuration: configuration:
# 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 # 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。
# 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 # oracle 数据库建议配置为 JdbcType.NULL, 默认是 Other
jdbc-type-for-null: 'null' jdbc-type-for-null: 'null'
jdbc-type-for-null: 'null' # 是否打印 sql 语句 调试的时候可以开启
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
``` ```
@ -164,7 +165,7 @@ public interface ProgrammerMapper {
```java ```java
/*** /***
/*** * @description: xml Sql 测试类
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
@ -223,7 +224,7 @@ public interface ProgrammerDao {
```java ```java
/*** /***
/*** * @description: 注解 Sql 测试类
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest

View File

@ -1,5 +1,6 @@
# spring boot 整合 rabbitmq # spring boot 整合 rabbitmq
## 目录<br/>
<a href="#一-项目结构说明">一、 项目结构说明</a><br/> <a href="#一-项目结构说明">一、 项目结构说明</a><br/>
<a href="#二关键依赖">二、关键依赖</a><br/> <a href="#二关键依赖">二、关键依赖</a><br/>
<a href="#三公共模块rabbitmq-common">三、公共模块rabbitmq-common</a><br/> <a href="#三公共模块rabbitmq-common">三、公共模块rabbitmq-common</a><br/>
@ -18,13 +19,13 @@
## 一、 项目结构说明 ## 一、 项目结构说明
1.1 之前关于 spring 整合 rabbitmq 我们采用的是单项目的方式,为了使得用例更具有实际意义,这里采用 maven 多模块的构建方式,在 spring-boot-rabbitmq 下构建三个子模块:
1. rabbitmq-common 是公共模块,用于存放公共的接口、配置和 bean,被 rabbitmq-producer 和 rabbitmq-consumer 在 pom.xml 中引用;
2. rabbitmq-producer 是消息的生产者模块; 2. rabbitmq-producer 是消息的生产者模块;
2. rabbitmq-producer 是消息的生产者模块 3. rabbitmq-consumer 是消息的消费者模块
1.2 关于 rabbitmq 安装、交换机、队列、死信队列等基本概念可以参考我的手记[《RabbitMQ 实战指南》读书笔记](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/RabbitMQ/%E3%80%8ARabbitMQ%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.md),里面有详细的配图说明。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-rabbitmq.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-rabbitmq.png"/> </div>
@ -32,7 +33,7 @@
## 二、关键依赖 ## 二、关键依赖
在父工程的项目中统一导入依赖 rabbitmq 的 starter(spring-boot-starter-amqp),父工程的 pom.xml 如下
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -133,7 +134,7 @@ public class RabbitInfo {
spring: spring:
rabbitmq: rabbitmq:
addresses: 127.0.0.1:5672 addresses: 127.0.0.1:5672
addresses: 127.0.0.1:5672 # RabbitMQ 默认的用户名和密码都是 guest 而虚拟主机名称是 "/"
# 如果配置其他虚拟主机地址,需要预先用管控台或者图形界面创建 图形界面地址 http://主机地址:15672 # 如果配置其他虚拟主机地址,需要预先用管控台或者图形界面创建 图形界面地址 http://主机地址:15672
username: guest username: guest
password: guest password: guest
@ -192,7 +193,7 @@ public class RabbitmqConsumer {
MessageHeaders headers = message.getHeaders(); MessageHeaders headers = message.getHeaders();
// 获取消息头信息和消息体 // 获取消息头信息和消息体
log.info("msgInfo:{} ; payload:{} ", headers.get("msgInfo"), message.getPayload()); log.info("msgInfo:{} ; payload:{} ", headers.get("msgInfo"), message.getPayload());
log.info("msgInfo:{} ; payload:{} ", headers.get("msgInfo"), message.getPayload()); // DELIVERY_TAG 代表 RabbitMQ 向该 Channel 投递的这条消息的唯一标识 ID是一个单调递增的正整数
Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG); Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
// 第二个参数代表是否一次签收多条,当该参数为 true 时,则可以一次性确认 DELIVERY_TAG 小于等于传入值的所有消息 // 第二个参数代表是否一次签收多条,当该参数为 true 时,则可以一次性确认 DELIVERY_TAG 小于等于传入值的所有消息
channel.basicAck(deliveryTag, false); channel.basicAck(deliveryTag, false);
@ -213,7 +214,7 @@ public class RabbitmqConsumer {
spring: spring:
rabbitmq: rabbitmq:
addresses: 127.0.0.1:5672 addresses: 127.0.0.1:5672
addresses: 127.0.0.1:5672 # RabbitMQ 默认的用户名和密码都是 guest 而虚拟主机名称是 "/"
# 如果配置其他虚拟主机地址,需要预先用管控台或者图形界面创建 图形界面地址 http://主机地址:15672 # 如果配置其他虚拟主机地址,需要预先用管控台或者图形界面创建 图形界面地址 http://主机地址:15672
username: guest username: guest
password: guest password: guest
@ -250,7 +251,7 @@ public class RabbitmqProducer {
// 创建消息 // 创建消息
Message<Object> msg = MessageBuilder.createMessage(message, messageHeaders); Message<Object> msg = MessageBuilder.createMessage(message, messageHeaders);
/* 确认的回调 确认消息是否到达 Broker 服务器 其实就是是否到达交换器 /* 确认的回调 确认消息是否到达 Broker 服务器 其实就是是否到达交换器
/* 确认的回调 确认消息是否到达 Broker 服务器 其实就是是否到达交换器 如果发送时候指定的交换器不存在 ack 就是 false 代表消息不可达 */
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> { rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
log.info("correlationData{} , ack:{}", correlationData.getId(), ack); log.info("correlationData{} , ack:{}", correlationData.getId(), ack);
if (!ack) { if (!ack) {
@ -264,7 +265,7 @@ public class RabbitmqProducer {
log.info("message:{}; replyCode: {}; replyText: {} ; exchange:{} ; routingKey:{}", log.info("message:{}; replyCode: {}; replyText: {} ; exchange:{} ; routingKey:{}",
message1, replyCode, replyText, exchange, routingKey); message1, replyCode, replyText, exchange, routingKey);
}); });
}); // 在实际中 ID 应该是全局唯一 能够唯一标识消息 消息不可达的时候触发 ConfirmCallback 回调方法时可以获取该值,进行对应的错误处理
CorrelationData correlationData = new CorrelationData(messageId); CorrelationData correlationData = new CorrelationData(messageId);
rabbitTemplate.convertAndSend(exchangeName, key, msg, correlationData); rabbitTemplate.convertAndSend(exchangeName, key, msg, correlationData);
} }
@ -288,14 +289,14 @@ public class RabbitmqProducerTests {
public void send() { public void send() {
Map<String, Object> heads = new HashMap<>(); Map<String, Object> heads = new HashMap<>();
heads.put("msgInfo", "自定义消息头信息"); heads.put("msgInfo", "自定义消息头信息");
heads.put("msgInfo", "自定义消息头信息"); // 模拟生成消息 ID,在实际中应该是全局唯一的 消息不可达时候可以在 setConfirmCallback 回调中取得,可以进行对应的重发或错误处理
String id = String.valueOf(Math.round(Math.random() * 10000)); String id = String.valueOf(Math.round(Math.random() * 10000));
producer.sendSimpleMessage(heads, "hello Spring", id, RabbitInfo.EXCHANGE_NAME, "springboot.simple.abc"); producer.sendSimpleMessage(heads, "hello Spring", id, RabbitInfo.EXCHANGE_NAME, "springboot.simple.abc");
} }
/*** /***
/*** * 发送消息体为 bean 的消息
*/ */
@Test @Test
public void sendBean() { public void sendBean() {
@ -311,13 +312,13 @@ public class RabbitmqProducerTests {
## 六、项目构建的说明 ## 六、项目构建的说明
因为在项目中consumer 和 producer 模块均依赖公共模块,所以在构建 consumer 和 producer 项目前需要将 common 模块安装到本地仓库,**依次**对**父工程**和**common 模块**执行:
```shell ```shell
mvn install -Dmaven.test.skip = true mvn install -Dmaven.test.skip = true
``` ```
consumer 中 pom.xml 如下
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -360,7 +361,7 @@ consumer中 pom.xml如下
</project> </project>
``` ```
producer 中 pom.xml 如下
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>

View File

@ -1,5 +1,6 @@
# spring boot 整合 redis # spring boot 整合 redis
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/>
@ -38,8 +39,8 @@
#### 1.1 项目结构 #### 1.1 项目结构
1. RedisConfig.java 实现了 redisTemplate 序列化与反序列化的配置;
1. RedisConfig.java实现了redisTemplate 序列化与反序列化的配置; 2. RedisOperation 和 RedisObjectOperation 分别封装了对基本类型和对象的操作。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-redis.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-redis.png"/> </div>
@ -70,7 +71,7 @@ spring:
redis: redis:
host: 127.0.0.1 host: 127.0.0.1
port: 6379 port: 6379
port: 6379 # 默认采用的也是 0 号数据库 redis 官方在 4.0 之后版本就不推荐采用单节点多数据库 (db1-db15) 的方式存储数据,如果有需要应该采用集群方式构建
database: 0 database: 0
# 如果是集群节点 采用如下配置指定节点 # 如果是集群节点 采用如下配置指定节点
@ -79,10 +80,10 @@ spring:
#### 2.2 封装redis基本操作 #### 2.2 封装redis基本操作
需要说明的是 spring boot 提供了两个 template 用于操作 redis:
- StringRedisTemplate由于 redis 在大多数使用情况下都是操作字符串类型的存储,所以 spring boot 将对字符串的操作单独封装在 StringRedisTemplate
- StringRedisTemplate由于redis在大多数使用情况下都是操作字符串类型的存储所以spring boot 将对字符串的操作单独封装在StringRedisTemplate - RedisTemplate<Object, Object> redis 用于操作任意类型的 template
```java ```java
/** /**
@ -212,15 +213,15 @@ public class RedisObjectOperation {
#### 2.3 redisTemplate 序列化为json格式与反序列化 #### 2.3 redisTemplate 序列化为json格式与反序列化
这里需要说明的 spring boot 的 redisTemplate 本身是实现了对象的序列化与反序列化的,是支持直接存取对象的。但是这里的序列化默认采用的是 JdkSerializationRedisSerializer.serialize() 序列化为二进制码,这个本身是不影响 redisTemplate 的操作的,因为 redisTemplate 在取出数据的时候会自动进行反序列化。
但是如果我们在命令行中使用 get 命令去获取数据时候,得到的是一串不直观的二进制码,所以我们尽量将其序列化为直观的 json 格式存储。
```java ```java
/** /**
* @author : heibaiying * @author : heibaiying
* @description : 自定义序列化器 * @description : 自定义序列化器
* @description : 自定义序列化器 * 不定义的话默认采用的是 serializer.JdkSerializationRedisSerializer.serialize() 序列化为二进制字节码 存储在数据库中不直观
*/ */
@Configuration @Configuration
public class RedisConfig { public class RedisConfig {
@ -230,7 +231,7 @@ public class RedisConfig {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用 Jackson2JsonRedisSerialize 需要导入依赖 com.fasterxml.jackson.core jackson-databind
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
@ -241,7 +242,7 @@ public class RedisConfig {
jackson2JsonRedisSerializer.setObjectMapper(objectMapper); jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置 key,value 序列化规则
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet(); redisTemplate.afterPropertiesSet();
@ -275,8 +276,8 @@ public class RedisTests {
} }
/* /*
/* * 需要注意的是 Redis 的集合set不仅不允许有重复元素并且集合中的元素是无序的
* 需要注意的是Redis的集合set不仅不允许有重复元素并且集合中的元素是无序的 * 不能通过索引下标获取元素。哪怕你在 java 中传入的集合是有序的 newLinkedHashSet但是实际在 Redis 存储的还是无序的集合
*/ */
@Test @Test
public void SetOperation() { public void SetOperation() {
@ -306,9 +307,9 @@ public class RedisTests {
5. 键过期:**expire key seconds** 5. 键过期:**expire key seconds**
ttl 命令会返回键的剩余过期时间, 它有 3 种返回值:
- 大于等于 0 的整数: 键剩余的过期时间。
- -1 键没设置过期时间。 - -1 键没设置过期时间。
- -2 键不存在 - -2 键不存在
@ -316,23 +317,23 @@ public class RedisTests {
#### 1.1.2 数据结构和内部编码 #### 1.1.2 数据结构和内部编码
type 命令实际返回的就是当前键的数据结构类型, 它们分别是:**string**(字符串) 、 **hash**(哈希) 、 **list**(列表) 、 **set**(集合) 、 **zset**(有序集合)
#### 1.1.3 单线程架构 #### 1.1.3 单线程架构
1. 纯内存访问, Redis 将所有数据放在内存中, 内存的响应时长大约为 100 纳秒, 这是 Redis 达到每秒万级别访问的重要基础。
1. 纯内存访问, Redis将所有数据放在内存中 内存的响应时长大约为100纳秒 这是Redis达到每秒万级别访问的重要基础。 2. 非阻塞 I/O Redis 使用 epoll 作为 I/O 多路复用技术的实现, 再加上 Redis 自身的事件处理模型将 epoll 中的连接、 读写、 关闭都转换为事件, 不在网络 I/O 上浪费过多的时间, 如图 2-6 所示。
3. 单线程避免了线程切换和竞态产生的消耗。 3. 单线程避免了线程切换和竞态产生的消耗。
### 1.2 字符串 ### 1.2 字符串
| 作用 | 格式 | 参数或示例 | | 作用 | 格式 | 参数或示例 |
| ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | 设置值 | set key value \[ex seconds]\[px milliseconds][nx\|xx] setnx setex | ex seconds 为键设置秒级过期时间。 <br/>px milliseconds 为键设置毫秒级过期时间。<br/>nx 键必须不存在, 才可以设置成功, 用于添加。<br/>xx 与 nx 相反, 键必须存在, 才可以设置成功, 用于更新。 |
| 设置值 | set key value \[ex seconds]\[px milliseconds][nx\|xx] setnx setex | ex seconds 为键设置秒级过期时间。 <br/>px milliseconds 为键设置毫秒级过期时间。<br/>nx 键必须不存在, 才可以设置成功, 用于添加。<br/>xx 与nx相反 键必须存在, 才可以设置成功, 用于更新。 | | 获取值 | get key | r 如果获取的键不存在 ,则返回 nil(空) |
| 批量设置 | mset key value [key value ...] | mset a 1 b 2 c 3 d 4 | | 批量设置 | mset key value [key value ...] | mset a 1 b 2 c 3 d 4 |
| 批量获取值 | mget key [key ...] | mget a b c d | | 批量获取值 | mget key [key ...] | mget a b c d |
| 批量获取值 | mget key [key ...] | mget a b c d | | 计数 | incr key decr key incrby key increment指定数值自增<br/>decrby key decrement指定数值自减<br/>incrbyfloat key increment (浮点数自增) | 值不是整数, 返回错误。 值是整数, 返回自增或自减后的结果。<br/>键不存在,创建键,并按照值为 0 自增或自减, 返回结果为 1。 |
| 追加值 | append key value | 向字符串的默认追加值 | | 追加值 | append key value | 向字符串的默认追加值 |
| 字符串长度 | strlen key | 获取字符串长度,中文占用三个字节 | | 字符串长度 | strlen key | 获取字符串长度,中文占用三个字节 |
| 设置并返回原值 | getset key value | | | 设置并返回原值 | getset key value | |
@ -345,13 +346,13 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
| ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 设置值 | hset key field value | hset user:1 name tom<br/>hset user:1 age 12 | | 设置值 | hset key field value | hset user:1 name tom<br/>hset user:1 age 12 |
| 获取值 | hget key field | hget user:1 name | | 获取值 | hget key field | hget user:1 name |
| 获取值 | hget key field | hget user:1 name | | 删除 field | hdel key field [field ...] | |
| 删除field | hdel key field [field ...] | | | 计算 field 个数 | hlen key | |
| 计算field个数 | hlen key | | | 批量设置或获取 field-value | hmget key field [field]<br/>hmset key field value [field value...] | hmset user:1 name mike age 12 city tianjin<br/>hmget user:1 name city |
| 批量设置或获取field-value | hmget key field [field]<br/>hmset key field value [field value...] | hmset user:1 name mike age 12 city tianjin<br/>hmget user:1 name city | | 判断 field 是否存在 | hexists key field | |
| 判断field是否存在 | hexists key field | | | 获取所有 field | hkeys key | |
| 获取所有field | hkeys key | | | 获取所有 value | hvals key | |
| 获取所有value | hvals key | | | 获取所有的 filed-value | hgetall key | 如果哈希元素个数比较多, 会存在阻塞 Redis 的可能。<br/>获取全部 可以使用 hscan 命令, 该命令会渐进式遍历哈希类型 |
| 计数 | hincrby key field<br/>hincrbyfloat key field | | | 计数 | hincrby key field<br/>hincrbyfloat key field | |
### 1.4 列表 ### 1.4 列表
@ -360,7 +361,7 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
| -------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | -------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 增 | 左侧插入lpush key value [value ...] 右侧插入rpush key value [value ...] 某个指定元素前后插入linsert key before\|after pivot value | | | 增 | 左侧插入lpush key value [value ...] 右侧插入rpush key value [value ...] 某个指定元素前后插入linsert key before\|after pivot value | |
| 查 | 获取指定范围内的元素列表lrange key start end 获取列表指定索引下标的元素lindex key index 获取列表指定长度llen key | lrange listkey 0 -1 | | 查 | 获取指定范围内的元素列表lrange key start end 获取列表指定索引下标的元素lindex key index 获取列表指定长度llen key | lrange listkey 0 -1 |
| | 获取指定范围内的元素列表lrange key start end 获取列表指定索引下标的元素lindex key index 获取列表指定长度llen key | lrange listkey 0 -1 | | | 从列表左侧弹出元素lpop key 从列表右侧弹出元素rpop key 删除指定元素lrem key count value 截取列表ltrim key start end | count>0 从左到右, 删除最多 count 个元素。<br/>count<0 从右到左 删除最多 count 绝对值个元素。<br/>count=0 删除所有 |
| 改 | 修改指定索引下标的元素lset key index newValue | | | 改 | 修改指定索引下标的元素lset key index newValue | |
| 阻塞操作 | blpop key [key ...] timeout brpop key [key ...] timeout | key[key...] 多个列表的键。 timeout 阻塞时间\|等待时间(单位: 秒) | | 阻塞操作 | blpop key [key ...] timeout brpop key [key ...] timeout | key[key...] 多个列表的键。 timeout 阻塞时间\|等待时间(单位: 秒) |
@ -378,9 +379,9 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
| 删除元素 | srem key element [element ...] | 返回结果为成功删除的元素个数 | | 删除元素 | srem key element [element ...] | 返回结果为成功删除的元素个数 |
| 计算元素个数 | scard key | | | 计算元素个数 | scard key | |
| 判断元素是否在集合中 | sismember key element | | | 判断元素是否在集合中 | sismember key element | |
| 判断元素是否在集合中 | sismember key element | | | 随机返回 | srandmember key [count] | 随机从集合返回指定个数元素count 默认为 1 |
| 从集合随机弹出元素 | spop key | srandmember 不会从集合中删除元素spop 会 | | 从集合随机弹出元素 | spop key | srandmember 不会从集合中删除元素spop 会 |
| 从集合随机弹出元素 | spop key | srandmember 不会从集合中删除元素spop 会 | | 获取集合中所有元素 | smembers key | 可用 sscan 代替 |
**集合间操作** **集合间操作**
@ -399,14 +400,14 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
| 作用 | 格式 | 参数或示例 | | 作用 | 格式 | 参数或示例 |
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | 添加成员 | zadd key score member [score member ...] | nx member 必须不存在, 才可设置成功, 用于添加。<br> xx member 必须存在, 才可以设置成功, 用于更新。<br/>ch 返回此次操作后, 有序集合元素和分数发生变化的个数<br/>incr 对 score 做增加, 相当于后面介绍的 zincrby。 |
| 计算成员个数 | zcard key | | | 计算成员个数 | zcard key | |
| 计算某个成员的分数 | zscore key member | | | 计算某个成员的分数 | zscore key member | |
| 计算某个成员的分数 | zscore key member | | | 计算某个成员的排名 | zrank key member zrevrank key member | zrank 是从分数从低到高返回排名, zrevrank 反之。 |
| 删除成员 | zrem key member [member ...] | | | 删除成员 | zrem key member [member ...] | |
| 增加成员分数 | zincrby key increment member | zincrby user:ranking 9 tom | | 增加成员分数 | zincrby key increment member | zincrby user:ranking 9 tom |
| 增加成员分数 | zincrby key increment member | zincrby user:ranking 9 tom | | 返回指定排名范围的成员 | zrange key start end [withscores] zrange key start end [withscores] | zrange 是从低到高返回, zrevrange 反之。 |
| 返回指定排名范围的成员 | zrange key start end [withscores] zrange key start end [withscores] | zrange是从低到高返回 zrevrange反之。 | | 返回指定分数范围的成员 | zrangebyscore key min max \[withscores][limit offset count] zrevrangebyscore key max min \[withscores][limit offset count] | 其中 zrangebyscore 按照分数从低到高返回, zrevrangebyscore 反之。 [limit offset count]选项可以限制输出的起始位置和个数: 同时 min 和 max 还支持开区间(小括号) 和闭区间(中括号) -inf 和 +inf 分别代表无限小和无限大 |
| 删除指定排名内的升序元素 | zremrangerank key start end | | | 删除指定排名内的升序元素 | zremrangerank key start end | |
| 删除指定分数范围的成员 | zremrangebyscore key min max | | | 删除指定分数范围的成员 | zremrangebyscore key min max | |
@ -420,8 +421,8 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
- destination 交集计算结果保存到这个键。 - destination 交集计算结果保存到这个键。
- numkeys 需要做交集计算键的个数。 - numkeys 需要做交集计算键的个数。
- key[key...] 需要做交集计算的键。 - key[key...] 需要做交集计算的键。
- key[key...] 需要做交集计算的键。 - weights weight[weight...] 每个键的权重, 在做交集计算时, 每个键中的每个 member 会将自己分数乘以这个权重, 每个键的权重默认是 1。
- weights weight[weight...] 每个键的权重, 在做交集计算时, 每个键中的每个member会将自己分数乘以这个权重 每个键的权重默认是1。 - aggregate sum|min|max 计算成员交集后, 分值可以按照 sum 、min最小值 、 max最大值 做汇总, 默认值是 sum。
### 1.7 键管理 ### 1.7 键管理
@ -431,7 +432,7 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
**rename key newkey** **rename key newkey**
为了防止被强行 rename Redis 提供了 renamenx 命令, 确保只有 newKey 不存在时候才被覆盖。
##### 2. 随机返回键 ##### 2. 随机返回键
@ -439,19 +440,19 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
##### 3.键过期 ##### 3.键过期
- expire key seconds 键在 seconds 秒后过期。
- expire key seconds 键在seconds秒后过期。 - expireat key timestamp 键在秒级时间戳 timestamp 后过期。
- expireat key timestamp 键在秒级时间戳timestamp后过期。 - pexpire key milliseconds 键在 milliseconds 毫秒后过期。
- pexpire key milliseconds 键在milliseconds毫秒后过期 - pexpireat key milliseconds-timestamp 键在毫秒级时间戳 timestamp 后过期
注意: 注意:
1. 如果 expire key 的键不存在, 返回结果为 0
1. 如果expire key的键不存在 返回结果为0 2. 如果设置过期时间为负值, 键会立即被删除, 犹如使用 del 命令一样
2. 如果设置过期时间为负值, 键会立即被删除, 犹如使用del命令一样 3. persist key t 命令可以将键的过期时间清除
3. persist key t命令可以将键的过期时间清除 4. 对于字符串类型键, 执行 set 命令会去掉过期时间, 这个问题很容易在开发中被忽视
4. 对于字符串类型键, 执行set命令会去掉过期时间 这个问题很容易在开发中被忽视 5. Redis 不支持二级数据结构(例如哈希、 列表) 内部元素的过期功能, 例如不能对列表类型的一个元素做过期时间设置
5. Redis不支持二级数据结构例如哈希、 列表) 内部元素的过期功能, 例如不能对列表类型的一个元素做过期时间设置 6. setex 命令作为 set+expire 的组合, 不但是原子执行, 同时减少了一次网络通讯的时间
#### 1.7.2 键遍历 #### 1.7.2 键遍历
@ -463,9 +464,9 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
scan cursor \[match pattern] \[count number] scan cursor \[match pattern] \[count number]
- cursor 是必需参数, 实际上 cursor 是一个游标, 第一次遍历从 0 开始, 每次 scan 遍历完都会返回当前游标的值, 直到游标值为 0 表示遍历结束。
- cursor是必需参数 实际上cursor是一个游标 第一次遍历从0开始 每次scan遍历完都会返回当前游标的值 直到游标值为0 表示遍历结束 - match pattern 是可选参数, 它的作用的是做模式的匹配, 这点和 keys 的模式匹配很像
- match pattern是可选参数, 它的作用的是做模式的匹配, 这点和keys的模式匹配很像。 - count number 是可选参数, 它的作用是表明每次要遍历的键个数, 默认值是 10 此参数可以适当增大。
#### 1.7.3 数据库管理 #### 1.7.3 数据库管理
@ -475,4 +476,4 @@ scan cursor \[match pattern] \[count number]
##### 2.flushdb/flushall ##### 2.flushdb/flushall
flushdb/flushall 命令用于清除数据库, 两者的区别的是 flushdb 只清除当前数据库, flushall 会清除所有数据库。

View File

@ -1,5 +1,6 @@
# spring boot 整合 servlet # spring boot 整合 servlet
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目依赖">1.2 项目依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目依赖">1.2 项目依赖</a><br/>
@ -18,9 +19,9 @@
#### 1.1 项目结构说明 #### 1.1 项目结构说明
1. 项目提供与 servlet 整合的两种方式,一种是 servlet3.0 原生的注解方式,一种是采用 spring 注册的方式;
1. 项目提供与servlet整合的两种方式一种是servlet3.0 原生的注解方式,一种是采用spring 注册方式; 2. servlet、过滤器、监听器分别位于 servlet、filter、listen 下,其中以 Annotation 命名结尾的代表是 servlet 注解方式实现,采用 spring 注册方式则在 ServletConfig 中进行注册
2. servlet、过滤器、监听器分别位于servlet、filter、listen 下其中以Annotation命名结尾的代表是servlet注解方式实现采用spring注册方式则在ServletConfig中进行注册 3. 为了说明外置容器对 servlet 注解的自动发现机制,项目采用外置容器构建,关于 spring boot 整合外置容器的详细说明可以参考[spring-boot-tomcat](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-tomcat)
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-servlet.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-servlet.png"/> </div>
@ -31,7 +32,7 @@
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
<artifactId>spring-boot-starter-web</artifactId> <!--排除依赖 使用外部 tomcat 容器启动-->
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -40,7 +41,7 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency> <dependency>
<dependency> <!--使用外置容器时候 SpringBootServletInitializer 依赖此包 -->
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId> <artifactId>servlet-api</artifactId>
<version>2.5</version> <version>2.5</version>
@ -73,7 +74,7 @@ public class CustomFilter implements Filter {
@Override @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setAttribute("filterParam","我是 filter 传递的参数");
chain.doFilter(request,response); chain.doFilter(request,response);
response.getWriter().append(" CustomFilter "); response.getWriter().append(" CustomFilter ");
} }
@ -92,7 +93,7 @@ public class CustomFilter implements Filter {
*/ */
public class CustomListen implements ServletContextListener { public class CustomListen implements ServletContextListener {
//Web 应用程序初始化过程正在启动的通知
@Override @Override
public void contextInitialized(ServletContextEvent sce) { public void contextInitialized(ServletContextEvent sce) {
System.out.println("容器初始化启动"); System.out.println("容器初始化启动");
@ -100,8 +101,8 @@ public class CustomListen implements ServletContextListener {
/* 通知 servlet 上下文即将关闭
/* 通知servlet上下文即将关闭 * 这个地方如果我们使用的是 spring boot 内置的容器 是监听不到销毁过程,所以我们使用了外置 tomcat 容器
*/ */
@Override @Override
public void contextDestroyed(ServletContextEvent sce) { public void contextDestroyed(ServletContextEvent sce) {
@ -113,7 +114,7 @@ public class CustomListen implements ServletContextListener {
```java ```java
/** /**
* @author : heibaiying * @author : heibaiying
* @author : heibaiying * @description : 自定义 servlet
*/ */
public class CustomServlet extends HttpServlet { public class CustomServlet extends HttpServlet {
@ -199,7 +200,7 @@ public class CustomFilterAnnotation implements Filter {
@WebListener @WebListener
public class CustomListenAnnotation implements ServletContextListener { public class CustomListenAnnotation implements ServletContextListener {
//Web 应用程序初始化过程正在启动的通知
@Override @Override
public void contextInitialized(ServletContextEvent sce) { public void contextInitialized(ServletContextEvent sce) {
System.out.println("容器初始化启动 Annotation"); System.out.println("容器初始化启动 Annotation");
@ -207,8 +208,8 @@ public class CustomListenAnnotation implements ServletContextListener {
/* 通知 servlet 上下文即将关闭
/* 通知servlet上下文即将关闭 * 这个地方如果我们使用的是 spring boot 内置的容器 是监听不到销毁过程,所以我们使用了外置 tomcat 容器
*/ */
@Override @Override
public void contextDestroyed(ServletContextEvent sce) { public void contextDestroyed(ServletContextEvent sce) {
@ -221,7 +222,7 @@ public class CustomListenAnnotation implements ServletContextListener {
/** /**
* @author : heibaiying * @author : heibaiying
* @author : heibaiying * @description : 自定义 servlet
*/ */
@WebServlet(urlPatterns = "/servletAnn") @WebServlet(urlPatterns = "/servletAnn")
public class CustomServletAnnotation extends HttpServlet { public class CustomServletAnnotation extends HttpServlet {
@ -241,4 +242,4 @@ public class CustomServletAnnotation extends HttpServlet {
#### 3.2 使注解生效 #### 3.2 使注解生效
1. 如果是内置容器,需要在启动类上添加@ServletComponentScan("com.heibaiying.springbootservlet") ,指定扫描的包目录; 1. 如果是内置容器,需要在启动类上添加@ServletComponentScan("com.heibaiying.springbootservlet") ,指定扫描的包目录;
1. 如果是置容器,需要在启动类上添加@ServletComponentScan("com.heibaiying.springbootservlet") ,指定扫描的包目录; 2. 如果是置容器,需要进行任何配置,依靠容器内建的 discovery 机制自动发现,需要说明的是这里的容器必须支持 servlet3.0tomcat 从 7.0 版本开始支持 Servlet3.0)。

View File

@ -17,23 +17,23 @@
### 1.1 Springfox ### 1.1 Springfox
Springfox 是一个开源的API Doc的框架 它的前身是swagger-springmvc能够完美的支持springmvc可以将spring 接口方法自动转换为接口文档。 目前spring fox 正致力于对更多JSON API规范和标准的扩展和支持例如[swagger](http://swagger.io/)[RAML](http://raml.org/)和[jsonapi](http://jsonapi.org/)。 Springfox 是一个开源的 API Doc 的框架, 它的前身是 swagger-springmvc能够完美的支持 springmvc可以将 spring 接口方法自动转换为接口文档。 目前 spring fox 正致力于对更多 JSON API 规范和标准的扩展和支持,例如:[swagger](http://swagger.io/)[RAML](http://raml.org/) 和[jsonapi](http://jsonapi.org/)。
### 1.2 Swagger ### 1.2 Swagger
Swagger是一个规范和完整的框架用于生成、描述、调用和可视化 RESTful 风格的 Web 服务支持从整个API生命周期从设计和文档到测试和部署的开发。 Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务,支持从整个 API 生命周期(从设计和文档到测试和部署)的开发。
swagger 是一个综合的开源项目,包含[swagger-core](https://github.com/swagger-api/swagger-core)、[swagger-ui](https://github.com/swagger-api/swagger-ui)、[swagger-codegen](https://github.com/swagger-api/swagger-codegen)、[swagger-editor](https://github.com/swagger-api/swagger-editor)等多个子项目。 swagger 是一个综合的开源项目,包含[swagger-core](https://github.com/swagger-api/swagger-core)、[swagger-ui](https://github.com/swagger-api/swagger-ui)、[swagger-codegen](https://github.com/swagger-api/swagger-codegen)、[swagger-editor](https://github.com/swagger-api/swagger-editor) 等多个子项目。
+ **swagger-core**Swagger CoreOpenAPI规范以前称为Swagger规范的**Java实现**。 + **swagger-core**Swagger CoreOpenAPI 规范(以前称为 Swagger 规范)的**Java 实现**。
+ **swagger-ui**依据可视化文档提供与API资源的可视化交互。 + **swagger-ui**:依据可视化文档,提供与 API 资源的可视化交互。
+ **swagger-codegen**:开源的代码生成器,根据Swagger定义的RESTful API可以自动建立服务端和客户端的连接。 + **swagger-codegen**:开源的代码生成器,根据 Swagger 定义的 RESTful API 可以自动建立服务端和客户端的连接。
+ **swagger-editor**开源的api文档编辑器。 + **swagger-editor**:开源的 api 文档编辑器。
下图为swagger-ui 提供的文档可视化界面示例: 下图为 swagger-ui 提供的文档可视化界面示例:
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/Swagger_UI.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/Swagger_UI.png"/> </div>
@ -41,7 +41,7 @@ swagger 是一个综合的开源项目,包含[swagger-core](https://github.com
### 1.3 OpenApi、Swagger、Springfox的关系 ### 1.3 OpenApi、Swagger、Springfox的关系
**Swagger Core 是 OpenApi 规范以前称为Swagger规范的Java 实现,而 Springfox 提供 Swagger 与 spring 的集成支持**<br/> **Swagger Core 是 OpenApi 规范(以前称为 Swagger 规范)的 Java 实现,而 Springfox 提供 Swagger 与 spring 的集成支持**<br/>
@ -68,17 +68,17 @@ swagger 是一个综合的开源项目,包含[swagger-core](https://github.com
### 2.2 进行swagger个性化配置、并用@EnableSwagger2开启Swagger支持 ### 2.2 进行swagger个性化配置、并用@EnableSwagger2开启Swagger支持
这里需要说明的是swagger虽然是一个非常直观易用的接口调试插件但是有可能导致接口信息泄露的危险所以建议在开发环境和测试环境开启在生产环境关闭。这里一共给出三种Swagger开关切换的方法 这里需要说明的是 swagger 虽然是一个非常直观易用的接口调试插件,但是有可能导致接口信息泄露的危险,所以建议在开发环境和测试环境开启,在生产环境关闭。这里一共给出三种 Swagger 开关切换的方法:
1. 如下面代码所示在配置文件中配置自定义的开关参数并在创建Docket时候在链式调用的enable()方法中传入; 1. 如下面代码所示,在配置文件中配置自定义的开关参数,并在创建 Docket 时候,在链式调用的 enable() 方法中传入;
2.`SwaggerConfig`配置类上添加`@Profile({"dev","test"}) `注解指明在开发环境和测试环境下激活此配置类打包或者部署时候使用spring.profiles.active指明环境即可 2. `SwaggerConfig` 配置类上添加 `@Profile({"dev","test"}) ` 注解,指明在开发环境和测试环境下激活此配置类,打包或者部署时候使用 spring.profiles.active 指明环境即可;
3. 在配置文件中配置自定义的开关参数,并在`SwaggerConfig`配置类上添加`@ConditionalOnProperty(name = "swagger.enable", havingValue = "true") `,指明配置类的生效条件 3. 在配置文件中配置自定义的开关参数,并在 `SwaggerConfig` 配置类上添加 `@ConditionalOnProperty(name = "swagger.enable", havingValue = "true") `,指明配置类的生效条件
注:@ConditionalOnProperty 注解说明 注:@ConditionalOnProperty 注解说明
@ConditionalOnProperty注解能够控制某个@configuration修饰的配置类是否生效。具体操作是通过namehavingValue属性来实现name对应application.propertiesyml中的某个属性值如果该值为空则返回false如果值不为空则将该值与havingValue指定的值进行比较如果一样则返回true否则返回false。如果返回值为false则该configuration不生效为true则生效。 @ConditionalOnProperty 注解能够控制某个@configuration 修饰的配置类是否生效。具体操作是通过 namehavingValue 属性来实现name 对应 application.propertiesyml中的某个属性值如果该值为空则返回 false如果值不为空则将该值与 havingValue 指定的值进行比较,如果一样则返回 true否则返回 false。如果返回值为 false则该 configuration 不生效;为 true 则生效。
```java ```java
/** /**
@ -93,8 +93,8 @@ public class SwaggerConfig {
private boolean swaggerEnable; private boolean swaggerEnable;
/*** /***
* 配置swagger * 配置 swagger
* 开发和测试环境下可以开启swagger辅助进行调试,而生产环境下可以关闭或者进行相应的权限控制,防止接口信息泄露 * 开发和测试环境下可以开启 swagger 辅助进行调试,而生产环境下可以关闭或者进行相应的权限控制,防止接口信息泄露
*/ */
@Bean @Bean
public Docket createRestApi() { public Docket createRestApi() {
@ -121,7 +121,7 @@ public class SwaggerConfig {
} }
/** /**
* 可以使用正则定义url过滤规则 * 可以使用正则定义 url 过滤规则
*/ */
private Predicate<String> doFilteringRules() { private Predicate<String> doFilteringRules() {
return not( return not(
@ -149,7 +149,7 @@ swagger.enable = true
public class ProductController { public class ProductController {
/*** /***
* 一个标准的swagger注解 * 一个标准的 swagger 注解
*/ */
@ApiOperation(notes = "查询所有产品", value = "产品查询接口") @ApiOperation(notes = "查询所有产品", value = "产品查询接口")
@ApiImplicitParams( @ApiImplicitParams(
@ -197,7 +197,7 @@ public class ProductController {
} }
/** /**
* 不加上任何swagger相关的注解也会被扫描到 如果不希望被扫描到,需要用 @ApiIgnore 修饰 * 不加上任何 swagger 相关的注解也会被扫描到 如果不希望被扫描到,需要用 @ApiIgnore 修饰
*/ */
@PostMapping(value = "/normal") @PostMapping(value = "/normal")
public ResponseEntity<Void> normal() { public ResponseEntity<Void> normal() {
@ -233,19 +233,19 @@ swagger 为了最大程度防止对逻辑代码的侵入,基本都是依靠注
4. `@ApiImplicitParams`:用在方法上说明方法参数的作用; 4. `@ApiImplicitParams`:用在方法上说明方法参数的作用;
5. `@ApiImplicitParam`:用在@ApiImplicitParams注解中,描述每个具体参数; 5. `@ApiImplicitParam`:用在@ApiImplicitParams 注解中,描述每个具体参数;
6. `@ApiResponses`:一组@ApiResponse的配置 6. `@ApiResponses`:一组@ApiResponse 的配置;
7. `@ApiResponse`:请求返回的配置; 7. `@ApiResponse`:请求返回的配置;
8. `@ResponseHeader`:响应头的配置; 8. `@ResponseHeader`:响应头的配置;
9. `@ApiModel`描述一个Model的信息一般用在post创建的时候使用@RequestBody接收参数的场景 9. `@ApiModel`:描述一个 Model 的信息(一般用在 post 创建的时候,使用@RequestBody 接收参数的场景);
10. `@ApiModelProperty`描述model的属性。 10. `@ApiModelProperty`:描述 model 的属性。
11. `@ApiIgnore`可以用于类、方法、属性代表该方法、类、属性不被swagger的文档所管理。 11. `@ApiIgnore`:可以用于类、方法、属性,代表该方法、类、属性不被 swagger 的文档所管理。
@ -258,10 +258,10 @@ swagger 为了最大程度防止对逻辑代码的侵入,基本都是依靠注
### 2.5 利用swagger-ui进行接口测试 ### 2.5 利用swagger-ui进行接口测试
​点击对应接口的`try it out`按钮可以进行接口测试,此时可以输入对应的参数的值,然后点击下方的`Execute`按钮发送请求。<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/swagger-try-it.png"/> </div> ​点击对应接口的 `try it out` 按钮可以进行接口测试,此时可以输入对应的参数的值,然后点击下方的 `Execute` 按钮发送请求。<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/swagger-try-it.png"/> </div>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/swagger-execute.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/swagger-execute.png"/> </div>
post方法可以直接修改model 对应的 json数据 ,然后点击下方的`Execute`按钮发送请求。 post 方法可以直接修改 model 对应的 json 数据 ,然后点击下方的 `Execute` 按钮发送请求。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/swagger-post-try.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/swagger-post-try.png"/> </div>

View File

@ -1,5 +1,6 @@
# spring boot 整合 tomcat # spring boot 整合 tomcat
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目主要依赖">1.2 项目主要依赖</a><br/>
@ -15,7 +16,7 @@
#### 1.1 项目结构说明 #### 1.1 项目结构说明
spring boot 整合 tomcat 后支持 jsp 的使用(内置容器默认是不支持 jsp所以项目整合后采用 jspController 跳转到 show.jsp 测试整合是否成功。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-tomcat.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-tomcat.png"/> </div>
@ -25,7 +26,7 @@ spring boot 整合 tomcat 后支持jsp 的使用(内置容器默认是不支
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
<artifactId>spring-boot-starter-web</artifactId> <!--排除内置容器依赖 使用外部 tomcat 容器启动-->
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -34,7 +35,7 @@ spring boot 整合 tomcat 后支持jsp 的使用(内置容器默认是不支
</exclusions> </exclusions>
</dependency> </dependency>
<dependency> <dependency>
<dependency> <!--使用外置容器时候 SpringBootServletInitializer 依赖此包 -->
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId> <artifactId>servlet-api</artifactId>
<version>2.5</version> <version>2.5</version>
@ -48,8 +49,8 @@ spring boot 整合 tomcat 后支持jsp 的使用(内置容器默认是不支
```java ```java
/** /**
/** * 如果用外置 tomcat,启动报错 java.lang.NoClassDefFoundError: javax/el/ELManager
* 如果用外置tomcat,启动报错java.lang.NoClassDefFoundError: javax/el/ELManager * 是因为 tomcat 7.0 el-api 包中没有 ELManager 类 , 切换 tomcat 为 8.0 以上版本即可
*/ */
@SpringBootApplication @SpringBootApplication
public class SpringBootTomcatApplication extends SpringBootServletInitializer { public class SpringBootTomcatApplication extends SpringBootServletInitializer {
@ -57,7 +58,7 @@ public class SpringBootTomcatApplication extends SpringBootServletInitializer {
@Override @Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { //传入 SpringBoot 应用的主程序
return application.sources(SpringBootTomcatApplication.class); return application.sources(SpringBootTomcatApplication.class);
} }

View File

@ -17,10 +17,10 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
1. 项目模拟一个简单的群聊功能为区分不同的聊天客户端登录时候将临时用户名存储在session当中 1. 项目模拟一个简单的群聊功能,为区分不同的聊天客户端,登录时候将临时用户名存储在 session 当中;
2. 关于websocket的主要配置在websocket文件夹下 2. 关于 websocket 的主要配置在 websocket 文件夹下;
3. 模板引擎采用freemaker 3. 模板引擎采用 freemaker
4. 项目以web的方式构建。 4. 项目以 web 的方式构建。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-websocket.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-boot-websocket.png"/> </div>
@ -101,9 +101,9 @@ public class ChatSocket {
public class WebSocketConfig { public class WebSocketConfig {
/*** /***
* 检测{@link javax.websocket.server.ServerEndpointConfig}和{@link ServerEndpoint} 类型的bean * 检测{@link javax.websocket.server.ServerEndpointConfig}和{@link ServerEndpoint} 类型的 bean
* 并在运行时使用标准Java WebSocket时注册。 * 并在运行时使用标准 Java WebSocket 时注册。
* 我们在{@link com.heibaiying.springboot.websocket.WebSocketConfig}中就是使用@ServerEndpoint去声明websocket服务 * 我们在{@link com.heibaiying.springboot.websocket.WebSocketConfig}中就是使用@ServerEndpoint 去声明 websocket 服务
*/ */
@Bean @Bean
public ServerEndpointExporter serverEndpointExporter() { public ServerEndpointExporter serverEndpointExporter() {
@ -147,7 +147,7 @@ public class WebSocketConfig {
} }
message.value = ""; message.value = "";
}); });
// 关闭页面时候关闭ws // 关闭页面时候关闭 ws
window.addEventListener("beforeunload", function (event) { window.addEventListener("beforeunload", function (event) {
ws.close(); ws.close();
}); });

View File

@ -1,5 +1,6 @@
# spring-boot-yml-profile # spring-boot-yml-profile
## 目录<br/>
<a href="#一项目结构">一、项目结构</a><br/> <a href="#一项目结构">一、项目结构</a><br/>
<a href="#二常用-yaml-语法讲解">二、常用 yaml 语法讲解</a><br/> <a href="#二常用-yaml-语法讲解">二、常用 yaml 语法讲解</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-基本规则">2.1 基本规则</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-基本规则">2.1 基本规则</a><br/>
@ -22,7 +23,7 @@
## 二、常用 yaml 语法讲解 ## 二、常用 yaml 语法讲解
项目中的 yml 配置文件如下:
```yaml ```yaml
programmer: programmer:
@ -45,7 +46,7 @@ programmer:
3. 缩进长度没有限制,只要元素对齐就表示这些元素属于一个层级。 3. 缩进长度没有限制,只要元素对齐就表示这些元素属于一个层级。
4. 使用#表示注释 4. 使用#表示注释
5. 字符串默认不用加单双引号,但单引号和双引号都可以使用,双引号不会对特殊字符转义。 5. 字符串默认不用加单双引号,但单引号和双引号都可以使用,双引号不会对特殊字符转义。
5. 字符串默认不用加单双引号,但单引号和双引号都可以使用,双引号不会对特殊字符转义 6. YAML 中提供了多种常量结构包括整数浮点数字符串NULL日期布尔时间
#### 2.2 对象的写法 #### 2.2 对象的写法
@ -82,16 +83,16 @@ key:
单引号和双引号都可以使用,双引号不会对特殊字符转义。 单引号和双引号都可以使用,双引号不会对特殊字符转义。
```yaml ```yaml
```yaml s1: '内容\n 字符串'
s1: '内容\n字符串' s2: "内容\n 字符串"
转换后: 转换后:
转换后: { s1: '内容\\n 字符串', s2: '内容\n 字符串' }
``` ```
#### 2.6 特殊符号 #### 2.6 特殊符号
--- YAML 可以在同一个文件中,使用---表示一个文档的开始。
@ -138,15 +139,15 @@ public class Programmer {
} }
``` ```
Spring Boot 将环境属性绑定到@ConfigurationProperties beans 时会使用一些宽松的规则,称之为松散绑定。所以 Environment 属性名和 bean 属性名不需要精确匹配。常见的示例中有用的包括虚线分割比如context-path 绑定到 contextPath将环境属性转为大写字母比如PORT 绑定 port
需要注意的是 `@Value` 是不支持松散绑定的,所以建议除非有特殊的需求,否则在 `ConfigurationProperties``value` 配置属性的时候最好都保持属性和变量的一致,以免造成不必要的勿扰。
## 五、多配置文件 ## 五、多配置文件
多配置文件可以在同一个 yml 中使用 --- 分割为多个配置,或者遵循 application-xxx.yml 的方式命名拆分为多个文件,并在主配置文件 application.yml 中确定激活哪个配置文件,当然也可在命令行中确定,命令行的优先级大于配置文件。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/profile.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/profile.png"/> </div>
@ -166,27 +167,27 @@ spring:
## 六、优先级的说明 ## 六、优先级的说明
Spring Boot 设计了一个非常特别的 PropertySource 顺序,以允许对属性值进行合理的覆盖,属性会以如下的顺序进行设值:
1. home 目录下的 devtools 全局设置属性(~/.spring-boot-devtools.properties如果 devtools 激活)。
1. home目录下的devtools全局设置属性~/.spring-boot-devtools.properties如果devtools激活 2. 测试用例上的@TestPropertySource 注解
2. 测试用例上的@TestPropertySource注解 3. 测试用例上的@SpringBootTest#properties 注解。
4. 命令行参数 4. 命令行参数
4. 命令行参数 5. 来自 SPRING_APPLICATION_JSON 的属性(环境变量或系统属性中内嵌的内联 JSON
5. 来自SPRING_APPLICATION_JSON的属性环境变量或系统属性中内嵌的内联JSON 6. ServletConfig 初始化参数
6. ServletConfig初始化参数。 7. ServletContext 初始化参数。
7. ServletContext初始化参数 8. 来自于 java:comp/env 的 JNDI 属性
8. 来自于java:comp/env的JNDI属性 9. Java 系统属性System.getProperties()
10. 操作系统环境变量。 10. 操作系统环境变量。
10. 操作系统环境变量 11. RandomValuePropertySource只包含 random.*中的属性
11. RandomValuePropertySource只包含random.*中的属性。 12. 没有打进 jar 包的 Profile-specific 应用属性application-{profile}.properties 和 YAML 变量)
12. 没有打进jar包的Profile-specific应用属性application-{profile}.propertiesYAML变量 13. 打进 jar 包中的 Profile-specific 应用属性application-{profile}.propertiesYAML 变量)
13. 打进jar包中的Profile-specific应用属性application-{profile}.propertiesYAML变量 14. 没有打进 jar 包的应用配置application.propertiesYAML 变量)。
14. 没有打进jar的应用配置application.propertiesYAML变量 15. 打进 jar 包中的应用配置application.propertiesYAML 变量)。
15. 打进jar包中的应用配置application.properties和YAML变量 16. @Configuration 类上的@PropertySource 注解
16. @Configuration类上的@PropertySource注解 17. 默认属性(使用 SpringApplication.setDefaultProperties 指定)
这里做一下说明,上文第 12,14 点没有打进 jar 包的文件指的是在启动时候通过 `spring.config.location` 参数指定的外部配置文件,外部配置文件的优先级应该是大于 jar 中的配置文件。
对上面的配置中常用的规则可以精简如下: 对上面的配置中常用的规则可以精简如下:

View File

@ -15,7 +15,7 @@
### 1.1 项目说明 ### 1.1 项目说明
本用例基于spring boot + druid + mybatis 配置多数据源,并采用 **JTA 实现分布式事务** 本用例基于 spring boot + druid + mybatis 配置多数据源,并采用 **JTA 实现分布式事务**
### 1.2 项目结构 ### 1.2 项目结构
@ -31,9 +31,9 @@
### 2.1 导入基本依赖 ### 2.1 导入基本依赖
除了mybatis 、durid 等依赖外我们依靠切面来实现动态数据源的切换所以还需要导入aop依赖。 除了 mybatis 、durid 等依赖外,我们依靠切面来实现动态数据源的切换,所以还需要导入 aop 依赖。
最主要的是还要导入spring-boot-starter-jta-atomikosSpring Boot通过[Atomkos](http://www.atomikos.com/)或[Bitronix](http://docs.codehaus.org/display/BTM/Home)的内嵌事务管理器支持跨多个XA资源的分布式JTA事务当发现JTA环境时Spring Boot将使用SpringJtaTransactionManager来管理事务。自动配置的JMSDataSourceJPA beans将被升级以支持XA事务。 最主要的是还要导入 spring-boot-starter-jta-atomikosSpring Boot 通过[Atomkos](http://www.atomikos.com/) 或[Bitronix](http://docs.codehaus.org/display/BTM/Home) 的内嵌事务管理器支持跨多个 XA 资源的分布式 JTA 事务,当发现 JTA 环境时Spring Boot 将使用 SpringJtaTransactionManager 来管理事务。自动配置的 JMSDataSourceJPA beans 将被升级以支持 XA 事务。
```xml ```xml
<!--mybatis starter--> <!--mybatis starter-->
@ -80,7 +80,7 @@
**注意**Spring Boot 2.X 版本不再支持配置继承,多数据源的话每个数据源的所有配置都需要单独配置,否则配置不会生效。 **注意**Spring Boot 2.X 版本不再支持配置继承,多数据源的话每个数据源的所有配置都需要单独配置,否则配置不会生效。
这里我用的本地数据库mysqlmysql02,配置如下: 这里我用的本地数据库 mysqlmysql02,配置如下:
```yml ```yml
spring: spring:
@ -92,29 +92,29 @@ spring:
password: root password: root
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
# 初始化时建立物理连接的个数。初始化发生在显示调用init方法或者第一次getConnection时 # 初始化时建立物理连接的个数。初始化发生在显示调用 init 方法,或者第一次 getConnection
initialSize: 5 initialSize: 5
# 最小连接池数量 # 最小连接池数量
minIdle: 5 minIdle: 5
# 最大连接池数量 # 最大连接池数量
maxActive: 10 maxActive: 10
# 获取连接时最大等待时间单位毫秒。配置了maxWait之后缺省启用公平锁并发效率会有所下降如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 # 获取连接时最大等待时间,单位毫秒。配置了 maxWait 之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 useUnfairLock 属性为 true 使用非公平锁。
maxWait: 60000 maxWait: 60000
# Destroy线程会检测连接的间隔时间如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 # Destroy 线程会检测连接的间隔时间,如果连接空闲时间大于等于 minEvictableIdleTimeMillis 则关闭物理连接。
timeBetweenEvictionRunsMillis: 60000 timeBetweenEvictionRunsMillis: 60000
# 连接保持空闲而不被驱逐的最小时间 # 连接保持空闲而不被驱逐的最小时间
minEvictableIdleTimeMillis: 300000 minEvictableIdleTimeMillis: 300000
# 用来检测连接是否有效的sql 因数据库方言而异, 例如 oracle 应该写成 SELECT 1 FROM DUAL # 用来检测连接是否有效的 sql 因数据库方言而异, 例如 oracle 应该写成 SELECT 1 FROM DUAL
validationQuery: SELECT 1 validationQuery: SELECT 1
# 建议配置为true不影响性能并且保证安全性。申请连接的时候检测如果空闲时间大于timeBetweenEvictionRunsMillis执行validationQuery检测连接是否有效。 # 建议配置为 true不影响性能并且保证安全性。申请连接的时候检测如果空闲时间大于 timeBetweenEvictionRunsMillis执行 validationQuery 检测连接是否有效。
testWhileIdle: true testWhileIdle: true
# 申请连接时执行validationQuery检测连接是否有效做了这个配置会降低性能。 # 申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
testOnBorrow: false testOnBorrow: false
# 归还连接时执行validationQuery检测连接是否有效做了这个配置会降低性能。 # 归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
testOnReturn: false testOnReturn: false
# 是否自动回收超时连接 # 是否自动回收超时连接
removeAbandoned: true removeAbandoned: true
# 超时时间(以秒数为单位) # 超时时间 (以秒数为单位)
remove-abandoned-timeout: 1800 remove-abandoned-timeout: 1800
db2: db2:
@ -123,45 +123,45 @@ spring:
password: root password: root
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
# 初始化时建立物理连接的个数。初始化发生在显示调用init方法或者第一次getConnection时 # 初始化时建立物理连接的个数。初始化发生在显示调用 init 方法,或者第一次 getConnection
initialSize: 6 initialSize: 6
# 最小连接池数量 # 最小连接池数量
minIdle: 6 minIdle: 6
# 最大连接池数量 # 最大连接池数量
maxActive: 10 maxActive: 10
# 获取连接时最大等待时间单位毫秒。配置了maxWait之后缺省启用公平锁并发效率会有所下降如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 # 获取连接时最大等待时间,单位毫秒。配置了 maxWait 之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 useUnfairLock 属性为 true 使用非公平锁。
maxWait: 60000 maxWait: 60000
# Destroy线程会检测连接的间隔时间如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 # Destroy 线程会检测连接的间隔时间,如果连接空闲时间大于等于 minEvictableIdleTimeMillis 则关闭物理连接。
timeBetweenEvictionRunsMillis: 60000 timeBetweenEvictionRunsMillis: 60000
# 连接保持空闲而不被驱逐的最小时间 # 连接保持空闲而不被驱逐的最小时间
minEvictableIdleTimeMillis: 300000 minEvictableIdleTimeMillis: 300000
# 用来检测连接是否有效的sql 因数据库方言而异, 例如 oracle 应该写成 SELECT 1 FROM DUAL # 用来检测连接是否有效的 sql 因数据库方言而异, 例如 oracle 应该写成 SELECT 1 FROM DUAL
validationQuery: SELECT 1 validationQuery: SELECT 1
# 建议配置为true不影响性能并且保证安全性。申请连接的时候检测如果空闲时间大于timeBetweenEvictionRunsMillis执行validationQuery检测连接是否有效。 # 建议配置为 true不影响性能并且保证安全性。申请连接的时候检测如果空闲时间大于 timeBetweenEvictionRunsMillis执行 validationQuery 检测连接是否有效。
testWhileIdle: true testWhileIdle: true
# 申请连接时执行validationQuery检测连接是否有效做了这个配置会降低性能。 # 申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
testOnBorrow: false testOnBorrow: false
# 归还连接时执行validationQuery检测连接是否有效做了这个配置会降低性能。 # 归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
testOnReturn: false testOnReturn: false
# 是否自动回收超时连接 # 是否自动回收超时连接
removeAbandoned: true removeAbandoned: true
# 超时时间(以秒数为单位) # 超时时间 (以秒数为单位)
remove-abandoned-timeout: 1800 remove-abandoned-timeout: 1800
# WebStatFilter用于采集web-jdbc关联监控的数据。 # WebStatFilter 用于采集 web-jdbc 关联监控的数据。
web-stat-filter: web-stat-filter:
# 是否开启 WebStatFilter 默认是true # 是否开启 WebStatFilter 默认是 true
enabled: true enabled: true
# 需要拦截的url # 需要拦截的 url
url-pattern: /* url-pattern: /*
# 排除静态资源的请求 # 排除静态资源的请求
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
# Druid内置提供了一个StatViewServlet用于展示Druid的统计信息。 # Druid 内置提供了一个 StatViewServlet 用于展示 Druid 的统计信息。
stat-view-servlet: stat-view-servlet:
#是否启用StatViewServlet 默认值true #是否启用 StatViewServlet 默认值 true
enabled: true enabled: true
# 需要拦截的url # 需要拦截的 url
url-pattern: /druid/* url-pattern: /druid/*
# 允许清空统计数据 # 允许清空统计数据
reset-enable: true reset-enable: true
@ -188,9 +188,9 @@ public class DruidMybatisMultiApplication {
#### 2. 创建多数据源配置类`DataSourceFactory.java`, 手动配置多数据源 #### 2. 创建多数据源配置类`DataSourceFactory.java`, 手动配置多数据源
+ 这里我们创建druid数据源的时候创建的是`DruidXADataSource`,它继承自`DruidDataSource `并支持XA分布式事务 + 这里我们创建 druid 数据源的时候,创建的是 `DruidXADataSource`,它继承自 `DruidDataSource ` 并支持 XA 分布式事务;
+ 使用 `AtomikosDataSourceBean` 包装我们创建的`DruidXADataSource`,使得数据源能够被 JTA 事务管理器管理; + 使用 `AtomikosDataSourceBean` 包装我们创建的 `DruidXADataSource`,使得数据源能够被 JTA 事务管理器管理;
+ 这里我们使用的sqlSessionTemplate是我们重写的`CustomSqlSessionTemplate`,原生的sqlSessionTemplate是不能实现在一个事务中实现数据源切换的。为了不占用篇幅我会在后文再给出详细的原因分析 + 这里我们使用的 sqlSessionTemplate 是我们重写的 `CustomSqlSessionTemplate`,原生的 sqlSessionTemplate 是不能实现在一个事务中实现数据源切换的。(为了不占用篇幅,我会在后文再给出详细的原因分析)
```java ```java
/** /**
@ -207,7 +207,7 @@ public class DataSourceFactory {
/*** /***
* 创建 DruidXADataSource 1 用@ConfigurationProperties自动配置属性 * 创建 DruidXADataSource 1 用@ConfigurationProperties 自动配置属性
*/ */
@Bean @Bean
@ConfigurationProperties("spring.datasource.druid.db1") @ConfigurationProperties("spring.datasource.druid.db1")
@ -225,7 +225,7 @@ public class DataSourceFactory {
} }
/** /**
* 创建支持XA事务的Atomikos数据源1 * 创建支持 XA 事务的 Atomikos 数据源 1
*/ */
@Bean @Bean
public DataSource dataSourceOne(DataSource druidDataSourceOne) { public DataSource dataSourceOne(DataSource druidDataSourceOne) {
@ -237,7 +237,7 @@ public class DataSourceFactory {
} }
/** /**
* 创建支持XA事务的Atomikos数据源2 * 创建支持 XA 事务的 Atomikos 数据源 2
*/ */
@Bean @Bean
public DataSource dataSourceTwo(DataSource druidDataSourceTwo) { public DataSource dataSourceTwo(DataSource druidDataSourceTwo) {
@ -249,8 +249,8 @@ public class DataSourceFactory {
/** /**
* @param dataSourceOne 数据源1 * @param dataSourceOne 数据源 1
* @return 数据源1的会话工厂 * @return 数据源 1 的会话工厂
*/ */
@Bean @Bean
public SqlSessionFactory sqlSessionFactoryOne(DataSource dataSourceOne) public SqlSessionFactory sqlSessionFactoryOne(DataSource dataSourceOne)
@ -260,8 +260,8 @@ public class DataSourceFactory {
/** /**
* @param dataSourceTwo 数据源2 * @param dataSourceTwo 数据源 2
* @return 数据源2的会话工厂 * @return 数据源 2 的会话工厂
*/ */
@Bean @Bean
public SqlSessionFactory sqlSessionFactoryTwo(DataSource dataSourceTwo) public SqlSessionFactory sqlSessionFactoryTwo(DataSource dataSourceTwo)
@ -271,10 +271,10 @@ public class DataSourceFactory {
/*** /***
* sqlSessionTemplateSpring事务管理一起使用以确保使用的实际SqlSession是与当前Spring事务关联的, * sqlSessionTemplateSpring 事务管理一起使用,以确保使用的实际 SqlSession 是与当前 Spring 事务关联的,
* 此外它还管理会话生命周期包括根据Spring事务配置根据需要关闭提交或回滚会话 * 此外它还管理会话生命周期,包括根据 Spring 事务配置根据需要关闭,提交或回滚会话
* @param sqlSessionFactoryOne 数据源1 * @param sqlSessionFactoryOne 数据源 1
* @param sqlSessionFactoryTwo 数据源2 * @param sqlSessionFactoryTwo 数据源 2
*/ */
@Bean @Bean
public CustomSqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactoryOne, public CustomSqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactoryOne,
@ -297,13 +297,13 @@ public class DataSourceFactory {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource); factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION)); factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
// 其他可配置项(包括是否打印sql,是否开启驼峰命名等) // 其他可配置项 (包括是否打印 sql,是否开启驼峰命名等)
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true); configuration.setMapUnderscoreToCamelCase(true);
configuration.setLogImpl(StdOutImpl.class); configuration.setLogImpl(StdOutImpl.class);
factoryBean.setConfiguration(configuration); factoryBean.setConfiguration(configuration);
/* * /* *
* 采用个如下方式配置属性的时候一定要保证已经进行数据源的配置(setDataSource)和数据源和MapperLocation配置(setMapperLocations) * 采用个如下方式配置属性的时候一定要保证已经进行数据源的配置 (setDataSource) 和数据源和 MapperLocation 配置 (setMapperLocations)
* factoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true); * factoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
* factoryBean.getObject().getConfiguration().setLogImpl(StdOutImpl.class); * factoryBean.getObject().getConfiguration().setLogImpl(StdOutImpl.class);
**/ **/
@ -314,7 +314,7 @@ public class DataSourceFactory {
#### 3. 自定义sqlSessionTemplate的主要实现逻辑 #### 3. 自定义sqlSessionTemplate的主要实现逻辑
这里主要覆盖重写了sqlSessionTemplategetSqlSessionFactory从ThreadLocal去获取实际使用的数据源AOP切面将实际使用的数据源存入ThreadLocal 这里主要覆盖重写了 sqlSessionTemplategetSqlSessionFactory ThreadLocal 去获取实际使用的数据源AOP 切面将实际使用的数据源存入 ThreadLocal
```java ```java
/*** /***
@ -340,12 +340,12 @@ public SqlSessionFactory getSqlSessionFactory() {
/** /**
* 这个方法的实现和父类的实现是基本一致的唯一不同的就是在getSqlSession方法传参中获取会话工厂的方式 * 这个方法的实现和父类的实现是基本一致的,唯一不同的就是在 getSqlSession 方法传参中获取会话工厂的方式
*/ */
private class SqlSessionInterceptor implements InvocationHandler { private class SqlSessionInterceptor implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在getSqlSession传参时候用我们重写的getSqlSessionFactory获取当前数据源对应的会话工厂 //在 getSqlSession 传参时候,用我们重写的 getSqlSessionFactory 获取当前数据源对应的会话工厂
final SqlSession sqlSession = getSqlSession( final SqlSession sqlSession = getSqlSession(
CustomSqlSessionTemplate.this.getSqlSessionFactory(), CustomSqlSessionTemplate.this.getSqlSessionFactory(),
CustomSqlSessionTemplate.this.executorType, CustomSqlSessionTemplate.this.executorType,
@ -375,7 +375,7 @@ private class SqlSessionInterceptor implements InvocationHandler {
#### 4. 使用AOP动态切换数据源将当前使用的数据源名称保存到线程隔离的ThreadLocal中 #### 4. 使用AOP动态切换数据源将当前使用的数据源名称保存到线程隔离的ThreadLocal中
这里我们直接对dao层接口进行切面如果第一个参数指明需要使用哪一个数据源就使用对应的数据源如果没有指定就使用默认的数据源。 这里我们直接对 dao 层接口进行切面,如果第一个参数指明需要使用哪一个数据源,就使用对应的数据源,如果没有指定,就使用默认的数据源。
注:使用切面来切换数据源是一种实现思路,而具体如何定义切入点可以按照自己的实际情况来定,你可以使用第一个参数指明数据源,也可以自定义注解来指定数据源,这个按照自己的实际使用方便来实现即可。 注:使用切面来切换数据源是一种实现思路,而具体如何定义切入点可以按照自己的实际情况来定,你可以使用第一个参数指明数据源,也可以自定义注解来指定数据源,这个按照自己的实际使用方便来实现即可。
@ -436,7 +436,7 @@ public class DataSourceContextHolder {
```java ```java
/** /**
* @author : heibaiying * @author : heibaiying
* @description : JTA事务配置 * @description : JTA 事务配置
*/ */
@Configuration @Configuration
@EnableTransactionManagement @EnableTransactionManagement
@ -474,9 +474,9 @@ public class XATransactionManagerConfig {
### 3.1 测试数据库整合结果 ### 3.1 测试数据库整合结果
这里我在mysqlmysql02的表中分别插入了一条数据 这里我在 mysqlmysql02 的表中分别插入了一条数据:
mysql数据库 mysql 数据库:
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/mysql01.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/mysql01.png"/> </div>
@ -494,7 +494,7 @@ mysql02 数据库:
### 3.2 测试单数据库事务 ### 3.2 测试单数据库事务
这里因为没有复杂的业务逻辑,我直接将@Transactional加载controller层实际中最好加到service层 这里因为没有复杂的业务逻辑,我直接将@Transactional 加载 controller 层,实际中最好加到 service
```java ```java
/** /**
@ -567,15 +567,15 @@ public class XATransactionController {
### 3.4 测试druid数据源是否整合成功 ### 3.4 测试druid数据源是否整合成功
访问 http://localhost:8080/druid/index.html 可以在数据源监控页面看到两个数据源已配置成功同时配置都与我们在yml配置文件中的一致。 访问 http://localhost:8080/druid/index.html ,可以在数据源监控页面看到两个数据源已配置成功,同时配置都与我们在 yml 配置文件中的一致。
数据源1 数据源 1
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/druid-mysql01.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/druid-mysql01.png"/> </div>
数据源2 数据源 2
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/durud-mysql02.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/durud-mysql02.png"/> </div>
@ -593,11 +593,11 @@ url 监控情况:
### 4.1 XA 与 JTA ### 4.1 XA 与 JTA
XA是由X/Open组织提出的分布式事务的规范。XA规范主要定义了(全局)事务管理器(Transaction Manager)(局部)资源管理器(Resource Manager)之间的接口。XA接口是双向的系统接口在事务管理器Transaction Manager以及一个或多个资源管理器Resource Manager之间形成通信桥梁。XA之所以需要引入事务管理器是因为在分布式系统中从理论上讲两台机器理论上无 法达到一致的状态,需要引入一个单点进行协调。 XA 是由 X/Open 组织提出的分布式事务的规范。XA 规范主要定义了 (全局) 事务管理器 (Transaction Manager)(局部) 资源管理器 (Resource Manager) 之间的接口。XA 接口是双向的系统接口在事务管理器Transaction Manager以及一个或多个资源管理器Resource Manager之间形成通信桥梁。XA 之所以需要引入事务管理器是因为,在分布式系统中,从理论上讲,两台机器理论上无 法达到一致的状态,需要引入一个单点进行协调。
事务管理器控制着全局事务,管理事务生命周期,并协调资源。资源管理器负责控制和管理实际资源(如数据库或 JMS队列 事务管理器控制着全局事务,管理事务生命周期,并协调资源。资源管理器负责控制和管理实际资源(如数据库或 JMS 队列)。
下图说明了事务管理器、资源管理器,与应用程序之间的关系。 下图说明了事务管理器、资源管理器,与应用程序之间的关系。
**而 JTA 就是 XA 规范在java语言上的实现。JTA 采用两阶段提交实现分布式事务。** **而 JTA 就是 XA 规范在 java 语言上的实现。JTA 采用两阶段提交实现分布式事务。**
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/XA.gif"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/XA.gif"/> </div>
@ -605,16 +605,16 @@ XA是由X/Open组织提出的分布式事务的规范。XA规范主要定义了(
### 4.2 两阶段提交 ### 4.2 两阶段提交
分布式事务必须满足传统事务的特性,即原子性,一致性,分离性和持久性。但是分布式事务处理过程中,某些节点(Server)可能发生故障,或 者由于网络发生故障而无法访问到某些节点。为了防止分布式系统部分失败时产生数据的不一致性。在分布式事务的控制中采用了两阶段提交协议Two- Phase Commit Protocol。即事务的提交分为两个阶段 分布式事务必须满足传统事务的特性,即原子性,一致性,分离性和持久性。但是分布式事务处理过程中,某些节点 (Server) 可能发生故障,或 者由于网络发生故障而无法访问到某些节点。为了防止分布式系统部分失败时产生数据的不一致性。在分布式事务的控制中采用了两阶段提交协议Two- Phase Commit Protocol。即事务的提交分为两个阶段
+ 预提交阶段(Pre-Commit Phase) + 预提交阶段 (Pre-Commit Phase)
+ 决策后阶段Post-Decision Phase + 决策后阶段Post-Decision Phase
两阶段提交用来协调参与一个更新中的多个服务器的活动,以防止分布式系统部分失败时产生数据的不一致性。例如,如果一个更新操作要求位于三个不同结点上的记录被改变,且其中只要有一个结点失败,另外两个结点必须检测到这个失败并取消它们所做的改变。为了支持两阶段提交,一个分布式更新事务中涉及到的服务器必须能够相互通信。一般来说一个服务器会被指定为"控制"或"提交"服务器并监控来自其它服务器的信息。 两阶段提交用来协调参与一个更新中的多个服务器的活动,以防止分布式系统部分失败时产生数据的不一致性。例如,如果一个更新操作要求位于三个不同结点上的记录被改变,且其中只要有一个结点失败,另外两个结点必须检测到这个失败并取消它们所做的改变。为了支持两阶段提交,一个分布式更新事务中涉及到的服务器必须能够相互通信。一般来说一个服务器会被指定为"控制"或"提交"服务器并监控来自其它服务器的信息。
在分布式更新期间,各服务器首先标志它们已经完成(但未提交)指定给它们的分布式事务的那一部分,并准备提交(以使它们的更新部分成为永久性的)。这是 两阶段提交的第一阶段。如果有一结点不能响应,那么控制服务器要指示其它结点撤消分布式事务的各个部分的影响。如果所有结点都回答准备好提交,控制服务器 则指示它们提交并等待它们的响应。等待确认信息阶段是第二阶段。在接收到可以提交指示后,每个服务器提交分布式事务中属于自己的那一部分,并给控制服务器 发回提交完成信息。 在分布式更新期间,各服务器首先标志它们已经完成(但未提交)指定给它们的分布式事务的那一部分,并准备提交(以使它们的更新部分成为永久性的)。这是 两阶段提交的第一阶段。如果有一结点不能响应,那么控制服务器要指示其它结点撤消分布式事务的各个部分的影响。如果所有结点都回答准备好提交,控制服务器 则指示它们提交并等待它们的响应。等待确认信息阶段是第二阶段。在接收到可以提交指示后,每个服务器提交分布式事务中属于自己的那一部分,并给控制服务器 发回提交完成信息。
在一个分布式事务中必须有一个场地的Server作为协调者(coordinator),它能向 其它场地的Server发出请求并对它们的回答作出响应由它来控制一个分布式事务的提交或撤消。该分布式事务中涉及到的其它场地的Server称为参与者(Participant)。 在一个分布式事务中,必须有一个场地的 Server 作为协调者 (coordinator),它能向 其它场地的 Server 发出请求,并对它们的回答作出响应,由它来控制一个分布式事务的提交或撤消。该分布式事务中涉及到的其它场地的 Server 称为参与者 (Participant)。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/commit.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/commit.png"/> </div>
@ -626,7 +626,7 @@ XA是由X/Open组织提出的分布式事务的规范。XA规范主要定义了(
(1) 协调者准备局部(即在本地)提交并在日志中写入"预提交"日志项,并包含有该事务的所有参与者的名字。 (1) 协调者准备局部(即在本地)提交并在日志中写入"预提交"日志项,并包含有该事务的所有参与者的名字。
(2) 协调者询问参与者能否提交该事务。一个参与者可能由于多种原因不能提交。例如该Server提供的约束条(Constraints)的延迟检查不符合 限制条件时不能提交参与者本身的Server进程或硬件发生故障不能提交或者协调者访问不到某参与者网络故障这时协调者都认为是收到了一个 否定的回答。 (2) 协调者询问参与者能否提交该事务。一个参与者可能由于多种原因不能提交。例如,该 Server 提供的约束条 (Constraints) 的延迟检查不符合 限制条件时,不能提交;参与者本身的 Server 进程或硬件发生故障,不能提交;或者协调者访问不到某参与者(网络故障),这时协调者都认为是收到了一个 否定的回答。
(3) 如果参与者能够提交,则在其本身的日志中写入"准备提交"日志项,该日志项立即写入硬盘,然后给协调者发回,已准备好提交"的回答。 (3) 如果参与者能够提交,则在其本身的日志中写入"准备提交"日志项,该日志项立即写入硬盘,然后给协调者发回,已准备好提交"的回答。
@ -652,19 +652,19 @@ XA是由X/Open组织提出的分布式事务的规范。XA规范主要定义了(
### 5.1 事务下多数据源无法切换 ### 5.1 事务下多数据源无法切换
这里是主要是对上文提到为什么不重写sqlSessionTemplate会导致在事务下数据源切换失败的补充我们先看看sqlSessionTemplate源码中关于该类的定义 这里是主要是对上文提到为什么不重写 sqlSessionTemplate 会导致在事务下数据源切换失败的补充,我们先看看 sqlSessionTemplate 源码中关于该类的定义:
> sqlSessionTemplateSpring事务管理一起使用以确保使用的实际SqlSession是与当前Spring事务关联的,此外它还管理会话生命周期包括根据Spring事务配置根据需要关闭提交或回滚会话 > sqlSessionTemplateSpring 事务管理一起使用,以确保使用的实际 SqlSession 是与当前 Spring 事务关联的,此外它还管理会话生命周期,包括根据 Spring 事务配置根据需要关闭,提交或回滚会话
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/sqlSessionTemplate.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/sqlSessionTemplate.png"/> </div>
这里最主要的是说明sqlSession是与当前是spring 事务是关联的。 这里最主要的是说明 sqlSession 是与当前是 spring 事务是关联的。
#### 1. sqlSession与事务关联导致问题 #### 1. sqlSession与事务关联导致问题
对于mybatis 来说是默认开启一级缓存的一级缓存是session级别的对于同一个session如果是相同的查询语句并且查询参数都相同第二次的查询就直接从一级缓存中获取。 对于 mybatis 来说,是默认开启一级缓存的,一级缓存是 session 级别的,对于同一个 session 如果是相同的查询语句并且查询参数都相同,第二次的查询就直接从一级缓存中获取。
这也就是说对于如下的情况由于sqlSession是与事务绑定的如果使用原生sqlSessionTemplate则第一次查询和第二次查询都是用的同一个sqlSession那么第二个查询数据库2的查询语句根本不会执行,会直接从一级缓存中获取查询结果。两次查询得到都是第一次查询的结果。 这也就是说,对于如下的情况,由于 sqlSession 是与事务绑定的,如果使用原生 sqlSessionTemplate则第一次查询和第二次查询都是用的同一个 sqlSession那么第二个查询数据库 2 的查询语句根本不会执行,会直接从一级缓存中获取查询结果。两次查询得到都是第一次查询的结果。
```java ```java
@GetMapping("ts/db/programmers") @GetMapping("ts/db/programmers")
@ -682,7 +682,7 @@ public List<Programmer> getAllProgrammers() {
先说一下为什么会出现连接的复用: 先说一下为什么会出现连接的复用:
我们可以在spring的源码中看到spring在通过`DataSourceUtils`类中去获取新的连接`doGetConnection`的时候,会通过`TransactionSynchronizationManager.getResource(dataSource)`方法去判断当前数据源是否有可用的连接,如果有就直接返回,如果没有就通过`fetchConnection`方法去获取。 我们可以在 spring 的源码中看到 spring 在通过 `DataSourceUtils` 类中去获取新的连接 `doGetConnection` 的时候,会通过 `TransactionSynchronizationManager.getResource(dataSource)` 方法去判断当前数据源是否有可用的连接,如果有就直接返回,如果没有就通过 `fetchConnection` 方法去获取。
```java ```java
public static Connection doGetConnection(DataSource dataSource) throws SQLException { public static Connection doGetConnection(DataSource dataSource) throws SQLException {
@ -734,37 +734,37 @@ public static Connection doGetConnection(DataSource dataSource) throws SQLExcept
} }
``` ```
这里主要的问题是`TransactionSynchronizationManager.getResource(dataSource)`dataSource参数是在哪里进行注入的这里可以沿着调用堆栈往上寻找可以看到是在这个参数是`SpringManagedTransaction`类中获取连接的时候传入的。 这里主要的问题是 `TransactionSynchronizationManager.getResource(dataSource)`dataSource 参数是在哪里进行注入的,这里可以沿着调用堆栈往上寻找,可以看到是在这个参数是 `SpringManagedTransaction` 类中获取连接的时候传入的。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/opneConnection.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/opneConnection.png"/> </div>
`SpringManagedTransaction`这类中的dataSource是如何得到赋值的这里可以进入这个类中查看只有在创建这个类的时候通过构造器为dataSource赋值那么是哪个方法创建了`SpringManagedTransaction`? `SpringManagedTransaction` 这类中的 dataSource 是如何得到赋值的,这里可以进入这个类中查看,只有在创建这个类的时候通过构造器为 dataSource 赋值,那么是哪个方法创建了 `SpringManagedTransaction`?
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/springManagerTransaction.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/springManagerTransaction.png"/> </div>
在构造器上打一个断点,沿着调用的堆栈往上寻找可以看到是`DefaultSqlSessionFactory`在创建`SpringManagedTransaction`中传入的,**这个数据源就是创建sqlSession`sqlSessionFactory`中数据源**。 在构造器上打一个断点,沿着调用的堆栈往上寻找可以看到是 `DefaultSqlSessionFactory` 在创建 `SpringManagedTransaction` 中传入的,**这个数据源就是创建 sqlSession`sqlSessionFactory` 中数据源**。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/DefaultSqlSessionFactory.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/DefaultSqlSessionFactory.png"/> </div>
**这里说明连接的复用是与我们创建sqlSession时候传入的sqlSessionFactory是否是同一个有关** **这里说明连接的复用是与我们创建 sqlSession 时候传入的 sqlSessionFactory 是否是同一个有关**
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/getsqlSession.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/getsqlSession.png"/> </div>
所以我们才重写了sqlSessionTemplate中的`getSqlSession`方法获取sqlSession时候传入正在使用的数据源对应的`sqlSessionFactory`,这样即便在同一个的事务中,由于传入的`sqlSessionFactory`中不同,就不会出现连接复用。 所以我们才重写了 sqlSessionTemplate 中的 `getSqlSession` 方法,获取 sqlSession 时候传入正在使用的数据源对应的 `sqlSessionFactory`,这样即便在同一个的事务中,由于传入的 `sqlSessionFactory` 中不同,就不会出现连接复用。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/customSqlSessionTemplate.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/customSqlSessionTemplate.png"/> </div>
关于mybati-spring 的更多事务处理机制,推荐阅读博客[mybatis-spring事务处理机制分析](https://my.oschina.net/fifadxj/blog/785621) 关于 mybati-spring 的更多事务处理机制,推荐阅读博客[mybatis-spring 事务处理机制分析](https://my.oschina.net/fifadxj/blog/785621)
### 5.2 出现org.apache.ibatis.binding.BindingExceptionInvalid bound statement (not found)异常 ### 5.2 出现org.apache.ibatis.binding.BindingExceptionInvalid bound statement (not found)异常
出现这个异常的的原因是在创建SqlSessionFactory的时候`setMapperLocations`配置好之前调用了`factoryBean.getObject()`方法 出现这个异常的的原因是在创建 SqlSessionFactory 的时候,在 `setMapperLocations` 配置好之前调用了 `factoryBean.getObject()` 方法
```java ```java
//一个错误的示范 //一个错误的示范
@ -779,7 +779,7 @@ private SqlSessionFactory createSqlSessionFactory(DataSource dataSource) throws
} }
``` ```
上面这段代码没有任何编译问题导致这个错误不容易发现但是在调用sql时候就会出现异常。原因是`factoryBean.getObject()`方法被调用时就已经创建了SqlSessionFactory并且SqlSessionFactory只会被创建一次。此时还没有指定sql 文件的位置导致mybatis无法将接口与xml中的sql语句进行绑定所以出现BindingExceptionInvalid 绑定异常。 上面这段代码没有任何编译问题,导致这个错误不容易发现,但是在调用 sql 时候就会出现异常。原因是 `factoryBean.getObject()` 方法被调用时就已经创建了 SqlSessionFactory并且 SqlSessionFactory 只会被创建一次。此时还没有指定 sql 文件的位置,导致 mybatis 无法将接口与 xml 中的 sql 语句进行绑定,所以出现 BindingExceptionInvalid 绑定异常。
```java ```java
@Override @Override
@ -792,7 +792,7 @@ private SqlSessionFactory createSqlSessionFactory(DataSource dataSource) throws
} }
``` ```
正常绑定的情况下我们是可以在sqlSessionFactory中查看到绑定好的查询接口 正常绑定的情况下,我们是可以在 sqlSessionFactory 中查看到绑定好的查询接口:
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/sqlSessionFactory.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/sqlSessionFactory.png"/> </div>

View File

@ -12,7 +12,7 @@
## 一、config 简介 ## 一、config 简介
spring cloud config 分为服务端和客户端服务端称为分布式配置中心集中管理配置文件客户端为各个业务单元它们从配置中心获取相关配置同时config 还实现了配置热更新,在服务不停机的情况下刷新配置。 spring cloud config 分为服务端和客户端,服务端称为分布式配置中心,集中管理配置文件,客户端为各个业务单元,它们从配置中心获取相关配置,同时 config 还实现了配置热更新,在服务不停机的情况下刷新配置。
@ -117,13 +117,13 @@ spring:
password: password:
# 指定拉取的配置文件的存放位置,配置文件最后存储的目录为 basedir + search-paths # 指定拉取的配置文件的存放位置,配置文件最后存储的目录为 basedir + search-paths
# 这个地方还需要注意的是,配置文件的仓库最好只放配置文件 # 这个地方还需要注意的是,配置文件的仓库最好只放配置文件
# 因为配置中心不仅会拉取search-paths下的文件还会把uri指定仓库中的全部文件拉取到basedir下 # 因为配置中心不仅会拉取 search-paths 下的文件,还会把 uri 指定仓库中的全部文件拉取到 basedir
basedir: D:\git-config basedir: D:\git-config
# 指定分支 # 指定分支
label: master label: master
``` ```
这里的git 仓库就是本用例的仓库,是公开的仓库,所以不用配置用户名和密码,配置文件如下 这里的 git 仓库就是本用例的仓库,是公开的仓库,所以不用配置用户名和密码,配置文件如下
- application.yml 为主配置; - application.yml 为主配置;
- application-dev.yml 为开发环境配置。 - application-dev.yml 为开发环境配置。
@ -136,19 +136,19 @@ spring:
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-application-dev.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-application-dev.png"/> </div>
这里需要注意的拉取配置的时候我们此时指定拉取的是dev配置application.yml实际 配置如下: 这里需要注意的拉取配置的时候,我们此时指定拉取的是 dev 配置application.yml 实际 配置如下:
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-dev.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-dev.png"/> </div>
这说明在用配置中心拉取配置的时候和我们在本地开发的时候是一致的配置是互补的即dev中的实际配置应该是主配置和dev配置的结合且遵循同名属性精确优先的原则。 这说明在用配置中心拉取配置的时候,和我们在本地开发的时候是一致的,配置是互补的,即 dev 中的实际配置应该是主配置和 dev 配置的结合,且遵循同名属性精确优先的原则。
#### 3.5 http请求地址和资源文件映射 #### 3.5 http请求地址和资源文件映射
在本用例中如果我们想要直接访问主配置,用以下路径 http://localhost:8020/application.yml 是不行的,会得到错误页面。如果想要访问主配置,可以用http://localhost:8020/application-X.yml其中可以是任意字符原因是 在本用例中如果我们想要直接访问主配置,用以下路径 http://localhost:8020/application.yml 是不行的,会得到错误页面。如果想要访问主配置,,可以用 http://localhost:8020/application-X.yml其中可以是任意字符原因是
请求地址和实际的配置文件应该遵循以下规则application为配置文件名profile 为环境label为分支如果不指定默认就是master分支 请求地址和实际的配置文件应该遵循以下规则application 为配置文件名profile 为环境label 为分支(如果不指定默认就是 master 分支)。
- /{application}/{profile}[/{label}] - /{application}/{profile}[/{label}]
- /{application}-{profile}.yml - /{application}-{profile}.yml
@ -212,9 +212,9 @@ spring:
#### 4.2 新建 `bootstrap.yml`配置文件,指定注册中心地址和配置中心服务名,并在启动类上开启自动注册@EnableDiscoveryClient #### 4.2 新建 `bootstrap.yml`配置文件,指定注册中心地址和配置中心服务名,并在启动类上开启自动注册@EnableDiscoveryClient
这里需要特别说明的是在之前的所有项目中我们采用的配置文件都是application.yml,但是这里**一定要采用bootstrap.yml**。 这里需要特别说明的是,在之前的所有项目中我们采用的配置文件都是 application.yml,但是这里**一定要采用 bootstrap.yml**。
假设我们的数据库配置是放在远程配置中心的那么我们应该先去远程配置中心拉取配置然后再去进行数据库的自动化配置反之如果我们先进行了数据库的自动化配置那么就会因为找不到url或驱动而抛出异常。 假设我们的数据库配置是放在远程配置中心的,那么我们应该先去远程配置中心拉取配置,然后再去进行数据库的自动化配置,反之如果我们先进行了数据库的自动化配置,那么就会因为找不到 url 或驱动而抛出异常。
- bootstrap.ymlbootstrap.properties用来程序引导时执行应用于更加早期配置信息读取bootstrap.yml 先于 application.yml 加载。 - bootstrap.ymlbootstrap.properties用来程序引导时执行应用于更加早期配置信息读取bootstrap.yml 先于 application.yml 加载。
@ -298,7 +298,7 @@ public class ConfigController {
#### 4.4 依次启动eureka,config-server,config-client ,访问 http://localhost:8030/programmer #### 4.4 依次启动eureka,config-server,config-client ,访问 http://localhost:8030/programmer
这里需要注意是在启动eurekaconfig-server要稍等一会在启动config-client这里是为了确保config-server已经将服务注册到eureka然后我们的config-client才能从eureka中获取配置中心的服务。 这里需要注意是在启动 eurekaconfig-server要稍等一会在启动 config-client这里是为了确保 config-server 已经将服务注册到 eureka然后我们的 config-client 才能从 eureka 中获取配置中心的服务。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-client-programmer.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-client-programmer.png"/> </div>
@ -317,7 +317,7 @@ Located environment: name=config-client, profiles=[dev], label=master, version=5
在微服务的架构中我们通常想要构建一个共同的消息主题被所有微服务实例所监听以便对所有微服务实例的管理和通知这就是消息总线spring cloud bus 就是消息总线的一种实现。 在微服务的架构中我们通常想要构建一个共同的消息主题被所有微服务实例所监听以便对所有微服务实例的管理和通知这就是消息总线spring cloud bus 就是消息总线的一种实现。
目前spring cloud bus 支持的消息中间件有 RabbitMQkafka, 我们下面的整合采用的是RrabbitMQ。 目前 spring cloud bus 支持的消息中间件有 RabbitMQkafka, 我们下面的整合采用的是 RrabbitMQ。
关于热更新只需要对配置客户端config-client做更改不需要对config-server做改动。 关于热更新只需要对配置客户端config-client做更改不需要对config-server做改动。
@ -356,10 +356,10 @@ spring:
bus: bus:
#开启总线 #开启总线
enabled: true enabled: true
# 打开ack跟踪的标志默认关闭 # 打开 ack 跟踪的标志(默认关闭)
trace: trace:
enabled: true enabled: true
# 使用bus实现热更新 # 使用 bus 实现热更新
rabbitmq: rabbitmq:
host: 127.0.0.1 host: 127.0.0.1
port: 5672 port: 5672
@ -408,7 +408,7 @@ public class Programmer{
#### 5.4 依次启动eurekaconfig-server, config-client 服务 #### 5.4 依次启动eurekaconfig-server, config-client 服务
在client服务端启动时候可以在控制台 看到bus 自动创建了交换机、队列等 client 服务端启动时候,可以在控制台 看到 bus 自动创建了交换机、队列等
``` ```
Created new connection: rabbitConnectionFactory#496c6d94:22/SimpleConnection@185d85d2 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 63713] Created new connection: rabbitConnectionFactory#496c6d94:22/SimpleConnection@185d85d2 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 63713]

View File

@ -19,9 +19,9 @@
## 一、项目结构 ## 一、项目结构
eureka-server为服务注册中心负责服务的管理 eureka-server 为服务注册中心,负责服务的管理;
eureka-client 为eureka客户端 eureka-client 为 eureka 客户端;
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-eureka-cluster.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-eureka-cluster.png"/> </div>
@ -103,7 +103,7 @@ eureka:
defaultZone: http://127.0.0.1:8010/eureka/,http://localhost:8020/eureka/ defaultZone: http://127.0.0.1:8010/eureka/,http://localhost:8020/eureka/
``` ```
需要注意的是Eureka互相注册要求各个Eureka实例的eureka.instance.hostname不同如果相同则会被Eureka标记为unavailable-replicas不可用副本 需要注意的是 Eureka 互相注册要求各个 Eureka 实例的 eureka.instance.hostname 不同,如果相同,则会被 Eureka 标记为 unavailable-replicas不可用副本
#### 2.3 启动类上增加注解@EnableEurekaServer激活eureka服务端自动配置 #### 2.3 启动类上增加注解@EnableEurekaServer激活eureka服务端自动配置
@ -170,22 +170,22 @@ public class EurekaClientApplication {
### 4.2 高可用集群搭建成功的判定 ### 4.2 高可用集群搭建成功的判定
这里需要主要的是仅仅status中出现其他注册中心时并不一定是搭建成功的**一定是当注册中心的DS Replicas 和 available replicas中显示其余的注册中心时候**,才代表搭建成功。 这里需要主要的是仅仅 status 中出现其他注册中心时,并不一定是搭建成功的,**一定是当注册中心的 DS Replicas 和 available replicas 中显示其余的注册中心时候**,才代表搭建成功。
#### 4.2.1 点击下面注册中心的可用实例列表中的地址,访问链接分以下几个情况: #### 4.2.1 点击下面注册中心的可用实例列表中的地址,访问链接分以下几个情况:
1. hostnameprefer-ip-address都没有配置则访问 主机名:服务名:端口号, 1. hostnameprefer-ip-address 都没有配置,则访问 主机名:服务名:端口号,
``` ```
http://desktop-8jgsflj:8761/info http://desktop-8jgsflj:8761/info
``` ```
2. 配置了hostname而没有配置prefer-ip-address则访问 hostname:服务名:端口号, 2. 配置了 hostname 而没有配置 prefer-ip-address则访问 hostname:服务名:端口号,
``` ```
http://server:8761/info http://server:8761/info
``` ```
3. 如果配置了prefer-ip-address则访问 ipAddress:服务名:端口号, 3. 如果配置了 prefer-ip-address则访问 ipAddress:服务名:端口号,
``` ```
http://192.168.200.228:8761/info http://192.168.200.228:8761/info
``` ```
@ -203,10 +203,10 @@ public class EurekaClientApplication {
### 4.3 prefer-ip-address 参数说明 ### 4.3 prefer-ip-address 参数说明
在有的配置示例中配置了prefer-ip-addresstrue。 在有的配置示例中,配置了 prefer-ip-addresstrue。
```properties ```properties
eureka.instance.prefer-ip-address=true eureka.instance.prefer-ip-address=true
``` ```
在多机器独立部署的情况下是没有问题的配置prefer-ip-addressture代表发现服务时候优先按照ip去搜寻对于多集群而言可以保证尽快准确搜索到服务。而对于单机部署来说ip地址都是相同的这会导致其余注册中心出现在unavailable-replicas(不可用副本)中。所以单机部署时候不建议开启这个参数默认值为false多机部署时候可以开启。 在多机器独立部署的情况下是没有问题的,配置 prefer-ip-addressture代表发现服务时候优先按照 ip 去搜寻对于多集群而言可以保证尽快准确搜索到服务。而对于单机部署来说ip 地址都是相同的,这会导致其余注册中心出现在 unavailable-replicas(不可用副本) 中。所以单机部署时候不建议开启这个参数(默认值为 false多机部署时候可以开启。

View File

@ -1,5 +1,6 @@
# eureka 服务的注册与发现 # eureka 服务的注册与发现
## 目录<br/>
<a href="#一eureka-简介">一、eureka 简介</a><br/> <a href="#一eureka-简介">一、eureka 简介</a><br/>
<a href="#二项目结构">二、项目结构</a><br/> <a href="#二项目结构">二、项目结构</a><br/>
<a href="#三三步搭建eureka-服务注册中心">三、三步搭建eureka 服务注册中心</a><br/> <a href="#三三步搭建eureka-服务注册中心">三、三步搭建eureka 服务注册中心</a><br/>
@ -17,19 +18,19 @@
## 一、eureka 简介 ## 一、eureka 简介
Spring Cloud Eureka 使用 Netflix Eureka 来实现服务注册与发现,它既包含了服务端组件,也包含了客户端组件。
**Eureka 服务端**:服务的注册中心,负责维护注册的服务列表。
**Eureka 客户端** 在应用程序运行时Eureka 客户端向注册中心注册自身提供的服务,并周期性地发送心跳来更新它的服务租约。同时它也能把从服务端查询到服务信息缓存到本地,并周期性地刷新服务状态。
## 二、项目结构 ## 二、项目结构
eureka-server 为服务注册中心,负责服务的管理;
eureka-client 为 eureka 客户端;
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-eureka.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-eureka.png"/> </div>
@ -55,9 +56,9 @@ eureka:
instance: instance:
hostname: localhost hostname: localhost
client: client:
client: # 设置为 false,代表不向注册中心注册自己
register-with-eureka: false register-with-eureka: false
register-with-eureka: false # 注册中心主要用于维护服务,并不需要检索服务,所以设置为 false
fetch-registry: false fetch-registry: false
serviceUrl: serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

View File

@ -12,14 +12,14 @@
## 一、feign 简介 ## 一、feign 简介
在上一个用例中我们使用ribbon+restTemplate 实现服务之间的远程调用实际上每一个调用都是模板化的内容所以spring cloud Feign 在此基础上进行了进一步的封装。我们只需要定义一个接口并使用feign注解的方式来进行配置同时采用springMvc 注解进行参数绑定就可以完成服务的调用。feign同时还内置实现了负载均衡、服务容错等功能。 在上一个用例中,我们使用 ribbon+restTemplate 实现服务之间的远程调用,实际上每一个调用都是模板化的内容,所以 spring cloud Feign 在此基础上进行了进一步的封装。我们只需要定义一个接口并使用 feign 注解的方式来进行配置,同时采用 springMvc 注解进行参数绑定就可以完成服务的调用。feign 同时还内置实现了负载均衡、服务容错等功能。
## 二、项目结构 ## 二、项目结构
+ common: 公共的接口和实体类; + common: 公共的接口和实体类;
+ consumer: 服务的消费者采用feign调用产品服务 + consumer: 服务的消费者,采用 feign 调用产品服务;
+ producer服务的提供者 + producer服务的提供者
+ eureka: 注册中心。 + eureka: 注册中心。
@ -186,7 +186,7 @@ public interface ProductFeign {
List<Product> productList(); List<Product> productList();
/** /**
* 这是需要强调的是使用feign时候@PathVariable一定要用value指明参数 * 这是需要强调的是使用 feign 时候@PathVariable 一定要用 value 指明参数,
* 不然会抛出.IllegalStateException: PathVariable annotation was empty on param 异常 * 不然会抛出.IllegalStateException: PathVariable annotation was empty on param 异常
*/ */
@GetMapping("product/{id}") @GetMapping("product/{id}")
@ -259,7 +259,7 @@ public class SellController {
#### 5.1 启动一个Eureka服务、三个producer服务注意区分端口、和一个消费者服务 #### 5.1 启动一个Eureka服务、三个producer服务注意区分端口、和一个消费者服务
feign 的依赖中导入了spring-cloud-starter-netflix-ribbon依赖并且在内部实现了基于ribbon的客户端负载均衡所以我们这里启动三个producer实例来观察负载均衡的情况。 feign 的依赖中导入了 spring-cloud-starter-netflix-ribbon 依赖,并且在内部实现了基于 ribbon 的客户端负载均衡,所以我们这里启动三个 producer 实例来观察负载均衡的情况。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-app.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-app.png"/> </div>
@ -292,7 +292,7 @@ feign 的依赖中导入了spring-cloud-starter-netflix-ribbon依赖并且在
```yml ```yml
feign: feign:
hystrix: hystrix:
# 如果为true则OpenFign客户端将使用Hystrix断路器进行封装 默认为false # 如果为 true OpenFign 客户端将使用 Hystrix 断路器进行封装 默认为 false
enabled: true enabled: true
``` ```
@ -301,7 +301,7 @@ feign:
```java ```java
/** /**
* @author : heibaiying * @author : heibaiying
* @description : 定义发生熔断时候的回退处理。除了继承自CProductFeign,还需要用@Component声明为spring的组件 * @description : 定义发生熔断时候的回退处理。除了继承自 CProductFeign,还需要用@Component 声明为 spring 的组件
*/ */
@Component @Component
public class CProductFeignImpl implements CProductFeign { public class CProductFeignImpl implements CProductFeign {
@ -368,7 +368,7 @@ public interface CProductFeign extends ProductFeign {
#### 6.5 测试熔断处理 #### 6.5 测试熔断处理
hystrix 默认调用超时时间为2s ,这里我们使用线程休眠的方式来模拟超时熔断。 hystrix 默认调用超时时间为 2s ,这里我们使用线程休眠的方式来模拟超时熔断。
```java ```java
public List<Product> queryAllProducts() { public List<Product> queryAllProducts() {

View File

@ -19,7 +19,7 @@
在分布式系统中,由于服务之间相互的依赖调用,如果一个服务单元发生了故障就有可能导致故障蔓延至整个系统,从而衍生出一系列的保护机制,断路器就是其中之一。 在分布式系统中,由于服务之间相互的依赖调用,如果一个服务单元发生了故障就有可能导致故障蔓延至整个系统,从而衍生出一系列的保护机制,断路器就是其中之一。
断路器可以在服务单元发生故障的时候及时切断与服务单元的连接避免资源被长时间占用。spring cloud hystrix组件实现了断路器、线程隔离等一系列基本功能并具有服务降级、服务熔断、请求缓存、请求合并以及服务监控等配套功能。 断路器可以在服务单元发生故障的时候及时切断与服务单元的连接避免资源被长时间占用。spring cloud hystrix 组件实现了断路器、线程隔离等一系列基本功能,并具有服务降级、服务熔断、请求缓存、请求合并以及服务监控等配套功能。
@ -33,10 +33,10 @@
## 二、项目结构 ## 二、项目结构
[spring-cloud-ribbon](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-ribbon)用例已经实现通过ribbon+restTemplate实现服务间的调用本用例在其基础上进行hystrix 的整合。 [spring-cloud-ribbon](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-ribbon) 用例已经实现通过 ribbon+restTemplate 实现服务间的调用,本用例在其基础上进行 hystrix 的整合。
+ common: 公共的接口和实体类; + common: 公共的接口和实体类;
+ consumer: 服务的消费者采用RestTemplate调用产品服务 + consumer: 服务的消费者,采用 RestTemplate 调用产品服务;
+ producer服务的提供者 + producer服务的提供者
+ eureka: 注册中心; + eureka: 注册中心;
+ turbine:多个熔断器的聚合监控。 + turbine:多个熔断器的聚合监控。
@ -49,7 +49,7 @@
#### 3.1 引入依赖 #### 3.1 引入依赖
hystrix的仪表盘功能实际上是从[端点](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-actuator)获取数据所以需要actuator starter开启端点的相关功能。 hystrix 的仪表盘功能实际上是从[端点](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-actuator) 获取数据,所以需要 actuator starter 开启端点的相关功能。
```xml ```xml
<!--hystrix 依赖--> <!--hystrix 依赖-->
@ -76,7 +76,7 @@ management:
endpoints: endpoints:
web: web:
exposure: exposure:
# 需要开启hystrix.stream端点的暴露 这样才能获取到监控信息 * 代表开启所有可监控端点 # 需要开启 hystrix.stream 端点的暴露 这样才能获取到监控信息 * 代表开启所有可监控端点
include: "*" include: "*"
``` ```
@ -140,11 +140,11 @@ public List<Product> queryProductsFail() {
#### 3.5 模拟熔断 #### 3.5 模拟熔断
这里被调用方采用线程休眠的方式模拟服务超时Hystrix默认超时时间为2s,调用远程服务时候超过这个时间,会触发熔断。 这里被调用方采用线程休眠的方式模拟服务超时Hystrix 默认超时时间为 2s,调用远程服务时候超过这个时间,会触发熔断。
```java ```java
public List<Product> queryAllProducts() { public List<Product> queryAllProducts() {
// hystrix 默认超时是2 // hystrix 默认超时是 2
int i = new Random().nextInt(2500); int i = new Random().nextInt(2500);
try { try {
Thread.sleep(i); Thread.sleep(i);
@ -155,15 +155,15 @@ public List<Product> queryAllProducts() {
} }
``` ```
3.5 启动服务访问http://localhost:8030/sell/products ,多次刷新查看熔断情况 3.5 启动服务,访问 http://localhost:8030/sell/products ,多次刷新查看熔断情况
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-8030.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-8030.png"/> </div>
#### 3.5 启动服务,访问 localhost:8030/hystrix #### 3.5 启动服务,访问 localhost:8030/hystrix
依次输出http://localhost:8030/actuator/hystrix.stream监控地址 2000延迟时间title可以任意填写进入监控台。 依次输出 http://localhost:8030/actuator/hystrix.stream监控地址 2000延迟时间title 可以任意填写,进入监控台。
需要注意的是在spring cloud Finchley.SR2监控地址中都是有/actuator的因为在spring boot 2.x 的所有端点(包括自定义端点)都是暴露在这个路径下,在启动时候的控制台输出的日志可以查看到所有暴露端点的映射。 需要注意的是在 spring cloud Finchley.SR2监控地址中都是有/actuator 的,因为在 spring boot 2.x 的所有端点(包括自定义端点)都是暴露在这个路径下,在启动时候的控制台输出的日志可以查看到所有暴露端点的映射。
**登录页面** **登录页面**
@ -173,7 +173,7 @@ public List<Product> queryAllProducts() {
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-8030-login.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-8030-login.png"/> </div>
**关于各个参数的说明参见[官方wiki](https://github.com/Netflix-Skunkworks/hystrix-dashboard/wiki)提供的图** **关于各个参数的说明参见[官方 wiki](https://github.com/Netflix-Skunkworks/hystrix-dashboard/wiki) 提供的图**
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dashboard.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dashboard.png"/> </div>
@ -290,7 +290,7 @@ public class TurbineApplication {
#### 4.4 依次启动eureka、producer、consumer、turbine四个项目 #### 4.4 依次启动eureka、producer、consumer、turbine四个项目
在 localhost:8030/hystrix或者localhost:8030/hystrixconsumerproducer都集成了hystrix 页面输入http://localhost:8040/turbine.stream查看断路器聚合信息 在 localhost:8030/hystrix 或者 localhost:8030/hystrixconsumerproducer 都集成了 hystrix 页面输入 http://localhost:8040/turbine.stream查看断路器聚合信息
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-cluster-login.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-cluster-login.png"/> </div>
@ -302,7 +302,7 @@ public class TurbineApplication {
#### 5.1 无法访问监控页面 #### 5.1 无法访问监控页面
1. 一般是端点链接输入不对,在F版本的spring cloud 中,输入监控的端点链接是 http://localhost:8030/actuator/hystrix.stream ,中间是有/actuator/(之前版本的没有/actuator/ 1. 一般是端点链接输入不对,在 F 版本的 spring cloud 中,输入监控的端点链接是 http://localhost:8030/actuator/hystrix.stream ,中间是有/actuator/(之前版本的没有/actuator/
2. 没有暴露端点链接,暴露端点链接有两种方式,一种是我们在上文中提到的基于配置的方式 2. 没有暴露端点链接,暴露端点链接有两种方式,一种是我们在上文中提到的基于配置的方式
@ -311,7 +311,7 @@ public class TurbineApplication {
endpoints: endpoints:
web: web:
exposure: exposure:
# 需要开启hystrix.stream端点的暴露 这样才能获取到监控信息 * 代表开启所有可监控端点 # 需要开启 hystrix.stream 端点的暴露 这样才能获取到监控信息 * 代表开启所有可监控端点
include: "*" include: "*"
``` ```
@ -329,7 +329,7 @@ public class TurbineApplication {
} }
``` ```
这两种方式二选一即可,就算是采用代码的方式,还是建议将地址设置为/actuator/hystrix.stream而不是原来的hystrix.stream因为turbine默认也是从/actuator/hystrix.stream去获取信息。 这两种方式二选一即可,就算是采用代码的方式,还是建议将地址设置为/actuator/hystrix.stream而不是原来的 hystrix.stream因为 turbine 默认也是从/actuator/hystrix.stream 去获取信息。
#### 5.2 页面一直loading 或者访问端点页面一直出现ping #### 5.2 页面一直loading 或者访问端点页面一直出现ping

View File

@ -18,16 +18,16 @@
## 一、ribbon 简介 ## 一、ribbon 简介
ribbonNetfix公司开源的负载均衡组件采用服务端负载均衡的方式即消费者客户端维护可用的服务列表并通过负载均衡的方式将请求按照指定的策略分摊给消费者从而达到负载均衡的方式。 ribbonNetfix 公司开源的负载均衡组件,采用服务端负载均衡的方式,即消费者客户端维护可用的服务列表,并通过负载均衡的方式将请求按照指定的策略分摊给消费者,从而达到负载均衡的方式。
## 二、项目结构 ## 二、项目结构
+ common: 公共的接口和实体类; + common: 公共的接口和实体类;
+ consumer: 服务的消费者采用RestTemplate调用产品服务 + consumer: 服务的消费者,采用 RestTemplate 调用产品服务;
+ producer服务的提供者 + producer服务的提供者
+ eureka: 注册中心ribbon 从注册中心获取可用的服务列表,是实现负载均衡的基础。这里使用我们在[服务的注册与发现](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-eureka)这个用例中搭建的简单注册中心作为测试即可。 + eureka: 注册中心ribbon 从注册中心获取可用的服务列表,是实现负载均衡的基础。这里使用我们在[服务的注册与发现](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-eureka) 这个用例中搭建的简单注册中心作为测试即可。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon.png"/> </div>
@ -200,7 +200,7 @@ public class RibbonConfig {
#### 4.4 使用RestTemplate调用远程服务 #### 4.4 使用RestTemplate调用远程服务
这里我们在调用远程服务的时候url填写的是服务名+具体接口地址 ,由于我们的同一个服务会存在多个实例,在使用@LoadBalanced配置RestTemplate调用服务时客户端就会从按照指定的负载均衡的方式将请求分摊到多个实例上。默认的负载均衡采用的是RoundRobinRule轮询的策略有特殊需求时候可以采用其他内置的策略规则或者实现IRule来定义自己的负载均衡策略 这里我们在调用远程服务的时候url 填写的是服务名 + 具体接口地址 ,由于我们的同一个服务会存在多个实例,在使用@LoadBalanced 配置 RestTemplate 调用服务时,客户端就会从按照指定的负载均衡的方式将请求分摊到多个实例上。(默认的负载均衡采用的是 RoundRobinRule轮询的策略有特殊需求时候可以采用其他内置的策略规则或者实现 IRule 来定义自己的负载均衡策略)。
```java ```java
@Service @Service
@ -250,18 +250,18 @@ public class ProductService implements IProductService {
#### 6.1 restTemplate 规范 #### 6.1 restTemplate 规范
restTemplate 调用对应resultful接口时候使用的方法应该与接口声明方式@GetMapping@PostMapping@PutMapping@DeleteMapping)保持一致。请求类型与对应的调用方法如下。 restTemplate 调用对应 resultful 接口时候,使用的方法应该与接口声明方式(@GetMapping@PostMapping@PutMapping@DeleteMapping)保持一致。请求类型与对应的调用方法如下。
- GET请求(getForObject 、getForEntity) - GET 请求 (getForObject 、getForEntity)
- POST请求postForObject 、postForEntity - POST 请求postForObject 、postForEntity
- PUT请求put - PUT 请求put
- DELETE请求 delete - DELETE 请求 delete
#### 6.2 ForEntity()和ForObject()的区别 #### 6.2 ForEntity()和ForObject()的区别
- `ForEntity()`发送一个请求返回的ResponseEntity包含了响应体所映射成的对象 - `ForEntity()` 发送一个请求,返回的 ResponseEntity 包含了响应体所映射成的对象
- `ForObject()`发送一个请求,返回的请求体将映射为一个对象 - `ForObject()` 发送一个请求,返回的请求体将映射为一个对象
例如: 例如:
@ -274,19 +274,19 @@ Product product = restTemplate.getForObject("http://producer/product/{1}", Produ
## 七、 附2 关于ribbon更多负载均衡的策略 ## 七、 附2 关于ribbon更多负载均衡的策略
Ribbon内置了多种负载均衡策略如果有更复杂的需求可以自己实现IRule。 Ribbon 内置了多种负载均衡策略,如果有更复杂的需求,可以自己实现 IRule。
#### 7.1 内置的负载均衡的策略如下图 #### 7.1 内置的负载均衡的策略如下图
![Ribbon负载均衡策略.png](http://upload-images.jianshu.io/upload_images/6944619-0355d316f5df9b3f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ![Ribbon 负载均衡策略.png](http://upload-images.jianshu.io/upload_images/6944619-0355d316f5df9b3f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
图片来源于博客:[Ribbon负载均衡策略与自定义配置](https://blog.csdn.net/jrn1012/article/details/77837680) 图片来源于博客:[Ribbon 负载均衡策略与自定义配置](https://blog.csdn.net/jrn1012/article/details/77837680)
#### 7.2 配置文件指定负载均衡的方式 #### 7.2 配置文件指定负载均衡的方式
要设置`IRule`名为的服务名称`users`,您可以设置以下属性: 要设置 `IRule` 名为的服务名称 `users`,您可以设置以下属性:
```yaml ```yaml
users: users:
@ -316,10 +316,10 @@ public class CustomConfiguration {
} }
``` ```
在使用代码方式的时候需要注意 [官方文档](http://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html#_customizing_the_ribbon_client)中关于注解方式有以下强调: 在使用代码方式的时候需要注意 [官方文档](http://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html#_customizing_the_ribbon_client) 中关于注解方式有以下强调:
The CustomConfiguration clas must be a @Configuration class, but take care that it is not in a @ComponentScan for the main application context. Otherwise, it is shared by all the @RibbonClients. If you use @ComponentScan (or @SpringBootApplication), you need to take steps to avoid it being included (for instance, you can put it in a separate, non-overlapping package or specify the packages to scan explicitly in the @ComponentScan). The CustomConfiguration clas must be a @Configuration class, but take care that it is not in a @ComponentScan for the main application context. Otherwise, it is shared by all the @RibbonClients. If you use @ComponentScan (or @SpringBootApplication), you need to take steps to avoid it being included (for instance, you can put it in a separate, non-overlapping package or specify the packages to scan explicitly in the @ComponentScan).
`CustomConfiguration`类必须是`@Configuration`标注的,但需要注意的它不是在`@ComponentScan`主应用程序上下文。否则,它将由所有`@RibbonClients`共享。如果你使用`@ComponentScan`(或`@SpringBootApplication`),你需要采取一些措施来避免它被扫描到(例如,你可以把它放在一个独立的,非重叠的包,或用`@ComponentScan`时显示扫描指定的包)。 `CustomConfiguration` 类必须是 `@Configuration` 标注的,但需要注意的它不是在 `@ComponentScan` 主应用程序上下文。否则,它将由所有 `@RibbonClients` 共享。如果你使用 `@ComponentScan`(或 `@SpringBootApplication`),你需要采取一些措施来避免它被扫描到(例如,你可以把它放在一个独立的,非重叠的包,或用 `@ComponentScan` 时显示扫描指定的包)。

View File

@ -1,5 +1,6 @@
# spring-sleuth-zipkin # spring-sleuth-zipkin
## 目录<br/>
<a href="#一简介">一、简介</a><br/> <a href="#一简介">一、简介</a><br/>
<a href="#二项目结构">二、项目结构</a><br/> <a href="#二项目结构">二、项目结构</a><br/>
<a href="#三构建-zipkin-服务端">三、构建 zipkin 服务端</a><br/> <a href="#三构建-zipkin-服务端">三、构建 zipkin 服务端</a><br/>
@ -14,31 +15,31 @@
在微服务架构中,几乎每一个前端的请求都会经过多个服务单元协调来提供服务,形成复杂的服务调用链路。当服务发生问题时候,很难知道问题来源于链路的哪一个环节,这时候就需要进行链路追踪。 在微服务架构中,几乎每一个前端的请求都会经过多个服务单元协调来提供服务,形成复杂的服务调用链路。当服务发生问题时候,很难知道问题来源于链路的哪一个环节,这时候就需要进行链路追踪。
zipkin 是一个开源的分布式跟踪系统,可以使用 spring cloud sleuth 来轻松的集成 zipkin。
## 二、项目结构 ## 二、项目结构
这里的项目是在之前的 [spring-cloud-zuul](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-zuul) 进行集成zuul 项目的产品接口调用链路从 网关 -> consumer -> producer,历经三个环节的调用链路可以直观展示 zipkin 对链路追踪可视化的好处。
+ common: 公共的接口和实体类; + common: 公共的接口和实体类;
+ common: 公共的接口和实体类 + consumer: 服务的消费者,采用 feign 调用产品服务
+ producer服务的提供者 + producer服务的提供者
+ eureka: 注册中心; + eureka: 注册中心;
+ eureka: 注册中心; + zuul: api 网关。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-sleuth-zipkin.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-sleuth-zipkin.png"/> </div>
## 三、构建 zipkin 服务端 ## 三、构建 zipkin 服务端
zipkin 客户端可以不用自己构建,直接从[官网](https://zipkin.io/pages/quickstart) 上下载对应的 jar 包启动即可,默认端口 9411
```shell ```shell
java -jar zipkin.jar java -jar zipkin.jar
``` ```
可以直接从 docker 仓库拉取,然后启动容器:
```shell ```shell
docker run -d -p 9411:9411 openzipkin/zipkin docker run -d -p 9411:9411 openzipkin/zipkin
@ -52,7 +53,7 @@ docker run -d -p 9411:9411 openzipkin/zipkin
## 四、集成zipkin ## 四、集成zipkin
这里我们对 zuul、consumer、producer 三个模块都进行集成
#### 4.1 对三个模块(zuul、consumer、producer )添加依赖 #### 4.1 对三个模块(zuul、consumer、producer )添加依赖
@ -98,6 +99,6 @@ SpanId ; 工作单元 (Span) 的唯一标识。
TraceId : 一条请求链路 (Trace) 的唯 一 标识。 TraceId : 一条请求链路 (Trace) 的唯 一 标识。
除了 TraceID 外,还需要 SpanID 用于记录调用父子关系。每个服务会记录下 parent id 和 span id通过他们可以组织一次完整调用链的父子关系。
注:关于以上概念可以比对链表的实现原理来理解。 注:关于以上概念可以比对链表的实现原理来理解。

View File

@ -19,25 +19,25 @@
api 网关是整个微服务系统的门面,所有的外部访问需要通过网关进行调度和过滤。它实现了请求转发、负载均衡、校验过滤、错误熔断、服务聚合等功能。 api 网关是整个微服务系统的门面,所有的外部访问需要通过网关进行调度和过滤。它实现了请求转发、负载均衡、校验过滤、错误熔断、服务聚合等功能。
下图是直观的显示api Gateway 在微服务网关中的作用图片引用自spring boot 官网)。 下图是直观的显示 api Gateway 在微服务网关中的作用(图片引用自 spring boot 官网)。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/apiGateway.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/apiGateway.png"/> </div>
### 1.2 zuul ### 1.2 zuul
spring cloud 中提供了基础Net flix Zuul 实现的网关组件这就是Zuul,它除了实现负载均衡、错误熔断、路由转发等功能还能与spring 其他组件无缝配合使用。 spring cloud 中提供了基础 Net flix Zuul 实现的网关组件,这就是 Zuul,它除了实现负载均衡、错误熔断、路由转发等功能,还能与 spring 其他组件无缝配合使用。
## 二、项目结构 ## 二、项目结构
[spring-cloud-feign](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-feign)用例已经实现通过feign实现服务间的调用且提供了两个业务服务单元(consumer、producer)可以方便直观的测试zuul的路由、负载均衡、和错误熔断等功能所以本用例在其基础上进行zuul的整合。 [spring-cloud-feign](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-feign) 用例已经实现通过 feign 实现服务间的调用,且提供了两个业务服务单元 (consumer、producer),可以方便直观的测试 zuul 的路由、负载均衡、和错误熔断等功能,所以本用例在其基础上进行 zuul 的整合。
+ common: 公共的接口和实体类; + common: 公共的接口和实体类;
+ consumer: 服务的消费者采用feign调用产品服务 + consumer: 服务的消费者,采用 feign 调用产品服务;
+ producer服务的提供者 + producer服务的提供者
+ eureka: 注册中心; + eureka: 注册中心;
+ zuul: api网关。 + zuul: api 网关。
聚合项目目录如下: 聚合项目目录如下:
@ -131,7 +131,7 @@ zuul 项目目录如下:
#### 3.2 在启动类上添加注解@EnableZuulProxy和@EnableDiscoveryClient #### 3.2 在启动类上添加注解@EnableZuulProxy和@EnableDiscoveryClient
@EnableZuulProxy会自动设置Zuul服务器端点并在其中开启一些反向代理过滤器,以便将请求转发到后端服务器。 @EnableZuulProxy 会自动设置 Zuul 服务器端点并在其中开启一些反向代理过滤器,以便将请求转发到后端服务器。
```java ```java
@SpringBootApplication @SpringBootApplication
@ -150,7 +150,7 @@ public class ZuulApplication {
#### 3.3 指定注册中心、配置网关的路由规则 #### 3.3 指定注册中心、配置网关的路由规则
zuul 需要指定注册中心的地址zuul 会从eureka获取其他微服务的实例信息然后按照指定的路由规则进行请求转发。 zuul 需要指定注册中心的地址zuul 会从 eureka 获取其他微服务的实例信息,然后按照指定的路由规则进行请求转发。
```yaml ```yaml
server: server:
@ -212,7 +212,7 @@ public class CustomZuulFallbackProvider implements FallbackProvider {
return new ClientHttpResponse() { return new ClientHttpResponse() {
/** /**
* 返回响应的HTTP状态代码 * 返回响应的 HTTP 状态代码
*/ */
@Override @Override
public HttpStatus getStatusCode() throws IOException { public HttpStatus getStatusCode() throws IOException {
@ -220,7 +220,7 @@ public class CustomZuulFallbackProvider implements FallbackProvider {
} }
/** /**
* 返回HTTP状态代码 * 返回 HTTP 状态代码
*/ */
@Override @Override
public int getRawStatusCode() throws IOException { public int getRawStatusCode() throws IOException {
@ -228,7 +228,7 @@ public class CustomZuulFallbackProvider implements FallbackProvider {
} }
/** /**
* 返回响应的HTTP状态文本 * 返回响应的 HTTP 状态文本
*/ */
@Override @Override
public String getStatusText() throws IOException { public String getStatusText() throws IOException {
@ -262,7 +262,7 @@ public class CustomZuulFallbackProvider implements FallbackProvider {
} }
``` ```
正确返回了内容、同时返回的http状态码也和我们设置的一样。 正确返回了内容、同时返回的 http 状态码也和我们设置的一样。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-broker.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-broker.png"/> </div>
@ -270,12 +270,12 @@ public class CustomZuulFallbackProvider implements FallbackProvider {
## 五、zuul 过滤器 ## 五、zuul 过滤器
创建自定义过滤器继承自CustomZuulFilter当我们访问网关的时候如果判断session 中没有对应的 code,则跳转到我们自定义的登录页面。 创建自定义过滤器继承自 CustomZuulFilter当我们访问网关的时候如果判断 session 中没有对应的 code,则跳转到我们自定义的登录页面。
```java ```java
/** /**
* @author : heibaiying * @author : heibaiying
* @description : 自定义filter过滤器 * @description : 自定义 filter 过滤器
*/ */
@Component @Component
@ -306,7 +306,7 @@ public class CustomZuulFilter extends ZuulFilter {
} }
/** /**
* ZuulFilter的核心校验方法 * ZuulFilter 的核心校验方法
*/ */
@Override @Override
public Object run() throws ZuulException { public Object run() throws ZuulException {
@ -314,7 +314,7 @@ public class CustomZuulFilter extends ZuulFilter {
HttpServletRequest request = currentContext.getRequest(); HttpServletRequest request = currentContext.getRequest();
String code = (String)request.getSession().getAttribute("code"); String code = (String)request.getSession().getAttribute("code");
if (StringUtils.isEmpty(code)){ if (StringUtils.isEmpty(code)){
// 设置值为false 不将请求转发到对应的服务上 // 设置值为 false 不将请求转发到对应的服务上
currentContext.setSendZuulResponse(false); currentContext.setSendZuulResponse(false);
// 设置返回的状态码 // 设置返回的状态码
currentContext.setResponseStatusCode(HttpStatus.NON_AUTHORITATIVE_INFORMATION.value()); currentContext.setResponseStatusCode(HttpStatus.NON_AUTHORITATIVE_INFORMATION.value());
@ -372,7 +372,7 @@ index.ftl:
## 七、附:关于版本问题可能导致的 zuul 启动失败 ## 七、附:关于版本问题可能导致的 zuul 启动失败
如果出现以下错误导致启动失败,是 spring boot 版本不兼容导致的错误Finchley SR2版本 spring cloud 中的 zuul 和 spring boot 2.1.x 版本存在不兼容。如果出现这个问题,则将 spring boot 将至 2.0.x 的版本即可,用例中采用的是 2.0.8 版本。在实际的开发中应该严格遵循spring 官方的版本依赖说明。 如果出现以下错误导致启动失败,是 spring boot 版本不兼容导致的错误Finchley SR2 版本 spring cloud 中的 zuul 和 spring boot 2.1.x 版本存在不兼容。如果出现这个问题,则将 spring boot 将至 2.0.x 的版本即可,用例中采用的是 2.0.8 版本。在实际的开发中应该严格遵循 spring 官方的版本依赖说明。
```java ```java
APPLICATION FAILED TO START APPLICATION FAILED TO START

View File

@ -1,5 +1,6 @@
# spring AOP注解方式 # spring AOP注解方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
@ -17,9 +18,9 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
1. 切面配置位于 com.heibaiying.config 下 AopConfig.java 文件;
1. 切面配置位于com.heibaiying.config下AopConfig.java文件 2. 自定义切面位于 advice 下,其中 CustomAdvice 是标准的自定义切面FirstAdvice 和 SecondAdvice 用于测试多切面共同作用于同一个被切入点时的执行顺序
2. 自定义切面位于advice下其中CustomAdvice是标准的自定义切面FirstAdvice和SecondAdvice用于测试多切面共同作用于同一个被切入点时的执行顺序 3. OrderService 是待切入方法。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-aop-annotation.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-aop-annotation.png"/> </div>
@ -27,7 +28,7 @@
### 1.2 依赖说明 ### 1.2 依赖说明
除了 spring 的基本依赖外,需要导入 aop 依赖包
```xml ```xml
<!--aop 相关依赖--> <!--aop 相关依赖-->
@ -71,7 +72,7 @@ public class OrderServiceImpl implements OrderService {
#### 2.2 创建自定义切面类 #### 2.2 创建自定义切面类
注:@Pointcut 的值可以是多个切面表达式的组合。
```java ```java
/** /**
@ -79,7 +80,7 @@ public class OrderServiceImpl implements OrderService {
* @description : 自定义切面 * @description : 自定义切面
*/ */
@Aspect @Aspect
@Aspect @Component //除了加上@Aspect 外 还需要声明为 spring 的组件 @Aspect 只是一个切面声明
public class CustomAdvice { public class CustomAdvice {
@ -140,7 +141,7 @@ public class CustomAdvice {
*/ */
@Configuration @Configuration
@ComponentScan("com.heibaiying.*") @ComponentScan("com.heibaiying.*")
@ComponentScan("com.heibaiying.*") @EnableAspectJAutoProxy // 开启@Aspect 注解支持 等价于<aop:aspectj-autoproxy>
public class AopConfig { public class AopConfig {
} }
``` ```
@ -163,8 +164,8 @@ public class AopTest {
} }
/** /**
/** * 多个切面作用于同一个切入点时,可以用@Order 指定切面的执行顺序
* 多个切面作用于同一个切入点时,可以用@Order指定切面的执行顺序 * 优先级高的切面在切入方法前执行的通知 (before) 会优先执行,但是位于方法后执行的通知 (after,afterReturning) 反而会延后执行
*/ */
@Test @Test
public void delete() { public void delete() {
@ -175,11 +176,11 @@ public class AopTest {
#### 2.5 切面执行顺序 #### 2.5 切面执行顺序
- 多个切面作用于同一个切入点时,可以用@Order 指定切面的执行顺序
- 优先级高的切面在切入方法前执行的通知 (before) 会优先执行,但是位于方法后执行的通知 (after,afterReturning) 反而会延后执行,类似于同心圆原理。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/aop 执行顺序.png"/> </div>
@ -194,7 +195,7 @@ execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-patter
- 除了返回类型模式,名字模式和参数模式以外,所有的部分都是可选的; - 除了返回类型模式,名字模式和参数模式以外,所有的部分都是可选的;
- `*`,它代表了匹配任意的返回类型; - `*`,它代表了匹配任意的返回类型;
- `*`,它代表了匹配任意的返回类型; - `()` 匹配了一个不接受任何参数的方法, 而 `(..)` 匹配了一个接受任意数量参数的方法(零或者更多)。 模式 `(*)` 匹配了一个接受一个任何类型的参数的方法。 模式 `(*,String)` 匹配了一个接受两个参数的方法,第一个可以是任意类型,第二个则必须是 String 类型。
下面给出一些常见切入点表达式的例子。 下面给出一些常见切入点表达式的例子。
@ -216,31 +217,31 @@ execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-patter
execution(* com.xyz.service.AccountService.*(..)) execution(* com.xyz.service.AccountService.*(..))
``` ```
- 定义在 service 包里的任意方法的执行:
```java ```java
execution(* com.xyz.service.*.*(..)) execution(* com.xyz.service.*.*(..))
``` ```
- 定义在 service 包或者子包里的任意方法的执行:
```java ```java
execution(* com.xyz.service..*.*(..)) execution(* com.xyz.service..*.*(..))
``` ```
- 在 service 包里的任意连接点(在 Spring AOP 中只是方法执行)
```java ```java
within(com.xyz.service.*) within(com.xyz.service.*)
``` ```
- 在 service 包或者子包里的任意连接点(在 Spring AOP 中只是方法执行)
``` ```
within(com.xyz.service..*) within(com.xyz.service..*)
``` ```
- 实现了 `AccountService` 接口的代理对象的任意连接点(在 Spring AOP 中只是方法执行)
``` ```
this(com.xyz.service.AccountService) this(com.xyz.service.AccountService)

View File

@ -1,5 +1,6 @@
# spring AOPxml配置方式 # spring AOPxml配置方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
@ -16,7 +17,7 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
切面配置位于 resources 下的 aop.xml 文件,其中 CustomAdvice 是自定义切面类OrderService 是待切入的方法。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-aop.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-aop.png"/> </div>
@ -24,7 +25,7 @@
### 1.2 依赖说明 ### 1.2 依赖说明
除了 spring 的基本依赖外,需要导入 aop 依赖包
```xml ```xml
<!--aop 相关依赖--> <!--aop 相关依赖-->
@ -84,7 +85,7 @@ public class CustomAdvice {
System.out.println(name + "方法调用前:获取调用参数" + Arrays.toString(args)); System.out.println(name + "方法调用前:获取调用参数" + Arrays.toString(args));
} }
//后置通知 (抛出异常后不会被执行)
public void afterReturning(JoinPoint joinPoint, Object result) { public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("后置返回通知结果" + result); System.out.println("后置返回通知结果" + result);
} }
@ -125,7 +126,7 @@ public class CustomAdvice {
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启后允许使用 Spring AOP 的@AspectJ 注解 如果是纯 xml 配置 可以不用开启这个声明-->
<aop:aspectj-autoproxy/> <aop:aspectj-autoproxy/>
<!-- 1.配置目标对象 --> <!-- 1.配置目标对象 -->
@ -134,7 +135,7 @@ public class CustomAdvice {
<bean name="myAdvice" class="com.heibaiying.advice.CustomAdvice"/> <bean name="myAdvice" class="com.heibaiying.advice.CustomAdvice"/>
<!-- 3.配置将通知织入目标对象 --> <!-- 3.配置将通知织入目标对象 -->
<aop:config> <aop:config>
<aop:config> <!--命名切入点 关于切入点更多表达式写法可以参见 README.md-->
<aop:pointcut expression="execution(* com.heibaiying.service.OrderService.*(..))" id="cutPoint"/> <aop:pointcut expression="execution(* com.heibaiying.service.OrderService.*(..))" id="cutPoint"/>
<aop:aspect ref="myAdvice"> <aop:aspect ref="myAdvice">
<!-- 前置通知 --> <!-- 前置通知 -->
@ -184,7 +185,7 @@ execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-patter
- 除了返回类型模式,名字模式和参数模式以外,所有的部分都是可选的; - 除了返回类型模式,名字模式和参数模式以外,所有的部分都是可选的;
- `*`,它代表了匹配任意的返回类型; - `*`,它代表了匹配任意的返回类型;
- `*`,它代表了匹配任意的返回类型; - `()` 匹配了一个不接受任何参数的方法, 而 `(..)` 匹配了一个接受任意数量参数的方法(零或者更多)。 模式 `(*)` 匹配了一个接受一个任何类型的参数的方法。 模式 `(*,String)` 匹配了一个接受两个参数的方法,第一个可以是任意类型,第二个则必须是 String 类型。
下面给出一些常见切入点表达式的例子。 下面给出一些常见切入点表达式的例子。
@ -206,31 +207,31 @@ execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-patter
execution(* com.xyz.service.AccountService.*(..)) execution(* com.xyz.service.AccountService.*(..))
``` ```
- 定义在 service 包里的任意方法的执行:
```java ```java
execution(* com.xyz.service.*.*(..)) execution(* com.xyz.service.*.*(..))
``` ```
- 定义在 service 包或者子包里的任意方法的执行:
```java ```java
execution(* com.xyz.service..*.*(..)) execution(* com.xyz.service..*.*(..))
``` ```
- 在 service 包里的任意连接点(在 Spring AOP 中只是方法执行)
```java ```java
within(com.xyz.service.*) within(com.xyz.service.*)
``` ```
- 在 service 包或者子包里的任意连接点(在 Spring AOP 中只是方法执行)
``` ```
within(com.xyz.service..*) within(com.xyz.service..*)
``` ```
- 实现了 `AccountService` 接口的代理对象的任意连接点(在 Spring AOP 中只是方法执行)
``` ```
this(com.xyz.service.AccountService) this(com.xyz.service.AccountService)

View File

@ -76,7 +76,7 @@ public class DispatcherServletInitializer extends AbstractAnnotationConfigDispat
#### 3、基于servlet 3.0的支持可以采用注解的方式注册druid的servlet和filter #### 3、基于servlet 3.0的支持可以采用注解的方式注册druid的servlet和filter
关于servlet 更多注解支持可以查看[Servlet 规范文档](https://github.com/heibaiying/spring-samples-for-all/blob/master/referenced%20documents/Servlet3.1%E8%A7%84%E8%8C%83%EF%BC%88%E6%9C%80%E7%BB%88%E7%89%88%EF%BC%89.pdf)中**8.1小节 注解和可插拔性** 注:关于 servlet 更多注解支持可以查看[Servlet 规范文档](https://github.com/heibaiying/spring-samples-for-all/blob/master/referenced%20documents/Servlet3.1%E8%A7%84%E8%8C%83%EF%BC%88%E6%9C%80%E7%BB%88%E7%89%88%EF%BC%89.pdf) 中**8.1 小节 注解和可插拔性**
```java ```java
/** /**
@ -98,7 +98,7 @@ public class DruidStatViewServlet extends StatViewServlet {
/** /**
* @author : heibaiying * @author : heibaiying
* @description : WebStatFilter用于采集web-jdbc关联监控的数据 * @description : WebStatFilter 用于采集 web-jdbc 关联监控的数据
*/ */
@WebFilter(filterName="druidWebStatFilter",urlPatterns="/*", @WebFilter(filterName="druidWebStatFilter",urlPatterns="/*",
initParams={ initParams={
@ -124,7 +124,7 @@ mysql.password=root
```properties ```properties
# oracle 数据库配置 # oracle 数据库配置
oracle.driverClassName=oracle.jdbc.driver.OracleDriver oracle.driverClassName=oracle.jdbc.driver.OracleDriver
oracle.url=jdbc:oracle:thin:@//IP地址:端口号/数据库实例名 oracle.url=jdbc:oracle:thin:@//IP 地址:端口号/数据库实例名
oracle.username=用户名 oracle.username=用户名
oracle.password=密码 oracle.password=密码
``` ```
@ -157,7 +157,7 @@ public class DataSourceConfig {
* @author : heibaiying * @author : heibaiying
*/ */
@Configuration @Configuration
@EnableTransactionManagement // 开启声明式事务处理 等价于xml中<tx:annotation-driven/> @EnableTransactionManagement // 开启声明式事务处理 等价于 xml 中<tx:annotation-driven/>
@EnableWebMvc @EnableWebMvc
@ComponentScan(basePackages = {"com.heibaiying.*"}) @ComponentScan(basePackages = {"com.heibaiying.*"})
public class ServletConfig implements WebMvcConfigurer { public class ServletConfig implements WebMvcConfigurer {
@ -170,7 +170,7 @@ public class ServletConfig implements WebMvcConfigurer {
} }
/** /**
* 配置druid 数据源 * 配置 druid 数据源
*/ */
@Bean @Bean
public DruidDataSource dataSource(DataSourceConfig sourceConfig) throws SQLException { public DruidDataSource dataSource(DataSourceConfig sourceConfig) throws SQLException {
@ -189,24 +189,24 @@ public class ServletConfig implements WebMvcConfigurer {
dataSource.setMinEvictableIdleTimeMillis(600000); dataSource.setMinEvictableIdleTimeMillis(600000);
dataSource.setMaxEvictableIdleTimeMillis(900000); dataSource.setMaxEvictableIdleTimeMillis(900000);
/* validationQuery 用来检测连接是否有效的sql要求是一个查询语句常用select 'x'。 /* validationQuery 用来检测连接是否有效的 sql要求是一个查询语句常用 select 'x'。
* 但是在 oracle 数据库下需要写成 select 'x' from dual 不然实例化数据源的时候就会失败, * 但是在 oracle 数据库下需要写成 select 'x' from dual 不然实例化数据源的时候就会失败,
* 这是由于oracle 和 mysql 语法间的差异造成的 * 这是由于 oracle 和 mysql 语法间的差异造成的
*/ */
dataSource.setValidationQuery("select 'x'"); dataSource.setValidationQuery("select 'x'");
// 建议配置为true不影响性能并且保证安全性。申请连接的时候检测如果空闲时间大于timeBetweenEvictionRunsMillis执行validationQuery检测连接是否有效。 // 建议配置为 true不影响性能并且保证安全性。申请连接的时候检测如果空闲时间大于 timeBetweenEvictionRunsMillis执行 validationQuery 检测连接是否有效。
dataSource.setTestWhileIdle(true); dataSource.setTestWhileIdle(true);
// 申请连接时执行validationQuery检测连接是否有效做了这个配置会降低性能。 // 申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
dataSource.setTestOnBorrow(false); dataSource.setTestOnBorrow(false);
// 归还连接时执行validationQuery检测连接是否有效做了这个配置会降低性能 // 归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能
dataSource.setTestOnReturn(false); dataSource.setTestOnReturn(false);
// 连接池中的minIdle数量以内的连接空闲时间超过minEvictableIdleTimeMillis则会执行keepAlive操作。 // 连接池中的 minIdle 数量以内的连接,空闲时间超过 minEvictableIdleTimeMillis则会执行 keepAlive 操作。
dataSource.setPhyMaxUseCount(100000); dataSource.setPhyMaxUseCount(100000);
/*配置监控统计拦截的filters Druid连接池的监控信息主要是通过StatFilter 采集的, /*配置监控统计拦截的 filters Druid 连接池的监控信息主要是通过 StatFilter 采集的,
采集的信息非常全面包括SQL执行、并发、慢查、执行时间区间分布等*/ 采集的信息非常全面,包括 SQL 执行、并发、慢查、执行时间区间分布等*/
dataSource.setFilters("stat"); dataSource.setFilters("stat");
return dataSource; return dataSource;
@ -214,10 +214,10 @@ public class ServletConfig implements WebMvcConfigurer {
/** /**
* 配置mybatis 会话工厂 * 配置 mybatis 会话工厂
* *
* @param dataSource 这个参数的名称需要保持和上面方法名一致 才能自动注入,因为 * @param dataSource 这个参数的名称需要保持和上面方法名一致 才能自动注入,因为
* 采用@Bean注解生成的bean 默认采用方法名为名称,当然也可以在使用@Bean时指定name属性 * 采用@Bean 注解生成的 bean 默认采用方法名为名称,当然也可以在使用@Bean 时指定 name 属性
*/ */
@Bean @Bean
public SqlSessionFactoryBean sessionFactoryBean(DruidDataSource dataSource) throws IOException { public SqlSessionFactoryBean sessionFactoryBean(DruidDataSource dataSource) throws IOException {
@ -230,7 +230,7 @@ public class ServletConfig implements WebMvcConfigurer {
} }
/** /**
* 配置mybatis 会话工厂 * 配置 mybatis 会话工厂
*/ */
@Bean @Bean
public MapperScannerConfigurer MapperScannerConfigurer() { public MapperScannerConfigurer MapperScannerConfigurer() {
@ -267,7 +267,7 @@ public class ServletConfig implements WebMvcConfigurer {
<settings> <settings>
<!-- 开启驼峰命名 --> <!-- 开启驼峰命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 打印查询sql --> <!-- 打印查询 sql -->
<setting name="logImpl" value="STDOUT_LOGGING"/> <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings> </settings>
@ -358,6 +358,6 @@ public class OracleController {
#### 10、druid 监控页面访问地址http://localhost:8080/druid/index.html #### 10、druid 监控页面访问地址http://localhost:8080/druid/index.html
![druid控制台](https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/druid%E6%8E%A7%E5%88%B6%E5%8F%B0.png) ![druid 控制台](https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/druid%E6%8E%A7%E5%88%B6%E5%8F%B0.png)

View File

@ -63,7 +63,7 @@
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"> version="3.1">
<!--配置spring前端控制器--> <!--配置 spring 前端控制器-->
<servlet> <servlet>
<servlet-name>springMvc</servlet-name> <servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
@ -104,7 +104,7 @@
<url-pattern>/druid/*</url-pattern> <url-pattern>/druid/*</url-pattern>
</servlet-mapping> </servlet-mapping>
<!--配置WebStatFilter用于采集web-jdbc关联监控的数据--> <!--配置 WebStatFilter 用于采集 web-jdbc 关联监控的数据-->
<filter> <filter>
<filter-name>DruidWebStatFilter</filter-name> <filter-name>DruidWebStatFilter</filter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class> <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
@ -131,7 +131,7 @@ mysql.username=root
mysql.password=root mysql.password=root
# oracle 数据库配置 # oracle 数据库配置
oracle.url=jdbc:oracle:thin:@//IP地址:端口号/数据库实例名 oracle.url=jdbc:oracle:thin:@//IP 地址:端口号/数据库实例名
oracle.username=用户名 oracle.username=用户名
oracle.password=密码 oracle.password=密码
``` ```
@ -150,19 +150,19 @@ oracle.password=密码
<!-- 开启注解包扫描--> <!-- 开启注解包扫描-->
<context:component-scan base-package="com.heibaiying.*"/> <context:component-scan base-package="com.heibaiying.*"/>
<!--使用默认的Servlet来响应静态文件 --> <!--使用默认的 Servlet 来响应静态文件 -->
<mvc:default-servlet-handler/> <mvc:default-servlet-handler/>
<!-- 开启注解驱动--> <!-- 开启注解驱动-->
<mvc:annotation-driven/> <mvc:annotation-driven/>
<!--引入druid.xml配置 由于druid 的可配置项比较多,所以可以单独拆分为一个配置文件--> <!--引入 druid.xml 配置 由于 druid 的可配置项比较多,所以可以单独拆分为一个配置文件-->
<import resource="druid.xml"/> <import resource="druid.xml"/>
<!--配置 mybatis 会话工厂 --> <!--配置 mybatis 会话工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/> <property name="dataSource" ref="dataSource"/>
<!--指定mapper文件所在的位置--> <!--指定 mapper 文件所在的位置-->
<property name="mapperLocations" value="classpath*:/mappers/**/*.xml"/> <property name="mapperLocations" value="classpath*:/mappers/**/*.xml"/>
<property name="configLocation" value="classpath:mybatisConfig.xml"/> <property name="configLocation" value="classpath:mybatisConfig.xml"/>
</bean> </bean>
@ -172,7 +172,7 @@ oracle.password=密码
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定会话工厂 --> <!--指定会话工厂 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 指定mybatis接口所在的包 --> <!-- 指定 mybatis 接口所在的包 -->
<property name="basePackage" value="com.heibaiying.dao"/> <property name="basePackage" value="com.heibaiying.dao"/>
</bean> </bean>
@ -182,7 +182,7 @@ oracle.password=密码
<property name="dataSource" ref="dataSource"/> <property name="dataSource" ref="dataSource"/>
</bean> </bean>
<!-- 开启事务注解@Transactional支持 --> <!-- 开启事务注解@Transactional 支持 -->
<tx:annotation-driven/> <tx:annotation-driven/>
@ -201,7 +201,7 @@ oracle.password=密码
<!--指定配置文件的位置--> <!--指定配置文件的位置-->
<context:property-placeholder location="classpath:jdbc.properties"/> <context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置druid数据源 关于更多的配置项 可以参考官方文档 <a href="https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8" > --> <!--配置 druid 数据源 关于更多的配置项 可以参考官方文档 <a href="https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8" > -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password --> <!-- 基本属性 url、user、password -->
<property name="url" value="${mysql.url}"/> <property name="url" value="${mysql.url}"/>
@ -224,24 +224,24 @@ oracle.password=密码
<!-- 配置一个连接在池中最大生存的时间,单位是毫秒 --> <!-- 配置一个连接在池中最大生存的时间,单位是毫秒 -->
<property name="maxEvictableIdleTimeMillis" value="900000"/> <property name="maxEvictableIdleTimeMillis" value="900000"/>
<!--validationQuery 用来检测连接是否有效的sql要求是一个查询语句常用select 'x'。 <!--validationQuery 用来检测连接是否有效的 sql要求是一个查询语句常用 select 'x'。
但是在 oracle 数据库下需要写成 select 'x' from dual 不然实例化数据源的时候就会失败, 但是在 oracle 数据库下需要写成 select 'x' from dual 不然实例化数据源的时候就会失败,
这是由于oracle 和 mysql 语法间的差异造成的--> 这是由于 oracle 和 mysql 语法间的差异造成的-->
<property name="validationQuery" value="select 'x'"/> <property name="validationQuery" value="select 'x'"/>
<!--建议配置为true不影响性能并且保证安全性。申请连接的时候检测 <!--建议配置为 true不影响性能并且保证安全性。申请连接的时候检测
如果空闲时间大于timeBetweenEvictionRunsMillis执行validationQuery检测连接是否有效。--> 如果空闲时间大于 timeBetweenEvictionRunsMillis执行 validationQuery 检测连接是否有效。-->
<property name="testWhileIdle" value="true"/> <property name="testWhileIdle" value="true"/>
<!--申请连接时执行validationQuery检测连接是否有效做了这个配置会降低性能。--> <!--申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。-->
<property name="testOnBorrow" value="false"/> <property name="testOnBorrow" value="false"/>
<!--归还连接时执行validationQuery检测连接是否有效做了这个配置会降低性能。--> <!--归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。-->
<property name="testOnReturn" value="false"/> <property name="testOnReturn" value="false"/>
<!--连接池中的minIdle数量以内的连接空闲时间超过minEvictableIdleTimeMillis则会执行keepAlive操作。--> <!--连接池中的 minIdle 数量以内的连接,空闲时间超过 minEvictableIdleTimeMillis则会执行 keepAlive 操作。-->
<property name="keepAlive" value="true"/> <property name="keepAlive" value="true"/>
<property name="phyMaxUseCount" value="100000"/> <property name="phyMaxUseCount" value="100000"/>
<!-- 配置监控统计拦截的filters Druid连接池的监控信息主要是通过StatFilter 采集的, <!-- 配置监控统计拦截的 filters Druid 连接池的监控信息主要是通过 StatFilter 采集的,
采集的信息非常全面包括SQL执行、并发、慢查、执行时间区间分布等--> 采集的信息非常全面,包括 SQL 执行、并发、慢查、执行时间区间分布等-->
<property name="filters" value="stat"/> <property name="filters" value="stat"/>
</bean> </bean>
@ -262,7 +262,7 @@ oracle.password=密码
<settings> <settings>
<!-- 开启驼峰命名 --> <!-- 开启驼峰命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 打印查询sql --> <!-- 打印查询 sql -->
<setting name="logImpl" value="STDOUT_LOGGING"/> <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings> </settings>
@ -382,4 +382,4 @@ public class OracleController {
#### 8、druid 监控页面访问地址http://localhost:8080/druid/index.html #### 8、druid 监控页面访问地址http://localhost:8080/druid/index.html
![druid控制台](https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/druid%E6%8E%A7%E5%88%B6%E5%8F%B0.png) ![druid 控制台](https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/druid%E6%8E%A7%E5%88%B6%E5%8F%B0.png)

View File

@ -1,5 +1,6 @@
# spring 整合 dubbo注解方式 # spring 整合 dubbo注解方式
## 目录<br/>
<a href="#一-项目结构说明">一、 项目结构说明</a><br/> <a href="#一-项目结构说明">一、 项目结构说明</a><br/>
<a href="#二项目依赖">二、项目依赖</a><br/> <a href="#二项目依赖">二、项目依赖</a><br/>
<a href="#三公共模块dubbo-ano-common">三、公共模块dubbo-ano-common</a><br/> <a href="#三公共模块dubbo-ano-common">三、公共模块dubbo-ano-common</a><br/>
@ -16,13 +17,13 @@
## 一、 项目结构说明 ## 一、 项目结构说明
1.1 按照 dubbo 文档推荐的服务最佳实践,建议将服务接口、服务模型、服务异常等均放在 API 包中,所以项目采用 maven 多模块的构建方式,在 spring-dubbo-annotation 下构建三个子模块:
1. dubbo-ano-common 是公共模块,用于存放公共的接口和 bean,被 dubbo-ano-provider 和 dubbo-ano-provider 在 pom.xml 中引用;
2. dubbo-ano-provider 是服务的提供者,提供商品的查询服务; 2. dubbo-ano-provider 是服务的提供者,提供商品的查询服务;
2. dubbo-ano-provider 是服务的提供者,提供商品的查询服务 3. dubbo-ano-provider 是服务的消费者,调用 provider 提供的查询服务
1.2 本项目 dubbo 的搭建采用 zookeeper 作为注册中心, 关于 zookeeper 的安装和基本操作可以参见我的手记[Zookeeper 基础命令与 Java 客户端](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/ZooKeeper/ZooKeeper%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA%E4%B8%8EJava%E5%AE%A2%E6%88%B7%E7%AB%AF.md)
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-dubbo.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-dubbo.png"/> </div>
@ -30,9 +31,9 @@
## 二、项目依赖 ## 二、项目依赖
**在父工程的项目中统一导入依赖 dubbo 依赖的的 jar 包**
这里需要注意的是 ZooKeeper 3.5.x 和 ZooKeeper 3.4.x 是存在不兼容的情况 详见官网解释[ZooKeeper Version Compatibility](https://curator.apache.org/zk-compatibility.html), zookeeper 3.5 目前是 beta 版本,所以 zookeeper 我选择的版本是 zookeeper-3.4.9 作为服务端。但默认情况下 curator-framework 自动引用的最新的 3.5 的版本客户端,会出现 KeeperException$UnimplementedException 异常
```xml ```xml
<!--dubbo 依赖--> <!--dubbo 依赖-->
@ -88,7 +89,7 @@ public class DubboConfiguration {
} }
/** /**
/** * 使用 zookeeper 注册中心暴露服务地址
*/ */
@Bean @Bean
public RegistryConfig registryConfig() { public RegistryConfig registryConfig() {
@ -99,7 +100,7 @@ public class DubboConfiguration {
} }
/** /**
/** * 用 dubbo 协议在 20880 端口暴露服务
*/ */
@Bean @Bean
public ProtocolConfig protocolConfig() { public ProtocolConfig protocolConfig() {
@ -113,7 +114,7 @@ public class DubboConfiguration {
#### 4.2 使用注解@Service暴露服务 #### 4.2 使用注解@Service暴露服务
需要注意的是这里的@Service 注解不是 spring 的注解,而是 dubbo 的注解 com.alibaba.dubbo.config.annotation.Service
```java ```java
package com.heibaiying.service; package com.heibaiying.service;
@ -192,7 +193,7 @@ public class DubboConfiguration {
} }
/** /**
/** * 使用 zookeeper 注册中心暴露发现服务地址
*/ */
@Bean @Bean
public RegistryConfig registryConfig() { public RegistryConfig registryConfig() {
@ -224,7 +225,7 @@ import java.util.List;
@RequestMapping("sell") @RequestMapping("sell")
public class SellController { public class SellController {
// dubbo 远程引用注解
@Reference @Reference
private IProductService productService; private IProductService productService;
@ -247,13 +248,13 @@ public class SellController {
## 六、项目构建的说明 ## 六、项目构建的说明
因为在项目中consumer 和 provider 模块均依赖公共模块,所以在构建 consumer 和 provider 项目前需要将 common 模块安装到本地仓库,**依次**对**父工程**和**common 模块**执行:
```shell ```shell
mvn install -Dmaven.test.skip = true mvn install -Dmaven.test.skip = true
``` ```
consumer 中 pom.xml 如下
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -281,7 +282,7 @@ consumer中 pom.xml如下
</project> </project>
``` ```
provider 中 pom.xml 如下
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -324,7 +325,7 @@ mvn clean package
```sh ```sh
配置文件为: 配置文件为:
dubbo-admin-backend/src/main/resources/application.properties dubbo-admin-backend/src/main/resources/application.properties
dubbo-admin-backend/src/main/resources/application.properties 主要的配置有 默认的配置就是 127.0.0.1:2181
dubbo.registry.address=zookeeper://127.0.0.1:2181 dubbo.registry.address=zookeeper://127.0.0.1:2181
``` ```

View File

@ -1,5 +1,6 @@
# spring 整合 dubboxml配置方式 # spring 整合 dubboxml配置方式
## 目录<br/>
<a href="#一-项目结构说明">一、 项目结构说明</a><br/> <a href="#一-项目结构说明">一、 项目结构说明</a><br/>
<a href="#二项目依赖">二、项目依赖</a><br/> <a href="#二项目依赖">二、项目依赖</a><br/>
<a href="#三公共模块dubbo-common">三、公共模块dubbo-common</a><br/> <a href="#三公共模块dubbo-common">三、公共模块dubbo-common</a><br/>
@ -16,13 +17,13 @@
## 一、 项目结构说明 ## 一、 项目结构说明
1.1 按照 dubbo 文档推荐的服务最佳实践,建议将服务接口、服务模型、服务异常等均放在 API 包中,所以项目采用 maven 多模块的构建方式,在 spring-dubbo 下构建三个子模块:
1. dubbo-common 是公共模块,用于存放公共的接口和 bean,被 dubbo-provider 和 dubbo-provider 在 pom.xml 中引用;
2. dubbo-provider 是服务的提供者,提供商品的查询服务; 2. dubbo-provider 是服务的提供者,提供商品的查询服务;
2. dubbo-provider 是服务的提供者,提供商品的查询服务 3. dubbo-provider 是服务的消费者,调用 provider 提供的查询服务
1.2 本项目 dubbo 的搭建采用 zookeeper 作为注册中心, 关于 zookeeper 的安装和基本操作可以参见我的手记[Zookeeper 基础命令与 Java 客户端](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/ZooKeeper/ZooKeeper%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA%E4%B8%8EJava%E5%AE%A2%E6%88%B7%E7%AB%AF.md)
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-dubbo.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-dubbo.png"/> </div>
@ -30,9 +31,9 @@
## 二、项目依赖 ## 二、项目依赖
**在父工程的项目中统一导入依赖 dubbo 依赖的的 jar 包**
这里需要注意的是 ZooKeeper 3.5.x 和 ZooKeeper 3.4.x 是存在不兼容的情况 详见官网解释[ZooKeeper Version Compatibility](https://curator.apache.org/zk-compatibility.html), zookeeper 3.5 目前是 beta 版本,所以 zookeeper 我选择的版本是 zookeeper-3.4.9 作为服务端。但默认情况下 curator-framework 自动引用的最新的 3.5 的版本客户端,会出现 KeeperException$UnimplementedException 异常
```xml ```xml
<!--dubbo 依赖--> <!--dubbo 依赖-->
@ -73,7 +74,7 @@
#### 4.1 productService是服务的提供者 商品数据用模拟数据展示) #### 4.1 productService是服务的提供者 商品数据用模拟数据展示)
注:这里实现的接口 IProductService 来源于公共模块
```java ```java
/** /**
@ -122,16 +123,16 @@ public class ProductService implements IProductService {
<!-- 提供方应用信息,用于计算依赖关系 --> <!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="dubbo-provider"/> <dubbo:application name="dubbo-provider"/>
<!-- 使用 zookeeper 注册中心暴露服务地址 如果是集群配置 用, 分隔地址 -->
<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/> <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>
<!-- 用 dubbo 协议在 20880 端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880"/> <dubbo:protocol name="dubbo" port="20880"/>
<!-- 声明需要暴露的服务接口 --> <!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.heibaiying.api.IProductService" ref="productService"/> <dubbo:service interface="com.heibaiying.api.IProductService" ref="productService"/>
<!-- 和本地 bean 一样实现服务 -->
<bean id="productService" class="com.heibaiying.service.ProductService"/> <bean id="productService" class="com.heibaiying.service.ProductService"/>
</beans> </beans>
``` ```
@ -161,10 +162,10 @@ public class ProductService implements IProductService {
<!--可以关闭所有服务的启动时检查 --> <!--可以关闭所有服务的启动时检查 -->
<dubbo:consumer check="false" /> <dubbo:consumer check="false" />
<!-- 使用 zookeeper 注册中心暴露发现服务地址 -->
<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/> <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>
<!-- 生成远程服务代理,可以和本地 bean 一样使用 demoService -->
<dubbo:reference id="sellService" interface="com.heibaiying.api.IProductService"/> <dubbo:reference id="sellService" interface="com.heibaiying.api.IProductService"/>
@ -199,13 +200,13 @@ public class SellController {
## 六、项目构建的说明 ## 六、项目构建的说明
因为在项目中consumer 和 provider 模块均依赖公共模块,所以在构建 consumer 和 provider 项目前需要将 common 模块安装到本地仓库,**依次**对**父工程**和**common 模块**执行:
```shell ```shell
mvn install -Dmaven.test.skip = true mvn install -Dmaven.test.skip = true
``` ```
consumer 中 pom.xml 如下
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -233,7 +234,7 @@ consumer中 pom.xml如下
</project> </project>
``` ```
provider 中 pom.xml 如下
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -276,7 +277,7 @@ mvn clean package
```sh ```sh
配置文件为: 配置文件为:
dubbo-admin-backend/src/main/resources/application.properties dubbo-admin-backend/src/main/resources/application.properties
dubbo-admin-backend/src/main/resources/application.properties 主要的配置有 默认的配置就是 127.0.0.1:2181
dubbo.registry.address=zookeeper://127.0.0.1:2181 dubbo.registry.address=zookeeper://127.0.0.1:2181
``` ```

View File

@ -1,5 +1,6 @@
# spring 邮件发送xml配置方式 # spring 邮件发送xml配置方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
@ -14,9 +15,9 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
1. 邮件发送配置类为 com.heibaiying.config 下 EmailConfig.java;
1. 邮件发送配置类为com.heibaiying.config下EmailConfig.java; 2. 简单邮件发送、附件邮件发送、内嵌资源邮件发送、模板邮件发送的方法封装在 SpringMail 类中;
2. 简单邮件发送、附件邮件发送、内嵌资源邮件发送、模板邮件发送的方法封装在SpringMail类中 3. 项目以单元测试的方法进行测试,测试类为 SendEmail。
@ -26,7 +27,7 @@
### 1.2 依赖说明 ### 1.2 依赖说明
除了 spring 的基本依赖外,需要导入邮件发送的支持包 spring-context-support
```xml ```xml
<!--邮件发送依赖包--> <!--邮件发送依赖包-->
@ -36,7 +37,7 @@
<version>${spring-base-version}</version> <version>${spring-base-version}</version>
</dependency> </dependency>
<!--模板引擎--> <!--模板引擎-->
<!--模板引擎--> <!--这里采用的是 beetl,beetl 性能很卓越并且功能也很全面 官方文档地址 <a href="http://ibeetl.com/guide/#beetl">-->
<dependency> <dependency>
<groupId>com.ibeetl</groupId> <groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId> <artifactId>beetl</artifactId>
@ -61,7 +62,7 @@
public class EmailConfig { public class EmailConfig {
/*** /***
/*** * 在这里可以声明不同的邮件服务器主机,通常是 SMTP 主机,而具体的用户名和时授权码则建议在业务中从数据库查询
*/ */
@Bean(name = "qqMailSender") @Bean(name = "qqMailSender")
JavaMailSenderImpl javaMailSender() { JavaMailSenderImpl javaMailSender() {
@ -76,9 +77,9 @@ public class EmailConfig {
*/ */
@Bean @Bean
GroupTemplate groupTemplate() throws IOException { GroupTemplate groupTemplate() throws IOException {
GroupTemplate groupTemplate() throws IOException { //指定加载模板资源的位置 指定在 classpath:beetl 下-
ClasspathResourceLoader loader = new ClasspathResourceLoader("beetl"); ClasspathResourceLoader loader = new ClasspathResourceLoader("beetl");
ClasspathResourceLoader loader = new ClasspathResourceLoader("beetl"); //beetl 配置 这里采用默认的配置-
org.beetl.core.Configuration configuration = org.beetl.core.Configuration.defaultConfiguration(); org.beetl.core.Configuration configuration = org.beetl.core.Configuration.defaultConfiguration();
return new GroupTemplate(loader, configuration); return new GroupTemplate(loader, configuration);
} }
@ -103,9 +104,9 @@ public class SpringMail {
/** /**
* 发送简单邮件 * 发送简单邮件
* 发送简单邮件 * 在 qq 邮件发送的测试中,测试结果表明不管是简单邮件还是复杂邮件都必须指定发送用户,
* 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user * 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user
* 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user * qq 的授权码 可以在 设置/账户/POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV 服务 中开启服务后获取
*/ */
public void sendTextMessage(String from, String authWord, String to, String subject, String content) { public void sendTextMessage(String from, String authWord, String to, String subject, String content) {
// 设置发送人邮箱和授权码 // 设置发送人邮箱和授权码
@ -138,7 +139,7 @@ public class SpringMail {
qqMailSender.setPassword(authWord); qqMailSender.setPassword(authWord);
// 实例化消息对象 // 实例化消息对象
MimeMessage message = qqMailSender.createMimeMessage(); MimeMessage message = qqMailSender.createMimeMessage();
MimeMessage message = qqMailSender.createMimeMessage(); // 需要指定第二个参数为 true 代表创建支持可选文本,内联元素和附件的多部分消息
MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
helper.setFrom(from); helper.setFrom(from);
helper.setTo(to); helper.setTo(to);
@ -169,12 +170,12 @@ public class SpringMail {
qqMailSender.setPassword(authWord); qqMailSender.setPassword(authWord);
// 实例化消息对象 // 实例化消息对象
MimeMessage message = qqMailSender.createMimeMessage(); MimeMessage message = qqMailSender.createMimeMessage();
MimeMessage message = qqMailSender.createMimeMessage(); // 需要指定第二个参数为 true 代表创建支持可选文本,内联元素和附件的多部分消息
MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
helper.setFrom(from); helper.setFrom(from);
helper.setTo(to); helper.setTo(to);
helper.setSubject(subject); helper.setSubject(subject);
helper.setSubject(subject); // 使用 true 标志来指示包含的文本是 HTML 固定格式资源前缀 cid:
helper.setText("<html><body><img src='cid:image'></body></html>", true); helper.setText("<html><body><img src='cid:image'></body></html>", true);
// 需要先指定文本 再指定资源文件 // 需要先指定文本 再指定资源文件
FileSystemResource res = new FileSystemResource(file); FileSystemResource res = new FileSystemResource(file);
@ -208,7 +209,7 @@ public class SpringMail {
helper.setFrom(from); helper.setFrom(from);
helper.setTo(to); helper.setTo(to);
helper.setSubject(subject); helper.setSubject(subject);
helper.setSubject(subject); // 为 true 时候 表示文本内容以 html 渲染
helper.setText(text, true); helper.setText(text, true);
this.qqMailSender.send(message); this.qqMailSender.send(message);
System.out.println("发送邮件成功"); System.out.println("发送邮件成功");
@ -226,9 +227,9 @@ public class SpringMail {
- 模板引擎最主要的作用是,在对邮件格式有要求的时候,采用拼接字符串不够直观,所以采用模板引擎; - 模板引擎最主要的作用是,在对邮件格式有要求的时候,采用拼接字符串不够直观,所以采用模板引擎;
- 这里我们使用的 beetl 模板引擎,原因是其性能优异,官网是介绍其性能 6 倍与 freemaker,并有完善的文档支持。当然大家也可以换成任何其他的模板引擎freemarker,thymeleaf
一个简单的模板 template.html 如下:
```html ```html
<!doctype html> <!doctype html>
@ -269,29 +270,29 @@ public class SendEmail {
@Test @Test
public void sendMessage() { public void sendMessage() {
springMail.sendTextMessage(from, authWord, to, "spring 简单邮件", "Hello Spring Email!");
} }
@Test @Test
public void sendComplexMessage() { public void sendComplexMessage() {
Map<String, File> fileMap = new HashMap<>(); Map<String, File> fileMap = new HashMap<>();
Map<String, File> fileMap = new HashMap<>(); fileMap.put("image1.jpg", new File("D:\\LearningNotes\\picture\\msm 相关依赖.png"));
fileMap.put("image1.jpg", new File("D:\\LearningNotes\\picture\\msm相关依赖.png")); fileMap.put("image2.jpg", new File("D:\\LearningNotes\\picture\\RabbitMQ 模型架构.png"));
fileMap.put("image2.jpg", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png")); springMail.sendEmailWithAttachments(from, authWord, to, "spring 多附件邮件"
, "Hello Spring Email!", fileMap); , "Hello Spring Email!", fileMap);
} }
@Test @Test
public void sendEmailWithInline() { public void sendEmailWithInline() {
public void sendEmailWithInline() { springMail.sendEmailWithInline(from, authWord, to, "spring 内嵌资源邮件"
springMail.sendEmailWithInline(from, authWord, to, "spring内嵌资源邮件" , "Hello Spring Email!", new File("D:\\LearningNotes\\picture\\RabbitMQ 模型架构.png"));
} }
@Test @Test
public void sendEmailByTemplate() { public void sendEmailByTemplate() {
springMail.sendEmailByTemplate(from, authWord, to, springMail.sendEmailByTemplate(from, authWord, to,
springMail.sendEmailByTemplate(from, authWord, to, "spring 模板邮件", "Hello Spring Email!");
} }
} }
``` ```

View File

@ -1,5 +1,6 @@
# spring 邮件发送xml配置方式 # spring 邮件发送xml配置方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
@ -14,9 +15,9 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
1. 邮件发送配置文件为 springApplication.xml;
1. 邮件发送配置文件为springApplication.xml; 2. 简单邮件发送、附件邮件发送、内嵌资源邮件发送、模板邮件发送的方法封装在 SpringMail 类中;
2. 简单邮件发送、附件邮件发送、内嵌资源邮件发送、模板邮件发送的方法封装在SpringMail类中 3. 项目以单元测试的方法进行测试,测试类为 SendEmail。
@ -26,7 +27,7 @@
### 1.2 依赖说明 ### 1.2 依赖说明
除了 spring 的基本依赖外,需要导入邮件发送的支持包 spring-context-support
```xml ```xml
<!--邮件发送依赖包--> <!--邮件发送依赖包-->
@ -36,7 +37,7 @@
<version>${spring-base-version}</version> <version>${spring-base-version}</version>
</dependency> </dependency>
<!--模板引擎--> <!--模板引擎-->
<!--模板引擎--> <!--这里采用的是 beetl,beetl 性能很卓越并且功能也很全面 官方文档地址 <a href="http://ibeetl.com/guide/#beetl">-->
<dependency> <dependency>
<groupId>com.ibeetl</groupId> <groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId> <artifactId>beetl</artifactId>
@ -63,16 +64,16 @@
<!-- 开启注解包扫描--> <!-- 开启注解包扫描-->
<context:component-scan base-package="com.heibaiying.email"/> <context:component-scan base-package="com.heibaiying.email"/>
<!--在这里可以声明不同的邮件服务器主机,通常是 SMTP 主机,而具体的用户名和时授权码则建议在业务中从数据库查询-->
<bean id="qqMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <bean id="qqMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<!--qq 邮箱配置 <a href="https://service.mail.qq.com/cgi-bin/help?subtype=1&no=167&id=28"> --> <!--qq 邮箱配置 <a href="https://service.mail.qq.com/cgi-bin/help?subtype=1&no=167&id=28"> -->
<property name="host" value="smtp.qq.com"/> <property name="host" value="smtp.qq.com"/>
<property name="port" value="587"/> <property name="port" value="587"/>
</bean> </bean>
<!--配置 beetle 模板引擎 如果不使用模板引擎,以下的配置不是必须的-->
<bean id="resourceLoader" class="org.beetl.core.resource.ClasspathResourceLoader"> <bean id="resourceLoader" class="org.beetl.core.resource.ClasspathResourceLoader">
<bean id="resourceLoader" class="org.beetl.core.resource.ClasspathResourceLoader"> <!--指定加载模板资源的位置 指定在 classpath:beetl 下-->
<constructor-arg name="root" value="beetl"/> <constructor-arg name="root" value="beetl"/>
</bean> </bean>
<!--beetl 配置 这里采用默认的配置--> <!--beetl 配置 这里采用默认的配置-->
@ -102,9 +103,9 @@ public class SpringMail {
/** /**
* 发送简单邮件 * 发送简单邮件
* 发送简单邮件 * 在 qq 邮件发送的测试中,测试结果表明不管是简单邮件还是复杂邮件都必须指定发送用户,
* 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user * 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user
* 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user * qq 的授权码 可以在 设置/账户/POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV 服务 中开启服务后获取
*/ */
public void sendTextMessage(String from, String authWord, String to, String subject, String content) { public void sendTextMessage(String from, String authWord, String to, String subject, String content) {
// 设置发送人邮箱和授权码 // 设置发送人邮箱和授权码
@ -137,7 +138,7 @@ public class SpringMail {
qqMailSender.setPassword(authWord); qqMailSender.setPassword(authWord);
// 实例化消息对象 // 实例化消息对象
MimeMessage message = qqMailSender.createMimeMessage(); MimeMessage message = qqMailSender.createMimeMessage();
MimeMessage message = qqMailSender.createMimeMessage(); // 需要指定第二个参数为 true 代表创建支持可选文本,内联元素和附件的多部分消息
MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
helper.setFrom(from); helper.setFrom(from);
helper.setTo(to); helper.setTo(to);
@ -168,12 +169,12 @@ public class SpringMail {
qqMailSender.setPassword(authWord); qqMailSender.setPassword(authWord);
// 实例化消息对象 // 实例化消息对象
MimeMessage message = qqMailSender.createMimeMessage(); MimeMessage message = qqMailSender.createMimeMessage();
MimeMessage message = qqMailSender.createMimeMessage(); // 需要指定第二个参数为 true 代表创建支持可选文本,内联元素和附件的多部分消息
MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
helper.setFrom(from); helper.setFrom(from);
helper.setTo(to); helper.setTo(to);
helper.setSubject(subject); helper.setSubject(subject);
helper.setSubject(subject); // 使用 true 标志来指示包含的文本是 HTML 固定格式资源前缀 cid:
helper.setText("<html><body><img src='cid:image'></body></html>", true); helper.setText("<html><body><img src='cid:image'></body></html>", true);
// 需要先指定文本 再指定资源文件 // 需要先指定文本 再指定资源文件
FileSystemResource res = new FileSystemResource(file); FileSystemResource res = new FileSystemResource(file);
@ -207,7 +208,7 @@ public class SpringMail {
helper.setFrom(from); helper.setFrom(from);
helper.setTo(to); helper.setTo(to);
helper.setSubject(subject); helper.setSubject(subject);
helper.setSubject(subject); // 为 true 时候 表示文本内容以 html 渲染
helper.setText(text, true); helper.setText(text, true);
this.qqMailSender.send(message); this.qqMailSender.send(message);
System.out.println("发送邮件成功"); System.out.println("发送邮件成功");
@ -225,9 +226,9 @@ public class SpringMail {
- 模板引擎最主要的作用是,在对邮件格式有要求的时候,采用拼接字符串不够直观,所以采用模板引擎; - 模板引擎最主要的作用是,在对邮件格式有要求的时候,采用拼接字符串不够直观,所以采用模板引擎;
- 这里我们使用的 beetl 模板引擎,原因是其性能优异,官网是介绍其性能 6 倍与 freemaker,并有完善的文档支持。当然大家也可以换成任何其他的模板引擎freemarker,thymeleaf
一个简单的模板 template.html 如下:
```html ```html
<!doctype html> <!doctype html>
@ -271,7 +272,7 @@ public class SendEmail {
*/ */
@Test @Test
public void sendMessage() { public void sendMessage() {
public void sendMessage() { springMail.sendTextMessage(from, authWord, to, "spring 简单邮件", "Hello Spring Email!");
} }
/** /**
@ -280,9 +281,9 @@ public class SendEmail {
@Test @Test
public void sendComplexMessage() { public void sendComplexMessage() {
Map<String, File> fileMap = new HashMap<>(); Map<String, File> fileMap = new HashMap<>();
Map<String, File> fileMap = new HashMap<>(); fileMap.put("image1.jpg", new File("D:\\LearningNotes\\picture\\msm 相关依赖.png"));
fileMap.put("image1.jpg", new File("D:\\LearningNotes\\picture\\msm相关依赖.png")); fileMap.put("image2.jpg", new File("D:\\LearningNotes\\picture\\RabbitMQ 模型架构.png"));
fileMap.put("image2.jpg", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png")); springMail.sendEmailWithAttachments(from, authWord, to, "spring 多附件邮件"
, "Hello Spring Email!", fileMap); , "Hello Spring Email!", fileMap);
} }
@ -291,8 +292,8 @@ public class SendEmail {
*/ */
@Test @Test
public void sendEmailWithInline() { public void sendEmailWithInline() {
public void sendEmailWithInline() { springMail.sendEmailWithInline(from, authWord, to, "spring 内嵌资源邮件"
springMail.sendEmailWithInline(from, authWord, to, "spring内嵌资源邮件" , "Hello Spring Email!", new File("D:\\LearningNotes\\picture\\RabbitMQ 模型架构.png"));
} }
/** /**
@ -301,7 +302,7 @@ public class SendEmail {
@Test @Test
public void sendEmailByTemplate() { public void sendEmailByTemplate() {
springMail.sendEmailByTemplate(from, authWord, to, springMail.sendEmailByTemplate(from, authWord, to,
springMail.sendEmailByTemplate(from, authWord, to, "spring 模板邮件", "Hello Spring Email!");
} }
} }

View File

@ -15,7 +15,7 @@
#### 1.1 项目目录结构 #### 1.1 项目目录结构
1. 数据源配置位于config目录下的DatabaseConfig.javaDataSourceConfig.java 1. 数据源配置位于 config 目录下的 DatabaseConfig.javaDataSourceConfig.java
2. 项目以单元测试的方法进行测试 2. 项目以单元测试的方法进行测试
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-jdbc-annotation.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-jdbc-annotation.png"/> </div>
@ -59,7 +59,7 @@ mysql.password=root
```properties ```properties
# oracle 数据库配置 # oracle 数据库配置
oracle.driverClassName=oracle.jdbc.driver.OracleDriver oracle.driverClassName=oracle.jdbc.driver.OracleDriver
oracle.url=jdbc:oracle:thin:@//IP地址:端口号/数据库实例名 oracle.url=jdbc:oracle:thin:@//IP 地址:端口号/数据库实例名
oracle.username=用户名 oracle.username=用户名
oracle.password=密码 oracle.password=密码
``` ```
@ -87,7 +87,7 @@ public class DataSourceConfig {
```java ```java
@Configuration @Configuration
@EnableTransactionManagement // 开启声明式事务处理 等价于xml中<tx:annotation-driven/> @EnableTransactionManagement // 开启声明式事务处理 等价于 xml 中<tx:annotation-driven/>
@ComponentScan(basePackages = {"com.heibaiying.*"}) @ComponentScan(basePackages = {"com.heibaiying.*"})
public class DatabaseConfig { public class DatabaseConfig {
@ -106,9 +106,9 @@ public class DatabaseConfig {
/** /**
* 配置jdbcTemplate * 配置 jdbcTemplate
* @param dataSource 这个参数的名称需要保持和上面方法名一致 才能自动注入,因为 * @param dataSource 这个参数的名称需要保持和上面方法名一致 才能自动注入,因为
* 采用@Bean注解生成的bean 默认采用方法名为名称,当然也可以在使用@Bean时指定name属性 * 采用@Bean 注解生成的 bean 默认采用方法名为名称,当然也可以在使用@Bean 时指定 name 属性
*/ */
@Bean @Bean
public JdbcTemplate jdbcTemplate(DriverManagerDataSource dataSource) { public JdbcTemplate jdbcTemplate(DriverManagerDataSource dataSource) {
@ -141,7 +141,7 @@ public class MysqlDaoImpl implements MysqlDao {
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;
/** /**
* 更多JDBC 的使用可以参考官方文档 * 更多 JDBC 的使用可以参考官方文档
* @see <a href="https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#jdbc-JdbcTemplate">JdbcTemplate</a> * @see <a href="https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#jdbc-JdbcTemplate">JdbcTemplate</a>
*/ */
public List<Relation> get() { public List<Relation> get() {
@ -171,7 +171,7 @@ public class OracleDaoImpl implements OracleDao {
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;
/** /**
* 更多JDBC 的使用可以参考官方文档 * 更多 JDBC 的使用可以参考官方文档
* @see <a href="https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#jdbc-JdbcTemplate">JdbcTemplate</a> * @see <a href="https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#jdbc-JdbcTemplate">JdbcTemplate</a>
*/ */
public List<Flow> get() { public List<Flow> get() {

View File

@ -55,7 +55,7 @@ mysql.password=root
# oracle 数据库配置 # oracle 数据库配置
oracle.driverClassName=oracle.jdbc.driver.OracleDriver oracle.driverClassName=oracle.jdbc.driver.OracleDriver
oracle.url=jdbc:oracle:thin:@//IP地址:端口号/数据库实例名 oracle.url=jdbc:oracle:thin:@//IP 地址:端口号/数据库实例名
oracle.username=用户名 oracle.username=用户名
oracle.password=密码 oracle.password=密码
``` ```
@ -102,7 +102,7 @@ oracle.password=密码
<property name="dataSource" ref="dataSource"/> <property name="dataSource" ref="dataSource"/>
</bean> </bean>
<!-- 开启事务注解@Transactional支持 --> <!-- 开启事务注解@Transactional 支持 -->
<tx:annotation-driven/> <tx:annotation-driven/>
</beans> </beans>
@ -118,7 +118,7 @@ public class MysqlDaoImpl implements MysqlDao {
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;
/** /**
* 更多JDBC 的使用可以参考官方文档 * 更多 JDBC 的使用可以参考官方文档
* @see <a href="https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#jdbc-JdbcTemplate">JdbcTemplate</a> * @see <a href="https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#jdbc-JdbcTemplate">JdbcTemplate</a>
*/ */
public List<Relation> get() { public List<Relation> get() {
@ -146,7 +146,7 @@ public class OracleDaoImpl implements OracleDao {
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;
/** /**
* 更多JDBC 的使用可以参考官方文档 * 更多 JDBC 的使用可以参考官方文档
* @see <a href="https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#jdbc-JdbcTemplate">JdbcTemplate</a> * @see <a href="https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#jdbc-JdbcTemplate">JdbcTemplate</a>
*/ */
public List<Flow> get() { public List<Flow> get() {

View File

@ -1,5 +1,6 @@
# spring 整合 mecached注解方式 # spring 整合 mecached注解方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--XMemcached客户端说明">1.1 XMemcached客户端说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--XMemcached客户端说明">1.1 XMemcached客户端说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目结构说明">1.2 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目结构说明">1.2 项目结构说明</a><br/>
@ -17,17 +18,17 @@
### 1.1 XMemcached客户端说明 ### 1.1 XMemcached客户端说明
XMemcached 是基于 java nio 的 memcached 高性能客户端,支持完整的 memcached 协议,支持客户端分布并且提供了一致性哈希 (consistent hash) 算法的实现。
### 1.2 项目结构说明 ### 1.2 项目结构说明
1. memcached 的整合配置位于 com.heibaiying.config 文件夹下。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-memcached-annotation.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-memcached-annotation.png"/> </div>
### 1.3 依赖说明 ### 1.3 依赖说明
除了 spring 的基本依赖外,需要导入 xmemcached 依赖包
```xml ```xml
<!--memcached java 客户端--> <!--memcached java 客户端-->
@ -74,7 +75,7 @@ public MemcachedClient memcachedClientForCluster() {
builder.setConnectionPoolSize(10); builder.setConnectionPoolSize(10);
// 协议工厂 // 协议工厂
builder.setCommandFactory(new TextCommandFactory()); builder.setCommandFactory(new TextCommandFactory());
builder.setCommandFactory(new TextCommandFactory()); // 分布策略,一致性哈希 KetamaMemcachedSessionLocator 或者 ArraySessionLocator(默认)
builder.setSessionLocator(new KetamaMemcachedSessionLocator()); builder.setSessionLocator(new KetamaMemcachedSessionLocator());
// 设置序列化器 // 设置序列化器
builder.setTranscoder(new SerializingTranscoder()); builder.setTranscoder(new SerializingTranscoder());
@ -90,7 +91,7 @@ public MemcachedClient memcachedClientForCluster() {
#### 2.3 存储基本类型测试用例 #### 2.3 存储基本类型测试用例
xmemcached 单机版本和集群版本注入的实例是相同的;
```java ```java
/** /**
@ -150,14 +151,14 @@ public class MemObjectSamples {
| 命令 | 格式 | 说明 | | 命令 | 格式 | 说明 |
| --------------- | -------------------------------------------------- | ------------------------------------- | | --------------- | -------------------------------------------------- | ------------------------------------- |
| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 | | 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 |
| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 | | 新增 add | add key flags exTime length -> value | 只有当 key 不存在的情况下,才可以插入 |
| 新增 add | add key flags exTime length -> value | 只有当key不存在的情况下才可以插入 | | 替换 replace | replace key flags exTime length -> value | 只修改已存在 key 的 value 值 |
| 替换 replace | replace key flags exTime length -> value | 只修改已存在key的value值 | | 追加内容 append | append key flags exTime length -> value | length 表示追加的长度而不是总长度 |
| 追加内容append | append key flags exTime length -> value | length表示追加的长度而不是总长度 | | 前面追加 prepend | prepend key flags exTime length -> value | length 表示追加的长度而不是总长度 |
| 查询操作 get | get key | | | 查询操作 get | get key | |
| 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 | | 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 |
| 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 | | 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 |
| 删除 delete | delete key | 将数据打一个删除标记 | | 删除 delete | delete key | 将数据打一个删除标记 |
| 删除 delete | delete key | 将数据打一个删除标记 | | 自增 incr | incr key 增加偏移量 | incr 和 decr 只能操作能转换为数字的 Value |
| 自增 incr | incr key 增加偏移量 | incr和decr只能操作能转换为数字的Value | | 自减 decr | decr key 减少偏移量 | desr 不能将数字减少至 0 以下 |
| 清库 | flush_all | | | 清库 | flush_all | |

View File

@ -1,5 +1,6 @@
# spring 整合 mecachedxml配置方式 # spring 整合 mecachedxml配置方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--XMemcached客户端说明">1.1 XMemcached客户端说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--XMemcached客户端说明">1.1 XMemcached客户端说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目结构说明">1.2 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目结构说明">1.2 项目结构说明</a><br/>
@ -17,16 +18,16 @@
### 1.1 XMemcached客户端说明 ### 1.1 XMemcached客户端说明
XMemcached 是基于 java nio 的 memcached 高性能客户端,支持完整的 memcached 协议,支持客户端分布并且提供了一致性哈希 (consistent hash) 算法的实现。
### 1.2 项目结构说明 ### 1.2 项目结构说明
1. memcached 的整合配置位于 resources 下的 memcached 文件夹下,其中集群配置用 cluster 开头。所有配置按照需要在 springApplication.xml 用 import 导入。
1. memcached的整合配置位于resources下的memcached文件夹下其中集群配置用cluster开头。所有配置按照需要在springApplication.xml用import导入。 2. 实体类 Programmer.java 用于测试 memcached 序列化与反序列化
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-memcached.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-memcached.png"/> </div>
**springapplication.xml 文件:**
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -45,7 +46,7 @@ XMemcached是基于java nio的memcached高性能客户端支持完整的memca
### 1.3 依赖说明 ### 1.3 依赖说明
除了 spring 的基本依赖外,需要导入 xmemcached 依赖包
```xml ```xml
<!--memcached java 客户端--> <!--memcached java 客户端-->
@ -100,7 +101,7 @@ XMemcached是基于java nio的memcached高性能客户端支持完整的memca
</bean> </bean>
</list> </list>
</constructor-arg> </constructor-arg>
</constructor-arg> <!--与 servers 对应的节点的权重-->
<constructor-arg name="weights"> <constructor-arg name="weights">
<list> <list>
<value>1</value> <value>1</value>
@ -113,16 +114,16 @@ XMemcached是基于java nio的memcached高性能客户端支持完整的memca
<property name="commandFactory"> <property name="commandFactory">
<bean class="net.rubyeye.xmemcached.command.TextCommandFactory"/> <bean class="net.rubyeye.xmemcached.command.TextCommandFactory"/>
</property> </property>
</property> <!--分布策略,一致性哈希 net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator 或者 ArraySessionLocator(默认)-->
<property name="sessionLocator"> <property name="sessionLocator">
<bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"/> <bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"/>
</property> </property>
</property> <!--序列化转换器,默认使用 net.rubyeye.xmemcached.transcoders.SerializingTranscoder-->
<property name="transcoder"> <property name="transcoder">
<bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder"/> <bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder"/>
</property> </property>
</bean> </bean>
</bean> <!-- 集群配置 实例化 bean -->
<bean name="memcachedClientForCulster" factory-bean="memcachedClientBuilder" <bean name="memcachedClientForCulster" factory-bean="memcachedClientBuilder"
factory-method="build" destroy-method="shutdown"/> factory-method="build" destroy-method="shutdown"/>
</beans> </beans>
@ -130,7 +131,7 @@ XMemcached是基于java nio的memcached高性能客户端支持完整的memca
#### 2.3 存储基本类型测试用例 #### 2.3 存储基本类型测试用例
xmemcached 单机版本和集群版本注入的实例是相同的;
```java ```java
/** /**
@ -191,14 +192,14 @@ public class MemObjectSamples {
| 命令 | 格式 | 说明 | | 命令 | 格式 | 说明 |
| --------------- | -------------------------------------------------- | ------------------------------------- | | --------------- | -------------------------------------------------- | ------------------------------------- |
| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 | | 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 |
| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 | | 新增 add | add key flags exTime length -> value | 只有当 key 不存在的情况下,才可以插入 |
| 新增 add | add key flags exTime length -> value | 只有当key不存在的情况下才可以插入 | | 替换 replace | replace key flags exTime length -> value | 只修改已存在 key 的 value 值 |
| 替换 replace | replace key flags exTime length -> value | 只修改已存在key的value值 | | 追加内容 append | append key flags exTime length -> value | length 表示追加的长度而不是总长度 |
| 追加内容append | append key flags exTime length -> value | length表示追加的长度而不是总长度 | | 前面追加 prepend | prepend key flags exTime length -> value | length 表示追加的长度而不是总长度 |
| 查询操作 get | get key | | | 查询操作 get | get key | |
| 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 | | 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 |
| 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 | | 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 |
| 删除 delete | delete key | 将数据打一个删除标记 | | 删除 delete | delete key | 将数据打一个删除标记 |
| 删除 delete | delete key | 将数据打一个删除标记 | | 自增 incr | incr key 增加偏移量 | incr 和 decr 只能操作能转换为数字的 Value |
| 自增 incr | incr key 增加偏移量 | incr和decr只能操作能转换为数字的Value | | 自减 decr | decr key 减少偏移量 | desr 不能将数字减少至 0 以下 |
| 清库 | flush_all | | | 清库 | flush_all | |

View File

@ -1,5 +1,6 @@
# spring 整合 mongodb注解方式 # spring 整合 mongodb注解方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
@ -14,7 +15,7 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
配置文件位于 com.heibaiying.config 下,项目以单元测试的方式进行测试。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mongodb-annotation.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mongodb-annotation.png"/> </div>
@ -22,7 +23,7 @@
### 1.2 依赖说明 ### 1.2 依赖说明
除了 spring 的基本依赖外,需要导入 mongodb 整合依赖包
```xml ```xml
<!--spring mongodb 整合依赖--> <!--spring mongodb 整合依赖-->

View File

@ -1,5 +1,6 @@
# spring 整合 mongodbxml配置方式 # spring 整合 mongodbxml配置方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
@ -14,7 +15,7 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
配置文件位于 resources 下,项目以单元测试的方式进行测试。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mongodb.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mongodb.png"/> </div>
@ -22,7 +23,7 @@
### 1.2 依赖说明 ### 1.2 依赖说明
除了 spring 的基本依赖外,需要导入 mongodb 整合依赖包
```xml ```xml
<!--spring mongodb 整合依赖--> <!--spring mongodb 整合依赖-->
@ -76,7 +77,7 @@ mongo.socketTimeout=1500
<!--扫描配置文件--> <!--扫描配置文件-->
<context:property-placeholder location="classpath:mongodb.properties"/> <context:property-placeholder location="classpath:mongodb.properties"/>
<!--定义用于访问 MongoDB 的 MongoClient 实例-->
<mongo:mongo-client host="${mongo.host}" port="${mongo.port}"> <mongo:mongo-client host="${mongo.host}" port="${mongo.port}">
<mongo:client-options <mongo:client-options
connections-per-host="${mongo.connectionsPerHost}" connections-per-host="${mongo.connectionsPerHost}"
@ -91,7 +92,7 @@ mongo.socketTimeout=1500
<!--定义用于连接到数据库的连接工厂--> <!--定义用于连接到数据库的连接工厂-->
<mongo:db-factory dbname="${mongo.dbname}" mongo-ref="mongoClient"/> <mongo:db-factory dbname="${mongo.dbname}" mongo-ref="mongoClient"/>
<!--实际操作 mongodb 的 template,在代码中注入-->
<bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> <bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/> <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
</bean> </bean>

View File

@ -1,5 +1,6 @@
# spring 整合 mybatis注解方式 # spring 整合 mybatis注解方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目依赖">1.2 项目依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目依赖">1.2 项目依赖</a><br/>
@ -20,7 +21,7 @@
#### 1.2 项目依赖 #### 1.2 项目依赖
除了 spring 相关依赖外,还需要导入数据库驱动和对应的 mybatis 依赖包
```xml ```xml
<!--jdbc 相关依赖包--> <!--jdbc 相关依赖包-->
@ -65,7 +66,7 @@ mysql.password=root
# oracle 数据库配置 # oracle 数据库配置
oracle.driverClassName=oracle.jdbc.driver.OracleDriver oracle.driverClassName=oracle.jdbc.driver.OracleDriver
oracle.driverClassName=oracle.jdbc.driver.OracleDriver oracle.url=jdbc:oracle:thin:@//IP 地址:端口号/数据库实例名
oracle.username=用户名 oracle.username=用户名
oracle.password=密码 oracle.password=密码
``` ```
@ -95,14 +96,14 @@ public class DataSourceConfig {
* @author : heibaiying * @author : heibaiying
*/ */
@Configuration @Configuration
@Configuration @EnableTransactionManagement // 开启声明式事务处理 等价于 xml 中<tx:annotation-driven/>
@ComponentScan(basePackages = {"com.heibaiying.*"}) @ComponentScan(basePackages = {"com.heibaiying.*"})
public class DatabaseConfig { public class DatabaseConfig {
/* @Autowired /* @Autowired
* private DataSourceConfig sourceConfig; * private DataSourceConfig sourceConfig;
* private DataSourceConfig sourceConfig; * 不要采用这种方式注入 DataSourceConfig,由于类的加载顺序影响会报空指针异常
* 不要采用这种方式注入DataSourceConfig,由于类的加载顺序影响会报空指针异常 * 最好的方式是在 DriverManagerDataSource 构造中采用参数注入
*/ */
/** /**
@ -119,10 +120,10 @@ public class DatabaseConfig {
} }
/** /**
/** * 配置 mybatis 会话工厂
* *
* @param dataSource 这个参数的名称需要保持和上面方法名一致 才能自动注入,因为 * @param dataSource 这个参数的名称需要保持和上面方法名一致 才能自动注入,因为
* @param dataSource 这个参数的名称需要保持和上面方法名一致 才能自动注入,因为 * 采用@Bean 注解生成的 bean 默认采用方法名为名称,当然也可以在使用@Bean 时指定 name 属性
*/ */
@Bean @Bean
public SqlSessionFactoryBean sessionFactoryBean(DriverManagerDataSource dataSource) throws IOException { public SqlSessionFactoryBean sessionFactoryBean(DriverManagerDataSource dataSource) throws IOException {
@ -135,7 +136,7 @@ public class DatabaseConfig {
} }
/** /**
/** * 配置 mybatis 会话工厂
*/ */
@Bean @Bean
public MapperScannerConfigurer MapperScannerConfigurer() { public MapperScannerConfigurer MapperScannerConfigurer() {
@ -172,7 +173,7 @@ public class DatabaseConfig {
<settings> <settings>
<!-- 开启驼峰命名 --> <!-- 开启驼峰命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 打印查询 sql -->
<setting name="logImpl" value="STDOUT_LOGGING"/> <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings> </settings>

View File

@ -1,5 +1,6 @@
# spring 整合 mybatisxml配置方式 # spring 整合 mybatisxml配置方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目依赖">1.2 项目依赖</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目依赖">1.2 项目依赖</a><br/>
@ -20,7 +21,7 @@
#### 1.2 项目依赖 #### 1.2 项目依赖
除了 spring 相关依赖外,还需要导入数据库驱动和对应的 mybatis 依赖包
```xml ```xml
<!--jdbc 相关依赖包--> <!--jdbc 相关依赖包-->
@ -65,7 +66,7 @@ mysql.password=root
# oracle 数据库配置 # oracle 数据库配置
oracle.driverClassName=oracle.jdbc.driver.OracleDriver oracle.driverClassName=oracle.jdbc.driver.OracleDriver
oracle.driverClassName=oracle.jdbc.driver.OracleDriver oracle.url=jdbc:oracle:thin:@//IP 地址:端口号/数据库实例名
oracle.username=用户名 oracle.username=用户名
oracle.password=密码 oracle.password=密码
``` ```
@ -103,7 +104,7 @@ oracle.password=密码
<!--配置 mybatis 会话工厂 --> <!--配置 mybatis 会话工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/> <property name="dataSource" ref="dataSource"/>
<property name="dataSource" ref="dataSource"/> <!--指定 mapper 文件所在的位置-->
<property name="mapperLocations" value="classpath*:/mappers/**/*.xml"/> <property name="mapperLocations" value="classpath*:/mappers/**/*.xml"/>
<property name="configLocation" value="classpath:mybatisConfig.xml"/> <property name="configLocation" value="classpath:mybatisConfig.xml"/>
</bean> </bean>
@ -113,7 +114,7 @@ oracle.password=密码
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定会话工厂 --> <!--指定会话工厂 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <!-- 指定 mybatis 接口所在的包 -->
<property name="basePackage" value="com.heibaiying.dao"/> <property name="basePackage" value="com.heibaiying.dao"/>
</bean> </bean>
@ -124,7 +125,7 @@ oracle.password=密码
<property name="dataSource" ref="dataSource"/> <property name="dataSource" ref="dataSource"/>
</bean> </bean>
<!-- 开启事务注解@Transactional 支持 -->
<tx:annotation-driven/> <tx:annotation-driven/>
</beans> </beans>
@ -143,7 +144,7 @@ oracle.password=密码
<settings> <settings>
<!-- 开启驼峰命名 --> <!-- 开启驼峰命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 打印查询 sql -->
<setting name="logImpl" value="STDOUT_LOGGING"/> <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings> </settings>

View File

@ -1,5 +1,6 @@
# spring 整合 rabbitmq注解方式 # spring 整合 rabbitmq注解方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
@ -20,13 +21,13 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
1. 本用例关于 rabbitmq 的整合提供**简单消息发送**和**对象消费发送**两种情况下的 sample。
2. rabbitBaseAnnotation.java 中声明了 topic 类型的交换机、持久化队列、及其绑定关系,用于测试说明 topic 交换机路由键的绑定规则。
3. rabbitObjectAnnotation.java 中声明了 direct 类型的交换机,持久化队列,及其绑定关系,用于示例对象消息的传输。
注:关于 rabbitmq 安装、交换机、队列、死信队列等基本概念可以参考我的手记[《RabbitMQ 实战指南》读书笔记](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/RabbitMQ/%E3%80%8ARabbitMQ%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.md),里面有详细的配图说明。
@ -36,7 +37,7 @@
### 1.2 依赖说明 ### 1.2 依赖说明
除了 spring 的基本依赖外,需要导入 spring rabbitmq 整合依赖
```xml ```xml
<!--spring rabbitmq 整合依赖--> <!--spring rabbitmq 整合依赖-->
@ -115,7 +116,7 @@ public class RabbitBaseConfig {
/** /**
* 创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。 * 创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。
* 创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定 * auto-startup 指定是否自动声明上下文中的队列,交换和绑定, 默认值为 true
*/ */
@Bean @Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) { public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
@ -164,19 +165,19 @@ public class RabbitBaseAnnotation {
@Bean @Bean
public Queue firstQueue() { public Queue firstQueue() {
public Queue firstQueue() { // 创建一个持久化的队列 1
return new Queue("FirstQueue", true); return new Queue("FirstQueue", true);
} }
@Bean @Bean
public Queue secondQueue() { public Queue secondQueue() {
public Queue secondQueue() { // 创建一个持久化的队列 2
return new Queue("SecondQueue", true); return new Queue("SecondQueue", true);
} }
/** /**
* BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词 * BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词
* BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词 * 这里我们声明三个绑定关系用于测试 topic 这种类型交换器
*/ */
@Bean @Bean
public Binding orange() { public Binding orange() {
@ -194,7 +195,7 @@ public class RabbitBaseAnnotation {
} }
/*创建队列 1 消费者监听*/
@Bean @Bean
public SimpleMessageListenerContainer firstQueueLister(ConnectionFactory connectionFactory) { public SimpleMessageListenerContainer firstQueueLister(ConnectionFactory connectionFactory) {
@ -217,7 +218,7 @@ public class RabbitBaseAnnotation {
System.out.println(firstQueue().getName() + "收到消息:" + new String(body)); System.out.println(firstQueue().getName() + "收到消息:" + new String(body));
/* /*
* DeliveryTag 是一个单调递增的整数 * DeliveryTag 是一个单调递增的整数
* DeliveryTag 是一个单调递增的整数 * 第二个参数 代表是否一次签收多条,如果设置为 true,则所有 DeliveryTag 小于该 DeliveryTag 的消息都会被签收
*/ */
channel.basicAck(properties.getDeliveryTag(), false); channel.basicAck(properties.getDeliveryTag(), false);
} }
@ -226,7 +227,7 @@ public class RabbitBaseAnnotation {
} }
/*创建队列 2 消费者监听*/
@Bean @Bean
public SimpleMessageListenerContainer secondQueueLister(ConnectionFactory connectionFactory) { public SimpleMessageListenerContainer secondQueueLister(ConnectionFactory connectionFactory) {
@ -264,15 +265,15 @@ public class RabbitTest {
public void sendMessage() { public void sendMessage() {
MessageProperties properties = new MessageProperties(); MessageProperties properties = new MessageProperties();
String allReceived = "我的路由键 quick.orange.rabbit 符合 queue1 和 queue2 的要求,我应该被两个监听器接收到";
Message message1 = new Message(allReceived.getBytes(), properties); Message message1 = new Message(allReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "quick.orange.rabbit", message1); rabbitTemplate.send("topic01", "quick.orange.rabbit", message1);
String firstReceived = "我的路由键 quick.orange.fox 只符合 queue1 的要求,只能被 queue 1 接收到";
Message message2 = new Message(firstReceived.getBytes(), properties); Message message2 = new Message(firstReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "quick.orange.fox", message2); rabbitTemplate.send("topic01", "quick.orange.fox", message2);
String secondReceived = "我的路由键 lazy.brown.fox 只符合 queue2 的要求,只能被 queue 2 接收到";
Message message3 = new Message(secondReceived.getBytes(), properties); Message message3 = new Message(secondReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "lazy.brown.fox", message3); rabbitTemplate.send("topic01", "lazy.brown.fox", message3);
@ -285,10 +286,10 @@ public class RabbitTest {
```java ```java
结果: 结果:
结果: SecondQueue 收到消息:我的路由键 quick.orange.rabbit 符合 queue1 queue2 的要求我应该被两个监听器接收到
SecondQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 queue2 的要求我应该被两个监听器接收到 FirstQueue 收到消息:我的路由键 quick.orange.rabbit 符合 queue1 queue2 的要求我应该被两个监听器接收到
FirstQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 queue2 的要求我应该被两个监听器接收到 FirstQueue 收到消息:我的路由键 quick.orange.fox 符合 queue1 的要求只能被 queue 1 接收到
FirstQueue收到消息:我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到 SecondQueue 收到消息:我的路由键 lazy.brown.fox 只符合 queue2 的要求只能被 queue 2 接收到
``` ```
@ -297,7 +298,7 @@ public class RabbitTest {
#### 4.1 创建消息的委托处理器 #### 4.1 创建消息的委托处理器
这里为了增强用例的实用性,我们创建的处理器的 handleMessage 方法是一个重载方法,对于同一个队列的监听,不仅可以传输对象消息,同时针对不同的对象类型调用不同的处理方法。
```java ```java
/** /**
@ -355,7 +356,7 @@ public class RabbitObjectAnnotation {
container.setQueues(objectQueue()); container.setQueues(objectQueue());
// 将监听到的消息委派给实际的处理类 // 将监听到的消息委派给实际的处理类
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate()); MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate()); // 指定由哪个方法来处理消息 默认就是 handleMessage
adapter.setDefaultListenerMethod("handleMessage"); adapter.setDefaultListenerMethod("handleMessage");
// 消息转换 // 消息转换
@ -391,12 +392,12 @@ public class RabbitSendObjectTest {
@Test @Test
public void sendProgrammer() throws JsonProcessingException { public void sendProgrammer() throws JsonProcessingException {
MessageProperties messageProperties = new MessageProperties(); MessageProperties messageProperties = new MessageProperties();
MessageProperties messageProperties = new MessageProperties(); //必须设置 contentType 为 application/json
messageProperties.setContentType("application/json"); messageProperties.setContentType("application/json");
// 必须指定类型 // 必须指定类型
messageProperties.getHeaders().put("__TypeId__", Type.PROGRAMMER); messageProperties.getHeaders().put("__TypeId__", Type.PROGRAMMER);
Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date()); Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date());
Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date()); // 序列化与反序列化都使用的 Jackson
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
String programmerJson = mapper.writeValueAsString(programmer); String programmerJson = mapper.writeValueAsString(programmer);
Message message = new Message(programmerJson.getBytes(), messageProperties); Message message = new Message(programmerJson.getBytes(), messageProperties);

View File

@ -1,5 +1,6 @@
# spring 整合 rabbitmqxml配置方式 # spring 整合 rabbitmqxml配置方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
@ -18,13 +19,13 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
1. 本用例关于 rabbitmq 的整合提供**简单消息发送**和**对象消费发送**两种情况下的 sample。
2. rabbitBaseAnnotation.java 中声明了 topic 类型的交换机、持久化队列、及其绑定关系,用于测试说明 topic 交换机路由键的绑定规则。
3. rabbitObjectAnnotation.java 中声明了 direct 类型的交换机,持久化队列,及其绑定关系,用于示例对象消息的传输。
注:关于 rabbitmq 安装、交换机、队列、死信队列等基本概念可以参考我的手记[《RabbitMQ 实战指南》读书笔记](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/RabbitMQ/%E3%80%8ARabbitMQ%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.md),里面有详细的配图说明。
@ -34,7 +35,7 @@
### 1.2 依赖说明 ### 1.2 依赖说明
除了 spring 的基本依赖外,需要导入 spring rabbitmq 整合依赖
```xml ```xml
<!--spring rabbitmq 整合依赖--> <!--spring rabbitmq 整合依赖-->
@ -87,14 +88,14 @@ rabbitmq.virtualhost=/
virtual-host="${rabbitmq.virtualhost}"/> virtual-host="${rabbitmq.virtualhost}"/>
<!--创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。 <!--创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。
<!--创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。 auto-startup 指定是否自动声明上下文中的队列,交换和绑定, 默认值为 true。-->
<rabbit:admin connection-factory="connectionFactory" auto-startup="true"/> <rabbit:admin connection-factory="connectionFactory" auto-startup="true"/>
<!--声明 template 的时候需要声明 id 不然会抛出异常-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/> <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
<!--可以在 xml 采用如下方式声明交换机、队列、绑定管理 但是建议使用代码方式声明 方法更加灵活且可以采用链调用-->
<rabbit:queue name="remoting.queue"/> <rabbit:queue name="remoting.queue"/>
<rabbit:direct-exchange name="remoting.exchange"> <rabbit:direct-exchange name="remoting.exchange">
@ -103,7 +104,7 @@ rabbitmq.virtualhost=/
</rabbit:bindings> </rabbit:bindings>
</rabbit:direct-exchange> </rabbit:direct-exchange>
<!--扫描 rabbit 包 自动声明交换器、队列、绑定关系-->
<context:component-scan base-package="com.heibaiying.rabbit"/> <context:component-scan base-package="com.heibaiying.rabbit"/>
</beans> </beans>
@ -140,19 +141,19 @@ public class RabbitBaseAnnotation {
@Bean @Bean
public Queue firstQueue() { public Queue firstQueue() {
public Queue firstQueue() { // 创建一个持久化的队列 1
return new Queue("FirstQueue", true); return new Queue("FirstQueue", true);
} }
@Bean @Bean
public Queue secondQueue() { public Queue secondQueue() {
public Queue secondQueue() { // 创建一个持久化的队列 2
return new Queue("SecondQueue", true); return new Queue("SecondQueue", true);
} }
/** /**
* BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词 * BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词
* BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词 * 这里我们声明三个绑定关系用于测试 topic 这种类型交换器
*/ */
@Bean @Bean
public Binding orange() { public Binding orange() {
@ -170,7 +171,7 @@ public class RabbitBaseAnnotation {
} }
/*创建队列 1 消费者监听*/
@Bean @Bean
public SimpleMessageListenerContainer firstQueueLister(ConnectionFactory connectionFactory) { public SimpleMessageListenerContainer firstQueueLister(ConnectionFactory connectionFactory) {
@ -193,7 +194,7 @@ public class RabbitBaseAnnotation {
System.out.println(firstQueue().getName() + "收到消息:" + new String(body)); System.out.println(firstQueue().getName() + "收到消息:" + new String(body));
/* /*
* DeliveryTag 是一个单调递增的整数 * DeliveryTag 是一个单调递增的整数
* DeliveryTag 是一个单调递增的整数 * 第二个参数 代表是否一次签收多条,如果设置为 true,则所有 DeliveryTag 小于该 DeliveryTag 的消息都会被签收
*/ */
channel.basicAck(properties.getDeliveryTag(), false); channel.basicAck(properties.getDeliveryTag(), false);
} }
@ -202,7 +203,7 @@ public class RabbitBaseAnnotation {
} }
/*创建队列 2 消费者监听*/
@Bean @Bean
public SimpleMessageListenerContainer secondQueueLister(ConnectionFactory connectionFactory) { public SimpleMessageListenerContainer secondQueueLister(ConnectionFactory connectionFactory) {
@ -240,15 +241,15 @@ public class RabbitTest {
public void sendMessage() { public void sendMessage() {
MessageProperties properties = new MessageProperties(); MessageProperties properties = new MessageProperties();
String allReceived = "我的路由键 quick.orange.rabbit 符合 queue1 和 queue2 的要求,我应该被两个监听器接收到";
Message message1 = new Message(allReceived.getBytes(), properties); Message message1 = new Message(allReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "quick.orange.rabbit", message1); rabbitTemplate.send("topic01", "quick.orange.rabbit", message1);
String firstReceived = "我的路由键 quick.orange.fox 只符合 queue1 的要求,只能被 queue 1 接收到";
Message message2 = new Message(firstReceived.getBytes(), properties); Message message2 = new Message(firstReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "quick.orange.fox", message2); rabbitTemplate.send("topic01", "quick.orange.fox", message2);
String secondReceived = "我的路由键 lazy.brown.fox 只符合 queue2 的要求,只能被 queue 2 接收到";
Message message3 = new Message(secondReceived.getBytes(), properties); Message message3 = new Message(secondReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "lazy.brown.fox", message3); rabbitTemplate.send("topic01", "lazy.brown.fox", message3);
@ -261,10 +262,10 @@ public class RabbitTest {
```java ```java
结果: 结果:
结果: SecondQueue 收到消息:我的路由键 quick.orange.rabbit 符合 queue1 queue2 的要求我应该被两个监听器接收到
SecondQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 queue2 的要求我应该被两个监听器接收到 FirstQueue 收到消息:我的路由键 quick.orange.rabbit 符合 queue1 queue2 的要求我应该被两个监听器接收到
FirstQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 queue2 的要求我应该被两个监听器接收到 FirstQueue 收到消息:我的路由键 quick.orange.fox 符合 queue1 的要求只能被 queue 1 接收到
FirstQueue收到消息:我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到 SecondQueue 收到消息:我的路由键 lazy.brown.fox 只符合 queue2 的要求只能被 queue 2 接收到
``` ```
@ -273,7 +274,7 @@ public class RabbitTest {
#### 4.1 创建消息的委托处理器 #### 4.1 创建消息的委托处理器
这里为了增强用例的实用性,我们创建的处理器的 handleMessage 方法是一个重载方法,对于同一个队列的监听,不仅可以传输对象消息,同时针对不同的对象类型调用不同的处理方法。
```java ```java
/** /**
@ -331,7 +332,7 @@ public class RabbitObjectAnnotation {
container.setQueues(objectQueue()); container.setQueues(objectQueue());
// 将监听到的消息委派给实际的处理类 // 将监听到的消息委派给实际的处理类
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate()); MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate()); // 指定由哪个方法来处理消息 默认就是 handleMessage
adapter.setDefaultListenerMethod("handleMessage"); adapter.setDefaultListenerMethod("handleMessage");
// 消息转换 // 消息转换
@ -367,12 +368,12 @@ public class RabbitSendObjectTest {
@Test @Test
public void sendProgrammer() throws JsonProcessingException { public void sendProgrammer() throws JsonProcessingException {
MessageProperties messageProperties = new MessageProperties(); MessageProperties messageProperties = new MessageProperties();
MessageProperties messageProperties = new MessageProperties(); //必须设置 contentType 为 application/json
messageProperties.setContentType("application/json"); messageProperties.setContentType("application/json");
// 必须指定类型 // 必须指定类型
messageProperties.getHeaders().put("__TypeId__", Type.PROGRAMMER); messageProperties.getHeaders().put("__TypeId__", Type.PROGRAMMER);
Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date()); Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date());
Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date()); // 序列化与反序列化都使用的 Jackson
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
String programmerJson = mapper.writeValueAsString(programmer); String programmerJson = mapper.writeValueAsString(programmer);
Message message = new Message(programmerJson.getBytes(), messageProperties); Message message = new Message(programmerJson.getBytes(), messageProperties);

View File

@ -1,5 +1,6 @@
# spring 整合 redis (注解方式) # spring 整合 redis (注解方式)
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--Redis-客户端说明">1.1 Redis 客户端说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--Redis-客户端说明">1.1 Redis 客户端说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-Redis可视化软件">1.2 Redis可视化软件 </a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-Redis可视化软件">1.2 Redis可视化软件 </a><br/>
@ -44,12 +45,12 @@
### 1.1 Redis 客户端说明 ### 1.1 Redis 客户端说明
关于 spring 整合 mybatis 本用例提供两种整合方法:
1. jedis: 官方推荐的 java 客户端,能够胜任 redis 的大多数基本使用;
1. jedis: 官方推荐的java客户端能够胜任redis的大多数基本使用 2. redisson也是官方推荐的客户端比起 jedis 提供了更多高级的功能,比如分布式锁、集合数据切片等功能。同时提供了丰富而全面的中英文版本的 wiki。
注:关于 redis 其他语言官方推荐的客户端可以在[客户端](http://www.redis.cn/clients.html) 该网页查看,其中官方推荐的用了黄色星星:star:标注。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/redis官方推荐客户端.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/redis官方推荐客户端.png"/> </div>
@ -61,8 +62,8 @@
### 1.3 项目结构说明 ### 1.3 项目结构说明
1. jedis 和 redisson 的配置类和单元测试分别位于 config 和 test 下对应的包中,其中集群的配置类以 cluster 开头。
1. jedis和redisson的配置类和单元测试分别位于config和test下对应的包中其中集群的配置类以cluster开头。 2. 实体类 Programmer.java 用于测试 Redisson 序列化与反序列化
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-redis-annotation.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-redis-annotation.png"/> </div>
@ -70,7 +71,7 @@
### 1.3 依赖说明 ### 1.3 依赖说明
除了 spring 的基本依赖外,需要导入 jedis 和 redisson 对应的客户端依赖包
```xml ```xml
<dependency> <dependency>
@ -179,9 +180,9 @@ public class ClusterJedisConfig {
#### 2.4 单机版本测试用例 #### 2.4 单机版本测试用例
1.需要注意的是,对于 jedis 而言,单机版本和集群版本注入的实例是不同的;
2.jedis 本身并不支持序列化于反序列化操作,如果需要存储实体类,需要序列化后存入。(redisson 本身就支持序列化于反序列化,详见下文)
```java ```java
/** /**
@ -208,7 +209,7 @@ public class JedisSamples {
@Test @Test
public void setEx() { public void setEx() {
public void setEx() { String s = jedis.setex("spring", 10, "我会在 10 秒后过期");
System.out.println(s); System.out.println(s);
} }
@ -238,7 +239,7 @@ public class JedisClusterSamples {
@Test @Test
public void setEx() { public void setEx() {
public void setEx() { String s = jedisCluster.setex("spring", 10, "我会在 10 秒后过期");
System.out.println(s); System.out.println(s);
} }
@ -286,7 +287,7 @@ public class ClusterRedissonConfig {
Config config = new Config(); Config config = new Config();
config.useClusterServers() config.useClusterServers()
.setScanInterval(2000) // 集群状态扫描间隔时间,单位是毫秒 .setScanInterval(2000) // 集群状态扫描间隔时间,单位是毫秒
.setScanInterval(2000) // 集群状态扫描间隔时间,单位是毫秒 //可以用"rediss://"来启用 SSL 连接
.addNodeAddress("redis://127.0.0.1:6379", "redis://127.0.0.1:6380") .addNodeAddress("redis://127.0.0.1:6379", "redis://127.0.0.1:6380")
.addNodeAddress("redis://127.0.0.1:6381"); .addNodeAddress("redis://127.0.0.1:6381");
return Redisson.create(config); return Redisson.create(config);
@ -297,7 +298,7 @@ public class ClusterRedissonConfig {
#### 2.3 存储基本类型测试用例 #### 2.3 存储基本类型测试用例
1. 这里需要注意的是,对于 Redisson 而言, 单机和集群最后在使用的时候注入的都是 RedissonClient这和 jedis 是不同的。
```java ```java
/** /**
@ -359,7 +360,7 @@ public class RedissonObjectSamples {
@Autowired @Autowired
private RedissonClient redissonClient; private RedissonClient redissonClient;
// Redisson 的对象编码类是用于将对象进行序列化和反序列化 默认采用 Jackson
@Test @Test
public void Set() { public void Set() {
@ -398,9 +399,9 @@ public class RedissonObjectSamples {
5. 键过期:**expire key seconds** 5. 键过期:**expire key seconds**
ttl 命令会返回键的剩余过期时间, 它有 3 种返回值:
- 大于等于 0 的整数: 键剩余的过期时间。
- -1 键没设置过期时间。 - -1 键没设置过期时间。
- -2 键不存在 - -2 键不存在
@ -408,23 +409,23 @@ public class RedissonObjectSamples {
#### 1.1.2 数据结构和内部编码 #### 1.1.2 数据结构和内部编码
type 命令实际返回的就是当前键的数据结构类型, 它们分别是:**string**(字符串) 、 **hash**(哈希) 、 **list**(列表) 、 **set**(集合) 、 **zset**(有序集合)
#### 1.1.3 单线程架构 #### 1.1.3 单线程架构
1. 纯内存访问, Redis 将所有数据放在内存中, 内存的响应时长大约为 100 纳秒, 这是 Redis 达到每秒万级别访问的重要基础。
1. 纯内存访问, Redis将所有数据放在内存中 内存的响应时长大约为100纳秒 这是Redis达到每秒万级别访问的重要基础。 2. 非阻塞 I/O Redis 使用 epoll 作为 I/O 多路复用技术的实现, 再加上 Redis 自身的事件处理模型将 epoll 中的连接、 读写、 关闭都转换为事件, 不在网络 I/O 上浪费过多的时间, 如图 2-6 所示。
3. 单线程避免了线程切换和竞态产生的消耗。 3. 单线程避免了线程切换和竞态产生的消耗。
### 1.2 字符串 ### 1.2 字符串
| 作用 | 格式 | 参数或示例 | | 作用 | 格式 | 参数或示例 |
| ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | 设置值 | set key value \[ex seconds]\[px milliseconds][nx\|xx] setnx setex | ex seconds 为键设置秒级过期时间。 <br/>px milliseconds 为键设置毫秒级过期时间。<br/>nx 键必须不存在, 才可以设置成功, 用于添加。<br/>xx 与 nx 相反, 键必须存在, 才可以设置成功, 用于更新。 |
| 设置值 | set key value \[ex seconds]\[px milliseconds][nx\|xx] setnx setex | ex seconds 为键设置秒级过期时间。 <br/>px milliseconds 为键设置毫秒级过期时间。<br/>nx 键必须不存在, 才可以设置成功, 用于添加。<br/>xx 与nx相反 键必须存在, 才可以设置成功, 用于更新。 | | 获取值 | get key | r 如果获取的键不存在 ,则返回 nil(空) |
| 批量设置 | mset key value [key value ...] | mset a 1 b 2 c 3 d 4 | | 批量设置 | mset key value [key value ...] | mset a 1 b 2 c 3 d 4 |
| 批量获取值 | mget key [key ...] | mget a b c d | | 批量获取值 | mget key [key ...] | mget a b c d |
| 批量获取值 | mget key [key ...] | mget a b c d | | 计数 | incr key decr key incrby key increment指定数值自增<br/>decrby key decrement指定数值自减<br/>incrbyfloat key increment (浮点数自增) | 值不是整数, 返回错误。 值是整数, 返回自增或自减后的结果。<br/>键不存在,创建键,并按照值为 0 自增或自减, 返回结果为 1。 |
| 追加值 | append key value | 向字符串的默认追加值 | | 追加值 | append key value | 向字符串的默认追加值 |
| 字符串长度 | strlen key | 获取字符串长度,中文占用三个字节 | | 字符串长度 | strlen key | 获取字符串长度,中文占用三个字节 |
| 设置并返回原值 | getset key value | | | 设置并返回原值 | getset key value | |
@ -437,13 +438,13 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
| ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 设置值 | hset key field value | hset user:1 name tom<br/>hset user:1 age 12 | | 设置值 | hset key field value | hset user:1 name tom<br/>hset user:1 age 12 |
| 获取值 | hget key field | hget user:1 name | | 获取值 | hget key field | hget user:1 name |
| 获取值 | hget key field | hget user:1 name | | 删除 field | hdel key field [field ...] | |
| 删除field | hdel key field [field ...] | | | 计算 field 个数 | hlen key | |
| 计算field个数 | hlen key | | | 批量设置或获取 field-value | hmget key field [field]<br/>hmset key field value [field value...] | hmset user:1 name mike age 12 city tianjin<br/>hmget user:1 name city |
| 批量设置或获取field-value | hmget key field [field]<br/>hmset key field value [field value...] | hmset user:1 name mike age 12 city tianjin<br/>hmget user:1 name city | | 判断 field 是否存在 | hexists key field | |
| 判断field是否存在 | hexists key field | | | 获取所有 field | hkeys key | |
| 获取所有field | hkeys key | | | 获取所有 value | hvals key | |
| 获取所有value | hvals key | | | 获取所有的 filed-value | hgetall key | 如果哈希元素个数比较多, 会存在阻塞 Redis 的可能。<br/>获取全部 可以使用 hscan 命令, 该命令会渐进式遍历哈希类型 |
| 计数 | hincrby key field<br/>hincrbyfloat key field | | | 计数 | hincrby key field<br/>hincrbyfloat key field | |
### 1.4 列表 ### 1.4 列表
@ -452,7 +453,7 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
| -------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | -------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 增 | 左侧插入lpush key value [value ...] 右侧插入rpush key value [value ...] 某个指定元素前后插入linsert key before\|after pivot value | | | 增 | 左侧插入lpush key value [value ...] 右侧插入rpush key value [value ...] 某个指定元素前后插入linsert key before\|after pivot value | |
| 查 | 获取指定范围内的元素列表lrange key start end 获取列表指定索引下标的元素lindex key index 获取列表指定长度llen key | lrange listkey 0 -1 | | 查 | 获取指定范围内的元素列表lrange key start end 获取列表指定索引下标的元素lindex key index 获取列表指定长度llen key | lrange listkey 0 -1 |
| | 获取指定范围内的元素列表lrange key start end 获取列表指定索引下标的元素lindex key index 获取列表指定长度llen key | lrange listkey 0 -1 | | | 从列表左侧弹出元素lpop key 从列表右侧弹出元素rpop key 删除指定元素lrem key count value 截取列表ltrim key start end | count>0 从左到右, 删除最多 count 个元素。<br/>count<0 从右到左 删除最多 count 绝对值个元素。<br/>count=0 删除所有 |
| 改 | 修改指定索引下标的元素lset key index newValue | | | 改 | 修改指定索引下标的元素lset key index newValue | |
| 阻塞操作 | blpop key [key ...] timeout brpop key [key ...] timeout | key[key...] 多个列表的键。 timeout 阻塞时间\|等待时间(单位: 秒) | | 阻塞操作 | blpop key [key ...] timeout brpop key [key ...] timeout | key[key...] 多个列表的键。 timeout 阻塞时间\|等待时间(单位: 秒) |
@ -470,9 +471,9 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
| 删除元素 | srem key element [element ...] | 返回结果为成功删除的元素个数 | | 删除元素 | srem key element [element ...] | 返回结果为成功删除的元素个数 |
| 计算元素个数 | scard key | | | 计算元素个数 | scard key | |
| 判断元素是否在集合中 | sismember key element | | | 判断元素是否在集合中 | sismember key element | |
| 判断元素是否在集合中 | sismember key element | | | 随机返回 | srandmember key [count] | 随机从集合返回指定个数元素count 默认为 1 |
| 从集合随机弹出元素 | spop key | srandmember 不会从集合中删除元素spop 会 | | 从集合随机弹出元素 | spop key | srandmember 不会从集合中删除元素spop 会 |
| 从集合随机弹出元素 | spop key | srandmember 不会从集合中删除元素spop 会 | | 获取集合中所有元素 | smembers key | 可用 sscan 代替 |
**集合间操作** **集合间操作**
@ -491,14 +492,14 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
| 作用 | 格式 | 参数或示例 | | 作用 | 格式 | 参数或示例 |
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | 添加成员 | zadd key score member [score member ...] | nx member 必须不存在, 才可设置成功, 用于添加。<br> xx member 必须存在, 才可以设置成功, 用于更新。<br/>ch 返回此次操作后, 有序集合元素和分数发生变化的个数<br/>incr 对 score 做增加, 相当于后面介绍的 zincrby。 |
| 计算成员个数 | zcard key | | | 计算成员个数 | zcard key | |
| 计算某个成员的分数 | zscore key member | | | 计算某个成员的分数 | zscore key member | |
| 计算某个成员的分数 | zscore key member | | | 计算某个成员的排名 | zrank key member zrevrank key member | zrank 是从分数从低到高返回排名, zrevrank 反之。 |
| 删除成员 | zrem key member [member ...] | | | 删除成员 | zrem key member [member ...] | |
| 增加成员分数 | zincrby key increment member | zincrby user:ranking 9 tom | | 增加成员分数 | zincrby key increment member | zincrby user:ranking 9 tom |
| 增加成员分数 | zincrby key increment member | zincrby user:ranking 9 tom | | 返回指定排名范围的成员 | zrange key start end [withscores] zrange key start end [withscores] | zrange 是从低到高返回, zrevrange 反之。 |
| 返回指定排名范围的成员 | zrange key start end [withscores] zrange key start end [withscores] | zrange是从低到高返回 zrevrange反之。 | | 返回指定分数范围的成员 | zrangebyscore key min max \[withscores][limit offset count] zrevrangebyscore key max min \[withscores][limit offset count] | 其中 zrangebyscore 按照分数从低到高返回, zrevrangebyscore 反之。 [limit offset count]选项可以限制输出的起始位置和个数: 同时 min 和 max 还支持开区间(小括号) 和闭区间(中括号) -inf 和 +inf 分别代表无限小和无限大 |
| 删除指定排名内的升序元素 | zremrangerank key start end | | | 删除指定排名内的升序元素 | zremrangerank key start end | |
| 删除指定分数范围的成员 | zremrangebyscore key min max | | | 删除指定分数范围的成员 | zremrangebyscore key min max | |
@ -512,8 +513,8 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
- destination 交集计算结果保存到这个键。 - destination 交集计算结果保存到这个键。
- numkeys 需要做交集计算键的个数。 - numkeys 需要做交集计算键的个数。
- key[key...] 需要做交集计算的键。 - key[key...] 需要做交集计算的键。
- key[key...] 需要做交集计算的键。 - weights weight[weight...] 每个键的权重, 在做交集计算时, 每个键中的每个 member 会将自己分数乘以这个权重, 每个键的权重默认是 1。
- weights weight[weight...] 每个键的权重, 在做交集计算时, 每个键中的每个member会将自己分数乘以这个权重 每个键的权重默认是1。 - aggregate sum|min|max 计算成员交集后, 分值可以按照 sum 、min最小值 、 max最大值 做汇总, 默认值是 sum。
### 1.7 键管理 ### 1.7 键管理
@ -523,7 +524,7 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
**rename key newkey** **rename key newkey**
为了防止被强行 rename Redis 提供了 renamenx 命令, 确保只有 newKey 不存在时候才被覆盖。
##### 2. 随机返回键 ##### 2. 随机返回键
@ -531,19 +532,19 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
##### 3.键过期 ##### 3.键过期
- expire key seconds 键在 seconds 秒后过期。
- expire key seconds 键在seconds秒后过期。 - expireat key timestamp 键在秒级时间戳 timestamp 后过期。
- expireat key timestamp 键在秒级时间戳timestamp后过期。 - pexpire key milliseconds 键在 milliseconds 毫秒后过期。
- pexpire key milliseconds 键在milliseconds毫秒后过期 - pexpireat key milliseconds-timestamp 键在毫秒级时间戳 timestamp 后过期
注意: 注意:
1. 如果 expire key 的键不存在, 返回结果为 0
1. 如果expire key的键不存在 返回结果为0 2. 如果设置过期时间为负值, 键会立即被删除, 犹如使用 del 命令一样
2. 如果设置过期时间为负值, 键会立即被删除, 犹如使用del命令一样 3. persist key t 命令可以将键的过期时间清除
3. persist key t命令可以将键的过期时间清除 4. 对于字符串类型键, 执行 set 命令会去掉过期时间, 这个问题很容易在开发中被忽视
4. 对于字符串类型键, 执行set命令会去掉过期时间 这个问题很容易在开发中被忽视 5. Redis 不支持二级数据结构(例如哈希、 列表) 内部元素的过期功能, 例如不能对列表类型的一个元素做过期时间设置
5. Redis不支持二级数据结构例如哈希、 列表) 内部元素的过期功能, 例如不能对列表类型的一个元素做过期时间设置 6. setex 命令作为 set+expire 的组合, 不但是原子执行, 同时减少了一次网络通讯的时间
#### 1.7.2 键遍历 #### 1.7.2 键遍历
@ -555,9 +556,9 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
scan cursor \[match pattern] \[count number] scan cursor \[match pattern] \[count number]
- cursor 是必需参数, 实际上 cursor 是一个游标, 第一次遍历从 0 开始, 每次 scan 遍历完都会返回当前游标的值, 直到游标值为 0 表示遍历结束。
- cursor是必需参数 实际上cursor是一个游标 第一次遍历从0开始 每次scan遍历完都会返回当前游标的值 直到游标值为0 表示遍历结束 - match pattern 是可选参数, 它的作用的是做模式的匹配, 这点和 keys 的模式匹配很像
- match pattern是可选参数, 它的作用的是做模式的匹配, 这点和keys的模式匹配很像。 - count number 是可选参数, 它的作用是表明每次要遍历的键个数, 默认值是 10 此参数可以适当增大。
#### 1.7.3 数据库管理 #### 1.7.3 数据库管理
@ -567,4 +568,4 @@ scan cursor \[match pattern] \[count number]
##### 2.flushdb/flushall ##### 2.flushdb/flushall
flushdb/flushall 命令用于清除数据库, 两者的区别的是 flushdb 只清除当前数据库, flushall 会清除所有数据库。

View File

@ -1,5 +1,6 @@
# spring 整合 redis xml配置方式 # spring 整合 redis xml配置方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--Redis-客户端说明">1.1 Redis 客户端说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--Redis-客户端说明">1.1 Redis 客户端说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-Redis可视化软件">1.2 Redis可视化软件 </a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-Redis可视化软件">1.2 Redis可视化软件 </a><br/>
@ -44,12 +45,12 @@
### 1.1 Redis 客户端说明 ### 1.1 Redis 客户端说明
关于 spring 整合 mybatis 本用例提供两种整合方法:
1. jedis: 官方推荐的 java 客户端,能够胜任 redis 的大多数基本使用;
1. jedis: 官方推荐的java客户端能够胜任redis的大多数基本使用 2. redisson也是官方推荐的客户端比起 jedis 提供了更多高级的功能,比如分布式锁、集合数据切片等功能。同时提供了丰富而全面的中英文版本的 wiki。
注:关于 redis 其他语言官方推荐的客户端可以在[客户端](http://www.redis.cn/clients.html) 该网页查看,其中官方推荐的用了黄色星星:star:标注。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/redis官方推荐客户端.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/redis官方推荐客户端.png"/> </div>
@ -61,12 +62,12 @@
### 1.3 项目结构说明 ### 1.3 项目结构说明
1. jedis 和 redisson 的配置和单元测试分别位于 resources 和 test 下对应的包中,其中集群的配置文件以 cluster 结尾。所有配置按照需要在 springApplication.xml 用 import 导入。
1. jedis和redisson的配置和单元测试分别位于resources和test下对应的包中其中集群的配置文件以cluster结尾。所有配置按照需要在springApplication.xml用import导入。 2. 实体类 Programmer.java 用于测试 Redisson 序列化与反序列化
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-redis.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-redis.png"/> </div>
**springapplication.xml 文件:**
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -74,16 +75,16 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--jedis 连接单机 redis-->
<import resource="classpath:jedis/jedis.xml"/> <import resource="classpath:jedis/jedis.xml"/>
<!--jedis 连接集群 redis-->
<!--<import resource="classpath:jedis/jedisCluster.xml"/>--> <!--<import resource="classpath:jedis/jedisCluster.xml"/>-->
<!--redisson 连接单机 redis-->
<import resource="classpath:redisson/redisson.xml"/> <import resource="classpath:redisson/redisson.xml"/>
<!--redisson 连接集群 redis-->
<!--<import resource="classpath:redisson/redissonCluster.xml"/>--> <!--<import resource="classpath:redisson/redissonCluster.xml"/>-->
</beans> </beans>
@ -91,7 +92,7 @@
### 1.3 依赖说明 ### 1.3 依赖说明
除了 spring 的基本依赖外,需要导入 jedis 和 redisson 对应的客户端依赖包
```xml ```xml
<dependency> <dependency>
@ -148,7 +149,7 @@ redis.maxTotal=16
<property name="maxTotal" value="${redis.maxTotal}"/> <property name="maxTotal" value="${redis.maxTotal}"/>
</bean> </bean>
<!--配置 jedis 连接池-->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool"> <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg name="poolConfig" ref="jedisPoolConfig"/> <constructor-arg name="poolConfig" ref="jedisPoolConfig"/>
<constructor-arg name="host" value="${redis.host}"/> <constructor-arg name="host" value="${redis.host}"/>
@ -156,7 +157,7 @@ redis.maxTotal=16
<constructor-arg name="timeout" value="${redis.timeout}"/> <constructor-arg name="timeout" value="${redis.timeout}"/>
</bean> </bean>
<!--把 jedis 创建与销毁交给 spring 来管理-->
<bean id="jedis" factory-bean="jedisPool" factory-method="getResource" destroy-method="close"/> <bean id="jedis" factory-bean="jedisPool" factory-method="getResource" destroy-method="close"/>
</beans> </beans>
@ -181,7 +182,7 @@ redis.maxTotal=16
<property name="maxTotal" value="${redis.maxTotal}"/> <property name="maxTotal" value="${redis.maxTotal}"/>
</bean> </bean>
<!--配置 jedis 连接池 (集群)-->
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster"> <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg name="nodes"> <constructor-arg name="nodes">
<set> <set>
@ -207,9 +208,9 @@ redis.maxTotal=16
#### 2.4 单机版本测试用例 #### 2.4 单机版本测试用例
1.需要注意的是,对于 jedis 而言,单机版本和集群版本注入的实例是不同的;
2.jedis 本身并不支持序列化于反序列化操作,如果需要存储实体类,需要序列化后存入。(redisson 本身就支持序列化于反序列化,详见下文)
```java ```java
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@ -232,7 +233,7 @@ public class JedisSamples {
@Test @Test
public void setEx() { public void setEx() {
public void setEx() { String s = jedis.setex("spring", 10, "我会在 10 秒后过期");
System.out.println(s); System.out.println(s);
} }
@ -264,7 +265,7 @@ public class JedisClusterSamples {
@Test @Test
public void setEx() { public void setEx() {
public void setEx() { String s = jedisCluster.setex("spring", 10, "我会在 10 秒后过期");
System.out.println(s); System.out.println(s);
} }
@ -289,7 +290,7 @@ public class JedisClusterSamples {
<redisson:client> <redisson:client>
<redisson:client> <!--更多可配置项见官方文档 2.6.2. 通过 JSON、YAML 和 Spring XML 文件配置单节点模式
<a href="https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95#26-%E5%8D%95redis%E8%8A%82%E7%82%B9%E6%A8%A1%E5%BC%8F"> --> <a href="https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95#26-%E5%8D%95redis%E8%8A%82%E7%82%B9%E6%A8%A1%E5%BC%8F"> -->
<redisson:single-server <redisson:single-server
address="redis://127.0.0.1:6379" address="redis://127.0.0.1:6379"
@ -320,7 +321,7 @@ public class JedisClusterSamples {
<!-- 最基本配置 --> <!-- 最基本配置 -->
<redisson:client> <redisson:client>
<redisson:client> <!--集群更多配置参数见官方文档 2.4.2 通过 JSON、YAML 和 Spring XML 文件配置集群模式
<a href="https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95#24-%E9%9B%86%E7%BE%A4%E6%A8%A1%E5%BC%8F"> --> <a href="https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95#24-%E9%9B%86%E7%BE%A4%E6%A8%A1%E5%BC%8F"> -->
<redisson:cluster-servers> <redisson:cluster-servers>
<redisson:node-address value="redis://127.0.0.1:6379"/> <redisson:node-address value="redis://127.0.0.1:6379"/>
@ -333,7 +334,7 @@ public class JedisClusterSamples {
#### 2.3 存储基本类型测试用例 #### 2.3 存储基本类型测试用例
1. 这里需要注意的是,对于 Redisson 而言, 单机和集群最后在使用的时候注入的都是 RedissonClient这和 jedis 是不同的。
```java ```java
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@ -382,7 +383,7 @@ public class RedissonObjectSamples {
@Autowired @Autowired
private RedissonClient redissonClient; private RedissonClient redissonClient;
// Redisson 的对象编码类是用于将对象进行序列化和反序列化 默认采用 Jackson
@Test @Test
public void Set() { public void Set() {
@ -421,9 +422,9 @@ public class RedissonObjectSamples {
5. 键过期:**expire key seconds** 5. 键过期:**expire key seconds**
ttl 命令会返回键的剩余过期时间, 它有 3 种返回值:
- 大于等于 0 的整数: 键剩余的过期时间。
- -1 键没设置过期时间。 - -1 键没设置过期时间。
- -2 键不存在 - -2 键不存在
@ -431,23 +432,23 @@ public class RedissonObjectSamples {
#### 1.1.2 数据结构和内部编码 #### 1.1.2 数据结构和内部编码
type 命令实际返回的就是当前键的数据结构类型, 它们分别是:**string**(字符串) 、 **hash**(哈希) 、 **list**(列表) 、 **set**(集合) 、 **zset**(有序集合)
#### 1.1.3 单线程架构 #### 1.1.3 单线程架构
1. 纯内存访问, Redis 将所有数据放在内存中, 内存的响应时长大约为 100 纳秒, 这是 Redis 达到每秒万级别访问的重要基础。
1. 纯内存访问, Redis将所有数据放在内存中 内存的响应时长大约为100纳秒 这是Redis达到每秒万级别访问的重要基础。 2. 非阻塞 I/O Redis 使用 epoll 作为 I/O 多路复用技术的实现, 再加上 Redis 自身的事件处理模型将 epoll 中的连接、 读写、 关闭都转换为事件, 不在网络 I/O 上浪费过多的时间, 如图 2-6 所示。
3. 单线程避免了线程切换和竞态产生的消耗。 3. 单线程避免了线程切换和竞态产生的消耗。
### 1.2 字符串 ### 1.2 字符串
| 作用 | 格式 | 参数或示例 | | 作用 | 格式 | 参数或示例 |
| ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | 设置值 | set key value \[ex seconds]\[px milliseconds][nx\|xx] setnx setex | ex seconds 为键设置秒级过期时间。 <br/>px milliseconds 为键设置毫秒级过期时间。<br/>nx 键必须不存在, 才可以设置成功, 用于添加。<br/>xx 与 nx 相反, 键必须存在, 才可以设置成功, 用于更新。 |
| 设置值 | set key value \[ex seconds]\[px milliseconds][nx\|xx] setnx setex | ex seconds 为键设置秒级过期时间。 <br/>px milliseconds 为键设置毫秒级过期时间。<br/>nx 键必须不存在, 才可以设置成功, 用于添加。<br/>xx 与nx相反 键必须存在, 才可以设置成功, 用于更新。 | | 获取值 | get key | r 如果获取的键不存在 ,则返回 nil(空) |
| 批量设置 | mset key value [key value ...] | mset a 1 b 2 c 3 d 4 | | 批量设置 | mset key value [key value ...] | mset a 1 b 2 c 3 d 4 |
| 批量获取值 | mget key [key ...] | mget a b c d | | 批量获取值 | mget key [key ...] | mget a b c d |
| 批量获取值 | mget key [key ...] | mget a b c d | | 计数 | incr key decr key incrby key increment指定数值自增<br/>decrby key decrement指定数值自减<br/>incrbyfloat key increment (浮点数自增) | 值不是整数, 返回错误。 值是整数, 返回自增或自减后的结果。<br/>键不存在,创建键,并按照值为 0 自增或自减, 返回结果为 1。 |
| 追加值 | append key value | 向字符串的默认追加值 | | 追加值 | append key value | 向字符串的默认追加值 |
| 字符串长度 | strlen key | 获取字符串长度,中文占用三个字节 | | 字符串长度 | strlen key | 获取字符串长度,中文占用三个字节 |
| 设置并返回原值 | getset key value | | | 设置并返回原值 | getset key value | |
@ -460,13 +461,13 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
| ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 设置值 | hset key field value | hset user:1 name tom<br/>hset user:1 age 12 | | 设置值 | hset key field value | hset user:1 name tom<br/>hset user:1 age 12 |
| 获取值 | hget key field | hget user:1 name | | 获取值 | hget key field | hget user:1 name |
| 获取值 | hget key field | hget user:1 name | | 删除 field | hdel key field [field ...] | |
| 删除field | hdel key field [field ...] | | | 计算 field 个数 | hlen key | |
| 计算field个数 | hlen key | | | 批量设置或获取 field-value | hmget key field [field]<br/>hmset key field value [field value...] | hmset user:1 name mike age 12 city tianjin<br/>hmget user:1 name city |
| 批量设置或获取field-value | hmget key field [field]<br/>hmset key field value [field value...] | hmset user:1 name mike age 12 city tianjin<br/>hmget user:1 name city | | 判断 field 是否存在 | hexists key field | |
| 判断field是否存在 | hexists key field | | | 获取所有 field | hkeys key | |
| 获取所有field | hkeys key | | | 获取所有 value | hvals key | |
| 获取所有value | hvals key | | | 获取所有的 filed-value | hgetall key | 如果哈希元素个数比较多, 会存在阻塞 Redis 的可能。<br/>获取全部 可以使用 hscan 命令, 该命令会渐进式遍历哈希类型 |
| 计数 | hincrby key field<br/>hincrbyfloat key field | | | 计数 | hincrby key field<br/>hincrbyfloat key field | |
### 1.4 列表 ### 1.4 列表
@ -475,7 +476,7 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
| -------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | -------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 增 | 左侧插入lpush key value [value ...] 右侧插入rpush key value [value ...] 某个指定元素前后插入linsert key before\|after pivot value | | | 增 | 左侧插入lpush key value [value ...] 右侧插入rpush key value [value ...] 某个指定元素前后插入linsert key before\|after pivot value | |
| 查 | 获取指定范围内的元素列表lrange key start end 获取列表指定索引下标的元素lindex key index 获取列表指定长度llen key | lrange listkey 0 -1 | | 查 | 获取指定范围内的元素列表lrange key start end 获取列表指定索引下标的元素lindex key index 获取列表指定长度llen key | lrange listkey 0 -1 |
| | 获取指定范围内的元素列表lrange key start end 获取列表指定索引下标的元素lindex key index 获取列表指定长度llen key | lrange listkey 0 -1 | | | 从列表左侧弹出元素lpop key 从列表右侧弹出元素rpop key 删除指定元素lrem key count value 截取列表ltrim key start end | count>0 从左到右, 删除最多 count 个元素。<br/>count<0 从右到左 删除最多 count 绝对值个元素。<br/>count=0 删除所有 |
| 改 | 修改指定索引下标的元素lset key index newValue | | | 改 | 修改指定索引下标的元素lset key index newValue | |
| 阻塞操作 | blpop key [key ...] timeout brpop key [key ...] timeout | key[key...] 多个列表的键。 timeout 阻塞时间\|等待时间(单位: 秒) | | 阻塞操作 | blpop key [key ...] timeout brpop key [key ...] timeout | key[key...] 多个列表的键。 timeout 阻塞时间\|等待时间(单位: 秒) |
@ -493,9 +494,9 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
| 删除元素 | srem key element [element ...] | 返回结果为成功删除的元素个数 | | 删除元素 | srem key element [element ...] | 返回结果为成功删除的元素个数 |
| 计算元素个数 | scard key | | | 计算元素个数 | scard key | |
| 判断元素是否在集合中 | sismember key element | | | 判断元素是否在集合中 | sismember key element | |
| 判断元素是否在集合中 | sismember key element | | | 随机返回 | srandmember key [count] | 随机从集合返回指定个数元素count 默认为 1 |
| 从集合随机弹出元素 | spop key | srandmember 不会从集合中删除元素spop 会 | | 从集合随机弹出元素 | spop key | srandmember 不会从集合中删除元素spop 会 |
| 从集合随机弹出元素 | spop key | srandmember 不会从集合中删除元素spop 会 | | 获取集合中所有元素 | smembers key | 可用 sscan 代替 |
**集合间操作** **集合间操作**
@ -514,14 +515,14 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
| 作用 | 格式 | 参数或示例 | | 作用 | 格式 | 参数或示例 |
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | 添加成员 | zadd key score member [score member ...] | nx member 必须不存在, 才可设置成功, 用于添加。<br> xx member 必须存在, 才可以设置成功, 用于更新。<br/>ch 返回此次操作后, 有序集合元素和分数发生变化的个数<br/>incr 对 score 做增加, 相当于后面介绍的 zincrby。 |
| 计算成员个数 | zcard key | | | 计算成员个数 | zcard key | |
| 计算某个成员的分数 | zscore key member | | | 计算某个成员的分数 | zscore key member | |
| 计算某个成员的分数 | zscore key member | | | 计算某个成员的排名 | zrank key member zrevrank key member | zrank 是从分数从低到高返回排名, zrevrank 反之。 |
| 删除成员 | zrem key member [member ...] | | | 删除成员 | zrem key member [member ...] | |
| 增加成员分数 | zincrby key increment member | zincrby user:ranking 9 tom | | 增加成员分数 | zincrby key increment member | zincrby user:ranking 9 tom |
| 增加成员分数 | zincrby key increment member | zincrby user:ranking 9 tom | | 返回指定排名范围的成员 | zrange key start end [withscores] zrange key start end [withscores] | zrange 是从低到高返回, zrevrange 反之。 |
| 返回指定排名范围的成员 | zrange key start end [withscores] zrange key start end [withscores] | zrange是从低到高返回 zrevrange反之。 | | 返回指定分数范围的成员 | zrangebyscore key min max \[withscores][limit offset count] zrevrangebyscore key max min \[withscores][limit offset count] | 其中 zrangebyscore 按照分数从低到高返回, zrevrangebyscore 反之。 [limit offset count]选项可以限制输出的起始位置和个数: 同时 min 和 max 还支持开区间(小括号) 和闭区间(中括号) -inf 和 +inf 分别代表无限小和无限大 |
| 删除指定排名内的升序元素 | zremrangerank key start end | | | 删除指定排名内的升序元素 | zremrangerank key start end | |
| 删除指定分数范围的成员 | zremrangebyscore key min max | | | 删除指定分数范围的成员 | zremrangebyscore key min max | |
@ -535,8 +536,8 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
- destination 交集计算结果保存到这个键。 - destination 交集计算结果保存到这个键。
- numkeys 需要做交集计算键的个数。 - numkeys 需要做交集计算键的个数。
- key[key...] 需要做交集计算的键。 - key[key...] 需要做交集计算的键。
- key[key...] 需要做交集计算的键。 - weights weight[weight...] 每个键的权重, 在做交集计算时, 每个键中的每个 member 会将自己分数乘以这个权重, 每个键的权重默认是 1。
- weights weight[weight...] 每个键的权重, 在做交集计算时, 每个键中的每个member会将自己分数乘以这个权重 每个键的权重默认是1。 - aggregate sum|min|max 计算成员交集后, 分值可以按照 sum 、min最小值 、 max最大值 做汇总, 默认值是 sum。
### 1.7 键管理 ### 1.7 键管理
@ -546,7 +547,7 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
**rename key newkey** **rename key newkey**
为了防止被强行 rename Redis 提供了 renamenx 命令, 确保只有 newKey 不存在时候才被覆盖。
##### 2. 随机返回键 ##### 2. 随机返回键
@ -554,19 +555,19 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
##### 3.键过期 ##### 3.键过期
- expire key seconds 键在 seconds 秒后过期。
- expire key seconds 键在seconds秒后过期。 - expireat key timestamp 键在秒级时间戳 timestamp 后过期。
- expireat key timestamp 键在秒级时间戳timestamp后过期。 - pexpire key milliseconds 键在 milliseconds 毫秒后过期。
- pexpire key milliseconds 键在milliseconds毫秒后过期 - pexpireat key milliseconds-timestamp 键在毫秒级时间戳 timestamp 后过期
注意: 注意:
1. 如果 expire key 的键不存在, 返回结果为 0
1. 如果expire key的键不存在 返回结果为0 2. 如果设置过期时间为负值, 键会立即被删除, 犹如使用 del 命令一样
2. 如果设置过期时间为负值, 键会立即被删除, 犹如使用del命令一样 3. persist key t 命令可以将键的过期时间清除
3. persist key t命令可以将键的过期时间清除 4. 对于字符串类型键, 执行 set 命令会去掉过期时间, 这个问题很容易在开发中被忽视
4. 对于字符串类型键, 执行set命令会去掉过期时间 这个问题很容易在开发中被忽视 5. Redis 不支持二级数据结构(例如哈希、 列表) 内部元素的过期功能, 例如不能对列表类型的一个元素做过期时间设置
5. Redis不支持二级数据结构例如哈希、 列表) 内部元素的过期功能, 例如不能对列表类型的一个元素做过期时间设置 6. setex 命令作为 set+expire 的组合, 不但是原子执行, 同时减少了一次网络通讯的时间
#### 1.7.2 键遍历 #### 1.7.2 键遍历
@ -578,9 +579,9 @@ type命令实际返回的就是当前键的数据结构类型 它们分别是
scan cursor \[match pattern] \[count number] scan cursor \[match pattern] \[count number]
- cursor 是必需参数, 实际上 cursor 是一个游标, 第一次遍历从 0 开始, 每次 scan 遍历完都会返回当前游标的值, 直到游标值为 0 表示遍历结束。
- cursor是必需参数 实际上cursor是一个游标 第一次遍历从0开始 每次scan遍历完都会返回当前游标的值 直到游标值为0 表示遍历结束 - match pattern 是可选参数, 它的作用的是做模式的匹配, 这点和 keys 的模式匹配很像
- match pattern是可选参数, 它的作用的是做模式的匹配, 这点和keys的模式匹配很像。 - count number 是可选参数, 它的作用是表明每次要遍历的键个数, 默认值是 10 此参数可以适当增大。
#### 1.7.3 数据库管理 #### 1.7.3 数据库管理
@ -590,4 +591,4 @@ scan cursor \[match pattern] \[count number]
##### 2.flushdb/flushall ##### 2.flushdb/flushall
flushdb/flushall 命令用于清除数据库, 两者的区别的是 flushdb 只清除当前数据库, flushall 会清除所有数据库。

View File

@ -1,5 +1,6 @@
# spring 定时任务(注解方式) # spring 定时任务(注解方式)
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
@ -13,7 +14,7 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
关于任务的调度配置定义在 ServletConfig.java 中,为方便观察项目定时执行的情况,项目以 web 的方式构建。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-scheduling-annotation.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-scheduling-annotation.png"/> </div>
@ -105,8 +106,8 @@ public class Task {
@Scheduled(fixedDelay = 5000, initialDelay = 1000) @Scheduled(fixedDelay = 5000, initialDelay = 1000)
public void methodA() { public void methodA() {
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
Thread thread = Thread.currentThread(); System.out.println(String.format("线程名称:%s ; 线程 ID%s ; 调用方法:%s ; 调用时间:%s",
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", thread.getName(), thread.getId(), "methodA 方法执行", LocalDateTime.now()));
} }
/** /**
@ -116,16 +117,16 @@ public class Task {
@Async @Async
public void methodB() throws InterruptedException { public void methodB() throws InterruptedException {
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
Thread thread = Thread.currentThread(); System.out.println(String.format("线程名称:%s ; 线程 ID%s ; 调用方法:%s ; 调用时间:%s",
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", thread.getName(), thread.getId(), "methodB 方法执行", LocalDateTime.now()));
Thread.sleep(10 * 1000); Thread.sleep(10 * 1000);
} }
@Scheduled(cron = "0/10 * * * * ?") @Scheduled(cron = "0/10 * * * * ?")
public void methodC() { public void methodC() {
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
Thread thread = Thread.currentThread(); System.out.println(String.format("线程名称:%s ; 线程 ID%s ; 调用方法:%s ; 调用时间:%s",
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", thread.getName(), thread.getId(), "methodC 方法执行", LocalDateTime.now()));
} }
} }
@ -140,8 +141,8 @@ public class Task {
*/ */
@Configuration @Configuration
@EnableWebMvc @EnableWebMvc
@EnableWebMvc @EnableScheduling //启用 Spring 的计划任务执行功能
@EnableScheduling //启用Spring的计划任务执行功能 @EnableAsync //启用 Spring 的异步方法执行功能
@ComponentScan(basePackages = {"com.heibaiying.task"}) @ComponentScan(basePackages = {"com.heibaiying.task"})
public class ServletConfig implements WebMvcConfigurer, AsyncConfigurer, SchedulingConfigurer { public class ServletConfig implements WebMvcConfigurer, AsyncConfigurer, SchedulingConfigurer {
@ -169,7 +170,7 @@ public class ServletConfig implements WebMvcConfigurer, AsyncConfigurer, Schedul
}; };
} }
// 如果程序结束,需要关闭线程池 不然程序无法完全退出 只能 kill 才能完全退出
@PreDestroy @PreDestroy
public void destroy() { public void destroy() {
if (executor != null) { if (executor != null) {
@ -193,9 +194,9 @@ public class ServletConfig implements WebMvcConfigurer, AsyncConfigurer, Schedul
**关于调度程序线程池作用说明** **关于调度程序线程池作用说明**
按照例子 我们有 methodA 、 methodB 、methodC 三个方法 其中 methodB 是耗时的方法如果不声明调度程序线程池 则 methodB 会阻塞 methodA 、methodC 方法的执行 因为调度程序是单线程的
**关于任务执行线程池作用说明** **关于任务执行线程池作用说明**
按照例子 如果我们声明 methodB 是按照 fixedRate=5000 方法执行的 ,理论上不管任务耗时多久,任务都应该是每 5 秒执行一次,但是实际上任务是被加入执行队列,也不会立即被执行,因为默认执行任务是单线程的,这个时候需要开启@EnableAsync 并指定方法是 @Async 异步的,并且配置执行任务线程池 (如果不配置就使用默认的线程池配置)

View File

@ -1,5 +1,6 @@
# spring 定时任务xml配置方式 # spring 定时任务xml配置方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
@ -13,7 +14,7 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
关于任务的调度配置定义在 springApplication.xml 中,为方便观察项目定时执行的情况,项目以 web 的方式构建。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-scheduling.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-scheduling.png"/> </div>
@ -107,22 +108,22 @@ public class Task {
public void methodA() { public void methodA() {
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
Thread thread = Thread.currentThread(); System.out.println(String.format("线程名称:%s ; 线程 ID%s ; 调用方法:%s ; 调用时间:%s",
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", thread.getName(), thread.getId(), "methodA 方法执行", LocalDateTime.now()));
} }
@Async @Async
public void methodB() throws InterruptedException { public void methodB() throws InterruptedException {
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
Thread thread = Thread.currentThread(); System.out.println(String.format("线程名称:%s ; 线程 ID%s ; 调用方法:%s ; 调用时间:%s",
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", thread.getName(), thread.getId(), "methodB 方法执行", LocalDateTime.now()));
Thread.sleep(10 * 1000); Thread.sleep(10 * 1000);
} }
public void methodC() { public void methodC() {
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
Thread thread = Thread.currentThread(); System.out.println(String.format("线程名称:%s ; 线程 ID%s ; 调用方法:%s ; 调用时间:%s",
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", thread.getName(), thread.getId(), "methodC 方法执行", LocalDateTime.now()));
} }
} }
@ -171,12 +172,12 @@ public class Task {
pool-size="5-25" pool-size="5-25"
queue-capacity="100" queue-capacity="100"
rejection-policy="CALLER_RUNS"/> rejection-policy="CALLER_RUNS"/>
rejection-policy="CALLER_RUNS"/> <!--拒绝策略默认值为 ABORT
<!--拒绝策略默认值为ABORT CALLER_RUNS 来限制入栈任务
CALLER_RUNS来限制入栈任务 DISCARD 删除当前任务
DISCARD删除当前任务 DISCARD_OLDEST 将任务放在队列的头部。-->
<!--允许在任何 Spring 管理的对象上检测@Async 和@Scheduled 注释, 如果存在,将生成用于异步执行带注释的方法的代理。-->
<task:annotation-driven/> <task:annotation-driven/>
</beans> </beans>
@ -184,9 +185,9 @@ public class Task {
**关于调度程序线程池作用说明** **关于调度程序线程池作用说明**
按照例子 我们有 methodA 、 methodB 、methodC 三个方法 其中 methodB 是耗时的方法如果不声明调度程序线程池 则 methodB 会阻塞 methodA 、methodC 方法的执行 因为调度程序是单线程的
**关于任务执行线程池作用说明** **关于任务执行线程池作用说明**
按照例子 如果我们声明 methodB 是按照 fixedRate=5000 方法执行的 ,理论上不管任务耗时多久,任务都应该是每 5 秒执行一次,但是实际上任务是被加入执行队列,也不会立即被执行,因为默认执行任务是单线程的,这个时候需要开启@EnableAsync 并指定方法是 @Async 异步的,并且配置执行任务线程池 (如果不配置就使用默认的线程池配置)

View File

@ -1,5 +1,6 @@
# spring websocket注解方式 # spring websocket注解方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
@ -16,12 +17,12 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
1. 项目模拟一个简单的群聊功能,为区分不同的聊天客户端,登录时候将临时用户名存储在 session 当中;
1. 项目模拟一个简单的群聊功能为区分不同的聊天客户端登录时候将临时用户名存储在session当中 2. webconfig 包是基础注解的方式配置 web在 spring-base-annotation 项目中已经讲解过每个类作用
2. webconfig 包是基础注解的方式配置web在spring-base-annotation项目中已经讲解过每个类作用 3. CustomHander 为消息的自定义处理器
3. CustomHander为消息的自定义处理器; 4. CustomHandershakerInterceptor 为自定义的 websocket 的握手拦截器;
4. CustomHandershakerInterceptor为自定义的 websocket 的握手拦截器 5. webSocketConfig 是 websocket 的主要配置类
5. webSocketConfig 是websocket 的主要配置类; 6. 项目以 web 的方式构建。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-websocket-annotation.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-websocket-annotation.png"/> </div>
@ -29,7 +30,7 @@
### 1.2 依赖说明 ### 1.2 依赖说明
除了基本的 spring 依赖外,还需要导入 webSocket 的依赖包
```xml ```xml
<!--spring webSocket 的依赖包 --> <!--spring webSocket 的依赖包 -->
@ -112,10 +113,10 @@ public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor
InetAddress address = remoteAddress.getAddress(); InetAddress address = remoteAddress.getAddress();
System.out.println(address); System.out.println(address);
/* /*
/* * 最后需要要显示调用父类方法,父类的 beforeHandshake 方法
* 最后需要要显示调用父类方法父类的beforeHandshake方法 * 把 ServerHttpRequest 中 session 中对应的值拷贝到 WebSocketSession 中。
* 把ServerHttpRequest 中session中对应的值拷贝到WebSocketSession中。 * 如果我们没有实现这个方法,我们在最后的 handler 处理中 是拿不到 session 中的值
* 如果我们没有实现这个方法我们在最后的handler处理中 是拿不到 session中的值 * 作为测试 可以注释掉下面这一行 可以发现自定义处理器中 session 的 username 总是为空
*/ */
return super.beforeHandshake(request, response, wsHandler, attributes); return super.beforeHandshake(request, response, wsHandler, attributes);
} }
@ -174,7 +175,7 @@ public class WebSocketConfig implements WebSocketConfigurer {
} }
message.value = ""; message.value = "";
}); });
}); // 关闭页面时候关闭 ws
window.addEventListener("beforeunload", function(event) { window.addEventListener("beforeunload", function(event) {
ws.close(); ws.close();
}); });

View File

@ -1,5 +1,6 @@
# spring websocketxml配置方式 # spring websocketxml配置方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/> <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/> &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
@ -16,10 +17,10 @@
### 1.1 项目结构说明 ### 1.1 项目结构说明
1. 项目模拟一个简单的群聊功能,为区分不同的聊天客户端,登录时候将临时用户名存储在 session 当中;
1. 项目模拟一个简单的群聊功能为区分不同的聊天客户端登录时候将临时用户名存储在session当中 2. CustomHander 为消息的自定义处理器
2. CustomHander为消息的自定义处理器; 3. CustomHandershakerInterceptor 为自定义的 websocket 的握手拦截器;
3. CustomHandershakerInterceptor为自定义的 websocket 的握手拦截器; 4. 项目以 web 的方式构建。
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-websocket.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-websocket.png"/> </div>
@ -27,7 +28,7 @@
### 1.2 依赖说明 ### 1.2 依赖说明
除了基本的 spring 依赖外,还需要导入 webSocket 的依赖包
```xml ```xml
<!--spring webSocket 的依赖包 --> <!--spring webSocket 的依赖包 -->
@ -110,10 +111,10 @@ public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor
InetAddress address = remoteAddress.getAddress(); InetAddress address = remoteAddress.getAddress();
System.out.println(address); System.out.println(address);
/* /*
/* * 最后需要要显示调用父类方法,父类的 beforeHandshake 方法
* 最后需要要显示调用父类方法父类的beforeHandshake方法 * 把 ServerHttpRequest 中 session 中对应的值拷贝到 WebSocketSession 中。
* 把ServerHttpRequest 中session中对应的值拷贝到WebSocketSession中。 * 如果我们没有实现这个方法,我们在最后的 handler 处理中 是拿不到 session 中的值
* 如果我们没有实现这个方法我们在最后的handler处理中 是拿不到 session中的值 * 作为测试 可以注释掉下面这一行 可以发现自定义处理器中 session 的 username 总是为空
*/ */
return super.beforeHandshake(request, response, wsHandler, attributes); return super.beforeHandshake(request, response, wsHandler, attributes);
} }
@ -141,7 +142,7 @@ public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor
<!-- 开启注解包扫描--> <!-- 开启注解包扫描-->
<context:component-scan base-package="com.heibaiying.*"/> <context:component-scan base-package="com.heibaiying.*"/>
<!--使用默认的 Servlet 来响应静态文件 -->
<mvc:default-servlet-handler/> <mvc:default-servlet-handler/>
<!-- 开启注解驱动 --> <!-- 开启注解驱动 -->
@ -156,12 +157,12 @@ public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor
<property name="suffix" value=".jsp"/> <property name="suffix" value=".jsp"/>
</bean> </bean>
<!--配置 webSocket-->
<bean id="customHandler" class="com.heibaiying.websocket.CustomHandler"/> <bean id="customHandler" class="com.heibaiying.websocket.CustomHandler"/>
<websocket:handlers> <websocket:handlers>
<websocket:handlers> <!--指定 webSocket 地址-->
<websocket:mapping path="/socket" handler="customHandler"/> <websocket:mapping path="/socket" handler="customHandler"/>
<websocket:mapping path="/socket" handler="customHandler"/> <!--webSocket 握手-->
<websocket:handshake-interceptors> <websocket:handshake-interceptors>
<bean class="com.heibaiying.websocket.CustomHandshakeInterceptor"/> <bean class="com.heibaiying.websocket.CustomHandshakeInterceptor"/>
</websocket:handshake-interceptors> </websocket:handshake-interceptors>
@ -204,7 +205,7 @@ public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor
} }
message.value = ""; message.value = "";
}); });
}); // 关闭页面时候关闭 ws
window.addEventListener("beforeunload", function(event) { window.addEventListener("beforeunload", function(event) {
ws.close(); ws.close();
}); });

View File

@ -20,7 +20,7 @@
### 1.1 项目搭建 ### 1.1 项目搭建
1.新建maven web工程并引入相应的依赖 1.新建 maven web 工程,并引入相应的依赖
```xml ```xml
<properties> <properties>
@ -62,9 +62,9 @@
</dependencies> </dependencies>
``` ```
2.得益于servlet3.0spring的支持我们可以在没有web.xml的情况下完成关于servlet配置。 2.得益于 servlet3.0spring 的支持,我们可以在没有 web.xml 的情况下完成关于 servlet 配置。
新建DispatcherServletInitializer.java文件,这个类的作用相当于我们在xml方式下web.xml中配置的DispatcherServlet 新建 DispatcherServletInitializer.java 文件,这个类的作用相当于我们在 xml 方式下 web.xml 中配置的 DispatcherServlet
```java ```java
package com.heibaiying.config; package com.heibaiying.config;
@ -92,7 +92,7 @@ public class DispatcherServletInitializer extends AbstractAnnotationConfigDispat
``` ```
3.新建ServletConfig.java文件内容如下(这个类相当于我们在xml配置方式中的springApplication.xml) 3.新建 ServletConfig.java文件内容如下 (这个类相当于我们在 xml 配置方式中的 springApplication.xml)
```java ```java
package com.heibaiying.config; package com.heibaiying.config;
@ -144,7 +144,7 @@ public class ServletConfig implements WebMvcConfigurer {
``` ```
4.在src 下新建controller用于测试 4.在 src 下新建 controller 用于测试
```java ```java
package com.heibaiying.controller; package com.heibaiying.controller;
@ -171,7 +171,7 @@ public class HelloController {
``` ```
5.在WEB-INF 下新建jsp文件夹新建hello.jsp 文件 5.在 WEB-INF 下新建 jsp 文件夹,新建 hello.jsp 文件
```jsp ```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %>
@ -185,23 +185,23 @@ public class HelloController {
</html> </html>
``` ```
6.启动tomcat服务访问localhost:8080/mvc/hello 6.启动 tomcat 服务,访问 localhost:8080/mvc/hello
### 1.2 相关注解说明 ### 1.2 相关注解说明
**1.@Configuration** **1.@Configuration**
@Configuration用于定义配置类可替换xml配置文件被注解的类内部包含有一个或多个被@Bean注解的方法这些方法将会被AnnotationConfigApplicationContextAnnotationConfigWebApplicationContext类进行扫描并用于构建bean定义初始化Spring容器。 @Configuration 用于定义配置类,可替换 xml 配置文件,被注解的类内部包含有一个或多个被@Bean 注解的方法,这些方法将会被 AnnotationConfigApplicationContextAnnotationConfigWebApplicationContext 类进行扫描,并用于构建 bean 定义,初始化 Spring 容器。
**2.@EnableWebMvc** **2.@EnableWebMvc**
简单的说就是提供了部分springmvc的功能例如格式转换和参数绑定。 简单的说就是提供了部分 springmvc 的功能,例如格式转换和参数绑定。
## 二、配置自定义拦截器 ## 二、配置自定义拦截器
1.创建自定义拦截器实现接口HandlerInterceptor这里我们创建两个拦截器用于测试拦截器方法的执行顺序 1.创建自定义拦截器,实现接口 HandlerInterceptor这里我们创建两个拦截器用于测试拦截器方法的执行顺序
```java ```java
package com.heibaiying.interceptors; package com.heibaiying.interceptors;
@ -219,17 +219,17 @@ import javax.servlet.http.HttpServletResponse;
public class MyFirstInterceptor implements HandlerInterceptor { public class MyFirstInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("进入第一个拦截器preHandle"); System.out.println("进入第一个拦截器 preHandle");
return true; return true;
} }
// 需要注意的是,如果对应的程序报错,不一定会进入这个方法 但一定会进入afterCompletion这个方法 // 需要注意的是,如果对应的程序报错,不一定会进入这个方法 但一定会进入 afterCompletion 这个方法
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
System.out.println("进入第一个拦截器postHandle"); System.out.println("进入第一个拦截器 postHandle");
} }
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("进入第一个拦截器afterCompletion"); System.out.println("进入第一个拦截器 afterCompletion");
} }
} }
``` ```
@ -250,22 +250,22 @@ import javax.servlet.http.HttpServletResponse;
public class MySecondInterceptor implements HandlerInterceptor { public class MySecondInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("进入第二个拦截器preHandle"); System.out.println("进入第二个拦截器 preHandle");
return true; return true;
} }
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
System.out.println("进入第二个拦截器postHandle"); System.out.println("进入第二个拦截器 postHandle");
} }
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("进入第二个拦截器afterCompletion"); System.out.println("进入第二个拦截器 afterCompletion");
} }
} }
``` ```
2.在ServletConfig.java中注册自定义拦截器 2.在 ServletConfig.java 中注册自定义拦截器
```java ```java
/** /**
@ -279,7 +279,7 @@ public class MySecondInterceptor implements HandlerInterceptor {
3.关于多个拦截器方法执行顺序的说明 3.关于多个拦截器方法执行顺序的说明
拦截器的执行顺序是按声明的先后顺序执行的先声明的拦截器中的preHandle方法会先执行然而它的postHandle方法和afterCompletion方法却会后执行。 拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的 preHandle 方法会先执行,然而它的 postHandle 方法和 afterCompletion 方法却会后执行。
@ -340,7 +340,7 @@ public class NoAuthExceptionResolver implements HandlerExceptionResolver {
return new ModelAndView(); return new ModelAndView();
} }
// 判断是否是Ajax请求 // 判断是否是 Ajax 请求
private boolean isAjax(HttpServletRequest request) { private boolean isAjax(HttpServletRequest request) {
return "XMLHttpRequest".equalsIgnoreCase(request.getHeader("X-Requested-With")); return "XMLHttpRequest".equalsIgnoreCase(request.getHeader("X-Requested-With"));
} }
@ -348,7 +348,7 @@ public class NoAuthExceptionResolver implements HandlerExceptionResolver {
``` ```
3.在ServletConfig.java注册自定义异常处理器 3.在 ServletConfig.java 注册自定义异常处理器
```java ```java
/** /**
@ -359,7 +359,7 @@ public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> re
} }
``` ```
4.定义测试controller抛出自定义异常 4.定义测试 controller抛出自定义异常
```java ```java
@Controller @Controller
@ -379,7 +379,7 @@ public class HelloController {
} }
``` ```
调用这个controller时同时也可以验证在拦截器部分提到的如果对应的程序报错拦截器不一定会进入postHandle这个方法 但一定会进入afterCompletion这个方法 注:调用这个 controller 时,同时也可以验证在拦截器部分提到的:如果对应的程序报错,拦截器不一定会进入 postHandle 这个方法 但一定会进入 afterCompletion 这个方法
@ -387,7 +387,7 @@ public class HelloController {
### 4.1 参数绑定 ### 4.1 参数绑定
1.新建Programmer.java 1.新建 Programmer.java
```java ```java
package com.heibaiying.bean; package com.heibaiying.bean;
@ -412,9 +412,9 @@ public class Programmer {
``` ```
注:@Data 是lombok包下的注解用来生成相应的set、get方法使得类的书写更为简洁。 注:@Data lombok 包下的注解,用来生成相应的 set、get 方法,使得类的书写更为简洁。
2.新建ParamBindController.java 文件 2.新建 ParamBindController.java 文件
```java ```java
package com.heibaiying.controller; package com.heibaiying.controller;
@ -475,7 +475,7 @@ public class ParamBindController {
``` ```
3.新建param.jsp 文件 3.新建 param.jsp 文件
```jsp ```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %>
@ -495,11 +495,11 @@ public class ParamBindController {
``` ```
4.启动tomcat用[postman](https://www.getpostman.com/)软件发送请求进行测试 4.启动 tomcat用[postman](https://www.getpostman.com/) 软件发送请求进行测试
### 4.2 关于日期格式转换的三种方法 ### 4.2 关于日期格式转换的三种方法
1.如上实例代码所示在对应的controller中初始化绑定 1.如上实例代码所示,在对应的 controller 中初始化绑定
```java ```java
@InitBinder @InitBinder
@ -508,13 +508,13 @@ public class ParamBindController {
} }
``` ```
2.利用@DateTimeFormat注解,如果是用实体类去接收参数,则在对应的属性上用@DateTimeFormat@JsonFormat声明 2.利用@DateTimeFormat 注解,如果是用实体类去接收参数,则在对应的属性上用@DateTimeFormat@JsonFormat 声明
```java ```java
public String param(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday) public String param(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday)
``` ```
3.使用全局的日期格式绑定新建自定义日期格式转化类之后在ServletConfig.java中进行注册 3.使用全局的日期格式绑定,新建自定义日期格式转化类,之后在 ServletConfig.java 中进行注册
```java ```java
package com.heibaiying.convert; package com.heibaiying.convert;
@ -558,7 +558,7 @@ public void addFormatters(FormatterRegistry registry) {
## 五、数据校验 ## 五、数据校验
1.spring支持的数据校验是JSR303的标准需要引入依赖的jar包 1.spring 支持的数据校验是 JSR303 的标准,需要引入依赖的 jar
```java ```java
<!-- 数据校验依赖包 --> <!-- 数据校验依赖包 -->
@ -574,7 +574,7 @@ public void addFormatters(FormatterRegistry registry) {
</dependency> </dependency>
``` ```
2.新建测试ParamValidController.java主要是在需要校验的参数前加上@Validated声明参数需要被校验同时加上bindingResult参数这个参数中包含了校验的结果 2.新建测试 ParamValidController.java主要是在需要校验的参数前加上@Validated,声明参数需要被校验,同时加上 bindingResult 参数,这个参数中包含了校验的结果
```java ```java
package com.heibaiying.controller; package com.heibaiying.controller;
@ -620,7 +620,7 @@ public class ParamValidController {
``` ```
3.在Programmer.java的对应属性上加上注解约束(支持的注解可以在javax.validation.constraints包中查看) 3.在 Programmer.java 的对应属性上加上注解约束 (支持的注解可以在 javax.validation.constraints 包中查看)
```java ```java
package com.heibaiying.bean; package com.heibaiying.bean;
@ -655,7 +655,7 @@ public class Programmer {
#### 6.1 文件上传 #### 6.1 文件上传
1.在ServletConfig.java中进行配置使之支持文件上传 1.在 ServletConfig.java 中进行配置,使之支持文件上传
```java ```java
/** /**
@ -671,7 +671,7 @@ public CommonsMultipartResolver multipartResolver(){
} }
``` ```
2.新建测试上传的FileController.java 2.新建测试上传的 FileController.java
```java ```java
package com.heibaiying.controller; package com.heibaiying.controller;
@ -714,7 +714,7 @@ public class FileController {
*/ */
@PostMapping("upFile") @PostMapping("upFile")
public String upFile(MultipartFile file, HttpSession session) { public String upFile(MultipartFile file, HttpSession session) {
//保存在项目根目录下image文件夹下如果文件夹不存在则创建 //保存在项目根目录下 image 文件夹下,如果文件夹不存在则创建
FileUtil.saveFile(file, session.getServletContext().getRealPath("/image")); FileUtil.saveFile(file, session.getServletContext().getRealPath("/image"));
// success.jsp 就是一个简单的成功页面 // success.jsp 就是一个简单的成功页面
return "success"; return "success";
@ -732,7 +732,7 @@ public class FileController {
} }
/*** /***
* 多文件上传方式2 分别为不同文件指定不同名字 * 多文件上传方式 2 分别为不同文件指定不同名字
*/ */
@PostMapping("upFiles2") @PostMapping("upFiles2")
public String upFile(String extendParam, public String upFile(String extendParam,
@ -748,7 +748,7 @@ public class FileController {
``` ```
3.其中工具类FileUtil.java代码如下 3.其中工具类 FileUtil.java 代码如下
```java ```java
package com.heibaiying.utils; package com.heibaiying.utils;
@ -803,7 +803,7 @@ public class FileUtil {
} }
``` ```
4.新建用于上传的jsp页面上传文件时表单必须声明 enctype="multipart/form-data" 4.新建用于上传的 jsp 页面,上传文件时表单必须声明 enctype="multipart/form-data"
```jsp ```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %>
@ -820,13 +820,13 @@ public class FileUtil {
</form> </form>
<form action="${pageContext.request.contextPath }/upFiles" method="post" enctype="multipart/form-data"> <form action="${pageContext.request.contextPath }/upFiles" method="post" enctype="multipart/form-data">
请选择上传文件(多选)<input name="file" type="file" multiple><br> 请选择上传文件 (多选)<input name="file" type="file" multiple><br>
<input type="submit" value="点击上传文件"> <input type="submit" value="点击上传文件">
</form> </form>
<form action="${pageContext.request.contextPath }/upFiles2" method="post" enctype="multipart/form-data"> <form action="${pageContext.request.contextPath }/upFiles2" method="post" enctype="multipart/form-data">
请选择上传文件1<input name="file1" type="file"><br> 请选择上传文件 1<input name="file1" type="file"><br>
请选择上传文件2<input name="file2" type="file"><br> 请选择上传文件 2<input name="file2" type="file"><br>
文件内容额外备注: <input name="extendParam" type="text"><br> 文件内容额外备注: <input name="extendParam" type="text"><br>
<input type="submit" value="点击上传文件"> <input type="submit" value="点击上传文件">
</form> </form>
@ -838,7 +838,7 @@ public class FileUtil {
#### 6.2 文件下载 #### 6.2 文件下载
1.在fileController.java中加上方法 1.在 fileController.java 中加上方法:
```java ```java
/*** /***
@ -869,7 +869,7 @@ public class FileUtil {
} }
``` ```
2.其中fileDownload.jsp 如下: 2.其中 fileDownload.jsp 如下:
```jsp ```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %>
@ -887,7 +887,7 @@ public class FileUtil {
## 七、Restful风格的请求 ## 七、Restful风格的请求
1.新建Pet.java实体类 1.新建 Pet.java 实体类
```java ```java
package com.heibaiying.bean; package com.heibaiying.bean;
@ -896,7 +896,7 @@ import lombok.Data;
/** /**
* @author : heibaiying * @author : heibaiying
* @description :测试restful风格的实体类 * @description :测试 restful 风格的实体类
*/ */
@Data @Data
@ -909,13 +909,13 @@ public class Pet {
``` ```
2.新建RestfulController.java@PathVariable@ModelAttribute注解进行参数绑定 2.新建 RestfulController.java@PathVariable@ModelAttribute 注解进行参数绑定。
注: 在REST中资源通过URL进行识别和定位。REST中的行为是通过HTTP方法定义的。在进行不同行为时对应HTTP方法和Spring注解分别如下 注: 在 REST 中,资源通过 URL 进行识别和定位。REST 中的行为是通过 HTTP 方法定义的。在进行不同行为时对应 HTTP 方法和 Spring 注解分别如下:
- 创建资源时POSTPostMapping - 创建资源时POSTPostMapping
- 读取资源时GET @GetMapping - 读取资源时GET @GetMapping
- 更新资源时PUTPATCHPutMapping、PatchMapping - 更新资源时PUTPATCHPutMapping、PatchMapping
- 删除资源时DELETEDeleteMapping - 删除资源时DELETEDeleteMapping
```java ```java

View File

@ -21,7 +21,7 @@
### 1.1 项目搭建 ### 1.1 项目搭建
1.新建maven web工程并引入相应的依赖 1.新建 maven web 工程,并引入相应的依赖
```xml ```xml
<properties> <properties>
@ -63,7 +63,7 @@
</dependencies> </dependencies>
``` ```
2.配置web.xml 2.配置 web.xml
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -73,7 +73,7 @@
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"> version="3.1">
<!--配置spring前端控制器--> <!--配置 spring 前端控制器-->
<servlet> <servlet>
<servlet-name>springMvc</servlet-name> <servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
@ -92,7 +92,7 @@
</web-app> </web-app>
``` ```
3.在resources下新建springApplication.xml文件文件内容如下: 3.在 resources 下新建 springApplication.xml 文件,文件内容如下:
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -107,7 +107,7 @@
<!-- 开启注解包扫描--> <!-- 开启注解包扫描-->
<context:component-scan base-package="com.heibaiying.*"/> <context:component-scan base-package="com.heibaiying.*"/>
<!--使用默认的Servlet来响应静态文件 详见 1.2 --> <!--使用默认的 Servlet 来响应静态文件 详见 1.2 -->
<mvc:default-servlet-handler/> <mvc:default-servlet-handler/>
<!-- 开启注解驱动 详见 1.2 --> <!-- 开启注解驱动 详见 1.2 -->
@ -125,7 +125,7 @@
</beans> </beans>
``` ```
4.在src 下新建controller用于测试 4.在 src 下新建 controller 用于测试
```java ```java
package com.heibaiying.controller; package com.heibaiying.controller;
@ -152,7 +152,7 @@ public class HelloController {
``` ```
5.在WEB-INF 下新建jsp文件夹新建hello.jsp 文件 5.在 WEB-INF 下新建 jsp 文件夹,新建 hello.jsp 文件
```jsp ```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %>
@ -166,15 +166,15 @@ public class HelloController {
</html> </html>
``` ```
6.启动tomcat服务访问localhost:8080/mvc/hello 6.启动 tomcat 服务,访问 localhost:8080/mvc/hello
### 1.2 相关配置讲解 ### 1.2 相关配置讲解
**1.\<mvc:default-servlet-handler/>** **1.\<mvc:default-servlet-handler/>**
在web.xml配置中我们将DispatcherServlet的拦截路径设置为“\”则spring会捕获所有web请求包括对静态资源的请求为了正确处理对静态资源的请求spring提供了两种解决方案 web.xml 配置中,我们将 DispatcherServlet 的拦截路径设置为“\”,则 spring 会捕获所有 web 请求包括对静态资源的请求为了正确处理对静态资源的请求spring 提供了两种解决方案:
- 配置\<mvc:default-servlet-handler/> 配置<mvc:default-servlet-handler />会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler它会对进入DispatcherServletURL进行筛查如果发现是静态资源的请求就将该请求转由Web应用服务器默认的Servlet处理如果不是静态资源的请求才由DispatcherServlet继续处理。 - 配置\<mvc:default-servlet-handler/> 配置<mvc:default-servlet-handler />后,会在 Spring MVC 上下文中定义一个 org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler它会对进入 DispatcherServletURL 进行筛查,如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理。
- 配置\<mvc:resources /> :指定静态资源的位置和路径映射: - 配置\<mvc:resources /> :指定静态资源的位置和路径映射:
@ -187,13 +187,13 @@ public class HelloController {
**2.\<mvc:annotation-driven/>** **2.\<mvc:annotation-driven/>**
<mvc:annotation-driven /> 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter <mvc:annotation-driven /> 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter
两个bean,用以支持@Controllers分发请求。并提供了数据绑定、参数转换、json转换等功能所以必须加上这个配置。 两个 bean,用以支持@Controllers 分发请求。并提供了数据绑定、参数转换、json 转换等功能,所以必须加上这个配置。
## 二、配置自定义拦截器 ## 二、配置自定义拦截器
1.创建自定义拦截器实现接口HandlerInterceptor这里我们创建两个拦截器用于测试拦截器方法的执行顺序 1.创建自定义拦截器,实现接口 HandlerInterceptor这里我们创建两个拦截器用于测试拦截器方法的执行顺序
```java ```java
package com.heibaiying.interceptors; package com.heibaiying.interceptors;
@ -211,17 +211,17 @@ import javax.servlet.http.HttpServletResponse;
public class MyFirstInterceptor implements HandlerInterceptor { public class MyFirstInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("进入第一个拦截器preHandle"); System.out.println("进入第一个拦截器 preHandle");
return true; return true;
} }
// 需要注意的是,如果对应的程序报错,不一定会进入这个方法 但一定会进入afterCompletion这个方法 // 需要注意的是,如果对应的程序报错,不一定会进入这个方法 但一定会进入 afterCompletion 这个方法
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
System.out.println("进入第一个拦截器postHandle"); System.out.println("进入第一个拦截器 postHandle");
} }
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("进入第一个拦截器afterCompletion"); System.out.println("进入第一个拦截器 afterCompletion");
} }
} }
``` ```
@ -242,22 +242,22 @@ import javax.servlet.http.HttpServletResponse;
public class MySecondInterceptor implements HandlerInterceptor { public class MySecondInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("进入第二个拦截器preHandle"); System.out.println("进入第二个拦截器 preHandle");
return true; return true;
} }
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
System.out.println("进入第二个拦截器postHandle"); System.out.println("进入第二个拦截器 postHandle");
} }
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("进入第二个拦截器afterCompletion"); System.out.println("进入第二个拦截器 afterCompletion");
} }
} }
``` ```
2.在springApplication.xml中注册自定义拦截器 2.在 springApplication.xml 中注册自定义拦截器
```xml ```xml
<!--配置拦截器--> <!--配置拦截器-->
@ -276,7 +276,7 @@ public class MySecondInterceptor implements HandlerInterceptor {
3.关于多个拦截器方法执行顺序的说明 3.关于多个拦截器方法执行顺序的说明
拦截器的执行顺序是按声明的先后顺序执行的先声明的拦截器中的preHandle方法会先执行然而它的postHandle方法和afterCompletion方法却会后执行。 拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的 preHandle 方法会先执行,然而它的 postHandle 方法和 afterCompletion 方法却会后执行。
@ -337,7 +337,7 @@ public class NoAuthExceptionResolver implements HandlerExceptionResolver {
return new ModelAndView(); return new ModelAndView();
} }
// 判断是否是Ajax请求 // 判断是否是 Ajax 请求
private boolean isAjax(HttpServletRequest request) { private boolean isAjax(HttpServletRequest request) {
return "XMLHttpRequest".equalsIgnoreCase(request.getHeader("X-Requested-With")); return "XMLHttpRequest".equalsIgnoreCase(request.getHeader("X-Requested-With"));
} }
@ -345,14 +345,14 @@ public class NoAuthExceptionResolver implements HandlerExceptionResolver {
``` ```
3.在springApplication.xml注册自定义异常处理器 3.在 springApplication.xml 注册自定义异常处理器
```xml ```xml
<!--配置全局异常处理器--> <!--配置全局异常处理器-->
<bean class="com.heibaiying.exception.NoAuthExceptionResolver"/> <bean class="com.heibaiying.exception.NoAuthExceptionResolver"/>
``` ```
4.定义测试controller抛出自定义异常 4.定义测试 controller抛出自定义异常
```java ```java
@Controller @Controller
@ -372,7 +372,7 @@ public class HelloController {
} }
``` ```
调用这个controller时同时也可以验证在拦截器部分提到的如果对应的程序报错拦截器不一定会进入postHandle这个方法 但一定会进入afterCompletion这个方法 注:调用这个 controller 时,同时也可以验证在拦截器部分提到的:如果对应的程序报错,拦截器不一定会进入 postHandle 这个方法 但一定会进入 afterCompletion 这个方法
@ -380,7 +380,7 @@ public class HelloController {
### 4.1 参数绑定 ### 4.1 参数绑定
1.新建Programmer.java 1.新建 Programmer.java
```java ```java
package com.heibaiying.bean; package com.heibaiying.bean;
@ -405,9 +405,9 @@ public class Programmer {
``` ```
注:@Data 是lombok包下的注解用来生成相应的set、get方法使得类的书写更为简洁。 注:@Data 是 lombok 包下的注解,用来生成相应的 set、get 方法,使得类的书写更为简洁。
2.新建ParamBindController.java 文件 2.新建 ParamBindController.java 文件
```java ```java
package com.heibaiying.controller; package com.heibaiying.controller;
@ -468,7 +468,7 @@ public class ParamBindController {
``` ```
3.新建param.jsp 文件 3.新建 param.jsp 文件
```jsp ```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %>
@ -488,11 +488,11 @@ public class ParamBindController {
``` ```
4.启动tomcat用[postman](https://www.getpostman.com/)软件发送请求进行测试 4.启动 tomcat用[postman](https://www.getpostman.com/) 软件发送请求进行测试
### 4.2 关于日期格式转换的三种方法 ### 4.2 关于日期格式转换的三种方法
1.如上实例代码所示在对应的controller中初始化绑定 1.如上实例代码所示,在对应的 controller 中初始化绑定
```java ```java
@InitBinder @InitBinder
@ -501,13 +501,13 @@ public class ParamBindController {
} }
``` ```
2.利用@DateTimeFormat注解如果是用实体类去接收参数则在对应的属性上用@DateTimeFormat和@JsonFormat声明 2.利用@DateTimeFormat 注解,如果是用实体类去接收参数,则在对应的属性上用@DateTimeFormat 和@JsonFormat 声明
```java ```java
public String param(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday) public String param(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday)
``` ```
3.使用全局的日期格式绑定新建自定义日期格式转化类之后在springApplication.xml中进行注册 3.使用全局的日期格式绑定,新建自定义日期格式转化类,之后在 springApplication.xml 中进行注册
```java ```java
package com.heibaiying.convert; package com.heibaiying.convert;
@ -553,7 +553,7 @@ springApplication.xml
## 五、数据校验 ## 五、数据校验
1.spring支持的数据校验是JSR303的标准需要引入依赖的jar包 1.spring 支持的数据校验是 JSR303 的标准,需要引入依赖的 jar
```java ```java
<!-- 数据校验依赖包 --> <!-- 数据校验依赖包 -->
@ -569,7 +569,7 @@ springApplication.xml
</dependency> </dependency>
``` ```
2.新建测试ParamValidController.java主要是在需要校验的参数前加上@Validated声明参数需要被校验同时加上bindingResult参数这个参数中包含了校验的结果 2.新建测试 ParamValidController.java主要是在需要校验的参数前加上@Validated声明参数需要被校验同时加上 bindingResult 参数,这个参数中包含了校验的结果
```java ```java
package com.heibaiying.controller; package com.heibaiying.controller;
@ -615,7 +615,7 @@ public class ParamValidController {
``` ```
3.在Programmer.java的对应属性上加上注解约束(支持的注解可以在javax.validation.constraints包中查看) 3.在 Programmer.java 的对应属性上加上注解约束 (支持的注解可以在 javax.validation.constraints 包中查看)
```java ```java
package com.heibaiying.bean; package com.heibaiying.bean;
@ -650,7 +650,7 @@ public class Programmer {
#### 6.1 文件上传 #### 6.1 文件上传
1.在springApplication.xml中进行配置使之支持文件上传 1.在 springApplication.xml 中进行配置,使之支持文件上传
```xml ```xml
<!--配置文件上传--> <!--配置文件上传-->
@ -663,7 +663,7 @@ public class Programmer {
</bean> </bean>
``` ```
2.新建测试上传的FileController.java 2.新建测试上传的 FileController.java
```java ```java
package com.heibaiying.controller; package com.heibaiying.controller;
@ -706,7 +706,7 @@ public class FileController {
*/ */
@PostMapping("upFile") @PostMapping("upFile")
public String upFile(MultipartFile file, HttpSession session) { public String upFile(MultipartFile file, HttpSession session) {
//保存在项目根目录下image文件夹下如果文件夹不存在则创建 //保存在项目根目录下 image 文件夹下,如果文件夹不存在则创建
FileUtil.saveFile(file, session.getServletContext().getRealPath("/image")); FileUtil.saveFile(file, session.getServletContext().getRealPath("/image"));
// success.jsp 就是一个简单的成功页面 // success.jsp 就是一个简单的成功页面
return "success"; return "success";
@ -724,7 +724,7 @@ public class FileController {
} }
/*** /***
* 多文件上传方式2 分别为不同文件指定不同名字 * 多文件上传方式 2 分别为不同文件指定不同名字
*/ */
@PostMapping("upFiles2") @PostMapping("upFiles2")
public String upFile(String extendParam, public String upFile(String extendParam,
@ -740,7 +740,7 @@ public class FileController {
``` ```
3.其中工具类FileUtil.java代码如下 3.其中工具类 FileUtil.java 代码如下
```java ```java
package com.heibaiying.utils; package com.heibaiying.utils;
@ -795,7 +795,7 @@ public class FileUtil {
} }
``` ```
4.新建用于上传的jsp页面上传文件时表单必须声明 enctype="multipart/form-data" 4.新建用于上传的 jsp 页面,上传文件时表单必须声明 enctype="multipart/form-data"
```jsp ```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %>
@ -812,13 +812,13 @@ public class FileUtil {
</form> </form>
<form action="${pageContext.request.contextPath }/upFiles" method="post" enctype="multipart/form-data"> <form action="${pageContext.request.contextPath }/upFiles" method="post" enctype="multipart/form-data">
请选择上传文件(多选)<input name="file" type="file" multiple><br> 请选择上传文件 (多选)<input name="file" type="file" multiple><br>
<input type="submit" value="点击上传文件"> <input type="submit" value="点击上传文件">
</form> </form>
<form action="${pageContext.request.contextPath }/upFiles2" method="post" enctype="multipart/form-data"> <form action="${pageContext.request.contextPath }/upFiles2" method="post" enctype="multipart/form-data">
请选择上传文件1<input name="file1" type="file"><br> 请选择上传文件 1<input name="file1" type="file"><br>
请选择上传文件2<input name="file2" type="file"><br> 请选择上传文件 2<input name="file2" type="file"><br>
文件内容额外备注: <input name="extendParam" type="text"><br> 文件内容额外备注: <input name="extendParam" type="text"><br>
<input type="submit" value="点击上传文件"> <input type="submit" value="点击上传文件">
</form> </form>
@ -830,7 +830,7 @@ public class FileUtil {
#### 6.2 文件下载 #### 6.2 文件下载
1.在fileController.java中加上方法 1.在 fileController.java 中加上方法:
```java ```java
/*** /***
@ -861,7 +861,7 @@ public class FileUtil {
} }
``` ```
2.其中fileDownload.jsp 如下: 2.其中 fileDownload.jsp 如下:
```jsp ```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %>
@ -879,7 +879,7 @@ public class FileUtil {
## 七、Restful风格的请求 ## 七、Restful风格的请求
1.新建Pet.java实体类 1.新建 Pet.java 实体类
```java ```java
package com.heibaiying.bean; package com.heibaiying.bean;
@ -888,7 +888,7 @@ import lombok.Data;
/** /**
* @author : heibaiying * @author : heibaiying
* @description :测试restful风格的实体类 * @description :测试 restful 风格的实体类
*/ */
@Data @Data
@ -901,13 +901,13 @@ public class Pet {
``` ```
2.新建RestfulController.java用@PathVariable和@ModelAttribute注解进行参数绑定。 2.新建 RestfulController.java用@PathVariable 和@ModelAttribute 注解进行参数绑定。
注: 在REST中资源通过URL进行识别和定位。REST中的行为是通过HTTP方法定义的。在进行不同行为时对应HTTP方法和Spring注解分别如下 注: 在 REST 中,资源通过 URL 进行识别和定位。REST 中的行为是通过 HTTP 方法定义的。在进行不同行为时对应 HTTP 方法和 Spring 注解分别如下:
- 创建资源时POSTPostMapping - 创建资源时POSTPostMapping
- 读取资源时GET @GetMapping - 读取资源时GET @GetMapping
- 更新资源时PUTPATCHPutMapping、PatchMapping - 更新资源时PUTPATCHPutMapping、PatchMapping
- 删除资源时DELETEDeleteMapping - 删除资源时DELETEDeleteMapping
```java ```java