├── .nojekyll ├── 01~基础使用 ├── README.md ├── 06~视图与模板 │ ├── 模板渲染.md │ └── 静态资源.md ├── 08~打包与部署 │ ├── README.md │ ├── Docker 与 K8s.md │ └── K8s 部署 Spring │ │ └── README.md ├── 04~请求与响应 │ ├── 接口文档 │ │ └── Springfox.md │ ├── 异常处理 │ │ ├── README.md │ │ └── 重试机制.md │ ├── 异步处理 │ │ ├── README.md │ │ └── Async │ │ │ └── Async.md │ ├── 响应返回 │ │ └── README.md │ └── 路由与参数 │ │ └── README.md ├── 03~依赖注入 │ ├── 应用上下文 │ │ ├── README.md │ │ ├── Spring Runner.md │ │ └── 自动配置.md │ ├── README.md │ ├── Bean │ │ └── Bean 注入.md │ └── 参数配置 │ │ └── README.md ├── .DS_Store ├── 09~定时任务 │ ├── README.md │ ├── 延时任务.md │ ├── 定时器.md │ └── TaskScheduler.md ├── 02~快速开始 │ └── DevTools.md └── 05~拦截与切面 │ ├── README.md │ ├── 异步拦截器.md │ └── 过滤器.md ├── 04~工程实践 ├── 消息通知 │ ├── 微信.md │ ├── 短信.md │ ├── 钉钉.md │ ├── README.md │ └── 邮件.md ├── 监控与运维 │ ├── 分布式跟踪 │ │ ├── 应用监控.md │ │ ├── 分布式链路跟踪.md │ │ └── README.md │ ├── 指标度量 │ │ └── Micrometer │ │ │ └── README.md │ ├── README.md │ └── Prometheus.md ├── .DS_Store ├── Spring DDD 实战 │ └── README.link ├── 测试 │ ├── .DS_Store │ ├── JMH │ │ └── README.md │ ├── Mock │ │ └── README.md │ ├── JUnit 4 │ │ ├── README.md │ │ ├── 测试用例.md │ │ └── 单元测试.md │ └── JUnit 5 │ │ ├── README.md │ │ └── 测试用例.md └── 日志处理 │ ├── Slf4j │ └── Slf4j.md │ └── README.md ├── 05~实践案例 └── README.md ├── 02~数据库 ├── 09~分布式事务 │ ├── README.md │ └── Seate │ │ └── README.link ├── 03~ORM │ ├── Hibernate │ │ └── README.md │ ├── .DS_Store │ ├── README.md │ ├── JPA │ │ └── README.md │ └── 99~参考资料 │ │ └── 2021-Java ORM 概念总结.md ├── 04~缓存与 KV 存储 │ ├── 6.文档数据库 │ │ └── README.md │ ├── Redis │ │ ├── README.md │ │ └── Redisson │ │ │ ├── 锁.md │ │ │ ├── README.md │ │ │ ├── 集合.md │ │ │ ├── 服务.md │ │ │ └── 对象.md │ ├── .DS_Store │ ├── README.md │ ├── Hazelcast │ │ └── README.md │ └── 进程内缓存 │ │ ├── Ehcache.md │ │ └── Cache 注解.md ├── 02~MyBatis │ ├── MyBatis 基础使用 │ │ ├── 02~工程架构 │ │ │ ├── 自定义插件.md │ │ │ └── README.md │ │ └── 01~配置使用 │ │ │ └── 2023~MyBatis 基础使用 │ │ │ ├── 05~SpringBoot 集成.md │ │ │ ├── 04~注解映射.md │ │ │ └── 06~流式查询.md │ ├── .DS_Store │ └── Mybatis Plus │ │ ├── README.md │ │ ├── 2023~Mybatis Plus 官方案例 │ │ └── 06~多租户与优化.md │ │ └── 2024.11~MyBatis Plus BaseMapper 与 IService 差异.md ├── .DS_Store ├── 01~连接池 │ ├── .DS_Store │ ├── README.md │ ├── Druid │ │ └── README.md │ └── HikariCP │ │ └── README.md ├── 05~数据库工具 │ ├── .DS_Store │ ├── 数据库迁移 │ │ ├── README.md │ │ └── Flyway │ │ │ └── README.md │ └── 嵌入式数据库 │ │ ├── HSQLDB │ │ ├── README.md │ │ ├── 运行模式.md │ │ └── 集成到 Spring Boot.md │ │ └── H2 │ │ └── README.md └── 01~Spring JDBC │ ├── .DS_Store │ ├── 事务 │ ├── Transactonal.md │ ├── 事务回滚.md │ └── 编程式事务.md │ ├── README.md │ └── Spring 数据库连接池.md ├── INTRODUCTION.md ├── 03~Spring Security ├── RBAC │ └── README.md ├── OAuth │ ├── README.md │ ├── .DS_Store │ ├── OAuth2 │ │ └── README.md │ └── Keycloak │ │ └── README.md ├── .DS_Store ├── 权限验证 │ ├── .DS_Store │ ├── 基于 JWT 的验证 │ │ ├── JWT 解析库 │ │ │ └── nimbus-jose-jwt.md │ │ ├── README.md │ │ └── 99~参考资料 │ │ │ └── 2023~实现基于 JWT 的登录认证 │ │ │ ├── 02~注册与登录.md │ │ │ └── 03~访问与鉴权.md │ ├── HTTP Basic Auth.md │ └── 表单登录.md ├── 策略配置 │ ├── CORS.md │ └── CSRF.md └── README.md ├── 09~源码解析 ├── 深入请求处理 │ ├── Tomcat │ │ ├── README.md │ │ └── SpringBoot Tomcat.md │ ├── WebSocket.md │ ├── .DS_Store │ ├── 请求的处理流程.md │ ├── Servlet │ │ ├── README.md │ │ └── 请求处理.md │ ├── README.md │ └── Jetty │ │ └── README.md ├── .DS_Store ├── 99~参考资料 │ └── 2021-半糖-Spring 源码解析 │ │ └── README.md └── 依赖机制 │ ├── Jar 加载.md │ ├── README.md │ └── 启动流程.md ├── 10~Spring Cloud ├── Spring Cloud Netflix │ ├── README.md │ └── Spring Cloud Config.md ├── Spring Cloud Square │ ├── README.md │ └── Retrofit │ │ └── 99~参考资料 │ │ └── 2022~还在用 Feign?推荐一款微服务间调用神器,跟 SpringCloud 绝配!.md ├── Spring Cloud Alibaba │ ├── README.md │ ├── Sentinel │ │ └── 99~参考资料 │ │ │ └── 2019-Sentinel 实现熔断与限流.md │ ├── Nacos │ │ ├── 服务注册与发现.md │ │ └── README.md │ ├── .DS_Store │ └── Sofa │ │ ├── SOFAArk │ │ └── README.md │ │ └── SOFABoot │ │ ├── README.md │ │ └── 快速开始.md ├── .DS_Store └── README.md ├── .DS_Store ├── .github ├── .DS_Store └── ISSUE_TEMPLATE │ ├── custom.md │ ├── feature_request.md │ └── bug_report.md ├── 99~参考资料 ├── .DS_Store └── 2015~《Spring in Action》~V5 │ ├── 02~集成 Spring │ ├── 第 7 章 调用 REST 服务 │ │ ├── 7.3 小结.md │ │ ├── 第 7 章 调用 REST 服务.md │ │ └── 7.2 使用 Traverson 引导 REST API.md │ ├── 第 6 章 创建 REST 服务 │ │ ├── 图 6.2 显示最近创建的 tacos.jpg │ │ ├── 6.4 小结.md │ │ └── 第 6 章 创建 REST 服务.md │ ├── 第 9 章 集成 Spring │ │ ├── 9.4 总结.md │ │ └── 第 9 章 集成 Spring.md │ ├── 第 8 章 发送异步消息 │ │ ├── 8.4 小结.md │ │ └── 第 8 章 发送异步消息.md │ └── 第二部分 集成 Spring.md │ ├── 01~Spring 基础 │ ├── 第 3 章 处理数据 │ │ ├── 图 3.2.jpg │ │ ├── 3.3 小结.md │ │ └── 第 3 章 处理数据.md │ ├── 第 5 章 使用配置属性 │ │ ├── 图 5.1.jpg │ │ ├── 图 5.2 缺少配置属性元数据出现的警告.jpg │ │ ├── 图 5.6 配置属性元数据让属性值自动填充.jpg │ │ ├── 图 5.3 在 Spring Tool Suite 中悬停显示配置属性文档.jpg │ │ ├── 图 5.4 在 Spring Tool Suite 中使用快速弹出方式创建配置属性元数据.jpg │ │ ├── 5.4 小结.md │ │ └── 第 5 章 使用配置属性.md │ ├── 第 1 章 Spring 入门 │ │ ├── 图 1.4 选择依赖.jpg │ │ ├── 图 1.8 Taco Cloud 主页.jpg │ │ ├── 图1-1 通过Spring上下文管理应用组件和注入.png │ │ ├── 图 1.7 Spring Boot Dashboard 高亮.jpg │ │ ├── 1.5 小结.md │ │ ├── 第 1 章 Spring 入门.md │ │ ├── 1.4 俯瞰 Spring 风景线.md │ │ └── 1.1 什么是 Spring.md │ ├── 第 4 章 Spring 安全 │ │ ├── 图 4.2 Spring Security 提供了一个免费的普通登录页面.jpg │ │ ├── 图 4.1 使用 Spring Tool Suite 添加 security starter.jpg │ │ ├── 4.5 小结.md │ │ ├── 第 4 章 Spring 安全.md │ │ ├── 4.1 启用 Spring Security.md │ │ └── 4.4 了解你的用户.md │ ├── 第 2 章 开发 Web 应用程序 │ │ ├── 2.6 小结.md │ │ ├── 第 2 章 开发 Web 应用程序.md │ │ ├── 2.4 使用视图控制器.md │ │ └── 2.5 选择视图模板库.md │ └── 第一部分 Spring 基础.md │ └── 03~响应式 Spring │ ├── 第 10 章 Reactor 介绍 │ ├── 10.11.png │ ├── 10.12.png │ ├── 10.13.png │ ├── 10.14.png │ ├── 10.15.png │ ├── 10.16.png │ ├── 10.17.png │ ├── 10.18.png │ ├── 10.19.png │ ├── 10.2.jpg │ ├── 10.22.png │ ├── 图 10.1.jpg │ ├── 1583726317491.png │ ├── Acrobat_EgcrkessnX.png │ ├── Acrobat_hparWHSeug.png │ ├── Snipaste_2020-03-25_13-50-21.png │ ├── Snipaste_2020-03-25_14-31-23.png │ ├── Snipaste_2020-03-26_11-37-13.png │ ├── Snipaste_2020-03-26_13-49-36.png │ ├── Snipaste_2020-03-26_14-09-54.png │ ├── Snipaste_2020-03-25_14-31-23-1585192471116.png │ ├── 10.4 总结.md │ ├── 第 10 章 Reactor 介绍.md │ ├── 10.2 Reactor.md │ └── 10.1 理解响应式编程.md │ ├── 第三部分 响应式 Spring.md │ └── 第 11 章 开发响应式 API │ ├── 第 11 章 开发响应式 API.md │ └── 11.5 安全的响应式 Web API.md ├── .gitattributes ├── 00~特性与生态 ├── 生态圈 │ ├── README.md │ ├── Spring Messaging.md │ ├── Spring Cloud.md │ └── Spring Integration.md ├── 背景特性.md └── 框架对比.md ├── .gitignore └── README.md /.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /01~基础使用/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /04~工程实践/消息通知/微信.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /04~工程实践/消息通知/短信.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /04~工程实践/消息通知/钉钉.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /05~实践案例/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /02~数据库/09~分布式事务/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /INTRODUCTION.md: -------------------------------------------------------------------------------- 1 | # 本篇导读 2 | -------------------------------------------------------------------------------- /02~数据库/03~ORM/Hibernate/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /03~Spring Security/RBAC/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /04~工程实践/消息通知/README.md: -------------------------------------------------------------------------------- 1 | # 消息通知 2 | -------------------------------------------------------------------------------- /09~源码解析/深入请求处理/Tomcat/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /01~基础使用/06~视图与模板/模板渲染.md: -------------------------------------------------------------------------------- 1 | # 模板渲染 2 | -------------------------------------------------------------------------------- /02~数据库/04~缓存与 KV 存储/6.文档数据库/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /03~Spring Security/OAuth/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /04~工程实践/监控与运维/分布式跟踪/应用监控.md: -------------------------------------------------------------------------------- 1 | # 应用监控 2 | -------------------------------------------------------------------------------- /01~基础使用/08~打包与部署/README.md: -------------------------------------------------------------------------------- 1 | # Spring 应用发布与部署 2 | -------------------------------------------------------------------------------- /02~数据库/04~缓存与 KV 存储/Redis/README.md: -------------------------------------------------------------------------------- 1 | # Redis 2 | -------------------------------------------------------------------------------- /10~Spring Cloud/Spring Cloud Netflix/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /10~Spring Cloud/Spring Cloud Square/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /01~基础使用/04~请求与响应/接口文档/Springfox.md: -------------------------------------------------------------------------------- 1 | # Springfox 2 | -------------------------------------------------------------------------------- /01~基础使用/08~打包与部署/Docker 与 K8s.md: -------------------------------------------------------------------------------- 1 | # Docker 容器化部署 2 | -------------------------------------------------------------------------------- /01~基础使用/03~依赖注入/应用上下文/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot 应用上下文配置 2 | -------------------------------------------------------------------------------- /04~工程实践/监控与运维/指标度量/Micrometer/README.md: -------------------------------------------------------------------------------- 1 | # Micrometer 2 | -------------------------------------------------------------------------------- /09~源码解析/深入请求处理/WebSocket.md: -------------------------------------------------------------------------------- 1 | # Spring Boot WebSocket 处理 2 | -------------------------------------------------------------------------------- /02~数据库/02~MyBatis/MyBatis 基础使用/02~工程架构/自定义插件.md: -------------------------------------------------------------------------------- 1 | # Mybatis 自定义插件 2 | -------------------------------------------------------------------------------- /10~Spring Cloud/Spring Cloud Alibaba/README.md: -------------------------------------------------------------------------------- 1 | # Alibaba Cloud 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/.DS_Store -------------------------------------------------------------------------------- /02~数据库/09~分布式事务/Seate/README.link: -------------------------------------------------------------------------------- 1 | https://github.com/wx-chevalier/DistributedSystem-Notes -------------------------------------------------------------------------------- /04~工程实践/监控与运维/README.md: -------------------------------------------------------------------------------- 1 | # 监控与运维 2 | 3 | 本章我们会从 Spring 的角度来讨论常用的 Java 应用线上监控与运维的手段。 4 | -------------------------------------------------------------------------------- /02~数据库/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/02~数据库/.DS_Store -------------------------------------------------------------------------------- /.github/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/.github/.DS_Store -------------------------------------------------------------------------------- /01~基础使用/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/01~基础使用/.DS_Store -------------------------------------------------------------------------------- /04~工程实践/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/04~工程实践/.DS_Store -------------------------------------------------------------------------------- /09~源码解析/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/09~源码解析/.DS_Store -------------------------------------------------------------------------------- /99~参考资料/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/.DS_Store -------------------------------------------------------------------------------- /04~工程实践/Spring DDD 实战/README.link: -------------------------------------------------------------------------------- 1 | https://github.com/wx-chevalier/DDD-and-Clean-Architecture-Notes 2 | -------------------------------------------------------------------------------- /04~工程实践/测试/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/04~工程实践/测试/.DS_Store -------------------------------------------------------------------------------- /02~数据库/01~连接池/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/02~数据库/01~连接池/.DS_Store -------------------------------------------------------------------------------- /02~数据库/03~ORM/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/02~数据库/03~ORM/.DS_Store -------------------------------------------------------------------------------- /04~工程实践/消息通知/邮件.md: -------------------------------------------------------------------------------- 1 | # 邮件 2 | 3 | # Links 4 | 5 | - https://mp.weixin.qq.com/s/8UiEMpono-hUrRVwvDjUgA 6 | -------------------------------------------------------------------------------- /02~数据库/05~数据库工具/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/02~数据库/05~数据库工具/.DS_Store -------------------------------------------------------------------------------- /09~源码解析/深入请求处理/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/09~源码解析/深入请求处理/.DS_Store -------------------------------------------------------------------------------- /10~Spring Cloud/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/10~Spring Cloud/.DS_Store -------------------------------------------------------------------------------- /01~基础使用/09~定时任务/README.md: -------------------------------------------------------------------------------- 1 | # 异步任务 2 | 3 | # Links 4 | 5 | - https://mp.weixin.qq.com/s/TVh0Qe4jxI2pxVoOSVib8A 6 | -------------------------------------------------------------------------------- /02~数据库/02~MyBatis/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/02~数据库/02~MyBatis/.DS_Store -------------------------------------------------------------------------------- /03~Spring Security/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/03~Spring Security/.DS_Store -------------------------------------------------------------------------------- /09~源码解析/99~参考资料/2021-半糖-Spring 源码解析/README.md: -------------------------------------------------------------------------------- 1 | > [原文地址](https://www.bantanger.fun/pages/9c9e9c/#%E5%89%8D%E8%A8%80) 2 | -------------------------------------------------------------------------------- /02~数据库/01~Spring JDBC/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/02~数据库/01~Spring JDBC/.DS_Store -------------------------------------------------------------------------------- /02~数据库/04~缓存与 KV 存储/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/02~数据库/04~缓存与 KV 存储/.DS_Store -------------------------------------------------------------------------------- /02~数据库/04~缓存与 KV 存储/README.md: -------------------------------------------------------------------------------- 1 | # Java 缓存 2 | 3 | # Links 4 | 5 | - https://www.itcodemonkey.com/article/15233.html 6 | -------------------------------------------------------------------------------- /01~基础使用/09~定时任务/延时任务.md: -------------------------------------------------------------------------------- 1 | # 延时任务 2 | 3 | # Links 4 | 5 | - https://mp.weixin.qq.com/s/nxiUVtNZn8jvhU6daWI96Q 说说延时队列的实现有哪几种姿势 -------------------------------------------------------------------------------- /03~Spring Security/权限验证/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/03~Spring Security/权限验证/.DS_Store -------------------------------------------------------------------------------- /09~源码解析/深入请求处理/请求的处理流程.md: -------------------------------------------------------------------------------- 1 | # Spring MVC 请求的处理流程 2 | 3 | # Links 4 | 5 | - https://mp.weixin.qq.com/s/LGuXzhtHt2LNcgLq36jqyw 6 | -------------------------------------------------------------------------------- /03~Spring Security/OAuth/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/03~Spring Security/OAuth/.DS_Store -------------------------------------------------------------------------------- /09~源码解析/深入请求处理/Tomcat/SpringBoot Tomcat.md: -------------------------------------------------------------------------------- 1 | # Links 2 | 3 | - https://zhuanlan.zhihu.com/p/80314409 Tomcat 在 SpringBoot 中是如何启动的 4 | -------------------------------------------------------------------------------- /04~工程实践/测试/JMH/README.md: -------------------------------------------------------------------------------- 1 | # JMH 2 | 3 | # Links 4 | 5 | - JMH,Java 代码性能测试的终极利器、必须掌握 https://mp.weixin.qq.com/s/-0enKREKarGRipk0PkXgBA 6 | -------------------------------------------------------------------------------- /10~Spring Cloud/Spring Cloud Alibaba/Sentinel/99~参考资料/2019-Sentinel 实现熔断与限流.md: -------------------------------------------------------------------------------- 1 | > [原文地址](https://mp.weixin.qq.com/s/Qlnrj3QB773IpUjBpxSpyQ) 2 | -------------------------------------------------------------------------------- /04~工程实践/监控与运维/分布式跟踪/分布式链路跟踪.md: -------------------------------------------------------------------------------- 1 | # 分布式链路跟踪 2 | 3 | # Links 4 | 5 | - https://mp.weixin.qq.com/s/rIhBHiGWrfZVG8wZfUksjQ 日志排查问题困难?分布式日志链路跟踪来帮你 6 | -------------------------------------------------------------------------------- /09~源码解析/依赖机制/Jar 加载.md: -------------------------------------------------------------------------------- 1 | # Jar 加载 2 | 3 | # Links 4 | 5 | - https://mp.weixin.qq.com/s/hLtIh8z4RJD5zGkL-lK1zQ?from=groupmessage&isappinstalled=0 6 | -------------------------------------------------------------------------------- /09~源码解析/依赖机制/README.md: -------------------------------------------------------------------------------- 1 | # Spring IoC 2 | 3 | # Links 4 | 5 | - https://www.baeldung.com/inversion-control-and-dependency-injection-in-spring 6 | -------------------------------------------------------------------------------- /10~Spring Cloud/Spring Cloud Alibaba/Nacos/服务注册与发现.md: -------------------------------------------------------------------------------- 1 | # 服务注册与发现 2 | 3 | # Links 4 | 5 | - https://mp.weixin.qq.com/s/X8fbwQjYH3cGFsrOwLjx3Q 6 | -------------------------------------------------------------------------------- /03~Spring Security/OAuth/OAuth2/README.md: -------------------------------------------------------------------------------- 1 | # OAuth2 2 | 3 | # Links 4 | 5 | - https://mp.weixin.qq.com/s/E6qvcZYWMi4QCoYx_fG4RQ SpringBoot 整合 Oauth2 6 | -------------------------------------------------------------------------------- /10~Spring Cloud/Spring Cloud Alibaba/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/10~Spring Cloud/Spring Cloud Alibaba/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.xmind filter=lfs diff=lfs merge=lfs -text 2 | *.zip filter=lfs diff=lfs merge=lfs -text 3 | *.pdf filter=lfs diff=lfs merge=lfs -text 4 | -------------------------------------------------------------------------------- /02~数据库/01~连接池/README.md: -------------------------------------------------------------------------------- 1 | # 数据库连接池 2 | 3 | JDBC 连接池的实现并不复杂,主要是对 JDBC 中几个核心对象 Connection、Statement、PreparedStatement、CallableStatement 以及 ResultSet 的封装与动态代理。 4 | -------------------------------------------------------------------------------- /10~Spring Cloud/Spring Cloud Alibaba/Sofa/SOFAArk/README.md: -------------------------------------------------------------------------------- 1 | # SOFAArk 2 | 3 | # Links 4 | 5 | - https://houbb.github.io/2021/01/05/sofa-sofaark-01-overview 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | -------------------------------------------------------------------------------- /02~数据库/04~缓存与 KV 存储/Hazelcast/README.md: -------------------------------------------------------------------------------- 1 | # Hazelcast 2 | 3 | # Links 4 | 5 | - https://github.com/mumucache/mumu-hazelcast/wiki/hazelcast%E6%95%99%E7%A8%8B-%E5%85%A5%E9%97%A8 6 | -------------------------------------------------------------------------------- /04~工程实践/测试/Mock/README.md: -------------------------------------------------------------------------------- 1 | # Java Mock 测试 2 | 3 | 目前主流的 Mock 测试工具包含以下几种: 4 | 5 | - 动态代理:Mockito、EasyMock、MockRunner 6 | - 自定义类加载器:PowerMock 7 | - 运行时字节码修改:JMockit、TestableMock 8 | -------------------------------------------------------------------------------- /03~Spring Security/权限验证/基于 JWT 的验证/JWT 解析库/nimbus-jose-jwt.md: -------------------------------------------------------------------------------- 1 | # nimbus-jose-jwt 2 | 3 | # Links 4 | 5 | - [听说你的 JWT 库用起来特别扭,推荐这款贼好用的!](https://mp.weixin.qq.com/s/Jo3PZoa7nL99c8UCxPiTTA) 6 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/02~集成 Spring/第 7 章 调用 REST 服务/7.3 小结.md: -------------------------------------------------------------------------------- 1 | ## 7.3 小结 2 | 3 | - 客户端可以使用 RestTemplate 针对 REST API 发出 HTTP 请求。 4 | - Traverson 可以通过使用在响应中嵌入的超链接来让客户端引导 API。 5 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 3 章 处理数据/图 3.2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 3 章 处理数据/图 3.2.jpg -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 5 章 使用配置属性/图 5.1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 5 章 使用配置属性/图 5.1.jpg -------------------------------------------------------------------------------- /01~基础使用/04~请求与响应/异常处理/README.md: -------------------------------------------------------------------------------- 1 | # 异常处理 2 | 3 | # Links 4 | 5 | - https://www.zhihu.com/question/359626115/answer/927761057 6 | 7 | - https://www.baeldung.com/exception-handling-for-rest-with-spring 8 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.11.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.12.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.13.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.14.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.15.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.16.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.17.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.18.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.19.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.2.jpg -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.22.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 1 章 Spring 入门/图 1.4 选择依赖.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 1 章 Spring 入门/图 1.4 选择依赖.jpg -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/图 10.1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/图 10.1.jpg -------------------------------------------------------------------------------- /00~特性与生态/生态圈/README.md: -------------------------------------------------------------------------------- 1 | # 生态圈项目 2 | 3 | ![Spring Framework Runtime](https://s2.ax1x.com/2019/09/03/nFNE40.png) 4 | 5 | # Links 6 | 7 | - https://mp.weixin.qq.com/s/_LAW17MKWIIrKjLxcishRA?from=groupmessage&isappinstalled=0 8 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 5 章 使用配置属性/图 5.2 缺少配置属性元数据出现的警告.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 5 章 使用配置属性/图 5.2 缺少配置属性元数据出现的警告.jpg -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 5 章 使用配置属性/图 5.6 配置属性元数据让属性值自动填充.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 5 章 使用配置属性/图 5.6 配置属性元数据让属性值自动填充.jpg -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/1583726317491.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/1583726317491.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 1 章 Spring 入门/图 1.8 Taco Cloud 主页.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 1 章 Spring 入门/图 1.8 Taco Cloud 主页.jpg -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/02~集成 Spring/第 6 章 创建 REST 服务/图 6.2 显示最近创建的 tacos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/02~集成 Spring/第 6 章 创建 REST 服务/图 6.2 显示最近创建的 tacos.jpg -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Acrobat_EgcrkessnX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Acrobat_EgcrkessnX.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Acrobat_hparWHSeug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Acrobat_hparWHSeug.png -------------------------------------------------------------------------------- /10~Spring Cloud/Spring Cloud Alibaba/Nacos/README.md: -------------------------------------------------------------------------------- 1 | # Nacos 2 | 3 | Nacos 真正将配置从应用中剥离出来,统一管理,优雅的解决了配置的动态变更、持久化、运维成本等问题。应用自身既不需要去添加管理配置接口,也不需要自己去实现配置的持久化,更不需要引入定时任务以便降低运维成本。Nacos 提供的配置管理功能,将配置相关的所有逻辑都收拢,并且提供简单易用的 SDK,让应用的配置可以非常方便被 Nacos 管理起来。 4 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 1 章 Spring 入门/图1-1 通过Spring上下文管理应用组件和注入.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 1 章 Spring 入门/图1-1 通过Spring上下文管理应用组件和注入.png -------------------------------------------------------------------------------- /01~基础使用/08~打包与部署/K8s 部署 Spring/README.md: -------------------------------------------------------------------------------- 1 | # 在 Kubernetes 中部署 Spring 应用 2 | 3 | # Links 4 | 5 | - https://www.baeldung.com/spring-boot-minikube 6 | - https://github.com/eugenp/tutorials/tree/master/spring-cloud/spring-cloud-kubernetes/demo-backend 7 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 1 章 Spring 入门/图 1.7 Spring Boot Dashboard 高亮.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 1 章 Spring 入门/图 1.7 Spring Boot Dashboard 高亮.jpg -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Snipaste_2020-03-25_13-50-21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Snipaste_2020-03-25_13-50-21.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Snipaste_2020-03-25_14-31-23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Snipaste_2020-03-25_14-31-23.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Snipaste_2020-03-26_11-37-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Snipaste_2020-03-26_11-37-13.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Snipaste_2020-03-26_13-49-36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Snipaste_2020-03-26_13-49-36.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Snipaste_2020-03-26_14-09-54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Snipaste_2020-03-26_14-09-54.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 5 章 使用配置属性/图 5.3 在 Spring Tool Suite 中悬停显示配置属性文档.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 5 章 使用配置属性/图 5.3 在 Spring Tool Suite 中悬停显示配置属性文档.jpg -------------------------------------------------------------------------------- /10~Spring Cloud/Spring Cloud Square/Retrofit/99~参考资料/2022~还在用 Feign?推荐一款微服务间调用神器,跟 SpringCloud 绝配!.md: -------------------------------------------------------------------------------- 1 | > [原文地址](https://juejin.cn/post/7064742963015843870) 2 | 3 | # 还在用 Feign?推荐一款微服务间调用神器,跟 SpringCloud 绝配! 4 | 5 | OpenFeign 集成了 Ribbon 和 Hystrix,并简化了服务调用方式,使用起来非常方便。 6 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 4 章 Spring 安全/图 4.2 Spring Security 提供了一个免费的普通登录页面.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 4 章 Spring 安全/图 4.2 Spring Security 提供了一个免费的普通登录页面.jpg -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 3 章 处理数据/3.3 小结.md: -------------------------------------------------------------------------------- 1 | ## 3.3 小结 2 | 3 | - JdbcTemplate 大大简化了 JDBC 的工作。 4 | - 当需要知道数据库生成的 id 时,可以同时使用 PreparedStatementCreator 和 KeyHolder。 5 | - 为了方便执行数据插入,使用 SimpleJdbcInsert。 6 | - Spring Data JPA 使得 JPA 持久化就像编写存储库接口一样简单。 7 | -------------------------------------------------------------------------------- /04~工程实践/监控与运维/分布式跟踪/README.md: -------------------------------------------------------------------------------- 1 | # Log-Introduction 2 | 3 | 日志记录一直是每个 Java 应用程序,特别是服务端应用程序的标配。譬如 Log4j2/Log4j, Logback 等。 4 | 5 | # java.util.logging.Logger 6 | 7 | java.util.logging.Logger 不是什么新鲜东西了,1.4 就有了,可是因为 log4j 的存在,这个 logger 一直沉默着,其实在一些测试性的代码中,jdk 自带的 logger 比 log4j 更方便。 8 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 5 章 使用配置属性/图 5.4 在 Spring Tool Suite 中使用快速弹出方式创建配置属性元数据.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 5 章 使用配置属性/图 5.4 在 Spring Tool Suite 中使用快速弹出方式创建配置属性元数据.jpg -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Snipaste_2020-03-25_14-31-23-1585192471116.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/Snipaste_2020-03-25_14-31-23-1585192471116.png -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 4 章 Spring 安全/图 4.1 使用 Spring Tool Suite 添加 security starter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/Spring-Notes/HEAD/99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 4 章 Spring 安全/图 4.1 使用 Spring Tool Suite 添加 security starter.jpg -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/02~集成 Spring/第 9 章 集成 Spring/9.4 总结.md: -------------------------------------------------------------------------------- 1 | ## 9.4 总结 2 | 3 | - Spring Integration 允许定义数据在进入或离开应用程序时可以通过的流。 4 | - Integration 流可以以 XML、Java 或 Java DSL 配置的风格进行定义。 5 | - 消息网关和通道适配器充当集成信息流的入口和出口。 6 | - 消息可以被转化,分割,聚集,路由和由服务活化器在流动的过程中进行处理。 7 | - 消息通道连接集成流的组件。 8 | -------------------------------------------------------------------------------- /04~工程实践/监控与运维/Prometheus.md: -------------------------------------------------------------------------------- 1 | # Prometheus 2 | 3 | # Links 4 | 5 | - https://stackabuse.com/monitoring-spring-boot-apps-with-micrometer-prometheus-and-grafana/ 6 | - https://blog.kubernauts.io/https-blog-kubernauts-io-monitoring-java-spring-boot-applications-with-prometheus-part-1-c0512f2acd7b 7 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 5 章 使用配置属性/5.4 小结.md: -------------------------------------------------------------------------------- 1 | ## 5.4 小结 2 | 3 | - 可以使用 @ConfigurationProperties 注解 Spring bean,以支持从几个属性源之一注入值。 4 | - 可以在命令行参数、环境变量、JVM 系统属性、属性文件或 YAML 文件等选项中设置配置属性。 5 | - 配置属性可用于覆盖自动配置设置,包括指定数据源 URL 和日志级别的能力。 6 | - Spring profile 文件可以与属性源一起使用,根据活动配置文件有条件地设置配置属性。 7 | -------------------------------------------------------------------------------- /02~数据库/02~MyBatis/MyBatis 基础使用/02~工程架构/README.md: -------------------------------------------------------------------------------- 1 | # MyBatis 架构机制 2 | 3 | # Links 4 | 5 | - https://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&mid=2247487562&idx=1&sn=c0269f64ce806994b041924f3fc7f87c&chksm=9bd0bdd2aca734c48d43498adf1a39b64ae976a87584e347f34fc33c033324b1e0d9b55d5f2c&scene=21#wechat_redirect 6 | -------------------------------------------------------------------------------- /02~数据库/05~数据库工具/数据库迁移/README.md: -------------------------------------------------------------------------------- 1 | # 数据库迁移 2 | 3 | 在多人开发的项目中,我们都习惯了使用 SVN 或者 Git 来对代码做版本控制,主要的目的就是为了解决多人开发代码冲突和版本回退的问题。其实,数据库的变更也需要版本控制,在日常开发中,我们经常会遇到下面的问题: 4 | 5 | - 自己写的 SQL 忘了在所有环境执行; 6 | - 别人写的 SQL 我们不能确定是否都在所有环境执行过了; 7 | - 有人修改了已经执行过的 SQL,期望再次执行; 8 | - 需要新增环境做数据迁移; 9 | - 每次发版需要手动控制先发 DB 版本,再发布应用版本; 10 | - 其它场景... 11 | -------------------------------------------------------------------------------- /02~数据库/05~数据库工具/嵌入式数据库/HSQLDB/README.md: -------------------------------------------------------------------------------- 1 | # HSQLDB 2 | 3 | hsql 数据库是一款纯 Java 编写的免费数据库,许可是 BSD-style 的协议,如果你是使用 Java 编程的话,不凡考虑一下使用它,相对其他数据库来说,其体积小,才 563kb。仅一个 hsqldb.jar 文件就包括了数据库引擎,数据库驱动,还有其他用户界面操作等内容。在 Java 开源世界里,hsql 是极为受欢迎的(就 Java 本身来说),JBoss 应用程序服务器默认也提供了这个数据库引 擎。由于其体积小的原因,又是纯 Java 设计,又支持 SQL99,SQL2003 大部分的标准,所以也是作为商业应用程序展示的一种选择。 4 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 4 章 Spring 安全/4.5 小结.md: -------------------------------------------------------------------------------- 1 | ## 4.5 小结 2 | 3 | - Spring Security 自动配置是一种很好的开始学习安全的方式,但大多数应用程序需要明确地配置安全,以满足其独特的安全需求。 4 | - 用户细节可以在关系数据库、LDAP 或完全自定义实现支持的用户存储中进行管理。 5 | - Spring Security 自动防御 CSRF 攻击。 6 | - 通过 SecurityContext 对象(从 SecurityContextHolder. getcontext() 中返回)或使用 @AuthenticationPrincipal 注入控制器中,可以获得认证用户的信息。 7 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.4 总结.md: -------------------------------------------------------------------------------- 1 | ## 10.4 总结 2 | 3 | - 响应式编程包括创建数据流通过的管道。 4 | - Reactor Stream 规范定义了四种类型:Publisher、Subscriber、Subscription 和 Transformer(Publisher 和 Subscriber 的组合)。 5 | - Project Reactor 实现了 Reactive Steam,并将流定义抽象为两种主要类型,Flux 和 Mono,每种类型都提供数百个操作。 6 | - Spring 5 利用 Reactor 创建响应式控制器、存储库、REST 客户端和其他反应式框架支持。 7 | -------------------------------------------------------------------------------- /10~Spring Cloud/README.md: -------------------------------------------------------------------------------- 1 | # Spring Cloud 2 | 3 | Spring Cloud 来源于 Spring,质量、稳定性、持续性都可以得到保证,它天然支持 Spring Boot,更加便于业务落地,可谓 Java 领域最适合做微服务的框架。相比于其它框架,Spring Cloud 对微服务周边环境的支持力度最大,对于中小企业来讲,使用门槛较低,Spring Cloud 是微服务架构的最佳落地方案。 4 | 5 | # Links 6 | 7 | - https://juejin.im/post/5be13b83f265da6116393fc7 8 | - Spring Cloud 系列教程 https://souyunku.gitbooks.io/spring-cloud/content/ 9 | -------------------------------------------------------------------------------- /10~Spring Cloud/Spring Cloud Netflix/Spring Cloud Config.md: -------------------------------------------------------------------------------- 1 | # Spring Cloud Config 2 | 3 | 在上面的例子中,我们将 Spring Boot 应用需要的配置内容都放在了项目工程中,虽然我们已经能够通过 spring.profiles.active 或是通过 Maven 来实现多环境的支持。但是,当我们的团队逐渐壮大,分工越来越细致之后,往往我们不需要让开发人员知道测试或是生成环境的细节,而是希望由每个环境各自的负责人(QA 或是运维)来集中维护这些信息。那么如果还是以这样的方式存储配置内容,对于不同环境配置的修改就不得不去获取工程内容来修改这些配置内容,当应用非常多的时候就变得非常不方便。同时,配置内容都对开发人员可见,本身这也是一种安全隐患。对此,现在出现了很多将配置内容外部化的框架和工具 4 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 1 章 Spring 入门/1.5 小结.md: -------------------------------------------------------------------------------- 1 | ## 1.5 小结 2 | 3 | - Spring 的目标是让开发人员轻松应对挑战,比如创建 web 应用程序、使用数据库、保护应用程序和使用微服务。 4 | - Spring Boot 构建在 Spring 之上,简化了依赖管理、自动配置和运行时监控,让 Spring 变得更加简单。 5 | - Spring 应用程序可以使用 Spring Initializr 进行初始化,它是基于 web 的,并且在大多数 Java 开发环境中都支持它。 6 | - 在 Spring 应用程序上下文中,组件(通常称为 bean)可以用 Java 或 XML 显式地声明,可以通过组件扫描进行发现,也可以用 Spring Boot 进行自动配置。 7 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/02~集成 Spring/第 6 章 创建 REST 服务/6.4 小结.md: -------------------------------------------------------------------------------- 1 | ## 6.4 小结 2 | 3 | - Spring MVC 可以创建端点,控制器遵循与以浏览器为目标的控制器相同的编程模型。 4 | - 控制器处理程序方法可以使用 @ResponseBody 进行注解,也可以返回 ResponseEntity 对象,从而绕过模型,直接将数据写入响应体。 5 | - @RestController 注解简化了 REST 控制器,无需在处理程序方法上使用 @ResponseBody。 6 | - Spring HATEOAS 启用了能够从 Spring MVC 控制器返回的资源的超链接。 7 | - Spring Data 存储仓库可以使用 Spring Data REST 自动公开为 REST API。 8 | -------------------------------------------------------------------------------- /02~数据库/01~连接池/Druid/README.md: -------------------------------------------------------------------------------- 1 | # Druid 2 | 3 | Druid 是由阿里巴巴数据库事业部出品的开源项目。它除了是一个高性能数据库连接池之外,更是一个自带监控的数据库连接池。虽然 HikariCP 已经很优秀,但是对于国内用户来说,可能对于 Druid 更为熟悉。所以,对于如何在 Spring Boot 中使用 Druid 是后端开发人员必须要掌握的基本技能。 4 | 5 | # Links 6 | 7 | - https://parg.co/kmH 8 | 9 | - [2021~池化技术有多牛?来,告诉你阿里的 Druid 为啥如此牛逼!](https://mp.weixin.qq.com/s/p2JqgLu5qWgEpd9hfVrYuQ): 今天无聊带你们撩一下 Druid,从底层的分析 Druid 是如何管理数据库连接? 10 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/02~集成 Spring/第 8 章 发送异步消息/8.4 小结.md: -------------------------------------------------------------------------------- 1 | ## 8.4 小结 2 | 3 | - 异步消息传递在通信的应用程序之间提供了一个间接层,允许更松散的耦合和更大的可伸缩性。 4 | - Spring 支持 JMS、RabbitMQ 或 Apache Kafka 的异步消息传递。 5 | - 应用程序可以使用基于模板的客户端(JmsTemplate、RabbitTemplate 或 KafkaTemplate)通过消息 broker 发送消息。 6 | - 接收应用程序可以使用相同的基于模板的客户端,在基于 pull 的模型中使用消息。 7 | - 也可以通过向 bean 方法应用消息监听器注解(@JmsListener、@RabbitListener 或 @KafkaListener)将消息推送给消费者。 8 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第三部分 响应式 Spring.md: -------------------------------------------------------------------------------- 1 | # 第三部分 响应式 Spring 2 | 3 | 在第三部分中,我们将探讨 Spring 中对响应式编程的支持。第 10 章讨论了使用 Project Reactor 进行响应式编程的要点,Project Reactor 是支持 Spring 5 响应式特性的响应式编程库。然后我们来看看 Reactor 最有用的响应式操作。在第 11 章中,我们将重新讨论 REST API 的开发,接着介绍 Spring WebFlux,这是一个新的 web 框架,它借鉴了 Spring MVC 的形式,同时为 web 开发提供了一个新的响应式模型。第三部分的最后是第 12 章,介绍了如何使用 Spring Data 来对响应式数据进行持久化,其实就是对 Cassandra 和 Mongo 数据库的读写操作。 4 | -------------------------------------------------------------------------------- /02~数据库/01~Spring JDBC/事务/Transactonal.md: -------------------------------------------------------------------------------- 1 | # Transactional 2 | 3 | Spring 团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。 4 | 你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。 5 | 因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,请接受 Spring 团队的建议并且在具体的类上使用 @Transactional 注解。 6 | @Transactional 注解标识的方法,处理过程尽量的简单。尤其是带锁的事务方法,能不放在事务里面的最好不要放在事务里面。可以将常规的数据库查询操作放在事务前面进行,而事务内进行增、删、改、加锁查询等操作。 7 | -------------------------------------------------------------------------------- /04~工程实践/测试/JUnit 4/README.md: -------------------------------------------------------------------------------- 1 | # JUnit 4 2 | 3 | JUnit ——是一个开源的 Java 测试框架,主要用于编写白盒测试,回归测试。无论白盒测试还是回归测试,都是运行可重复的测试。所谓”回归测试“——就是,软件或环境的修复或更正后的“再测试”,自动测试工具对这类测试尤其有用;而所谓”单元测试“——就是,最小粒度的测试,以测试某个功能或代码块。一般由程序员来做,因为它需要知道内部程序设计和编码的细节。对于持续发展的产品,单元测试在后期的维护,回归有重要等方面有重要作用。 4 | 5 | ```java 6 | @Test 7 | public void testMultiply() { 8 | MyClass tester = new MyClass(); 9 | assertEquals( "10 x 5 must be 50 ", 50, tester.multiply( 10, 5 )); 10 | } 11 | ``` 12 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 4 章 Spring 安全/第 4 章 Spring 安全.md: -------------------------------------------------------------------------------- 1 | # 第 4 章 Spring 安全 2 | 3 | 本章内容: 4 | 5 | - 自动配置 Spring Security 6 | - 自定义用户存储 7 | - 自定义登录页面 8 | - 防御 CSRF 攻击 9 | - 了解你的用户 10 | 11 | 你有没有注意到电视情景喜剧里的大多数人都不锁门?在《_Leave it to Beaver_》的时代,人们不锁门并不是什么稀罕事。但是,在我们关心隐私和安全的今天,我们却看到电视上的人物能够畅通无阻地进入他们的公寓和家中,这似乎很疯狂。 12 | 13 | 信息可能是我们现在拥有的最有价值的东西;骗子们正在寻找通过潜入不安全的应用程序来窃取我们的数据和身份的方法。作为软件开发人员,我们必须采取措施保护应用程序中的信息。无论是用用户名与密码保护的电子邮件帐户,还是用交易密码保护的经济帐户,安全性都是大多数应用程序的一个重要方面。 14 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 11 章 开发响应式 API/第 11 章 开发响应式 API.md: -------------------------------------------------------------------------------- 1 | # 第 11 章 开发响应式 API 2 | 3 | 本章介绍 4 | 5 | - 使用 Spring WebFlux 6 | - 编写和测试响应式控制器和客户端 7 | - 消费 REST API 8 | - 保护响应式 web 应用程序安全 9 | 10 | 既然你已经对响应式编程和 Project Reactor 有了很好的了解,就可以开始在 Spring 应用程序中应用这些技术了。在本章中,我们将重温你在第 6 章中编写的一些控制器,以利用 Spring5 的响应式编程模型。 11 | 12 | 更具体地说,我们将看看 Spring5 的新的响应式 web 框架 Spring WebFlux。你将很快发现 Spring WebFlux 与 Spring MVC 非常相似,同时你已经知道的在 Spring 中构建 REST API 的知识,这些条件下,它的应用很容易。 13 | -------------------------------------------------------------------------------- /01~基础使用/02~快速开始/DevTools.md: -------------------------------------------------------------------------------- 1 | # SpringBoot DevTools 2 | 3 | SpringBoot Dev Tools 为我们提供了便捷的开发时辅助功能,其主要包含了以下特性: 4 | 5 | - Property defaults 6 | - Automatic Restart 7 | - Live Reload 8 | - Global settings 9 | - Remote applications 10 | 11 | 在 Spring 项目中如果需要引入 SpringBoot DevTools 也非常的方便,如其他依赖一样添加到 POM 或者 build.gradle 文件中即可: 12 | 13 | ```xml 14 | 15 | org.springframework.boot 16 | spring-boot-devtools 17 | 18 | ``` 19 | -------------------------------------------------------------------------------- /01~基础使用/09~定时任务/定时器.md: -------------------------------------------------------------------------------- 1 | # Spring Boot 中定时器使用 2 | 3 | Spring 的定时器有三种模式,分别是 fixedDelay、cron、fixedRate。fixedDelay 会让某个任务在执行完成后以固定间隔执行,而 cron 与 fixedRate 都是希望以固定间隔来执行任务。假如希望定时器每 5s 执行一次,而某个任务却已经执行了 8s,那么对于 cron,它每 5s 会来检视一次,如果当前空闲则执行,否则等到下一个执行点;而 fixedRate 则会在任务延期之后尽快执行。 4 | 5 | # fixedDelay 6 | 7 | ```java 8 | 9 | ``` 10 | 11 | # Links 12 | 13 | - https://mp.weixin.qq.com/s/1IyXrkhCvG1hR21Vr1ttkA 14 | - [Spring Boot 中使用 @Scheduled 创建定时任务](http://blog.didispace.com/springbootscheduled/) 15 | -------------------------------------------------------------------------------- /02~数据库/03~ORM/README.md: -------------------------------------------------------------------------------- 1 | # ORM 2 | 3 | 前面我们介绍了 Spring Boot 中最基本的 Jdbc 模块来实现关系型数据库的数据读写操作。然而当我们有一定的开发经验之后,不难发现,在实际开发过程中,对数据库的操作大多可以归结为:“增删改查”。就最为普遍的单表操作而言,除了表和字段不同外,语句几乎都是类似的,开发人员需要写大量类似而枯燥的语句来完成业务逻辑。 4 | 5 | 在 Hibernate 的帮助下,Java 实体映射到数据库表数据完成之后,再进一步解决抽象各个 Java 实体基本的“增删改查”操作,我们通常会以泛型的方式封装一个模板 Dao 来进行抽象简化,但是这样依然不是很方便,我们需要针对每个实体编写一个继承自泛型模板 Dao 的接口,再编写该接口的实现。虽然一些基础的数据访问已经可以得到很好的复用,但是在代码结构上针对每个实体都会有一堆 Dao 的接口和实现。由于模板 Dao 的实现,使得这些具体实体的 Dao 层已经变的非常“薄”,有一些具体实体的 Dao 实现可能完全就是对模板 Dao 的简单代理,并且往往这样的实现类可能会出现在很多实体上。 6 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/02~集成 Spring/第二部分 集成 Spring.md: -------------------------------------------------------------------------------- 1 | # 第二部分 集成 Spring 2 | 3 | 第 2 部分的章节介绍了一些有助于将 Spring 应用程序与其他应用程序集成在一起的主题。 4 | 5 | 第 6 章通过讨论如何在 Spring 中编写 REST api 来扩展第 2 章中开始的关于 Spring MVC 的讨论。我们将了解如何在 Spring MVC 中定义 REST 端点,启用超链接 REST 资源,以及如何使用 Spring Data REST 自动生成基于存储库的 REST 端点。第 7 章切换透视图,以展示 Spring 应用程序如何使用 REST API。在第 8 章中,我们将讨论如何使用异步通信使 Spring 应用程序能够使用 Java 消息服务(JMS)、RabbitMQ 和 Kafka 发送和接收消息。最后,第 9 章讨论了使用 Spring Integration 项目的声明性应用程序集成。我们将介绍实时处理数据、定义集成流以及与外部系统(如电子邮件和文件系统)的集成。 6 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 2 章 开发 Web 应用程序/2.6 小结.md: -------------------------------------------------------------------------------- 1 | ## 2.6 小结 2 | 3 | - Spring 提供了一个强大的 web 框架,称为 Spring MVC,可以用于开发 Spring 应用程序的 web 前端。 4 | - Spring MVC 是基于注解的,可以使用 @RequestMapping、@GetMapping 和 @PostMapping 等注解来声明请求处理方法。 5 | - 大多数请求处理方法通过返回视图的逻辑名称来结束,例如一个 Thymeleaf 模板,请求(以及任何模型数据)被转发到该模板。 6 | - Spring MVC 通过 Java Bean Validation API 和 Hibernate Validator 等验证 API 的实现来支持验证。 7 | - 视图控制器可以用来处理不需要模型数据或处理的 HTTP GET 请求。 8 | - 除了 Thymeleaf,Spring 还支持多种视图选项,包括 FreeMarker、Groovy Templates 和 Mustache。 9 | -------------------------------------------------------------------------------- /09~源码解析/深入请求处理/Servlet/README.md: -------------------------------------------------------------------------------- 1 | ![](http://mmbiz.qpic.cn/mmbiz/Pn4Sm0RsAuiaLQLq8hzyGTqXX5N4DsgkPdr9KOu2OfLibxXMbhoQjU3sEoDu8bI9wGA9eXqhNykKLZibDnVNEveFg/0?wx_fmt=gif&wxfrom=5&wx_lazy=1) 2 | 3 | # Servlet 4 | 5 | Servlet 是 J2EE 规范之一,在遵守该规范的前提下,我们可将 Web 应用部署在 Servlet 容器下。这样做的好处是什么呢?我觉得可使开发者聚焦业务逻辑,而不用去关心 HTTP 协议方面的事情。比如,普通的 HTTP 请求就是一段有格式的文本,服务器需要去解析这段文本才能知道用户请求的内容是什么。 6 | 7 | # Links 8 | 9 | - https://mp.weixin.qq.com/s/LGuXzhtHt2LNcgLq36jqyw 10 | - https://www.zhihu.com/question/21416727/answer/690289895 servlet 的本质是什么,它是如何工作的? 11 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第一部分 Spring 基础.md: -------------------------------------------------------------------------------- 1 | # 第一部分 Spring 基础 2 | 3 | 本书的第 1 部分将帮助你开始编写 Spring 应用程序,学习 Spring 的基础。 4 | 5 | 在第一章中,将快速概述 Spring 和 Spring Boot 的要点,并展示如何在 Taco Cloud 上初始化第一个 Spring 项目。在第二章中,你将深入研究 Spring MVC,并了解如何在浏览器中呈现模型数据以及如何处理并验证表单输入,还将获得选择视图模板库的一些提示。在第三章中,将在 Taco Cloud 应用程序中添加数据持久性,我们将介绍使用 Spring 的 JDBC 模板,如何插入数据以及如何使用 Spring Data 声明 JPA 存储库。第四章介绍了 Spring 应用程序的安全性,包括自动配置 Spring Security,定义自定义用户存储,自定义登录页面并防止跨站点请求伪造(CSRF)攻击。在第一部分中,我们将在第五章中介绍配置属性,将学习如何微调自动配置的 bean 和应用配置属性到应用程序组件,并与 Spring 的 profile 文件一起使用。 6 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 3 章 处理数据/第 3 章 处理数据.md: -------------------------------------------------------------------------------- 1 | # 第 3 章 处理数据 2 | 3 | 本章内容: 4 | 5 | - 使用 Spring JdbcTemplate 6 | - 使用 SimpleJdbcInsert 插入数据 7 | - 使用 Spring Data 声明 JPA repositories 8 | 9 | 大多数应用程序提供的不仅仅是一张漂亮的脸。虽然用户界面可能提供与应用程序的交互,但它所呈现和存储的数据将应用程序与静态网站区分开来。 10 | 11 | 在 Taco Cloud 应用程序中,需要能够维护关于 ingredients、tacos 和 orders 的信息。如果没有一个数据库来存储这些信息,应用程序将无法比在第 2 章中开发的应用程序取得更大的进展。 12 | 13 | 在本章中,将向 Taco Cloud 应用程序添加数据持久化操作。首先使用 Spring 对 JDBC(Java Database Connectivity)的支持来消除样板代码。然后,将重新使用 JPA(Java Persistence API)处理数据存储库,从而消除更多代码。 14 | -------------------------------------------------------------------------------- /02~数据库/01~Spring JDBC/事务/事务回滚.md: -------------------------------------------------------------------------------- 1 | # 事务回滚 2 | 3 | # rollbackFor 4 | 5 | Spring 框架的事务基础架构代码将默认地只在抛出运行时和 unchecked exceptions 时才标识事务回滚。也就是说,当抛出个 RuntimeException 或其子类例的实例时。(Errors 也一样默认地标识事务回滚。)从事务方法中抛出的 Checked exceptions 将 不 被标识进行事务回滚。 6 | 7 | - 让 checked 例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class) 8 | - 让 unchecked 例外不回滚:@Transactional(notRollbackFor=RunTimeException.class) 9 | - 不需要事务管理的(只查询的)方法: @Transactional(propagation=Propagation.NOT_SUPPORTED) 10 | 11 | 注意:如果异常被 try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛 try{}catch{throw Exception}。 12 | -------------------------------------------------------------------------------- /01~基础使用/06~视图与模板/静态资源.md: -------------------------------------------------------------------------------- 1 | # 静态资源 2 | 3 | Spring Boot 中不需要任何的配置即可直接访问静态资源,可以参考[Spring Boot Static Content](https://github.com/spring-projects/spring-boot/blob/master/spring-boot-samples%2Fspring-boot-sample-web-static%2Fsrc%2Fmain%2Fjava%2Fsample%2Fweb%2Fstaticcontent%2FSampleWebStaticApplication.java)这个示范。 4 | 5 | ![](http://i.stack.imgur.com/QnNmy.png) 6 | 7 | 直接将你的资源文件放置在 resources/static 目录下即可,然后直接访问 http://localhost:8080/css/style.css 即可。 8 | 9 | Spring 3.0.4 以后版本提供了 `` 映射到 ResourceHttpRequestHandler 进行处理; 10 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 2 章 开发 Web 应用程序/第 2 章 开发 Web 应用程序.md: -------------------------------------------------------------------------------- 1 | ## 第 2 章 开发 Web 应用程序 2 | 3 | 本章内容: 4 | 5 | - 在浏览器中展示模型数据 6 | - 处理和验证表单输入 7 | - 选择视图模板库 8 | 9 | 第一印象很重要:抑制吸引力可以在购房者进门之前很久就把房子卖掉;一辆车的喷漆工作将会比引擎盖下的东西吸引更多的人;文学作品中充满了一见钟情的故事。内在的东西很重要,但外在的 —— 先看到的 —— 才是重要的。 10 | 11 | 使用 Spring 构建的应用程序将执行各种操作,包括处理数据、从数据库中读取信息以及与其他应用程序进行交互。但是应用程序用户得到的第一印象来自于用户界面。在许多应用程序中,UI 界面是在浏览器中显示的 web 应用程序。 12 | 13 | 在第 1 章中,创建了第一个 Spring MVC 控制器来显示应用程序主页。但是 Spring MVC 能做的远不止简单地显示静态内容。在本章中,将开发 Taco Cloud 应用程序的第一个主要功能 —— 设计自定义 Taco 的能力。在此过程中,将深入研究 Spring MVC,并了解如何显示模型数据和处理表单输入。 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 5 章 使用配置属性/第 5 章 使用配置属性.md: -------------------------------------------------------------------------------- 1 | # 第 5 章 使用配置属性 2 | 3 | 本章内容: 4 | 5 | - 微调自动配置 bean 6 | - 将配置属性应用于应用程序组件 7 | - 使用 Spring 配置文件 8 | 9 | 你还记得 iPhone 第一次出现的时候吗?一块由金属和玻璃制成的小平板几乎不符合人们对电话的认知。然而,它开创了现代智能手机时代,改变了我们交流的一切方式。尽管在很多方面,触控手机都比上一代的翻盖手机更简单、功能更强大,但在 iPhone 第一次发布时,很难想象一个只有一个按钮的设备怎么能用来打电话。 10 | 11 | 在某些方面,Spring Boot 自动配置是这样的。自动配置大大简化了 Spring 应用程序的开发。但是,在使用 Spring XML 配置中设置属性值和调用 bean 实例上 setter 方法十年之后,如何在没有显式配置的 bean 上设置属性并不是很明显。 12 | 13 | 幸运的是,Spring Boot 提供了一种配置属性的方法。配置属性不过是 Spring 应用程序上下文中 bean 上的属性,可以从几个属性源(包括 JVM 系统属性、命令行参数和环境变量)之一进行设置。 14 | 15 | 在本章中,将从实现 Taco Cloud 应用程序中的新功能中后退一步,以研究配置属性。在接下来的章节中,你学到的东西无疑会对你以后的学习很有帮助。我们将首先了解如何使用配置属性来微调 Spring Boot 自动配置的内容。 16 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/02~集成 Spring/第 9 章 集成 Spring/第 9 章 集成 Spring.md: -------------------------------------------------------------------------------- 1 | # 第 9 章 集成 Spring 2 | 3 | 本章内容: 4 | 5 | - 实时数据处理 6 | - 定义集成流程 7 | - 使用 Spring Integration 的 Java DSL 定义 8 | - 集成电子邮件、文件系统和其他外部系统 9 | 10 | 我在旅行中遇到的最令人沮丧的事情之一就是在长途飞行中,飞机上的互联网连接很差或者根本不存在。我喜欢利用我的飞行时间完成一些工作,包括写这本书。如果没有网络连接,那么在需要获取库或查找 Java 文档时,我就处于不利地位了,也无法完成大量工作。我学会了在这种场合下带本书来读。 11 | 12 | 正如我们需要连接到互联网来提高生产力一样,许多应用程序也必须连接到外部系统来执行它们的工作。应用程序可能需要读取或发送电子邮件、与外部 API 交互或对写入数据库的数据作出响应。而且,由于数据是从这些外部系统获取或写入的,应用程序可能需要以某种方式处理数据,以便将其转换到或从应用程序自己的域。 13 | 14 | 在本章中,将看到如何使用 Spring Integration 的通用集成模式。Spring Integration 是由 Gregor Hohpe 和 Bobby Woolf 在*《企业级集成模式》*一书中编目的许多集成模式的实现。每个模式都被实现为一个组件,消息通过该组件传输管道中的数据。使用 Spring 配置,可以将这些组件组装到数据流经的管道中。让我们从定义一个简单的集成流开始,它引入了 Spring Integration 的许多特性。 15 | -------------------------------------------------------------------------------- /10~Spring Cloud/Spring Cloud Alibaba/Sofa/SOFABoot/README.md: -------------------------------------------------------------------------------- 1 | # Sofa Boot 2 | 3 | SOFABoot 是蚂蚁金服开源的基于 Spring Boot 的研发框架,它在 Spring Boot 的基础上,提供了诸如 Readiness Check,类隔离,日志空间隔离等能力。在增强了 Spring Boot 的同时,SOFABoot 提供了让用户可以在 Spring Boot 中非常方便地使用 SOFA 中间件的能力。SOFABoot 在 Spring Boot 基础上,提供了以下能力: 4 | 5 | - 扩展 Spring Boot 健康检查的能力:在 Spring Boot 健康检查能力基础上,提供了 Readiness Check 的能力,保证应用实例安全上线。 6 | - 提供模块化开发的能力:基于 Spring 上下文隔离提供模块化开发能力,每个 SOFABoot 模块使用独立的 Spring 上下文,避免不同 SOFABoot 模块间的 BeanId 冲 突。 7 | - 增加模块并行加载和 Spring Bean 异步初始化能力,加速应用启动; 8 | - 增加日志空间隔离的能力:中间件框架自动发现应用的日志实现依赖并独立打印日志,避免中间件和应用日志实现绑定,通过 sofa-common-tools 实现。 9 | - 增加类隔离的能力:基于 SOFAArk 框架提供类隔离能力,方便使用者解决各种类冲突问题。 10 | - 增加中间件集成管理的能力:统一管控、提供中间件统一易用的编程接口、每一个 SOFA 中间件都是独立可插拔的组件。 11 | - 提供完全兼容 Spring Boot 的能力:SOFABoot 基于 Spring Boot 的基础上进行构建,并且完全兼容 Spring Boot。 12 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/02~集成 Spring/第 6 章 创建 REST 服务/第 6 章 创建 REST 服务.md: -------------------------------------------------------------------------------- 1 | # 第 6 章 创建 REST 服务 2 | 3 | 本章内容: 4 | 5 | - 在 Spring MVC 中定义 REST 端点 6 | - 启用超链接 REST 资源 7 | - 自动生成基于存储库的 REST 端点 8 | 9 | “网络浏览器挂掉了,现在怎么办?” 10 | 11 | 大约十多年前,我听到有人说,web 浏览器正接近濒死状态,可能会被其他东西取代。但这是怎么回事呢?什么有可能取代几乎无处不在的 web 浏览器呢?如果不使用 web 浏览器,我们将如何消费越来越多的网站和在线服务?这的确是一个疯子的胡言乱语! 12 | 13 | 快进到今天,很明显,web 浏览器并没有消失,但它不再是上网的主要方式。移动设备、平板电脑、智能手表和语音设备现在都很常见,甚至许多基于浏览器的应用程序实际上都在运行 JavaScript 应用程序,而不是让浏览器成为服务器呈现内容的无声终端。 14 | 15 | 有了如此多的客户端选项,许多应用程序采用了一种常见的设计:将用户界面推近客户端,而服务器公开一个 API,通过该 API,所有类型的客户端都可以与后端进行交互。 16 | 17 | 在本章中,将使用 Spring 为 Taco Cloud 应用程序提供 REST API。将使用在第 2 章中所学到的关于 Spring MVC 的知识,使用 Spring MVC 控制器创建 RESTful 端点。还将自动公开第 4 章中定义的 Spring Data 存储库的 REST 端点。最后,我们将研究测试和保护这些端点的方法。 18 | 19 | 但首先,将从编写一些新的 Spring MVC 控制器开始,这些控制器公开带有 REST 端点的后端功能,以供丰富的 web 前端使用。 20 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/02~集成 Spring/第 8 章 发送异步消息/第 8 章 发送异步消息.md: -------------------------------------------------------------------------------- 1 | # 第 8 章 发送异步消息 2 | 3 | 本章内容: 4 | 5 | - 异步消息 6 | - 使用 JMS、RabbitMQ 和 Kafka 发送消息 7 | - 从 Broker 拉取消息 8 | - 监听消息 9 | 10 | 现在是星期五下午 4 点 55 分。你还有几分钟就要开始期待已久的假期了。你有足够的时间开车去机场赶飞机。但是在你打包好行李准备出去的时候,你需要确保你的老板和同事们知道你一直在做的工作的状态,这样周一他们可以从你断掉的地方接着做。不幸的是,你的一些同事已经跳过了周末,并且你的老板一直在开会。你该做些什么? 11 | 12 | 这是最实用的办法就是,向老板和同事发一封简短的电子邮件,详细说明你的进展情况,并承诺寄张明信片,这样做既能传达你的所处的工作做到的地步,又能赶上飞机。你不知道他们在哪里,也不知道他们什么时候会读这封邮件,但你知道他们最终会回到自己的办公桌上读这封邮件。与此同时,你正在去机场的路上。 13 | 14 | _同步_ 通信有它的地位,这是我们在 REST 中所看到的。但这并不是开发人员可以使用的惟一应用程序间通信方式。异步消息传递是一种间接地将消息从一个应用程序发送到另一个应用程序而无需等待响应的方式。这种间接方式提供了通信应用程序之间更松散的耦合和更大的可伸缩性。 15 | 16 | 在本章中,将使用异步消息传递将订单从 Taco Cloud 网站发送到 Taco Cloud 厨房中的一个独立应用程序,在那里将准备 tacos。我们将考虑 Spring 为异步消息传递提供的三个选项:Java 消息服务(JMS)、RabbitMQ 和高级消息队列协议(AMQP)以及 Apache Kafka。除了基本的消息发送和接收之外,我们还将了解 Spring 对消息驱动 POJO 的支持:一种类似于 EJB 的消息驱动 bean(MDB)的消息接收方式。 17 | -------------------------------------------------------------------------------- /01~基础使用/05~拦截与切面/README.md: -------------------------------------------------------------------------------- 1 | # AOP 2 | 3 | AOP:Aspect Oriented Programming 的缩写,意为:面向切面编程。面向切面编程的目标就是分离关注点。使用 AOP,首先需要引入 AOP 的依赖。 4 | 5 | ```xml 6 | 7 | org.springframework.boot 8 | spring-boot-starter-aop 9 | 10 | ``` 11 | 12 | 过滤器依赖于 servlet 容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改 HttpServletRequest 的一些参数,包括:过滤低俗文字、危险字符等。拦截器依赖于 web 框架,在 SpringMVC 中就是依赖于 SpringMVC 框架。在实现上基于 Java 的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于 web 框架的调用,因此可以使用 Spring 的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个 controller 生命周期之内可以多次调用。但是缺点是只能对 controller 请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。总结而言: 13 | 14 | - Filter 是依赖于 Servlet 容器,属于 Servlet 规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。 15 | - Filter 的执行由 Servlet 容器回调完成,而拦截器通常通过动态代理的方式来执行。 16 | - Filter 的生命周期由 Servlet 容器管理,而拦截器则可以通过 IoC 容器来管理,因此可以通过注入等方式来获取其他 Bean 的实例,因此使用会更方便。 17 | -------------------------------------------------------------------------------- /03~Spring Security/权限验证/HTTP Basic Auth.md: -------------------------------------------------------------------------------- 1 | # HTTP Basic Auth 2 | 3 | HTTP Basic Auth 是较为简单的静态用户名密码认证方式,分别需要声明路由规则与配置 AuthenticationManagerBuilder, 本部分完整代码参考 [spring-security-basic-auth](https://github.com/wx-chevalier/Backend-Boilerplate/tree/master/java/spring/spring-basic-auth)。 4 | 5 | ```java 6 | // 声明路由规则 7 | @Override 8 | protected void configure(HttpSecurity http) throws Exception { 9 | http 10 | .authorizeRequests() 11 | .anyRequest() 12 | .authenticated() 13 | .and() 14 | .httpBasic(); 15 | } 16 | 17 | // 声明权限验证 18 | @Override 19 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 20 | auth 21 | .inMemoryAuthentication() 22 | .withUser("user") 23 | .password("password") 24 | .roles("USER") 25 | .and() 26 | .withUser("admin") 27 | .password("admin") 28 | .roles("USER", "ADMIN"); 29 | } 30 | ``` 31 | -------------------------------------------------------------------------------- /02~数据库/04~缓存与 KV 存储/进程内缓存/Ehcache.md: -------------------------------------------------------------------------------- 1 | # Ehcache 2 | 3 | - [Ehcache 详细解读](http://www.blogjava.net/libin2722/articles/406569.html) 4 | - [ehcache-documentation](http://www.ehcache.org/documentation/3.0/getting-started.html) 5 | 6 | ```java 7 | CacheManager cacheManager 8 | = CacheManagerBuilder.newCacheManagerBuilder() 9 | .withCache("preConfigured", 10 | CacheConfigurationBuilder.newCacheConfigurationBuilder() 11 | .buildConfig(Long.class, String.class)) 12 | .build(false); 13 | cacheManager.init(); 14 | 15 | Cache preConfigured = 16 | cacheManager.getCache("preConfigured", Long.class, String.class); 17 | 18 | Cache myCache = cacheManager.createCache("myCache", 19 | CacheConfigurationBuilder.newCacheConfigurationBuilder().buildConfig(Long.class, String.class)); 20 | 21 | myCache.put(1L, "da one!"); 22 | String value = myCache.get(1L); 23 | 24 | cacheManager.removeCache("preConfigured"); 25 | 26 | cacheManager.close(); 27 | ``` 28 | -------------------------------------------------------------------------------- /02~数据库/04~缓存与 KV 存储/Redis/Redisson/锁.md: -------------------------------------------------------------------------------- 1 | # 锁 2 | 3 | Redisson 的分布式锁允许跨应用程序/服务器进行线程同步。Redisson 的锁和同步器列表包括: 4 | 5 | - Lock 6 | - FairLock 7 | - MultiLock 8 | - ReadWriteLock 9 | - Semaphore 10 | - PermitExpirableSemaphore 11 | - CountDownLatch 12 | 13 | > 参与《[DistributedSystem-Notes](https://github.com/wx-chevalier/DistributedSystem-Notes?q=)》中有关于分布式锁的详细讨论。 14 | 15 | # Lock,非公平锁 16 | 17 | ```java 18 | RLock lock = client.getLock("lock"); 19 | lock.lock(); 20 | 21 | // perform some long operations... 22 | lock.unlock(); 23 | ``` 24 | 25 | # MultiLock 26 | 27 | Redisson 的 RedissonMultiLock 组合了多个 RLock 对象,并将它们视为一个锁: 28 | 29 | ```java 30 | RLock lock1 = clientInstance1.getLock("lock1"); 31 | RLock lock2 = clientInstance2.getLock("lock2"); 32 | RLock lock3 = clientInstance3.getLock("lock3"); 33 | 34 | RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3); 35 | lock.lock(); 36 | // perform long running operation... 37 | lock.unlock(); 38 | ``` 39 | 40 | # Fair Lock 41 | 42 | # RedLock 43 | -------------------------------------------------------------------------------- /02~数据库/04~缓存与 KV 存储/Redis/Redisson/README.md: -------------------------------------------------------------------------------- 1 | # Redisson 2 | 3 | [Redisson](https://github.com/mrniko/redisson) 是基于 Redis 进行的一种顶层的封装,提供了一系列的分布式与可扩展的 Java 的数据结构。Redisson 底层使用了 Netty 进行封装,同时将 Java 本身大量的类似于 CountDownLatch 这样的同步辅助类利用 Redis 映射到了分布式环境下。在 redisson 中,各个部分均采用了最新的一些技术栈,包括 java 5 线程语义,Promise 编程语义,在技术的学习上有很高的学习意义。相比 jedis,其支持的特性并不是很高,但对于日常的使用还是没有问题的。其对集合的封装,编解码的处理,都达到了一个开箱即用的目的。相比 jedis,仅完成了一个基本的 redis 网络实现,可以理解为 redisson 是一个完整的框架,而 jedis 即完成了语言层的适配。其次,redisson 在设计模式,以及编码上,都有完整的测试示例,代码可读性也非常好,很值得进行源码级学习。如果在项目中已经使用了 netty,那么如果需要集成 redis,那么使用 redisson 是最好的选择了,都不需要另外增加依赖信息。 4 | 5 | ```java 6 | // connects to default Redis server 127.0.0.1:6379 7 | Redisson redisson = Redisson.create(); 8 | // connects to single Redis server via Config 9 | Config config = new Config(); 10 | config.useSingleServer().setAddress("127.0.0.1:6379"); 11 | 12 | //or with database select num = 1 13 | config.useSingleServer().setAddress("127.0.0.1:6379").setDatabase(1); 14 | 15 | Redisson redisson = Redisson.create(config); 16 | ``` 17 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 11 章 开发响应式 API/11.5 安全的响应式 Web API.md: -------------------------------------------------------------------------------- 1 | ## 安全的响应式 Web API 2 | 3 | 在 Spring Security 出现之前(在此之前,当它被称为 Acegi 安全),Web 安全模型都是围绕 Servlet 过滤器构建。毕竟,这是有道理的。如果需要拦截绑定基于 Servlet 的 Web 框架的请求,以确保请求者具有适当的权限,那么使用 Servlet 过滤器是一个明智地选择。但是 Spring WebFlux 将一个折衷的方法引入其中。 4 | 5 | 当用 Spring WebFlux 编写 Web 应用程序时,甚至不能保证有 Servlet。事实上,一个被动的 Web 应用程序更有可能构建在 Netty 或其他非 Servlet 服务器上。这是否意味着不能用基于 Servlet 过滤器的 Spring Security 来保护 Spring WebFlux 应用程序? 6 | 7 | 在保护 Spring WebFlux 应用程序时,确实不应该使用 Servlet 过滤器。但是 Spring Security 仍然可以胜任这个任务。从 5.0.0 版本开始,Spring Security 可用于保护基于 Servlet 的 Spring MVC 和被动式 Spring WebFlux 应用程序。它使用 Spring 的 WebFilter 来完成这项工作,Spring-specific 模拟 servlet 过滤器不需要依赖 servlet API。 8 | 9 | 然而,更值得注意的是,被动式 SpringSecurity 的配置模型与第 4 章中看到的没有太大区别。实际上,SpringSecurity 不同于 SpringWebFlux,后者与 SpringMVC 有单独的依赖,无论您打算使用它来保护 SpringMVCWeb 应用程序还是用 SpringWebFlux 编写的应用程序,SpringSecurity 都与 SpringBoot 安全启动程序相同。提醒一下,这里是安全启动程序的样子: 10 | 11 | 也就是说,SpringSecurity 的被动式配置模型和非被动式配置模型之间有一些小的区别。值得快速了解一下这两种配置模型之间的比较。 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | 28 | - OS: [e.g. iOS] 29 | - Browser [e.g. chrome, safari] 30 | - Version [e.g. 22] 31 | 32 | **Smartphone (please complete the following information):** 33 | 34 | - Device: [e.g. iPhone6] 35 | - OS: [e.g. iOS8.1] 36 | - Browser [e.g. stock browser, safari] 37 | - Version [e.g. 22] 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /01~基础使用/04~请求与响应/异步处理/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot 中异步请求处理 2 | 3 | 在 [Linux 并发编程](https://github.com/wx-chevalier/Linux-Notes)系列中我们讨论了 Linux 系统中的 IO 模型。 4 | 5 | ![IO 模型](https://ngte-superbed.oss-cn-beijing.aliyuncs.com/item/20230417205601.png) 6 | 7 | # 同步与异步请求的对比 8 | 9 | 传统的 Web 应用都会采用同步模式,即浏览器发起请求,Web 服务器开一个线程处理(请求处理线程),处理完把处理结果返回浏览器。 10 | 11 | ![同步请求的阻塞等待](https://s2.ax1x.com/2020/01/01/lGIfYD.png) 12 | 13 | Tomcat 等应用服务器的连接线程池实际上是有限制的;每一个连接请求都会耗掉线程池的一个连接数;如果某些耗时很长的操作,如对大量数据的查询操作、调用外部系统提供的服务以及一些 IO 密集型操作等,会占用连接很长时间,这个时候这个连接就无法被释放而被其它请求重用。如果连接占用过多,服务器就很可能无法及时响应每个请求;极端情况下如果将线程池中的所有连接耗尽,服务器将长时间无法向外提供服务 14 | 15 | Spring MVC 3.2 之后支持异步请求,能够在 Controller 中返回一个 Callable 或者 DeferredResult。这样就允许请求处理线程仅进行请求获取操作,而后异步交由业务处理线程: 16 | 17 | ![异步请求非阻塞等待](https://s2.ax1x.com/2020/01/01/lGIxpQ.png) 18 | 19 | 异步模式处理步骤概述如下: 20 | 21 | - Controller 返回值是 Callable 的时候,Spring 就会将 Callable 交给 TaskExecutor 去处理(一个隔离的线程池)。 22 | - 同时将 DispatcherServlet 里的拦截器、Filter 等等都马上退出主线程,但是 response 仍然保持打开的状态。 23 | - Callable 线程处理完成后,Spring MVC 讲请求重新派发给容器,根据 Callabel 返回结果,继续处理。 24 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/第 10 章 Reactor 介绍.md: -------------------------------------------------------------------------------- 1 | # 第 10 章 Reactor 介绍 2 | 3 | 本章介绍 4 | 5 | - 理解响应式编程 6 | - Project Reactor 7 | - 响应式的操作数据 8 | 9 | 你曾经是否订阅过报纸或杂志呢?互联网已经大幅度削减了传统出版物的用户群,但有当订阅的报纸是与当天发生的事件跟上的最佳途径之一的时候,传统媒体还是有一定机会的。你可以每个清晨细数投递来的报纸上的实事,在早餐或在上班路上进行阅读。 10 | 11 | 现在假设在订阅然后付费后,好几天去了,都没有收到任何报纸。又过了几天,你致电报纸销售处问道,为什么还没有收到你每日的报纸。试想一下,如果他们解释说,“你订阅的是整整一年的报纸,一年的报纸现在尚未全部完成,但是放心,当全年的报纸都准备完成后,你一定会得到所有的报纸。”这样你该会有多吃惊。 12 | 13 | 值得庆幸的是,不是所有的订阅都是这样工作的。报纸还有一定的时效性。在出版后,它们被尽快的投递出去,这样保证了当报纸被阅读时,内容仍然是最新的。此外,当你正在阅读最新的内容时,本报记者正在写后续的新故事,同时印刷机也在并行的出版下一个版本。 14 | 15 | 当我们开发应用程序时,有两种风格的代码我们可以写:命令式和响应式: 16 | 17 | - _同步式_ 的代码很像是假想的荒谬的报纸订阅的情况。这是一套串行任务,每次运行一个,完成前一个任务后再完成后一个。数据是批量进行处理的,在前面的任务没有完成批量数据处理前,不能将工作移交到下一个任务。 18 | - _响应式_ 的代码很像是真正的报纸订阅的情况。定义一组任务去处理数据,但这些任务可并行运行。每个任务处理这些数据的一个子集,当它处理另外一个子集的时候,把处理完成的数据交给下一个任务。 19 | 20 | 在本章中,我们将暂时离开 Tao Cloud 应用程序,来探索 Project Reactor。Reactor 是响应式编程的一个库,这个库是 Spring 家族的一部分。而且,因为它是 Spring 5 对响应式编程支持的基础,在使用 Spring 建立响应式 controller 和 repository 之前,很重要的一步是让你理解 Reactor。在我们开始理解 Reactor 之前,让我们来快速浏览响应式编程的要领。 21 | -------------------------------------------------------------------------------- /01~基础使用/03~依赖注入/README.md: -------------------------------------------------------------------------------- 1 | # IoC 2 | 3 | 在 Spring 中,常常用到控制反转(IoC),而这个概念的理解恰恰是最困难的部分:控制反转中,“反转”的概念在于,将对象的控制权由代码本身移交给容器,过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。多数时候,控制反转也叫依赖注入(DI),(虽然控制反转的实现不止依赖注入一种,还有其它方式如依赖查找等) 4 | 5 | 依赖注入的方法是: 6 | 7 | - 如果类 X 的一个实例对象 x 调用了类 Y 的一个实例对象 y 的方法,那么就称类 X 依赖于类 Y。为了打破这种依赖——“反转”,首先我们可以引入一个接口 I(第三方类),接口 I 中声明了对象 y 将被对象 x 调用的所有方法;然后,我们对类 Y 稍加改造,使其实现接口 I;最后,我们把在对象 x 中对 y 的调用改为对接口 I 中对应方法的调用。经过这番改造后,原先的 X 对 Y 的依赖关系不存在了,类 X 和 Y 现在都依赖于接口 I。 8 | - 这种通过引入接口 I 来消除类 X 对 Y 的依赖的方法,被称作“控制反转”,又叫做“依赖注入”。 9 | - 需要注意的是,类 Y 可能还依赖于其他类。在应用反转之前,X 依赖于 Y,从而也间接依赖于 Y 所依赖的所有“其他类”。应用控制反转之后,不仅消除了 X 对 Y 的直接依赖,且前面提到的那些所有的间接依赖也消除了。而新引入的接口 I 则不依赖于任何类。 10 | 11 | 一般控制反转分为 3 类: 12 | 13 | - Type 1 : 基于接口(interface injection)。Depending object 需要实现(implement) 特定 interface 以供框架注入所需对象。 14 | - Type 2 : 基于设值函数(setter injection)。Depending object 需要实现特定 setter 方法(但不需要依赖特定 interface), 15 | - Type 3 : 基于构造函数(constructor injection) 16 | 17 | 套用 Laravel 中的名词,Spring 中的 ApplicationContext 本身就是起到了一个服务容器的作用,所有在 ApplicationContext 中注入的 Bean 会由 Spring 自动构造,并且根据@AutoWired 关键字自动的注入到各个类的成员中。 18 | -------------------------------------------------------------------------------- /02~数据库/02~MyBatis/MyBatis 基础使用/01~配置使用/2023~MyBatis 基础使用/05~SpringBoot 集成.md: -------------------------------------------------------------------------------- 1 | # Spring Boot 中集成 MyBatis 2 | 3 | # 多类型支持 4 | 5 | ## Optional 6 | 7 | 在 Mybatis 3.5 支持 Optional 之后,我们还可以将判断优雅化为如下形式: 8 | 9 | ```java 10 | @Mapper 11 | public interface UserMapper { 12 | @Select("select * from user where id = #{id}") 13 | Optional selectById(Long id); 14 | } 15 | ``` 16 | 17 | ```java 18 | public class UserController { 19 | @Autowired 20 | private UserMapper userMapper; 21 | 22 | @GetMapping("/{id}") 23 | public User findById(@PathVariable Long id) { 24 | User user = this.userMapper.selectById(id); 25 | if (user == null) { 26 | // 抛异常,或者做点其他事情 27 | } 28 | } 29 | } 30 | ``` 31 | 32 | ```java 33 | public class UserController { 34 | @Autowired 35 | private UserMapper userMapper; 36 | 37 | @GetMapping("/{id}") 38 | public User findById(@PathVariable Long id) { 39 | return this.userMapper.selectById(id) 40 | .orElseThrow( 41 | () -> new IllegalArgumentException("This user does not exit!") 42 | ); 43 | } 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /02~数据库/02~MyBatis/Mybatis Plus/README.md: -------------------------------------------------------------------------------- 1 | ![Mybatis Plus 概念图](https://s2.ax1x.com/2019/12/16/Q4ASun.png) 2 | 3 | # Mybatis Plus 4 | 5 | MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 6 | 7 | ![MyBatis Plus 架构](https://s3.ax1x.com/2021/02/08/yNvc4J.png) 8 | 9 | ## 背景特性 10 | 11 | - **无侵入**:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑 12 | - **损耗小**:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作 13 | - **强大的 CRUD 操作**:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 14 | - **支持 Lambda 形式调用**:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错 15 | - **支持主键自动生成**:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题 16 | - **支持 ActiveRecord 模式**:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作 17 | - **支持自定义全局通用操作**:支持全局通用方法注入(Write once, use anywhere) 18 | - **内置代码生成器**:采用代码或者 Maven 插件可快速生成 Mapper、Model、Service、Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用 19 | - **内置分页插件**:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询 20 | - **分页插件支持多种数据库**:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库 21 | - **内置性能分析插件**:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询 22 | - **内置全局拦截插件**:提供全表 delete、update 操作智能分析阻断,也可自定义拦截规则,预防误操作 23 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/02~集成 Spring/第 7 章 调用 REST 服务/第 7 章 调用 REST 服务.md: -------------------------------------------------------------------------------- 1 | # 第 7 章 调用 REST 服务 2 | 3 | 本章内容: 4 | 5 | - 使用 RestTemplate 调用 REST API 6 | - 使用 Traverson 引导超媒体 API 7 | 8 | 你是否曾经去看电影,当电影开始的时候,你发现只有你一个人在电影院?从本质上说,这是一次私人观影的美妙经历。你可以选择任何你想要的座位,和屏幕上的人物交谈,甚至可以打开你的手机发推特谈论它,而不会有人因为破坏了他们的观影体验而生气。最棒的是,也没有其他人会为你毁了这部电影! 9 | 10 | 这种情况在我身上并不常见。但当它出现的时候,我在想如果我没有出现会发生什么。他们还会放映这部电影吗?英雄还会拯救世界吗?电影结束后,工作人员还会打扫影院吗? 11 | 12 | 没有观众的电影就像没有客户端的 API。它已经准备好接受和提供数据了,但是如果 API 从未被调用过,那么它真的是一个 API 吗?就像薛定谔的猫一样,在我们向它发出请求之前,我们无法知道 API 是活动的还是返回 HTTP 404 响应。 13 | 14 | 在前一章中,重点介绍了如何定义 REST 端点,以供应用程序外部的一些客户端使用。尽管开发这样一个 API 的驱动力是一个用作 Taco Cloud 网站的单页面 Angular 应用程序,但事实是客户端可以是任何语言的任何应用程序 —— 甚至是另一个 Java 应用程序。 15 | 16 | Spring 应用程序既提供 API,又向另一个应用程序的 API 发出请求,这种情况并不少见。事实上,在微服务的世界里,这正变得越来越普遍。因此,花点时间看看如何使用 Spring 与 REST API 交互是值得的。 17 | 18 | Spring 应用程序可以通过以下方式使用 REST API: 19 | 20 | - RestTemplate —— 一个由 Spring 核心框架提供的简单、同步 REST 客户端。 21 | - _Traverson_ —— 可感知超链接的同步 REST 客户端,由 Spring HATEOAS 提供,灵感来自同名的 JavaScript 库。 22 | - WebClient —— 一个在 Spring 5 中引入的响应式、异步 REST 客户端。 23 | 24 | 我将推迟到第 11 章讨论 Spring 的响应式 web 框架的时候再讨论 WebClient。现在,我们将主要关注另外两个 REST 客户端,首先是 RestTemplate。 25 | -------------------------------------------------------------------------------- /09~源码解析/深入请求处理/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot 请求处理 2 | 3 | # Controller 4 | 5 | # @RestController 6 | 7 | Spring 4.0 引入了 @RestController,这是一个控制器的专用版本,它是一个方便的注释,除了添加 @Controller 和 @ResponseBody 注释之外什么都不做。通过使用 @RestController 注释注释控制器类,您不再需要将 @ResponseBody 添加到所有请求映射方法。@ResponseBody 注释默认处于活动状态。 8 | 9 | ![](https://resources.cloud.genuitec.com/wp-content/uploads/2015/09/4.x-diagram.png) 10 | 11 | ```java 12 | @RestController 13 | @RequestMapping("employees") 14 | public class EmployeeController { 15 | Employee employee = new Employee(); 16 | 17 | @RequestMapping(value = "/{name}", method = RequestMethod.GET, produces = "application/json") 18 | public Employee getEmployeeInJSON(@PathVariable String name) { 19 | employee.setName(name); 20 | employee.setEmail("employee1@genuitec.com"); 21 | return employee; 22 | } 23 | 24 | @RequestMapping(value = "/{name}.xml", method = RequestMethod.GET, produces = "application/xml") 25 | public Employee getEmployeeInXML(@PathVariable String name) { 26 | employee.setName(name); 27 | employee.setEmail("employee1@genuitec.com"); 28 | return employee; 29 | } 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /02~数据库/04~缓存与 KV 存储/Redis/Redisson/集合.md: -------------------------------------------------------------------------------- 1 | # Collections 2 | 3 | 我们以与处理对象相同的方式处理 Redisson 集合。Redisson 提供的分布式集合包括: 4 | 5 | - Map 6 | - Multimap 7 | - Set 8 | - SortedSet 9 | - ScoredSortedSet 10 | - LexSortedSet 11 | - List 12 | - Queue 13 | - Deque 14 | - BlockingQueue 15 | - BoundedBlockingQueue 16 | - BlockingDeque 17 | - BlockingFairQueue 18 | - DelayedQueue 19 | - PriorityQueue 20 | - PriorityDeque 21 | 22 | # Map 23 | 24 | 基于 Redisson 的 Map 实现 java.util.concurrent.ConcurrentMap 和 java.util.Map 接口。Redisson 有四个 Map 实现,这些是 RMap,RMapCache,RLocalCachedMap 和 RClusteredMap。 25 | 26 | ```java 27 | RMap map = client.getMap("ledger"); 28 | Ledger newLedger = map.put("123", new Ledger());map 29 | ``` 30 | 31 | RMapCache 支持地图条目驱逐,RLocalCachedMap 允许对地图条目进行本地缓存。RClusteredMap 允许跨 Redis 群集主节点拆分来自单个映射的数据。 32 | 33 | # Set 34 | 35 | 基于 Redisson 的 Set 实现 java.util.Set 接口。Redisson 具有三个 Set 实现,即 RSet,RSetCache 和 RClusteredSet,其功能与对应的地图类似。 36 | 37 | ```java 38 | RSet ledgerSet = client.getSet("ledgerSet"); 39 | ledgerSet.add(new Ledger()); 40 | ``` 41 | 42 | # List 43 | 44 | ```java 45 | RList ledgerList = client.getList("ledgerList"); 46 | ledgerList.add(new Ledger()); 47 | ``` 48 | -------------------------------------------------------------------------------- /02~数据库/05~数据库工具/嵌入式数据库/HSQLDB/运行模式.md: -------------------------------------------------------------------------------- 1 | # 运行模式 2 | 3 | hsql 数据库引擎有几种服务器模式:常用的 Server 模式、WebServer 模式、Servlet 模式、Standlone 模式、Memory-Only 数据库。 4 | 5 | ## 内存(Memory-Only)模式 6 | 7 | 所有的数据都将在内存中完成,如果程序退出,则相应的数据也将同时被销毁。连接 JDBC 的实例为:`jdbc:hsqldb:mem:dbname`。 8 | 9 | ## 进行(In-Process)模式 10 | 11 | 此模式从应用程序启动数据库,由于所有的数据都将写到文件中,所以,即使程序退出,数据也不会被销毁。In-Process 不需要另外启动,可以通过 12 | 13 | ```java 14 | DriverManager.getConnection("jdbcUriName","username","password"); 15 | ``` 16 | 17 | 方式即可启动数据库。连接 JDBC 的实例为: 18 | 19 | ```xml 20 | jdbc:hsqldb:file:/E:/hsqldb/data/dbname 21 | jdbc:hsqldb:file:/opt/db/dbname 22 | jdbc:hsqldb:file:dbname 23 | ``` 24 | 25 | ## 服务器模式 26 | 27 | 此模式下 HSQLDB 跟其它数据库服务器一样,需要通过服务器的形式来进行启动,可以通过: 28 | 29 | ```java 30 | $ java -classpath ../lib/hsqldb.jar org.hsqldb.server.WebServer –database.0 testdb –dbname.0 testdbName 31 | ``` 32 | 33 | 的命令启动一个守护进程。连接 JDBC 的实例为: 34 | 35 | ```sql 36 | jdbc:hsqldb:hsql://localhost:port/dbname 37 | ``` 38 | 39 | ## Web 服务器模式 40 | 41 | 此模式以 WEB 服务器的形式启动,并通过 HTTP 协议接受客户端命令。从 1.7.2 版本开始,Web 服务器模式的 HSQLDB 开始支持事务处理。可以通过: 42 | 43 | ```sh 44 | java -classpath ../lib/hsqldb.jar org.hsqldb.WebServer –database.0 testdb –dbname.0 testdbname 45 | ``` 46 | 47 | 的命令来启动。 48 | -------------------------------------------------------------------------------- /01~基础使用/03~依赖注入/应用上下文/Spring Runner.md: -------------------------------------------------------------------------------- 1 | # Spring 应用的生命周期 2 | 3 | # Runner 4 | 5 | Spring Boot 为我们提供了两个接口,CommandLineRunner 与 ApplicationRunner,它们能够在应用启动之后执行部分业务逻辑。 6 | 7 | ## CommandLineRunner 8 | 9 | CommandLineRunner 能够允许我们访问到应用的启动参数: 10 | 11 | ```java 12 | @Component 13 | public class CommandLineAppStartupRunner implements CommandLineRunner { 14 | private static final Logger logger = LoggerFactory.getLogger( 15 | CommandLineAppStartupRunner.class 16 | ); 17 | 18 | @Override 19 | public void run(String... args) throws Exception { 20 | logger.info( 21 | "Application started with command-line arguments: {} . \n To kill this application, press Ctrl + C.", 22 | Arrays.toString(args) 23 | ); 24 | } 25 | } 26 | ``` 27 | 28 | ## ApplicationRunner 29 | 30 | ApplicationRunner 则是对启动参数进行了二次封装: 31 | 32 | ```java 33 | @Component 34 | public class AppStartupRunner implements ApplicationRunner { 35 | private static final Logger logger = LoggerFactory.getLogger( 36 | AppStartupRunner.class 37 | ); 38 | 39 | @Override 40 | public void run(ApplicationArguments args) throws Exception { 41 | logger.info( 42 | "Your application started with option names : {}", 43 | args.getOptionNames() 44 | ); 45 | } 46 | } 47 | ``` 48 | -------------------------------------------------------------------------------- /02~数据库/01~Spring JDBC/README.md: -------------------------------------------------------------------------------- 1 | # Spring Jdbc 2 | 3 | 对于信息的存储,现在已经有非常非常多的产品可以选择,其中不乏许多非常优秀的开源免费产品,比如:MySQL,Redis 等。接下来,我们将继续学习在使用 Spring Boot 开发服务端程序的时候,如何实现对各流行数据存储产品的增删改查操作。 4 | 5 | # 基本概念 6 | 7 | ## JDBC 8 | 9 | Java 数据库连接(Java Database Connectivity,简称 JDBC)是 Java 语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC 也是 Sun Microsystems 的商标。我们通常说的 JDBC 是面向关系型数据库的。JDBC API 主要位于 JDK 中的 java.sql 包中(之后扩展的内容位于 javax.sql 包中),主要包括(斜体代表接口,需驱动程序提供者来具体实现): 10 | 11 | - DriverManager:负责加载各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接(Connection)。 12 | - Driver:驱动程序,会将自身加载到 DriverManager 中去,并处理相应的请求并返回相应的数据库连接(Connection)。 13 | - Connection:数据库连接,负责与进行数据库间通讯,SQL 执行以及事务处理都是在某个特定 Connection 环境中进行的。可以产生用以执行 SQL 的 Statement。 14 | - Statement:用以执行 SQL 查询和更新(针对静态 SQL 语句和单次执行)。PreparedStatement:用以执行包含动态参数的 SQL 查询和更新(在服务器端编译,允许重复执行以提高效率)。 15 | - CallableStatement:用以调用数据库中的存储过程。 16 | - SQLException:代表在数据库连接的建立和关闭和 SQL 语句的执行过程中发生了例外情况(即错误)。 17 | 18 | ## 数据源 19 | 20 | 可以看到,在 java.sql 中并没有数据源(Data Source)的概念。这是由于在 java.sql 中包含的是 JDBC 内核 API,另外还有个 javax.sql 包,其中包含了 JDBC 标准的扩展 API。而关于数据源(Data Source)的定义,就在 javax.sql 这个扩展包中。实际上,在 JDBC 内核 API 的实现下,就已经可以实现对数据库的访问了,那么我们为什么还需要数据源呢?主要出于以下几个目的: 21 | 22 | - 封装关于数据库访问的各种参数,实现统一管理 23 | - 通过对数据库的连接池管理,节省开销并提高效率 24 | 25 | 在 Java 这个自由开放的生态中,已经有非常多优秀的开源数据源可以供大家选择,比如:DBCP、C3P0、Druid、HikariCP 等。 26 | -------------------------------------------------------------------------------- /04~工程实践/日志处理/Slf4j/Slf4j.md: -------------------------------------------------------------------------------- 1 | # Slf4j 2 | 3 | SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统。按照官方的说法,SLF4J 是一个用于日志系统的简单 Facade,允许最终用户在部署其应用时使用其所希望的日志系统。 4 | 5 | ![](http://www.slf4j.org/images/concrete-bindings.png) 6 | 7 | ```java 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class HelloWorld { 12 | 13 | public static void main(String[] args) { 14 | Logger logger = LoggerFactory.getLogger(HelloWorld.class); 15 | logger.info("Hello World"); 16 | } 17 | } 18 | ``` 19 | 20 | Spring Boot 对 slf4j 支持的很好,内部已经集成了 slf4j,一般我们在使用的时候,会对 slf4j 做一下配置。`application.yml` 文件是 Spring Boot 中唯一一个需要配置的文件,一开始创建工程的时候是 `application.properties` 文件,个人比较细化用 yml 文件,因为 yml 文件的层次感特别好,看起来更直观,但是 yml 文件对格式要求比较高,比如英文冒号后面必须要有个空格,否则项目估计无法启动,而且也不报错。用 properties 还是 yml 视个人习惯而定,都可以。本课程使用 yml。 21 | 22 | 我们看一下 application.yml 文件中对日志的配置: 23 | 24 | ``` 25 | logging: 26 | config: logback.xml 27 | level: 28 | com.test.course03.dao: trace 29 | ``` 30 | 31 | `logging.config` 是用来指定项目启动的时候,读取哪个配置文件,这里指定的是日志配置文件是根路径下的 `logback.xml` 文件,关于日志的相关配置信息,都放在 `logback.xml` 文件中了。`logging.level` 是用来指定具体的 mapper 中日志的输出级别,上面的配置表示 `com.test.course03.dao` 包下的所有 mapper 日志输出级别为 trace,会将操作数据库的 sql 打印出来,开发时设置成 trace 方便定位问题,在生产环境上,将这个日志级别再设置成 error 级别即可。 32 | 33 | 常用的日志级别按照从高到低依次为:ERROR、WARN、INFO、DEBUG。 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all 2 | * 3 | 4 | # Unignore all with extensions 5 | !*.* 6 | 7 | # Unignore all dirs 8 | !*/ 9 | 10 | .DS_Store 11 | 12 | # Logs 13 | logs 14 | *.log 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | # Runtime data 20 | pids 21 | *.pid 22 | *.seed 23 | *.pid.lock 24 | 25 | # Directory for instrumented libs generated by jscoverage/JSCover 26 | lib-cov 27 | 28 | # Coverage directory used by tools like istanbul 29 | coverage 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # Bower dependency directory (https://bower.io/) 38 | bower_components 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (https://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Dependency directories 47 | node_modules/ 48 | jspm_packages/ 49 | 50 | # TypeScript v1 declaration files 51 | typings/ 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Optional REPL history 60 | .node_repl_history 61 | 62 | # Output of 'npm pack' 63 | *.tgz 64 | 65 | # Yarn Integrity file 66 | .yarn-integrity 67 | 68 | # dotenv environment variables file 69 | .env 70 | 71 | # next.js build output 72 | .next 73 | -------------------------------------------------------------------------------- /00~特性与生态/背景特性.md: -------------------------------------------------------------------------------- 1 | # Spring 背景特性 2 | 3 | # 特性 4 | 5 | - 轻量非侵入:从大小与开销两方面而言,Spring 都是非常轻量的,完整的 Spring 框架可以在一个大小仅有 1M 多的 JAR 文件里发布,并且 Spring 所需要的处理开销也是微不足道的。 6 | 7 | - 控制反转:Spring 通过所谓的 IoC 控制反转的技术来促进低耦合,在应用了 IoC 之后,一个对象依赖的其他对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。 8 | 9 | - 面向切面:Spring 支持面向切面的编程,并且把应用业务逻辑和系统服务分开。 10 | 11 | - 容器:Spring 包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,我们可以基于可配置原型来配置 Bean 是如何被创建的、如何被关联的;可以设置是创建一个单独的实例或者每次需要时都生成一个新的实例。 12 | 13 | - 框架:Spring 可以将简单的组件配置,组合成复杂的应用,在 Spring 中,应用对象被声明式地组合,典型的是在 XML 文件中进行声明。Spring 也提供了很多的基础功能(事务管理、持久化框架集成等),将应用逻辑的开发留给开发者。 14 | 15 | # 配置方式 16 | 17 | 在 Spring 1.x 的时代,主要使用 XML 来配置 Bean,我们需要频繁的在开发的类和配置文件之间切换。到了在 Spring2.x 时代,随着 JDK1.5 带来的注解支持,Spring 提供了声明 Bean 的注解(@Component,@Service 等),大大减少了配置量。而从 Spring 3.x 以后,Spring 提供了更为丰富的 Java 配置的能力(@Configuration,@Bean)等。 18 | 19 | # Spring Boot 20 | 21 | Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是 Spring Boot 其实不是什么新的框架,它默认配置了很多框架的使用方式,就像 Maven 整合了所有的 Jar 包,Spring Boot 整合了所有的框架。 22 | 23 | ![Spring Boot 概述](https://s2.ax1x.com/2019/09/03/nFQxUO.png) 24 | 25 | Spring Boot 充分利用了 JavaConfig 的配置模式以及“约定优于配置”的理念,能够极大的简化基于 Spring MVC 的 Web 应用和 REST 服务开发。Spring Boot 可以使得创建独立的产品级别的基于 Spring 的应用程序变得更加简单,使得能够做到开箱即用。Spring Boot 可以用于创建能够直接用 `java -jar` 命令运行的或者类似于传统的 war 部署方式的应用程序。同样也提供了所谓的 `spring scripts` 这样的命令行工具。 26 | -------------------------------------------------------------------------------- /03~Spring Security/策略配置/CORS.md: -------------------------------------------------------------------------------- 1 | # CORS 2 | 3 | [CORS, Cross-Origin Resource Sharing](https://parg.co/reV) 是常用的跨域请求访问协议,允许您指定授权的跨域请求类型,而不是使用基于 IFRAME 或 JSONP 的安全性较低且功能较弱的变通方法。Spring MVC 的 HandlerMapping 内建支持了 CORS 协议,它能够检查给定请求和处理程序的 CORS 配置并采取进一步操作:直接处理预检请求,同时拦截,验证简单和实际的 CORS 请求,并设置所需的 CORS 响应头。 4 | 5 | 我们可以使用 CrossOrigin 注解在类级别或者方法级别的注解来标识跨域访问: 6 | 7 | ```java 8 | @CrossOrigin(maxAge = 3600) 9 | @RestController 10 | @RequestMapping("/account") 11 | public class AccountController { 12 | 13 | @CrossOrigin("http://domain2.com") 14 | @GetMapping("/{id}") 15 | public Account retrieve(@PathVariable Long id) { 16 | // ... 17 | } 18 | 19 | @DeleteMapping("/{id}") 20 | public void remove(@PathVariable Long id) { 21 | // ... 22 | } 23 | } 24 | ``` 25 | 26 | 我们也可以在全局的配置中添加 CORS 统一配置: 27 | 28 | ```java 29 | @Configuration 30 | @EnableWebMvc 31 | public class WebConfig implements WebMvcConfigurer { 32 | 33 | @Override 34 | public void addCorsMappings(CorsRegistry registry) { 35 | registry 36 | .addMapping("/api/**") 37 | .allowedOrigins("http://domain2.com") 38 | .allowedMethods("PUT", "DELETE") 39 | .allowedHeaders("header1", "header2", "header3") 40 | .exposedHeaders("header1", "header2") 41 | .allowCredentials(true) 42 | .maxAge(3600); 43 | // Add more mappings... 44 | } 45 | } 46 | ``` 47 | -------------------------------------------------------------------------------- /01~基础使用/04~请求与响应/异常处理/重试机制.md: -------------------------------------------------------------------------------- 1 | # Spring 重试机制 2 | 3 | 如今,Spring Retry 是一个独立的包了(早期是 Spring Batch 的一部分),下面是使用 Spring Retry 框架进行重试的几个重要步骤。首先加入 Spring Retry 依赖包 4 | 5 | ```xml 6 | 7 | org.springframework.retry 8 | spring-retry 9 | 1.1.2.RELEASE 10 | 11 | ``` 12 | 13 | 然后在应用中包含 main()方法的类或者在包含@Configuration 的类上加上@EnableRetry 注解,最后在想要进行重试的方法(可能发生异常)上加上@Retryable 注解: 14 | 15 | ```java 16 | @Retryable(maxAttempts=5,backoff = @Backoff(delay = 3000)) 17 | public void retrySomething() throws Exception{ 18 | logger.info("printSomething{} is called"); 19 | thrownew SQLException(); 20 | } 21 | ``` 22 | 23 | 在上面这个案例当中的重试策略就是重试 5 次,每次延时 3 秒。详细的使用文档看这里,它的主要配置参数有下面这样几个。其中 exclude、include、maxAttempts、value 几个属性很容易理解,比较看不懂的是 backoff 属性,它也是个注解,包含 delay、maxDelay、multiplier、random 四个属性。 24 | 25 | - **delay:**如果不设置的话默认是 1 秒 26 | - **maxDelay:**最大重试等待时间 27 | - **multiplier:**用于计算下一个延迟时间的乘数(大于 0 生效) 28 | - **random:**随机重试等待时间(一般不用) 29 | 30 | Spring Retry 的优点很明显: 31 | 32 | - 属于 Spring 大生态,使用起来不会太生硬; 33 | - 只需要在需要重试的方法上加上注解并配置重试策略属性就好,不需要太多侵入代码。 34 | 35 | 但同时也存在两个主要不足: 36 | 37 | - 第一,由于 Spring Retry 用到了 Aspect 增强,所以就会有使用 Aspect 不可避免的坑——方法内部调用,如果被 @Retryable 注解的方法的调用方和被调用方处于同一个类中,那么重试将会失效; 38 | - 第二,Spring 的重试机制只支持对异常进行捕获,而无法对返回值进行校验判断重试。如果想要更灵活的重试策略可以考虑使用 Guava Retry,也是一个不错的选择。 39 | -------------------------------------------------------------------------------- /02~数据库/03~ORM/JPA/README.md: -------------------------------------------------------------------------------- 1 | # JPA 2 | 3 | Java 持久化 API(JPA)是一个独立于供应商的、用于映射 Java 对象和关系型数据库表的规范。此规范的实现,使得应用程序的开发者们可以不依赖于他们工作中面对的特定数据库产品,从而开发出可以与不同数据库产品良好工作的 CRUD(创建、读取、更新、删除)操作代码。这些框架除了可以用于处理与数据库交互的代码(JDBC 代码),也可以用于映射数据和应用程序中的对象。 4 | 5 | JPA 由三个不同的组件构成: 6 | 7 | - `实体(Entities)`: 在当前版本的 JPA 中实体是普通 Java 对象(POJO)。老版本的 JPA 中实体类需要继承 JPA 提供的实体基类,但是这样的设计导致框架中存在了严重的依赖关系,测试变得更加困难;所以在新版 JPA 中不再要求实体类继承任何框架类。 8 | - `对象-关系型元数据(Object-relational metadata)`: 应用程序的开发者们必须正确设定 Java 类和它们的属性与数据库中的表和列的映射关系。有两种设定方式:通过特定的配置文件建立映射;或者使用在新版本中支持的注解。 9 | - `Java持久化查询语句(Java Persistence Query Language - JPQL):` 因为 JPA 旨在建立不依赖于特定的数据库的抽象层,所以它也提供了一种专有查询语言来代替 SQL。这种由 JPQL 到 SQL 语言的转换,为 JPA 提供了支持不同数据库方言的特性,使得开发者们在实现查询逻辑时不需要考虑特定的数据库类型。 10 | 11 | # 实体生命周期 12 | 13 | 描述了实体对象从创建到受控、从删除到游离的状态变换。对实体的操作主要就是改变实体的状态。 14 | 15 | ![实体生命周期](https://s3.ax1x.com/2021/02/07/yNu46f.png) 16 | 17 | - New,新创建的实体对象,没有主键(identity)值 18 | - Managed,对象处于 Persistence Context(持久化上下文)中,被 EntityManager 管理 19 | - Detached,对象已经游离到 Persistence Context 之外,进入 Application Domain 20 | - Removed, 实体对象被删除 21 | 22 | EntityManager 提供一系列的方法管理实体对象的生命周期,包括: 23 | 24 | - persist, 将新创建的或已删除的实体转变为 Managed 状态,数据存入数据库。 25 | - remove,删除受控实体 26 | - merge,将游离实体转变为 Managed 状态,数据存入数据库。 27 | 28 | 如果使用了事务管理,则事务的 commit/rollback 也会改变实体的状态。 29 | 30 | # 实体关系映射(ORM) 31 | 32 | ![实体关系映射](https://s3.ax1x.com/2021/02/07/yNKFhR.png) 33 | 34 | ## ID 生成策略 35 | -------------------------------------------------------------------------------- /03~Spring Security/权限验证/基于 JWT 的验证/README.md: -------------------------------------------------------------------------------- 1 | # JWT 2 | 3 | 本文将会详细介绍 Spring Boot 中集成 Spring Security 并基于 JWT 进行用户权限验证的案例,下图(图片源自 [bezkoder.com/spring-boot-jwt-authentication](https://bezkoder.com/spring-boot-jwt-authentication/))显示了我们如何实现用户注册、用户登录和授权的流程: 4 | 5 | ![请求与响应交互流程](https://s3.ax1x.com/2021/02/25/yvC7iq.png) 6 | 7 | 如果客户端访问受保护的资源,必须在 HTTP 授权头中添加一个合法的 JWT。你可以通过下图来了解我们的 Spring Boot Server。 8 | 9 | ![Spring Boot Server](https://s3.ax1x.com/2021/02/25/yvPym4.png) 10 | 11 | - WebSecurityConfigurerAdapter 是我们安全实现的核心。它提供了 HttpSecurity 配置来配置 cors、csrf、会话管理、受保护资源的规则。我们也可以扩展和定制默认的配置,它包含下面的元素。 12 | - UserDetailsService 接口有一个通过用户名加载 User 的方法,并返回一个 UserDetails 对象,Spring Security 可以用来进行认证和验证。 13 | - UserDetails 包含必要的信息(如:用户名、密码、权限)来构建一个认证对象。 14 | - UsernamePasswordAuthenticationToken 从登录请求中获取{用户名,密码},AuthenticationManager 将使用它来验证登录账户。 15 | - AuthenticationManager 有一个 DaoAuthenticationProvider(在 UserDetailsService 和 PasswordEncoder 的帮助下)来验证 UsernamePasswordAuthenticationToken 对象。如果成功,AuthenticationManager 返回一个完全填充的 Authentication 对象(包括授权)。 16 | - OncePerRequestFilter 对我们的 API 的每个请求进行一次执行。它提供了一个 doFilterInternal()方法,我们将实现解析和验证 JWT,加载 User 详情(使用 UserDetailsService),检查 Authorizaion(使用 UsernamePasswordAuthenticationToken)。 17 | - AuthenticationEntryPoint 将捕获认证错误。 18 | 19 | # Links 20 | 21 | - 本文关联的代码仓库参阅:[Spring-Notes/examples](https://github.com/wx-chevalier/Spring-Notes) 22 | -------------------------------------------------------------------------------- /01~基础使用/09~定时任务/TaskScheduler.md: -------------------------------------------------------------------------------- 1 | # Spring TaskScheduler 2 | 3 | # Ticker 4 | 5 | ```java 6 | @Component 7 | public class Ticker { 8 | private final TaskScheduler taskScheduler; 9 | private final Long monitorMeasurementIntervalMS; 10 | private final List listeners = new ArrayList<>(); 11 | 12 | public Ticker( 13 | TaskScheduler taskScheduler, 14 | ApplicationProperty applicationProperty 15 | ) { 16 | this.taskScheduler = taskScheduler; 17 | this.monitorMeasurementIntervalMS = 18 | applicationProperty.getMonitorMeasurementIntervalMS(); 19 | } 20 | 21 | @PostConstruct 22 | public void init() { 23 | this.taskScheduler.scheduleAtFixedRate( 24 | this::execute, 25 | Duration.ofMillis(monitorMeasurementIntervalMS) 26 | ); 27 | } 28 | 29 | @PreDestroy 30 | public void destroy() {} 31 | 32 | private void execute() { 33 | for (TickListener listener : listeners) { 34 | listener.onTick(); 35 | } 36 | } 37 | 38 | public void register(TickListener listener) { 39 | listeners.add(listener); 40 | } 41 | 42 | public void unregister(TickListener tickListener) { 43 | listeners.remove(tickListener); 44 | } 45 | 46 | public interface TickListener { 47 | void onTick(); 48 | } 49 | } 50 | ``` 51 | 52 | # Links 53 | 54 | - https://www.baeldung.com/spring-task-scheduler 55 | -------------------------------------------------------------------------------- /02~数据库/04~缓存与 KV 存储/Redis/Redisson/服务.md: -------------------------------------------------------------------------------- 1 | # Services 2 | 3 | Redisson 提供了 4 种类型的分布式服务:Remote Service, Live Object Service, Executor Service and Scheduled Executor Service。 4 | 5 | ## Remote Service 6 | 7 | 该服务提供了 Redis 促进的 Java 远程方法调用。Redisson 远程服务由服务器端(Worker Instance)和客户端实现组成。服务器端实现执行客户端调用的远程方法。服务端注册用于远程调用的接口: 8 | 9 | ```java 10 | RRemoteService remoteService = client.getRemoteService(); 11 | LedgerServiceImpl ledgerServiceImpl = new LedgerServiceImpl(); 12 | 13 | remoteService.register(LedgerServiceInterface.class, ledgerServiceImpl); 14 | ``` 15 | 16 | 客户端则可以直接执行调用: 17 | 18 | ```java 19 | RRemoteService remoteService = client.getRemoteService(); 20 | LedgerServiceInterface ledgerService 21 | = remoteService.get(LedgerServiceInterface.class); 22 | 23 | List entries = ledgerService.getEntries(10); 24 | ``` 25 | 26 | # Live Object Service 27 | 28 | Redisson Live Objects 将只能从单个 JVM 访问的标准 Java 对象的概念扩展到可以在不同机器中的不同 JVM 之间共享的增强 Java 对象。它通过将对象的字段映射到 Redis 哈希来完成的。通过运行时构造的代理类进行此映射,字段获取器和设置器被映射到 Redis hget/hset 命令。 29 | 30 | ```java 31 | @REntity 32 | public class LedgerLiveObject { 33 | @RId 34 | private String name; 35 | 36 | // getters and setters... 37 | } 38 | 39 | RLiveObjectService service = client.getLiveObjectService(); 40 | 41 | LedgerLiveObject ledger = new LedgerLiveObject(); 42 | ledger.setName("ledger1"); 43 | 44 | ledger = service.persist(ledger); 45 | 46 | LedgerLiveObject returnLedger 47 | = service.get(LedgerLiveObject.class, "ledger1"); 48 | ``` 49 | -------------------------------------------------------------------------------- /04~工程实践/测试/JUnit 5/README.md: -------------------------------------------------------------------------------- 1 | # JUnit 5 2 | 3 | JUnit 是 Java 中使用最广泛的测试框架,JUnit5 主要在希望能够适应 Java8 风格的编码以及相关工,这就是为什么建议在 Java 8 之后的项目中使用 JUnit5 来创建和执行测试。JUnit5 的第一个可用性版本是在 2017 年 9 月 10 日发布的。`JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage`: 4 | 5 | - JUnit Platform: 启动 Junit 测试、IDE、构建工具或插件都需要包含和扩展 Platform API,它定义了 TestEngine 在平台运行的新测试框架的 API。它还提供了一个控制台启动器,可以从命令行启动 Platform,为 Gradle 和 Maven 插件提供支持。 6 | - JUnit Jupiter: 它用于编写测试代码的新的编程和扩展模型。它具有所有新的 Junit 注释和 TestEngine 实现来运行这些注释编写的测试。 7 | - JUnit Vintage: 它主要的目的是支持在 JUnit5 的测试代码中运行 JUnit3 和 4 方式写的测试,它能够向前兼容之前的测试代码。 8 | 9 | ![JUnit 5 平台构成](https://ngte-superbed.oss-cn-beijing.aliyuncs.com/item/20230416204449.png) 10 | 11 | # 快速开始 12 | 13 | 使用 JUnit 5 的时候需要在 Maven 添加对应的依赖: 14 | 15 | ```xml 16 | 17 | org.junit.jupiter 18 | junit-jupiter-engine 19 | ${junit.jupiter.version} 20 | 21 | 22 | org.junit.platform 23 | junit-platform-runner 24 | ${junit.platform.version} 25 | test 26 | 27 | ``` 28 | 29 | 或者在 Gradle 中添加如下的依赖: 30 | 31 | ```groovy 32 | testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0-M4") 33 | testRuntime("org.junit.platform:junit-platform-runner:1.0.0-M4") 34 | ``` 35 | 36 | > Tips: 无论是单元测试,还是集成测试,我们都必须小心地控制代码的边界,避免耦合带来的意外失败,并提高用例地运行效率。 37 | 38 | # Links 39 | 40 | - https://www.baeldung.com/spring-boot-testing 41 | -------------------------------------------------------------------------------- /09~源码解析/深入请求处理/Jetty/README.md: -------------------------------------------------------------------------------- 1 | # Jetty 内部解析 2 | 3 | ![](http://www.eclipse.org/jetty/documentation/current/images/Jetty_DeployManager_DeploymentManager_Roles.png) 4 | 5 | # ThreadPool:线程池的实现 6 | 7 | 最近在项目中使用到了 Jetty,想弄清楚 Jetty 在默认情况下会使用多少个线程来处理请求,看了一下源代码。 8 | Jetty 中的 org.eclipse.jetty.server.Server 类的默认构造函数如下: 9 | 10 | public Server(@Name("port")int port) 11 | { 12 | this((ThreadPool)null); 13 | ServerConnector connector=new ServerConnector(this); 14 | connector.setPort(port); 15 | setConnectors(new Connector[]{connector}); 16 | } 17 | 18 | 可以看出,默认情况下,这个构造函数调用了另一个构造函数: 19 | public Server(@Name("threadpool") ThreadPool pool) 20 | { 21 | \_threadPool=pool!=null?pool:new QueuedThreadPool(); 22 | addBean(\_threadPool); 23 | setServer(this); 24 | } 25 | 26 | 于是,这个 QueuedThreadPool 就是我们的线程池了。果断跟踪进去看一下,QueuedThreadPool 有以下几个构造函数,可以看出,默认情况下,线程池中最少的线程数为 8,而默认最大线程数为 200,问题解决。 27 | 28 | public QueuedThreadPool() 29 | { 30 | this(200); 31 | } 32 | 33 | public QueuedThreadPool(@Name("maxThreads") int maxThreads) 34 | { 35 | this(maxThreads, 8); 36 | } 37 | 38 | public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads) 39 | { 40 | this(maxThreads, minThreads, 60000); 41 | } 42 | 43 | public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout")int idleTimeout) 44 | { 45 | this(maxThreads, minThreads, idleTimeout, null); 46 | } 47 | 48 | # Links 49 | 50 | - [jetty 线程池的实现](http://blog.csdn.net/pwlazy/article/details/7166395) 51 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 1 章 Spring 入门/第 1 章 Spring 入门.md: -------------------------------------------------------------------------------- 1 | # 第 1 章  Spring 入门 2 | 3 | 本章内容: 4 | 5 | - Spring 和 Spring Boot 概述 6 | - 初始化一个 Spring 项目 7 | - 纵览 Spring 8 | 9 | 尽管希腊哲学家赫拉克利特(Heraclitus)并非以软件开发人员而闻名,他在这个问题上似乎掌握得很好。有人引用他的话说:“唯一不变的就是变化。” 这句话体现了软件开发的实质。 10 | 11 | 当 Spring 的最初形式在 Rod Johnson 的《_Expert One-on-One J2EE Design and Development_》书中介绍了框架(Wrox,2002,http://mng.bz/oVjy)。 12 | 13 | 当 Rod Johnson 在书《_Expert One-on-One J2EE Design and Development_》(Wrox,2002,http://mng.bz/oVjy)中介绍了 Spring 框架的最初形式后,我们今天开发应用程序的方式与一年前、五年前、十年前,甚至 15 年前都不一样了。 14 | 15 | 当时,最常见的应用程序类型是基于浏览器的由关系数据库支持的 Web 应用程序。虽然这种类型的发展仍然很重要,同时 Spring 很好地适合这些应用,我们现在也对开发由微服务组成的应用程序感兴趣,旨在将数据持久保存在各种数据库中的云。还有新的兴趣响应式编程的目的是提供更大的可扩展性和改进的性能与非阻塞操作。 16 | 17 | 随着软件开发的发展,Spring 框架也改变为解决现代发展问题,包括微服务和反应式编程。Spring 还着手通过引入 Spring Boot 来简化自己的开发模型。无论是开发简单的数据库支持的 Web 应用程序,还是构建一个围绕微服务构建的现代应用程序,Spring 是框架这将帮助您实现目标。本章是您踏上旅程的第一步通过使用 Spring 进行现代应用程序开发。 18 | 19 | 尽管希腊哲学家赫拉克利(Heraclitus)不是著名的软件开发人员,但他似乎很好地掌握了这一主题。他曾说过:“唯一不变的是变化。” 这句话抓住了软件开发的一个基本真理。 20 | 我们今天开发应用程序的方法是不同的比一年前,五年前,十年前甚至十五年前,当最初的形式介绍了 Spring 框架的 Rod Johnson 的书,专家一对一的 J2EE 设计和开发(Wrox 电台,2002,http://mng.bz/oVjy)。 21 | 22 | 当时,开发的最常见的应用程序类型是基于浏览器的 web 应用程序,由关系数据库支持。虽然这种类型的开发仍然是相关的,而且 Spring 已经为这种类型的应用程序做好了很好的准备,但是我们现在还对开发由面向云的微服务组成的应用程序感兴趣,这些服务将数据持久化到各种数据库中。而对响应式编程的新兴趣在于通过非阻塞操作提供更大的可伸缩性和更好的性能。 23 | 24 | 随着软件开发的发展,Spring 框架也发生了变化,以解决现代开发问题,包括微服务和响应式编程。Spring 还通过引入 Spring Boot 来简化自己的开发模型。 25 | 26 | 无论您是开发简单的数据库支持的 web 应用程序,还是构建基于微服务的现代应用程序,Spring 都是帮助您实现目标的框架。本章是您使用 Spring 进行现代应用程序开发的第一步。 27 | -------------------------------------------------------------------------------- /04~工程实践/日志处理/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot 中日志处理 2 | 3 | 日志处理是服务端开发中的常见需求,其能够帮助我们对系统的运行情况进行实时监控,以及及时地排查、解决系统中存在的问题。在开发中,我们经常使用 System.out.println() 来打印一些信息,但是这样不好,因为大量的使用 System.out 会增加资源的消耗。 4 | 5 | Spring Boot 中日志的用法与其他并无差异,直接获取 logger 对象并使用,Spring Boot 预置了 Logback 的配置。 6 | 7 | ```java 8 | @RestController 9 | public class LoggingController { 10 | Logger logger = LoggerFactory.getLogger(LoggingController.class); 11 | 12 | @RequestMapping("/") 13 | public String index() { 14 | logger.trace("A TRACE Message"); 15 | logger.debug("A DEBUG Message"); 16 | logger.info("An INFO Message"); 17 | logger.warn("A WARN Message"); 18 | logger.error("An ERROR Message"); 19 | 20 | return "Howdy! Check out the Logs to see the output..."; 21 | } 22 | } 23 | ``` 24 | 25 | Spring Boot 内置的日志级别是 Info,如果我们需要打印 Debug 或者 Trace 级别的日志,可以添加环境变量: 26 | 27 | ```sh 28 | $ mvn spring-boot:run 29 | -Dspring-boot.run.arguments=--logging.level.org.springframework=TRACE,--logging.level.com.baeldung=TRACE 30 | 31 | $ ./gradlew bootRun -Pargs=--logging.level.org.springframework=TRACE,--logging.level.com.baeldung=TRACE 32 | ``` 33 | 34 | 也可以通过修改 application.properties 文件: 35 | 36 | ```sh 37 | logging.level.root=WARN 38 | logging.level.com.baeldung=TRACE 39 | ``` 40 | 41 | 在实际的工程中,我们需要严格规范日志输出等级,影响业务进行或意料外异常输出 Error 级别,并统一输出到独立文件,接入系统错误监控告警。不过 Error 级别的日志也要不断地进行优化降噪,以保证及时有效地人为介入处理。对于接口层,则需要统一拦截捕获,避免异常向外系统传播,自身系统无法感知问题;服务层中则应该统一日志输出,包括耗时、接口成功标识、业务成功标识,为监控做准备。并且应该统一输出日志的 traceId,以方便进行分布式追踪,可以通过扩展 `ch.qos.logback.classic.pattern.ClassicConverter` 来实现自动输出。 42 | -------------------------------------------------------------------------------- /01~基础使用/04~请求与响应/响应返回/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot 请求响应 2 | 3 | # 重定向与转发 4 | 5 | ```java 6 | @RequestMapping(value = "/redirect", method = RequestMethod.GET) 7 | public void method(HttpServletResponse httpServletResponse) { 8 | httpServletResponse.setHeader("Location", projectUrl); 9 | } 10 | 11 | @RequestMapping(value = "/redirect", method = RequestMethod.GET) 12 | public ModelAndView method() { 13 | return new ModelAndView("redirect:" + projectUrl); 14 | } 15 | ``` 16 | 17 | # Jsonp 18 | 19 | 对于 Jsonp 风格的请求,Spring 在返回时需要加以修改已支持前端的跨域调用,在后端处理方式如下: 20 | 21 | ```java 22 | protected String responseHandler(JSONObject rtn, 23 | HttpServletRequest request) { 24 | if(request.getParameter("callback") != null){ 25 | //将数据填充到callback,并回调 26 | StringBuffer buf = new StringBuffer(); 27 | buf.append(request.getParameter("callback")); 28 | buf.append("("); 29 | buf.append(rtn.toJSONString()); 30 | buf.append(");"); 31 | return buf.toString(); 32 | } 33 | // TODO Auto-generated method stub 34 | return rtn.toJSONString(); 35 | } 36 | ``` 37 | 38 | 前端请求方式: 39 | 40 | ```js 41 | jsonp: function (path, params, callback) { 42 | //构造出URL 43 | var url = 'http://localhost:8080/' + path + '?requestData=' + JSON.stringify(params) + '&callback=JSON_CALLBACK'; 44 | console.log(url); 45 | //发起网络请求 46 | $http({ 47 | method: 'JSONP', 48 | url: url 49 | }).success(function (data, status, headers, config) { 50 | console.log(data); 51 | }).error(errorCallBack); 52 | 53 | } 54 | ``` 55 | 56 | # 文件处理 57 | 58 | # 缓存 59 | -------------------------------------------------------------------------------- /00~特性与生态/生态圈/Spring Messaging.md: -------------------------------------------------------------------------------- 1 | # Spring Messaging 2 | 3 | Spring Messaging 是 Spring Framework 中的一个模块,其作用就是统一消息的编程模型。比如消息 Messaging 对应的模型就包括一个消息体 Payload 和消息头 Header: 4 | 5 | ![消息结构](https://s2.ax1x.com/2019/10/19/KnCV2Q.png) 6 | 7 | ```java 8 | package org.springframework.messaging; 9 | 10 | public interface Message { 11 | T getPayload(); 12 | MessageHeaders getHeaders(); 13 | } 14 | ``` 15 | 16 | 消息通道 MessageChannel 用于接收消息,调用 send 方法可以将消息发送至该消息通道中: 17 | 18 | ![消息传递](https://s2.ax1x.com/2019/10/19/KnCG24.png) 19 | 20 | ```java 21 | @FunctionalInterface 22 | public interface MessageChannel { 23 | long INDEFINITE_TIMEOUT = -1; 24 | default boolean send(Message message) { 25 | return send(message, INDEFINITE_TIMEOUT); 26 | } 27 | boolean send(Message message, long timeout); 28 | } 29 | ``` 30 | 31 | # 消息消费 32 | 33 | 由消息通道的子接口可订阅的消息通道 SubscribableChannel 实现,被 MessageHandler 消息处理器所订阅: 34 | 35 | ```java 36 | public interface SubscribableChannel extends MessageChannel { 37 | boolean subscribe(MessageHandler handler); 38 | boolean unsubscribe(MessageHandler handler); 39 | } 40 | ``` 41 | 42 | 由 MessageHandler 真正地消费/处理消息: 43 | 44 | ```java 45 | @FunctionalInterface 46 | public interface MessageHandler { 47 | void handleMessage(Message message) throws MessagingException; 48 | } 49 | ``` 50 | 51 | Spring Messaging 内部在消息模型的基础上衍生出了其它的一些功能,如: 52 | 53 | - 消息接收参数及返回值处理:消息接收参数处理器 HandlerMethodArgumentResolver 配合 @Header, @Payload 等注解使用;消息接收后的返回值处理器 HandlerMethodReturnValueHandler 配合 @SendTo 注解使用; 54 | - 消息体内容转换器 MessageConverter; 55 | - 统一抽象的消息发送模板 AbstractMessageSendingTemplate; 56 | - 消息通道拦截器 ChannelInterceptor; 57 | -------------------------------------------------------------------------------- /00~特性与生态/框架对比.md: -------------------------------------------------------------------------------- 1 | # Java Web 框架对比 2 | 3 | - Spring MVC: This is pretty much the de facto standard. If I'm interviewing someone and they haven't learned Spring-Core it is a definite mark against them. I don't care if your prefer another IoC like Guice, Spring is just too popular to ignore. 4 | 5 | - Spring Boot: This is a wrapper layer around Spring MVC. It provides sensible defaults so that you can quickly get up and moving on your app. This framework is great in that you can quickly write code, but it is bad in that it will seem like magic until you've dug into it. 6 | 7 | - Grails: If you want to dip into the groovy world, Grails is another excellent choice. It is heavily inspired by Ruby on Rails. It is great for getting an app up and running quickly. Like Rails, it requires discipline in adhering to MVC pattern to create a scalable app. Although you can get up and running quickly, it is easy to write an unmaintainable mess without guidance. 8 | 9 | - Play: Another popular framework built on Akka. Favored by the scala community (although it works with plain java). Play is quite performant due to its non-blocking architecture. One of the killer features is that you can reload your changes without having to compile (although this is now available in Spring MVC/Boot with devtools). Honestly, it would be hard for me to recommend Play to someone that isn't already invested in Scala. Scala is a great language, but it has its own learning curve that will slow you down having to pick up multiple things at once. 10 | 11 | - Dropwizard: A lightweight REST framework.It's been gaining popularity lately.Having used it on several projects, I find some of the documentation lacking, but otherwise haven't had many problems. 12 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 4 章 Spring 安全/4.1 启用 Spring Security.md: -------------------------------------------------------------------------------- 1 | ## 4.1 启用 Spring Security 2 | 3 | 保护 Spring 应用程序的第一步是将 Spring Boot security starter 依赖项添加到构建中。在项目的 pom.xml 文件中,添加以下 内容: 4 | 5 | ```xml 6 | 7 | org.springframework.boot 8 | spring-boot-starter-security 9 | 10 | ``` 11 | 12 | 如果正在使用 Spring Tool Suite,这甚至更简单。右键单击 pom.xml 文件并从 Spring 上下文菜单中选择 编辑 Starters。将出现 “启动依赖项” 对话框。检查核心类别下的安全条目,如图 4.1 所示。 13 | 14 | ![图 4.1 使用 Spring Tool Suite 添加 security starter](E:\Document\spring-in-action-v5-translate\第一部分 Spring 基础\第四章 Spring 安全\图 4.1 使用 Spring Tool Suite 添加 security starter.jpg) 15 | 16 | **图 4.1 使用 Spring Tool Suite 添加 security starter** 17 | 18 | 上面的依赖项是保护应用程序所需的唯一的东西。当应用程序启动时,自动配置将检测类路径中的 Spring Security,并设置一些基本的安全性配置。 19 | 20 | 如果想尝试一下,启动应用程序并访问主页(或任何页面)。将提示使用 HTTP 基本身份验证对话框进行身份验证。要想通过认证,需要提供用户名和密码。用户名是 _user_。至于密码,它是随机生成并写入了应用程序日志文件。日志条目应该是这样的: 21 | 22 | ``` 23 | Using default security password: 087cfc6a-027d-44bc-95d7-cbb3a798a1ea 24 | ``` 25 | 26 | 假设正确地输入了用户名和密码,将被授予对应用程序的访问权。 27 | 28 | 保护 Spring 应用程序似乎非常简单。Taco Cloud 应用程序的已经被保护了,我想我现在可以结束这一章,进入下一个主题了。但是在我们开始之前,让我们考虑一下自动配置提供了什么样的安全性。 29 | 30 | 只需要在项目构建中添加 security starter,就可以获得以下安全特性: 31 | 32 | - 所有的 HTTP 请求路径都需要认证。 33 | - 不需要特定的角色或权限。 34 | - 没有登录页面。 35 | - 身份验证由 HTTP 基本身份验证提供。 36 | - 只有一个用户;用户名是 _user_。 37 | 38 | 这是一个良好的开端,但我认为大多数应用程序(包括 Taco Cloud)的安全需求将与这些基本的安全特性有很大的不同。 39 | 40 | 如果要正确地保护 Taco Cloud 应用程序,还有更多的工作要做。至少需要配置 Spring Security 来完成以下工作: 41 | 42 | - 提示使用登录页面进行身份验证,而不是使用 HTTP 基本对话框。 43 | - 为多个用户提供注册页面,让新的 Taco Cloud 用户可以注册。 44 | - 为不同的请求路径应用不同的安全规则。例如,主页和注册页面根本不需要身份验证。 45 | 46 | 为了满足对 Taco Cloud 的安全需求,必须编写一些显式的配置,覆盖自动配置提供的内容。首先需要配置一个合适的用户存储,这样就可以有多个用户。 47 | -------------------------------------------------------------------------------- /00~特性与生态/生态圈/Spring Cloud.md: -------------------------------------------------------------------------------- 1 | # Spring Cloud 2 | 3 | 我们将 Spring Cloud 与 K8s 中的概念进行协调对比: 4 | 5 | ![Spring Cloud 与 K8s 对比](https://ngte-superbed.oss-cn-beijing.aliyuncs.com/item/20230417205616.png) 6 | 7 | Spring Cloud 为我们提供了分布式/版本化配置、服务注册和发现、路由、服务和服务之间的调用、负载均衡、断路器、分布式消息传递等特性。其核心子项目包含了如下模块: 8 | 9 | - Spring Cloud Config: 配置中心,利用 Git 来集中管理程序的配置。 10 | 11 | - Spring Cloud Netflix: 集成众多 Netflix 的开源软件,包括 Eureka、Hystrix、Zuul、Archaius 等。 12 | 13 | - Spring Cloud Bus: Bus 即消息总线,消息总线利用分布式消息将服务和服务实例连接在一起,用于在一个集群中传播状态的变化,比如配置更改事件,其可以与 Spring Cloud Config 联合来实现热部署。 14 | 15 | - Spring Cloud Cluster: 基于 Zookeeper、Redis、Hazelcast、Consul 实现的领导选举和平民状态模式的抽象和实现。 16 | 17 | - Spring Cloud Consul: 基于 Hashicorp Consul 实现的服务发现和配置管理。 18 | 19 | - Spring Cloud Security: 在 Zuul 代理中 OAuth2 REST 客户端和认证头转发提供负载均衡。 20 | 21 | - Spring Cloud Sleuth: 适用于 Spring Cloud 应用程序的分布式跟踪,与 Zipkin HTrace 和基于日志(例如 ELK)的跟踪相兼容,可以实现日志的收集。 22 | 23 | - Spring Cloud Data Flow: 一种针对现代运行时可组合的微服务应用程序的云本地编排服务,易于使用的 DSL、拖拽式 GUI 和 REST API 一起简化了基于微服务的数据管道的整体编排。 24 | 25 | - Spring Cloud Stream: 一个轻量级的事件驱动的微服务框架,来快速构建可以连接到外部系统的应用程序。使用 Apache Kafka 或者 RabbitMQ 在 Spring Boot 应用程序之间发生和接受消息的简单声明模型。 26 | 27 | - Spring Cloud Stream App Starters: 基于 Spring Boot 为外部系统提供 Spring 的集成。 28 | 29 | - Spring Cloud Task App Starters: Spring Cloud Task App Starters 是 Spring Boot 应用程序,可以是任意的进程,包括 Spring Batch 作业,并可以在数据处理有限时间内终止。 30 | 31 | - Spring Cloud Connectors: 便于 PaaS 应用在各平台上连接到后端数据库或者消息服务。 32 | 33 | - Spring Cloud Starters: 基于 Spring Boot 的项目,用以简化 Spring Cloud 的依赖管理。 34 | 35 | - Spring Cloud CLI: Spring Boot CLI 插件用于在 Groovy 中快速创建 Spring Cloud 组件应用程序。 36 | 37 | - Spring Cloud Contract: Spring Cloud Contract 是一个总体项目,其中包含帮助用户成功实施消费者驱动契约(Consumer Driven Contracts)的解决方案。 38 | 39 | - Spring Cloud for Cloud Foundry: Cloud Foundry 是 VMware 推出的开源 PaaS 云平台,利用 Pivotal Cloud Foundry 集成你的应用程序。 40 | 41 | - Spring Cloud Cloud Foundry Service Broker: 为建立管理云托管服务的服务代理提供一个起点。 42 | -------------------------------------------------------------------------------- /09~源码解析/依赖机制/启动流程.md: -------------------------------------------------------------------------------- 1 | # Spring IoC 启动流程与循环依赖处理 2 | 3 | ![image](https://pic.imgdb.cn/item/615180a82ab3f51d916cd901.jpg) 4 | 5 | Spring IoC 容器的整个工作流程大致可以分为两个阶段:容器启动阶段与 Bean 的实例化阶段。 6 | 7 | - 容器启动时,会通过某种途径加载 ConfigurationMetaData。除了代码方式比较直接外,在大部分情况下,容器需要依赖某些工具类,比如:BeanDefinitionReader,BeanDefinitionReader 会对加载的 ConfigurationMetaData 进行解析和分析,并将分析后的信息组装为相应的 BeanDefinition,最后把这些保存了 bean 定义的 BeanDefinition,注册到相应的 BeanDefinitionRegistry,这样容器的启动工作就完成了。这个阶段主要完成一些准备性工作,更侧重于 bean 对象管理信息的收集,当然一些验证性或者辅助性的工作也在这一阶段完成。 8 | 9 | - 经过第一阶段,所有 bean 定义都通过 BeanDefinition 的方式注册到 BeanDefinitionRegistry 中,当某个请求通过容器的 getBean 方法请求某个对象,或者因为依赖关系容器需要隐式的调用 getBean 时,就会触发第二阶段的活动:容器会首先检查所请求的对象之前是否已经实例化完成。如果没有,则会根据注册的 BeanDefinition 所提供的信息实例化被请求对象,并为其注入依赖。当该对象装配完毕后,容器会立即将其返回给请求方法使用。 10 | 11 | BeanFactory 只是 Spring IoC 容器的一种实现,如果没有特殊指定,它采用采用延迟初始化策略:只有当访问容器中的某个对象时,才对该对象进行初始化和依赖注入操作。而在实际场景下,我们更多的使用另外一种类型的容器:ApplicationContext,它构建在 BeanFactory 之上,属于更高级的容器,除了具有 BeanFactory 的所有能力之外,还提供对事件监听机制以及国际化的支持等。它管理的 bean,在容器启动时全部完成初始化和依赖注入操作。 12 | 13 | # Spring Boot 14 | 15 | ```java 16 | private void initialize(Object[] sources) { 17 | if (sources != null && sources.length > 0) { 18 | this.sources.addAll(Arrays.asList(sources)); 19 | } 20 | 21 | // 判断是否是 Web 项目 22 | this.webEnvironment = deduceWebEnvironment(); 23 | 24 | setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); 25 | 26 | setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 27 | 28 | // 找到入口类 29 | this.mainApplicationClass = deduceMainApplicationClass(); 30 | 31 | } 32 | ``` 33 | 34 | 初始化流程中最重要的就是通过 SpringFactoriesLoader 找到 spring.factories 文件中配置的 ApplicationContextInitializer 和 ApplicationListener 两个接口的实现类名称,以便后期构造相应的实例。ApplicationContextInitializer 的主要目的是在 ConfigurableApplicationContext 做 refresh 之前,对 ConfigurableApplicationContext 实例做进一步的设置或处理。ConfigurableApplicationContext 继承自 ApplicationContext,其主要提供了对 ApplicationContext 进行设置的能力。 35 | -------------------------------------------------------------------------------- /09~源码解析/深入请求处理/Servlet/请求处理.md: -------------------------------------------------------------------------------- 1 | # Servlet 异步请求 2 | 3 | ```java 4 | @RequestMapping(value = "/email/servletReq", method = GET) 5 | public void servletReq (HttpServletRequest request, HttpServletResponse response) { 6 | AsyncContext asyncContext = request.startAsync(); 7 | //设置监听器:可设置其开始、完成、异常、超时等事件的回调处理 8 | asyncContext.addListener(new AsyncListener() { 9 | @Override 10 | public void onTimeout(AsyncEvent event) throws IOException { 11 | System.out.println("超时了..."); 12 | //做一些超时后的相关操作... 13 | } 14 | @Override 15 | public void onStartAsync(AsyncEvent event) throws IOException { 16 | System.out.println("线程开始"); 17 | } 18 | @Override 19 | public void onError(AsyncEvent event) throws IOException { 20 | System.out.println("发生错误:"+event.getThrowable()); 21 | } 22 | @Override 23 | public void onComplete(AsyncEvent event) throws IOException { 24 | System.out.println("执行完成"); 25 | //这里可以做一些清理资源的操作... 26 | } 27 | }); 28 | //设置超时时间 29 | asyncContext.setTimeout(20000); 30 | asyncContext.start(new Runnable() { 31 | @Override 32 | public void run() { 33 | try { 34 | Thread.sleep(10000); 35 | System.out.println("内部线程:" + Thread.currentThread().getName()); 36 | asyncContext.getResponse().setCharacterEncoding("utf-8"); 37 | asyncContext.getResponse().setContentType("text/html;charset=UTF-8"); 38 | asyncContext.getResponse().getWriter().println("这是异步的请求返回"); 39 | } catch (Exception e) { 40 | System.out.println("异常:"+e); 41 | } 42 | //异步请求完成通知 43 | //此时整个请求才完成 44 | asyncContext.complete(); 45 | } 46 | }); 47 | //此时之类 request的线程连接已经释放了 48 | System.out.println("主线程:" + Thread.currentThread().getName()); 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /02~数据库/04~缓存与 KV 存储/Redis/Redisson/对象.md: -------------------------------------------------------------------------------- 1 | # Objects 2 | 3 | Redisson 对象的单个实例被序列化并存储在任何支持 Redisson 的可用 Redis 节点中。这些对象可以分布在群集中的多个节点上,并且可以由单个应用程序或多个应用程序/服务器访问。这些分布式对象遵循 java.util.concurrent.atomic 软件包中的规范。它们支持对 Redis 中存储的对象进行无锁,线程安全和原子操作。由于在另一个应用程序读取对象时不会更新值,因此可以确保应用程序/服务器之间的数据一致性。Redisson 对象绑定到 Redis keys,我们可以通过 RKeys 接口管理这些密钥,并使用这些键访问我们的 Redisson 对象。 4 | 5 | ```java 6 | RKeys keys = client.getKeys(); 7 | 8 | Iterable allKeys = keys.getKeys(); 9 | 10 | Iterable keysByPattern = keys.getKeysByPattern('key*'); 11 | ``` 12 | 13 | RKeys 接口还允许删除键,按模式删除键以及其他可用于管理键和对象的基于键的有用操作。Redisson 提供的分布式对象包括: 14 | 15 | - ObjectHolder 16 | - BinaryStreamHolder 17 | - GeospatialHolder 18 | - BitSet 19 | - AtomicLong 20 | - AtomicDouble 21 | - Topic 22 | - BloomFilter 23 | - HyperLogLog 24 | 25 | # Object Holder 26 | 27 | 由 RBucket 类表示,此对象可以保存任何类型的对象。该对象的最大大小为 512MB: 28 | 29 | ```java 30 | RBucket bucket = client.getBucket("ledger"); 31 | bucket.set(new Ledger()); 32 | Ledger ledger = bucket.get(); 33 | ``` 34 | 35 | RBucket 对象可以对其持有的对象执行原子操作,例如 compareAndSet 和 getAndSet。 36 | 37 | # AtomicLong 38 | 39 | 该对象由 RAtomicLong 类表示,非常类似于 java.util.concurrent.atomic.AtomicLong 类,并表示可以通过原子方式更新的长值: 40 | 41 | ```java 42 | RAtomicLong atomicLong = client.getAtomicLong("myAtomicLong"); 43 | atomicLong.set(5); 44 | atomicLong.incrementAndGet(); 45 | ``` 46 | 47 | # Topic 48 | 49 | Topic 对象支持 Redis 的“发布和订阅”机制。收听已发布的消息: 50 | 51 | ```java 52 | RTopic subscribeTopic = client.getTopic("wx"); 53 | 54 | subscribeTopic.addListener( 55 | (channel, customMessage) 56 | -> future.complete(customMessage.getMessage())); 57 | ``` 58 | 59 | 这里注册了 Topic 以收听来自 wx 频道的消息。然后,我们向该 Topic 添加一个侦听器,以处理来自该通道的传入消息。我们可以将多个侦听器添加到一个频道。让我们将消息发布到 wx 通道: 60 | 61 | ```java 62 | RTopic publishTopic = client.getTopic("baeldung"); 63 | long clientsReceivedMessage 64 | = publishTopic.publish(new CustomMessage("This is a message")); 65 | ``` 66 | 67 | 这可以从另一个应用程序或服务器发布。CustomMessage 对象将由侦听器接收,并按 onMessage 方法中的定义进行处理。 68 | -------------------------------------------------------------------------------- /03~Spring Security/权限验证/表单登录.md: -------------------------------------------------------------------------------- 1 | # Spring Security 表单登录 2 | 3 | # Form Login | 用户名密码表单登录 4 | 5 | 本部分完整代码参考 [spring-security-form-login](https://github.com/wx-chevalier/Backend-Boilerplate/tree/master/java/spring/spring-security-login), 首先在 WebSecurityConfig 的 configure 方法中,注册路由表: 6 | 7 | ```java 8 | @Override 9 | protected void configure(HttpSecurity http) throws Exception { 10 | http 11 | .authorizeRequests() 12 | .antMatchers("/", "/home").permitAll() 13 | .anyRequest().authenticated() 14 | .and() 15 | .formLogin() 16 | .loginPage("/login") 17 | .permitAll() 18 | .and() 19 | .logout() 20 | .permitAll(); 21 | } 22 | ``` 23 | 24 | 然后需要声明 UserDetailsService,以供 Spring Context 来自动获取用户实例,该方法会在 `authenticationManager.authenticate()` 调用时被调用: 25 | 26 | ```java 27 | @Bean 28 | @Override 29 | public UserDetailsService userDetailsService() { 30 | UserDetails user = 31 | User.withDefaultPasswordEncoder() 32 | .username("user") 33 | .password("password") 34 | .roles("USER") 35 | .build(); 36 | 37 | return new InMemoryUserDetailsManager(user); 38 | } 39 | ``` 40 | 41 | 此时 Spring Security 为我们自动生成了 `/login` 与 `/logout` 两个 POST 接口,分别用来处理用户登录与登出,其对应的前台 Form 表单如下所示: 42 | 43 | ```html 44 |
45 |
46 | 47 |
48 |
49 | 50 |
51 |
52 |
53 | ``` 54 | 55 | 在很多情况下,我们位于第三方存储中的密码是经过 Hash 混淆处理的,而不是直接读取的明文信息;此时我们可以为 Spring Security 提供自定义的密码编码器,来方便其执行比较操作: 56 | 57 | ```java 58 | @Override 59 | public void configure(AuthenticationManagerBuilder auth) throws Exception { 60 | auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder); 61 | } 62 | ``` 63 | 64 | # Links 65 | 66 | - https://www.baeldung.com/spring-security-login 67 | -------------------------------------------------------------------------------- /01~基础使用/03~依赖注入/应用上下文/自动配置.md: -------------------------------------------------------------------------------- 1 | # Spring 自动配置 2 | 3 | Spring 为我们提供了 @EnableAutoConfiguration 注解,当使用了该注解,Spring 会自动地扫描本地以及依赖中的 @Configuration 等注解的类来生成 Bean。 4 | 5 | # @Configuration 6 | 7 | 在早期的 Spring Boot 项目中,我们需要手动地指定 @Configuration: 8 | 9 | ```java 10 | @Configuration 11 | @ConfigurationProperties(prefix = "mail") 12 | public class ConfigProperties { 13 | private String hostName; 14 | private int port; 15 | private String from; 16 | // standard getters and setters 17 | } 18 | ``` 19 | 20 | 如果我们不使用 @Configuration,则需要 `@EnableConfigurationProperties(ConfigProperties.class)` 在 Spring 应用类中进行绑定: 21 | 22 | ```java 23 | @SpringBootApplication 24 | @EnableConfigurationProperties(ConfigProperties.class) 25 | public class DemoApplication { 26 | 27 | public static void main(String[] args) { 28 | SpringApplication.run(DemoApplication.class, args); 29 | } 30 | } 31 | ``` 32 | 33 | Spring 将自动绑定属性文件中定义的任何属性,该属性文件中带有前缀 mail 且名称与 ConfigProperties 类中的字段之一相同。或者使用 AutoConfiguration,这里配置的 EnableAutoConfiguration 会自动搜索使用 `@Configuration` 进行注解的类,同时 @ImportResource 是自动导入关联的 XML 文件。 34 | 35 | ```java 36 | @EnableAutoConfiguration 37 | @ImportResource("classpath:spring/applicationContext.xml") 38 | public class Application { 39 | 40 | public static void main(String[] args) throws Exception { 41 | SpringApplication.run(Application.class, args); 42 | } 43 | } 44 | ``` 45 | 46 | 而在 Spring Boot 2.2 版本之后,Spring 通过类路径扫描查找并注册@ConfigurationProperties 类。因此,无需使用 @Component(和其他元注释,如@Configuration)注释此类,甚至无需使用 @EnableConfigurationProperties: 47 | 48 | ```java 49 | @ConfigurationProperties(prefix = "mail") 50 | public class ConfigProperties { 51 | private String hostName; 52 | private int port; 53 | private String from; 54 | // standard getters and setters 55 | } 56 | ``` 57 | 58 | 当然,我们还可以使用 @ConfigurationPropertiesScan 来指定自定义地扫描路径: 59 | 60 | ```java 61 | @SpringBootApplication 62 | @ConfigurationPropertiesScan("wx") 63 | public class DemoApplication { 64 | 65 | public static void main(String[] args) { 66 | SpringApplication.run(DemoApplication.class, args); 67 | } 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /02~数据库/02~MyBatis/Mybatis Plus/2023~Mybatis Plus 官方案例/06~多租户与优化.md: -------------------------------------------------------------------------------- 1 | # 多租户 2 | 3 | ```java 4 | public interface TenantLineHandler { 5 | 6 | /** 7 | * 获取租户 ID 值表达式,只支持单个 ID 值 8 | *

9 | * 10 | * @return 租户 ID 值表达式 11 | */ 12 | Expression getTenantId(); 13 | 14 | /** 15 | * 获取租户字段名 16 | *

17 | * 默认字段名叫: tenant_id 18 | * 19 | * @return 租户字段名 20 | */ 21 | default String getTenantIdColumn() { 22 | return "tenant_id"; 23 | } 24 | 25 | /** 26 | * 根据表名判断是否忽略拼接多租户条件 27 | *

28 | * 默认都要进行解析并拼接多租户条件 29 | * 30 | * @param tableName 表名 31 | * @return 是否忽略, true:表示忽略,false:需要解析并拼接多租户条件 32 | */ 33 | default boolean ignoreTable(String tableName) { 34 | return false; 35 | } 36 | } 37 | ``` 38 | 39 | - Mybatis Plus 配置 40 | 41 | ```java 42 | @Configuration 43 | @MapperScan("com.baomidou.mybatisplus.samples.tenant.mapper") 44 | public class MybatisPlusConfig { 45 | 46 | /** 47 | * 新多租户插件配置,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存万一出现问题 48 | */ 49 | @Bean 50 | public MybatisPlusInterceptor mybatisPlusInterceptor() { 51 | MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 52 | interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() { 53 | @Override 54 | public Expression getTenantId() { 55 | return new LongValue(1); 56 | } 57 | 58 | // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件 59 | @Override 60 | public boolean ignoreTable(String tableName) { 61 | return !"user".equalsIgnoreCase(tableName); 62 | } 63 | })); 64 | // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor 65 | // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false 66 | // interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 67 | return interceptor; 68 | } 69 | 70 | @Bean 71 | public ConfigurationCustomizer configurationCustomizer() { 72 | return configuration -> configuration.setUseDeprecatedExecutor(false); 73 | } 74 | } 75 | ``` 76 | -------------------------------------------------------------------------------- /04~工程实践/测试/JUnit 4/测试用例.md: -------------------------------------------------------------------------------- 1 | # JUnit 4 2 | 3 | - @Test(expected = Exception.class) 表示预期会抛出 Exception.class 的异常 4 | - @Ignore 含义是“某些方法尚未完成,暂不参与此次测试”。这样的话测试结果就会提示你有几个测试被忽略,而不是失败。一旦你完成了相应函数,只需要把@Ignore 注解删去,就可以进行正常的测试。 5 | - @Test(timeout=100) 表示预期方法执行不会超过 100 毫秒,控制死循环 6 | - @Before 表示该方法在每一个测试方法之前运行,可以使用该方法进行初始化之类的操作 7 | - @After 表示该方法在每一个测试方法之后运行,可以使用该方法进行释放资源,回收内存之类的操 8 | - @BeforeClass 表示该方法只执行一次,并且在所有方法之前执行。一般可以使用该方法进行数据库连接操作,注意该注解运用在静态方法。 9 | - @AfterClass 表示该方法只执行一次,并且在所有方法之后执行。一般可以使用该方法进行数据库连接关闭操作,注意该注解运用在静态方法。 10 | 11 | # TestSuite 12 | 13 | 如果你须有多个测试单元,可以合并成一个测试套件进行测试,况且在一个项目中,只写一个测试类是不可能的,我们会写出很多很多个测试类。可是这些测试类必须一个一个的执行,也是比较麻烦的事情。鉴于此,JUnit 为我们提供了打包测试的功能,将所有需要运行的测试类集中起来,一次性的运行完毕,大大的方便了我们的测试工作。并且可以按照指定的顺序执行所有的测试类。下面的代码示例创建了一个测试套件来执行两个测试单元。如果你要添加其他的测试单元可以使用语句 @Suite.SuiteClasses 进行注解。 14 | 15 | ```java 16 | @RunWith(Suite.class) 17 | @SuiteClasses({ JUnit1Test.class, StringUtilTest.class }) 18 | public class JSuit {} 19 | ``` 20 | 21 | TestSuite 测试包类——多个测试的组合 TestSuite 类负责组装多个 Test Cases。待测得类中可能包括了对被测类的多个测试,而 TestSuit 负责收集这些测试,使我们可以在一个测试中,完成全部的对被测类的多个测试。TestSuite 类实现了 Test 接口,且可以包含其它的 TestSuites。它可以处理加入 Test 时的所有抛出的异常。 22 | 23 | TestResult 结果类集合了任意测试累加结果,通过 TestResult 实例传递个每个测试的 Run() 方法。TestResult 在执行 TestCase 是如果失败会异常抛出 TestListener 接口是个事件监听规约,可供 TestRunner 类使用。它通知 listener 的对象相关事件,方法包括测试开始 startTest(Test test),测试结束 endTest(Test test),错误,增加异常 addError(Test test, Throwable t) 和增加失败 addFailure(Test test, AssertionFailedError t)。TestFailure 失败类是个“失败”状况的收集类,解释每次测试执行过程中出现的异常情况,其 toString() 方法返回“失败”状况的简要描述。 24 | 25 | # Assert 26 | 27 | JUnit 中的 assert 方法全部放在 Assert 类中,总结一下 JUnit 类中 assert 方法的分类: 28 | 29 | - `assertTrue/False([String message,]boolean condition)`: 判断一个条件是 true 还是 false。感觉这个最好用了,不用记下来那么多的方法名。 30 | 31 | - `fail([String message,])`: 失败,可以有消息,也可以没有消息。 32 | 33 | - `assertEquals([String message,]Object expected,Object actual)`: 判断是否相等,可以指定输出错误信息。第一个参数是期望值,第二个参数是实际的值。这个方法对各个变量有多种实现。在 JDK1.5 中基本一样。但是需要主意的是 float 和 double 最后面多一个 delta 的值。 34 | 35 | - `assertNotNull/Null([String message,]Object obj)`: 判读一个对象是否非空(非空)。 36 | 37 | - `assertSame/NotSame([String message,]Object expected,Object actual)`: 通过内存地址,判断两个对象是否指向同一个对象。 38 | 39 | - `failNotSame/failNotEquals(String message, Object expected, Object actual)`: 当不指向同一个内存地址或者不相等的时候,输出错误信息。注意信息是必须的,而且这个输出是格式化过的。 40 | -------------------------------------------------------------------------------- /03~Spring Security/OAuth/Keycloak/README.md: -------------------------------------------------------------------------------- 1 | # 集成 Spring Security 2 | 3 | 之前,Spring Security OAuth 协议栈提供了将授权服务器设置为 Spring 应用的可能性。然后,我们必须将其配置为使用 JwtTokenStore,这样我们就可以使用 JWT 令牌。然而,OAuth 协议栈已经被 Spring 废弃,现在我们将使用 Keycloak 作为我们的授权服务器。所以这次,我们将把我们的授权服务器设置为 Spring Boot 应用中的嵌入式 Keycloak 服务器。它默认会发出 JWT 令牌,所以在这方面不需要任何其他配置。 4 | 5 | # Resource Server 6 | 7 | 首先在 application.yml 中进行如下定义: 8 | 9 | ```yaml 10 | server: 11 | port: 8081 12 | servlet: 13 | context-path: /resource-server 14 | 15 | spring: 16 | security: 17 | oauth2: 18 | resourceserver: 19 | jwt: 20 | issuer-uri: http://localhost:8083/auth/realms/baeldung 21 | jwk-set-uri: http://localhost:8083/auth/realms/baeldung/protocol/openid-connect/certs 22 | ``` 23 | 24 | JWTs 包括 Token 内的所有信息。因此,资源服务器需要验证 Token 的签名,以确保数据没有被修改。jwk-set-uri 属性包含了服务器可用于此目的的公钥。issuer-uri 属性指向基础授权服务器的 URI,它也可以用来验证 iss 声明,作为一种额外的安全措施。 25 | 26 | 此外,如果没有设置 jwk-set-uri 属性,资源服务器将尝试使用 issuer-ui 从授权服务器元数据端点确定该密钥的位置。重要的是,添加 issuer-uri 属性强制要求我们在启动 Resource Server 应用程序之前,应该先让 Authorization Server 运行。现在让我们看看如何使用 Java 配置来配置 JWT 支持。 27 | 28 | ```java 29 | @Configuration 30 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 31 | 32 | @Override 33 | protected void configure(HttpSecurity http) throws Exception { 34 | http.cors() 35 | .and() 36 | .authorizeRequests() 37 | .antMatchers(HttpMethod.GET, "/user/info", "/api/foos/**") 38 | .hasAuthority("SCOPE_read") 39 | .antMatchers(HttpMethod.POST, "/api/foos") 40 | .hasAuthority("SCOPE_write") 41 | .anyRequest() 42 | .authenticated() 43 | .and() 44 | .oauth2ResourceServer() 45 | .jwt(); 46 | } 47 | } 48 | ``` 49 | 50 | 在这里,我们覆盖了默认的 Http 安全配置。因此,我们需要明确地指定,我们希望这是个资源服务器,并且我们将分别使用 oauth2ResourceServer()和 jwt()方法来使用 JWT 格式的访问令牌。上面的 JWT 配置是默认的 Spring Boot 实例为我们提供的。这也可以被定制,我们很快就会看到。 51 | 52 | # Custom Claims in the Token 53 | 54 | 现在让我们设置一些基础设施,以便能够在授权服务器返回的访问令牌中添加一些自定义声明。框架提供的标准声明都是很好的,但大多数时候我们需要在令牌中添加一些额外的信息,以便在客户端使用。 55 | 56 | 让我们举一个自定义声明的例子,组织,它将包含一个给定用户的组织名称。 57 | 58 | ## Authorization Server Configuration 59 | 60 | 为此,我们需要在域定义文件 baeldung-realm.json 中添加一些配置: 61 | 62 | # Links 63 | 64 | - https://www.baeldung.com/spring-security-oauth-jwt 65 | -------------------------------------------------------------------------------- /01~基础使用/03~依赖注入/Bean/Bean 注入.md: -------------------------------------------------------------------------------- 1 | # Bean 注入 2 | 3 | # Autowired 4 | 5 | ## 多线程注入 6 | 7 | 在多线程下如果使用 Autowired 来进行依赖注入,可能会出现 Null 异常,譬如如下代码: 8 | 9 | ```java 10 | public class UserThreadTask implements Runnable { 11 | @Autowired 12 | private UserThreadService userThreadService; 13 | 14 | @Override 15 | public void run() { 16 | AdeUser user = userThreadService.get("0"); 17 | System.out.println(user); 18 | } 19 | } 20 | ``` 21 | 22 | 造成这种注入失败的原因就是 spring 和多线程的安全问题,不支持这样的注入方式。我们可以通过构造函数传入到多线程环境中: 23 | 24 | ```java 25 | public class UserThreadTask implements Runnable { 26 | private UserThreadService userThreadService; 27 | 28 | public UserThreadTask(UserThreadService userThreadService) { 29 | this.userThreadService = userThreadService; 30 | } 31 | 32 | @Override 33 | public void run() { 34 | AdeUser user = userThreadService.get("0"); 35 | System.out.println(user); 36 | } 37 | } 38 | ``` 39 | 40 | 调用方式如下: 41 | 42 | ```java 43 | Thread t = new Thread(new UserThreadTask(userThreadService)); 44 | t.start(); 45 | ``` 46 | 47 | 我们也可以通过 ApplicaContext 获取所需的 Service: 48 | 49 | ```java 50 | import org.springframework.beans.BeansException; 51 | import org.springframework.context.ApplicationContext; 52 | import org.springframework.context.ApplicationContextAware; 53 | 54 | public class ApplicationContextHolder implements ApplicationContextAware { 55 | private static ApplicationContext context; 56 | 57 | @Override 58 | public void setApplicationContext(ApplicationContext context) 59 | throws BeansException { 60 | ApplicationContextHolder.context = context; 61 | } 62 | 63 | // 根据 Bean name 获取实例 64 | public static Object getBeanByName(String beanName) { 65 | if (beanName == null || context == null) { 66 | return null; 67 | } 68 | return context.getBean(beanName); 69 | } 70 | 71 | // 只适合一个 class 只被定义一次的 bean(也就是说,根据 class 不能匹配出多个该 class 的实例) 72 | public static Object getBeanByType(Class clazz) { 73 | if (clazz == null || context == null) { 74 | return null; 75 | } 76 | return context.getBean(clazz); 77 | } 78 | 79 | public static String[] getBeanDefinitionNames() { 80 | return context.getBeanDefinitionNames(); 81 | } 82 | } 83 | ``` 84 | 85 | 调用方式如下: 86 | 87 | ```java 88 | UserService user = (UserService) ApplicationContextHolder.getBeanByName("userService"); 89 | ``` 90 | 91 | 这种方式不管是否为多线程,还是不接收 Spring 管理的类,都可以用这种方式获得 spring 管理的类。 92 | -------------------------------------------------------------------------------- /03~Spring Security/策略配置/CSRF.md: -------------------------------------------------------------------------------- 1 | # CSRF 2 | 3 | 基于 Token 做 CSRF 防范的话,最基础的需要解决 Token 的生成、存储、查询等问题。针对这些问题,Spring Security 定义了 CSRF Token 仓库接口 org.springframework.security.web.csrf.CsrfTokenRepository.java。目前该接口有 3 个实现类:CookieCsrfTokenRepository、HttpSessionCsrfTokenRepository 以及 LazyCsrfTokenRepository。 4 | 5 | CookieCsrfTokenRepository 将 Token 信息存放于客户端的 Cookie 中;每次生成 Token 后将其保存到 Cookie 中,该 Token 随 Cookie 一起返回给客户端。客户端请求被 CSRF 保护的 URL 时,需要携带 Cookie,便于服务端 loadToken 使用。使用该类型 Token 仓库时,通常需要允许 Javascript 脚本从 Cookied 中获取 Token,因此需要将 Cookie 的 httpOnly 属性值设置为 false。如果 Javascript 不需要从 Cookie 中获取 Token(例如将 Token 存放于 Session 中),建议将 httponly 设置为 true,以提高安全性。默认存放在 Cookie 中的 Token 名为 XSRF-TOKEN。 6 | 7 | 服务端在收到请求以后,会从请求中提取 Token,并与仓库中的 Token 校验,以判断该请求是否合法。具体看,Spring Security 通过 Filter 的方式提供了 CSRF 校验逻辑 org.springframework.security.web.csrf.CsrfFilter。 8 | 9 | ```java 10 | @Override 11 | protected void doFilterInternal(HttpServletRequest request, 12 | HttpServletResponse response, FilterChain filterChain) 13 | throws ServletException, IOException { 14 | request.setAttribute(HttpServletResponse.class.getName(), response); 15 | 16 | // 从Token仓库中加载token。如果不存在则生成并保存之 17 | CsrfToken csrfToken = this.tokenRepository.loadToken(request); 18 | final boolean missingToken = csrfToken == null; 19 | if (missingToken) { 20 | csrfToken = this.tokenRepository.generateToken(request); 21 | this.tokenRepository.saveToken(csrfToken, request, response); 22 | } 23 | request.setAttribute(CsrfToken.class.getName(), csrfToken); 24 | request.setAttribute(csrfToken.getParameterName(), csrfToken); 25 | 26 | // 判断是否为需要保护的路径 27 | if (!this.requireCsrfProtectionMatcher.matches(request)) { 28 | filterChain.doFilter(request, response); 29 | return; 30 | } 31 | 32 | // 从请求头或参数中获取携带的token 33 | String actualToken = request.getHeader(csrfToken.getHeaderName()); 34 | if (actualToken == null) { 35 | actualToken = request.getParameter(csrfToken.getParameterName()); 36 | } 37 | 38 | // 比较请求中携带的token和仓库中的token是否一致,若不一致则请求非法 39 | if (!csrfToken.getToken().equals(actualToken)) { 40 | ... 41 | } 42 | 43 | filterChain.doFilter(request, response); 44 | } 45 | ``` 46 | 47 | # Links 48 | 49 | - https://www.baeldung.com/spring-security-csrf 50 | - https://www.codesandnotes.be/2015/02/05/spring-securitys-csrf-protection-for-rest-services-the-client-side-and-the-server-side/ 51 | -------------------------------------------------------------------------------- /03~Spring Security/权限验证/基于 JWT 的验证/99~参考资料/2023~实现基于 JWT 的登录认证/02~注册与登录.md: -------------------------------------------------------------------------------- 1 | # 注册与登录 2 | 3 | 这里我们开始讨论 UserDetailsService 的具体实现,首先我们定义用到的用户模型: 4 | 5 | ```java 6 | @Entity 7 | @Data 8 | public class User { 9 | 10 | @Id 11 | @GeneratedValue(strategy= GenerationType.AUTO) 12 | private long id; 13 | @Column 14 | private String username; 15 | @Column 16 | @JsonIgnore 17 | private String password; 18 | @Column 19 | private long salary; 20 | @Column 21 | private int age; 22 | 23 | @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) 24 | @JoinTable(name = "USER_ROLES", joinColumns = { 25 | @JoinColumn(name = "USER_ID") }, inverseJoinColumns = { 26 | @JoinColumn(name = "ROLE_ID") }) 27 | private Set roles; 28 | } 29 | 30 | @Data 31 | @Entity 32 | public class Role { 33 | 34 | @Id 35 | @GeneratedValue(strategy = GenerationType.AUTO) 36 | private long id; 37 | 38 | @Column private String name; 39 | 40 | @Column private String description; 41 | } 42 | ``` 43 | 44 | 然后我们定义 UserServiceImpl,其继承了 UserDetailsService,提供了 loadUserByUsername 方法: 45 | 46 | ```java 47 | 48 | @Service(value = "userService") 49 | public class UserServiceImpl implements UserDetailsService, UserService { 50 | 51 | @Autowired private UserDAO userDao; 52 | 53 | @Autowired private BCryptPasswordEncoder bcryptEncoder; 54 | 55 | @Override 56 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 57 | User user = userDao.findByUsername(username); 58 | if (user == null) { 59 | throw new UsernameNotFoundException("Invalid username or password."); 60 | } 61 | return new org.springframework.security.core.userdetails.User( 62 | user.getUsername(), user.getPassword(), getAuthority(user)); 63 | } 64 | 65 | private Set getAuthority(User user) { 66 | Set authorities = new HashSet<>(); 67 | user.getRoles() 68 | .forEach( 69 | role -> { 70 | // authorities.add(new SimpleGrantedAuthority(role.getName())); 71 | authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName())); 72 | }); 73 | return authorities; 74 | // return Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN")); 75 | } 76 | 77 | // ... 78 | } 79 | ``` 80 | 81 | 这里 loadUserByUsername 检索到的用户会被填充到 User 对象中,并被添加到 SecurityContext 上下文中。 82 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 2 章 开发 Web 应用程序/2.4 使用视图控制器.md: -------------------------------------------------------------------------------- 1 | ## 2.4 使用视图控制器 2 | 3 | 到目前为止,已经为 Taco Cloud 应用程序编写了三个控制器。尽管每个控制器在应用程序的功能上都有不同的用途,但它们几乎都遵循相同的编程模型: 4 | 5 | - 它们都用 @Controller 进行了注释,以表明它们是控制器类,应该由 Spring 组件扫描自动发现,并在 Spring 应用程序上下文中作为 bean 进行实例化。 6 | - 除了 HomeController 之外,所有的控制器都在类级别上使用 @RequestMapping 进行注释,以定义控制器将处理的基本请求模式。 7 | - 它们都有一个或多个方法,这些方法都用 @GetMapping 或 @PostMapping 进行了注释,以提供关于哪些方法应该处理哪些请求的细节。 8 | 9 | 即将编写的大多数控制器都将遵循这种模式。但是,如果一个控制器足够简单,不填充模型或流程输入(就像 HomeController 一样),那么还有另一种定义控制器的方法。请查看下一个程序清单,了解如何声明视图控制器 —— 一个只将请求转发给视图的控制器。程序清单 2.15 声明视图控制器。 10 | 11 | ```java 12 | package tacos.web; 13 | 14 | import org.springframework.context.annotation.Configuration; 15 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 16 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 17 | 18 | @Configuration 19 | public class WebConfig implements WebMvcConfigurer { 20 | 21 | @Override 22 | public void addViewControllers(ViewControllerRegistry registry) { 23 | registry.addViewController("/").setViewName("home"); 24 | } 25 | } 26 | ``` 27 | 28 | 关于 @WebConfig 最值得注意的是它实现了 WebMvcConfigurer 接口。WebMvcConfigurer 定义了几个配置 Spring MVC 的方法。尽管它是一个接口,但它提供了所有方法的默认实现,因此只需覆盖所需的方法。在本例中,覆盖了 addViewControllers() 方法。 29 | 30 | addViewControllers() 方法提供了一个 ViewControllerRegistry,可以使用它来注册一个或多个视图控制器。在这里,在注册表上调用 addViewController(),传入 “/”,这是视图控制器处理 GET 请求的路径。该方法返回一个 ViewControllerRegistration 对象,在该对象上立即调用 setViewName() 来指定 home 作为应该转发 “/” 请求的视图。 31 | 32 | 就像这样,已经能够用配置类中的几行代码替换 HomeController。现在可以删除 HomeController,应用程序的行为应该与以前一样。惟一需要做的其他更改是重新访问第 1 章中的 HomeControllerTest,从 @WebMvcTest 注释中删除对 HomeController 的引用,这样测试类就可以无错误地编译了。 33 | 34 | 这里,已经创建了一个新的 WebConfig 配置类来存放视图控制器声明。但是任何配置类都可以实现 WebMvcConfigurer 并覆盖 addViewController() 方法。例如,可以将相同的视图控制器声明添加到引导 TacoCloudApplication 类中,如下所示: 35 | 36 | ```java 37 | @SpringBootApplication 38 | public class TacoCloudApplication implements WebMvcConfigurer { 39 | 40 | public static void main(String[] args) { 41 | SpringApplication.run(TacoCloudApplication.class, args); 42 | } 43 | 44 | @Override 45 | public void addViewControllers(ViewControllerRegistry registry) { 46 | registry.addViewController("/").setViewName("home"); 47 | } 48 | } 49 | ``` 50 | 51 | 通过扩展现有的配置类,可以避免创建新的配置类,从而降低项目工件数量。但是我倾向于为每种配置(web、数据、安全性等等)创建一个新的配置类,保持应用程序引导配置的简洁。 52 | 53 | 说到视图控制器,更一般地说,是控制器将请求转发给的视图,到目前为止,已经为所有视图使用了 Thymeleaf。我非常喜欢 Thymeleaf,但也许你更喜欢应用程序视图的不同模板模型。让我们看看 Spring 支持的许多视图选项。 54 | -------------------------------------------------------------------------------- /04~工程实践/测试/JUnit 4/单元测试.md: -------------------------------------------------------------------------------- 1 | # Unit Test 2 | 3 | ```java 4 | @RunWith(SpringJUnit4ClassRunner.class) 5 | @WebAppConfiguration 6 | @ActiveProfiles("dev") 7 | @SpringApplicationConfiguration( 8 | classes = { Application.class, MockServletContext.class } 9 | ) 10 | @ImportResource("classpath:spring/applicationContext.xml") 11 | public class AbstractTest {} 12 | ``` 13 | 14 | You will want to add a test for the endpoint you added, and Spring Test already provides some machinery for that, and it’s easy to include in your project. Add this to your build file’s list of dependencies: 15 | 16 | ```groovy 17 | testCompile("org.springframework.boot:spring-boot-starter-test") 18 | ``` 19 | 20 | If you are using Maven, add this to your list of dependencies: 21 | 22 | ```xml 23 | 24 | org.springframework.boot 25 | spring-boot-starter-test 26 | test 27 | 28 | ``` 29 | 30 | Now write a simple unit test that mocks the servlet request and response through your endpoint: 31 | 32 | ```java 33 | @RunWith(SpringJUnit4ClassRunner.class) 34 | @SpringApplicationConfiguration(classes = MockServletContext.class) 35 | @WebAppConfiguration 36 | public class HelloControllerTest { 37 | private MockMvc mvc; 38 | 39 | @Autowired 40 | private WebApplicationContext wac; 41 | 42 | /**如果是配置启用整个Web环境 43 | @Before 44 | public void setUp() throws Exception { 45 | this.mvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); 46 | } 47 | **/ 48 | @Before 49 | public void setUp() throws Exception { 50 | mvc = MockMvcBuilders.standaloneSetup(new HelloController()).build(); 51 | } 52 | 53 | @Test 54 | public void getHello() throws Exception { 55 | mvc 56 | .perform( 57 | MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON) 58 | ) 59 | .andExpect(status().isOk()) 60 | .andExpect(content().string(equalTo("Greetings from Spring Boot!"))); 61 | } 62 | } 63 | ``` 64 | 65 | Note the use of the `MockServletContext` to set up an empty `WebApplicationContext` so the `HelloController` can be created in the `@Before` and passed to`MockMvcBuilders.standaloneSetup()`. An alternative would be to create the full application context using the `Application` class and `@Autowired` the `HelloController` into the test. The `MockMvc` comes from Spring Test and allows you, via a set of convenient builder classes, to send HTTP requests into the `DispatcherServlet` and make assertions about the result. 66 | -------------------------------------------------------------------------------- /02~数据库/02~MyBatis/MyBatis 基础使用/01~配置使用/2023~MyBatis 基础使用/04~注解映射.md: -------------------------------------------------------------------------------- 1 | # MyBatis 注解方式 2 | 3 | 在 MyBatis 3.0 之后,即可以直接以 Annotation 方式将 SQL 与配置写在 Java 文件中,也可以直接写在 XML 文件中。笔者建议的简单的 SQL 语句可以直接以 Annotation 方式编写,复杂的 SQL 语句可以写在 XML 文件中。 4 | 5 | ```java 6 | public interface BlogMapper 7 | { 8 | @Insert('INSERT INTO BLOG(BLOG_NAME, CREATED_ON) VALUES(#{blogName}, #{createdOn})') 9 | @Options(useGeneratedKeys=true, keyProperty='blogId') 10 | public void insertBlog(Blog blog); 11 | 12 | @Select('SELECT BLOG_ID AS blogId, BLOG_NAME as blogName, CREATED_ON as createdOn FROM BLOG WHERE BLOG_ID=#{blogId}') 13 | public Blog getBlogById(Integer blogId); 14 | 15 | @Select('SELECT * FROM BLOG ') 16 | @Results({ 17 | @Result(id=true, property='blogId', column='BLOG_ID'), 18 | @Result(property='blogName', column='BLOG_NAME'), 19 | @Result(property='createdOn', column='CREATED_ON') 20 | }) 21 | public List getAllBlogs(); 22 | 23 | @Update('UPDATE BLOG SET BLOG_NAME=#{blogName}, CREATED_ON=#{createdOn} WHERE BLOG_ID=#{blogId}') 24 | public void updateBlog(Blog blog); 25 | 26 | @Delete('DELETE FROM BLOG WHERE BLOG_ID=#{blogId}') 27 | public void deleteBlog(Integer blogId); 28 | 29 | } 30 | ``` 31 | 32 | # 查询 33 | 34 | # 结果集 35 | 36 | # 插入 37 | 38 | ```java 39 | @Options(useGeneratedKeys = true, keyProperty = "challenge_id") 40 | @Insert("insert into t_challenge(" + 41 | "challenge_user_id_creator," + 42 | "challenge_city_id)" + 43 | "values(" + 44 | "#{challenge_user_id_creator}," + 45 | "#{challenge_city_id}" + 46 | ");") 47 | public boolean insertChallenge(ChallengeResource.Entity challenge); 48 | ``` 49 | 50 | ## 多行插入 51 | 52 | ```java 53 | @Options(useGeneratedKeys = false, keyProperty = "challengeAttendResourceList[].challenge_attend_id", keyColumn = "challenge_attend_id") 54 | @Insert("") 66 | public boolean insertChallengeAttendSingleOrMultiple(@Param("challengeAttendResourceList") List challengeAttendResourceList); 67 | ``` 68 | -------------------------------------------------------------------------------- /01~基础使用/05~拦截与切面/异步拦截器.md: -------------------------------------------------------------------------------- 1 | # 异步拦截器 2 | 3 | Spring MVC 给提供了异步拦截器,能让我们更深入的参与进去异步 request 的生命周期里面去。其中最为常用的为:AsyncHandlerInterceptor: 4 | 5 | ```java 6 | public class AsyncHelloInterceptor implements AsyncHandlerInterceptor { 7 | 8 | // 这是Spring3.2提供的方法,专门拦截异步请求的方式 9 | @Override 10 | public void afterConcurrentHandlingStarted( 11 | HttpServletRequest request, 12 | HttpServletResponse response, 13 | Object handler 14 | ) 15 | throws Exception { 16 | System.out.println( 17 | Thread.currentThread().getName() + 18 | "---afterConcurrentHandlingStarted-->" + 19 | request.getRequestURI() 20 | ); 21 | } 22 | 23 | @Override 24 | public boolean preHandle( 25 | HttpServletRequest request, 26 | HttpServletResponse response, 27 | Object handler 28 | ) 29 | throws Exception { 30 | System.out.println( 31 | Thread.currentThread().getName() + 32 | "---preHandle-->" + 33 | request.getRequestURI() 34 | ); 35 | return true; 36 | } 37 | 38 | @Override 39 | public void postHandle( 40 | HttpServletRequest request, 41 | HttpServletResponse response, 42 | Object handler, 43 | ModelAndView modelAndView 44 | ) 45 | throws Exception { 46 | System.out.println( 47 | Thread.currentThread().getName() + 48 | "---postHandle-->" + 49 | request.getRequestURI() 50 | ); 51 | } 52 | 53 | @Override 54 | public void afterCompletion( 55 | HttpServletRequest request, 56 | HttpServletResponse response, 57 | Object handler, 58 | Exception ex 59 | ) 60 | throws Exception { 61 | System.out.println( 62 | Thread.currentThread().getName() + 63 | "---afterCompletion-->" + 64 | request.getRequestURI() 65 | ); 66 | } 67 | } 68 | ``` 69 | 70 | AsyncHandlerInterceptor 提供了一个 afterConcurrentHandlingStarted()方法, 这个方法会在 Controller 方法异步执行时开始执行, 而 Interceptor 的 postHandle 方法则是需要等到 Controller 的异步执行完才能执行。比如我们用 DeferredResult 的话,afterConcurrentHandlingStarted 是在 return 的之后执行,而 postHandle()是执行.setResult()之后执行。 71 | 72 | 需要说明的是:如果我们不是异步请求,afterConcurrentHandlingStarted 是不会执行的。所以我们可以把它当做加强版的 HandlerInterceptor 来用。平时我们若要使用拦截器,建议使用它。同样可以注册 CallableProcessingInterceptor 或者一个 DeferredResultProcessingInterceptor 用于更深度的集成异步 request 的生命周期: 73 | 74 | ```java 75 | @Override 76 | public void configureAsyncSupport(AsyncSupportConfigurer configurer) { 77 | // 注册异步的拦截器、默认的超时时间、任务处理器TaskExecutor等等 78 | //configurer.registerCallableInterceptors(); 79 | //configurer.registerDeferredResultInterceptors(); 80 | //configurer.setDefaultTimeout(); 81 | //configurer.setTaskExecutor(); 82 | } 83 | ``` 84 | -------------------------------------------------------------------------------- /01~基础使用/03~依赖注入/参数配置/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot 参数配置 2 | 3 | Spring Boot 针对我们常用的开发场景提供了一系列自动化配置来减少原本复杂而又几乎很少改动的模板化配置内容。但是,我们还是需要去了解如何在 Spring Boot 中修改这些自动化的配置内容,以应对一些特殊的场景需求,比如:我们在同一台主机上需要启动多个基于 Spring Boot 的 web 应用,若我们不为每个应用指定特别的端口号,那么默认的 8080 端口必将导致冲突。 4 | 5 | # 程序中设置参数 6 | 7 | 在 `database.xml` 中可以这么写: 8 | 9 | ```xml 10 | ... 11 | 16 | 17 | 18 | ... 19 | ``` 20 | 21 | 我们可以通过在启动应用程序时设置特定属性或通过自定义嵌入式服务器配置来以编程方式配置端口。 22 | 23 | ```java 24 | @SpringBootApplication 25 | public class CustomApplication { 26 | 27 | public static void main(String[] args) { 28 | SpringApplication app = new SpringApplication(CustomApplication.class); 29 | app.setDefaultProperties(Collections.singletonMap("server.port", "8083")); 30 | app.run(args); 31 | } 32 | } 33 | ``` 34 | 35 | 我们也可以直接设置实体类的属性,来修改端口号: 36 | 37 | ```java 38 | @Component 39 | public class ServerPortCustomizer 40 | implements WebServerFactoryCustomizer { 41 | 42 | @Override 43 | public void customize(ConfigurableWebServerFactory factory) { 44 | factory.setPort(8086); 45 | } 46 | } 47 | ``` 48 | 49 | 也可以使用 properties 属性: 50 | 51 | ```java 52 | public static void main(String[] args) { 53 | new SpringApplicationBuilder(Application.class).properties(properties()).run(args); 54 | } 55 | 56 | // 手动注入写死的配置信息 57 | private static Properties properties() { 58 | Properties properties = new Properties(); 59 | 60 | Locale locale = Locale.getDefault(); 61 | properties.setProperty("locale.language", locale.getLanguage()); 62 | properties.setProperty("locale.country", locale.getCountry()); 63 | 64 | properties.setProperty("management.endpoints.web.exposure.include", "*"); 65 | properties.setProperty("management.endpoints.web.exposure.include", "*"); 66 | 67 | String druidPrefix = "spring.datasource"; 68 | 69 | properties.setProperty(druidPrefix + ".type", "com.alibaba.druid.pool.DruidDataSource"); 70 | properties.setProperty(druidPrefix + ".druid.max-active", "50"); 71 | properties.setProperty(druidPrefix + ".druid.min-idle", "5"); 72 | // properties.setProperty(druidPrefix + ".druid.remove-abandoned", "true"); 73 | // properties.setProperty(druidPrefix + ".druid.remove-abandoned-timeout-millis", "120000"); 74 | 75 | return properties; 76 | } 77 | ``` 78 | 79 | # Links 80 | 81 | - https://mp.weixin.qq.com/s/e0tO2zogV-L6mXLfaiFCfw?from=groupmessage&isappinstalled=0 这样讲 SpringBoot 自动配置原理,你应该能明白了吧 82 | -------------------------------------------------------------------------------- /02~数据库/05~数据库工具/数据库迁移/Flyway/README.md: -------------------------------------------------------------------------------- 1 | # Flyway 2 | 3 | Flyway 是一个简单开源数据库版本控制器(约定大于配置),主要提供 migrate、clean、info、validate、baseline、repair 等命令。它支持 SQL(PL/SQL、T-SQL)方式和 Java 方式,支持命令行客户端等,还提供一系列的插件支持(Maven、Gradle、SBT、ANT 等)。 4 | 5 | # 快速开始 6 | 7 | 在 start.spring.io 上新建一个 SpringBoot 工程,要求能连上自己本地新建的 mysql 数据库 flyway;但要注意的是,application.properties 中数据库的配置务必配置正确,下述步骤中系统启动时,flyway 需要凭借这些配置连接到数据库: 8 | 9 | ```conf 10 | # db config 11 | spring.datasource.url=jdbc:mysql://localhost:3306/flyway?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT 12 | spring.datasource.username=root 13 | spring.datasource.password=root 14 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 15 | ``` 16 | 17 | 首先,在 pom 文件中引入 flyway 的核心依赖包: 18 | 19 | ```xml 20 | 21 | org.flywaydb 22 | flyway-core 23 | 5.2.4 24 | 25 | ``` 26 | 27 | 其次,在 src/main/resources 目录下面新建 db.migration 文件夹,默认情况下,该目录下的.sql 文件就算是需要被 flyway 做版本控制的数据库 SQL 语句。但是此处的 SQL 语句命名需要遵从一定的规范,否则运行的时候 flyway 会报错。命名规则主要有两种: 28 | 29 | - 仅需要被执行一次的 SQL 命名以大写的"V"开头,后面跟上"0~9"数字的组合,数字之间可以用“.”或者下划线"\_"分割开,然后再以两个下划线分割,其后跟文件名称,最后以.sql 结尾。比如,`V2.1.5__create_user_ddl.sql、V4.1_2__add_user_dml.sql`。 30 | - 可重复运行的 SQL,则以大写的“R”开头,后面再以两个下划线分割,其后跟文件名称,最后以.sql 结尾。比如,`R__truncate_user_dml.sql`。 31 | 32 | 其中,V 开头的 SQL 执行优先级要比 R 开头的 SQL 优先级高。如下,我们准备了三个脚本,分别为: 33 | 34 | - `V1__create_user.sql`,其中代码如下,目的是建立一张 user 表,且只执行一次。 35 | 36 | ```sql 37 | CREATE TABLE IF NOT EXISTS `USER`( 38 | `USER_ID` INT(11) NOT NULL AUTO_INCREMENT, 39 | `USER_NAME` VARCHAR(100) NOT NULL COMMENT '用户姓名', 40 | `AGE` INT(3) NOT NULL COMMENT '年龄', 41 | `CREATED_TIME` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, 42 | `CREATED_BY` varchar(100) NOT NULL DEFAULT 'UNKNOWN', 43 | `UPDATED_TIME` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, 44 | `UPDATED_BY` varchar(100) NOT NULL DEFAULT 'UNKNOWN', 45 | PRIMARY KEY (`USER_ID`) 46 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 47 | ``` 48 | 49 | - `V2__add_user.sql`,其中代码如下,目的是往 user 表中插入一条数据,且只执行一次。 50 | 51 | ```sql 52 | insert into `user`(user_name,age) values('lisi',33); 53 | ``` 54 | 55 | - `R__add_unknown_user.sql`,其中代码如下,目的是每次启动倘若有变化,则往 user 表中插入一条数据。 56 | 57 | ```sql 58 | insert into `user`(user_name,age) values('unknown',33); 59 | ``` 60 | 61 | ![文件目录](https://pic.imgdb.cn/item/61a2f3cf2ab3f51d91844a8d.jpg) 62 | 63 | 其中 2.1.6、2.1.7 和 every 的文件夹不会影响 flyway 对 SQL 的识别和运行,可以自行取名和分类。执行 Flyway Migrate 指令,可以看到会生成如下的表: 64 | 65 | ![flyway_schema_history](https://pic.imgdb.cn/item/61a2f42b2ab3f51d9184a783.jpg) 66 | 67 | 而且,user 表也已经创建好了并插入了两条数据: 68 | 69 | ![user 表](https://pic.imgdb.cn/item/61a2f44a2ab3f51d9184c693.jpg) 70 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 1 章 Spring 入门/1.4 俯瞰 Spring 风景线.md: -------------------------------------------------------------------------------- 1 | ## 1.4 俯瞰 Spring 风景线 2 | 3 | 要了解 Spring 的风景线,只需查看完整版 Spring Initializr web 表单上的大量复选框列表即可。它列出了 100 多个依赖项选择,所以我不会在这里全部列出或者提供一个屏幕截图。但我鼓励你们去看看。与此同时,我将提到一些亮点。 4 | 5 | ### 1.4.1 Spring 核心框架 6 | 7 | 正如你所期望的,Spring 核心框架是 Spring 领域中其他一切的基础。它提供了核心容器和依赖注入框架。但它也提供了一些其他的基本特性。 8 | 9 | 其中包括 Spring MVC 和 Spring web 框架。已经了解了如何使用 Spring MVC 编写控制器类来处理 web 请求。但是,您还没有看到的是,Spring MVC 也可以用于创建产生非 HTML 输出的 REST API。我们将在第 2 章深入研究 Spring MVC,然后在第 6 章中讨论如何使用它来创建 REST API。 10 | 11 | Spring 核心框架还提供了一些基本数据持久性支持,特别是基于模板的 JDBC 支持。将在第 3 章中看到如何使用 JdbcTemplate。 12 | 13 | 在 Spring 的最新版本(5.0.8)中,添加了对响应式编程的支持,包括一个新的响应式 web 框架 —— Spring WebFlux,它大量借鉴了 Spring MVC。将在第 3 部分中看到 Spring 的响应式编程模型,并在第 10 章中看到 Spring WebFlux。 14 | 15 | ### 1.4.2 Spring Boot 16 | 17 | 我们已经看到了 Spring Boot 的许多好处,包括启动依赖项和自动配置。在本书中我们确实会尽可能多地使用 Spring Boot,并避免任何形式的显式配置,除非绝对必要。但除了启动依赖和自动配置,Spring Boot 还提供了一些其他有用的特性: 18 | 19 | - Actuator 提供了对应用程序内部工作方式的运行时监控,包括端点、线程 dump 信息、应用程序健康状况和应用程序可用的环境属性。 20 | - 灵活的环境属性规范。 21 | - 在核心框架的测试辅助之外,还有额外的测试支持。 22 | 23 | 此外,Spring Boot 提供了一种基于 Groovy 脚本的替代编程模型,称为 Spring Boot CLI(命令行界面)。使用 Spring Boot CLI,可以将整个应用程序编写为 Groovy 脚本的集合,并从命令行运行它们。我们不会在 Spring Boot CLI 上花太多时间,但是当它适合我们的需要时,我们会接触它。 24 | 25 | Spring Boot 已经成为 Spring 开发中不可或缺的一部分;我无法想象开发一个没有它的 Spring 应用程序。因此,本书采用了以 Spring Boot 为中心的观点,当我提到 Spring Boot 正在做的事情时,你可能会发现我在使用 Spring 这个词。 26 | 27 | ### 1.4.3 Spring Data 28 | 29 | 尽管 Spring 核心框架提供了基本的数据持久性支持,但 Spring Data 提供了一些非常惊人的功能:将应用程序的数据存储库抽象为简单的 Java 接口,同时当定义方法用于如何驱动数据进行存储和检索的问题时,对方法使用了命名约定。 30 | 31 | 更重要的是,Spring Data 能够处理几种不同类型的数据库,包括关系型(JPA)、文档型(Mongo)、图型(Neo4j)等。在第 3 章中,将使用 Spring Data 来帮助创建 Taco Cloud 应用程序的存储库。 32 | 33 | ### 1.4.4 Spring Security 34 | 35 | 应用程序安全性一直是一个重要的主题,而且似乎一天比一天重要。幸运的是,Spring 在 Spring security 中有一个健壮的安全框架。 36 | 37 | Spring Security 解决了广泛的应用程序安全性需求,包括身份验证、授权和 API 安全性。尽管 Spring Security 的范围太大,本书无法恰当地涵盖,但我们将在第 4 章和第 12 章中讨论一些最常见的用例。 38 | 39 | ### 1.4.5 Spring Integration 和 Spring Batch 40 | 41 | 在某种程度上,大多数应用程序将需要与其他应用程序集成,甚至需要与同一应用程序的其他组件集成。为了满足这些需求,出现了几种应用程序集成模式。Spring Integration 和 Spring Batch 为基于 Spring 的应用程序提供了这些模式的实现。 42 | 43 | Spring Integration 解决了实时集成,即数据在可用时进行处理。相反,Spring Batch 解决了批量集成的问题,允许在一段时间内收集数据,直到某个触发器(可能是一个时间触发器)发出信号,表示该处理一批数据了。将在第 9 章中研究 Spring Batch 和 Spring Integration。 44 | 45 | ### 1.4.6 Spring Cloud 46 | 47 | 在我写这篇文章的时候,应用程序开发领域正在进入一个新时代,在这个时代中,我们不再将应用程序作为单个部署单元来开发,而是将由几个称为 _微服务_ 的单个部署单元组成应用程序。 48 | 49 | 微服务是一个热门话题,解决了几个实际的开发和运行时问题。然而,在这样做的同时,他们也带来了自己的挑战。这些挑战都将由 Spring Cloud 直接面对,Spring Cloud 是一组用 Spring 开发云本地应用程序的项目。 50 | 51 | Spring Cloud 覆盖了很多地方,这本书不可能涵盖所有的地方。我们将在第 13、14 和 15 章中查看 Spring Cloud 的一些最常见的组件。关于 Spring Cloud 的更完整的讨论,我建议看看 John Carnell 的 Spring Microservices in Action(Manning, 2017, www.manning.com/books/spring-microservices-in-action)。 52 | -------------------------------------------------------------------------------- /02~数据库/05~数据库工具/嵌入式数据库/HSQLDB/集成到 Spring Boot.md: -------------------------------------------------------------------------------- 1 | # Spring Boot 中集成使用 HSQLDB 2 | 3 | 首先我们添加最新的依赖: 4 | 5 | ```xml 6 | 7 | org.springframework.boot 8 | spring-boot-starter-data-jpa 9 | 2.1.1.RELEASE 10 | 11 | 12 | org.hsqldb 13 | hsqldb 14 | 2.4.0 15 | runtime 16 | 17 | ``` 18 | 19 | 然后可以以服务器模式运行 HSQLDB: 20 | 21 | ```sh 22 | java -cp ../lib/hsqldb.jar org.hsqldb.server.Server --database.0 file.testdb --dbname0.testdb 23 | ``` 24 | 25 | 或者以内存模式运行,其配置方式分别如下: 26 | 27 | ```s 28 | spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver 29 | spring.datasource.url=jdbc:hsqldb:hsql://localhost/testdb 30 | spring.datasource.username=sa 31 | spring.datasource.password= 32 | spring.jpa.hibernate.ddl-auto=update 33 | 34 | spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver 35 | spring.datasource.url=jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1 36 | spring.datasource.username=sa 37 | spring.datasource.password= 38 | spring.jpa.hibernate.ddl-auto=create 39 | ``` 40 | 41 | 然后我们创建实体类以及 CrudRepository: 42 | 43 | ```java 44 | @Entity 45 | @Table(name = "customers") 46 | public class Customer { 47 | @Id 48 | @GeneratedValue(strategy = GenerationType.AUTO) 49 | private long id; 50 | 51 | private String name; 52 | 53 | private String email; 54 | // standard constructors / setters / getters / toString 55 | } 56 | 57 | @Repository 58 | public interface CustomerRepository extends CrudRepository {} 59 | ``` 60 | 61 | 最后的测试代码如下所示: 62 | 63 | ```java 64 | @RunWith(SpringRunner.class) 65 | @SpringBootTest 66 | public class CustomerRepositoryTest { 67 | @Autowired 68 | private CustomerRepository customerRepository; 69 | 70 | @Test 71 | public void whenFindingCustomerById_thenCorrect() { 72 | customerRepository.save(new Customer("John", "john@domain.com")); 73 | assertThat(customerRepository.findById(1L)).isInstanceOf(Optional.class); 74 | } 75 | 76 | @Test 77 | public void whenFindingAllCustomers_thenCorrect() { 78 | customerRepository.save(new Customer("John", "john@domain.com")); 79 | customerRepository.save(new Customer("Julie", "julie@domain.com")); 80 | assertThat(customerRepository.findAll()).isInstanceOf(List.class); 81 | } 82 | 83 | @Test 84 | public void whenSavingCustomer_thenCorrect() { 85 | customerRepository.save(new Customer("Bob", "bob@domain.com")); 86 | Customer customer = customerRepository 87 | .findById(1L) 88 | .orElseGet(() -> new Customer("john", "john@domain.com")); 89 | assertThat(customer.getName()).isEqualTo("Bob"); 90 | } 91 | } 92 | ``` 93 | -------------------------------------------------------------------------------- /01~基础使用/05~拦截与切面/过滤器.md: -------------------------------------------------------------------------------- 1 | # Filter | 过滤器 2 | 3 | # 过滤器配置 4 | 5 | 现在我们通过过滤器来实现记录请求执行时间的功能,其实现如下: 6 | 7 | ```java 8 | public class LogCostFilter implements Filter { 9 | @Override 10 | public void init(FilterConfig filterConfig) throws ServletException { 11 | 12 | } 13 | 14 | @Override 15 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 16 | long start = System.currentTimeMillis(); 17 | filterChain.doFilter(servletRequest,servletResponse); 18 | System.out.println("Execute cost="+(System.currentTimeMillis()-start)); 19 | } 20 | 21 | @Override 22 | public void destroy() { 23 | 24 | } 25 | } 26 | ``` 27 | 28 | 这段代码的逻辑比较简单,就是在方法执行前先记录时间戳,然后通过过滤器链完成请求的执行,在返回结果之间计算执行的时间。这里需要主要,这个类必须继承 Filter 类,这个是 Servlet 的规范,这个跟以前的 Web 项目没区别。但是,有了过滤器类以后,以前的 web 项目可以在 web.xml 中进行配置,但是 spring boot 项目并没有 web.xml 这个文件,那怎么配置?在 Spring boot 中,我们需要 FilterRegistrationBean 来完成配置。其实现过程如下: 29 | 30 | ```java 31 | @Configuration 32 | public class FilterConfig { 33 | 34 | @Bean 35 | public FilterRegistrationBean registFilter() { 36 | FilterRegistrationBean registration = new FilterRegistrationBean(); 37 | registration.setFilter(new LogCostFilter()); 38 | registration.addUrlPatterns("/*"); 39 | registration.setName("LogCostFilter"); 40 | registration.setOrder(1); 41 | return registration; 42 | } 43 | 44 | } 45 | ``` 46 | 47 | 除了通过 FilterRegistrationBean 来配置以外,还有一种更直接的办法,直接通过注解就可以完成了: 48 | 49 | ```java 50 | @WebFilter(urlPatterns = "/*", filterName = "logFilter2") 51 | public class LogCostFilter2 implements Filter { 52 | @Override 53 | public void init(FilterConfig filterConfig) throws ServletException { 54 | 55 | } 56 | 57 | @Override 58 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 59 | long start = System.currentTimeMillis(); 60 | filterChain.doFilter(servletRequest, servletResponse); 61 | System.out.println("LogFilter2 Execute cost=" + (System.currentTimeMillis() - start)); 62 | } 63 | 64 | @Override 65 | public void destroy() { 66 | 67 | } 68 | } 69 | ``` 70 | 71 | 这里直接用@WebFilter 就可以进行配置,同样,可以设置 url 匹配模式,过滤器名称等。这里需要注意一点的是@WebFilter 这个注解是 Servlet3.0 的规范,并不是 Spring boot 提供的。除了这个注解以外,我们还需在配置类中加另外一个注解:@ServletComponetScan,指定扫描的包。 72 | 73 | ```java 74 | @SpringBootApplication 75 | @MapperScan("com.pandy.blog.dao") 76 | @ServletComponentScan("com.pandy.blog.filters") 77 | public class Application { 78 | public static void main(String[] args) throws Exception { 79 | SpringApplication.run(Application.class, args); 80 | } 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /03~Spring Security/权限验证/基于 JWT 的验证/99~参考资料/2023~实现基于 JWT 的登录认证/03~访问与鉴权.md: -------------------------------------------------------------------------------- 1 | # 访问与鉴权 2 | 3 | 首先是用户的注册与登录: 4 | 5 | ```java 6 | 7 | @CrossOrigin(origins = "*", maxAge = 3600) 8 | @RestController 9 | @RequestMapping("/auth") 10 | public class AuthController { 11 | 12 | @Autowired private AuthenticationManager authenticationManager; 13 | 14 | @Autowired private TokenProvider jwtTokenUtil; 15 | 16 | @Autowired private UserService userService; 17 | 18 | @RequestMapping(value = "/sign_in", method = RequestMethod.POST) 19 | public ResponseEntity sign_in(@RequestBody LoginUser loginUser) 20 | throws AuthenticationException { 21 | 22 | final Authentication authentication = 23 | authenticationManager.authenticate( 24 | new UsernamePasswordAuthenticationToken( 25 | loginUser.getUsername(), loginUser.getPassword())); 26 | 27 | SecurityContextHolder.getContext().setAuthentication(authentication); 28 | final String token = jwtTokenUtil.generateToken(authentication); 29 | return ResponseEntity.ok(new AuthToken(token)); 30 | } 31 | 32 | @RequestMapping(value = "/sign_up", method = RequestMethod.POST) 33 | public User saveUser(@RequestBody UserDTO user) { 34 | return userService.save(user); 35 | } 36 | } 37 | 38 | ``` 39 | 40 | 我们需要使用声明的编码器加密密码后放入数据库: 41 | 42 | ```java 43 | @Override 44 | public User save(UserDTO user) { 45 | User newUser = new User(); 46 | newUser.setUsername(user.getUsername()); 47 | newUser.setPassword(bcryptEncoder.encode(user.getPassword())); 48 | newUser.setAge(user.getAge()); 49 | newUser.setSalary(user.getSalary()); 50 | return userDao.save(newUser); 51 | } 52 | ``` 53 | 54 | 而在登录的时候: 55 | 56 | ```java 57 | authenticationManager.authenticate( 58 | new UsernamePasswordAuthenticationToken( 59 | loginUser.getUsername(), loginUser.getPassword())); 60 | ``` 61 | 62 | authenticationManager 会根据传入的用户信息,调用 UserDetailsService 判断用户是否真实,然后创建 JWT 的 Token 并返回。注意,这里是把密码从数据库中读取出来,然后再次进行核对。最后在具体的接口访问,譬如在访问用户信息时,我们可以通过注解来指定某个接口的权限控制: 63 | 64 | ```java 65 | @CrossOrigin(origins = "*", maxAge = 3600) 66 | @RestController 67 | public class UserController { 68 | 69 | @Autowired private UserService userService; 70 | 71 | // @Secured({"ROLE_ADMIN", "ROLE_USER"}) 72 | @PreAuthorize("hasRole('ADMIN')") 73 | @RequestMapping(value = "/users", method = RequestMethod.GET) 74 | public List listUser() { 75 | return userService.findAll(); 76 | } 77 | 78 | // @Secured("ROLE_USER") 79 | @PreAuthorize("hasRole('USER')") 80 | //// @PreAuthorize("hasAnyRole('USER', 'ADMIN')") 81 | @RequestMapping(value = "/users/{id}", method = RequestMethod.GET) 82 | public User getOne(@PathVariable(value = "id") Long id) { 83 | return userService.findById(id); 84 | } 85 | } 86 | ``` 87 | -------------------------------------------------------------------------------- /01~基础使用/04~请求与响应/路由与参数/README.md: -------------------------------------------------------------------------------- 1 | # 路由与参数 2 | 3 | # 路由 4 | 5 | ## 路径匹配 6 | 7 | @RequestMapping 是 Spring MVC 中最常用的注解之一,`org.springframework.web.bind.annotation.RequestMapping` 被用于将某个请求映射到具体的处理类或者方法中: 8 | 9 | ```java 10 | // @RequestMapping with Class 11 | @Controller 12 | @RequestMapping("/home") 13 | public class HomeController {} 14 | 15 | // @RequestMapping with Method 16 | @RequestMapping(value="/method0") 17 | @ResponseBody 18 | public String method0(){ 19 | return "method0"; 20 | } 21 | 22 | // @RequestMapping with Multiple URI 23 | @RequestMapping(value={"/method1","/method1/second"}) 24 | @ResponseBody 25 | public String method1(){ 26 | return "method1"; 27 | } 28 | 29 | // @RequestMapping with HTTP Method 30 | @RequestMapping(value="/method3", method={RequestMethod.POST,RequestMethod.GET}) 31 | @ResponseBody 32 | public String method3(){ 33 | return "method3"; 34 | } 35 | 36 | // @RequestMapping default method 37 | @RequestMapping() 38 | @ResponseBody 39 | public String defaultMethod(){ 40 | return "default method"; 41 | } 42 | 43 | // @RequestMapping fallback method 44 | @RequestMapping("*") 45 | @ResponseBody 46 | public String fallbackMethod(){ 47 | return "fallback method"; 48 | } 49 | 50 | // @RequestMapping headers 51 | @RequestMapping(value="/method5", headers={"name=pankaj", "id=1"}) 52 | @ResponseBody 53 | public String method5(){ 54 | return "method5"; 55 | } 56 | 57 | // 表示将功能处理方法将生产 json 格式的数据,此时根据请求头中的 Accept 进行匹配,如请求头 Accept:application/json 时即可匹配; 58 | @RequestMapping(value = "/produces", produces = "application/json") 59 | @RequestMapping(produces={"text/html", "application/json"}) 60 | ``` 61 | 62 | ## 路由日志 63 | 64 | 该 Spring Boot 2.1.x 版本开始,将这些日志的打印级别做了调整:从原来的 INFO 调整为 TRACE。所以,当我们希望在应用启动的时候打印这些信息的话,只需要在配置文件增增加对 RequestMappingHandlerMapping 类的打印级别设置即可,比如在 application.properties 中增加下面这行配置: 65 | 66 | ```sh 67 | logging.level.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping=trace 68 | ``` 69 | 70 | 在增加了上面的配置之后重启应用,便可以看到如下的日志打印: 71 | 72 | ```sh 73 | 2020-02-11 15:36:09.787 TRACE 49215 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : 74 | c.d.c.UserController: 75 | {PUT /users/{id}}: putUser(Long,User) 76 | {GET /users/{id}}: getUser(Long) 77 | {POST /users/}: postUser(User) 78 | {GET /users/}: getUserList() 79 | {DELETE /users/{id}}: deleteUser(Long) 80 | 2020-02-11 15:36:09.791 TRACE 49215 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : 81 | o.s.b.a.w.s.e.BasicErrorController: 82 | { /error}: error(HttpServletRequest) 83 | { /error, produces [text/html]}: errorHtml(HttpServletRequest,HttpServletResponse) 84 | 2020-02-11 15:36:09.793 DEBUG 49215 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : 7 mappings in 'requestMappingHandlerMapping' 85 | 86 | ``` 87 | 88 | 可以看到在 2.1.x 版本之后,除了调整了日志级别之外,对于打印内容也做了调整。现在的打印内容根据接口创建的 Controller 类做了分类打印,这样更有助于开发者根据自己编写的 Controller 来查找初始化了那些 HTTP 接口。 89 | -------------------------------------------------------------------------------- /02~数据库/04~缓存与 KV 存储/进程内缓存/Cache 注解.md: -------------------------------------------------------------------------------- 1 | # Cache 注解 2 | 3 | User 实体的数据访问实现: 4 | 5 | ```java 6 | public interface UserRepository extends JpaRepository { 7 | 8 | User findByName(String name); 9 | 10 | User findByNameAndAge(String name, Integer age); 11 | 12 | @Query("from User u where u.name=:name") 13 | User findUser(@Param("name") String name); 14 | 15 | } 16 | ``` 17 | 18 | 在 pom.xml 中引入 cache 依赖,添加如下内容: 19 | 20 | ```xml 21 | 22 | org.springframework.boot 23 | spring-boot-starter-cache 24 | 25 | ``` 26 | 27 | 在 Spring Boot 主类中增加@EnableCaching 注解开启缓存功能,如下: 28 | 29 | ```java 30 | @EnableCaching 31 | @SpringBootApplication 32 | public class Chapter51Application { 33 | 34 | public static void main(String[] args) { 35 | SpringApplication.run(Chapter51Application.class, args); 36 | } 37 | 38 | } 39 | ``` 40 | 41 | 在数据访问接口中,增加缓存配置注解,如: 42 | 43 | ```java 44 | @CacheConfig(cacheNames = "users") 45 | public interface UserRepository extends JpaRepository { 46 | 47 | @Cacheable 48 | User findByName(String name); 49 | 50 | } 51 | ``` 52 | 53 | 到这里,我们可以看到,在调用第二次 findByName 函数时,没有再执行 select 语句,也就直接减少了一次数据库的读取操作。 54 | 55 | 回过头来我们再来看这里使用到的两个注解分别作了什么事情: 56 | 57 | - `@CacheConfig`:主要用于配置该类中会用到的一些共用的缓存配置。在这里`@CacheConfig(cacheNames = "users")`:配置了该数据访问对象中返回的内容将存储于名为 users 的缓存对象中,我们也可以不使用该注解,直接通过`@Cacheable`自己配置缓存集的名字来定义。 58 | 59 | - @Cacheable:配置了 findByName 函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。该注解主要有下面几个参数: 60 | 61 | - `value`、`cacheNames`:两个等同的参数(`cacheNames`为 Spring 4 新增,作为`value`的别名),用于指定缓存存储的集合名。由于 Spring 4 中新增了`@CacheConfig`,因此在 Spring 3 中原本必须有的`value`属性,也成为非必需项了 62 | - `key`:缓存对象存储在 Map 集合中的 key 值,非必需,缺省按照函数的所有参数组合作为 key 值,若自己配置需使用 SpEL 表达式,比如:`@Cacheable(key = "#p0")`:使用函数第一个参数作为缓存的 key 值,更多关于 SpEL 表达式的详细内容可参考[官方文档](http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html#cache-spel-context) 63 | - `condition`:缓存对象的条件,非必需,也需使用 SpEL 表达式,只有满足表达式条件的内容才会被缓存,比如:`@Cacheable(key = "#p0", condition = "#p0.length() < 3")`,表示只有当第一个参数的长度小于 3 的时候才会被缓存,若做此配置上面的 AAA 用户就不会被缓存,读者可自行实验尝试。 64 | - `unless`:另外一个缓存条件参数,非必需,需使用 SpEL 表达式。它不同于`condition`参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对 result 进行判断。 65 | - `keyGenerator`:用于指定 key 生成器,非必需。若需要指定一个自定义的 key 生成器,我们需要去实现`org.springframework.cache.interceptor.KeyGenerator`接口,并使用该参数来指定。需要注意的是:**该参数与`key`是互斥的** 66 | - `cacheManager`:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用 67 | - `cacheResolver`:用于指定使用那个缓存解析器,非必需。需通过`org.springframework.cache.interceptor.CacheResolver`接口来实现自己的缓存解析器,并用该参数指定。 68 | 69 | 除了这里用到的两个注解之外,还有下面几个核心注解: 70 | 71 | - `@CachePut`:配置于函数上,能够根据参数定义条件来进行缓存,它与`@Cacheable`不同的是,它每次都会真是调用函数,所以主要用于数据新增和修改操作上。它的参数与`@Cacheable`类似,具体功能可参考上面对`@Cacheable`参数的解析 72 | - @CacheEvict:配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。除了同@Cacheable 一样的参数之外,它还有下面两个参数: 73 | - `allEntries`:非必需,默认为 false。当为 true 时,会移除所有数据 74 | - `beforeInvocation`:非必需,默认为 false,会在调用方法之后移除数据。当为 true 时,会在调用方法之前移除数据。 75 | -------------------------------------------------------------------------------- /02~数据库/05~数据库工具/嵌入式数据库/H2/README.md: -------------------------------------------------------------------------------- 1 | # H2 2 | 3 | H2 是一个用 Java 开发的嵌入式数据库,它本身只是一个类库,即只有一个 jar 文件,可以直接嵌入到应用项目中。H2 主要有如下三个用途: 4 | 5 | - 第一个用途,也是最常使用的用途就在于可以同应用程序打包在一起发布,这样可以非常方便地存储少量结构化数据。 6 | 7 | - 第二个用途是用于单元测试。启动速度快,而且可以关闭持久化功能,每一个用例执行完随即还原到初始状态。 8 | 9 | - 第三个用途是作为缓存,即当做内存数据库,作为 NoSQL 的一个补充。当某些场景下数据模型必须为关系型,可以拿它当 Memcached 使,作为后端 MySQL/Oracle 的一个缓冲层,缓存一些不经常变化但需要频繁访问的数据,比如字典表、权限表。 10 | 11 | # 连接方式 12 | 13 | H2 支持以下三种连接模式: 14 | 15 | - 嵌入式模式(使用 JDBC 的本地连接) 16 | 17 | - 服务器模式(使用 JDBC 或 ODBC 在 TCP/IP 上的远程连接) 18 | 19 | - 混合模式(本地和远程连接同时进行) 20 | 21 | ## 嵌入式模式 22 | 23 | 在嵌入式模式下,应用程序使用 JDBC 从同一 JVM 中打开数据库。这是最快也是最容易的连接方式。缺点是数据库可能只在任何时候在一个虚拟机(和类加载器)中打开。与所有模式一样,支持持久性和内存数据库。对并发打开数据库的数量或打开连接的数量没有限制。 24 | 25 | ![Embedded](https://s2.ax1x.com/2019/12/16/Q5K9PI.png) 26 | 27 | ## 服务器模式 28 | 29 | 当使用服务器模式(有时称为远程模式或客户机/服务器模式)时,应用程序使用 JDBC 或 ODBC API 远程打开数据库。服务器需要在同一台或另一台虚拟机上启动,或者在另一台计算机上启动。许多应用程序可以通过连接到这个服务器同时连接到同一个数据库。在内部,服务器进程在嵌入式模式下打开数据库。 30 | 31 | 服务器模式比嵌入式模式慢,因为所有数据都通过 TCP/IP 传输。与所有模式一样,支持持久性和内存数据库。对每个服务器并发打开的数据库数量或打开连接的数量没有限制。 32 | 33 | ![服务器模式](https://s2.ax1x.com/2019/12/16/Q5KZZQ.png) 34 | 35 | ## 混合模式 36 | 37 | 混合模式是嵌入式和服务器模式的结合。连接到数据库的第一个应用程序在嵌入式模式下运行,但也启动服务器,以便其他应用程序(在不同进程或虚拟机中运行)可以同时访问相同的数据。本地连接的速度与数据库在嵌入式模式中的使用速度一样快,而远程连接速度稍慢。 38 | 39 | 服务器可以从应用程序内(使用服务器 API)启动或停止,或自动(自动混合模式)。当使用自动混合模式时,所有想要连接到数据库的客户端(无论是本地连接还是远程连接)都可以使用完全相同的数据库 URL 来实现。 40 | 41 | ![混合模式](https://s2.ax1x.com/2019/12/16/Q5KKGq.png) 42 | 43 | # Spring Boot 中使用 44 | 45 | ## 数据准备 46 | 47 | 我们要创建两个 Sql 文件,以便项目启动的时候,将表结构和数据初始化到数据库。表结构文件(schema-h2.sql)内容: 48 | 49 | ```sql 50 | DROP TABLE IF EXISTS user; 51 | 52 | CREATE TABLE user 53 | ( 54 | id BIGINT(20) NOT NULL COMMENT '主键ID', 55 | name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', 56 | age INT(11) NULL DEFAULT NULL COMMENT '年龄', 57 | email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', 58 | PRIMARY KEY (id) 59 | ); 60 | ``` 61 | 62 | 表数据文件(data-h2.sql)内容: 63 | 64 | ```sql 65 | INSERT INTO user (id, name, age, email) VALUES 66 | (1, 'neo', 18, 'smile1@ityouknow.com'), 67 | (2, 'keep', 36, 'smile2@ityouknow.com'), 68 | (3, 'pure', 28, 'smile3@ityouknow.com'), 69 | (4, 'smile', 21, 'smile4@ityouknow.com'), 70 | (5, 'it', 24, 'smile5@ityouknow.com'); 71 | ``` 72 | 73 | 在示例项目的 resources 目录下创建 db 文件夹,将两个文件放入其中。 74 | 75 | ## 添加依赖 76 | 77 | 添加相关依赖包,pom.xml 中的相关依赖内容如下: 78 | 79 | ```xml 80 | 81 | 82 | com.h2database 83 | h2 84 | runtime 85 | 86 | 87 | ``` 88 | 89 | 然后配置文件如下: 90 | 91 | ```yml 92 | # DataSource Config 93 | spring: 94 | datasource: 95 | driver-class-name: org.h2.Driver 96 | schema: classpath:db/schema-h2.sql 97 | data: classpath:db/data-h2.sql 98 | url: jdbc:h2:mem:test 99 | username: root 100 | password: test 101 | 102 | # Logger Config 103 | logging: 104 | level: 105 | wx: debug 106 | ``` 107 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 4 章 Spring 安全/4.4 了解你的用户.md: -------------------------------------------------------------------------------- 1 | ## 4.4 了解你的用户 2 | 3 | 通常,仅仅知道用户已经登录是不够的。通常重要的是要知道他们是谁,这样才能调整他们的体验。 4 | 5 | 例如,在 OrderController 中,当最初创建绑定到订单表单的订单对象时,如果能够用用户名和地址预先填充订单就更好了,这样他们就不必为每个订单重新输入它。也许更重要的是,在保存订单时,应该将订单实体与创建订单的用户关联起来。 6 | 7 | 为了在 Order 实体和 User 实体之间实现所需的连接,需要向 Order 类添加一个新属性: 8 | 9 | ```java 10 | @Data 11 | @Entity 12 | @Table(name="Taco_Order") 13 | public class Order implements Serializable { 14 | ... 15 | 16 | @ManyToOne 17 | private User user; 18 | 19 | ... 20 | } 21 | ``` 22 | 23 | 此属性上的 @ManyToOne 注解表明一个订单属于单个用户,相反,一个用户可能有多个订单。(因为使用的是 Lombok,所以不需要显式地定义属性的访问方法。) 24 | 25 | 在 OrderController 中,processOrder() 方法负责保存订单。需要对其进行修改,以确定经过身份验证的用户是谁,并调用 Order 对象上的 setUser() 以将 Order 与该用户连接起来。 26 | 27 | 有几种方法可以确定用户是谁。以下是一些最常见的方法: 28 | 29 | - 将主体对象注入控制器方法 30 | - 将身份验证对象注入控制器方法 31 | - 使用 SecurityContext 获取安全上下文 32 | - 使用 @AuthenticationPrincipal 注解的方法 33 | 34 | 例如,可以修改 processOrder() 来接受 java.security.Principal 作为参数。然后可以使用主体名从 UserRepository 查找用户: 35 | 36 | ```java 37 | @PostMapping 38 | public String processOrder(@Valid Order order, Errors errors, 39 | SessionStatus sessionStatus, 40 | Principal principal) { 41 | ... 42 | 43 | User user = userRepository.findByUsername(principal.getName()); 44 | order.setUser(user); 45 | 46 | ... 47 | } 48 | ``` 49 | 50 | 这可以很好地工作,但是它会将与安全性无关的代码与安全代码一起丢弃。可以通过修改 processOrder() 来减少一些特定于安全的代码,以接受 Authentication 对象作为参数而不是 Principal: 51 | 52 | ```java 53 | @PostMapping 54 | public String processOrder(@Valid Order order, Errors errors, 55 | SessionStatus sessionStatus, 56 | Authentication authentication) { 57 | ... 58 | 59 | User user = (User) authentication.getPrincipal(); 60 | order.setUser(user); 61 | 62 | ... 63 | } 64 | ``` 65 | 66 | 有了身份验证,可以调用 getPrincipal() 来获取主体对象,在本例中,该对象是一个用户。注意,getPrincipal() 返回一个 java.util.Object,因此需要将其转换为 User。 67 | 68 | 然而,也许最干净的解决方案是简单地接受 processOrder() 中的用户对象,但是使用 @AuthenticationPrincipal 对其进行注解,以便它成为身份验证的主体: 69 | 70 | ```java 71 | @PostMapping 72 | public String processOrder(@Valid Order order, Errors errors, 73 | SessionStatus sessionStatus, 74 | @AuthenticationPrincipal User user) { 75 | if (errors.hasErrors()) { 76 | return "orderForm"; 77 | } 78 | 79 | order.setUser(user); 80 | 81 | orderRepo.save(order); 82 | sessionStatus.setComplete(); 83 | 84 | return "redirect:/"; 85 | } 86 | ``` 87 | 88 | @AuthenticationPrincipal 的优点在于它不需要强制转换(与身份验证一样),并且将特定于安全性的代码限制为注释本身。当在 processOrder() 中获得 User 对象时,它已经准备好被使用并分配给订单了。 89 | 90 | 还有一种方法可以识别通过身份验证的用户是谁,尽管这种方法有点麻烦,因为它包含了大量与安全相关的代码。你可以从安全上下文获取一个认证对象,然后像这样请求它的主体: 91 | 92 | ```java 93 | Authentication authentication = 94 | SecurityContextHolder.getContext().getAuthentication(); 95 | User user = (User) authentication.getPrincipal(); 96 | ``` 97 | 98 | 尽管这个代码段充满了与安全相关的代码,但是它与所描述的其他方法相比有一个优点:它可以在应用程序的任何地方使用,而不仅仅是在控制器的处理程序方法中,这使得它适合在较低级别的代码中使用。 99 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 1 章 Spring 入门/1.1 什么是 Spring.md: -------------------------------------------------------------------------------- 1 | ## 1.1 什么是 Spring? 2 | 3 | 我知道你可能很想开始编写 Spring 应用程序,我向你保证,在本章结束之前,你将开发一个简单的应用程序。但是首先,我得介绍一些 Spring 的基本概念,以帮助你了解 Spring 的变化。 4 | 5 | 任何不平凡的应用程序都由许多组件组成,每个组件负责自己的在整体应用程序中的那部分功能,并与其他应用程序元素协调以完成工作。在运行应用程序时,需要以某种方式创建这些组件并相互引用。 6 | 7 | Spring 的核心是一个 _容器_,通常称为 _Spring 应用程序上下文_,用于创建和管理应用程序组件。这些组件(或 bean)在 Spring 应用程序上下文中连接在一起以构成一个完整的应用程序,就像将砖、灰浆、木材、钉子、管道和电线绑在一起以组成房屋。 8 | 9 | 将 bean 连接在一起的行为是基于一种称为 _依赖注入_(DI)的模式。依赖项注入的应用程序不是由组件自身创建和维护它们依赖的其他 bean 的生命周期,而是依赖于单独的实体(容器)来创建和维护所有组件,并将这些组件注入需要它们的 bean。通常通过构造函数参数或属性访问器方法完成此操作。 10 | 11 | 例如,假设在应用程序的许多组件中,要处理两个组件:inventory service(用于获取库存级别)和 product service(用于提供基本产品信息)。product service 取决于 inventory service,以便能够提供有关产品的完整信息。图 1.1 说明了这些 bean 与 Spring 应用程序上下文之间的关系。 12 | 13 | 除了其核心容器之外,Spring 和完整的相关库产品组合还提供 Web 框架、各种数据持久性选项、安全框架与其他系统的集成、运行时监视、微服务支持、响应式编程模型以及许多其他功能,应用于现代应用程序开发。 14 | 15 | 从历史上看,引导 Spring 应用程序上下文将 bean 连接在一起的方式是使用一个或多个 XML 文件,这些文件描述了组件及其与其他组件的关系。例如,以下 XML 声明两个 bean,一个 `InventoryService` bean 和一个 `ProductService` bean,然后通过构造函数参数将 `InventoryService` bean 注入到 `ProductService` 中: 16 | 17 | ![图1-1 通过Spring上下文管理应用组件和注入](E:\Document\spring-in-action-v5-translate\第一部分 Spring 基础\第一章 Spring 入门\图 1-1 通过 Spring 上下文管理应用组件和注入.png) 18 | 19 | **图 1-1 通过 Spring 应用程序上下文管理应用程序组件并将它们相互注入** 20 | 21 | ```xml 22 | 23 | 24 | 25 | 26 | ``` 27 | 28 | 但是,在最新版本的 Spring 中,基于 Java 的配置更为常见。以下基于 Java 的配置类等效于 XML 配置: 29 | 30 | ```java 31 | @Configuration 32 | public class ServiceConfiguration { 33 | @Bean 34 | public InventoryService inventoryService() { 35 | return new InventoryService(); 36 | } 37 | 38 | @Bean 39 | public ProductService productService() { 40 | return new ProductService(inventoryService()); 41 | } 42 | } 43 | ``` 44 | 45 | `@Configuration` 注释向 Spring 表明这是一个配置类,它将为 Spring 应用程序上下文提供 beans。配置的类方法带有 `@Bean` 注释,指示它们返回的对象应作为 beans 添加到应用程序上下文中(默认情况下,它们各自的 bean IDs 将与定义它们的方法的名称相同)。 46 | 47 | 与基于 XML 的配置相比,基于 Java 的配置具有多个优点,包括更高的类型安全性和改进的可重构性。即使这样,仅当 Spring 无法自动配置组件时,才需要使用 Java 或 XML 进行显式配置。 48 | 49 | 自动配置起源于 Spring 技术,即 _自动装配_ 和 _组件扫描_。借助组件扫描,Spring 可以自动从应用程序的类路径中发现组件,并将其创建为 Spring 应用程序上下文中的 bean。通过自动装配,Spring 会自动将组件与它们依赖的其他 bean 一起注入。 50 | 51 | 最近,随着 Spring Boot 的推出,自动配置的优势已经远远超出了组件扫描和自动装配。Spring Boot 是 Spring 框架的扩展,它提供了多项生产力增强功能。这些增强功能中最著名的就是 _自动配置_,在这种配置中,Spring Boot 可以根据类路径中的条目、环境变量和其他因素,合理地猜测需要配置哪些组件,并将它们连接在一起。 52 | 53 | 这里想要展示一些演示自动配置的示例代码,但是并没有这样的代码,自动配置就如同风一样,可以看到它的效果,但是没有代码可以展示。我可以说 “看!这是自动配置的示例!” 事情发生、组件启用并且提供了功能,而无需编写代码。缺少代码是自动配置必不可少的要素,这使它如此出色。 54 | 55 | Spring Boot 自动配置大大减少了构建应用程序所需的显式配置(无论是 XML 还是 Java)的数量。实际上,当完成本章中的示例时,将拥有一个正在运行的 Spring 应用程序,该应用程序仅包含一行 Spring 配置代码! 56 | 57 | Spring Boot 极大地增强了 Spring 开发的能力,很难想象没有它如何开发 Spring 应用程序。因此,本书将 Spring 和 Spring Boot 视为一模一样。我们将尽可能使用 Spring Boot,并仅在必要时使用显式配置。而且,由于 Spring XML 配置是使用 Spring 的老派方式,因此我们将主要关注基于 Java 的 Spring 配置。 58 | 59 | 但是有这些功能就足够了,本书的标题包括 _实战_ 这个词语,因此让我们动起来,立马开始使用 Spring 编写第一个应用程序。 60 | -------------------------------------------------------------------------------- /00~特性与生态/生态圈/Spring Integration.md: -------------------------------------------------------------------------------- 1 | # Spring Integration 2 | 3 | Spring Integration 提供了 Spring 编程模型的扩展用来支持企业集成模式(Enterprise Integration Patterns),是对 Spring Messaging 的扩展。它提出了不少新的概念,包括消息路由 MessageRoute、消息分发 MessageDispatcher、消息过滤 Filter、消息转换 Transformer、消息聚合 Aggregator、消息分割 Splitter 等等。同时还提供了 MessageChannel 和 MessageHandler 的实现,分别包括 DirectChannel、ExecutorChannel、PublishSubscribeChannel 和 MessageFilter、ServiceActivatingHandler、MethodInvokingSplitter 等内容。 4 | 5 | # 消息处理 6 | 7 | 消息的分割: 8 | 9 | ![消息的分割](https://s2.ax1x.com/2019/10/19/KnP3wt.png) 10 | 11 | 消息的聚合: 12 | 13 | ![消息的聚合](https://s2.ax1x.com/2019/10/19/KnPUSg.png) 14 | 15 | 消息的过滤: 16 | 17 | ![消息的过滤](https://s2.ax1x.com/2019/10/19/KnPDwq.png) 18 | 19 | 消息的分发: 20 | 21 | ![消息的分发](https://s2.ax1x.com/2019/10/19/KnPrT0.png) 22 | 23 | # 简单实例 24 | 25 | ```java 26 | SubscribableChannel messageChannel =new DirectChannel(); // 1 27 | 28 | messageChannel.subscribe(msg-> { // 2 29 | System.out.println("receive: " +msg.getPayload()); 30 | }); 31 | 32 | messageChannel.send(MessageBuilder.withPayload("msgfrom alibaba").build()); // 3 33 | ``` 34 | 35 | 1. 构造一个可订阅的消息通道 `messageChannel`; 36 | 37 | 2. 使用 `MessageHandler` 去消费这个消息通道里的消息; 38 | 39 | 3. 发送一条消息到这个消息通道,消息最终被消息通道里的 `MessageHandler` 所消费。 40 | 41 | 最后控制台打印出: `receive: msg from alibaba`; 42 | 43 | `DirectChannel` 内部有个 `UnicastingDispatcher` 类型的消息分发器,会分发到对应的消息通道 `MessageChannel` 中,从名字也可以看出来,`UnicastingDispatcher` 是个单播的分发器,只能选择一个消息通道。那么如何选择呢? 内部提供了 `LoadBalancingStrategy` 负载均衡策略,默认只有轮询的实现,可以进行扩展。 44 | 45 | 我们对上段代码做一点修改,使用多个 `MessageHandler` 去处理消息: 46 | 47 | ```java 48 | SubscribableChannel messageChannel = new DirectChannel(); 49 | 50 | messageChannel.subscribe(msg -> { 51 | System.out.println("receive1: " + msg.getPayload()); 52 | }); 53 | 54 | messageChannel.subscribe(msg -> { 55 | System.out.println("receive2: " + msg.getPayload()); 56 | }); 57 | 58 | messageChannel.send(MessageBuilder.withPayload("msg from alibaba").build()); 59 | messageChannel.send(MessageBuilder.withPayload("msg from alibaba").build()); 60 | ``` 61 | 62 | 由于 `DirectChannel` 内部的消息分发器是 `UnicastingDispatcher` 单播的方式,并且采用轮询的负载均衡策略,所以这里两次的消费分别对应这两个 `MessageHandler`。控制台打印出: 63 | 64 | ```java 65 | receive1: msg from alibaba 66 | receive2: msg from alibaba 67 | ``` 68 | 69 | 既然存在单播的消息分发器 `UnicastingDispatcher`,必然也会存在广播的消息分发器,那就是 `BroadcastingDispatcher`,它被 `PublishSubscribeChannel` 这个消息通道所使用。广播消息分发器会把消息分发给所有的 `MessageHandler`: 70 | 71 | ```java 72 | SubscribableChannel messageChannel = new PublishSubscribeChannel(); 73 | 74 | messageChannel.subscribe(msg -> { 75 | System.out.println("receive1: " + msg.getPayload()); 76 | }); 77 | 78 | messageChannel.subscribe(msg -> { 79 | System.out.println("receive2: " + msg.getPayload()); 80 | }); 81 | 82 | messageChannel.send(MessageBuilder.withPayload("msg from alibaba").build()); 83 | messageChannel.send(MessageBuilder.withPayload("msg from alibaba").build()); 84 | ``` 85 | 86 | 发送两个消息,都被所有的 `MessageHandler` 所消费。控制台打印: 87 | 88 | ```java 89 | receive1: msg from alibaba 90 | receive2: msg from alibaba 91 | receive1: msg from alibaba 92 | receive2: msg from alibaba 93 | ``` 94 | -------------------------------------------------------------------------------- /02~数据库/01~Spring JDBC/Spring 数据库连接池.md: -------------------------------------------------------------------------------- 1 | # Spring 数据库连接池 2 | 3 | 由于 Spring Boot 的自动化配置机制,大部分对于数据源的配置都可以通过配置参数的方式去改变。只有一些特殊情况,比如:更换默认数据源,多数据源共存等情况才需要去修改覆盖初始化的 Bean 内容。在 Spring Boot 自动化配置中,对于数据源的配置可以分为两类: 4 | 5 | - 通用配置:以`spring.datasource.*`的形式存在,主要是对一些即使使用不同数据源也都需要配置的一些常规内容。比如:数据库链接地址、用户名、密码等。这里就不做过多说明了,通常就这些配置: 6 | 7 | ```java 8 | spring.datasource.url=jdbc:mysql://localhost:3306/test 9 | spring.datasource.username=root 10 | spring.datasource.password=123456 11 | spring.datasource.driver-class-name=com.mysql.jdbc.Driver 12 | ``` 13 | 14 | - 数据源连接池配置:以`spring.datasource.<数据源名称>.*`的形式存在,比如:Hikari 的配置参数就是`spring.datasource.hikari.*`形式。下面这个是我们最常用的几个配置项及对应说明: 15 | 16 | ```java 17 | spring.datasource.hikari.minimum-idle=10 18 | spring.datasource.hikari.maximum-pool-size=20 19 | spring.datasource.hikari.idle-timeout=500000 20 | spring.datasource.hikari.max-lifetime=540000 21 | spring.datasource.hikari.connection-timeout=60000 22 | spring.datasource.hikari.connection-test-query=SELECT 1 23 | ``` 24 | 25 | 这些配置的含义: 26 | 27 | - `spring.datasource.hikari.minimum-idle`: 最小空闲连接,默认值 10,小于 0 或大于 maximum-pool-size,都会重置为 maximum-pool-size 28 | - `spring.datasource.hikari.maximum-pool-size`: 最大连接数,小于等于 0 会被重置为默认值 10;大于零小于 1 会被重置为 minimum-idle 的值 29 | - `spring.datasource.hikari.idle-timeout`: 空闲连接超时时间,默认值 600000(10 分钟),大于等于 max-lifetime 且 max-lifetime>0,会被重置为 0;不等于 0 且小于 10 秒,会被重置为 10 秒。 30 | - `spring.datasource.hikari.max-lifetime`: 连接最大存活时间,不等于 0 且小于 30 秒,会被重置为默认值 30 分钟.设置应该比 mysql 设置的超时时间短 31 | - `spring.datasource.hikari.connection-timeout`: 连接超时时间:毫秒,小于 250 毫秒,否则被重置为默认值 30 秒 32 | - `spring.datasource.hikari.connection-test-query`: 用于测试连接是否可用的查询语句 33 | 34 | ## 手动创建连接池 35 | 36 | 现在,如果我们坚持使用 Spring Boot 的自动 DataSource 配置,并在当前状态下运行我们的项目,它就会像预期的那样工作。Spring Boot 将为我们完成所有繁重的基础架构工作。这包括创建一个 H2 DataSource 实现,它将由 HikariCP、Apache Tomcat 或 Commons DBCP 自动处理,并设置一个内存数据库实例。 37 | 38 | 此外,我们甚至不需要创建一个 application.properties 文件,因为 Spring Boot 也会提供一些默认的数据库设置。正如我们之前提到的,有时我们需要更高层次的定制,因此我们必须以编程的方式配置自己的 DataSource 实现。最简单的方法是定义一个 DataSource 工厂方法,并将其放置在用 @Configuration 注解的类中。 39 | 40 | ```java 41 | @Configuration 42 | public class DataSourceConfig { 43 | 44 | @Bean 45 | public DataSource getDataSource() { 46 | DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(); 47 | dataSourceBuilder.driverClassName("org.h2.Driver"); 48 | dataSourceBuilder.url("jdbc:h2:mem:test"); 49 | dataSourceBuilder.username("SA"); 50 | dataSourceBuilder.password(""); 51 | return dataSourceBuilder.build(); 52 | } 53 | } 54 | ``` 55 | 56 | 当然,我们也可以局部地应用 application.properties 中定义的属性: 57 | 58 | ```java 59 | @Bean 60 | public DataSource getDataSource() { 61 | DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(); 62 | dataSourceBuilder.username("SA"); 63 | dataSourceBuilder.password(""); 64 | return dataSourceBuilder.build(); 65 | } 66 | 67 | ``` 68 | 69 | 并在 application.properties 文件中额外指定一些: 70 | 71 | ```xml 72 | spring.datasource.url=jdbc:h2:mem:test 73 | spring.datasource.driver-class-name=org.h2.Driver 74 | ``` 75 | 76 | # Links 77 | 78 | - https://mp.weixin.qq.com/s/cgR-KVs1UKEM-xTEjIWKQg 79 | -------------------------------------------------------------------------------- /04~工程实践/测试/JUnit 5/测试用例.md: -------------------------------------------------------------------------------- 1 | # 测试用例 2 | 3 | JUnit4 和 JUnit5 在测试编码风格上没有太大变化,这是其生命周期方法的样本测试。 4 | 5 | ```java 6 | public class AppTest { 7 | 8 | @BeforeAll 9 | static void setup() { 10 | System.out.println("@BeforeAll executed"); 11 | } 12 | 13 | @BeforeEach 14 | void setupThis() { 15 | System.out.println("@BeforeEach executed"); 16 | } 17 | 18 | @Tag("DEV") 19 | @Test 20 | void testCalcOne() { 21 | System.out.println("======TEST ONE EXECUTED======="); 22 | Assertions.assertEquals(4, Calculator.add(2, 2)); 23 | } 24 | 25 | @Tag("PROD") 26 | @Disabled 27 | @Test 28 | void testCalcTwo() { 29 | System.out.println("======TEST TWO EXECUTED======="); 30 | Assertions.assertEquals(6, Calculator.add(2, 4)); 31 | } 32 | 33 | @AfterEach 34 | void tearThis() { 35 | System.out.println("@AfterEach executed"); 36 | } 37 | 38 | @AfterAll 39 | static void tear() { 40 | System.out.println("@AfterAll executed"); 41 | } 42 | } 43 | ``` 44 | 45 | ## 测试套件 46 | 47 | 使用 JUnit5 的测试套件,可以将测试扩展到多个测试类和不同的软件包。JUnit5 提供了两个注解:@SelectPackages 和 @SelectClasses 来创建测试套件。要执行测试套件,可以是使用 `@RunWith(JUnitPlatform.class)` 48 | 49 | ```java 50 | @RunWith(JUnitPlatform.class) 51 | @SelectPackages("test.junit5.examples") 52 | public class JUnit5TestSuiteExample {} 53 | ``` 54 | 55 | 另外,你也可以使用以下注解来过滤测试包、类甚至测试方法。 56 | 57 | - @IncludePackages 和 `@ExcludePackages` 来过滤包 58 | - @IncludeClassNamePatterns 和 `@ExcludeClassNamePatterns` 过滤测试类 59 | - @IncludeTags 和 `@ExcludeTags` 过滤测试方法 60 | 61 | ```java 62 | @RunWith(JUnitPlatform.class) 63 | @SelectPackages("test.junit5.examples") 64 | @IncludePackages("test.junit5.examples.packageC") 65 | @ExcludeTags("PROD") 66 | public class JUnit5TestSuiteExample { 67 | // ... 68 | } 69 | ``` 70 | 71 | ## 断言 72 | 73 | 断言有助于使用测试用例的实际输出验证预期输出。为了保持简单,所有 JUnit Jupiter 断言是 org.junit.jupiter.Assertions 类中的静态方法,例如 assertEquals(),assertNotEquals()。 74 | 75 | ```java 76 | void testCase() 77 | { 78 | //Test will pass 79 | Assertions.assertNotEquals(3, Calculator.add(2, 2)); 80 | //Test will fail 81 | Assertions.assertNotEquals(4, Calculator.add(2, 2), "Calculator.add(2, 2) test failed"); 82 | //Test will fail 83 | Supplier messageSupplier = ()-> "Calculator.add(2, 2) test failed"; 84 | Assertions.assertNotEquals(4, Calculator.add(2, 2), messageSupplier); 85 | } 86 | ``` 87 | 88 | ## 假设 89 | 90 | Assumptions 类提供了静态方法来支持基于假设的条件测试执行。失败的假设导致测试被中止。无论何时继续执行给定的测试方法没有意义,通常使用假设。在测试报告中,这些测试将被标记为已通过。JUnit 的 Jupiter 假设类有两个这样的方法:assumeFalse(),assumeTrue()。 91 | 92 | ```java 93 | public class AppTest { 94 | 95 | @Test 96 | void testOnDev() { 97 | System.setProperty("ENV", "DEV"); 98 | Assumptions.assumeTrue( 99 | "DEV".equals(System.getProperty("ENV")), 100 | AppTest::message 101 | ); 102 | } 103 | 104 | @Test 105 | void testOnProd() { 106 | System.setProperty("ENV", "PROD"); 107 | Assumptions.assumeFalse("DEV".equals(System.getProperty("ENV"))); 108 | } 109 | 110 | private static String message() { 111 | return "TEST Execution Failed::"; 112 | } 113 | } 114 | ``` 115 | -------------------------------------------------------------------------------- /02~数据库/03~ORM/99~参考资料/2021-Java ORM 概念总结.md: -------------------------------------------------------------------------------- 1 | ## 什么是 ORM? 2 | 3 | ORM,即对象关系映射(**O**bject **R**elational **M**apping)模式 4 | 5 | 在初学 Java 的时候,都是使用 JDBC 方式连接数据库。之后逐步使用 EclipseLink、iBATIS(半自动) 和 Hibernate(全自动)等开源 ORM 框架。 6 | 7 | JDBC 的使用流程为: 8 | 9 | 1. 加载数据库驱动 (JDBC Driver) 10 | 2. 创建数据库链接 11 | 3. 创建编译对象(预编译对象 PrepareStatement) 12 | 4. 设置入参执行 SQL 13 | 5. 返回结果集 (resultSet) 14 | 15 | ![image-20230319134837722](https://ngte-superbed.oss-cn-beijing.aliyuncs.com/item/20230514224404.png) 16 | 17 | JDBC 使用流程 18 | 19 | ![image-20230312192006619](https://ngte-superbed.oss-cn-beijing.aliyuncs.com/item/20230514224423.png) 20 | 21 | 使用 JDBC 连接数据库 22 | 23 | **ORM 框架主要可以解决面向对象与关系数据库之间互不匹配的问题,即用于处理面向对象编程语言中不同类型系统间的数据转换** 24 | 25 | ### [#](https://www.bantanger.fun/pages/5f5e77/#jdbc-的缺点)JDBC 的缺点 26 | 27 | - 硬编码 --> 反射,封装,代理 28 | - 频繁释放数据库连接资源 --> 连接池 29 | 30 | ![image-20230319140832760](https://cdn.staticaly.com/gh/BanTanger/image-hosting@master/00.%E6%80%BB%E7%BB%93-assets/202303191408128.png) 31 | 32 | ### [#](https://www.bantanger.fun/pages/5f5e77/#为什么会出现-orm-思想)为什么会出现 ORM 思想 33 | 34 | 先从项目中数据流存储形式这个角度说起.简单拿 MVC 这种分层模式来说,Model 作为数据承载实体. 在用户界面层和业务逻辑层之间数据实现面向对象 OO 形式传递. 当我们需要通过 Control 层分发请求把数据持久化时我们会发现. 内存中的面向对象的 **OO** 如何**持久化成**关系型数据中存储**一条实际数据记录**呢? 35 | 36 | 面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的。两者之间是不匹配的。而 ORM 作为项目中间件形式实现数据在不同场景下数据关系映射。**对象关系映射(Object Relational Mapping,简称 ORM)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术**,ORM 就是这样而来的. 37 | 38 | ORM 是连接数据库的桥梁,只要提供了持久化类与表的映射关系,ORM 框架在运行是就能**参照映射文件的信息将对象持久化到数据库中** 39 | 40 | 它的作用就是在关系型数据库和业务实体对象之间做**一层映射**,这样在具体操作业务对象时,就不需要和复杂的 SQL 语句打交道,而只需简单操作对象的属性和方法 41 | 42 | ![在这里插入图片描述](https://cdn.staticaly.com/gh/BanTanger/image-hosting@master/00.%E6%80%BB%E7%BB%93-assets/202303121549216.png) 43 | 44 | ### [#](https://www.bantanger.fun/pages/5f5e77/#优缺点)优缺点 45 | 46 | 优点: 47 | 48 | 1. 隐藏数据访问细节,封闭的进行交互。 49 | 2. 构造固化数据结构简单。 50 | 51 | 缺点: 52 | 53 | 1. 自动化意味着映射和关联管理,代价是牺牲性能(现在 ORM 采用各种方法减轻这块,LazyLoad Cache) 54 | 2. 面对复杂查询,ORM 力不从心 55 | 56 | ### [#](https://www.bantanger.fun/pages/5f5e77/#orm-框架和-mybatis-的区别)ORM 框架和 MyBatis 的区别 57 | 58 | ORM 框架:将数据库表一行对应一个类实例。对类的操作会影响到数据库 59 | 60 | MyBatis:将查询语句所得到的 ResultSet 映射成类,在使用数据库时需要写 SQL 语句,对类的修改不会影响到数据库 61 | 62 | 参考: 63 | 64 | [什么是 ORM,设计架构(opens new window)](https://zhuanlan.zhihu.com/p/486987053) 65 | 66 | ## [#](https://www.bantanger.fun/pages/5f5e77/#orm-框架设计)ORM 框架设计 67 | 68 | ORM 框架主要通过**参数映射、SQL 解析和执行,以及结果映射**的方式对数据库进行操作 69 | 70 | ![image-20230312162050029](https://cdn.staticaly.com/gh/BanTanger/image-hosting@master/00.%E6%80%BB%E7%BB%93-assets/202303121620277.png) 71 | 72 | ORM 框架实现的核心类包括加载配置文件、解析 XML 文件、获取数据库 Session、操作数据库以及返回结果 73 | 74 | ![image-20230312165147079](https://cdn.staticaly.com/gh/BanTanger/image-hosting@master/00.%E6%80%BB%E7%BB%93-assets/202303121651695.png) 75 | 76 | - SqlSession 时对数据库进行定义和处理的类,包括常用的方法,如 selectOne、selectList 等 77 | - DefaultSqlSessionFactory 对数据库配置的开启绘画的工厂处理类,这里的工厂会操作 DefaultSqlSession 78 | - SqlSessionFactoryBuilder 是对数据库进行操作的核心类,包括处理工厂、解析解析 和获取会话 79 | 80 | ## [#](https://www.bantanger.fun/pages/5f5e77/#如果让你实现一个-mybatis-应该怎么设计)如果让你实现一个 MyBatis ,应该怎么设计? 81 | 82 | ![手写MyBatis-第 3 页.drawio](https://cdn.staticaly.com/gh/BanTanger/image-hosting@master/00.%E6%80%BB%E7%BB%93-assets/202303211639742.png) 83 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/02~集成 Spring/第 7 章 调用 REST 服务/7.2 使用 Traverson 引导 REST API.md: -------------------------------------------------------------------------------- 1 | ## 7.2 使用 Traverson 引导 REST API 2 | 3 | Traverson 附带了 Spring Data HATEOAS,作为在 Spring 应用程序中使用超媒体 API 的开箱即用解决方案。这个基于 Java 的库的灵感来自于同名的类似的 JavaScript 库(https://github.com/traverson)。 4 | 5 | 你可能已经注意到 Traverson 的名字听起来有点像 “traverse on”,这是描述它用法的好方式。在本节中,将通过遍历关系名称上的 API 来调用 API。 6 | 7 | 要使用 Traverson,首先需要实例化一个 Traverson 对象和一个 API 的基础 URI: 8 | 9 | ```java 10 | Traverson traverson = new Traverson( 11 | URI.create("http://localhost:8080/api"), MediaType.HAL_JSON); 12 | ``` 13 | 14 | 这里将 Traverson 指向 Taco Cloud 的基本 URL(在本地运行),这是需要给 Traverson 的唯一 URL。从这里开始,将通过链接关系名称来引导 API。还将指定 API 将生成带有 HAL 风格的超链接的 JSON 响应,以便 Traverson 知道如何解析传入的资源数据。与 RestTemplate 一样,可以选择在使用 Traverson 对象之前实例化它,或者将它声明为一个 bean,以便在需要的地方注入它。 15 | 16 | 有了 Traverson 对象,可以通过以下链接开始使用 API。例如,假设想检索所有 Ingredient 的列表。从第 6.3.1 节了解到,Ingredient 链接有一个链接到配料资源的 href 属性,需要点击这个链接: 17 | 18 | ```java 19 | ParameterizedTypeReference> ingredientType = 20 | new ParameterizedTypeReference>() {}; 21 | 22 | Resources ingredientRes = 23 | traverson.follow("ingredients").toObject(ingredientType); 24 | 25 | Collection ingredients = ingredientRes.getContent(); 26 | ``` 27 | 28 | 通过调用 Traverson 对象上的 follow() 方法,可以引导到链接关系名称为 ingredients 的资源。现在客户端已经引导到 ingredients,需要通过调用 toObject() 来提取该资源的内容。 29 | 30 | toObject() 方法要求你告诉它要将数据读入哪种对象。考虑到需要将其作为 Resources 对象读入,而 Java 类型擦除机制使让其为泛型提供类型信息变得困难,因此这可能有点棘手。但是创建一个 ParameterizedTypeReference 有助于实现这一点。 31 | 32 | 打个比方,假设这不是一个 REST API,而是一个网站的主页。设想这是在浏览器中查看的主页,而不是 REST 客户端代码。在页面上看到一个链接,上面写着 Ingredient,然后点击这个链接。当到达下一页时,将读取该页,这类似于 Traverson 以 Resources 对象的形式提取内容。 33 | 34 | 现在让我们考虑一个更有趣的用例,假设想获取最近创建的 tacos,从 home 资源开始,可以引导到最近的 tacos 资源,像这样: 35 | 36 | ```java 37 | ParameterizeTypeReference> tacoType = 38 | new ParameterizedTypeReference>() {}; 39 | 40 | Resources tacoRes = 41 | traverson.follow("tacos").follow("recents").toObject(tacoType); 42 | 43 | Collection tacos = tacoRes.getContent(); 44 | ``` 45 | 46 | 在这里可以点击 Tacos 链接,然后点击 Recents 链接。这会将你带到你所感兴趣的资源,因此使用适当的 ParameterizedTypeReference 调用 toObject() 可以得到想要的结果。调用 follow() 方法可以通过列出跟随的关系名称来简化: 47 | 48 | ```java 49 | Resources tacoRes = 50 | traverson.follow("tacos", "recents").toObject(tacoType); 51 | ``` 52 | 53 | Traverson 可以轻松地引导启用了 HATEOAS 的 API 并调用其资源。但有一件事它没有提供任何方法来编写或删除这些 API。相比之下,RestTemplate 可以编写和删除资源,但不便于引导 API。 54 | 55 | 当需要同时引导 API 和更新或删除资源时,需要同时使用 RestTemplate 和 Traverson。Traverson 仍然可以用于引导到将创建新资源的链接。然后可以给 RestTemplate 一个链接来执行 POST、PUT、DELETE 或任何其他 HTTP 请求。 56 | 57 | 例如,假设想要向 Taco Cloud 菜单添加新的 Ingredient。下面的 addIngredient() 方法将 Traverson 和 RestTemplate 组合起来,向 API POST 一个新 Ingredient: 58 | 59 | ```java 60 | private Ingredient addIngredient(Ingredient ingredient) { 61 | String ingredientsUrl = traverson.follow("ingredients") 62 | .asLink().getHref(); 63 | 64 | return rest.postForObject(ingredientsUrl, 65 | ingredient, 66 | Ingredient.class); 67 | } 68 | ``` 69 | 70 | 在 follow Ingredient 链接之后,通过调用 asLink() 请求链接本身。在该链接中,通过调用 getHref() 请求链接的 URL。有了 URL,就有了在 RestTemplate 实例上调用 postForObject() 并保存新 Ingredient 所需的一切。 71 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.2 Reactor.md: -------------------------------------------------------------------------------- 1 | ## 10.2 Reactor 2 | 3 | 响应式编程需要我们从与命令式编程完全不同的角度去思考。响应式编程是通过直接建立一个用于数据流通的管道,而不是描述一系列需要进行的步骤。作为数据流通的管道,它可以被改变或以某种方式被使用。 4 | 5 | 例如,假设你想到利用一个人的名字,把它的所有字母变为大写,然后用它来创建一个问候语,最后将它打印出来。在命令式编程模型中,代码会是这个样子: 6 | 7 | ```java 8 | String name = "Craig"; 9 | String capitalName = name.toUpperCase(); 10 | String greeting = "Hello, " + capitalName + "!"; 11 | System.out.println(greeting); 12 | ``` 13 | 14 | 在命令式编程中,每一行为一步,一步接着一步,同时绝对是在同一个线程中。每一步都会阻塞直到完成才能进行下一步动作。 15 | 16 | 相反,函数式的响应式代码可以以下面这种方式达到目的: 17 | 18 | ```java 19 | Mono.just("Craig") 20 | .map(n -> n.toUpperCase()) 21 | .map(cn -> "Hello, " + cn + "!") 22 | .subscribe(System.out::println); 23 | ``` 24 | 25 | 不要太担心这个例子中的细节;我们很快将讨论所有关于 just()、map() 和 subscribe() 的操作。就目前而言,要了解的是,虽然响应式的例子似乎仍然遵循一步一步的模式,但是这确实是一个用于数据流的管道。在管道的每个阶段,数据被以某种方式修改了,但是不能知道哪一步操作被哪一个线程执行了的。它们可能在同一个线程也可能不是。 26 | 27 | 例子中的 Mono 是 Reactor 的两个核心类型之一,另一个是 Flux。两者都是响应式流的 Publisher 的实现。Flux 表示零个、一个或多个(可能是无限个)数据项的管道。Mono 特定用于已知的数据返回项不多于一个的响应式类型。 28 | 29 | > Reactor 与 RxJava 30 | > 31 | > 如果你已经熟悉 RxJava 或 ReactiveX,你可能会认为 Mono 和 Flux 听起来很像 Observable 和 Single。事实上,它们在语义上近似相等,甚至提供许多相同的操作。 32 | > 33 | > 尽管我们在本书中重点讨论 Reactor,但可以在 Reactor 和 RxJava 类型之间进行转换。此外,你将在下面的章节中看到 Spring 还可以使用 RxJava 的类型。 34 | 35 | 在前面的例子中实际上有三个 Mono。just() 操作创建了第一个。当 Mono 发出一个值时,该值将被赋予大写操作的 map() 并用于创建另一个 Mono。当第二个 Mono 发布其数据时,被赋予第二个 map() 操作来执行一些字符串连接,其结果用于创建第三个 Mono。最后,对 Mono 的 subscribe() 进行调用,接收数据并打印它。 36 | 37 | ### 10.2.1 图解响应式流 38 | 39 | 响应式流通常使用弹珠图(Marble Diagram)进行绘制。弹珠图最简单的形式就是,在最上面画出数据流经 Flux 或是 Mono 的时间线,在中间画出操作,在最下面画出 Flux 或是 Mono 结果的时间线。图 10.1 展示了 Flux 的弹珠图模板。正如你所看到的,当数据流通过原始的 Flux 后,它通过一些操作进行处理,通过数据流处理后产生一个新的 Flux。 40 | 41 | 图 10.2 展示了一个类似的弹珠图,但是是对于 Mono 而言的。正如你所看到的,关键的区别在于 Mono 会有零个或一个数据项,或一个错误。 42 | 43 | 在 10.3 节中,我们将探讨 Flux 和 Mono 支持的许多操作,将使用弹珠图来想象它们是如何工作的。 44 | 45 | ![图 10.1](E:\Document\spring-in-action-v5-translate\第三部分 响应式 Spring\第 10 章 Reactor 介绍\图 10.1.jpg) 46 | 47 | **图 10.1 Flux 基本流的可视化弹珠图** 48 | 49 | ![10.2](E:\Document\spring-in-action-v5-translate\第三部分 响应式 Spring\第 10 章 Reactor 介绍\10.2.jpg) 50 | 51 | **图 10.2 Flux 基本流的可视化弹珠图** 52 | 53 | ### 10.2.2 添加 Reactor 依赖 54 | 55 | 让我们开始使用 Reactor 吧,把一下依赖添加到项目构建中: 56 | 57 | ```xml 58 | 59 | io.projectreactor 60 | reactor-core 61 | 62 | ``` 63 | 64 | Reactor 还提供了测试支持。你会写在你的 Reactor 代码周围写很多的测试,所以你肯定会想把这个依赖添加到项目构建中: 65 | 66 | ```xml 67 | 68 | io.projectreactor 69 | reactor-test 70 | test 71 | 72 | ``` 73 | 74 | 我假定你要向 Spring Boot 项目中添加这些依赖,它可以为你处理的依赖管理,所以没有必要指定依赖的 元素。但是如果你想在非 Spring Boot 项目中使用 Reactor,那么你需要在构建中设置 Reactor 的 BOM(物料清单)。下面的依赖管理条目增加了 Reactor 的 Bismuth-RELEASE 到构建中: 75 | 76 | ```xml 77 | 78 | 79 | 80 | io.projectreactor 81 | reactor-bom 82 | Bismuth-RELEASE 83 | pom 84 | import 85 | 86 | 87 | 88 | ``` 89 | 90 | 现在,Reactor 在你的项目构建中了,可以使用 Mono 和 Flux 开始创建响应式管道了。对于本章的其余部分,我们将使用 Mono 和 Flux 提供的一些操作。 91 | -------------------------------------------------------------------------------- /10~Spring Cloud/Spring Cloud Alibaba/Sofa/SOFABoot/快速开始.md: -------------------------------------------------------------------------------- 1 | # 快速开始 2 | 3 | 在 Springboot init 脚手架生成的项目中,首先修改 maven 项目的配置文件 pom.xml,将 4 | 5 | ```xml 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | ${spring.boot.version} 10 | 11 | 12 | ``` 13 | 14 | 修改为: 15 | 16 | ```xml 17 | 18 | com.alipay.sofa 19 | sofaboot-dependencies 20 | ${sofa.boot.version} 21 | 22 | ``` 23 | 24 | 然后,添加 SOFABoot 健康检查扩展能力的依赖及 Web 依赖(方便查看健康检查结果): 25 | 26 | ```xml 27 | 28 | com.alipay.sofa 29 | healthcheck-sofa-boot-starter 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | ``` 37 | 38 | 最后,在工程的 application.properties 文件下添加 SOFABoot 工程常用的参数配置,其中 spring.application.name 是必需的参数,用于标示当前应用的名称;logging path 用于指定日志的输出目录。 39 | 40 | ```properties 41 | # Application Name 42 | spring.application.name=SOFABoot Demo 43 | # logging path 44 | logging.path=./logs 45 | ``` 46 | 47 | 直接运行我们的启动类 main 方法,日志如下: 48 | 49 | ```java 50 | ,---. ,-----. ,------. ,---. ,-----. ,--. 51 | ' .-' ' .-. ' | .---' / O \ | |) /_ ,---. ,---. ,-' '-. 52 | `. `-. | | | | | `--, | .-. | | .-. \ | .-. | | .-. | '-. .-' 53 | .-' | ' '-' ' | |` | | | | | '--' / ' '-' ' ' '-' ' | | 54 | `-----' `-----' `--' `--' `--' `------' `---' `---' `--' 55 | 56 | 57 | Spring Boot Version: 2.1.13.RELEASE (v2.1.13.RELEASE) 58 | SOFABoot Version: 3.4.6 (v3.4.6) 59 | Powered By Ant Group 60 | ... 61 | 2021-01-05 09:57:50.623 INFO 12720 --- [2)-172.17.160.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 7 ms 62 | ``` 63 | 64 | ## 检查 65 | 66 | 直接浏览器访问 http://localhost:8080/actuator/versions 来查看当前 SOFABoot 中使用 Maven 插件生成的版本信息汇总。我们选取一个,如下: 67 | 68 | ```json 69 | { 70 | "GroupId": "com.alipay.sofa", 71 | "Doc-Url": "http://www.sofastack.tech/sofa-boot/docs/Home", 72 | "ArtifactId": "healthcheck-sofa-boot-starter", 73 | "Commit-Time": "2020-11-18T13:07:33+0800", 74 | "Commit-Id": "0e6f10b9f5f1c4c8070814691b8ef9cbff8a550d", 75 | "Version": "3.4.6", 76 | "Build-Time": "2020-11-23T13:49:02+0800" 77 | } 78 | ``` 79 | 80 | 可以通过在浏览器中输入 http://localhost:8080/actuator/readiness 查看应用 Readiness Check 的状况 81 | 82 | ```json 83 | { 84 | "status": "UP", 85 | "details": { 86 | "SOFABootReadinessHealthCheckInfo": { 87 | "status": "UP", 88 | "details": { 89 | "HealthChecker": { "sofaComponentHealthChecker": { "status": "UP" } } 90 | } 91 | }, 92 | "diskSpace": { 93 | "status": "UP", 94 | "details": { 95 | "total": 127083565056, 96 | "free": 69193203712, 97 | "threshold": 10485760 98 | } 99 | } 100 | } 101 | } 102 | ``` 103 | 104 | SOFABoot 也提供了日志的物理隔离: 105 | 106 | ```log 107 | ./logs 108 | ├── health-check 109 | │ ├── sofaboot-common-default.log 110 | │ └── sofaboot-common-error.log 111 | ├── infra 112 | │ ├── common-default.log 113 | │ └── common-error.log 114 | └── spring.log 115 | ``` 116 | -------------------------------------------------------------------------------- /03~Spring Security/README.md: -------------------------------------------------------------------------------- 1 | # Spring Security CheatSheet 2 | 3 | Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在 Spring 应用上下文中配置的 Bean,充分利用了 Spring IoC(Inversion of Control 控制反转),DI(Dependency Injection 依赖注入)和 AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。Spring Security 拥有以下特性: 4 | 5 | - 对身份验证和授权的全面且可扩展的支持 6 | - 防御会话固定、点击劫持,跨站请求伪造等攻击 7 | - 支持 Servlet API 集成 8 | - 支持与 Spring Web MVC 集成 9 | 10 | 目前 Spring Security 5 支持与以下技术进行集成: 11 | 12 | - HTTP basic access authentication 13 | - LDAP system 14 | - OpenID identity providers 15 | - JAAS API 16 | - CAS Server 17 | - ESB Platform 18 | - …… 19 | - Your own authentication system 20 | 21 | 在进入 Spring Security 正题之前,我们先来了解一下它的整体架构: 22 | 23 | ![Spring Security 结构](https://s3.ax1x.com/2021/02/24/yOxiJe.png) 24 | 25 | # 核心组件 26 | 27 | Spring Security 的核心处理过程如下: 28 | 29 | - 用户登陆,会被 AuthenticationProcessingFilter 拦截,调用 AuthenticationManager 的实现,而且 AuthenticationManager 会调用 ProviderManager 来获取用户验证信息(不同的 Provider 调用的服务不同,因为这些信息可以是在数据库上,可以是在 LDAP 服务器上,可以是 xml 配置文件上等),如果验证通过后会将用户的权限信息封装一个 User 放到 Spring 的全局缓存 SecurityContextHolder 中,以备后面访问资源时使用。 30 | 31 | - 访问资源(即授权管理),访问 url 时,会通过 AbstractSecurityInterceptor 拦截器拦截,其中会调用 FilterInvocationSecurityMetadataSource 的方法来获取被拦截 url 所需的全部权限,在调用授权管理器 AccessDecisionManager,这个授权管理器会通过 spring 的全局缓存 SecurityContextHolder 获取用户的权限信息,还会获取被拦截的 url 和被拦截 url 所需的全部权限,然后根据所配的策略(有:一票决定,一票否定,少数服从多数等),如果权限足够,则返回,权限不够则报错并调用权限不足页面。 32 | 33 | ## SecurityContextHolder,SecurityContext 和 Authentication 34 | 35 | 最基本的对象是 SecurityContextHolder,它是我们存储当前应用程序安全上下文的详细信息,其中包括当前使用应用程序的主体的详细信息。如当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限等。默认情况下,SecurityContextHolder 使用 ThreadLocal 来存储这些详细信息,这意味着 Security Context 始终可用于同一执行线程中的方法,即使 Security Context 未作为这些方法的参数显式传递。 36 | 37 | ## 获取当前用户的信息 38 | 39 | 因为身份信息与当前执行线程已绑定,所以可以使用以下代码块在应用程序中获取当前已验证用户的用户名: 40 | 41 | ```java 42 | Object principal = SecurityContextHolder.getContext() 43 | .getAuthentication().getPrincipal(); 44 | 45 | if (principal instanceof UserDetails) { 46 | String username = ((UserDetails)principal).getUsername(); 47 | } else { 48 | String username = principal.toString(); 49 | } 50 | ``` 51 | 52 | 调用 getContext() 返回的对象是 SecurityContext 接口的一个实例,对应 SecurityContext 接口定义如下: 53 | 54 | ```java 55 | // org/springframework/security/core/context/SecurityContext.java 56 | public interface SecurityContext extends Serializable { 57 | Authentication getAuthentication(); 58 | void setAuthentication(Authentication authentication); 59 | } 60 | ``` 61 | 62 | ## Authentication 63 | 64 | 在 SecurityContext 接口中定义了 getAuthentication 和 setAuthentication 两个抽象方法,当调用 getAuthentication 方法后会返回一个 Authentication 类型的对象,这里的 Authentication 也是一个接口,它的定义如下: 65 | 66 | ```java 67 | // org/springframework/security/core/Authentication.java 68 | public interface Authentication extends Principal, Serializable { 69 | // 权限信息列表,默认是GrantedAuthority接口的一些实现类,通常是代表权限信息的一系列字符串。 70 | Collection getAuthorities(); 71 | // 密码信息,用户输入的密码字符串,在认证过后通常会被移除,用于保障安全。 72 | Object getCredentials(); 73 | Object getDetails(); 74 | // 最重要的身份信息,大部分情况下返回的是UserDetails接口的实现类,也是框架中的常用接口之一。 75 | Object getPrincipal(); 76 | boolean isAuthenticated(); 77 | void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException; 78 | } 79 | ``` 80 | 81 | 以上的 Authentication 接口是 spring-security-core jar 包中的接口,直接继承自 Principal 类,而 Principal 是位于 java.security 包中,由此可知 Authentication 是 spring security 中核心的接口。通过这个 Authentication 接口的实现类,我们可以得到用户拥有的权限信息列表,密码,用户细节信息,用户身份信息,认证信息等。 82 | -------------------------------------------------------------------------------- /02~数据库/01~连接池/HikariCP/README.md: -------------------------------------------------------------------------------- 1 | # HikariCP 2 | 3 | HikariCP 是一个高性能的 JDBC 连接池,基于 BoneCP 做了不少的改进和优化。它提供了很多特性,使得它成为 Java 应用程序中常用的连接池之一。以下是 HikariCP 的一些主要特性和优点: 4 | 5 | - 快速启动时间:HikariCP 具有非常快速的启动时间,这意味着当应用程序启动时,它可以迅速准备好数据库连接池,而不会造成长时间的延迟。 6 | - 高性能:HikariCP 以高效的方式管理连接池,减少了与数据库交互时的延迟。它的设计目标是最小化对数据库的负载,同时最大化性能。 7 | - 轻量级:HikariCP 是一个轻量级的连接池,jar 文件大小很小,且没有依赖其他库,这使得它在应用程序中的集成变得非常简单。 8 | - 自动化管理:HikariCP 可以自动地管理连接的生命周期,包括创建、释放、回收等操作,开发者无需手动干预连接的管理。 9 | - 高度可配置:HikariCP 提供了丰富的配置选项,允许开发者根据应用程序的需求进行调整和优化。这些配置选项包括连接池大小、最大连接数、最小空闲连接数、连接超时等。 10 | - 监控和诊断:HikariCP 内置了监控和诊断功能,可以实时地监视连接池的状态,包括活动连接数、空闲连接数、连接等待时间等指标。 11 | - 兼容性:HikariCP 与各种数据库和 JDBC 驱动程序兼容性良好,可以与 MySQL、PostgreSQL、Oracle 等主流数据库无缝集成。 12 | - 开源:HikariCP 是一个开源项目,代码托管在 GitHub 上,可以自由获取、使用和修改。 13 | 14 | # 快速开始 15 | 16 | 以下是一个简单的 Java 应用程序示例,演示如何在 Java 项目中使用 HikariCP 连接到数据库:首先,确保将 HikariCP 的 jar 文件(例如 hikaricp-xxx.jar)添加到你的项目中的 classpath 中。接下来,创建一个 HikariCP 连接池并使用它来获取数据库连接。 17 | 18 | ```java 19 | import com.zaxxer.hikari.HikariConfig; 20 | import com.zaxxer.hikari.HikariDataSource; 21 | import java.sql.Connection; 22 | import java.sql.PreparedStatement; 23 | import java.sql.ResultSet; 24 | import java.sql.SQLException; 25 | 26 | public class HikariCPExample { 27 | 28 | public static void main(String[] args) { 29 | // 创建Hikari配置对象 30 | HikariConfig config = new HikariConfig(); 31 | config.setJdbcUrl("jdbc:mysql://localhost:3306/mydatabase"); 32 | config.setUsername("username"); 33 | config.setPassword("password"); 34 | 35 | // 设置连接池属性 36 | config.setMaximumPoolSize(10); 37 | config.setMinimumIdle(5); 38 | config.setConnectionTimeout(30000); // 30秒超时 39 | 40 | // 创建Hikari数据源 41 | HikariDataSource dataSource = new HikariDataSource(config); 42 | 43 | Connection connection = null; 44 | PreparedStatement preparedStatement = null; 45 | ResultSet resultSet = null; 46 | 47 | try { 48 | // 从连接池获取连接 49 | connection = dataSource.getConnection(); 50 | 51 | // 执行SQL查询 52 | String sql = "SELECT * FROM my_table"; 53 | preparedStatement = connection.prepareStatement(sql); 54 | resultSet = preparedStatement.executeQuery(); 55 | 56 | // 处理查询结果 57 | while (resultSet.next()) { 58 | // 处理结果集 59 | String column1 = resultSet.getString("column1"); 60 | int column2 = resultSet.getInt("column2"); 61 | // 输出结果 62 | System.out.println("Column1: " + column1 + ", Column2: " + column2); 63 | } 64 | } catch (SQLException e) { 65 | e.printStackTrace(); 66 | } finally { 67 | // 关闭资源 68 | try { 69 | if (resultSet != null) resultSet.close(); 70 | if (preparedStatement != null) preparedStatement.close(); 71 | if (connection != null) connection.close(); 72 | } catch (SQLException e) { 73 | e.printStackTrace(); 74 | } 75 | } 76 | 77 | // 关闭数据源 78 | dataSource.close(); 79 | } 80 | } 81 | ``` 82 | 83 | 在这个示例中,我们首先创建了一个 HikariConfig 对象,设置了数据库的连接 URL、用户名和密码等信息,然后配置了连接池的一些属性,比如最大连接数、最小空闲连接数、连接超时等。 84 | 85 | 然后,我们通过创建 HikariDataSource 来实例化数据源,该数据源可以用于从连接池中获取数据库连接。接着,我们通过调用 getConnection()方法从连接池中获取一个连接,然后执行 SQL 查询,并处理查询结果。最后,我们在 finally 块中关闭了所有资源,包括连接、预处理语句和结果集,并调用数据源的 close() 方法关闭数据源。 86 | 87 | 这是一个简单的 HikariCP 入门示例,演示了如何在 Java 应用程序中使用 HikariCP 连接到数据库并执行 SQL 查询。 88 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/01~Spring 基础/第 2 章 开发 Web 应用程序/2.5 选择视图模板库.md: -------------------------------------------------------------------------------- 1 | ## 2.5 选择视图模板库 2 | 3 | 在大多数情况下,对视图模板库的选择取决于个人喜好。Spring 非常灵活,支持许多常见的模板选项。除了一些小的例外,所选择的模板库本身甚至不知道它是在 Spring 中工作的。 4 | 5 | 表 2.2 列出了 Spring Boot 自动配置支持的模板选项。表 2.2 支持的模板选项。 6 | 7 | | 模板 | Spring Boot starter 依赖 | 8 | | -------------------- | ------------------------------------ | 9 | | FreeMarker | spring-boot-starter-freemarker | 10 | | Groovy Templates | spring-boot-starter-groovy-templates | 11 | | JavaServer Page(JSP) | None (provided by Tomcat or Jetty) | 12 | | Mustache | spring-boot-starter-mustache | 13 | | Thymeleaf | spring-boot-starter-thymeleaf | 14 | 15 | 一般来说,可以选择想要的视图模板库,将其作为依赖项添加到构建中,然后开始在 /templates 目录中(在 Maven 或 Gradl 构建项目的 src/main/resources 目录下)编写模板。Spring Boot 将检测选择的模板库,并自动配置所需的组件来为 Spring MVC 控制器提供视图。 16 | 17 | 已经在 Taco Cloud 应用程序中用 Thymeleaf 实现了这一点。在第 1 章中,在初始化项目时选择了 Thymeleaf 复选框。这导致 Spring Boot 的 Thymeleaf starter 被包含在 pom.xml 文件中。当应用程序启动时,Spring Boot 自动配置会检测到 Thymeleaf 的存在,并自动配置 Thymeleaf bean。现在要做的就是开始在 /templates 中编写模板。 18 | 19 | 如果希望使用不同的模板库,只需在项目初始化时选择它,或者编辑现有的项目构建以包含新选择的模板库。 20 | 21 | 例如,假设想使用 Mustache 而不是 Thymeleaf。没有问题。只需访问项目 pom.xml 文件,将: 22 | 23 | ```xml 24 | 25 | org.springframework.boot 26 | spring-boot-starter-thymeleaf 27 | 28 | ``` 29 | 30 | 替换为: 31 | 32 | ```xml 33 | 34 | org.springframework.boot 35 | spring-boot-starter-mustache 36 | 37 | ``` 38 | 39 | 当然,需要确保使用 Mustache 语法而不是 Thymeleaf 标签来编写所有模板。Mustache 的使用细节(或选择的任何模板语言)不在这本书的范围之内,但为了让你知道会发生什么,这里有一个从 Mustache 模板摘录过来的片段,这个片段渲染了玉米饼设计表单的成分列表中的一个: 40 | 41 | ```html 42 |

Designate your wrap:

43 | {{#wrap}} 44 |
45 | 46 | {{name}}
47 |
48 | {{/wrap}} 49 | ``` 50 | 51 | 这是 Mustache 与第 2.1.3 节中的 Thymeleaf 片段的等价替换。`{{#wrap}}` 块(以 `{{/wrap}}` 结尾)迭代 request 属性中的一个集合,该集合的键为 wrap,并为每个项目呈现嵌入的 HTML。`{{id}}` 和 `{{name}}` 标记引用项目的 id 和 name 属性(应该是一个 Ingredient)。 52 | 53 | 在表 2.2 中请注意,JSP 在构建中不需要任何特殊的依赖关系。这是因为 servlet 容器本身(默认情况下是 Tomcat)实现了 JSP 规范,因此不需要进一步的依赖关系。 54 | 55 | 但是如果选择使用 JSP,就会遇到一个问题。事实证明,Java servlet 容器 —— 包括嵌入式 Tomcat 和 Jetty 容器 —— 通常在 /WEB-INF 下寻找 jsp。但是如果将应用程序构建为一个可执行的 JAR 文件,就没有办法满足这个需求。因此,如果将应用程序构建为 WAR 文件并将其部署在传统的 servlet 容器中,那么 JSP 只是一个选项。如果正在构建一个可执行的 JAR 文件,必须选择 Thymeleaf、FreeMarker 或表 2.2 中的其他选项之一。 56 | 57 | ### 2.5.1 缓存模板 58 | 59 | 默认情况下,模板在第一次使用时只解析一次,解析的结果被缓存以供后续使用。对于生产环境来说,这是一个很好的特性,因为它可以防止对每个请求进行冗余的模板解析,从而提高性能。 60 | 61 | 但是,在开发时,这个特性并不那么好。假设启动了应用程序并点击了玉米饼设计页面,并决定对其进行一些更改。当刷新 web 浏览器时,仍然会显示原始版本。查看更改的惟一方法是重新启动应用程序,这非常不方便。 62 | 63 | 幸运的是,有一种方法可以禁用缓存。只需将 templateappropriate 高速缓存属性设置为 false。表 2.3 列出了每个支持的模板库的缓存属性。 64 | 65 | **表 2.3 启用/禁用模板缓存的属性** 66 | 67 | | 模板 | 缓存使能属性 | 68 | | ---------------- | ---------------------------- | 69 | | Freemarker | spring.freemarker.cache | 70 | | Groovy Templates | spring.groovy.template.cache | 71 | | Mustache | spring.mustache.cache | 72 | | Thymeleaf | spring.thymeleaf.cache | 73 | 74 | 默认情况下,所有这些属性都设置为 true 以启用缓存。可以通过将其缓存属性设置为 false 来禁用所选模板引擎的缓存。例如,要禁用 Thymeleaf 缓存,请在 application.properties 中添加以下行: 75 | 76 | ```properties 77 | spring.thymeleaf.cache = false 78 | ``` 79 | 80 | 惟一的问题是,在将应用程序部署到生产环境之前,一定要删除这一行(或将其设置为 true)。一种选择是在 profile 文件中设置属性。(我们将在第 5 章讨论 profiles 文件。) 81 | 82 | 一个更简单的选择是使用 Spring Boot 的 DevTools,就像我们在第 1 章中选择的那样。在 DevTools 提供的许多有用的开发时帮助中,它将禁用所有模板库的缓存,但在部署应用程序时将禁用自身(从而重新启用模板缓存)。 83 | -------------------------------------------------------------------------------- /02~数据库/01~Spring JDBC/事务/编程式事务.md: -------------------------------------------------------------------------------- 1 | # 编程式事务 2 | 3 | ## PlatformTransactionManager 4 | 5 | ```java 6 | DefaultTransactionDefinition def = new DefaultTransactionDefinition(); 7 | // explicitly setting the transaction name is something that can only be done programmatically 8 | 9 | def.setName("SomeTxName"); 10 | def.setPropagationBehavior(PROPAGATION_REQUIRED); 11 | 12 | TransactionStatus status = txManager.getTransaction(def); 13 | try { 14 | // execute your business logic here 15 | } 16 | catch (MyException ex) { 17 | txManager.rollback(status); 18 | throw ex; 19 | } 20 | txManager.commit(status); 21 | ``` 22 | 23 | ## TransactionTemplate 24 | 25 | 如果使用 TransactionTemplate 的话,那么整个应用的代码必须包裹在一个事务的上下文中,如下所示。一般来说用匿名内部类来实现一个`TransactionCallback`中的`execute(..)`方法。 26 | 27 | ```java 28 | public class SimpleService implements Service { 29 | // single TransactionTemplate shared amongst all methods in this instance 30 | private final TransactionTemplate transactionTemplate; 31 | 32 | // use constructor-injection to supply the PlatformTransactionManager 33 | public SimpleService(PlatformTransactionManager transactionManager) { 34 | Assert.notNull( 35 | transactionManager, 36 | "The 'transactionManager' argument must not be null." 37 | ); 38 | this.transactionTemplate = new TransactionTemplate(transactionManager); 39 | } 40 | 41 | public Object someServiceMethod() { 42 | return transactionTemplate.execute( 43 | new TransactionCallback() { 44 | 45 | // the code in this method executes in a transactional context 46 | public Object doInTransaction(TransactionStatus status) { 47 | updateOperation1(); 48 | return resultOfUpdateOperation2(); 49 | } 50 | } 51 | ); 52 | } 53 | } 54 | ``` 55 | 56 | 如果你不希望有返回值,可以用如下: 57 | 58 | ```java 59 | transactionTemplate.execute(new TransactionCallbackWithoutResult() { 60 | protected void doInTransactionWithoutResult(TransactionStatus status) { 61 | updateOperation1(); 62 | updateOperation2(); 63 | } 64 | }); 65 | ``` 66 | 67 | 也可以手动地控制回滚: 68 | 69 | ```java 70 | transactionTemplate.execute(new TransactionCallbackWithoutResult() { 71 | protected void doInTransactionWithoutResult(TransactionStatus status) { 72 | try { 73 | updateOperation1(); 74 | updateOperation2(); 75 | } catch (SomeBusinessExeption ex) { 76 | status.setRollbackOnly(); 77 | } 78 | } 79 | }); 80 | ``` 81 | 82 | ### Settings 83 | 84 | ```java 85 | public class SimpleService implements Service { 86 | private final TransactionTemplate transactionTemplate; 87 | 88 | public SimpleService(PlatformTransactionManager transactionManager) { 89 | Assert.notNull( 90 | transactionManager, 91 | "The 'transactionManager' argument must not be null." 92 | ); 93 | this.transactionTemplate = new TransactionTemplate(transactionManager); 94 | 95 | // the transaction settings can be set here explicitly if so desired 96 | this.transactionTemplate.setIsolationLevel( 97 | ISOLATION_READ_UNCOMMITTED 98 | ); 99 | this.transactionTemplate.setTimeout(30); // 30 seconds 100 | // and so forth... 101 | } 102 | } 103 | ``` 104 | 105 | 或者直接配置在 XML 中: 106 | 107 | ```xml 108 | 110 | 111 | 112 | " 113 | ``` 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Contributors][contributors-shield]][contributors-url] 2 | [![Forks][forks-shield]][forks-url] 3 | [![Stargazers][stars-shield]][stars-url] 4 | [![Issues][issues-shield]][issues-url] 5 | [![license: CC BY-NC-SA 4.0](https://img.shields.io/badge/license-CC%20BY--NC--SA%204.0-lightgrey.svg)][license-url] 6 | 7 | 8 |
9 |

10 | 11 | Logo 12 | 13 | 14 |

15 | 在线阅读 >> 16 |
17 |
18 | 代码案例 19 | · 20 | 参考资料 21 | 22 |

23 |

24 | 25 | # Spring Series | Spring 微服务与云原生实战 26 | 27 | ![Spring 题图](https://s2.ax1x.com/2019/09/03/nFQaut.png) 28 | 29 | Spring 的设计目标是为我们提供一个一站式的轻量级应用开发平台,抽象了应用开发中遇到的共性问题。作为平台,它考虑到了企业应用资源的使用,比如数据的持久化、数据集成、事务处理、消息中间件、分布式式计算等高效可靠处理企业数据方法的技术抽象。开发过程中的共性问题,Spring 封装成了各种组件,而且 Spring 通过社区,形成了一个开放的生态系统,比如 Spring Security 就是来源于一个社区贡献。 30 | 31 | 使用 Spring 进行开发,对开发人员比较轻量,可以使用 POJO 和 Java Bean 的开发方式,使应用面向接口开发,充分支持了面向对象的设计方法。通过 IOC 容器减少了直接耦合,通过 AOP 以动态和非侵入的方式增加了服务的功能,为灵活选取不同的服务实现提供了基础,这也是 Spring 的核心。轻量级是相对于传统 J2EE 而言的,传统的 J2EE 开发,需要依赖按照 J2EE 规范实现的 J2EE 应用服务器,设计和实现时,需要遵循一系列的接口标准,这种开发方式耦合性高,使应用在可测试性和部署上都有影响,对技术的理解和要求相对较高。 32 | 33 | ![Spring](https://s2.ax1x.com/2019/09/03/nkYetx.png) 34 | 35 | 本篇是[《服务端开发实践与工程架构](https://ng-tech.icu/books/Backend-Notes/#/)》系列文章的一部分,关联的示例代码请参考 [java-snippets](https://github.com/Dev-Snippets/java-snippets),[spring-examples](https://github.com/BE-Kits/spring-snippets)。 36 | 37 | > 如果你是才入门的新手,那么建议首先阅读 [《Java Series]()》、[《数据结构与算法]()》。如果你已经有了一定的经验,想了解更多工程架构方面的知识,那么建议阅读 []()。如果你想寻求分布式系统的解决方案,那么建议阅读 []()。 38 | 39 | # About 40 | 41 | ## Copyright 42 | 43 | 44 | 45 | 笔者所有文章遵循 [知识共享 署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh),欢迎转载,尊重版权。如果觉得本系列对你有所帮助,欢迎给我家布丁买点狗粮(支付宝扫码)~ 46 | 47 | ## Copyright & More | 延伸阅读 48 | 49 | 您还可以前往 [NGTE Books](https://ng-tech.icu/books-gallery/) 主页浏览包含知识体系、编程语言、软件工程、模式与架构、Web 与大前端、服务端开发实践与工程架构、分布式基础架构、人工智能与深度学习、产品运营与创业等多类目的书籍列表: 50 | 51 | [![NGTE Books](https://s2.ax1x.com/2020/01/18/19uXtI.png)](https://ng-tech.icu/books-gallery/) 52 | 53 | ## Links 54 | 55 | - https://zhuanlan.zhihu.com/p/78104880 56 | - https://netfilx.github.io/spring-boot/ 57 | 58 | 59 | 60 | 61 | [contributors-shield]: https://img.shields.io/github/contributors/wx-chevalier/Spring-Notes.svg?style=flat-square 62 | [contributors-url]: https://github.com/wx-chevalier/Spring-Notes/graphs/contributors 63 | [forks-shield]: https://img.shields.io/github/forks/wx-chevalier/Spring-Notes.svg?style=flat-square 64 | [forks-url]: https://github.com/wx-chevalier/Spring-Notes/network/members 65 | [stars-shield]: https://img.shields.io/github/stars/wx-chevalier/Spring-Notes.svg?style=flat-square 66 | [stars-url]: https://github.com/wx-chevalier/Spring-Notes/stargazers 67 | [issues-shield]: https://img.shields.io/github/issues/wx-chevalier/Spring-Notes.svg?style=flat-square 68 | [issues-url]: https://github.com/wx-chevalier/Spring-Notes/issues 69 | [license-shield]: https://img.shields.io/github/license/wx-chevalier/Spring-Notes.svg?style=flat-square 70 | [license-url]: https://github.com/wx-chevalier/Spring-Notes/blob/master/LICENSE.txt 71 | -------------------------------------------------------------------------------- /02~数据库/02~MyBatis/MyBatis 基础使用/01~配置使用/2023~MyBatis 基础使用/06~流式查询.md: -------------------------------------------------------------------------------- 1 | # 流式查询 2 | 3 | 流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。流式查询的好处是能够降低内存使用。如果没有流式查询,我们想要从数据库取 1000 万条记录而又没有足够的内存时,就不得不分页查询,而分页查询效率取决于表设计,如果设计的不好,就无法执行高效的分页查询。因此流式查询是一个数据库访问框架必须具备的功能。 4 | 流式查询的过程当中,数据库连接是保持打开状态的,因此要注意的是:执行一个流式查询后,数据库访问框架就不负责关闭数据库连接了,需要应用在取完数据后自己关闭。 5 | 6 | # MyBatis 流式查询接口 7 | 8 | MyBatis 提供了一个叫 org.apache.ibatis.cursor.Cursor 的接口类用于流式查询,这个接口继承了 java.io.Closeable 和 java.lang.Iterable 接口,由此可知: 9 | 10 | - Cursor 是可关闭的; 11 | - Cursor 是可遍历的。 12 | 13 | 除此之外,Cursor 还提供了三个方法: 14 | 15 | - isOpen():用于在取数据之前判断 Cursor 对象是否是打开状态。只有当打开时 Cursor 才能取数据; 16 | - isConsumed():用于判断查询结果是否全部取完。 17 | - getCurrentIndex():返回已经获取了多少条数据 18 | 19 | 因为 Cursor 实现了迭代器接口,因此在实际使用当中,从 Cursor 取数据非常简单: 20 | 21 | ```java 22 | cursor.forEach(rowObject -> {...}); 23 | ``` 24 | 25 | 但构建 Cursor 的过程不简单,下面是一个 Mapper 类: 26 | 27 | ```java 28 | @Mapper 29 | public interface FooMapper { 30 | @Select("select * from foo limit #{limit}") 31 | Cursor scan(@Param("limit") int limit); 32 | } 33 | ``` 34 | 35 | 方法 scan() 是一个非常简单的查询。通过指定 Mapper 方法的返回值为 Cursor 类型,MyBatis 就知道这个查询方法一个流式查询。然后我们再写一个 SpringMVC Controller 方法来调用 Mapper(无关的代码已经省略): 36 | 37 | ```java 38 | @GetMapping("foo/scan/0/{limit}") 39 | public void scanFoo0(@PathVariable("limit") int limit) throws Exception { 40 | try (Cursor cursor = fooMapper.scan(limit)) { // 1 41 | cursor.forEach(foo -> {}); // 2 42 | } 43 | } 44 | ``` 45 | 46 | 上面的代码中,fooMapper 是 @Autowired 进来的。注释 1 处调用 scan 方法,得到 Cursor 对象并保证它能最后关闭;2 处则是从 cursor 中取数据。上面的代码看上去没什么问题,但是执行 scanFoo0() 时会报错: 47 | 48 | ``` 49 | java.lang.IllegalStateException: A Cursor is already closed. 50 | ``` 51 | 52 | 这是因为我们前面说了在取数据的过程中需要保持数据库连接,而 Mapper 方法通常在执行完后连接就关闭了,因此 Cusor 也一并关闭了。 53 | 54 | # SqlSessionFactory 55 | 56 | 我们可以用 SqlSessionFactory 来手工打开数据库连接,将 Controller 方法修改如下: 57 | 58 | ```java 59 | @GetMapping("foo/scan/1/{limit}") 60 | public void scanFoo1(@PathVariable("limit") int limit) throws Exception { 61 | try ( 62 | SqlSession sqlSession = sqlSessionFactory.openSession(); // 1 63 | Cursor cursor = 64 | sqlSession.getMapper(FooMapper.class).scan(limit) // 2 65 | ) { 66 | cursor.forEach(foo -> { }); 67 | } 68 | } 69 | ``` 70 | 71 | 上面的代码中,1 处我们开启了一个 SqlSession (实际上也代表了一个数据库连接),并保证它最后能关闭;2 处我们使用 SqlSession 来获得 Mapper 对象。这样才能保证得到的 Cursor 对象是打开状态的。 72 | 73 | # TransactionTemplate 74 | 75 | 在 Spring 中,我们可以用 TransactionTemplate 来执行一个数据库事务,这个过程中数据库连接同样是打开的。代码如下: 76 | 77 | ```java 78 | @GetMapping("foo/scan/2/{limit}") 79 | public void scanFoo2(@PathVariable("limit") int limit) throws Exception { 80 | TransactionTemplate transactionTemplate = 81 | new TransactionTemplate(transactionManager); // 1 82 | 83 | transactionTemplate.execute(status -> { // 2 84 | try (Cursor cursor = fooMapper.scan(limit)) { 85 | cursor.forEach(foo -> { }); 86 | } catch (IOException e) { 87 | e.printStackTrace(); 88 | } 89 | return null; 90 | }); 91 | } 92 | ``` 93 | 94 | 上面的代码中,1 处我们创建了一个 TransactionTemplate 对象(此处 transactionManager 是怎么来的不用多解释,本文假设读者对 Spring 数据库事务的使用比较熟悉了),2 处执行数据库事务,而数据库事务的内容则是调用 Mapper 对象的流式查询。注意这里的 Mapper 对象无需通过 SqlSession 创建。 95 | 96 | # @Transactional 注解 97 | 98 | 这个本质上和方案二一样,代码如下: 99 | 100 | ```java 101 | @GetMapping("foo/scan/3/{limit}") 102 | @Transactional 103 | public void scanFoo3(@PathVariable("limit") int limit) throws Exception { 104 | try (Cursor cursor = fooMapper.scan(limit)) { 105 | cursor.forEach(foo -> { }); 106 | } 107 | } 108 | ``` 109 | 110 | 它仅仅是在原来方法上面加了个 @Transactional 注解。这个方案看上去最简洁,但请注意 Spring 框架当中注解使用的坑:只在外部调用时生效。在当前类中调用这个方法,依旧会报错。 111 | -------------------------------------------------------------------------------- /99~参考资料/2015~《Spring in Action》~V5/03~响应式 Spring/第 10 章 Reactor 介绍/10.1 理解响应式编程.md: -------------------------------------------------------------------------------- 1 | ## 10.1 理解响应式编程 2 | 3 | 响应式编程是对命令式编程进行替代的一个范例。这种替代的存在是因为响应式编程解决了命令式编程的限制。通过了解这些限制,可以更好地把握响应式模式的好处。 4 | 5 | > 注意:响应式编程不是银弹。不应该从这章或是其他任何对于响应式编程的讨论中推断出命令式编程是魔鬼而响应式编程是天使。与作为一个开发人员学习的任何东西一样,响应式编程在某些地方很合适,在某些地方完全没有,应该对症下药。 6 | 7 | 如果和许多开发者一样,都是从命令式编程起步的。很自然地,现在你所写的大多数代码都是命令式的。命令式编程是非常直观的,现在的学生在他们学校的 STEM 课程中很轻松地学习它,它很强大,以至于它构成了大部分的代码,驱动着最大的企业。 8 | 9 | 这个想法很简单:你写的代码就是一行接一行的指令,按照它们的顺序一次一条地出现。一个任务被执行,程序就需要等到它执行完了,才能执行下一个任务。每一步,数据都需要完全获取到了才能被处理,因此它需要作为一个整体来处理。 10 | 11 | 这样做还行吧...直到它不得行了。当正在执行的任务被阻塞了,特别是它是一个 IO 任务,例如将数据写入到数据库或从远程服务器获取数据,那么调用该任务的线程将无法做任何事情,直到任务完成。说穿了,阻塞的线程就是一种浪费。 12 | 13 | 大多数编程语言,包括 Java,支持并发编程。在 Java 中启动另一个线程并将其发送到执行某项工作的分支上是相当容易的,而调用线程则继续执行其他工作。尽管很容易创建线程,但这些线程可能最终会阻塞自己。在管理在多线程里面的并发是很具有挑战性的。更多的线程意味着更高的复杂度。 14 | 15 | 相反,响应式编程是函数式和声明式的。响应式编程涉及描述通过该数据流的 pipeline 或 stream,而不是描述的一组按顺序执行的步骤。响应式流处理数据时只要数据是可用的就进行处理,而不是需要将数据作为一个整体进行提供。事实上,输入数据可以是无穷的(例如,一个地点的实时温度数据的恒定流)。 16 | 17 | 应用于一个真实世界的类比就是,将命令式编程看做一个装水的气球,响应式编程看做花园里面的水管。两者都是在炎热的夏天让你的朋友惊喜并沉浸其中的方式。但是它们的执行风格是不同的: 18 | 19 | - 一个水气球一次能携带的它的有效载荷,在撞击的那一刻浸湿了它预定的目标。然而,水球的容量是有限的,如果你想用水泡更多的人(或把同一个人淋得更湿),你唯一的选择就是增加水球的数量。 20 | - 一根花园水龙带将其有效载荷作为一股水流从水龙头流向喷嘴。花园水龙头接的水带的容量可能是有限的,但在打水仗的过程中水是源源不断的。只要水从水龙头进入软管,就会一直通过软管然后从喷嘴喷出。同一个花园软管是很容易扩展的,你和朋友们可以玩得更尽兴。 21 | 22 | 命令式编程就类似打水仗中的水球,本质上没有什么问题,但是拿着类似响应式编程的水管的人在可扩展性和性能方面是有优势的。 23 | 24 | ### 10.1.1 定义响应式流 25 | 26 | Reactive Streams 是 2013 年底由 Netflix、Lightbend 和 Pivotal(Spring 背后的公司)的工程师发起的一项计划。响应式流旨在为无阻塞异步流处理提供一个标准。 27 | 28 | 我们已经谈到了响应式编程的异步特性;它使我们能够并行执行任务以获得更大的可伸缩性。Backpressure(译者注:[如何形象的描述反应式编程中的背压(Backpressure)机制?](https://www.zhihu.com/question/49618581/answer/237078934) )是一种手段,通过对用户愿意处理的数据量设定限制,数据消费者可以避免被生产速度过快的数据淹没。 29 | 30 | > **Java Streams** 与 **Reactive Streams** 对比 31 | > 32 | > 在 Java 流和响应式流之间有很大的相似性。首先,它们的名字中都含有 Streams。它们也都为处理数据提供函数式接口。事实上,稍后当学到容器的时候,你会看到,其实它们有很多共同操作。 33 | > 34 | > 然而,Java 流通常是同步的,同时只能处理有限数据集。它们本质上是使用函数式进行集合迭代的一种手段。 35 | > 36 | > 响应式流支持任何大小的数据集,包括无限数据集的异步处理。它们使实时处理数据成为了可能。 37 | 38 | 响应式流的规范可以通过四个接口定义来概括:Publisher,Subscriber,Subscription 和 Processor。Publisher 为每一个 Subscription 的 Subscriber 生产数据。Publisher 接口声明了一个 subscribe() 方法,通过这个方法 Subscriber 可以订阅 Publisher: 39 | 40 | ```java 41 | public interface Publisher { 42 | void subscribe(Subscriber subscriber); 43 | } 44 | ``` 45 | 46 | Subscriber 一旦进行了订阅,就可以从 Publisher 中接收消息,这些消息都是通过 Subscriber 接口中的方法进行发送: 47 | 48 | ```java 49 | public interface Subscriber { 50 | void onSubscribe(Subscription sub); 51 | void onNext(T item); 52 | void onError(Throwable ex); 53 | void onComplete(); 54 | } 55 | ``` 56 | 57 | Subscriber 通过调用 onSubscribe() 函数将会收到第一个消息。当 Publisher 调用 onSubscribe(),它通过一个 Subscription 对象将消息传输给 Subscriber。消息是通过 Subscription 进行传递的,Subscriber 可以管理他自己的订阅内容: 58 | 59 | ```java 60 | public interface Subscription { 61 | void request(long n); 62 | void cancel(); 63 | } 64 | ``` 65 | 66 | Subscriber 可以调用 request() 去请求被被发送了的数据,或者调用 cancel() 来表明他对接收的数据不感兴趣,并取消订阅。当调用 request() 时,Subscriber 通过传递一个 long 值的参数来表示它将会接收多少数据。这时就会引进 backpressure,用以阻止 Publisher 发送的数据超过 Subscriber 能够处理的数据。在 Publisher 发送了足够的被请求的数据后,Subscriber 可以再次调用 request() 来请求更多的数据。 67 | 68 | 一旦 Subcriber 已经接收到数据,数据就通过流开始流动了。每一个 Publisher 发布的项目都会通过调用 onNext() 方法将数据传输到 Subscriber。如果出现错误,onError() 方法将被调用。如果 Publisher 没有更多的数据需要发送了,同时也不会再生产任何数据了,将会调用 onComplete() 方法来告诉 Subscriber,它已经结束了。 69 | 70 | 对于 Processor 接口而言,它连接了 Subscriber 和 Publisher: 71 | 72 | ```java 73 | public interface Processor extends Subscriber, Publisher {} 74 | ``` 75 | 76 | 作为 Subscriber,Processor 将会接收数据然后以一定的方式处理这些数据。然后它会像变戏法一样地变为一个 Publisher 将处理的结果发布给 Subscriber。 77 | 78 | 正如你所看到的,响应式流规范相当地简单。关于如何从 Publisher 开始建立起一个数据处理的通道,这也是一件很容易的事情了,通过将数据不输入或是输入到多个 Processor 中,然后将最终结果传递到 Subscriber 中就行了。 79 | 80 | Reactor 工程实现了响应式流的规范,它提供由响应式流组成的函数式 API。正如你将在后面的章节中看到的,Reactor 是 Spring 5 响应式编程模型的基础。在本章的剩余部分,我们将探索 Reactor 工程。 81 | -------------------------------------------------------------------------------- /02~数据库/02~MyBatis/Mybatis Plus/2024.11~MyBatis Plus BaseMapper 与 IService 差异.md: -------------------------------------------------------------------------------- 1 | # BaseMapper 与 IService 对比 2 | 3 | ## 1. 基本概念 4 | 5 | ### BaseMapper 6 | 7 | - 最基础的 Mapper 接口,提供基本的 CRUD 操作 8 | - 直接与数据库交互的 DAO 层接口 9 | - 操作粒度较细,方法都是原子性的 10 | 11 | ### IService 12 | 13 | - 在 BaseMapper 的基础上进行了封装,提供了更多的批量操作和链式调用方法 14 | - 业务服务层接口,通常用在 Service 层 15 | - 提供了更高层次的封装,包含了事务等特性 16 | 17 | ## 2. 主要区别 18 | 19 | ### 2.1 功能范围 20 | 21 | ```java 22 | // BaseMapper - 基础功能 23 | public interface BaseMapper { 24 | int insert(T entity); 25 | int deleteById(Serializable id); 26 | int updateById(@Param("et") T entity); 27 | T selectById(Serializable id); 28 | // ...基础方法 29 | } 30 | 31 | // IService - 扩展功能 32 | public interface IService { 33 | // 包含 BaseMapper 的所有功能,并且扩展了: 34 | boolean saveBatch(Collection entityList); 35 | boolean saveOrUpdateBatch(Collection entityList); 36 | boolean updateBatchById(Collection entityList); 37 | // ...更多批量操作和链式调用方法 38 | } 39 | ``` 40 | 41 | ### 2.2 使用场景对比 42 | 43 | #### BaseMapper 适用场景: 44 | 45 | ```java 46 | @Mapper 47 | public interface UserMapper extends BaseMapper { 48 | // 1. 单表的基础 CRUD 操作 49 | // 2. 需要自定义 SQL 的场景 50 | // 3. 需要直接操作数据库的原子操作 51 | // 4. 与其他 ORM 框架配合使用 52 | 53 | // 自定义 SQL 示例 54 | @Select("SELECT * FROM user WHERE age > #{age}") 55 | List selectOlderThan(@Param("age") Integer age); 56 | } 57 | ``` 58 | 59 | #### IService 适用场景: 60 | 61 | ```java 62 | @Service 63 | public class UserServiceImpl extends ServiceImpl implements IService { 64 | // 1. 批量操作场景 65 | public void batchOperationDemo() { 66 | List users = new ArrayList<>(); 67 | // ... 添加用户 68 | // 批量保存,自动分批处理 69 | this.saveBatch(users, 1000); 70 | } 71 | 72 | // 2. 链式调用场景 73 | public void chainOperationDemo() { 74 | // 链式查询 75 | this.lambdaQuery() 76 | .like(User::getName, "张") 77 | .ge(User::getAge, 18) 78 | .list(); 79 | 80 | // 链式更新 81 | this.lambdaUpdate() 82 | .set(User::getAge, 20) 83 | .eq(User::getName, "张三") 84 | .update(); 85 | } 86 | 87 | // 3. 事务场景 88 | @Transactional(rollbackFor = Exception.class) 89 | public void transactionDemo() { 90 | // 批量操作自动开启事务 91 | this.saveOrUpdateBatch(userList); 92 | } 93 | } 94 | ``` 95 | 96 | ## 3. 使用建议 97 | 98 | ### 3.1 使用 BaseMapper 的情况 99 | 100 | 1. 需要进行简单的 CRUD 操作 101 | 2. 需要自定义 SQL 语句 102 | 3. 对性能要求较高的场景 103 | 4. 需要精确控制 SQL 执行的场景 104 | 105 | ### 3.2 使用 IService 的情况 106 | 107 | 1. 需要进行批量操作 108 | 2. 需要使用链式调用提高代码可读性 109 | 3. 需要事务管理 110 | 4. 需要更复杂的业务逻辑封装 111 | 112 | ## 4. 最佳实践 113 | 114 | ### 4.1 分层使用 115 | 116 | ```java 117 | // Mapper 层:使用 BaseMapper 118 | @Mapper 119 | public interface UserMapper extends BaseMapper { 120 | // 自定义方法 121 | } 122 | 123 | // Service 层:使用 IService 124 | @Service 125 | public class UserServiceImpl extends ServiceImpl implements UserService { 126 | // 业务方法 127 | } 128 | ``` 129 | 130 | ### 4.2 混合使用 131 | 132 | ```java 133 | @Service 134 | public class UserServiceImpl extends ServiceImpl implements UserService { 135 | @Autowired 136 | private UserMapper userMapper; // 注入 BaseMapper 137 | 138 | public void complexOperation() { 139 | // 使用 IService 的批量方法 140 | this.saveBatch(userList); 141 | 142 | // 使用 BaseMapper 的自定义方法 143 | userMapper.selectOlderThan(18); 144 | } 145 | } 146 | ``` 147 | 148 | ## 5. 总结 149 | 150 | 1. **BaseMapper** 151 | 152 | - 偏向底层的数据库操作 153 | - 适合单一的、原子性的数据库操作 154 | - 可以自定义 SQL 155 | - 性能较好 156 | 157 | 2. **IService** 158 | - 偏向业务层的封装 159 | - 提供了更多的批量操作和便利方法 160 | - 支持链式调用 161 | - 自动管理事务 162 | -------------------------------------------------------------------------------- /01~基础使用/04~请求与响应/异步处理/Async/Async.md: -------------------------------------------------------------------------------- 1 | # Async Support 2 | 3 | 在 Spring Boot 中,我们可以通过 Java 注解的方式来启用异步处理: 4 | 5 | ```java 6 | @Configuration 7 | @EnableAsync 8 | public class SpringAsyncConfig { ... } 9 | ``` 10 | 11 | 或者以 XML 的方式启用: 12 | 13 | ```xml 14 | 15 | 16 | ``` 17 | 18 | # @Async 19 | 20 | @Async 注解只能作用于 public 方法,并且不能够自调用。 21 | 22 | ```java 23 | @Async 24 | public void asyncMethodWithVoidReturnType() { 25 | System.out.println("Execute method asynchronously. " 26 | + Thread.currentThread().getName()); 27 | } 28 | 29 | @Async 30 | public Future asyncMethodWithReturnType() { 31 | System.out.println("Execute method asynchronously - " 32 | + Thread.currentThread().getName()); 33 | try { 34 | Thread.sleep(5000); 35 | return new AsyncResult("hello world !!!!"); 36 | } catch (InterruptedException e) { 37 | // 38 | } 39 | 40 | return null; 41 | } 42 | ``` 43 | 44 | Spring 还提供了一个实现 Future 的 AsyncResult 类。这可以用来跟踪异步方法执行的结果。现在,让我们调用上述方法,并使用 Future 对象检索异步过程的结果。 45 | 46 | ```java 47 | public void testAsyncAnnotationForMethodsWithReturnType() 48 | throws InterruptedException, ExecutionException { 49 | System.out.println("Invoking an asynchronous method. " 50 | + Thread.currentThread().getName()); 51 | Future future = asyncAnnotationExample.asyncMethodWithReturnType(); 52 | 53 | while (true) { 54 | if (future.isDone()) { 55 | System.out.println("Result from asynchronous process - " + future.get()); 56 | break; 57 | } 58 | System.out.println("Continue doing something else. "); 59 | Thread.sleep(1000); 60 | } 61 | } 62 | ``` 63 | 64 | # Executor 65 | 66 | 默认情况下,Spring 使用 SimpleAsyncTaskExecutor 实际异步运行这些方法。可以在两个级别上覆盖默认值:在应用程序级别或单个方法级别。 67 | 68 | ## 方法级别复写 69 | 70 | 所需的执行程序需要在配置类中声明: 71 | 72 | ```java 73 | @Configuration 74 | @EnableAsync 75 | public class SpringAsyncConfig { 76 | 77 | @Bean(name = "threadPoolTaskExecutor") 78 | public Executor threadPoolTaskExecutor() { 79 | return new ThreadPoolTaskExecutor(); 80 | } 81 | } 82 | 83 | @Async("threadPoolTaskExecutor") 84 | public void asyncMethodWithConfiguredExecutor() { 85 | System.out.println("Execute method with configured executor - " 86 | + Thread.currentThread().getName()); 87 | } 88 | ``` 89 | 90 | ## 应用级别复写 91 | 92 | 配置类应实现 AsyncConfigurer 接口,这意味着它具有实现 getAsyncExecutor() 方法。在这里,我们将返回整个应用程序的执行程序–现在,它成为运行以 @Async 注释的方法的默认执行程序: 93 | 94 | ```java 95 | @Configuration 96 | @EnableAsync 97 | public class SpringAsyncConfig implements AsyncConfigurer { 98 | 99 | @Override 100 | public Executor getAsyncExecutor() { 101 | return new ThreadPoolTaskExecutor(); 102 | } 103 | } 104 | ``` 105 | 106 | # 异常处理 107 | 108 | 当方法返回类型为 Future 时,异常处理很容易:Future.get() 方法将引发异常。但是,如果返回类型为 void,则异常不会传播到调用线程;因此,我们需要添加额外的配置来处理异常。我们将通过实现 AsyncUncaughtExceptionHandler 接口来创建自定义异步异常处理程序。当存在任何未捕获的异步异常时,将调用 handleUncaughtException() 方法: 109 | 110 | ```java 111 | public class CustomAsyncExceptionHandler 112 | implements AsyncUncaughtExceptionHandler { 113 | 114 | @Override 115 | public void handleUncaughtException( 116 | Throwable throwable, 117 | Method method, 118 | Object... obj 119 | ) { 120 | System.out.println("Exception message - " + throwable.getMessage()); 121 | System.out.println("Method name - " + method.getName()); 122 | for (Object param : obj) { 123 | System.out.println("Parameter value - " + param); 124 | } 125 | } 126 | } 127 | ``` 128 | 129 | 在上一节中,我们介绍了由配置类实现的 AsyncConfigurer 接口。作为其中的一部分,我们还需要重写 getAsyncUncaughtExceptionHandler() 方法以返回我们的自定义异步异常处理程序: 130 | 131 | ```java 132 | @Override 133 | public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { 134 | return new CustomAsyncExceptionHandler(); 135 | } 136 | ``` 137 | 138 | # Links 139 | 140 | 141 | --------------------------------------------------------------------------------