├── SpringBoot系列笔记 ├── 全局异常处理 │ ├── code │ │ └── Global_Exception │ │ │ ├── src │ │ │ ├── main │ │ │ │ ├── resources │ │ │ │ │ └── application.properties │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── jdkcb │ │ │ │ │ └── demo │ │ │ │ │ ├── service │ │ │ │ │ └── MyService.java │ │ │ │ │ ├── DemoApplication.java │ │ │ │ │ ├── exception │ │ │ │ │ ├── MyException.java │ │ │ │ │ └── CommonResult.java │ │ │ │ │ ├── config │ │ │ │ │ └── ExceptionConfig.java │ │ │ │ │ ├── api │ │ │ │ │ └── UserController.java │ │ │ │ │ └── advice │ │ │ │ │ └── MyControllerAdvice.java │ │ │ └── test │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── jdkcb │ │ │ │ └── demo │ │ │ │ └── DemoApplicationTests.java │ │ │ ├── .mvn │ │ │ └── wrapper │ │ │ │ ├── maven-wrapper.properties │ │ │ │ ├── maven-wrapper.jar │ │ │ │ └── MavenWrapperDownloader.java │ │ │ ├── .gitignore │ │ │ ├── pom.xml │ │ │ └── mvnw.cmd │ └── Springboot之自定义全局异常处理.md ├── mybatis多数据源配置 │ └── code │ │ └── MybatisDemo │ │ ├── .mvn │ │ └── wrapper │ │ │ ├── maven-wrapper.properties │ │ │ └── maven-wrapper.jar │ │ ├── .gitignore │ │ ├── src │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── jdkcb │ │ │ │ │ └── mybatisstuday │ │ │ │ │ ├── MybatisstudayApplication.java │ │ │ │ │ ├── mapper │ │ │ │ │ ├── two │ │ │ │ │ │ └── SecondaryUserMapper.java │ │ │ │ │ └── one │ │ │ │ │ │ └── PrimaryUserMapper.java │ │ │ │ │ ├── controller │ │ │ │ │ └── UserController.java │ │ │ │ │ ├── pojo │ │ │ │ │ └── User.java │ │ │ │ │ └── config │ │ │ │ │ ├── SecondaryDataSourceConfig.java │ │ │ │ │ └── PrimaryDataSourceConfig.java │ │ │ └── resources │ │ │ │ ├── mapping │ │ │ │ ├── one │ │ │ │ │ └── PrimaryUserMapper.xml │ │ │ │ └── two │ │ │ │ │ └── SecondaryUserMapper.xml │ │ │ │ └── application.properties │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── jdkcb │ │ │ └── mybatisstuday │ │ │ └── MybatisstudayApplicationTests.java │ │ ├── pom.xml │ │ └── mvnw.cmd └── 自定义starter │ └── Springboot 之创建自定义starter.md ├── Nginx初级入门教程 ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 123.png │ └── 234.png ├── 写给后端的Nginx初级入门教程基础篇.md └── 写给后端的Nginx初级入门教程Nginx原理初探.md ├── Vue系列学习笔记 ├── img │ └── img.png └── vue 使用 vue-pdf 实现文件在线预览.md ├── 废话设计模式系列 ├── 单例模式 │ ├── 代码 │ │ └── singleton │ │ │ ├── Test.java │ │ │ ├── stat │ │ │ └── Singleton.java │ │ │ ├── general │ │ │ └── Singleton.java │ │ │ ├── threadsafe │ │ │ └── Singleton.java │ │ │ └── dcl │ │ │ └── Singleton.java │ ├── 设计模式java实现之单例模式.pdf │ └── 设计模式Java语言实现之单例模式.md ├── 策略模式 │ ├── 设计模式Java语言实现之策略模式.pdf │ └── 代码 │ │ └── strategy │ │ ├── FlyBehavior.java │ │ ├── QuackBehavior.java │ │ ├── Squeak.java │ │ ├── FlyNoWay.java │ │ ├── Quack.java │ │ ├── FlyWithWings.java │ │ ├── MuteQuack.java │ │ ├── RubberDuck.java │ │ ├── LiveDuck.java │ │ ├── Text.java │ │ └── Duck.java ├── 观察者模式 │ ├── 设计模式Java语言实现之观察者模式.pdf │ └── 代码 │ │ └── observer │ │ ├── DisplayElement.java │ │ ├── Observer.java │ │ ├── Subject.java │ │ ├── observerUseJava │ │ ├── WeatherStation.java │ │ ├── WeatherData.java │ │ └── CurrentConditionsDisplay.java │ │ ├── WeatherStation.java │ │ ├── CurrentConditionsDisplay.java │ │ └── WeatherData.java ├── README.md ├── 建造者模式 │ └── 代码 │ │ └── builder │ │ ├── Test.java │ │ ├── fast │ │ ├── Test.java │ │ └── Computer.java │ │ ├── Builder.java │ │ ├── Director.java │ │ ├── ComputerBuilder.java │ │ └── Computer.java ├── 工厂模式 │ ├── 设计模式之工厂模式三连发.md │ └── 设计模式之工厂模式三连发(下).md ├── 代理模式 │ └── 设计模式java语言实现(八)之代理模式.md ├── 模板模式 │ └── 设计模式java语言实现之模板模式.md ├── 装饰者模式 │ └── 从手办到装饰者设计模式.md ├── 命令模式 │ └── 设计模式Java语言实现之命令模式.md └── MVC设计模式 │ └── 设计模式java语言实现之MVC设计模式.md ├── Jvm终极奥义系列 ├── images │ ├── jvm1.png │ ├── jvm2.png │ ├── jvm3.png │ ├── jvm4.png │ ├── jvm5.png │ ├── jvm6.png │ └── jvm7.png ├── jvm内存回收终极奥义:垃圾收集算法.md ├── 一个Java对象的死亡证明.md └── Jvm内存回收终极奥义垃圾收集器.md ├── java学习笔记 ├── 回调 │ ├── 浅谈JAVA回调机制.pdf │ └── 示例代码 │ │ └── CallBackDemo │ │ ├── bin │ │ └── demo │ │ │ ├── Text.class │ │ │ ├── CallBack.class │ │ │ ├── Student.class │ │ │ └── Teacher.class │ │ ├── src │ │ └── demo │ │ │ ├── Text.java │ │ │ ├── Student.java │ │ │ ├── Teacher.java │ │ │ └── CallBack.java │ │ ├── .classpath │ │ ├── .project │ │ └── .settings │ │ └── org.eclipse.jdt.core.prefs └── 到底什么是应用上下文?.md ├── Docker初级入门教程 ├── image │ ├── docker.png │ ├── index.png │ ├── index2.png │ └── 1515399447_1460.png ├── Docker 三要素 :镜像、容器和仓库.md ├── 面向初学者的docker学习教程:基础篇.md └── 写给后端的Docker初级入门教程:DockerFile 命令详解.md ├── 用java实现一个简单的区块链 ├── images │ ├── 1.png │ └── 2.png ├── 代码 │ └── blockchaindemo │ │ ├── blockchaindemo.iml │ │ ├── target │ │ └── classes │ │ │ └── com │ │ │ └── jdkcb │ │ │ └── blockchain │ │ │ ├── enity │ │ │ └── Block.class │ │ │ ├── BlockChainTest.class │ │ │ ├── utils │ │ │ ├── JsonUtil.class │ │ │ └── StringUtil.class │ │ │ └── BlockChainListTest.class │ │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── com │ │ │ └── jdkcb │ │ │ └── blockchain │ │ │ ├── utils │ │ │ ├── JsonUtil.java │ │ │ └── StringUtil.java │ │ │ ├── BlockChainTest.java │ │ │ ├── enity │ │ │ └── Block.java │ │ │ └── BlockChainListTest.java │ │ └── pom.xml └── 用java实现一个简单的区块链:UTXO交易模型.md ├── Spring学习笔记 ├── Cache │ ├── Spring使用Redis.pdf │ ├── 浅析SpringCache缓存.pdf │ └── 资料 │ │ └── JSR-107 │ │ └── jcache-1.0.pdf ├── IOC │ └── 浅析SpringIOC容器.pdf └── 事件驱动编程 │ ├── 代码 │ └── event │ │ ├── NotifyEvent.java │ │ ├── NotifyListener.java │ │ ├── TestController.java │ │ ├── NotifyPublisher.java │ │ ├── AysncListenerConfig.java │ │ └── ExceptionHandlingAsyncTaskExecutor.java │ └── Spring 事件驱动编程.md ├── Django系列学习笔记 ├── Django-redis 学习笔记.md ├── Django 使用分页插件实现数据库分页查询:.md ├── django自定义登陆拦截器.md └── Django项目 uwsgi+Nginx 部署教程.md ├── Hadoop初级入门教程 ├── 写给后端的Hadoop初级入门教程(三):Hadoop运行环境搭建.md ├── 写给后端的Hadoop初级入门教程:Hadoop组成.md └── 写给后端的Hadoop初级入门教程:基本概念篇.md └── README.md /SpringBoot系列笔记/全局异常处理/code/Global_Exception/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Nginx初级入门教程/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Nginx初级入门教程/img/1.png -------------------------------------------------------------------------------- /Nginx初级入门教程/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Nginx初级入门教程/img/2.png -------------------------------------------------------------------------------- /Nginx初级入门教程/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Nginx初级入门教程/img/3.png -------------------------------------------------------------------------------- /Nginx初级入门教程/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Nginx初级入门教程/img/4.png -------------------------------------------------------------------------------- /Nginx初级入门教程/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Nginx初级入门教程/img/5.png -------------------------------------------------------------------------------- /Nginx初级入门教程/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Nginx初级入门教程/img/6.png -------------------------------------------------------------------------------- /Vue系列学习笔记/img/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Vue系列学习笔记/img/img.png -------------------------------------------------------------------------------- /Nginx初级入门教程/img/123.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Nginx初级入门教程/img/123.png -------------------------------------------------------------------------------- /Nginx初级入门教程/img/234.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Nginx初级入门教程/img/234.png -------------------------------------------------------------------------------- /废话设计模式系列/单例模式/代码/singleton/Test.java: -------------------------------------------------------------------------------- 1 | package code.singleton; 2 | 3 | public class Test { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /Jvm终极奥义系列/images/jvm1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Jvm终极奥义系列/images/jvm1.png -------------------------------------------------------------------------------- /Jvm终极奥义系列/images/jvm2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Jvm终极奥义系列/images/jvm2.png -------------------------------------------------------------------------------- /Jvm终极奥义系列/images/jvm3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Jvm终极奥义系列/images/jvm3.png -------------------------------------------------------------------------------- /Jvm终极奥义系列/images/jvm4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Jvm终极奥义系列/images/jvm4.png -------------------------------------------------------------------------------- /Jvm终极奥义系列/images/jvm5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Jvm终极奥义系列/images/jvm5.png -------------------------------------------------------------------------------- /Jvm终极奥义系列/images/jvm6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Jvm终极奥义系列/images/jvm6.png -------------------------------------------------------------------------------- /Jvm终极奥义系列/images/jvm7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Jvm终极奥义系列/images/jvm7.png -------------------------------------------------------------------------------- /java学习笔记/回调/浅谈JAVA回调机制.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/java学习笔记/回调/浅谈JAVA回调机制.pdf -------------------------------------------------------------------------------- /Docker初级入门教程/image/docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Docker初级入门教程/image/docker.png -------------------------------------------------------------------------------- /Docker初级入门教程/image/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Docker初级入门教程/image/index.png -------------------------------------------------------------------------------- /Docker初级入门教程/image/index2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Docker初级入门教程/image/index2.png -------------------------------------------------------------------------------- /用java实现一个简单的区块链/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/用java实现一个简单的区块链/images/1.png -------------------------------------------------------------------------------- /用java实现一个简单的区块链/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/用java实现一个简单的区块链/images/2.png -------------------------------------------------------------------------------- /Spring学习笔记/Cache/Spring使用Redis.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Spring学习笔记/Cache/Spring使用Redis.pdf -------------------------------------------------------------------------------- /Spring学习笔记/IOC/浅析SpringIOC容器.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Spring学习笔记/IOC/浅析SpringIOC容器.pdf -------------------------------------------------------------------------------- /废话设计模式系列/单例模式/设计模式java实现之单例模式.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/废话设计模式系列/单例模式/设计模式java实现之单例模式.pdf -------------------------------------------------------------------------------- /Spring学习笔记/Cache/浅析SpringCache缓存.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Spring学习笔记/Cache/浅析SpringCache缓存.pdf -------------------------------------------------------------------------------- /废话设计模式系列/策略模式/设计模式Java语言实现之策略模式.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/废话设计模式系列/策略模式/设计模式Java语言实现之策略模式.pdf -------------------------------------------------------------------------------- /Docker初级入门教程/image/1515399447_1460.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Docker初级入门教程/image/1515399447_1460.png -------------------------------------------------------------------------------- /废话设计模式系列/观察者模式/设计模式Java语言实现之观察者模式.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/废话设计模式系列/观察者模式/设计模式Java语言实现之观察者模式.pdf -------------------------------------------------------------------------------- /用java实现一个简单的区块链/代码/blockchaindemo/blockchaindemo.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Spring学习笔记/Cache/资料/JSR-107/jcache-1.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/Spring学习笔记/Cache/资料/JSR-107/jcache-1.0.pdf -------------------------------------------------------------------------------- /废话设计模式系列/观察者模式/代码/observer/DisplayElement.java: -------------------------------------------------------------------------------- 1 | package observer; 2 | 3 | public interface DisplayElement { 4 | public void display(); 5 | } 6 | -------------------------------------------------------------------------------- /java学习笔记/回调/示例代码/CallBackDemo/bin/demo/Text.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/java学习笔记/回调/示例代码/CallBackDemo/bin/demo/Text.class -------------------------------------------------------------------------------- /java学习笔记/回调/示例代码/CallBackDemo/src/demo/Text.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/java学习笔记/回调/示例代码/CallBackDemo/src/demo/Text.java -------------------------------------------------------------------------------- /java学习笔记/回调/示例代码/CallBackDemo/src/demo/Student.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/java学习笔记/回调/示例代码/CallBackDemo/src/demo/Student.java -------------------------------------------------------------------------------- /java学习笔记/回调/示例代码/CallBackDemo/src/demo/Teacher.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/java学习笔记/回调/示例代码/CallBackDemo/src/demo/Teacher.java -------------------------------------------------------------------------------- /java学习笔记/回调/示例代码/CallBackDemo/bin/demo/CallBack.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/java学习笔记/回调/示例代码/CallBackDemo/bin/demo/CallBack.class -------------------------------------------------------------------------------- /java学习笔记/回调/示例代码/CallBackDemo/bin/demo/Student.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/java学习笔记/回调/示例代码/CallBackDemo/bin/demo/Student.class -------------------------------------------------------------------------------- /java学习笔记/回调/示例代码/CallBackDemo/bin/demo/Teacher.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/java学习笔记/回调/示例代码/CallBackDemo/bin/demo/Teacher.class -------------------------------------------------------------------------------- /java学习笔记/回调/示例代码/CallBackDemo/src/demo/CallBack.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/java学习笔记/回调/示例代码/CallBackDemo/src/demo/CallBack.java -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip 2 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip 2 | -------------------------------------------------------------------------------- /废话设计模式系列/策略模式/代码/strategy/FlyBehavior.java: -------------------------------------------------------------------------------- 1 | package strategy; 2 | 3 | /*** 4 | * 5 | * 定义鸭子飞行行为的接口 6 | * 7 | */ 8 | 9 | public interface FlyBehavior { 10 | public void fly(); 11 | } 12 | -------------------------------------------------------------------------------- /废话设计模式系列/策略模式/代码/strategy/QuackBehavior.java: -------------------------------------------------------------------------------- 1 | package strategy; 2 | 3 | /*** 4 | * 5 | * 定义鸭子叫声行为的接口 6 | * 7 | */ 8 | 9 | public interface QuackBehavior { 10 | public void quack(); 11 | } -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/SpringBoot系列笔记/全局异常处理/code/Global_Exception/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /用java实现一个简单的区块链/代码/blockchaindemo/target/classes/com/jdkcb/blockchain/enity/Block.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/用java实现一个简单的区块链/代码/blockchaindemo/target/classes/com/jdkcb/blockchain/enity/Block.class -------------------------------------------------------------------------------- /用java实现一个简单的区块链/代码/blockchaindemo/target/classes/com/jdkcb/blockchain/BlockChainTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/用java实现一个简单的区块链/代码/blockchaindemo/target/classes/com/jdkcb/blockchain/BlockChainTest.class -------------------------------------------------------------------------------- /用java实现一个简单的区块链/代码/blockchaindemo/target/classes/com/jdkcb/blockchain/utils/JsonUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/用java实现一个简单的区块链/代码/blockchaindemo/target/classes/com/jdkcb/blockchain/utils/JsonUtil.class -------------------------------------------------------------------------------- /用java实现一个简单的区块链/代码/blockchaindemo/target/classes/com/jdkcb/blockchain/BlockChainListTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/用java实现一个简单的区块链/代码/blockchaindemo/target/classes/com/jdkcb/blockchain/BlockChainListTest.class -------------------------------------------------------------------------------- /用java实现一个简单的区块链/代码/blockchaindemo/target/classes/com/jdkcb/blockchain/utils/StringUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanshuaikang/HanShu-Note/HEAD/用java实现一个简单的区块链/代码/blockchaindemo/target/classes/com/jdkcb/blockchain/utils/StringUtil.class -------------------------------------------------------------------------------- /废话设计模式系列/策略模式/代码/strategy/Squeak.java: -------------------------------------------------------------------------------- 1 | package strategy; 2 | 3 | /*** 4 | * 5 | * 定义鸭子吱吱叫的行为 6 | * 7 | */ 8 | 9 | public class Squeak implements QuackBehavior { 10 | public void quack() { 11 | System.out.println("吱吱叫"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /废话设计模式系列/策略模式/代码/strategy/FlyNoWay.java: -------------------------------------------------------------------------------- 1 | package strategy; 2 | 3 | 4 | 5 | /*** 6 | * 7 | * 定义鸭子飞行行为为不会飞的类 8 | * 9 | */ 10 | public class FlyNoWay implements FlyBehavior { 11 | public void fly() { 12 | System.out.println("我不会飞"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /废话设计模式系列/策略模式/代码/strategy/Quack.java: -------------------------------------------------------------------------------- 1 | package strategy; 2 | 3 | 4 | /*** 5 | * 6 | * 定义鸭子叫声是嘎嘎嘎的行为 7 | * 8 | */ 9 | 10 | 11 | public class Quack implements QuackBehavior { 12 | public void quack() { 13 | System.out.println("嘎嘎嘎"); 14 | } 15 | } -------------------------------------------------------------------------------- /废话设计模式系列/观察者模式/代码/observer/Observer.java: -------------------------------------------------------------------------------- 1 | package observer; 2 | 3 | /*** 4 | * 5 | * @author 韩数 6 | * 声明通知者通用的接口,定义updata方法 7 | * 8 | */ 9 | public interface Observer { 10 | public void update(float temp, float humidity, float pressure); 11 | } 12 | -------------------------------------------------------------------------------- /废话设计模式系列/策略模式/代码/strategy/FlyWithWings.java: -------------------------------------------------------------------------------- 1 | package strategy; 2 | 3 | 4 | /*** 5 | * 6 | * 定义鸭子飞行行为为会飞的类 7 | * 8 | */ 9 | 10 | public class FlyWithWings implements FlyBehavior { 11 | public void fly() { 12 | System.out.println("我会飞,哈哈哈!"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /废话设计模式系列/策略模式/代码/strategy/MuteQuack.java: -------------------------------------------------------------------------------- 1 | package strategy; 2 | 3 | 4 | /*** 5 | * 6 | * 定义鸭子叫声行为是不会叫的类 7 | * 8 | * 9 | */ 10 | 11 | public class MuteQuack implements QuackBehavior { 12 | public void quack() { 13 | System.out.println("<< 我不会叫,我是一个沉默的鸭子>>"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /java学习笔记/回调/示例代码/CallBackDemo/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/src/main/java/com/jdkcb/demo/service/MyService.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo.service; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | @Service 6 | public class MyService { 7 | 8 | public String getUserName(){ 9 | int num = 1/0; 10 | return "Helloword"; 11 | } 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /废话设计模式系列/README.md: -------------------------------------------------------------------------------- 1 | # design-pattern-java 2 | 设计模式java代码实现(你从未见过的船新版本) 3 | 4 | 5 | 6 | ### 个人简介: 7 | 8 | 9 | 10 | 韩数(化名),计算机科学与技术菜佬一枚,目前是一名在校大学生,得益于开源世界开放分享的精神,我从一个懵懂无知的小白慢慢成长成一个略有了解的小白,学习到现在,感觉自己终于可以为开源世界贡献一点什么了,遂开此专栏,记录自己java设计模式的笔记,同时,也努力通过自己的方式,将一些有趣的事情融入到文章中,让学习设计模式慢慢变成一件有趣的事情。 11 | 12 | **邮箱:** 1758504262@qq.com 13 | 14 | **QQ:** 2101970636 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /废话设计模式系列/观察者模式/代码/observer/Subject.java: -------------------------------------------------------------------------------- 1 | package observer; 2 | 3 | /*** 4 | * 5 | * @author 韩数 6 | * 定义一个主题的超类,规定方法行为 7 | * 8 | */ 9 | public interface Subject { 10 | //注册一个观察者(被通知者),这里Observer也是所有类的超类 11 | public void registerObserver(Observer o); 12 | //移除一个观察者(被通知者), 13 | public void removeObserver(Observer o); 14 | //通知所有观察者(被通知者), 15 | public void notifyObservers(); 16 | } 17 | -------------------------------------------------------------------------------- /废话设计模式系列/建造者模式/代码/builder/Test.java: -------------------------------------------------------------------------------- 1 | package code.builder; 2 | 3 | public class Test { 4 | 5 | public static void main(String[] args) { 6 | Builder builder = new ComputerBuilder(); 7 | Director director = new Director(builder); 8 | Computer computer = director.createComputer("I9", "三星", "华硕", "三星" ,"罗技", "罗技"); 9 | System.out.println(computer); 10 | 11 | } 12 | 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /废话设计模式系列/建造者模式/代码/builder/fast/Test.java: -------------------------------------------------------------------------------- 1 | package code.builder.fast; 2 | 3 | public class Test { 4 | 5 | public static void main(String[] args) { 6 | 7 | Computer computer = new Computer.ComputerBuilder("i9", "三星", "华硕", "金士顿") 8 | .buildMouse("罗技") 9 | .bulidKeyBoard("雷蛇") 10 | .create(); 11 | 12 | System.out.println(computer); 13 | 14 | 15 | 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /Spring学习笔记/事件驱动编程/代码/event/NotifyEvent.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo.event; 2 | 3 | import org.springframework.context.ApplicationEvent; 4 | 5 | public class NotifyEvent extends ApplicationEvent { 6 | 7 | private String msg; 8 | 9 | public NotifyEvent(Object source, String msg) { 10 | super(source); 11 | this.msg = msg; 12 | } 13 | 14 | public String getMsg() { 15 | return msg; 16 | } 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/src/main/java/com/jdkcb/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /废话设计模式系列/单例模式/代码/singleton/stat/Singleton.java: -------------------------------------------------------------------------------- 1 | package code.singleton.stat; 2 | 3 | /*** 4 | * 5 | * @author 韩数 6 | * 常用的单例模式实现 7 | * 线程安全,不足之处,可能会损失一部分性能,非延迟加载 8 | * 9 | */ 10 | 11 | 12 | public class Singleton { 13 | 14 | //在类初始化的时候就实例化对象,所以不存在多线程的安全问题 15 | private static Singleton uniqueInstance = new Singleton(); 16 | 17 | private Singleton() {} 18 | 19 | public static Singleton getInstance() { 20 | return uniqueInstance; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /废话设计模式系列/观察者模式/代码/observer/observerUseJava/WeatherStation.java: -------------------------------------------------------------------------------- 1 | package observer.observerUseJava; 2 | 3 | /*** 4 | * 5 | * 测试类 6 | * 模拟天气改变时通知操作 7 | * 8 | */ 9 | 10 | public class WeatherStation { 11 | 12 | public static void main(String[] args) { 13 | WeatherData weatherData = new WeatherData(); 14 | CurrentConditionsDisplay currentConditions = new CurrentConditionsDisplay(weatherData); 15 | weatherData.setMeasurements(80, 65, 30.4f); 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /java学习笔记/回调/示例代码/CallBackDemo/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | CallBackDemo 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /废话设计模式系列/单例模式/代码/singleton/general/Singleton.java: -------------------------------------------------------------------------------- 1 | package code.singleton.general; 2 | 3 | 4 | /*** 5 | * 6 | * @author 韩数 7 | * 一般单例模式实现,采用延迟加载方式实现 8 | * 9 | */ 10 | 11 | public class Singleton { 12 | 13 | private static Singleton uniqueInstance; 14 | 15 | private Singleton() {} 16 | 17 | public static Singleton getInstance() { 18 | if (uniqueInstance == null) { 19 | uniqueInstance = new Singleton(); 20 | } 21 | return uniqueInstance; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /废话设计模式系列/观察者模式/代码/observer/WeatherStation.java: -------------------------------------------------------------------------------- 1 | package observer; 2 | 3 | /*** 4 | * 5 | * 测试类 6 | * 模拟天气改变时通知操作 7 | * 8 | */ 9 | public class WeatherStation { 10 | 11 | public static void main(String[] args) { 12 | 13 | WeatherData weatherData = new WeatherData(); 14 | CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(weatherData); 15 | weatherData.setMeasurements(80, 90, 100l); 16 | weatherData.setMeasurements(90, 90, 100l); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /废话设计模式系列/建造者模式/代码/builder/Builder.java: -------------------------------------------------------------------------------- 1 | package code.builder; 2 | 3 | public interface Builder { 4 | 5 | void installMainBoard(String mainBoard) ;//安装主板 6 | void installCpu(String cpu) ; // 安装 cpu 7 | void installhardDisk(String hardDisk) ; // 安装硬盘 8 | void installMemory(String memory) ; // 安装内存 9 | void installKeyBoard(String keyboard) ; // 安装内存 10 | void installMouse(String mouse); //安装鼠标 11 | Computer createComputer() ; // 组成电脑 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/src/main/java/com/jdkcb/mybatisstuday/MybatisstudayApplication.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.mybatisstuday; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class MybatisstudayApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(MybatisstudayApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/src/test/java/com/jdkcb/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class DemoApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Spring学习笔记/事件驱动编程/代码/event/NotifyListener.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo.event; 2 | 3 | import org.springframework.context.event.EventListener; 4 | import org.springframework.scheduling.annotation.Async; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class NotifyListener { 9 | 10 | 11 | @Async 12 | @EventListener 13 | public void sayHello(NotifyEvent notifyEvent){ 14 | System.out.println("收到事件:"+notifyEvent.getMsg()); 15 | } 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/src/main/resources/mapping/one/PrimaryUserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | select * from sys_user; 9 | 10 | 11 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/src/main/resources/mapping/two/SecondaryUserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | select * from sys_user2; 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/src/main/java/com/jdkcb/mybatisstuday/mapper/two/SecondaryUserMapper.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.mybatisstuday.mapper.two; 2 | 3 | import com.jdkcb.mybatisstuday.pojo.User; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.util.List; 8 | 9 | 10 | @Component 11 | @Mapper 12 | public interface SecondaryUserMapper { 13 | 14 | // @Select("select * from sys_user;") 15 | List findAll(); 16 | } 17 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/src/test/java/com/jdkcb/mybatisstuday/MybatisstudayApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.mybatisstuday; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class MybatisstudayApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/src/main/java/com/jdkcb/mybatisstuday/mapper/one/PrimaryUserMapper.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.mybatisstuday.mapper.one; 2 | 3 | import com.jdkcb.mybatisstuday.pojo.User; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.apache.ibatis.annotations.Select; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | 10 | @Component 11 | @Mapper 12 | public interface PrimaryUserMapper { 13 | 14 | // @Select("select * from sys_user;") 15 | List findAll(); 16 | } 17 | -------------------------------------------------------------------------------- /废话设计模式系列/策略模式/代码/strategy/RubberDuck.java: -------------------------------------------------------------------------------- 1 | package strategy; 2 | 3 | 4 | 5 | /*** 6 | * 7 | * demo1:橡皮鸭,特征,不会飞,吱吱叫 8 | * 9 | */ 10 | 11 | public class RubberDuck extends Duck { 12 | 13 | public RubberDuck() { 14 | 15 | 16 | /* 17 | * 注:因为RubberDuck继承Duck类,所有Duck类中定义的 18 | * flyBehavior和quackBehavior可以直接赋值 19 | */ 20 | 21 | //定义橡皮鸭不会飞的行为 22 | flyBehavior = new FlyNoWay(); 23 | //定义橡皮鸭吱吱叫的行为 24 | quackBehavior = new Squeak(); 25 | } 26 | 27 | public void display() { 28 | System.out.println("我是一个橡皮鸭,我的身体是橡皮做哒"); 29 | } 30 | } -------------------------------------------------------------------------------- /用java实现一个简单的区块链/代码/blockchaindemo/src/main/java/com/jdkcb/blockchain/utils/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.blockchain.utils; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | 6 | /** 7 | * Created on 2018/1/17. 8 | * 9 | * @author zlf 10 | * @since 1.0 11 | */ 12 | public class JsonUtil { 13 | public static String toJson(Object object){ 14 | GsonBuilder gsonBuilder = new GsonBuilder(); 15 | gsonBuilder.setPrettyPrinting(); 16 | Gson gson = gsonBuilder.create(); 17 | return gson.toJson(object); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /废话设计模式系列/单例模式/代码/singleton/threadsafe/Singleton.java: -------------------------------------------------------------------------------- 1 | package code.singleton.threadsafe; 2 | 3 | /** 4 | * 5 | * @author 韩数 6 | * 线程安全方式实现单例模式,缺点是,每次都会调用synchronized的关键字修饰的方法,会损失一定的性能 7 | * 8 | */ 9 | 10 | public class Singleton { 11 | 12 | 13 | private static Singleton uniqueInstance; 14 | 15 | private Singleton() {} 16 | 17 | //synchronized修饰该方法,保证每次只有一个线程进入该方法,从而避免产生多个Singleton对象实例。 18 | public static synchronized Singleton getInstance() { 19 | if (uniqueInstance == null) { 20 | uniqueInstance = new Singleton(); 21 | } 22 | return uniqueInstance; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Spring学习笔记/事件驱动编程/代码/event/TestController.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo.event; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | 8 | @RestController 9 | public class TestController { 10 | 11 | @Autowired 12 | private NotifyPublisher notifyPublisher; 13 | 14 | @GetMapping("/sayHello") 15 | public String sayHello(){ 16 | notifyPublisher.publishEvent(1, "我发布了一个事件"); 17 | return "Hello Word"; 18 | 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /废话设计模式系列/策略模式/代码/strategy/LiveDuck.java: -------------------------------------------------------------------------------- 1 | package strategy; 2 | 3 | 4 | /*** 5 | * 6 | * @author韩数 7 | * demo1:活鸭子,特征是会飞,嘎嘎叫 8 | * 9 | */ 10 | 11 | 12 | public class LiveDuck extends Duck { 13 | 14 | 15 | 16 | public LiveDuck() { 17 | 18 | /* 19 | * 注:因为RubberDuck继承Duck类,所有Duck类中定义的 20 | * flyBehavior和quackBehavior可以直接赋值 21 | */ 22 | 23 | //定义鸭子会飞的行为 24 | flyBehavior = new FlyWithWings(); 25 | //定义鸭子嘎嘎叫的行为 26 | quackBehavior = new Quack(); 27 | 28 | 29 | } 30 | 31 | 32 | 33 | @Override 34 | void display() { 35 | System.out.println("我是一只栩栩如生的鸭子!"); 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /java学习笔记/回调/示例代码/CallBackDemo/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.8 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.source=1.8 12 | -------------------------------------------------------------------------------- /用java实现一个简单的区块链/代码/blockchaindemo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.jdkcb 8 | demo 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | com.google.code.gson 14 | gson 15 | 2.8.6 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /废话设计模式系列/单例模式/代码/singleton/dcl/Singleton.java: -------------------------------------------------------------------------------- 1 | package code.singleton.dcl; 2 | 3 | /*** 4 | * 5 | * @author 韩数 6 | * 线程安全,延迟加载方式实现单例模式 7 | * 8 | */ 9 | 10 | public class Singleton { 11 | 12 | private volatile static Singleton uniqueInstance; 13 | 14 | // 一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 15 | //保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是 立即可见的。 16 | //禁止进行指令重排序。 17 | 18 | private Singleton() {} 19 | 20 | public static Singleton getInstance() { 21 | //延迟加载 22 | if (uniqueInstance == null) { 23 | //加线程锁 24 | synchronized (Singleton.class) { 25 | if (uniqueInstance == null) { 26 | uniqueInstance = new Singleton(); 27 | } 28 | } 29 | } 30 | return uniqueInstance; 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /废话设计模式系列/建造者模式/代码/builder/Director.java: -------------------------------------------------------------------------------- 1 | package code.builder; 2 | 3 | public class Director { 4 | 5 | private Builder builder ; 6 | 7 | public Director(Builder builder){ 8 | this.builder = builder ; 9 | } 10 | 11 | public Computer createComputer(String cpu,String hardDisk,String mainBoard,String memory,String keyboard,String mouse){ 12 | // 具体的工作是装机工去做 13 | this.builder.installMainBoard(mainBoard); 14 | this.builder.installCpu(cpu) ; 15 | this.builder.installMemory(memory); 16 | this.builder.installhardDisk(hardDisk); 17 | this.builder.installKeyBoard(keyboard); 18 | this.builder.installMouse(mouse); 19 | return this.builder.createComputer() ; 20 | } 21 | 22 | 23 | 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/src/main/java/com/jdkcb/demo/exception/MyException.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo.exception; 2 | 3 | public class MyException extends RuntimeException { 4 | 5 | private String errorCode; 6 | private String errorMsg; 7 | 8 | public MyException(String errorCode, String errorMsg) { 9 | this.errorCode = errorCode; 10 | this.errorMsg = errorMsg; 11 | } 12 | 13 | 14 | public String getErrorCode() { 15 | return errorCode; 16 | } 17 | 18 | public void setErrorCode(String errorCode) { 19 | this.errorCode = errorCode; 20 | } 21 | 22 | public String getErrorMsg() { 23 | return errorMsg; 24 | } 25 | 26 | public void setErrorMsg(String errorMsg) { 27 | this.errorMsg = errorMsg; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/src/main/java/com/jdkcb/demo/config/ExceptionConfig.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo.config; 2 | 3 | import com.jdkcb.demo.exception.CommonResult; 4 | import com.jdkcb.demo.exception.MyException; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.bind.annotation.ExceptionHandler; 7 | import org.springframework.web.bind.annotation.RestControllerAdvice; 8 | 9 | @Configuration 10 | public class ExceptionConfig { 11 | 12 | @RestControllerAdvice("com.jdkcb.demo.api") 13 | static class UnifiedExceptionHandler{ 14 | @ExceptionHandler(MyException.class) 15 | public CommonResult handleBusinessException(MyException be){ 16 | return CommonResult.errorResult(be.getErrorCode(), be.getErrorMsg()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/testdatasource1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false 3 | spring.datasource.primary.username=root 4 | spring.datasource.primary.password=root 5 | spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver 6 | 7 | spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/testdatasource2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false 8 | spring.datasource.secondary.username=root 9 | spring.datasource.secondary.password=root 10 | spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver 11 | 12 | 13 | spring.http.encoding.charset=UTF-8 14 | spring.http.encoding.enabled=true 15 | spring.http.encoding.force=true 16 | 17 | -------------------------------------------------------------------------------- /废话设计模式系列/策略模式/代码/strategy/Text.java: -------------------------------------------------------------------------------- 1 | package strategy; 2 | 3 | 4 | /*** 5 | * 6 | * 7 | * @author 韩数 8 | * 测试类 9 | * 10 | */ 11 | public class Text { 12 | 13 | public static void main(String[] args) { 14 | Text t = new Text(); 15 | t.rubberDuckDemoText(); 16 | 17 | 18 | System.out.println("\n现在我们欢迎活的鸭子登场!\n"); 19 | 20 | t.liveDuckDemoText(); 21 | } 22 | 23 | 24 | 25 | public void rubberDuckDemoText() { 26 | 27 | RubberDuck rubberDuck = new RubberDuck(); 28 | rubberDuck.display(); 29 | rubberDuck.performFly(); 30 | rubberDuck.performQuack(); 31 | 32 | 33 | 34 | 35 | } 36 | 37 | public void liveDuckDemoText() { 38 | 39 | LiveDuck liveDuck = new LiveDuck(); 40 | liveDuck.display(); 41 | liveDuck.performFly(); 42 | liveDuck.performQuack(); 43 | 44 | 45 | 46 | 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /Spring学习笔记/事件驱动编程/代码/event/NotifyPublisher.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo.event; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class NotifyPublisher implements ApplicationContextAware { 10 | 11 | private ApplicationContext ctx; 12 | 13 | @Override 14 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 15 | this.ctx= applicationContext; 16 | } 17 | 18 | // 发布一个消息 19 | public void publishEvent(int status, String msg) { 20 | if (status == 0) { 21 | ctx.publishEvent(new NotifyEvent(this, msg)); 22 | } else { 23 | ctx.publishEvent(new NotifyEvent(this,msg)) ; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /废话设计模式系列/观察者模式/代码/observer/CurrentConditionsDisplay.java: -------------------------------------------------------------------------------- 1 | package observer; 2 | 3 | /*** 4 | * 5 | * @author 韩数 6 | * 定义一个客户端显示当前数据 7 | * 8 | */ 9 | public class CurrentConditionsDisplay implements Observer, DisplayElement { 10 | 11 | private float temperature;//温度 12 | private float humidity;//湿度 13 | private float pressure;//气压 14 | private Subject weatherData; 15 | 16 | public CurrentConditionsDisplay(Subject weatherData) { 17 | this.weatherData = weatherData;//保存 weatherData对象,方便后续移除该观察者 18 | weatherData.registerObserver(this);//注册观察者 19 | } 20 | 21 | //通知方法 22 | @Override 23 | public void update(float temperature, float humidity, float pressure) { 24 | this.temperature = temperature; 25 | this.humidity = humidity; 26 | this.pressure=pressure; 27 | display();//调用显示方法显示数据 28 | } 29 | 30 | //显示方法 31 | @Override 32 | public void display() { 33 | System.out.println("温度:"+temperature+"\n湿度:"+humidity+"\n气压:"+pressure); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /用java实现一个简单的区块链/代码/blockchaindemo/src/main/java/com/jdkcb/blockchain/BlockChainTest.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.blockchain; 2 | 3 | import com.jdkcb.blockchain.enity.Block; 4 | 5 | /** 6 | * Created on 2018/3/10 0010. 7 | * 8 | * @author zlf 9 | * @email i@merryyou.cn 10 | * @since 1.0 11 | */ 12 | public class BlockChainTest { 13 | 14 | public static void main(String[] args) { 15 | //第1个区块 16 | Block firstBlock = new Block("我是第1个区块", "0"); 17 | firstBlock.mineBlock(5); 18 | System.out.println("第1个区块hash: " + firstBlock.hash); 19 | 20 | //第2个区块 21 | Block secondBlock = new Block("我是第2个区块", firstBlock.hash); 22 | secondBlock.mineBlock(5); 23 | System.out.println("第2个区块hash: " + secondBlock.hash); 24 | 25 | //第3个区块 26 | Block thirdBlock = new Block("我是第3个区块", secondBlock.hash); 27 | thirdBlock.mineBlock(5); 28 | System.out.println("第3个区块hash: " + thirdBlock.hash); 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /废话设计模式系列/观察者模式/代码/observer/observerUseJava/WeatherData.java: -------------------------------------------------------------------------------- 1 | package observer.observerUseJava; 2 | 3 | import java.util.Observable; 4 | 5 | /*** 6 | * 7 | * @author 新建一个WeatherData类,需要继承java.util.Observable来实现观察者模式。 8 | * 9 | */ 10 | 11 | public class WeatherData extends Observable { 12 | private float temperature; 13 | private float humidity; 14 | private float pressure; 15 | 16 | public WeatherData() { } 17 | 18 | public void measurementsChanged() { 19 | setChanged();//激活改变状态 20 | notifyObservers();//调用通知 21 | } 22 | 23 | public void setMeasurements(float temperature, float humidity, float pressure) { 24 | this.temperature = temperature; 25 | this.humidity = humidity; 26 | this.pressure = pressure; 27 | measurementsChanged(); 28 | } 29 | 30 | public float getTemperature() { 31 | return temperature; 32 | } 33 | 34 | public float getHumidity() { 35 | return humidity; 36 | } 37 | 38 | public float getPressure() { 39 | return pressure; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Django系列学习笔记/Django-redis 学习笔记.md: -------------------------------------------------------------------------------- 1 | # Django-redis 学习笔记 2 | 3 | 安装: 4 | 5 | ```linux 6 | pip install django-redis 7 | ``` 8 | 9 | 在项目中setting文件中配置缓存redis链接: 10 | 11 | ```python 12 | CACHES = { 13 | "default": { 14 | "BACKEND": "django_redis.cache.RedisCache", 15 | "LOCATION": "redis://ip:6379",##地址 16 | "OPTIONS": { 17 | "CLIENT_CLASS": "django_redis.client.DefaultClient", 18 | "CONNECTION_POOL_KWARGS": {"max_connections": 100}, 19 | "PASSWORD": "123456",##密码 20 | } 21 | } 22 | } 23 | ``` 24 | 25 | 在视图中调用: 26 | 27 | ```python 28 | from django_redis import get_redis_connection 29 | from django.core.cache import cache 30 | 31 | #常用方法 32 | ##向缓存中放入key value 33 | cache.set("key", "value", timeout=None) 34 | ##timeout=0 立即过期 timeout=None 永不超时 时间以秒为单位 35 | 36 | ##ttl 任何有超时设置的 key 的超时值. 37 | cache.ttl("foo") 38 | 39 | ##通配符搜索key 40 | cache.keys("foo_*") 41 | 42 | ##获取key对应的value 43 | cache.get("value") 44 | 45 | 46 | ``` 47 | 48 | -------------------------------------------------------------------------------- /废话设计模式系列/建造者模式/代码/builder/ComputerBuilder.java: -------------------------------------------------------------------------------- 1 | package code.builder; 2 | 3 | public class ComputerBuilder implements Builder{ 4 | 5 | private Computer computer = new Computer() ; 6 | 7 | @Override 8 | public void installMainBoard(String mainBoard) { 9 | computer.setMainBoard(mainBoard); 10 | 11 | } 12 | 13 | @Override 14 | public void installCpu(String cpu) { 15 | computer.setCpu(cpu); 16 | 17 | } 18 | 19 | @Override 20 | public void installhardDisk(String hardDisk) { 21 | computer.setHardDisk(hardDisk); 22 | } 23 | 24 | @Override 25 | public void installMemory(String memory) { 26 | computer.setMemory(memory); 27 | } 28 | 29 | @Override 30 | public void installKeyBoard(String keyboard) { 31 | computer.setKeyboard(keyboard); 32 | } 33 | 34 | @Override 35 | public void installMouse(String mouse) { 36 | computer.setMouse(mouse); 37 | } 38 | 39 | @Override 40 | public Computer createComputer() { 41 | // TODO Auto-generated method stub 42 | return computer; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/src/main/java/com/jdkcb/demo/api/UserController.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo.api; 2 | 3 | import com.jdkcb.demo.exception.MyException; 4 | import com.jdkcb.demo.service.MyService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | 10 | @RestController 11 | public class UserController { 12 | 13 | 14 | 15 | @GetMapping("/hello") 16 | public String HelloWord(){ 17 | throw new MyException("1001", "根据ID查询用户异常"); 18 | } 19 | 20 | 21 | @GetMapping("/test") 22 | public String test(){ 23 | int num = 1/0; 24 | return "Hello Word"; 25 | } 26 | 27 | 28 | @GetMapping("/test1") 29 | public String test1(){ 30 | String name = null; 31 | if(name == null){ 32 | throw new MyException("101","用户名为空"); 33 | } 34 | return "Hello Word"; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /用java实现一个简单的区块链/代码/blockchaindemo/src/main/java/com/jdkcb/blockchain/utils/StringUtil.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.blockchain.utils; 2 | 3 | import java.security.MessageDigest; 4 | 5 | /** 6 | * 生成电子签名的工具类 7 | * Created on 2018/3/10 0010. 8 | * 9 | * @author zlf 10 | * @email i@merryyou.cn 11 | * @since 1.0 12 | */ 13 | public class StringUtil { 14 | 15 | public static String applySha256(String input) { 16 | try { 17 | MessageDigest digest = MessageDigest.getInstance("SHA-256"); 18 | byte[] hash = digest.digest(input.getBytes("UTF-8")); 19 | StringBuffer hexString = new StringBuffer(); 20 | for (int i = 0; i < hash.length; i++) { 21 | String hex = Integer.toHexString(0xff & hash[i]); 22 | if (hex.length() == 1) hexString.append('0'); 23 | hexString.append(hex); 24 | } 25 | return hexString.toString(); 26 | } catch (Exception e) { 27 | throw new RuntimeException(e); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /废话设计模式系列/观察者模式/代码/observer/observerUseJava/CurrentConditionsDisplay.java: -------------------------------------------------------------------------------- 1 | package observer.observerUseJava; 2 | 3 | import java.util.Observable; 4 | import java.util.Observer; 5 | 6 | import observer.DisplayElement; 7 | 8 | public class CurrentConditionsDisplay implements Observer, DisplayElement { 9 | 10 | Observable observable;//定义一个Observable 11 | private float temperature; 12 | private float humidity; 13 | 14 | public CurrentConditionsDisplay(Observable observable) { 15 | this.observable = observable; 16 | observable.addObserver(this);//注册该观察者 17 | } 18 | 19 | @Override 20 | public void update(Observable obs, Object arg) { 21 | if (obs instanceof WeatherData) {//如果obs是WeatherData类的一个实例 22 | WeatherData weatherData = (WeatherData)obs; 23 | //获取观察者需要的数据 24 | this.temperature = weatherData.getTemperature(); 25 | this.humidity = weatherData.getHumidity(); 26 | display();//通知显示 27 | } 28 | } 29 | 30 | //显示方法 31 | @Override 32 | public void display() { 33 | System.out.println("温度:"+temperature+"\n湿度:"+humidity); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/src/main/java/com/jdkcb/mybatisstuday/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.mybatisstuday.controller; 2 | 3 | import com.jdkcb.mybatisstuday.mapper.one.PrimaryUserMapper; 4 | import com.jdkcb.mybatisstuday.mapper.two.SecondaryUserMapper; 5 | import com.jdkcb.mybatisstuday.pojo.User; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | import java.util.List; 11 | 12 | @RestController 13 | public class UserController { 14 | 15 | @Autowired 16 | private PrimaryUserMapper primaryUserMapper; 17 | @Autowired 18 | private SecondaryUserMapper secondaryUserMapper; 19 | @RequestMapping("primary") 20 | public Object primary(){ 21 | List list = primaryUserMapper.findAll(); 22 | return list; 23 | } 24 | @RequestMapping("secondary") 25 | public Object secondary (){ 26 | List list = secondaryUserMapper.findAll(); 27 | return list; 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /废话设计模式系列/策略模式/代码/strategy/Duck.java: -------------------------------------------------------------------------------- 1 | package strategy; 2 | 3 | /*** 4 | * 5 | * 6 | * @author 韩数 7 | * 设计模式之策略模式 8 | * Github地址:https://github.com/hanshuaikang/design-pattern-java 9 | * 10 | */ 11 | 12 | 13 | public abstract class Duck { 14 | 15 | /* 16 | * 面向超类编程,主类Duck只保留所有鸭子通用不变的特征比如游泳 17 | * 变化的部分单独封装,提高代码的弹性,避免因为单一的向下继承 18 | * 造成的代码的灵活性降低,避免过于耦合情况的发生。 19 | */ 20 | 21 | FlyBehavior flyBehavior; 22 | QuackBehavior quackBehavior; 23 | 24 | public Duck() { 25 | } 26 | 27 | 28 | //定义set方法,可以动态的设定鸭子飞行的行为 29 | public void setFlyBehavior (FlyBehavior fb) { 30 | flyBehavior = fb; 31 | } 32 | 33 | //定义set方法,可以动态的设定鸭子叫声的行为 34 | public void setQuackBehavior(QuackBehavior qb) { 35 | quackBehavior = qb; 36 | } 37 | 38 | //鸭子的外表,这里定义为抽象方法,父类只做声明,不负责实现 39 | abstract void display(); 40 | 41 | //鸭子的行为,Duck类不适合实现,交给相应的模块实现。 42 | public void performFly() { 43 | flyBehavior.fly(); 44 | } 45 | 46 | public void performQuack() { 47 | quackBehavior.quack(); 48 | } 49 | 50 | 51 | //所有鸭子都会游泳 52 | public void swim() { 53 | System.out.println("All ducks float, even decoys!"); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/src/main/java/com/jdkcb/mybatisstuday/pojo/User.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.mybatisstuday.pojo; 2 | 3 | public class User { 4 | 5 | 6 | 7 | private Integer user_id; 8 | private String user_name; 9 | private Integer user_age; 10 | 11 | 12 | public Integer getUser_id() { 13 | return user_id; 14 | } 15 | 16 | public void setUser_id(Integer user_id) { 17 | this.user_id = user_id; 18 | } 19 | 20 | public String getUser_name() { 21 | return user_name; 22 | } 23 | 24 | public void setUser_name(String user_name) { 25 | this.user_name = user_name; 26 | } 27 | 28 | public Integer getUser_age() { 29 | return user_age; 30 | } 31 | 32 | public void setUser_age(Integer user_age) { 33 | this.user_age = user_age; 34 | } 35 | 36 | 37 | 38 | @Override 39 | public String toString() { 40 | return "User{" + 41 | "user_id=" + user_id + 42 | ", user_name='" + user_name + '\'' + 43 | ", user_age=" + user_age + 44 | '}'; 45 | } 46 | 47 | 48 | 49 | 50 | } -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/src/main/java/com/jdkcb/demo/exception/CommonResult.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo.exception; 2 | 3 | public class CommonResult { 4 | 5 | private String errorCode; 6 | private String errorMsg; 7 | private int status; 8 | 9 | 10 | 11 | public static CommonResult errorResult(String errorCode, String errorMsg){ 12 | CommonResult commonResult = new CommonResult<>(); 13 | commonResult.errorCode = errorCode; 14 | commonResult.errorMsg = errorMsg; 15 | commonResult.status = -1; 16 | return commonResult; 17 | } 18 | 19 | public String getErrorCode() { 20 | return errorCode; 21 | } 22 | 23 | public void setErrorCode(String errorCode) { 24 | this.errorCode = errorCode; 25 | } 26 | 27 | public String getErrorMsg() { 28 | return errorMsg; 29 | } 30 | 31 | public void setErrorMsg(String errorMsg) { 32 | this.errorMsg = errorMsg; 33 | } 34 | 35 | public int getStatus() { 36 | return status; 37 | } 38 | 39 | public void setStatus(int status) { 40 | this.status = status; 41 | } 42 | 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/src/main/java/com/jdkcb/demo/advice/MyControllerAdvice.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo.advice; 2 | 3 | 4 | import com.jdkcb.demo.exception.MyException; 5 | import org.springframework.web.bind.annotation.ControllerAdvice; 6 | import org.springframework.web.bind.annotation.ExceptionHandler; 7 | import org.springframework.web.bind.annotation.ResponseBody; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | @ControllerAdvice 13 | public class MyControllerAdvice { 14 | 15 | 16 | @ResponseBody 17 | @ExceptionHandler(value = Exception.class) 18 | public Map exceptionHandler(Exception ex){ 19 | Map map = new HashMap(); 20 | map.put("code",100); 21 | map.put("msg",ex.getMessage()); 22 | //发生异常进行日志记录,写入数据库或者其他处理,此处省略 23 | return map; 24 | } 25 | 26 | 27 | @ResponseBody 28 | @ExceptionHandler(value = MyException.class) 29 | public Map myExceptionHandler(MyException mex){ 30 | Map map = new HashMap(); 31 | map.put("code",mex.getErrorCode()); 32 | map.put("msg",mex.getErrorMsg()); 33 | //发生异常进行日志记录,写入数据库或者其他处理,此处省略 34 | return map; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /废话设计模式系列/建造者模式/代码/builder/Computer.java: -------------------------------------------------------------------------------- 1 | package code.builder; 2 | 3 | public class Computer { 4 | 5 | 6 | private String cpu ; // cpu 7 | private String hardDisk ; //硬盘 8 | private String mainBoard ; // 主板 9 | private String memory ; // 内存 10 | private String keyboard;//键盘 11 | private String mouse; 12 | 13 | 14 | 15 | public String getCpu() { 16 | return cpu; 17 | } 18 | public void setCpu(String cpu) { 19 | this.cpu = cpu; 20 | } 21 | public String getHardDisk() { 22 | return hardDisk; 23 | } 24 | public void setHardDisk(String hardDisk) { 25 | this.hardDisk = hardDisk; 26 | } 27 | public String getMainBoard() { 28 | return mainBoard; 29 | } 30 | public void setMainBoard(String mainBoard) { 31 | this.mainBoard = mainBoard; 32 | } 33 | public String getMemory() { 34 | return memory; 35 | } 36 | public void setMemory(String memory) { 37 | this.memory = memory; 38 | } 39 | public String getKeyboard() { 40 | return keyboard; 41 | } 42 | public void setKeyboard(String keyboard) { 43 | this.keyboard = keyboard; 44 | } 45 | public String getMouse() { 46 | return mouse; 47 | } 48 | public void setMouse(String mouse) { 49 | this.mouse = mouse; 50 | } 51 | @Override 52 | public String toString() { 53 | return "Computer [cpu=" + cpu + ", hardDisk=" + hardDisk + ", mainBoard=" + mainBoard + ", memory=" + memory 54 | + ", keyboard=" + keyboard + ", mouse=" + mouse + "]"; 55 | } 56 | 57 | 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Spring学习笔记/事件驱动编程/代码/event/AysncListenerConfig.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo.event; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.scheduling.annotation.AsyncConfigurer; 6 | import org.springframework.scheduling.annotation.EnableAsync; 7 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 8 | 9 | import java.util.concurrent.Executor; 10 | import java.util.concurrent.LinkedBlockingQueue; 11 | import java.util.concurrent.ThreadPoolExecutor; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | @Configuration 15 | @EnableAsync 16 | public class AysncListenerConfig implements AsyncConfigurer { 17 | /** 18 | * 获取异步线程池执行对象 19 | * 20 | * @return 21 | */ 22 | @Override 23 | @Bean(name = "taskExecutor") 24 | public Executor getAsyncExecutor() { 25 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 26 | executor.initialize(); 27 | executor.setCorePoolSize(10); //核心线程数 28 | executor.setMaxPoolSize(20); //最大线程数 29 | executor.setQueueCapacity(1000); //队列大小 30 | executor.setKeepAliveSeconds(300); //线程最大空闲时间 31 | executor.setThreadNamePrefix("ics-Executor-"); ////指定用于新创建的线程名称的前缀。 32 | executor.setRejectedExecutionHandler( 33 | new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略 34 | return new ExceptionHandlingAsyncTaskExecutor(executor); 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Django系列学习笔记/Django 使用分页插件实现数据库分页查询:.md: -------------------------------------------------------------------------------- 1 | ### Django 使用分页插件实现数据库分页查询: 2 | 3 | `Django`内置了数据库`orm`框架,无须写`sql`语句便可以进行复杂的数据库增删改查操作,我们可以使用`orm`来进行分页查询来手动实现分页,为了方便开发人员,`Django`提供了非常容易上手的分页插件`Paginator`。 4 | 5 | 使用方法也很简单,首先从数据库种获取对象列表,之后封装一个`Paginator`对象,就可以自动的完成分页操作。 6 | 7 | ```python 8 | user = User.objects.get(user_id=user_id) 9 | # 获取当前用户下的所有文件列表 10 | file_list_all = user.file_set.all() 11 | # 封装Paginator对象,其中第一个参数是列表,第二个参数是每页显示的数据数。 12 | paginator = Paginator(file_list_all, 5) 13 | # 传入当前页数就可以获得当前页的数据 14 | # 参数 :当前页 15 | file_list = paginator.page(1) 16 | ``` 17 | 18 | `Paginator`(*object_list*,*per_page*,*orphans = 0*,*allow_empty_first_page = True*) 19 | 20 | **` Paginator`参数**: 21 | 22 | 其中:orphans 和 allow_empty_first_page 为可选参数。 23 | 24 | - `orphans` :是否需要最后一页有比较少的记录,默认值为0,比如数据库中有11条记录,每页5条记录,这样第三页就只有一条记录,这个时候设置 `orphans` 为 1 最后数据将只有两页,其中第一页,五条记录,第二页 六条记录。 25 | - allow_empty_first_page : 是否允许第一页为空,默认为 True,如果设置为False,当第一页为空则会引`EmptyPage`错误。 26 | 27 | **` Paginator`属性:** 28 | 29 | ` Paginator` 提供了一些属性,来帮助我们进行分页: 30 | 31 | - `Paginator.count`:数据总数。 32 | - `Paginator.num_pages`:总页数 33 | - `Paginator.page_range`:返回页码的范围迭代器,比如你有四页数据,则返回[1,2,3,4] 34 | 35 | 其中: `page = paginator.page(1)`会返回一个当前页的Page对象.Page对象同样包含了如下的属性和方法: 36 | 37 | **Page属性**: 38 | 39 | - `number ` : 当前页的页码 40 | - `object_list ` : 当前页的数据查询集 41 | - `paginator` : 当前页的` paginator `对象 42 | 43 | **Page方法:** 44 | 45 | - `has_previous ` : 判断当前页是否有前一页,如果有 返回True。 46 | - `has_next` : 判断当前页是否有下一页,如果有 返回True。 47 | - `previous_page_number `:返回前一页的页码。 48 | - `previous_page_number` :返回下一页的页码。 49 | 50 | 其他方法详见官方文档:[官方文档](https://docs.djangoproject.com/zh-hans/2.2/topics/pagination/) 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /废话设计模式系列/观察者模式/代码/observer/WeatherData.java: -------------------------------------------------------------------------------- 1 | package observer; 2 | 3 | import java.util.*; 4 | 5 | /*** 6 | * 7 | * @author 韩数 8 | * 天气广播者,实现并扩展Subject类 9 | * 10 | */ 11 | public class WeatherData implements Subject { 12 | //定义一个ArrayList对象,用于存储注册的观察者类 13 | private ArrayList observers; 14 | private float temperature;//温度 15 | private float humidity;//湿度 16 | private float pressure;//气压 17 | 18 | //在构造器中初始化observers对象 19 | public WeatherData() { 20 | observers = new ArrayList(); 21 | } 22 | 23 | //注册一个观察者 24 | @Override 25 | public void registerObserver(Observer o) { 26 | observers.add(o); 27 | } 28 | 29 | //移除一个观察者 30 | @Override 31 | public void removeObserver(Observer o) { 32 | int i = observers.indexOf(o); 33 | if (i >= 0) { 34 | observers.remove(i); 35 | } 36 | } 37 | 38 | //通知所有观察者 39 | @Override 40 | public void notifyObservers() { 41 | for (int i = 0; i < observers.size(); i++) { 42 | //Observer是所以观察者的父类,此处运用了多态 43 | Observer observer = (Observer)observers.get(i); 44 | observer.update(temperature, humidity, pressure);//通知更新 45 | } 46 | } 47 | 48 | public void measurementsChanged() { 49 | //触发通知 50 | notifyObservers(); 51 | } 52 | 53 | public void setMeasurements(float temperature, float humidity, float pressure) { 54 | this.temperature = temperature; 55 | this.humidity = humidity; 56 | this.pressure = pressure; 57 | //数据改变触发通知 58 | measurementsChanged(); 59 | } 60 | 61 | //get方法,用于拉数据,后面会讲到 62 | public float getTemperature() { 63 | return temperature; 64 | } 65 | 66 | public float getHumidity() { 67 | return humidity; 68 | } 69 | 70 | public float getPressure() { 71 | return pressure; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.1.7.RELEASE 9 | 10 | 11 | com.jdkcb 12 | demo 13 | 0.0.1-SNAPSHOT 14 | demo 15 | Demo project for Spring Boot 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-actuator 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-test 34 | test 35 | 36 | 37 | 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-maven-plugin 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /用java实现一个简单的区块链/代码/blockchaindemo/src/main/java/com/jdkcb/blockchain/enity/Block.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.blockchain.enity; 2 | 3 | 4 | 5 | import com.jdkcb.blockchain.utils.StringUtil; 6 | 7 | import java.util.Date; 8 | 9 | /** 10 | * 区块 11 | * Created on 2018/3/10 0010. 12 | * 13 | * @author zlf 14 | * @email i@merryyou.cn 15 | * @since 1.0 16 | */ 17 | public class Block { 18 | 19 | /** 20 | * 当前区块的hash 21 | */ 22 | public String hash; 23 | 24 | /** 25 | * 前一个区块的hash 26 | */ 27 | public String previousHash; 28 | 29 | /** 30 | * 当前区块的数据 31 | */ 32 | private String data; 33 | 34 | /** 35 | * 时间戳 36 | */ 37 | private long timeStamp; 38 | 39 | private int nonce; 40 | 41 | 42 | public Block(String hash, String previousHash, String data) { 43 | this.hash = hash; 44 | this.previousHash = previousHash; 45 | this.data = data; 46 | } 47 | 48 | public Block(String data, String previousHash) { 49 | this.previousHash = previousHash; 50 | this.data = data; 51 | this.timeStamp = new Date().getTime(); 52 | this.hash = calculateHash(); 53 | } 54 | 55 | public String calculateHash() { 56 | String calculatedhash = StringUtil.applySha256( 57 | previousHash + 58 | Long.toString(timeStamp) + 59 | Integer.toString(nonce) + 60 | data); 61 | return calculatedhash; 62 | } 63 | 64 | public void mineBlock(int difficulty) { 65 | String target = new String(new char[difficulty]).replace('\0', '0'); //Create a string with difficulty * "0" 66 | while(!hash.substring( 0, difficulty).equals(target)) { 67 | nonce ++; 68 | hash = calculateHash(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Django系列学习笔记/django自定义登陆拦截器.md: -------------------------------------------------------------------------------- 1 | # django 自定义登陆拦截器 2 | 3 | django 没有提供类似于Spring中Interceptor的概念,但是提供了一系列的中间件来解决这个问题。 4 | 5 | django的中间件主要分为五种: 6 | 7 | - 请求(Request)中间件->对应函数process_request 8 | - 视图(View)中间件->对应函数process_view 9 | - 模板(Template)中间件->对应函数`process_template_response` 10 | - 响应(Response)中间件->对应函数`process_response` 11 | - 异常(Exception)中间件->对应函数`process_exception` 12 | 13 | 代码实现: 14 | 15 | 新建一个python文件,代码如下 `process_request`为具体请求拦截的逻辑实现: 16 | 17 | ```python 18 | # !/usr/bin/env python 19 | # -*- coding: utf-8 -*- 20 | from django.shortcuts import HttpResponseRedirect, redirect 21 | from django.urls import reverse 22 | 23 | try: 24 | from django.utils.deprecation import MiddlewareMixin # Django 1.10.x 25 | except ImportError: 26 | MiddlewareMixin = object # Django 1.4.x - Django 1.9.x 27 | 28 | 29 | class UserMiddleware(MiddlewareMixin): 30 | 31 | def process_request(self, request): 32 | ## 判断来源请求是否需要用户登陆 33 | if request.path != '/transfer/login/' and request.path != '/transfer/register/': 34 | # 如果需要用户登陆,则判断session中是否存在用户id(也可以判断其他的,不一定是seesion,tocken也可以) 35 | if request.session.get('user_id') is not None: 36 | ##放行请求 37 | pass 38 | else: 39 | # 请求重定向到登陆界面 40 | return redirect(reverse('transfer:login')) 41 | ``` 42 | 43 | 44 | 45 | 第二步就是在setting文件中,添加我们自定义的请求拦截器: 46 | 47 | ```python 48 | 49 | MIDDLEWARE = [ 50 | 'django.middleware.security.SecurityMiddleware', 51 | 'django.contrib.sessions.middleware.SessionMiddleware', 52 | 'django.middleware.common.CommonMiddleware', 53 | 'django.middleware.csrf.CsrfViewMiddleware', 54 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 55 | 'django.contrib.messages.middleware.MessageMiddleware', 56 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 57 | 'orangeproject.usermiddleware.UserMiddleware',## 这里 58 | ] 59 | ``` 60 | 61 | 重启项目,大功告成 62 | 63 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/src/main/java/com/jdkcb/mybatisstuday/config/SecondaryDataSourceConfig.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.mybatisstuday.config; 2 | 3 | 4 | import org.apache.ibatis.session.SqlSessionFactory; 5 | import org.mybatis.spring.SqlSessionFactoryBean; 6 | import org.mybatis.spring.SqlSessionTemplate; 7 | import org.mybatis.spring.annotation.MapperScan; 8 | import org.springframework.beans.factory.annotation.Qualifier; 9 | import org.springframework.boot.context.properties.ConfigurationProperties; 10 | import org.springframework.boot.jdbc.DataSourceBuilder; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 14 | 15 | import javax.sql.DataSource; 16 | 17 | @Configuration 18 | @MapperScan(basePackages = "com.jdkcb.mybatisstuday.mapper.two", sqlSessionFactoryRef = "SecondarySqlSessionFactory") 19 | public class SecondaryDataSourceConfig { 20 | 21 | 22 | @Bean(name = "SecondaryDataSource") 23 | @ConfigurationProperties(prefix = "spring.datasource.secondary") 24 | public DataSource getSecondaryDataSource() { 25 | return DataSourceBuilder.create().build(); 26 | } 27 | @Bean(name = "SecondarySqlSessionFactory") 28 | public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("SecondaryDataSource") DataSource datasource) 29 | throws Exception { 30 | SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); 31 | bean.setDataSource(datasource); 32 | bean.setMapperLocations( 33 | new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/two/*.xml")); 34 | return bean.getObject(); 35 | } 36 | @Bean("SecondarySqlSessionTemplate") 37 | public SqlSessionTemplate secondarySqlSessionTemplate( 38 | @Qualifier("SecondarySqlSessionFactory") SqlSessionFactory sessionfactory) { 39 | return new SqlSessionTemplate(sessionfactory); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /java学习笔记/到底什么是应用上下文?.md: -------------------------------------------------------------------------------- 1 | # 到底什么是应用上下文? 2 | 3 | ## 前言: 4 | 5 | 日常开发总是会看到上下文三个字,应用上下文,执行上下文等等,刚开始的时候看到这三个字其实也没什么感觉,就跟平常吃饭睡觉一样根本不会多想 6 | 7 | 直到有一天,一个可怕的念头突然出现在了我的脑海中 8 | 9 | **到底什么是应用上下文?** 10 | 11 |  12 | 13 | ?????????? 14 | 15 | 当时我就跟早上刚起床那状态一样,环顾四周,眼睛里充满了疑惑和迷茫,上下文那三个字就像迷雾中的一个影子,我好像知道那是个啥,但是我不知道那究竟是个啥,但是我有找不到别的那个啥去解释这个啥。 16 | 17 | nb,汉字文化博大精深。 18 | 19 | 可能是自己平时没有了解过这块知识吧,于是我问了问身边的小伙伴们什么是应用上下文 20 | 21 | ?????????? 22 | 23 | 就是,上下文,就是,,那种,语境啊,很普通的一个概念。 24 | 25 | 对不起,噗,我想起来好笑的事情(**致敬美人鱼梗**) 26 | 27 | 于是脑子里一团毛线球的我带着深深疑问寄希望于百度,企图在字里行间发现java编程的真谛。 28 | 29 | > 上下文,即语境、语意,是语言学科(语言学、社会语言学、篇章分析、语用学、符号学等)的概念。 30 | > 语意分析(semantic analysis)技术系指将一长串的文字或内容,从其中分析出该个段落的摘要以及大意,甚至更进一步,将整篇文章的文意整理出来。此项技术可以应用在解读影片、音讯等档案,使得搜索引擎能够搜寻到文字以外的物件,方便使用者省去大量时间观看影片、聆听音讯,同时也可以帮助使用者提前了解影片与音讯的内容。 31 | 32 | 我,%#¥……¥¥#¥@#%¥¥%……#¥#@ 33 | 34 |  35 | 36 | 37 | 38 | 所以到底什么是上下文,它在编程中的具体起什么作用,于是带着这个问题,我踏上了寻找答案的漫长的旅途中,终于,苍天不负有心人,我还是没有找到具体的解释。 39 | 40 | 好吧,今天发文章就到这里结束了,虽然上文中说了很多的废话,但是这件事情逐渐没有了下文。 41 | 42 | 等等,你刚才说了上文对吧,又说了下文对吧(致敬jojo梗) 43 | 44 | 是啊,合一起不就是上下文吗? 45 | 46 | 卧槽 47 | 48 | 所以,上下文代表的其实是一个整体的环境,就比如说这篇文章,我们可以说**上文中**,访问到上文所陈述的内容,也可以说**下文中**,访问到下文中的内容,而我们这篇文章中每一段文字所代表的意思,是要根据我们的上下文来决定的,因为你随便拿出来一句话不去结合整体的语境去理解出来的意思肯定不是准确的,所以,我们这篇文章的上下文就是我们整篇的中心思想。 49 | 50 | 举个例子,比如我们在Spring中的数据源,就是存储在上下文中的,这个时候,不论是哪里的代码,都是可以通过上下文获取到数据连接并进行相关的操作的,同样的,我们可以在任何地方通过spring中的上下文获取到bean和其他的配置信息,当然,这里的任何地方是我们Spring初始化工作完成之后的地方。 51 | 52 | 上下文中通常指的是我们当时运行的环境,比如说同样是break语句,在if里面和在switch里面作用却不一样,难道是break这个语句变了吗?当然是没有,是它当时的环境发送了变化,也就是上下文环境发生了变化。 53 | 54 | 再者,比如我们应用进行线程切换的时候,切换前都会把线程的状态信息暂时储存在寄存器中,这里的上下文就包括了当时寄存器的值,把寄存器的值都保存起来,等下次该线程又得到CPU时间的时候再恢复寄存器的值,这样线程才能正确的运行。 55 | 56 | 写到现在,我发现我的思路并没有清晰多少,但是对上下文已经有了整体的理解 57 | 58 | **上下文,上下文代表了程序当下所运行的环境,联系你整个app的生命周期与资源调用,是程序可以访问到的所有资源的总和,资源可以是一个变量,也可以是一个对象的引用。** 59 | 60 | 这大概就是我对上下文的理解了,如果大家有不同的理解,欢迎留言在本篇文章下方。 61 | 62 | 我是韩数,关注我,有你好果子吃~ 63 | 64 | -------------------------------------------------------------------------------- /Spring学习笔记/事件驱动编程/代码/event/ExceptionHandlingAsyncTaskExecutor.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.demo.event; 2 | 3 | import org.springframework.core.task.AsyncTaskExecutor; 4 | 5 | import java.util.concurrent.Callable; 6 | import java.util.concurrent.Future; 7 | 8 | public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor { 9 | 10 | private AsyncTaskExecutor executor; 11 | 12 | public ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) { 13 | this.executor = executor; 14 | } 15 | 16 | 17 | ////用独立的线程来包装,@Async其本质就是如此 18 | public void execute(Runnable task) { 19 | executor.execute(createWrappedRunnable(task)); 20 | } 21 | public void execute(Runnable task, long startTimeout) { 22 | //用独立的线程来包装,@Async其本质就是如此 23 | executor.execute(createWrappedRunnable(task), startTimeout); 24 | } 25 | public Future submit(Runnable task) { return executor.submit(createWrappedRunnable(task)); 26 | //用独立的线程来包装,@Async其本质就是如此。 27 | } 28 | public Future submit(final Callable task) { 29 | //用独立的线程来包装,@Async其本质就是如此。 30 | return executor.submit(createCallable(task)); 31 | } 32 | 33 | private Callable createCallable(final Callable task) { 34 | return new Callable(){ 35 | 36 | @Override 37 | public Object call() throws Exception { 38 | try { 39 | return task.call(); 40 | } catch (Exception ex) { 41 | handle(ex); 42 | throw ex; 43 | } 44 | } 45 | }; 46 | } 47 | 48 | private Runnable createWrappedRunnable(final Runnable task) { 49 | return new Runnable() { 50 | public void run() { 51 | try { 52 | task.run(); 53 | } catch (Exception ex) { 54 | handle(ex); 55 | } 56 | } 57 | }; 58 | } 59 | private void handle(Exception ex) { 60 | //具体的异常逻辑处理的地方 61 | System.err.println("Error during @Async execution: " + ex); 62 | } 63 | } -------------------------------------------------------------------------------- /用java实现一个简单的区块链/代码/blockchaindemo/src/main/java/com/jdkcb/blockchain/BlockChainListTest.java: -------------------------------------------------------------------------------- 1 | package com.jdkcb.blockchain; 2 | 3 | import com.jdkcb.blockchain.enity.Block; 4 | import com.jdkcb.blockchain.utils.JsonUtil; 5 | 6 | import java.util.ArrayList; 7 | 8 | /** 9 | * Created on 2018/3/10 0010. 10 | * 11 | * @author zlf 12 | * @email i@merryyou.cn 13 | * @since 1.0 14 | */ 15 | public class BlockChainListTest { 16 | 17 | //这玩意就是我们的区块链,存储我们所有的区块信息。(简陋版) 18 | public static ArrayList blockChain = new ArrayList(); 19 | 20 | //挖矿的难度,就是计算出来的hash前几个字符是0才是合法的。 21 | public static int difficulty = 5; 22 | 23 | public static void main(String[] args) { 24 | blockChain.add(new Block("我是第1个区块", "0")); 25 | blockChain.get(0).mineBlock(difficulty); 26 | 27 | blockChain.add(new Block("我是第2个区块", blockChain.get(blockChain.size() - 1).hash)); 28 | blockChain.get(1).mineBlock(difficulty); 29 | 30 | blockChain.add(new Block("我是第3个区块", blockChain.get(blockChain.size() - 1).hash)); 31 | blockChain.get(2).mineBlock(difficulty); 32 | 33 | System.out.println("区块链是否合法: " + isChainValid()); 34 | System.out.println(JsonUtil.toJson(blockChain)); 35 | } 36 | 37 | 38 | public static Boolean isChainValid(){ 39 | 40 | Block currentBlock; 41 | Block previousBlock; 42 | boolean flag = true; 43 | String hashTarget = new String(new char[difficulty]).replace('\0', '0'); 44 | 45 | //循环遍历列表检验hash 46 | for(int i=1;i An image is a read-only template with instructions for creating a Docker container. Often, an image is based on another image, with some additional customization. For example, you may build an image which is based on the ubuntu image, but installs the Apache web server and your application, as well as the configuration details needed to make your application run.‘ 20 | > 21 | > 映像是一个只读模板,带有创建Docker容器的指令。通常,一个映像是基于另一个映像的,还需要进行一些额外的定制。例如,您可以构建一个基于ubuntu映像的映像,但是安装Apache web服务器和您的应用程序,以及使您的应用程序运行所需的配置细节。 22 | 23 | 24 | 25 | PS:一个镜像可以创建多个容器。 26 | 27 | 28 | 29 | ## 容器: 30 | 31 | 容器是用镜像创建的运行实例。 32 | 33 | 每个容器都可以被启动,开始,停止,删除,同时容器之间相互隔离,保证应用运行期间的安全。 34 | 35 | 我们可以把容器理解为一个精简版的linux操作系统,包括root用户权限,进程空间,用户空间和网络空间等等这些,然后加上再它之上运行的应用程序。 36 | 37 | 比如我们现在基于mysql镜像创建了一个容器,那么,这个容器其实并不是只有一个mysql程序,而是mysql同样也是安装运行在我们容器内的linux环境中的。 38 | 39 | ### 容器和镜像的关系: 40 | 41 | 再说这个问题之前,我们不妨先来看一下下面这段java代码: 42 | 43 | ```java 44 | Person p = new Person(); 45 | Person p1 = new Person(); 46 | Person p2 = new Person(); 47 | ``` 48 | 49 | 镜像在这里就是我们的Person,容器就是一个个Person类的实例。一个Person可以创建多个实例,一个镜像也可以创建多个容器。 50 | 51 | 52 | 53 | ## 仓库: 54 | 55 | 仓库相对来说就比较容易理解了,仓库(Repository)是集中存放镜像文件的场所。 56 | 57 | 仓库分为公开仓库和私有仓库,目前的话,全世界最大的仓库是Docker官方的 [Docker Hub](https://hub.docker.com/) 58 | 59 | 由于一些不可抗拒的因素,导致我们如果从Docker Hub上下载公开的镜像是非常蛋疼的,这点大家可以参考你用百度网盘官方下载时的感觉。所以,国内我们一般使用阿里云或者网易云的镜像仓库。 60 | 61 | 62 | 63 | 镜像 容器 仓库 他们三者之间的关系图如下: 64 | 65 |  66 | 67 | 68 | 69 | 70 | ## 总结: 71 | 72 | 今天呢,我们简单的描述了一下Docker三要素,镜像,容器和仓库,在之后的学习中我们会经常看到镜像和容器这两个概念,同时也会编写我们自己的DockerFile构建我们自定义的镜像文件。 73 | 74 | 最后,相关笔记已经同步开源至Github: 75 | 76 | 有需要的同学可以前去下载: 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /废话设计模式系列/建造者模式/代码/builder/fast/Computer.java: -------------------------------------------------------------------------------- 1 | package code.builder.fast; 2 | 3 | public class Computer { 4 | 5 | private String cpu ; // cpu 6 | private String hardDisk ; //硬盘 7 | private String mainBoard ; // 主板 8 | private String memory ; // 内存 9 | private String keyboard;//键盘 10 | private String mouse;//鼠标 11 | 12 | //定义成private,使其不能被直接创建出来 13 | private Computer() {} 14 | 15 | 16 | public String getCpu() { 17 | return cpu; 18 | } 19 | 20 | public void setCpu(String cpu) { 21 | this.cpu = cpu; 22 | } 23 | 24 | public String getHardDisk() { 25 | return hardDisk; 26 | } 27 | 28 | public void setHardDisk(String hardDisk) { 29 | this.hardDisk = hardDisk; 30 | } 31 | 32 | public String getMainBoard() { 33 | return mainBoard; 34 | } 35 | 36 | public void setMainBoard(String mainBoard) { 37 | this.mainBoard = mainBoard; 38 | } 39 | 40 | public String getMemory() { 41 | return memory; 42 | } 43 | 44 | public void setMemory(String memory) { 45 | this.memory = memory; 46 | } 47 | 48 | public String getKeyboard() { 49 | return keyboard; 50 | } 51 | 52 | public void setKeyboard(String keyboard) { 53 | this.keyboard = keyboard; 54 | } 55 | 56 | public String getMouse() { 57 | return mouse; 58 | } 59 | 60 | public void setMouse(String mouse) { 61 | this.mouse = mouse; 62 | } 63 | 64 | 65 | @Override 66 | public String toString() { 67 | return "Computer [cpu=" + cpu + ", hardDisk=" + hardDisk + ", mainBoard=" + mainBoard + ", memory=" + memory 68 | + ", keyboard=" + keyboard + ", mouse=" + mouse + "]"; 69 | } 70 | 71 | 72 | //Builder 静态内部类 73 | public static class ComputerBuilder { 74 | 75 | 76 | //创建computer实例 77 | private Computer computer = new Computer(); 78 | //必须的参数 79 | public ComputerBuilder(String cpu,String hardDisk,String mainBoard,String memory) { 80 | computer.setCpu(cpu); 81 | computer.setHardDisk(hardDisk); 82 | computer.setMainBoard(mainBoard); 83 | computer.setMemory(memory); 84 | } 85 | 86 | //以下是额外可附加的配置 87 | public ComputerBuilder bulidKeyBoard(String keyboard) { 88 | computer.setKeyboard(keyboard); 89 | return this; 90 | } 91 | 92 | public ComputerBuilder buildMouse(String mouse) { 93 | computer.setMouse(mouse); 94 | return this; 95 | } 96 | 97 | 98 | public Computer create() { 99 | if (computer==null){ 100 | throw new IllegalStateException("computer is null"); 101 | } 102 | return computer; 103 | } 104 | 105 | 106 | } 107 | 108 | 109 | 110 | } 111 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.jdkcb 7 | mybatisstuday 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | mybatisstuday 12 | This my mybatis Studay demo 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.1.7.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-web 32 | 33 | 34 | 35 | org.mybatis.spring.boot 36 | mybatis-spring-boot-starter 37 | 1.3.2 38 | 39 | 40 | 41 | mysql 42 | mysql-connector-java 43 | 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-test 49 | test 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-maven-plugin 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | src/main/java 67 | 68 | **/*.xml 69 | 70 | 71 | 72 | 73 | 74 | src/main/resources 75 | 76 | **/*.* 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /废话设计模式系列/工厂模式/设计模式之工厂模式三连发.md: -------------------------------------------------------------------------------- 1 | # 工厂模式三连发之简单工厂模式 2 | 3 | ### 前言: 4 | 5 | 本篇文章电子版和配套代码下载地址:https://github.com/hanshuaikang/design-pattern-java 6 | 7 | 工厂模式也算是日常开发中应用比较多的设计模式,在Spring IOC 提供了BeanFactory,算是工厂模式的一种实现,当然看标题就可以知道,工厂模式针对不同的应用场景衍生出来很多个版本,目前应用的最广的是 简单工厂模式 ,抽象工厂模式,工厂方法模式这三种,由于按照我们一贯的风格,一次性将这三种写完会导致这篇文章将会很长,我不管,笔记本自带的键盘太硬了,我太懒了,我不想一次性写完了(气鼓鼓),除非你点关注,写个评论,立马发!哼 8 | 9 | ### 简单工厂模式定义: 10 | 11 | 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。 12 | 13 | ### 优/缺点: 14 | 15 | **优点:** 16 | 17 | 1. 一个调用者想创建一个对象,只要知道其名称就可以了。 18 | 2. 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 19 | 3. 屏蔽产品的具体实现,调用者只关心产品的接口。 20 | 21 | **缺点:** 22 | 23 | 1. 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。 24 | 25 | ### 应用场景: 26 | 27 | - spring IOC实现 28 | - 等 29 | - 等 30 | - 等 31 | 32 | ### 微剧场: 33 | 34 | 阿呆最近想买车,对,你没听错,这次不是什么艾玛电动车,凤凰自行车,而是想买一辆跑车(??捂嘴笑,天哪,谁给我的勇气写这个,看来我是膨胀了)。 35 | 36 | 当然,既然是阿呆买跑车,那么阿呆肯定不能自己造一辆出来,当然现在有太多中间商赚差价了,阿呆总不能自己先去4S店上班然后自己买车再辞职吧,好主意,阿呆穿上鞋就去4S店报道。 37 | 38 | 4S店老板一眼看出了阿呆的意图,阿呆被赶了出来。但没有什么能拦得住阿呆的,于是阿呆拖熟人是不是可以直接去工厂上班。 39 | 40 | 结果当然是可以的啊,不去工厂上班怎么扯掉今天要讲到工厂模式。我是作者,我说了算。 41 | 42 | 于是阿呆把自己想要车的型号告诉工厂,工厂呢根据阿呆所提供的型号交付了车辆,阿呆开着保时捷911驰骋在旧金山宽阔的道路上,笑着笑着口水就流到了床上。 43 | 44 | 看到这里大概就知道工厂模式所关注的职责了,简单工厂模式和我们的单例模式其实有着异曲同工之妙,即我们获取对象的时候只管要就行了,完全不care任何关于这个对象是怎么被造出来的,他们两个之间的区别在于简单工厂模式可以提供多个对象,根据客户的需求动态的生成满足客户条件的对象。同时,和我们之前系列文章不止一次的强调的那样,如果把对象的数量提升到一个很高的量级,即我们简单工厂模式需要维护成千上万的对象,那么我们程序肯定会爆炸的。 45 | 46 | 少废话,看代码,由于简单工厂模式实在是太过于简单,之所以花这么多的篇幅去介绍一个如此简单的设计,一是我闲得慌,二是很多东西虽然简单但是却在某些场景上要比复杂的设计模式提供更高的效率,所谓大智若愚,大繁至简。第三则是为了我们后面两个工厂模式做做一个尽可能详细的铺垫。 47 | 48 | 49 | 50 | ### 代码实战: 51 | 52 | 1. 定义一个汽车接口 53 | 54 | ```java 55 | public interface Car { 56 | 57 | public void description(); 58 | 59 | } 60 | 61 | ``` 62 | 63 | 2. 编写相关实现 64 | 65 | ```java 66 | public class BugattiCar implements Car { 67 | 68 | @Override 69 | public void description() { 70 | System.out.println("布加迪跑车"); 71 | 72 | } 73 | 74 | } 75 | 76 | 77 | public class PorscheCar implements Car { 78 | 79 | @Override 80 | public void description() { 81 | System.out.println("保时捷跑车"); 82 | 83 | } 84 | 85 | } 86 | 87 | ``` 88 | 89 | 90 | 91 | 编写相关工厂实现 92 | 93 | ```java 94 | public class CarFactory { 95 | 96 | public Car getCar(String name){ 97 | if("Porsche".endsWith(name)){ 98 | return new PorscheCar(); 99 | } 100 | else if("Bugatti".endsWith(name)){ 101 | return new BugattiCar(); 102 | } 103 | return new PorscheCar(); 104 | 105 | } 106 | 107 | } 108 | ``` 109 | 110 | ### 测试: 111 | 112 | ```java 113 | 114 | public class Test { 115 | 116 | public static void main(String[] args) { 117 | CarFactory factory = new CarFactory(); 118 | Car car = factory.getCar("Porsche"); 119 | car.description(); 120 | } 121 | 122 | } 123 | 124 | ``` 125 | 126 | ### 输出: 127 | 128 | ```java 129 | 保时捷汽车 130 | ``` 131 | 132 | 133 | 134 | ### 总结: 135 | 136 | 看过代码之后大概大家已经可以理解了,如果一旦工厂要管理的类过多,就会导致If else很多,这样写绝对是显得很沙雕的,为了解决这个问题,所以有了工厂方法模式和抽象工厂模式,同样的,因为简单工厂模式实在是太简单了,所以一般是不把它看作是设计模式的,更多的看作是一种代码思维。同样的,本篇文章已经同步首发至相关博客以及Github,我是韩数,我们下篇文章再见。 137 | 138 | 太困了,不行了,点个关注吧,高产似那啥。 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /Nginx初级入门教程/写给后端的Nginx初级入门教程基础篇.md: -------------------------------------------------------------------------------- 1 | # 写给后端的Nginx初级入门教程:基础篇 2 | 3 | ## Nginx是什么? 4 | 5 | Nginx ("engine x") 是一个高性能的 HTTP 和反向代理服务器,特点是占有内存少,并发能力强,事实上 nginx 的并发能力确实在同类型的网页服务器中表现较好,有报告表明能支持高 达 50,000 个并发连接数。国内京东,淘宝,阿里,新浪皆有使用Nginx。 6 | 7 | Nginx通常被用来实现**正向代理**,**反向代理,负载均衡,以及动静分离**这四个功能。 8 | 9 | 本篇文章作为基础篇,将主要讲解这四个Nginx应用中最基础的概念,帮助大家更好的理解,从而为下一章的代码配置做准备: 10 | 11 | 不废话,直接上干货。 12 | 13 | ## 正向代理: 14 | 15 | 在说什么是反向代理之前,我们需要先了解一下什么是正向代理,正如大家所知,由于某些不可抗拒的因素,我们没有办法在中国大陆直接访问Google等网站,或者说访问GitHub这些国外网站网速比较慢,不论是正向代理还是反向代理,都其实可以看作是代理模式的衍生版本。下面我们通过一个小栗子来理解正向代理在这里面起的作用: 16 | 17 | 阿呆这几天愁眉苦脸的,工作中某些业务需要用到谷歌邮箱,可是现在别说谷歌邮箱了,谷歌都打不开,这可急坏了阿呆,正当阿呆一筹莫展之际,突然想到二呆刚被公司派去俄罗斯北极圈推销冰箱了,自己上不了谷歌,但是二呆能上啊,而且二呆的电脑并没有被限制访问,于是阿呆就想出了一个天才的办法,阿呆在公司远程控制二呆的电脑访问Google邮箱,这么一来问题便得到了完美解决。 18 | 19 | 而这个例子中的请求路径是这样的,**我们先发送请求到我们的代理服务器,然后代理服务器再去请求Google的服务器,最后将请求到的内容返回给我们本机**。而这样一种模式呢,我们就称之为正向代理。如图所示(图画的丑,下次还丑): 20 | 21 |  22 | 23 | ## 反向代理: 24 | 25 | 在了解了什么是正向代理之后,反向代理就容易理解多了。正向代理中我们的代理服务器是作为一个消费者存在的,而反向代理中我们则对外提供服务,我们来看下面这个例子: 26 | 27 | 二呆从俄罗斯回来之后,业绩上是一无所获,好在公司不至于太过绝情,于是给了二呆一次将功补过的机会。工作内容也很简单,要求二呆去组织一个暖气事业部为将来非洲的暖气业务做准备,这当然没问题,刚开始的时候,二呆感觉还好,因为公司部门不多,所以可以很方便叫某个人的编号去安排工作,比如8080,你去处理这个请求,8081你去处理这个,8082你去问问非洲人民的意见怎么样,可是慢慢随着公司在此项目上投入的越来越多,部门员工也排到了10000多,这下二呆还要记住每个人的编号就不太现实了,**于是二呆设立了一台代理服务器,让代理服务器去记住这些人的编号和功能,而二呆只需要记住代理服务器的编号就行了,于是整个部门在二呆眼里慢慢变成了一个整体,而具体有多少人二呆则是完全不关心了,甚至二呆都不需要知道部门中都有哪些员工,**如图所示: 28 | 29 |  30 | 31 | 而反向代理和正向代理最大的区别就是**客户端完全感知不到代理的存在**,比如我们正向代理访问Google,可能需要在本地软件比如小飞机上设置相应的代理服务器的地址和端口,而反向代理则完全不需要设置,**比如淘宝,当客户端去访问淘宝的时候,可完全不知道淘宝开了多少台服务器,每个服务器的地址是多少,只需要像往常一样在浏览器中输入:taobao.com就行,至于双十一将至,阿里又额外开了多少台服务器,客户端则是完全感知不到的**,所有的请求则是通过代理服务器通过负载均衡的方式分发到不同的服务器中了。 32 | 33 | 一句话总结: 34 | 35 | **反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器 地址,隐藏了真实服务器 IP 地址。** 36 | 37 | 38 | 39 | ## 负载均衡: 40 | 41 | 负载均衡,其实不算是一个新的概念,负载均衡其实是在反向代理基础之上实现的,如果说反向代理的目的是为了隐藏真实服务器的IP地址的话,**负载均衡则是提供了一组策略来将请求从代理服务器上分发到这些真实的服务器上去**。 42 | 43 | 在Nginx中,一共提供了三种负载均衡策略供开发者灵活选择: 44 | 45 | - **轮询(默认方式):** 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器 down 掉,能自动剔除。 46 | - **权重(weight):** weight 代表权重,默认为 1,权重越高被分配的客户端越多,权重越大,能力越大,责任越大,处理的请求就越多。 47 | - **ip_hash**:每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决 session 的问题。 48 | 49 | 一句话总结: 50 | 51 | **增加服务器的数量,然后将请求分发到各个服务器上,将原先请求集中到单个服务器上的情况改为将请求分发到多个服务器上,将负载分发到不同的服务器,也就是我们所说的负载均衡** 52 | 53 | 54 | 55 | ## 动静分离: 56 | 57 | 当我们的应用初具规模,服务器后端需要面对大量请求的话,原来的动静混合打包发布的方式就不再那么适用了,因为我们服务器一边需要去处理客户端发过来的动态请求,比如数据库的查询,计算等,又同时要处理客户端发过来的静态请求,比如图片,css样式等静态文件,大量的请求毫无疑问会增加我们后端的压力,挤占我们用于处理动态请求的性能,为了解决这个问题呢,于是就有了动静分离这种部署的方式。 58 | 59 | 动静分离就是把很少会发生修改的诸如图像,视频,css样式等静态资源文件放置在单独的服务器上,而动态请求则由另外一台服务器上进行,这样一来,负责动态请求的服务器则可以专注在动态请求的处理上,从而提高了我们程序的运行效率,与此同时,我们也可以针对我们的静态资源服务器做专属的优化,增加我们静态请求的响应速度。 60 | 61 | Nginx 动静分离简单来说就是把动态跟静态请求分开,不能理解成只是单纯的把动态页面和静态页面物理分离。严格意义上说应该是动态请求跟静态请求分开,可以理解成使用 Nginx 处理静态页面,Tomcat 处理动态页面。动静分离从目前实现角度来讲大致分为两种, 62 | 63 | **一种是纯粹把静态文件独立成单独的域名,放在独立的服务器上,也是目前主流推崇的方案;** 64 | 65 | 另外一种方法就是动态跟静态文件混合在一起发布,通过 nginx 来分开,具体如何配置,后期Nginx实战会详细说明。 66 | 67 | 如下图所示: 68 | 69 |  70 | 71 | ## **总结:** 72 | 73 | 本篇文章作为Nginx的第一篇,主要侧重讲了以下Nginx的四个基本概念,也是我们日常开发中遇到的最为频繁的四个功能,下一节呢,我们则从实际的代码入手,通过编写相应的代码,一步一步的完成反向代理,负载均衡,动静分离的配置。而正向代理呢,如果有兴趣的小伙伴可以自行查阅相关资料(狗头保命)。 74 | 75 | 相关笔记已经同步开源至本人github,欢迎star: 76 | 77 | [韩数的开发笔记](https://github.com/hanshuaikang/HanShu-Note) 78 | 79 | 最后,欢迎点赞,关注我,有你好果子吃(滑稽) 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /废话设计模式系列/代理模式/设计模式java语言实现(八)之代理模式.md: -------------------------------------------------------------------------------- 1 | # 设计模式java语言实现(八)之代理模式 2 | 3 | ### 前言: 4 | 5 | 作者:韩数 6 | 7 | Github:https://github.com/hanshuaikang 8 | 9 | 本篇文章电子版和配套代码下载地址:https://github.com/hanshuaikang/design-pattern-java 10 | 11 | 时间:2019-07-31 12 | 13 | Jdk版本:1.8 14 | 15 | ### 代理模式定义: 16 | 17 | 为其他对象提供一种代理以控制对这个对象的访问。在面向对象中,有时候直接访问一些对象比较麻烦,所以代理模式就是在这个对象上加上一个访问该对象的访问层。类似于很多明星的事务实际都是交给经纪人处理的。 18 | 19 | ### UML图: 20 | 21 |  22 | 23 | ### 角色分工: 24 | 25 | - **抽象主题角色:定义了被代理角色和代理角色的共同接口或者抽象类。** 26 | - **被代理角色:实现或者继承抽象主题角色,定义实现具体业务逻辑的实现。** 27 | - **代理角色:实现或者继承抽象主题角色,持有被代理角色的引用,控制和限制被代理角色的实现,并且拥有自己的处理方法(预处理和善后)** 28 | 29 | ### 优/缺点: 30 | 31 | **优点:** 32 | 33 | 1. 职责清晰。 34 | 2. 高扩展性。 35 | 3. 智能化。 36 | 37 | **缺点:** 38 | 39 | 1. 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 40 | 2. 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。 41 | 42 | ### 应用场景: 43 | 44 | 1. 远程代理。 45 | 2. 虚拟代理。 46 | 3. Copy-on-Write 代理 47 | 48 | 在java中的应用:spring-aop实现,Mybatis 都有使用代理模式。 49 | 50 | ### 微剧场: 51 | 52 | 自从韩数荣登荣耀钻石宝座之后,前来拜师学艺的人络绎不绝,直接拉动了当地酒店行业的发展。 53 | 54 | 刚开始的时候还好,人毕竟来的不多,韩数把每个客人都安排妥当之后,仍然可以留一些时间去专注在自己的王者事业上,可是好久不长,奈何韩数王者技艺高超,方圆十里几乎无人不晓,渐渐地,找韩数的人越来越多,韩数不得不将大量的时间花在接待慕名前来的人们,导致自己在王者上投入的时间愈来愈少,韩数想长此以往下去,自己都没有时间打王者提升技艺了?核心竞争力一旦没有的话,那我岂不是要成为过气网红了?不能这样不能这样,我不要成为下一个乔碧萝。不!!!! 55 | 56 | 听说经纪公司给每一个明星都安排了一个经纪人,既然他们可以有的话,那么我为什么不能也雇一个呢?于是把阿呆叫过来做自己的经纪人。 57 | 58 | 每当有新的小迷妹过来拜访(咳咳,想什么呢?),阿呆负责把他们的生活起居安排好,包括酒店的预订,重要事情的通知等,当他们需要和韩数大神交流时直接通知韩数就行了。这样一来,韩数挤出来很多空闲的时间去提升自己的王者技巧,向无敌至上牛x轰轰荣耀王者的小目标又迈进了一步。 59 | 60 | ### 代码实战: 61 | 62 | 首先呢,我们声明一个明星接口,star,用来规范子类的行为。 63 | 64 | ```java 65 | public interface Star { 66 | //声明韩数与其他们交流王者上分技巧的方法 67 | public void exchange(); 68 | } 69 | 70 | ``` 71 | 72 | 然后声明我们的具体实现HStar ,并实现Star接口,作为代表韩数的类。 73 | 74 | ```java 75 | 76 | public class HStar implements Star { 77 | 78 | @Override 79 | public void exchange() { 80 | System.out.println("巴拉巴拉嘤嘤嘤交流中...."); 81 | 82 | } 83 | 84 | } 85 | 86 | ``` 87 | 88 | 同时我们创建一个代理类ProxyStar并实现Star接口,作为我们的经纪人角色。 89 | 90 | ```java 91 | public class ProxyStar implements Star{ 92 | 93 | private HStar star; 94 | 95 | @Override 96 | public void exchange() { 97 | if(star == null) { 98 | star = new HStar(); 99 | } 100 | 101 | System.out.println("经纪人安排访客住宿,吃饭等等等等,通知韩数接待客人"); 102 | star.exchange(); 103 | 104 | } 105 | 106 | } 107 | 108 | ``` 109 | 110 | ### 测试: 111 | 112 | ```java 113 | public class Test { 114 | 115 | public static void main(String[] args) { 116 | 117 | Star star = new ProxyStar(); 118 | star.exchange(); 119 | 120 | } 121 | 122 | } 123 | 124 | ``` 125 | 126 | ### 输出: 127 | 128 | ```java 129 | 经纪人安排访客住宿,吃饭等等等等,通知韩数接待客人 130 | 巴拉巴拉嘤嘤嘤交流中.... 131 | ``` 132 | 133 | 134 | 135 | 不对,等等,好像哪里不对,我想想,就是那个,那个,就是..... 136 | 137 | 你是不是想说怎么看着那么像装饰者设计模式呢?其实呢,本例用装饰者模式实现的话代码其实是几乎一样的。 138 | 139 | 那,那他为什么叫代理模式呢?这不是挂羊头卖狗肉吗?还是之前计划的设计模式,发现凑不齐,硬加上去的? 140 | 141 | 当然不是这样,装饰者设计模式和代理模式虽然很相似,但是侧重点却不一样,下面让我们来看看他们的异同吧。 142 | 143 | 144 | 145 | **相同点:** 146 | 147 | - 都需要实现同一个接口或者继承同一个抽象类,并且代理角色和装饰角色都持有被代理角色和构件角色的引用。 148 | - 两者都可以在被代理角色的业务方法前或者后添加额外的处理逻辑。 149 | 150 | **不同点:** 151 | 152 | - 装饰器模式为了增强功能,而代理模式是为了加以控制。 153 | 154 | 大家可以把代理模式和装饰者模式看作是双胞胎两兄弟,只是发展侧重的点不一样,一个是侧重于对类功能上的增强,另一个则是侧重于对类的控制,正如我们前面所看到的,代理模式对于对象的操作几乎都是由代理对象来操作的。 155 | 156 | 157 | 158 | 总结: 159 | 160 | 代理模式分为动态代理和静态代理两种,本文所使用的代理模式均为静态代理,动态代理将作为本文的番外篇另作讲解。另外,本文相关实例代码和电子版markdown笔记文件已经上传至Github,有需要的朋友可以点击文首的链接下载,欢迎各位star,我是韩数,我们下篇文章再见! 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/Springboot之自定义全局异常处理.md: -------------------------------------------------------------------------------- 1 | ## Springboot之自定义全局异常处理 2 | 3 | ## 前言: 4 | 5 | 在实际的应用开发中,很多时候往往因为一些不可控的因素导致程序出现一些错误,这个时候就要及时把异常信息反馈给客户端,便于客户端能够及时地进行处理,而针对代码导致的异常,我们一般有两种处理方式,一种是throws直接抛出,一种是使用try..catch捕获,一般的话,如果逻辑的异常,需要知道异常信息,我们往往选择将异常抛出,如果只是要保证程序在出错的情况下 依然可以继续运行,则使用try..catch来捕获。 6 | 7 | 但是try..catch会导致代码量的增加,让后期我们的代码变得臃肿且难以维护。当然,springboot作为一个如此优秀的框架,肯定不会坐视不管的,通过springboot自带的注解,我们可以方便的自定义我们的全局异常处理器,并且以json格式返回给我们的客户端。 8 | 9 | 10 | 11 | ## 代码实战: 12 | 13 | ### 捕获全局异常: 14 | 15 | 首先呢,我们新建我们负责全局异常捕捉处理的类:MyControllerAdvice,代码如下: 16 | 17 | ```java 18 | @ControllerAdvice 19 | public class MyControllerAdvice { 20 | 21 | 22 | @ResponseBody 23 | @ExceptionHandler(value = Exception.class) 24 | public Map exceptionHandler(Exception ex){ 25 | Map map = new HashMap(); 26 | map.put("code",100); 27 | map.put("msg",ex.getMessage()); 28 | //这里可以加上我们其他的异常处理代码,比如日志记录,,, 29 | return map; 30 | } 31 | 32 | } 33 | 34 | ``` 35 | 36 | 37 | 38 | ### 注解说明: 39 | 40 | @ControllerAdvice 通过AOP的方式配合@ExceptionHandler()注解捕获在Controller层面发生的异常。如果需要扫描自定路径下的Controller,添加basePackages属性 41 | 42 | ```java 43 | @ControllerAdvice(basePackages ="com.example.demo.controller") 44 | ``` 45 | 46 | @RestControllerAdvice : 和@ControllerAdvice作用相同,可以理解为 @ResponseBody+@ControllerAdvice 的组合使用。 47 | 48 | @ExceptionHandler():该注解作用主要在于声明一个或多个类型的异常,当符合条件的Controller抛出这些异常之后将会对这些异常进行捕获,然后按照其标注的方法的逻辑进行处理,从而改变返回的视图信息。 49 | 50 | ### 测试: 51 | 52 | ```java 53 | 54 | @RestController 55 | public class UserController { 56 | 57 | 58 | @GetMapping("/test") 59 | public String test(){ 60 | int num = 1/0; 61 | return "Hello World"; 62 | } 63 | 64 | 65 | } 66 | 67 | ``` 68 | 69 | ### 结果: 70 | 71 | ```json 72 | {"msg":"/ by zero","code":100} 73 | ``` 74 | 75 | 76 | 77 | ## 捕获自定义异常: 78 | 79 | 自定义我们的异常信息类MyException 并继承RuntimeException: 80 | 81 | ```java 82 | public class MyException extends RuntimeException { 83 | 84 | private String errorCode; 85 | private String errorMsg; 86 | 87 | public MyException(String errorCode, String errorMsg) { 88 | this.errorCode = errorCode; 89 | this.errorMsg = errorMsg; 90 | } 91 | 92 | 93 | public String getErrorCode() { 94 | return errorCode; 95 | } 96 | 97 | public void setErrorCode(String errorCode) { 98 | this.errorCode = errorCode; 99 | } 100 | 101 | public String getErrorMsg() { 102 | return errorMsg; 103 | } 104 | 105 | public void setErrorMsg(String errorMsg) { 106 | this.errorMsg = errorMsg; 107 | } 108 | 109 | } 110 | 111 | ``` 112 | 113 | 114 | 115 | 修改我们的MyControllerAdvice,将MyException添加进去: 116 | 117 | ```java 118 | @ResponseBody 119 | @ExceptionHandler(value = MyException.class) 120 | public Map myExceptionHandler(MyException mex){ 121 | Map map = new HashMap(); 122 | map.put("code",mex.getErrorCode()); 123 | map.put("msg",mex.getErrorMsg()); 124 | //其他业务代码... 125 | return map; 126 | } 127 | ``` 128 | 129 | ### 测试: 130 | 131 | ```java 132 | @GetMapping("/test1") 133 | public String test1(){ 134 | String name = null; 135 | if(name == null){ 136 | throw new MyException("101","用户名为空"); 137 | } 138 | return "Hello World"; 139 | } 140 | ``` 141 | 142 | ### 输出: 143 | 144 | ```json 145 | {"msg":"用户名为空","code":"101"} 146 | ``` 147 | 148 | 149 | 150 | 完成~,关注我,有你好果子吃,哼。 151 | 152 | -------------------------------------------------------------------------------- /Jvm终极奥义系列/jvm内存回收终极奥义:垃圾收集算法.md: -------------------------------------------------------------------------------- 1 | # jvm内存回收终极奥义:垃圾收集算法 2 | 3 | ## 前言: 4 | 5 | 上一篇文章我们用尽废话说了说一个对象是如何被判定为死亡的,并提到java内存回收局经历过多年的发展,发明出来一款**java验死仪**,从而一举解决了普通java对象,验死难,回收难的重大问题,受到了java虚拟机总局各位领导的一致表扬。咳咳,跑题了,当然,仅仅是有这一项神器是不足以java内存回收局荣获每年的最佳组织奖的,jvm为了更方便的进行内存回收,还发明出了多达7种java垃圾回收仪,它们被统称为: 6 | 7 | 垃圾对象回收仪!(哆啦A梦音效) 8 | 9 | 本着“**回收垃圾,掌握核心科技**”的信念,垃圾对象回收仪同样也是由四大算法为原理驱动的,它们分别是: 10 | 11 | **标记-清除算法、复制算法、标记整理算法,分代收集算法。** 12 | 13 | 14 | 15 | ## 基本概念: 16 | 17 | java中分代垃圾收集算法将java堆按存活时间分为两个区域,一个用来存放新生代对象,一个用来存放老生代对象。 18 | 19 | 新生代:新生代就容易理解多了,我还专门编了一个童谣(滑稽): 20 | 21 | 新生代,新生代 22 | 23 | 对象小,死的快 24 | 25 | 你有毒吧。 26 | 27 | 老生代:特指那些活得时间比较长的对象。 28 | 29 | ## 标记-清除算法(Mark-Sweep): 30 | 31 | 标记-清除算法,是所有垃圾收集算法中最基础的一个,在别的垃圾收集算法面前就是个弟弟。长江后浪推前浪吗,标记清除算法再沙滩上,和我们大多数发展规律一样,后面的算法很多都是基于标记-清除算法改进而来的。 32 | 33 | 标记-清除算法,就是一个先标记然后再清除的算法(你这不是脱了裤子放..吗),具体的回收流程是这样的: 34 | 35 | - 标记出所有需要回收的对象 36 | - 标记完之后统一回收这些被标记的对象 37 | 38 | 没了,要不说它简单呢。 39 | 40 | 但是在实现简单的同时也同时带来了很多额外的问题,其中主要集中在两点: 41 | 42 | - 首先首当其冲的就是效率问题,本身标记清除这两个过程都不太高,因为你要找到一个个对象,给他们盖上章,然后再一个个去清除,肯定没有一锅端了快。 43 | - 第二个就是空间利用问题了,因为在内存中那些需要被回收的对象不是一个挨着一个那么排着队等你回收的,而是分散在内存各处,清除的时候Java虚拟机要一个地儿一个地儿的跑,然后再去回收,因为对象是分散在各处的,所以我们回收完就容易出现,这一小块内存是空的,那一小块内存是空的,这么下去会导致内存中都是一小块一小块的内存,当一个比较大的对象需要分配内存时,系统找不到这么大块的连续内存了,找不到系统很着急啊,于是就不得不提前触发另外一次垃圾收集动作了。 44 | 45 | 图大概是长这个样子,来自掘金(刘望舒)大佬的图: 46 | 47 |  48 | 49 | 这样结合文字再看就是不是就容易理解多了。 50 | 51 | ## 复制算法(Copying): 52 | 53 | 标记-清除算法出来之后,处女座看不下去了。 54 | 55 | 处女座:你瞧瞧你回收的那都是什么玩意儿,回收完那内存乱的,哎呀妈呀我都没法看,就不能整理一下吗,一家人整整齐齐的多好。 56 | 57 | 于是复制算法应运而生了。(纯属瞎扯,切勿当真) 58 | 59 | 这个算法呢,将内存容量按照容量分为两个大小相等的两块,每次呢,我只用一块,把另一块先留着,当其中一块的内存用完的时候,就把还活着的对象复制到另外一块上去,然后将使用过的这块直接一次清完,**简单粗暴,运行高效**,因为是整个一大块内存一次回收,所以也不存在什么内存碎片的问题了,但是天下没有免费的午餐,这么做也是要付出一定的代价的,那就是原本我100M的内存,现在一次只让我用50M。 60 | 61 | **这就好像你有一整包辣条,一次却只让你吃半袋,想想就难受。** 62 | 63 | 复制算法呢,一般主要用来回收新生代的对象,为什么?因为**新生代,死的快**。 64 | 65 | 因为新生代的对象很快都会被回收掉,所以一般我们也不按照一比一那么分,在 hotspot虚拟机中,将内存分成三块,一块是比较大的Eden(伊甸园),和两块较小的Survivor(幸存者)空间,每次使用一块Eden和一块Survivor,当需要回收时,将这两块内存中存活的对象一次性复制到另一块survivor中,然后直接清理掉刚才用过的eden和survivor空间。在 hotspot虚拟机中,Eden和Survivor的比例默认为8:1,后期可以通过修改虚拟机参数来修改Eden和Survivor之间的比例,经过我细致入微头脑风暴般的计算(别人算的好吧),在 hotspot虚拟机中采用这种比例的复制算法在新生代中内存浪费率只有10%,的确要远远低于上面的百分之50. 66 | 67 | 等等,你不是说survivor空间较小吗,两块内存存活的对象都复制到一块survivor上,万一survivor装不下咋弄。难住你了吧,我看你怎么办。 68 | 69 | **我没有,我不能借吗**,jvm引入了分配担保这个概念,当survivor空间不够时,这些对象将通过分配担保机制直接进入老年代。至于分配担保具体是怎么玩的,大家可以查阅相关资料看下哈,我打字打的手疼(小声逼逼,你也没把握讲清楚好吧,嘘)。 70 | 71 | 那复制算法既然表现这么香,那为什么不能用来回收老年代区域呢? 72 | 73 | 你想想,老年代是什么,java虚拟机中的长寿村,平常一般都不死,你搞个复制算法过去一看 74 | 75 | 尼玛,都还活着呢 76 | 77 | 然后一个一个把一大堆活着的老对象复制过去,更别说极端条件下一个也没死的情况了。 78 | 79 | 对了,复制算法的图如下: 80 | 81 |  82 | 83 | ## 标记-整理算法(Mark-Compact): 84 | 85 | 难道针对老年代就没什么办法了,标题都写了,那肯定就是有的,那就是标记-整理算法,我又把它称为“**弟弟们都往边上靠靠”**算法。 86 | 87 | 标记-整理算法特别适合老年代,为什么呢,因为不叫标记 -复制啊,标记-整理算法其实和标记-清除算法原理差不多,只不过不是直接对内存整理,而是让**弟弟们都往边上靠靠**,即让存活的老年代对象都向一侧移动,等这些弟弟都挨着挪到一块儿之后,直接把其他地方的内存直接清理掉。 88 | 89 |  90 | 91 | 92 | 93 | ## 分代收集算法: 94 | 95 | 分代算法没什么新的东西,前面基本概念那一小节其实就是说的这个算法,分代收集算法根据对象的存活周期的不同把对象划分为新生代和老年代两种,分代收集算法的好处就是实现了对症下药,使得我们可以根据不同的对象采用不同的垃圾收集算法,比如新生代,死的快(又来),就用复制算法,老生代的话就用标记-整理算法,或者标记-清除算法来回收。 96 | 97 | 没了 98 | 99 | 这个真没图 100 | 101 | ## 总结: 102 | 103 | 本篇文章我们较为细致的说了我们垃圾收集算法四小龙,也举了比如辣条的实例帮助大家加深理解。四种垃圾回收算法各有优劣,并由此衍生出来7中常用的垃圾收集器,关于垃圾收集器的文章呢,我们后期再更新。 104 | 105 | 可以多点更新吗? 106 | 107 | 不托更,你关注我啊。 108 | 109 | 我关注你 110 | 111 | 好 112 | 113 | 我是韩数,我们下篇文章再见。 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /废话设计模式系列/模板模式/设计模式java语言实现之模板模式.md: -------------------------------------------------------------------------------- 1 | # 设计模式java语言实现之模板模式 2 | 3 | ### 前言: 4 | 5 | > 作者:韩数 6 | > 7 | > Github:https://github.com/hanshuaikang 8 | > 9 | > 本篇文章电子版和配套代码下载地址:https://github.com/hanshuaikang/design-pattern-java 10 | > 11 | > 时间:2019-07-29 12 | > 13 | > Jdk版本:1.8 14 | 15 | ### 模板方法定义: 16 | 17 | 在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。 18 | 19 | ### 优缺点: 20 | 21 | **优点:** 22 | 23 | 1. 封装不变部分,扩展可变部分。 24 | 2. 提取公共代码,便于维护。 25 | 3. 行为由父类控制,子类实现。 26 | 27 | **缺点:** 28 | 29 | 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。 30 | 31 | ### 应用场景: 32 | 33 | 1. 有多个子类共有的方法,且逻辑相同。 34 | 2. 重要的、复杂的方法,可以考虑作为模板方法。 35 | 3. Spring 中提供的数据库操作模板RedisTemplate,Hibernatetemplete 等都是模板设计模式的应用。 36 | 37 | ### **微剧场:** 38 | 39 | 阿呆一直觉得自己的出生就是为了弥补自己父亲年轻时未来得及完成的遗憾,二呆也是。 40 | 41 | 阿呆父亲从小的梦想就是当一名宇航员,可是生不逢时,那个时代喜之郎果冻还没有发明出来,慢慢地,这个梦想实现的可能性随着阿呆父亲年龄的增长而变得愈发微小,当阿呆父亲过完自己xx岁生日的那天,他意识到宇航员这三个字就注定和他肉眼可见的未来产生不了任何关系了。于是阿呆父亲选择在自己梦想破灭的第二年成为一名机械加工厂的普通工人,人生就是这样的,有时候事业上的失意往往伴随着婚姻上的幸福,在这一点阿呆父亲是幸运的,他遇到了自己生命中最重要的那个人,并且在随后的生活中有了两个可爱懂事的孩子,渐渐地,阿呆父亲当宇航员的梦想随着阿呆和二呆的出生逐渐在记忆中渐渐苏醒了,并且意志坚定一发不可收拾。之后阿呆父亲为阿呆和二呆制定了详细的人生规划,涵盖了小学初中高中大学等人生所有的重大节点。虽然在具体实施的细节上,阿呆父亲并不刻意要求,只要大体方向不变就好了,而这个规划阿呆父亲也是不容许任何一个人去篡改和干预的,即使阿呆和二呆也不能干预,每当阿呆和二呆表示不满的时候,阿呆父亲总是以一句“你们小孩子懂什么”而草草收场,终于在阿呆21岁二呆20岁那年,他们来到了未来国度职业分配局(尼玛还是个科幻题材?),当领到喜之郎果冻的那一刹那,阿呆父亲留下了喜悦的泪水。但是事以愿违,未来国度职业分配局工作人员很遗憾的表示目前未来星际并不需要航天员,于是未来国度便暂时把阿呆和二呆分配到了某家互联网公司去做程序员(就是现在这家),并承若日后一旦有名额就把阿呆和二呆分配到未来星际航天局去做宇航员。 42 | 43 | 看完这个故事是不是突然发现莫名有点像模板设计模式? 44 | 45 | 我,尼,,哪里像了?韩数,你自己看看,哪里像了???! 46 | 47 | 哎,别慌嘛,且听我一一道来。我们来看故事中阿呆父亲这个角色,是不是符合模板模式定义中的抽象类角色,定义了最基础的方法和执行框架,但是具体的实现是交给子类来完成的,但是最终子类也就是阿呆二呆他们的人生规划,却是使用的阿呆父亲的那一套,而不是阿呆自己的人生规划。 48 | 49 | **所以模板模式可以这么理解,阿呆他爸给阿呆计划了先上小学然后初中然后高中,但是具体怎么上的阿呆他爸不管,但是必须的按照我的这个计划来。** 50 | 51 | 所以上文模板设计模式的优点,**行为由父类控制,子类实现** 就体现在这里,同时呢,缺点也显而易见了,如果过分点,阿呆父亲有一百万个梦想,那他岂不是得生一百万个孩子出来,很明显的会导致类的个数增加,使得系统更加庞大。 52 | 53 | 理解了基础的概念呢,老规矩,我们下面看代码实现。 54 | 55 | 对了,本故事纯属虚构,还有喜之郎果冻看到的话别忘了打钱。 56 | 57 | 58 | 59 | ### 代码实战: 60 | 61 | 首先我们定义一个抽象类,Father,这里定义了一系列人生的关键节点以及执行的顺序。 62 | 63 | ```java 64 | public abstract class Father { 65 | 66 | 67 | abstract void primarySchool();//上小学 68 | abstract void juniorHighSchool();//上初中 69 | abstract void highSchool();//上高中 70 | abstract void university();//上大学 71 | abstract void astronaut();//成为宇航员 72 | 73 | //为阿呆规划的人生道路 74 | public final void step() { 75 | primarySchool(); 76 | juniorHighSchool(); 77 | highSchool(); 78 | university(); 79 | astronaut(); 80 | } 81 | 82 | 83 | } 84 | 85 | ``` 86 | 87 | 然后定义一个阿呆类,ADai,用来具体实现Father类所定义的方法。 88 | 89 | ```java 90 | public class ADai extends Father { 91 | 92 | @Override 93 | void primarySchool() { 94 | System.out.println("阿呆开始上小学了"); 95 | 96 | } 97 | 98 | @Override 99 | void juniorHighSchool() { 100 | System.out.println("时间荏苒,阿呆已经是一个初中生了"); 101 | 102 | } 103 | 104 | @Override 105 | void highSchool() { 106 | System.out.println("三年奋战,阿呆终于考上了自己理想的职高"); 107 | } 108 | 109 | @Override 110 | void university() { 111 | System.out.println("十九岁的阿呆站在蓝翔技校的门口,相信自己会有一个美好的未来"); 112 | } 113 | 114 | @Override 115 | void astronaut() { 116 | System.out.println("在厚厚的头盔中,阿呆俯视着脚下这颗蓝色的星球,瞬间感觉自己是多么的渺小"); 117 | 118 | } 119 | 120 | } 121 | 122 | ``` 123 | 124 | 测试: 125 | 126 | ```java 127 | public class Test { 128 | 129 | public static void main(String[] args) { 130 | ADai adai = new ADai(); 131 | adai.step(); 132 | } 133 | 134 | } 135 | 136 | ``` 137 | 138 | 输出: 139 | 140 | ```java 141 | 阿呆开始上小学了 142 | 时间荏苒,阿呆已经是一个初中生了 143 | 三年奋战,阿呆终于考上了自己理想的职高 144 | 十九岁的阿呆站在蓝翔技校的门口,相信自己会有一个美好的未来 145 | 在厚厚的头盔中,阿呆俯视着脚下这颗蓝色的星球,瞬间感觉自己是多么的渺小 146 | ``` 147 | 148 | 149 | 150 | ### 总结: 151 | 152 | 本篇笔记较为简单的阐述了模板设计模式的基本内容以及使用场景和代码形式,模板设计模式虽然简单,但是在实际开发中也有比较广泛的应用,配合微剧场,希望可以帮助正在学习设计模式的朋友可以更加深入的理解模板设计模式,只有真正的理解了设计模式,日常开发中我们才能够更好的使用。相关笔记电子版以配套代码以及上传开源至Github,欢迎star 。 153 | 154 | 我是韩数,我们下篇MVC设计模式再见, -------------------------------------------------------------------------------- /Docker初级入门教程/面向初学者的docker学习教程:基础篇.md: -------------------------------------------------------------------------------- 1 | # 面向初学者的docker学习教程:基础篇 2 | 3 | ## 前言: 4 | 5 | 之前很早就对Docker有所耳闻,但是碍于时间(就是懒得学)的关系,就一直没有开始行动,直到最近这个学期课比较少,实在不知道该干啥了,算了,学习吧。所以就开始了我漫长Docker学习之旅。当然,写这篇笔记的时候,我对Docker已经大概有了一个初步的了解,所以就有了这个面向初学者的Docker学习笔记系列,为什么是初学者呢,因为我自认为我对Docker的了解依然是一个比较初级的阶段,所以更高级的内容怕说不明白,以至于误导了别人,写这系列的笔记主要初衷有两个,一来是为了对自己这个阶段所学的知识做一个全面的梳理和总结,二来顺便将这些知识用我风骚的写法做成笔记,帮助后面学习Docker的小伙伴更通俗易懂的去理解Docker相关的知识和概念,本篇文章作为基础篇第一篇,将围绕这三个问题来展开: 6 | 7 | 1. 什么是Docker? 8 | 2. 为什么是Docker? 9 | 3. Docker 具体解决了什么样的问题? 10 | 11 | ## 什么是docker? 12 | 13 | Docker是基于Go语言实现的在2013年发布的云开源项目,它利用了围绕容器这个现有的计算概念,特别是在Linux世界中,这些原始概念被称为cgroups和命名空间。Docker的技术之所以独特是因为它专注于开发人员和系统操作员的需求,以将应用程序依赖项与基础架构分开。 14 | 15 | Docker的主要目标是“Build,Ship and Run Any App,Anywhere”,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP(可以是一个WEB应用或数据库应用等等)及其运行环境能够做到“一次封装,到处运行”。 16 | 17 | 一句话概括,Docker的出现解决了运行环境和配置环境不一致的情况,从而更方便的做持续集成并有助于整体发布。 18 | 19 | 20 | 21 | ## 为什么是Docker? 22 | 23 | 要了解这个问题,我们就需要了解在Docker之前的传统的虚拟机技术是怎样的,我相信大家都有使用过虚拟机软件在自己电脑上虚拟出另外的操作系统的经历,比如在win上通过vm安装一个linux系统,传统的虚拟机其实是一种带环境安装的解决方案,也就是说,我模拟的是一套完整的操作系统环境,这个系统依然是有它独立的内核,驱动等等。 24 | 25 | 如图所示: 26 | 27 |  28 | 29 | 对于虚拟机中运行的程序而言,由于虚拟机模拟了一整套系统的环境,那么在虚拟机中运行的应用程序是感知不到自己是在虚拟机中运行的,就像和在真实的操作系统中运行一样。 30 | 31 | 当然,看到这,很多人可能会觉得,这不是挺好的吗,的确,在需求不是很大,比如只需要额外开两三台虚拟机的时候,这种做法并没有什么明显的短板,但是,由于我们模拟的是一整套操作系统的环境,这就导致了什么问题呢,我们每开一个虚拟机都会额外占用很大一部分资源,尽管你可能两台虚拟机中的linux系统内核是一模一样的,这就造成了对资源的一个很大的浪费,同时呢,由于我们启动虚拟机的时候启动的是一整套操作系统,这就会导致启动变得非常的慢,可能需要几分钟,当然,几分钟并不是很长,可是如果有很多台虚拟机呢?可能当运维好不容易把所有虚拟机启动完成了,发现秒杀已经结束了,这对于很多大规模的应用来说是不能忍的,第三点,就是步骤非常繁琐,我们现在总结一下传统虚拟机最主要的三个缺点: 32 | 33 | - 资源占用比较多 34 | - 启动比较慢 35 | 36 | - 步骤繁琐 37 | 38 | 当然,时代再进步,linux也不能看着这些问题放任不管啊,于是linux发展出了另外一项虚拟化技术,即linux容器技术。 39 | 40 | linux容器技术是怎么一回事呢,这点和我们在实际开发中抽取公共逻辑的思路是类似的,之前不是开很多虚拟机内核什么的都一样造成资源浪费吗,那我这下把内核单独抽离出来,大家公用,**所以linux容器实际上运行的并不是一个完整的操作系统,而是通过进程对不同的容器进行了隔离,容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。** 41 | 42 | 如同所示: 43 | 44 |  45 | 46 | 由于启动的时候,启动的并不是整套操作系统环境,仅仅是启动应用所需的环境就行了,启动速度自然就比传统的虚拟机快了很多,甚至说可以做到秒启动,同时又解决了资源浪费的问题,而Docker正是基于linux容器技术而衍生出来的开源项目,使其对于广大开发者来说更容易上手,降低了使用的门槛。 47 | 48 | 49 | 50 | ## Docker 具体解决了什么样的问题? 51 | 52 | 这个就要从很久很久以前说起了。 53 | 54 | 故事发生在9012年全球最伟大的互联网公司阿里奶奶次世代产品**免费版某宝**的上线前夕,程序员阿呆把自己多日以来**呕心沥血呼呼大睡上班摸鱼下班蹦迪**写的程序代码交付给了运维二呆,本以为自己将代码给了二呆就可以**满面春光如释重负迈着六亲不认的步伐深夜买醉瘫倒在灯红酒绿纸醉金迷的酒吧**,但是正当阿呆准备开始这崭新的生活的时候,二呆拦住了他: 55 | 56 | 阿呆,你这代码有问题吧,我怎么跑不起来? 57 | 58 | 阿呆:WTF ?我没听错吧,你竟然说我的代码有问题,呵呵,某人运维技术不行,还好意思说我菜? 59 | 60 | 二呆:我运维技术不行,我运维技术不行,你行你上啊,开玩笑,你代码写的没问题我能跑不起来? 61 | 62 | 阿呆:你睁大你的24k钛合金x眼看看,我电脑上是不是跑的好好的,是你运维技术不行,ok? 63 | 64 | 二呆:我&%¥%*&*,你代码没问题是吧,你代码没问题是吧,你来啊,有本事你跑起来,你要能跑起来我直播女装,臭弟弟。 65 | 66 | 阿呆:我今天就让你看看,我是怎么把它完美的部署上去的,你给我好好看着,知道吗?! 67 | 68 | 此时,阿呆的女朋友打来电话:阿呆,不是说好了一起去醉生梦死的吗? 69 | 70 | 阿呆:滚,现在没空搭理你。 71 | 72 | 注:以上皆为节目效果,大家请勿当真。 73 | 74 | 传统的开发中总是会出现这类开发环境和生产环境不一致的问题,而Docker的出现毫无疑问极大地简化了运维工程师的工作量,也大大降低了开发和运维之间撕逼的概率。大家这么来理解: 75 | 76 | 我们以搬家为例,传统的流程就可以看作是把所有家具一件一件地搬到新的地方,万一哪个家具的摆放位置没记清楚,就有可能导致新家运行不起来(??什么鬼),而**Docker就很简单粗暴,Docker采取的方式就是把整套环境打包给你,也就是直接把整个楼都给你搬过去,问题完美解决**。 77 | 78 | 同时呢,Docker是内核级虚拟化,不像传统的虚拟化技术一样需要额外的Hypervisor支持,所以在一台物理机上可以运行很多个容器实例,可大大提升物理服务器的CPU和内存的利用率。 79 | 80 | 总结起来,docker大概就是以下四个优点: 81 | 82 | - 更快的应用交付和部署 83 | - 更便捷的升级和扩缩容 84 | - 更简单的系统运维 85 | - 更高效的计算资源利用 86 | 87 | 88 | 89 | 实在是太香了。 90 | 91 | ## 总结: 92 | 93 | 本篇笔记简单的讲了一下docker的一个基本情况,相信看到这里的小伙伴已经对docker有了一个基本的认识,至少知道docker是什么了,下一篇笔记呢,讲围Docker 三要素 镜像 容器 和仓库这三个概念展开,预知后事如何,点个赞再走吧。 94 | 95 | 我是韩数,我们下一篇笔记《Docker三要素:镜像,容器和仓库》再见。 96 | 97 | PS:更多笔记欢迎大家去我的github上下载(欢迎star): 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /Hadoop初级入门教程/写给后端的Hadoop初级入门教程(三):Hadoop运行环境搭建.md: -------------------------------------------------------------------------------- 1 | # 写给后端的Hadoop初级入门教程(三):Hadoop运行环境搭建 2 | 3 | ## 前言: 4 | 5 | 在在上一篇文章[写给后端的Hadoop初级入门教程:Hadoop组成部分](https://juejin.im/post/5df1e43451882512670ed134)中我们初略地叙述了`Hadoop`的三个重要组成部分 `Map - Reduce` , `Yarn` , `HDFS` 。难道到此一切都结束了吗, no no no,好戏才刚刚开始,既然要学习`Hadoop`,那么首先我们要解决的就是运行环境的问题,毕竟我个人认为学习编程最好的方式应该边看书边敲键盘,如果仅仅看书云学习的话,理论性的东西还好,但是代码性质的内容如果不敲一遍的话就会一个导致很明显的问题,就是自己觉得明明会了,写出来的程序却一直报错,整体学习效率是十分感人的。 6 | 7 | 不废话,直接上东西。 8 | 9 | ## 虚拟机: 10 | 11 | 首先,在学习`hadoop`之前。你需要一个虚拟机,**当然如果你恰好财力雄厚**,也可以自己买个服务器,或者后期买一组服务器做个集群,这样学习起来就方便多了。 12 | 13 | 因为考虑到我们之后要搭建集群,可能要同时启动多台linux主机,这个时候虚拟机就是一个非常合适的选择,特别是对于我们大家主要以学习为目的的人来说。然后这就要求你的电脑配置可能要稍微好一点,以我本人为例,I5+8G配置的台式机,同时运行三台虚拟机还是有点吃力的。 14 | 15 | 创建一个新的用户,我这里是 `hanshu`,并配置`hanshu`用户具有`root`权限。 16 | 17 | 在/opt目录下创建两个文件夹,分别是`module`和`software` 18 | 19 | ```shell 20 | sudo mkdir module 21 | sudo mkdir software 22 | ``` 23 | 24 | 修改`module`和`software`文件夹所有者为`hanshu` 25 | 26 | ```shell 27 | sudo chown hanshu:hanshu module/ software/ 28 | ``` 29 | 30 | 到此,我们虚拟机的基本准备就已经算是完成了。 31 | 32 | ## 设置java环境: 33 | 34 | 我们本次选择使用的`linux`发行版是`centos7`系统,`centos7`默认是带了`java`环境的,但由于`centos7`自带的`openjdk`并没有增加对`java `监控命令` jps`的支持。目前有两种方案可以解决这个问题,第一种是卸载原有的`openjdk`进行重装,第二个是通过yum安装`jdk`开发插件。 35 | 36 | 首先我们查看我们本机的`Openjdk`版本: 37 | 38 | ```shell 39 | rpm -qa | grep openjdk 40 | ``` 41 | 42 | 我这里是`java 1.8 `版本,然后执行yum命令安装我们对应版本的`jdk`开发插件: 43 | 44 | ```shell 45 | yum install -y java-1.8.0-openjdk-devel 46 | ``` 47 | 48 | 第三步则是在我们`/etc/profile`文件添加我们`java`的环境变量,具体的操作我就不列出来了,最后我会把我`/etc/profile`的内容贴出来供大家进行参考。 49 | 50 | ## 安装Hadoop: 51 | 52 | 首先第一步是下载我们的`Hadoop`,我这里选用的`Hadoop2.7.2`版本,我知道到这里很多小伙伴可能会问了: 53 | 54 | `Hadoop3.x`既然都已经出来了,那为啥不用`3.x`呢, 55 | 56 | 这里我想说的是,我们学会了一个版本做知识更新的成本是很低的,比如你掌握了`java 1.6` ,再去使用`java 1.8`的时候,其实是很快就可以过度完成的。而且以目前我了解到的情况来说,目前企业使用的最多的版本还是`Hadoop2.x`版本,毕竟企业追求的是开发的稳定性,但未来`Hadoop 3.x`版本一定会是一个趋势。 57 | 58 | `Hadoop`下载地址: 59 | 60 | https://archive.apache.org/dist/hadoop/common/hadoop-2.7.2/ 61 | 62 | 使用`Xshel`l或者其他的linux终端管理工具将我们下载好的`Hadoop`安装包上传至我们上文创建好的/opt/software目录下。 63 | 64 | 解压该压缩包至/opt/module目录: 65 | 66 | ```shell 67 | tar -zxvf hadoop-2.7.2.tar.gz -C /opt/module/ 68 | ``` 69 | 70 | ### 将Hadoop添加到环境变量: 71 | 72 | 在这里我就不一一展示具体的过程了,无非是把目录添加至/etc/profile文件里面,我直接贴出来我的/etc/profile相关的配置文件信息,如下: 73 | 74 | ```shell 75 | ##JAVA_HOME 76 | export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk/jre/ 77 | export JRE_HOME=$JAVA_HOME/jre 78 | export CLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH 79 | export PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH 80 | 81 | ##HADOOP_HOME 82 | export HADOOP_HOME=/opt/module/hadoop-2.7.2 83 | export PATH=$PATH:$HADOOP_HOME/bin 84 | export PATH=$PATH:$HADOOP_HOME/sbin 85 | ``` 86 | 87 | 执行命令使配置生效: 88 | 89 | ```shell 90 | source /etc/profile 91 | ``` 92 | 93 | 终端执行`hadoop version`命令,查看`hadoop`是否安装成功: 94 | 95 | ```shell 96 | [hanshu@hadoop100 ~]$ hadoop version 97 | Hadoop 2.7.2 98 | ``` 99 | 100 | 当出现`hadoop`版本信息时,则代表我们`hadoop`运行环境已经配置成功了。 101 | 102 | ### Hadoop目录结构: 103 | 104 | 前面光想着解压了,也忘了点进去看看里面都有些啥,和`java`一样,`Hadoop`也有着清晰的目录结构用来堆放对应的内容,接下来我们列几个重要目录简单地阐述一下它们的作用: 105 | 106 | - **bin目录**:存放对Hadoop相关服务(HDFS,YARN)进行操作的脚本. 107 | - **etc目录**:Hadoop的配置文件目录,存放Hadoop的配置文件等信息。 108 | - **lib目录**:存放Hadoop的本地库(对数据进行压缩解压缩功能)。 109 | - **sbin目录**:存放启动或停止Hadoop相关服务的脚本。 110 | - **share目录:**存放Hadoop的依赖jar包、文档、和官方案例,比如wordCount等。 111 | 112 | ## 下面开始技术总结: 113 | 114 | 今天这篇文章,我们简单地过了一遍`Hadoop`基本运行环境的配置。因为很多操作实在是太过于基础,比如查看文件目录,配置环境变量,使用vim编辑器等等这些操作都应该是一个java程序员的基本操作,所以就没有做非常详细的叙述,当然,如果有不明白的同学可以去谷歌或者百度查阅相关资料,整体配置成功还是不复杂的。下一节呢,我们将通过修改`Hadoop`的配置文件,实现`hadoop`伪分布式环境的搭建,等我周六考完试,后面更新频率大概会维持在两天一更这样的进度,比较马上要放寒假了,随我好多年的笔记本跑不起来集群了。 115 | 116 | 非常感谢能读到这里的朋友,你们的支持和关注是我坚持高质量分享下去的动力。 117 | 118 | 相关代码已经上传至本人github。一定要点个**star**啊啊啊啊啊啊啊 119 | 120 | **万水千山总是情,给个star行不行** 121 | 122 | [韩数的开发笔记](https://github.com/hanshuaikang/HanShu-Note) 123 | 124 | 欢迎点赞,关注我,**有你好果子吃**(滑稽) 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /Jvm终极奥义系列/一个Java对象的死亡证明.md: -------------------------------------------------------------------------------- 1 | # 一个Java对象的死亡证明 2 | 3 | ## 前言: 4 | 5 | **生的对立面不是死亡,而是遗忘。** 6 | 7 | **生的对立面不是死亡,而是遗忘。** 8 | 9 | **生的对立面不是死亡,而是遗忘。** 10 | 11 | 重要的事情说三遍!大家请一定要好好记住这句话,这句话是我们整篇文章的精髓所在。 12 | 13 | 我信你个鬼,你个世界上最帅最机智勇敢玉树临风风流倜傥的帅哥坏得很。 14 | 15 | 不信?走着瞧,扯不上去算我输。 16 | 17 | ## 概念准备: 18 | 19 | VM 洋文全称为Virtual Machine ,中文一般被译为虚拟机,虚拟机一般用来指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。 20 | 21 | 而 JVM则是代表java语言的虚拟机,洋文全称:Java Virtual Machine。 22 | 23 | 世界上第一款商用java虚拟机是Sun公司1996年在jdk1.0中发布的Sun Classic VM,很早之前Sun Classic VM就已经被废弃掉了,由HotSot VM虚拟机来代替。 24 | 25 | ## 扯蛋环节: 26 | 27 | 既然你是要写jvm内存回收系列的学习笔记,那关java对象死亡有什么关系? 28 | 29 | 诶,这你就不对了,怎么能没有关系呢,jvm回收的是什么?是内存,内存被谁占着呢?java对象(主要),那对象不死怎么回收他呢? 30 | 31 | 对象不死怎么就不能回收了? 32 | 33 | 杠是吧,扛是吧,扛是吧,我们把jvm虚拟机在垃圾回收过程中的角色理解为我们神话传说中的阎王爷,按照古老的传说,人只有死了之后灵魂才会被回收继续投胎,人活着就算再废物也不能直接让他投胎了,必须要等他死了才行,而且,在人没死之前直接回收这个人的灵魂可能会导致一些很严重的问题,比如你回收个美国的杀人犯的灵魂,那肯定对这个世界不会产生什么特别大的影响,但是万一你要不小心抽到特朗普了,那可能就会引起大乱子。 34 | 35 | jvm也是这么考虑的,万一你这个对象别的地方正用着呢,jvm直接给他回收了,就有可能直接导致整个应用的崩溃。 36 | 37 | ## 微剧场: 38 | 39 | 大家好,我是一个java对象,我叫阿呆。万万没想到的是,我现在竟然要证明自己死了。 40 | 41 | 事情是这样的,我是一个废材对象,当主人把我创造出来时,我满以为自己会光芒万丈大有作为成为java对象之王,可是这货把我new出来之后他娘的竟然把我给忘了,他和其他的对象嬉戏打闹,我却只能在角落里伤心假笑,我感觉整个运行环境的天空都变成了灰色,我不想活了,我太难了,于是我跑到**java虚拟机内存回收局**恳请可以将我回收掉,像我这种废材没人在乎的对象真的就是活着浪费内存了。 42 | 43 | 可是java虚拟机内存回收局残忍地拒绝了我,还让我哪凉快哪呆着去,当然我并不打算为此退缩,在我的威逼利诱(苦苦哀求)下,java虚拟机内存回收局终于告诉了我事情的真相。 44 | 45 | 很遗憾,阿呆先生,这个世界上还有人仍然记得你,所以我们不能把你回收掉。 46 | 47 | 还有人记得..记得我?(双眼闪烁着泪花) 48 | 49 | 听完阿呆屁颠屁颠迈着扭秧歌的魔鬼的步伐回去了。(尼玛,刚才还想自杀呢,节操呢?变这么快) 50 | 51 | 当然,java虚拟机内存回收局肯定不是随随便便就把阿呆认定死亡的,经历过几十年的发展,该机构研发了一款**java对象验死仪**,原理主要是基于**引用计数**和**可达性分析算法**这两个算法,那么下面我们就依次来介绍这两种算法。 52 | 53 | ## 引用计数算法: 54 | 55 | 官方概念:引用计数算法是通过判断对象的**引用数量**来决定对象是否可以被回收。 56 | 57 | Hello h = new Hello(); 其中h就称之为Hello对象的引用,即我们可以通过h来访问到我们的Hello对象。 58 | 59 | 在这里我们可以理解为,假如B对象中包含A对象的引用,就意味着B对象和A对象有所联系,即B对象记得A对象。当B对象因为一些原因比如意外死亡,老年痴呆,韩国偶像剧等,不记得A对象的存在了,则我们称之为B遗忘了A,我说嘛,我肯定可以扯到这里的。 60 | 61 | 每当有一个新的对象和A对象产生联系时,即引用A对象,那么A对象的引用计数器就+1,如果谁不小心把A对象给忘了, 则意味着引用失效了,那么A对象的引用计数器就-1,当A对象的引用计数器变成0的时候,意味着虚拟机中已经没有任何对象还保留着A对象的引用了,即除了A对象自己,没有对象记得A对象的存在了,这个时候A对象就会被判定为死亡,被内存回收掉。 62 | 63 | 这个时候可能有善良的小伙伴问了,A对象那么可爱,为什么要回收掉它呢,留着它不好吗。 64 | 65 | 现在我们回归引用的概念,A对象引用计数器为0意味着再也没有任何一个对象可以访问到A对象了,侧面就说明了A对象再也不能被使用,变成了一个彻彻底底的废对象。 66 | 67 | 所以:**生的对立面不是死亡,而是遗忘。** 68 | 69 | 这么一想,引用计数算法的确还不错,这样死亡的对象都会被正确识别出来,从而回收掉。 70 | 71 | 年轻人,你还是想的太简单了。你忘了这个世界上有奇葩的存在了吗?且看下面这段对话: 72 | 73 | A:我诅咒你今年秃头! 74 | 75 | B:诅咒反贪 76 | 77 | A:我也反弹 78 | 79 | B:我再反弹 80 | 81 | 此处省略N个字。 82 | 83 | 没完没了,放在对象也是这样,加入A对象包含了B对象的引用,B对象又包含了A对象的引用,因为相互引用,他们的引用计数器都无法为0,也就不会被回收掉了。 84 | 85 | **所以引用计数在目前主流的java虚拟机中被废弃掉了。** 86 | 87 | 88 | 89 | ## 可达性分析算法: 90 | 91 | 官方概念:可达性分析算法是通过判断对象的引用链是否可达来决定对象是否可以被回收。 92 | 93 | 从GC Roots作为起点,向下搜索它们引用的对象,可以生成一棵引用树,树的节点视为可达对象,反之视为不可达。图长这样: 94 | 95 |  96 | 97 | 98 | 99 | 这个算法就好玩多了,我们可以把这个算法看作是一个巨大的传销集团,而GC Roots就是这个集团的传销头子 ,那它是怎么判断对象是否死亡的呢? 100 | 101 | 当需要内存回收的时候,虚拟机会沿着传销头子往下依次搜索它的下线,如果这个对象能被搜索到,那么就意味着这个对象是在传销组织里面的,比如Object2 3 4 都在传销集团里面,而Object 5 6 7 呢。 102 | 103 | Object 5 6 7就是那些没有组织的野传销,传销集团怎么会容忍这些野传销存在呢,于是他们都被判断为死亡。 104 | 105 | 所以:**生的对立面不是死亡,而是遗忘。** 106 | 107 | 而 Object 5 6 7 这三个对象都被遗忘掉了,因为他们没有组织,因为失去了和组织的联系,所以组织也没有办法差遣他们,发挥他们的价值,于是它们变成废对象被回收掉了。 108 | 109 | 也就是说呢,在可达性分析算法中,如果一个对象无法被虚拟机沿着GC Roots依次向下搜索到,那么这个对象就是不可达的,因为没有任何一条路通往这个对象,这个对象就像是海上的孤岛一样,最后只能被java虚拟机回收。 110 | 111 | 的确优势明显,随便你们反弹,没有组织你们都要完蛋。 112 | 113 | 而GC Roots也不是谁都能当的,在长期的实践中,发现有四类对象拥有成为传销头子,呸,Gc Roots的潜质,他们分别是: 114 | 115 | - 虚拟机栈(栈帧中的本地变量表)中的引用对象。 116 | - 方法区中的类静态属性引用的对象。 117 | - 方法区中的常量引用的对象。 118 | - 本地方法栈中JNI(Native方法)的引用对象 119 | 120 | **正因为可达性分析在实际使用中的优异表现,所以很多现代虚拟机都采用了这一实现。** 121 | 122 | **珍爱生命,远离传销,珍爱生命,远离传销,珍爱生命,远离传销!** 123 | 124 | 下面到技术总结环节: 125 | 126 | ## 总结: 127 | 128 | 本篇文章我们可谓是用尽了废话来帮助刚开始学习java或者jvm的朋友们彻底理解jvm判断java对象死亡的两种算法,再学习jvm的过程中,发现很多jvm书籍,博客都写的比较学术,让新手们望而却步,导致很多人看了一遍以为自己懂了,但是过了几天之后再想却没有办法想起来,根本原因还是在于并没有彻底的理解这些知识,为了帮助一些朋友更好的理解jvm虚拟机,所以才有了jvm学习笔记系列,同样的,本系列笔记会开源至github,并保持更新。 129 | 130 | 我是韩数,欢迎大家关注我。我们下篇文章再见。 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /废话设计模式系列/装饰者模式/从手办到装饰者设计模式.md: -------------------------------------------------------------------------------- 1 | # 从手办到装饰者设计模式 2 | 3 | ### 前言: 4 | 5 | > 作者:韩数 6 | > 7 | > Github:https://github.com/hanshuaikang 8 | > 9 | > 本篇文章电子版和配套代码下载地址:https://github.com/hanshuaikang/design-pattern-java 10 | > 11 | > 时间:2019-07-27 12 | > 13 | > Jdk版本:1.8 14 | 15 | ### 装饰者模式定义: 16 | 17 | 装饰模式可以动态的给一个对象增加一些额外的功能(增强功能) 相比于继承,装饰模式能对不支持继承的类进行增强;并且比继承更灵活,不需要生成大量的子类。 18 | 19 | ### 优缺点: 20 | 21 | **优点:**装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。 22 | 23 | **缺点:**多层装饰比较复杂。 24 | 25 | ### 应用场景: 26 | 27 | 1. 扩展一个类的功能 28 | 2. 动态增加功能,动态撤销 29 | 3. Java IO 源码 30 | 31 | ### 微剧场: 32 | 33 | 上次做智能遥控器,投入市场之后销售惨淡,阿呆被老板狠狠的批评了一顿,这几天心情十分郁闷。但是郁闷归郁闷,生活还是要继续下去的,于是阿呆决定买一个手办安慰一下自己。 34 | 35 | 但刚买回来两天,阿呆静静看着自己手中的王者荣耀大乔的手办,顿时感觉审美有些疲劳,怎么办呢,看来只能动用我的钞能力了,于是阿呆就陆续买了大乔其他风格的手办,短短几天,手办便占据了阿呆的整个屋子,简直寸步难行了。这个时候,阿呆想,能不能用我平常所学的编程知识来解决我现在的苦恼呢。 36 | 37 | Hello,World !阿呆脑海中陆续显出来这几个字。好吧,显然不能。看来靠阿呆自己解决这个问题几乎没有什么希望了,正当阿呆一筹莫展之际,阿呆想到了第二篇笔记观察者模式出来过一次后来就被作者忘掉的角色二呆,于是急忙给二呆发短信求助。 38 | 39 | 阿呆:二呆,我最近喜欢收集大乔的手办,可是我现在屋子里已经没有地方放置新的手办了,但是官方又推出了很多款其他风格的手办,想买又没有地方放了,好苦恼。 40 | 41 | 二呆:有钱人的生活就是这么的简单且枯燥,容我稍稍思考片刻 42 | 43 | 一个小时后.... 44 | 45 | 两个小时后.... 46 | 47 | 阿呆你这样理解,我们把你第一个买的大乔手办想象成java中的一个类,而你其他的大乔手办看作成它的子类,也就是你现在既想要很多风格的大乔手办,又不想买很多大乔手办对不对? 48 | 49 | 阿呆:知我者莫过于二呆了,放心,明天我就去求这个世界上最帅气最善良最智慧的韩数大大把你复活,那么二呆,我下面应该怎么办呢? 50 | 51 | 二呆:装饰者设计模式就可以完美解决这个问题,你想,你去买一个没有穿衣服的大乔手办(咳咳,笔者也不知道怎么说了,裸办?),就是那种还没有后期穿服装的手办模型,然后你再去网上买很多大乔的衣服,不就可以实现一个手办,无数大乔的目的了吗? 52 | 53 | 阿呆:机智,那个,先不聊了,韩数说他不想往下写了,回见,拜拜。 54 | 55 | 二呆:嘟嘟嘟 56 | 57 | 58 | 59 | 想到这里大家差不多已经明白了,传统的方法对一个类的功能的增强,往往是增加一个这个类的子类,然后重写我们需要增强的方法,当然,一个两个子类没有什么问题,如果随着业务的复杂,成百上千个子类势必会增加我们开发人员维护的负担。更何况,有很多类是禁止继承的,为了解决这个问题呢,装饰者模式就应运而生了。我们需要增强的类就是那个手办,装饰者模式呢就是在不改变手办结构的情况下,为她穿上衣服,增强它的颜值。 60 | 61 | 62 | 63 | 还不明白?上代码 64 | 65 | ### 代码实战: 66 | 67 | 首先,定义手办相关的接口: 68 | 69 | ```java 70 | //大乔手办接口 71 | public interface GarageKit { 72 | 73 | //定义一个方法,还没有穿上衣服的手办 74 | public void bodyWithClothes(); 75 | 76 | } 77 | 78 | ``` 79 | 80 | 然后我们声明一个类作为大乔手办实现这个接口,完成具体的功能。并把这个类设置为不可继承。 81 | 82 | ```java 83 | //大乔手办 84 | final class DaQiaoGarageKit implements GarageKit { 85 | 86 | @Override 87 | public void bodyWithClothes() { 88 | System.out.println("嘤嘤嘤,我现在还没有穿好衣服,不漂亮,不能出门(羞耻)"); 89 | 90 | } 91 | 92 | } 93 | ``` 94 | 95 | 96 | 97 | 同样的,我们定义装饰者相关的接口: 98 | 99 | ```java 100 | 101 | //装饰者接口,定义装饰者的行为 102 | public interface Decorator extends GarageKit { 103 | 104 | //给大乔穿衣服 105 | public void getClothes(); 106 | 107 | } 108 | 109 | ``` 110 | 111 | 然后我们声明一个类作为实际的装饰者。 112 | 113 | ```java 114 | //大乔装饰者实现 115 | public class DaQiaoDecorator implements Decorator{ 116 | 117 | 118 | //定义内部成员变量 DaQiaoGarageKit 119 | private DaQiaoGarageKit daQiaoGarageKit = null; 120 | 121 | public DaQiaoDecorator(GarageKit garageKit) { 122 | this.daQiaoGarageKit =(DaQiaoGarageKit) garageKit; 123 | 124 | } 125 | 126 | 127 | //为大乔穿上衣服 128 | @Override 129 | public void getClothes() { 130 | // TODO Auto-generated method stub 131 | System.out.println("好了,我现在穿好衣服啦,可以出门了"); 132 | } 133 | 134 | 135 | //在这里增强DaQiaoGarageKit类的功能,使其拥有穿衣服的能力。 136 | @Override 137 | public void bodyWithClothes() { 138 | // TODO Auto-generated method stub 139 | daQiaoGarageKit.bodyWithClothes(); 140 | getClothes(); 141 | } 142 | 143 | 144 | } 145 | 146 | ``` 147 | 148 | ### 测试: 149 | 150 | ```java 151 | public class Test { 152 | 153 | public static void main(String[] args) { 154 | 155 | // TODO Auto-generated method stub 156 | GarageKit garageKit = new DaQiaoGarageKit(); 157 | Decorator decorator = new DaQiaoDecorator(garageKit); 158 | decorator.bodyWithClothes(); 159 | 160 | } 161 | 162 | } 163 | 164 | ``` 165 | 166 | OUTPUT: 167 | 168 | ```text 169 | 嘤嘤嘤,我现在还没有穿好衣服,不漂亮,不能出门(羞耻) 170 | 好了,我现在穿好衣服啦,可以出门了 171 | ``` 172 | 173 | ### 总结: 174 | 175 | 本篇文章较为简单的阐述了装饰者模式的优缺点,虽然这次微剧场部分有点尬,装饰者设计模式也是实际开发中使用的较为频繁的设计模式,在java IO中也有广泛的应用,看下面一行代码: 176 | 177 | ```java 178 | BufferedInputStream in = new BufferedInputStream(new FileInputStream("d:\\1.txt")); 179 | ``` 180 | 181 | 是不是有些许的熟悉呢,感兴趣的小伙伴可以自行查找资料了解,本系列笔记相关源码已经开源至github,需要的可以前去下载。我是韩数,练习代码时长两年半的大学生一枚,我们下篇文章再见! 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /Hadoop初级入门教程/写给后端的Hadoop初级入门教程:Hadoop组成.md: -------------------------------------------------------------------------------- 1 | # 写给后端的Hadoop初级入门教程:Hadoop组成部分。 2 | 3 | ## 前言: 4 | 5 | 在上一篇文章[写给后端的Hadoop初级入门教程:概念篇](https://juejin.im/post/5dec7ec5e51d45581e43ff17)中,我们主要讲了大数据的简单概念,什么是大数据,大数据的特点是什么?之后我们又从大数据扩展到`Hadoop`,讲了三个最主要的问题,`Hadoop`是什么,`Hadoop`发展史,`Hadoop`相较于其他大数据框架而言优势又是什么? 6 | 7 | 今天呢,我们依然沿着上一篇的脉络,去探索`Hadoop`的基本组成部分,是哪些技术有机地组合在了一起造就了`Hadoop`今天在大数据领域的出色表现,在`Hadoop2.0`之后,`Hadoop`主要由以下三个部分组成: 8 | 9 | - `Map - Reduce` :负责计算 10 | - `Yarn` :负责资源调度 11 | - `HDFS`: 负责数据的存储 12 | 13 | 它们三个相辅相成,互相成就,当然本篇文章今天只是初略地带大家理解一下这三种技术在`Hadoop`中所起到的作用,具体更加详细的细节,在我们之后关于`Map-Reduce`和`HDFS`专题中会做更加详细的概述。 14 | 15 | ## Map-Ruduce编程模型: 16 | 17 | 首先平常看到这种英语概念,第一时间就是打开我们的谷歌翻译,Map的意思我想大家都知道,毕竟`java`中用的不能再多,`Reduce`是降低减少归纳的意思,所以Map-Reduce就是一个先分隔(map)再归纳(Reduce)的过程。 18 | 19 | 我们来看下定义: 20 | 21 | `MapReduce`是一个分布式运算程序的编程框架,是用户开发“基于`Hadoop`的数据分析应用”的核心框架。核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,**并发运行**在一个`Hadoop`集群上。 22 | 23 | **`MapReduce`主要可以概括为`map`阶段和`reduce`阶段。** 24 | 25 | 只看定义确实是有点晦涩,那Map-Reduce通俗理解是什么呢?还是我们上一篇文章讲的那个例子: 26 | 27 | 初中的时候,男生爱看玄幻小说,因为怕被教导主任查到,于是采用分布式存储的方案,把书分成几页几页的,放在不同的同学那边放着,但教导主任不是傻子,所谓道高一尺魔高一丈,最后还是被发现了,而是还放言今天要是不把这本书凑齐交到他办公室,全部都等着叫家长吧。 28 | 29 | 最后大家都把手里的残本交给了班长小明,小明根据页码排序整理好,交给了教导主任。 30 | 31 | 教导主任说你这不是闲的吗,天天不好好学习搁那看的这什么,**头破苍穹**,是英语书不好背了,还是数学书不好看了?这么着,你不是闲得慌吗,就这个**萧炎**,就他,你下去给我查查,整本书这个名字一共出现了多少次!不查完今天别想吃饭了! 32 | 33 | 小明想,这不是玩完了,我自己查,我得查到猴年马月才能查完。 34 | 35 | > 重点来了,传统的编程模型要是需要知道一本书中某个单词出现的频率,只能写个程序,遍历整个文件,如果几个字还好说,但是把斗破苍穹遍历一遍,需要的时间绝对够你吃顿饭的。 36 | > 37 | > 那不是还有多线程吗? 38 | > 39 | > 是有多线程,但是前提是我们得有一台多核或者多处理器的计算机,而且多线程的程序写起来也有点小复杂。 40 | 41 | 但小明不傻啊,小明心想,mmp,又不是我一个人看的,为啥要我自己数,于是小明心生一计,回到班里,大家有福同享有难同当,老师现在让我数**萧炎**在书中一共出现了多少次,我自己数到明天也数不完,谁看的谁过来**大家一人数几页**,然后你们在下面数好了**汇总**一下交给我。 42 | 43 | 于是全班男生一人数了几十页,不到一个小时就数完了,小明成功渡过一劫。 44 | 45 | 这就是 Map - Reduce,我一个人算不过来了,我找十个人并行计算,最后把结果进行汇总,不用说也知道是什么思想了,数据结构中用的最多的**分而治之**。 46 | 47 | 当然,Map-Reduce肯定不止我们上面说的那么简单,具体实现细节还是略微有点繁琐的,详细的执行流程,原理到时候我们在Map-Reduce专题再细细分析。 48 | 49 | ## Yarn: 50 | 51 | `Yarn`这个东西在`Hadoop2.x`时代才诞生,在遥远的`Hadoop1.x`时代,`Map-Reduce`不仅要负责**计算**,还要负责**资源调度**,简直是又当爹又当妈,一两天还好,时间长了`Map-Reduce`就受不了了,就向`Hadoop`总部提意见,总部肯定装作没听到啊,一个人干俩人的活儿不能再划算了。于是就不搭理`Map-Reduce`,后来有一天,`Map-Reduce`终于忍无可忍了,就甩袖子不干了,因为之前Map-Reduce又干计算又干资源调度,所以Map-Reduce甩袖子不干了,整个`Hadoop`计算和资源调度系统全都歇菜了。 52 | 53 | 耦合太严重,于是`Hadoop`觉得这不行,被Map-Reduce卡脖子可还得了?于是`Hadoop`又招了一个专门负责资源调度,就是`Yarn`,这样一来,Map-Reduce只负责计算,Yarn只负责资源调度,`Hadoop`内部瞬间和谐多了。再也没有出现过一人罢工,全员歇菜的问题了。 54 | 55 | Yarn主要干四个事儿,分别是: 56 | 57 | **ResourceManager(RM):** 58 | 59 | - 处理客户端请求。 60 | - 监控`NodeManager`。 61 | - 启动或监控`ApplicationMaster`。 62 | - 资源的分配与调度。 63 | 64 | **NodeManager(NM):** 65 | 66 | - 管理单个节点上的资源。 67 | - 处理来自`ResourceManager`的命令 68 | - 处理来自`ApplicationMaster`的命令 69 | 70 | **ApplicationMaster(AM):** 71 | 72 | - 负责数据的切分。 73 | - 为应用程序申请资源并分配给内部的任务。 74 | - 任务的监控与容错。 75 | 76 | **Container :** 77 | 78 | `Container`是YARN中的资源抽象,它封装了某个节点上的多维度资源,如内存、CPU、磁盘、网络等。 79 | 80 | 等。 81 | 82 | ## HDFS: 83 | 84 | `HDFS `:` Hadoop`分布式文件系统(`Hadoop Distributed File System`),听名字就知道是`Hadoop`中负责文件存储部分的技术了。 85 | 86 | `HDFS`相对于前面的`Map-Reduce`和`Yarn`就比较容易理解了,`HDFS`架构主要分为三个部分: 87 | 88 | **NameNode(nn)**: 89 | 90 | 存储文件的元数据,如文件名,文件目录结构,文件属性(生成时间、副本数、文件权限),以及每个文件的块列表和块所在的`DataNode`等。 91 | 92 | `NameNode`主要存储文件的元数据,比如我们去图书馆借书,`NameNode`存的就是这个图书馆所有书籍的目录,作者,文件属性,以及书的位置等信息。 93 | 94 | **DataNode(dn)** : 95 | 96 | `DataNode(dn)`:在本地文件系统存储文件块数据,以及块数据的校验和。 97 | 98 | 还是上面那个图书馆的例子,如果`NameNode`主要存的是目录的话,那么`DataNode`就是存书的书架,也就是我们实际的数据实际是在`DataNode`上存放的。 99 | 100 | **Secondary NameNode(2nn):** 101 | 102 | `Secondary NameNode(2nn)`:用来监控`HDFS`状态的辅助后台程序,每隔一段时间获取`HDFS`元数据的快照 103 | 104 | 看名字就知道了,和我们`Nginx`中讲的万一`Nginx`挂了是一个性质,你只有一个`NameNode`,万一不小心`NameNode`挂了,所有文件的元数据都没法儿访问,找不到文件的实际位置,那不就gg了吗,所以`Secondary NameNode(2nn):`主要就起一个辅助备份的作用. 105 | 106 | 万一`NameNode`挂了,别怕,有`Secondary NameNode(2nn)`在,他那有备份,恢复都是小KS。 107 | 108 | 109 | 110 | ## 下面开始技术总结: 111 | 112 | 今天这篇文章,我们初略地讲了`Hadoop`的三个重要的组成部分,`Map-Ruduce` `Yarn` 和 `HDFS`文件系统,分别负责`Hadoop`的分布式计算,资源调度,分布式存储实现,每一个都不可或缺,正是这三项技术的在`Hadoop`内部的完美配合,造就了今天`Hadoop`在大数据领域的地位,看到这里,我想尽管我们可能还不知道Yarn内部是怎么协调资源的,MR是如何进行并行计算的,但是我相信,大家对于`Hadoop`一定有了一个初略的认识,下一篇文章,我们讲一步一步通过配置虚拟机,然后实现我们`Hadoop`的伪分布式环境的配置。 113 | 114 | 非常感谢能读到这里的朋友,你们的支持和关注是我坚持高质量分享下去的动力。 115 | 116 | 相关代码已经上传至本人github。一定要点个**star**啊啊啊啊啊啊啊 117 | 118 | **万水千山总是情,给个star行不行** 119 | 120 | [韩数的开发笔记](https://github.com/hanshuaikang/HanShu-Note) 121 | 122 | 欢迎点赞,关注我,**有你好果子吃**(滑稽) 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /用java实现一个简单的区块链/用java实现一个简单的区块链:UTXO交易模型.md: -------------------------------------------------------------------------------- 1 | # 用java实现一个简单的区块链:UTXO交易模型 2 | 3 | 在上一篇[用java实现一个简单的区块链](https://juejin.im/post/5dddfb826fb9a071735e1ed0)中,我们主要讲了什么是区块链(提示:`区块链就是谈恋爱`),以及区块链是如何做到去中心化,不可篡改性的。然后就是通过解析国外一篇关于java区块链的文章,用java实现了一个简单的区块链,当然,我们最后做出来的那个东西实在是有点太过于简陋,原因是缺少了一个极为重要的流程-`交易`,再去描述如何用代码去实现具体的交易逻辑之前,我们首先需要先了解一个区块链是如何进行交易的,具体的交易逻辑又是怎么样的等等这些问题,本篇文章,我们主要是从UTXO交易模型出发,浅入去了解一下UTXO交易系统究竟是如何运作的,和传统的账户余额模型相比又有些什么样的优势? 4 | 5 | 少废话,直接上东西 6 | 7 | ## 什么是UTXO交易模型? 8 | 9 | `UTXO`(Unspent Transaction Outputs)是未花费的交易输出,提出者是中本聪。它是比特币交易生成及验证的一个`核心概念`。交易构成了一组`链式`结构,所有合法的比特币交易都可以追溯到前向一个或多个交易的输出,这些链条的源头都是挖矿奖励,末尾则是当前未花费的交易输出。 10 | 11 | 每笔交易都有若干`交易输入`,也就是`资金来源`,也都有若干笔`交易输出`,也就是`资金去向`。一般来说,每一笔交易都要花费(spend)一笔输入,产生一笔输出,而其所产生的输出,就是“未花费过的交易输出”,也就是 UTXO。 12 | 13 | ??? 没看懂,抬走下一个。 14 | 15 | 的确,这个概念是有点小复杂,我搁这想半天也没想出来啥奇妙的比喻(`jojo梗`),如果上一篇文章我们把区块链比作是谈恋爱的话,那么**UTXO就是结婚送彩礼**,前面是讲我们是怎么挖到矿的,UTXO就是讲我们挖到的这笔钱是怎么花的。 16 | 17 | 在上一篇文章的故事中,我们说到了由于区块链的不可篡改性,`小绿`肯定会发现`小明`是个渣男欺骗了她,考虑到我们这章讲的是UTXO交易模型,`小绿`就不得不选择原谅`小明`,并且还要答应小明的求婚。为了便于大家下面的理解,我打算先搬出来我们的次世代革命性产品-**别逼币**。 18 | 19 | `小明`:哎,小绿他妈竟然给我要五个别逼币的彩礼(不是骂人),我从哪弄这么多别逼币去,还跟我说什么如果真心爱我们家小绿就别逼逼,一手交币,一手交绿,这不明摆着打劫么不是。 20 | 21 | 当天色渐晚的时候,郁闷的`小明`想起了自己家里的那台陪伴着自己长大的矿机,虽然它一次也没挖到矿,每次启动电表都要倒转,但是`小明`依然爱它,谁让它接的是邻居家的电线呢。晚上小明坐在`矿机`前面,按下了那被岁月附着了无数灰尘的开机键,随着一声声轰隆轰隆的机器声,一行小字缓缓显示在了屏幕上面: 22 | 23 | 24 | 25 | > 好运来,鸡你太美,**挖到合格哈希**,一块钱四个,贱卖,嘿嘿,奥利给,谁tm买小米儿 26 | 27 | 28 | 29 | 上一篇文章我们说到过,**挖矿的本质其实就是通过哈希计算得到符合条件的hash的过程。**但是问题在于,我计算出来的hash字符串又是如何和比特币产生联系的?它们两个明显都不是一个东西啊,关键就在`UTXO交易模型`上,那些矿场又是买矿机,又是交电费的,花那么大的算力好不容易挖出来一个新的区块,如果没办法转化成比特币的话那不是亏死,`挖矿毕竟不是扫雷` 原因就在这,每当矿工计算出一个合格的区块的时候,该矿工就拥有了一个特权,那就是允许创造一个`初始的交易`( coinbase),并且可以在里面放上一笔新钱。 30 | 31 | 放在现在的账户余额交易系统里面,就意味着如果你计算出一个新的区块,那么你就得到了一个特权,允许你直接在你银行账户余额的基础上合法地加上指定的金额,当然也不是无限加,要不玩命往上加的话谁还挖什么矿,比特币的话,现在每解锁一个新区块将获得`12.5个比特币`的奖励 ,这么一操作我们计算出来的hash就实实在在地蜕变成我们的比特币。 32 | 33 | 可能是因为爱情的力量,小明竟然成功地挖到一个区块,并且得到了创建一笔为10个`别逼币`初始交易的特权,于是小明在这笔交易的收款栏填写了自己钱包的地址,当然这还没完,由于区块链是去中心化的,需要更多的人认可才能保证这笔交易是合法的,不可窜改的,于是小明需要分别向六个节点去显摆这件事。 34 | 35 | `小明:`看见了没,刚挖的,热乎乎的别逼币,10个,我全给打我钱包里了,不信,这是账单,羡慕吧,羡慕死你。 36 | 37 | `节点:`知道了,滚。 38 | 39 |  40 | 41 | 经过六个节点的确认之后,这10个别逼币就正式地合法地划到了小明的名下。 42 | 43 | `小明`:钱有了,剩下的就是去小绿家把彩礼交了把小绿娶回来了,剩下的5个别逼币还可以带小绿去王者峡谷度个蜜月什么的。 44 | 45 | 但是`别逼币`和`美元`这种纸币不一样,不能说我给你10快,你找我5快那么简单,数字货币之所以安全,是因为除了初始的`coinbase`交易之外,其它所有的资金来源都必须来自于前面某个交易或者几个交易的UTXO,就跟链表一样,一个接一个,这就保证了区块链的任何一笔交易都是可查的,任何一个交易被篡改,链表就不可能正确接上去,所有就很容易验证这笔交易是不是合法的。而区块链中大家都可以创建订单进行交易,数字货币作为一个货币而言流动性就有了。 46 | 47 | 这个时候很多人就要问了,那第一笔交易之前指向谁呢? 48 | 49 | 问的好(谁问了?),在比特币中,第一笔交易前一个交易为`0`。 50 | 51 | 第二个**就是任何一笔交易的交易输入(资金来源)必须要等于交易输出(资金去向)** 52 | 53 | 对于小明来说,就自己有10个别逼币,但是交彩礼的时候,不能一次只花5个别逼币,而是一下都要把这10个别逼币全部花出去,只不过,其中5个支付给了小绿他妈,5个支付给了自己。 54 | 55 | 在区块链中,小明交彩礼的订单大概如下图所示: 56 | 57 |  58 | 59 | 这个订单一共产生了两个输出,也就是两个`UTXO`,一个是小绿他妈的,一个是小明自己的。 60 | 61 | 而且我们会发现,在整个交易过程中并没有什么余额的概念,有的只是每一笔交易的`输入`和`输出`,这个时候有的人可能就疑惑了,你这不对啊,那我比特币钱包里面明明显示我还剩5个比特币啊,这个余额并不是类似于银行系统显示的你的账户余额,而是比特币钱包在扫描了整个区块链所有的交易之后,通过聚合所有**收款人是你**的交易的金额计算出的。 62 | 63 | 就是说当你的钱包在扫描整个区块链交易订单的时候,发现 ,诶,这有一笔最新的交易地址填的是你的地址,于是就加进来算成你的余额,当把所有指向你交易的订单汇总计算之后,这个值就是你拥有的正确余额,在比特币系统中就是你拥有的比特币数量。 64 | 65 | 为什么一定要是最新的那一笔交易呢,比如说前面小明的输出,挖矿的时候产生了一笔10个别逼币输出的订单,然后交彩礼的时候又产生了一笔5个别逼币输出的订单,如果直接聚合相加的话,那么得到小明的余额是**15**个别逼币,显然这是不对的,因为一笔新的交易是建立在之前的那笔订单之上的,所以当一笔新的交易诞生的时候,之前的那笔订单在统计的时候就会被认定为是过期的,而小明正确的余额就最新的订单的输出聚合计算的结果,在本例中,也就是小明交彩礼生成的那个订单,也就是小明还有5个别逼币。 66 | 67 | 划重点,朋友们要考的。 68 | 69 | 不论是比特币还是莱特币,其实说到底,**根本没有什么所谓的数字货币,一切都只是UTXO而已**。 70 | 71 | ## UTXO和账户余额相比较优势在哪里? 72 | 73 | - 第一个:由于`UTOX交易模型`每一笔交易都可以追溯到与之对应的上一笔交易直至追溯到它诞生时挖出来的那个区块为止,也就是链表找到头,这个机制确保了区块链的不可伪造性,即一笔交易不可以被重复支付。 74 | 75 | - 第二个:相对于传统的账户余额模型,UTXO由于只记录最终状态,因为统计余额的时候只需要统计最新的订单就好了,数据库体积要比传统账户余额模型小很多。 76 | 77 | 78 | 但是这并不意味着传统账户余额相对于UTXO就一点优势也没了,首先,`UTXO`本身的实现逻辑上要比`账户余额`复杂的多,其次是账户余额可以很方便地看出整体,比如这个城市所有人的余额近几年是如何变化的,有着更好的发挥空间,而且相较于比特币那种动不动就 `0.00000001` 个比特币,传统账户余额可以很方便的控制交易的精度。 79 | 80 | 81 | 82 | ## 下面开始技术总结: 83 | 84 | 磕磕碰碰终于把这篇文章写完了,相对于上一篇文章,这篇文章可能更加偏向于概念,`UTXO交易模型`对于经常编程的工程师来说并不是特别难于理解,但是对于刚刚接触编程或者区块链技术的人来说就确实有些抽象了,本篇文章在写的过程中的确也下了许多功夫,意在希望让各个阶段的人都可以看懂这篇文章,知道UTXO交易模型是什么。本来想和生活中类比一下,结果发现UTXO似乎是天然为区块链数字货币设计的一样,想不到什么特别合适的比喻,于是只能沿着上一篇文章的脉络接着往下写了。了解了UTXO的运作机制之后,下一篇文章我们仍然以国外的那两篇博客第二篇入手,以一个coder的角度去看UTXO在实际的开发中又是如何实现的,实现一个可交易的区块链系统。 85 | 86 | 相关代码已经上传至本人github。一定要点个**star**啊啊啊啊啊啊啊 87 | 88 | **万水千山总是情,给个star行不行** 89 | 90 | [韩数的开发笔记](https://github.com/hanshuaikang/HanShu-Note) 91 | 92 | 欢迎点赞,关注我,**有你好果子吃**(滑稽) 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /废话设计模式系列/单例模式/设计模式Java语言实现之单例模式.md: -------------------------------------------------------------------------------- 1 | ### 设计模式java语言实现之单例模式 2 | 3 | 4 | 5 | #### 前言: 6 | 7 | 8 | 9 | > 作者:韩数 10 | > 11 | > Github:https://github.com/hanshuaikang 12 | > 13 | > 时间:2019-01-26 14 | > 15 | > Jdk版本:1.8 16 | 17 | 18 | 19 | #### 定义: 20 | 21 | > 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 22 | 23 | 24 | 25 | #### 适用范围: 26 | 27 | >一个全局使用的类频繁地创建与销毁。当某些如打印机程序只需要一个操作实例的时候(多个实例同时调用一台打印机打印文件可能会出现问题) 28 | 29 | 30 | 31 | #### 优/缺点 32 | 33 | > 优点: 34 | > 35 | > 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 36 | > 37 | > 避免对资源的多重占用(比如写文件操作) 38 | > 39 | > 缺点: 40 | > 41 | > 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化 42 | 43 | 44 | 45 | #### 前提引入: 46 | 47 | > 略 48 | 49 | 50 | 51 | 单例模式是设计模式中较为简单的设计模式之一,同时又在实际的开发过程中广泛使用的一种设计模式,本次,我们将从基础版开始,使用4种不同的方式来实现单例模式。 52 | 53 | 54 | 55 | #### 代码实战: 56 | 57 | 58 | 59 | ##### 1.基础版 60 | 61 | 62 | 63 | 这种方式实现的单例模式是我们日常使用较多的设计模式,采用了延迟加载来减少系统资源不必要的开支,但如果多个线程同时调用 **getInstance** 方法获取Singleton的实例时,可能会出现问题。 64 | 65 | 66 | 67 | **优/缺点:** 68 | 69 | > 优点:易于实现 70 | > 71 | > 缺点:线程不安全,在多线程下不能正常工作 72 | 73 | 74 | 75 | 代码如下: 76 | 77 | 78 | 79 | ```java 80 | /*** 81 | * 82 | * @author 韩数 83 | * 一般单例模式实现,采用延迟加载方式实现 84 | * 85 | */ 86 | 87 | public class Singleton { 88 | 89 | private static Singleton uniqueInstance; 90 | 91 | private Singleton() {} 92 | 93 | public static Singleton getInstance() { 94 | if (uniqueInstance == null) { 95 | uniqueInstance = new Singleton(); 96 | } 97 | return uniqueInstance; 98 | } 99 | 100 | } 101 | ``` 102 | 103 | #### 104 | 105 | ##### 2.线程安全基础版: 106 | 107 | 108 | 109 | 代码如下: 110 | 111 | 112 | 113 | **优/缺点:** 114 | 115 | > 优点:线程安全,没有加锁,执行效率会高一点 116 | > 117 | > 缺点:容易产生垃圾对象,类加载时就初始化,浪费内存 118 | 119 | 120 | 121 | 122 | 123 | ```java 124 | /*** 125 | * 126 | * @author 韩数 127 | * 常用的单例模式实现 128 | * 线程安全,不足之处,可能会损失一部分性能,非延迟加载 129 | * 130 | */ 131 | 132 | 133 | public class Singleton { 134 | 135 | //在类初始化的时候就实例化对象,所以不存在多线程的安全问题 136 | private static Singleton uniqueInstance = new Singleton(); 137 | 138 | private Singleton() {} 139 | 140 | public static Singleton getInstance() { 141 | return uniqueInstance; 142 | } 143 | } 144 | ``` 145 | 146 | 147 | 148 | ##### 3.线程安全加强版: 149 | 150 | 151 | 152 | **优/缺点:** 153 | 154 | > 优点:线程安全,延迟加载 155 | > 156 | > 缺点:必须加锁,效率比较低 157 | 158 | 159 | 160 | 代码如下: 161 | 162 | 163 | 164 | ```java 165 | /** 166 | * 167 | * @author 韩数 168 | * 线程安全方式实现单例模式,缺点是,每次都会调用synchronized的关键字修饰的方法,会损失一定的性能 169 | * 170 | */ 171 | 172 | public class Singleton { 173 | 174 | 175 | private static Singleton uniqueInstance; 176 | 177 | private Singleton() {} 178 | 179 | //synchronized修饰该方法,保证每次只有一个线程进入该方法,从而避免产生多个Singleton对象实例。 180 | public static synchronized Singleton getInstance() { 181 | if (uniqueInstance == null) { 182 | uniqueInstance = new Singleton(); 183 | } 184 | return uniqueInstance; 185 | } 186 | } 187 | 188 | ``` 189 | 190 | 191 | 192 | ##### 4.线程安全旗舰版: 193 | 194 | 195 | 196 | 双检锁/双重校验锁(DCL,即 double-checked locking): 可以在保证安全性的情况下,兼顾高性能 197 | 198 | 199 | 200 | **优/缺点:** 201 | 202 | > 优点:线程安全,延迟加载,执行效率高 203 | > 204 | > 缺点:实现难度比前几种稍显复杂 205 | 206 | 207 | 208 | 关于 volatile 关键字,感兴趣的伙伴可以去这个博客看一下,写得非常好 209 | 210 | https://www.cnblogs.com/dolphin0520/p/3920373.html 211 | 212 | 213 | 214 | 代码如下: 215 | 216 | ```java 217 | /*** 218 | * 219 | * @author 韩数 220 | * 线程安全,延迟加载方式实现单例模式 221 | * 222 | */ 223 | 224 | public class Singleton { 225 | 226 | private volatile static Singleton uniqueInstance; 227 | 228 | // 一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 229 | //保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是 立即可见的。 230 | //禁止进行指令重排序。 231 | 232 | private Singleton() {} 233 | 234 | public static Singleton getInstance() { 235 | //延迟加载 236 | if (uniqueInstance == null) { 237 | //加线程锁 238 | synchronized (Singleton.class) { 239 | if (uniqueInstance == null) { 240 | uniqueInstance = new Singleton(); 241 | } 242 | } 243 | } 244 | return uniqueInstance; 245 | } 246 | } 247 | 248 | ``` 249 | 250 | 251 | 252 | 253 | 254 | #### 总结: 255 | 256 | 本篇文章较为简单的阐述了单例模式的优缺点,使用场景,以及4种不同的实现方式,当然,现实生活中,单例模式的实现绝非只有本文中的4种,如果对单例模式的其他实现方式感兴趣的话,大家可以去互联网上查询相关的资料。 257 | 258 | 259 | 260 | #### 写在最后: 261 | 262 | 欢迎大家给小星星,您的星星是我写下去的不竭动力! 263 | 264 | 源码部分请移步本人Github下载: 265 | 266 | Github地址: 267 | 268 | Github:https://github.com/hanshuaikang/design-pattern-java 269 | 270 | 271 | 272 | 参考资料: 273 | 274 | 菜鸟教程:http://www.runoob.com/design-pattern/strategy-pattern.html 275 | 276 | Head frist of 设计模式 277 | 278 | 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /Hadoop初级入门教程/写给后端的Hadoop初级入门教程:基本概念篇.md: -------------------------------------------------------------------------------- 1 | # 写给后端的Hadoop初级入门教程:基本概念篇 2 | 3 | ## 前言: 4 | 5 | Hello大家好,我是`韩数`。距离我们上一个系列[写给后端的Nginx初级入门教程](https://juejin.im/post/5dc8ede66fb9a04a5e6da815)已经过去整整25天了,中间穿插了两篇区块链相关的文章,其实吧,这二十来天我一直在憋大招,那就是这个最新的系列写给后端的Hadoop初级入门教程,由于Hadoop本身的技术细节还是很多的,`Hadoop基础环境的搭建`,`分布式伪分布式的部署`,`集群启动的准备`,`hdfs文件系统`,`MR编程模型`,以及最后的`优化`等等,整个一套写下来工作量还是蛮大的,好在我快放寒假了(开心),这样使得我有充足的时间和精力去写这套教程,一来是为了帮助自己在写作的时候更加深入地理解这方面的知识,而来是希望可以帮助到那些刚刚准备入门大数据的朋友们去理解和使用Hadoop这门技术。毕竟大家都知道,现在网上搜到的那些技术教程,质量参差不齐,一不小心踩到坑就是: 6 | 7 | **一电脑,一根烟,一篇教程学半天,调试半天却不对,想送作者上青天。** 8 | 9 | 好湿好湿,本篇文章作为整套Hadoop入门教程的第一篇,我们依然从最基础的概念说起,什么是大数据,大数据如何影响我们的生活?什么是Hadoop,Hadoop和其他大数据技术相比又有哪些优势?明白了这些问题,我相信再学大数据,虽然不能说有buff加成,但是至少知道自己接下来要学的这玩意儿是个啥了。 10 | 11 | 不废话,直接上东西 12 | 13 | ## 什么是大数据: 14 | 15 | 大数据 (Big Data) : 主要是指`无法在一定范围`内用常规软件工具进行捕捉,管理和处理的数据集合,是需要新处理模式才能具有更强的决策力,洞察发现力和流程优化能力的`海量,高增长率和多样化的信息资产`。 16 | 17 | 一句话解释:**大数据就是大量数据,数据多到传统方案无法处理的程度。** 18 | 19 | 当然数据的体量并不是最重要的,重要的是隐藏在这些数据中的`信息`,这些信息不论是在商业上还是在研究上都有着巨大的价值,电商通过挖掘这些数据中的信息为每个用户画像,并且推荐合适的商品给用户增加购买,当然,也可以顺便调整一下改个价格杀个熟什么的。 20 | 21 | ### 大数据的单位: 22 | 23 | 但我们毕竟是严谨的理科生啊,你说大数据大数据,多大才是大数据?为了解决这个问题,减少撕逼,科学家就制定了一系列的数据单位,从小到大依次是: 24 | 25 | `bit` `Byte` `KB` `MB` `GB` `TB` `PB` `EB` `ZB` `YB` `BB` `NB(牛逼)` 和 `DB(呆逼)` 26 | 27 | 当然,光讲这些单位有什么意思,我怎么能知道这些单位能存多少数据?为了方便大家更加直接的感受到这些数据单位的威力,我找了一些小栗子: 28 | 29 | - 全世界所产生的印刷材料的数据大概是200PB。 30 | - 全世界人类总共说过的话大概是5EB。 31 | - 国外知名网站P站2017年网站产生的总数据量为 3732PB 。 32 | - 一百万个汉字大概所需要的内存是2MB。 33 | 34 | 刚才好像混入了什么奇怪的东西。 35 | 36 | ### 大数据的特点: 37 | 38 | - `大量:`必须的,不大都不好意思叫大数据。 39 | - `高速:`这么多数据肯定要快速消化掉的,处理几十年也等不起啊,今年双十一的成交额总不能算到明年双十一再公布吧。 40 | - `多样:`不同的场景会产生不同的数据,优酷就是用户浏览数据,视频数据,QQ音乐就是音乐数据。 41 | - `低价值密度:`这个意思是**即使数据量很大,但是我们关注的始终的特定的部分,而非整体**,就像警察叔叔调监控一样,一年前一个月前的数据通常对他来说是没什么用的,他只要那么几个关键节点的监控数据就可以了。 42 | 43 | 应用场景就不说了,哪都是应用场景。 44 | 45 | ## Hadoop是什么? 46 | 47 | 知道了什么是大数据,我们就得思考另外一个问题,弄这么多的数据我放哪啊? 48 | 49 | `杠精:`不明摆着的么,当然放硬盘里啊,要不放哪儿,还能写纸上? 50 | `我:`硬盘我知道,可是万一这块硬盘坏了,那数据不就没了吗? 51 | 52 | `路人`:你系不系傻,你多放几块硬盘,分别放上去不就行了吗? 53 | 54 | 这个时候`Hadoop`来了,弟弟们都往边上靠靠,你们那种办法太笨拙,交给我,轻轻松松地给你搞定,小意思。 55 | 56 | `Hadoop`是一个由Apache基金会所开发的分布式系统基础架构,主要用来解决大数据的存储和分析计算问题。 57 | 58 | 当然,`Hadoop`和`Spring`一样,到现在已经没法去仅仅理解为`Hadoop`这门技术了,就像你跟别人说,我这个新电商项目基于`Spring`写的,那别人肯定不会觉得你只用了`Spring`,会觉得你可能用了`Spring MVC `,`boot`,`JPA`等一系列`Spring`生态的技术。同样地,`Hadoop`也是如此,不仅仅是代表`Hadoop`本身这项技术,同时也代表围绕`Hadoop`的技术生态。 59 | 60 | 而且大家千万不要把事情想复杂,以为分布式存储什么这些概念都是多么深奥的东西,的确,官方概念确实是有点抽象晦涩了,但是我觉得,**任何一项理论都一定来源于生活,因为是生活给予了他们灵感,但是生活并不是十分复杂的,所以任何深奥复杂的理论一定可以在生活中找到一个通俗易懂的解释。** 61 | 62 | 什么是分布式存储,不跟大家吹,我初中的时候就已经在搞这个了,那时候流行看玄幻小说,那种大部头知道吧,特厚,通常一个班就只有那么一本,被教导主任没收了就完蛋了,谁都没得看,于是当时盛行把一本玄幻小说一页一页撕下来,每个同学几页,大家互相换着看,就算老师发现了也就只是没收了一部分,没办法全部歼灭。你看,分布式有了,存储有了,这不就是分布式存储吗?为了防止一本书被老师没收了导致这本书不完整,那就买三本,也这么几页几页分开存,这不就是多备份吗,没那么复杂,别老纠结那些学者写的给学者看的概念。 63 | 64 | ## Hadoop发展史: 65 | 66 | 这个也没啥好讲的,我这里就列几个关键的点,感兴趣的朋友下去可以自己搜,网上一搜一大堆。 67 | 68 | - 一个叫Dung Cutting 没事用java写了一个全文搜索的框架 - `Lucene` 69 | - 数据量大的时候,`Lucene`性能跟不上了就。 70 | - 巧了,Google本身也是做全文搜索的,为啥人家性能就那么顶呢? 71 | - 通过学习谷歌,搞了个`Nutch` 72 | - 后来谷歌公开了部分`GFS`和`MapReduce`的细节。 73 | - Dung Cutting 一看这答案都给自己了,于是花了两年,注意是业余时间,自己实现了`DFS`和`MapReduce`,`Nutch·性能一下字就提上去了,一个字,牛逼。 74 | - 后来`Hadoop`作为`Lucene`子项目`Nutch`的一部分被正式引进了Apache基金会。 75 | - 然后`Map-Reduce`和`NDFS`一块被整合进了`Hadoop`项目里面,`Hadoop`就这么诞生了。 76 | 77 | 为啥人家业余时间就能搞出来这么牛逼的东西,我业余时间王者荣耀王者都上不去,难道有中间商赚差价? 78 | 79 | ## Hadoop发行版本: 80 | 81 | 和Linux差不多,不同的公司在此基础上分别定制了自己的发行版本,`Hadoop`发行版本主要有三个,分别是: 82 | 83 | - Apache版本:最原始(最基础)的版本,对于入门学习最好,毕竟是出生地,血统也是最正的。 84 | - Cloudera :在大型互联网企业中用的较多。 85 | - Hortonworks:文档比较全。 86 | 87 | 不用想,我们肯定选Apache,也没啥别的原因,就是因为它基础,简单,不要钱。 88 | 89 | ## Hadoop优势是什么? 90 | 91 | `Hadoop`为啥这么牛逼,导致我们现在一说大数据开发,就会想到Hadoop? 92 | 93 | 毕竟写程序不是谈恋爱,没什么就算你不好我也依然爱你这回事,我们坏得很,哪个好用使哪个。 94 | 95 | `Hadoop`在江湖中能混到今天的地位主要靠以下四点: 96 | 97 | - 高可靠性:`Hadoop`底层使用多个数据副本,即使`Hadoop`某个计算元素或存储出现故障,也不会导致数据的丢失,想想上面讲的分布式存储的例子。 98 | - 高扩展性:在集群间分配任务数据,可以方便的扩展数以千计的节点。就是,有一天运维早上一上班,卧槽,集群存储不够了,但是问题不大,因为在集群中加入一个新的节点或者去掉一个节点都分分钟的事儿。 99 | - 高效性:在`MapReduce`的思想下,`Hadoop`是并行工作的,以加快任务处理速度。 100 | - 高容错性:能够将失败的任务重新分配。 101 | 102 | 你说了一堆优点,`Hadoop`就没啥缺点吗?必须有,但是这个要到后面写到`HDFS`,`MR`的时候才能说,要不现在都不知道`Hdfs`是啥,说缺点的话不形象,**就跟说人坏话一样,当着人家面儿说才有效果。** 103 | 104 | ## 下面开始技术总结: 105 | 106 | 今天这篇文章呢,作为整套`Hadoop`系列教程的第一篇,主要是按照我写博客的习惯讲了一些基本的概念,希望大家看过之后心里能够对大数据和`Hadoop`有个基本的认识,另外,我写技术文章比较口语化,废话比较多,这个欢迎大家提建议,放心,提了我也不改,但是我写小说啥的还是非常严肃的,而且废话文你读起来比那些深奥玩弄概念的文章快多了(滑稽),下一篇文章呢,我们同样也是概念篇,主要讲`HDFS`,`YARN`,`MR`这三个`Hadoop`核心概念,之后就是实打实的要和代码接触了。 107 | 108 | 非常感谢能读到这里的朋友,你们的支持和关注是我坚持高质量分享下去的动力。 109 | 110 | 相关代码已经上传至本人github。一定要点个**star**啊啊啊啊啊啊啊 111 | 112 | **万水千山总是情,给个star行不行** 113 | 114 | [韩数的开发笔记](https://github.com/hanshuaikang/HanShu-Note) 115 | 116 | 欢迎点赞,关注我,**有你好果子吃**(滑稽) 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /Django系列学习笔记/Django项目 uwsgi+Nginx 部署教程.md: -------------------------------------------------------------------------------- 1 | ## Django项目 uwsgi+Nginx 部署教程 2 | 3 | ## 背景: 4 | 5 | 最近在学习django的过程中,用django做了一个小demo,所以试试看能不能部署到服务器上,自己顺便也熟悉一下Django整个部署的流程,因为之前学习flask的时候,就使用的uwsgi来作为python web服务器来部署的,所以这次也就选择了nginx+uwsgi这样的一个组合。 6 | 7 | 当时买完云服务器之后,就顺手把宝塔装上了,之前用宝塔部署php项目是真的很容易,所以就偷懒试试宝塔能不能部署django,苦试一个小时,无果,遂放弃,还是老老实实用原生的uwsgi配合Nginx来部署吧。然后下面的流程我会争取把有可能是坑的地方重点标注出来,将来如果发博客或者自己需要再次部署的时候,也可以少写很多配置文件。 8 | 9 | ## 环境: 10 | 11 | `系统版本`:centos7.5 12 | 13 | `python`版本:3.6.5 14 | 15 | `nginx `版本:1.16.1 16 | 17 | `uwsgi`版本:2.0.18 18 | 19 | 如环境不同,还请查阅其他资料另行配置。 20 | 21 | ## 环境准备: 22 | 23 | 在部署之前,请确保你的linux服务器正确安装了对应的python版本,如果需要python3版本,请自行查阅资料进行升级安装。 24 | 25 | 首先为了更好的独立部署,避免对其他的项目产生干扰,我们需要安装python 虚拟环境: 26 | 27 | ```linux 28 | sudo pip install virtualenv 29 | sudo pip install virtualenvwrapper 30 | ``` 31 | 32 | > virtualenvwrapper 是virtualenv的扩展管理包,可以将所有的虚拟环境整合在一个目录下。 33 | 34 | **配置虚拟环境:** 35 | 36 | ```bash 37 | mkdir ~/.virtualenvs 38 | ``` 39 | 40 | 打开.bashrc: 41 | 42 | ```bash 43 | sudo vim ~/.bashrc 44 | ``` 45 | 46 | 在.bashrc的末尾增加下面内容: 47 | 48 | ```bash 49 | export WORKON_HOME=$HOME/.virtualenvs # 所有虚拟环境存储的目录 50 | source /usr/local/python3/bin/virtualenvwrapper.sh 51 | ``` 52 | 53 | > 注意!:这里的/usr/local/bin/virtualenvwrapper.sh只是针对于我当前系统环境的一个位置,并不是所有的服务器都是在这个位置,如果自己不知道virtualenvwrapper.sh在哪里,可以搜索文件来找到它在系统中的位置,并且修改.bashrc。 54 | 55 | 启用配置文件: 56 | 57 | ```bash 58 | source ~/.bashrc 59 | ``` 60 | 61 | 这个时候如果不报错,就代表我们的虚拟环境配置成功了,一般常见的报错就是virtualenvwrapper.sh文件找不对。 62 | 63 | **创建虚拟环境:** 64 | 65 | 找一个你自己觉得能记住的地方,新建一个env 文件夹: 66 | 67 | ```bash 68 | cd /www 69 | mkdir env 70 | cd env #进入env目录 71 | ``` 72 | 73 | 新建一个虚拟环境: 74 | 75 | ```bash 76 | mkvirtualenv -p /usr/bin/python3 orange_env # my_env是虚拟环境的名称 77 | ``` 78 | 79 | 注意:如果你的软连接/usr/bin/python3没有的话,会报错误,找不到/usr/bin/python3,这个时候就需要你自己新建一个软连接: 80 | 81 | 如果报错: 82 | 83 | ```bash 84 | ln -s /usr/local/python3/bin/python3 /usr/bin/python3 # 路径要改成自己的python安装路径 85 | ``` 86 | 87 | 之后便可以进入我们的虚拟环境了: 88 | 89 | ```bash 90 | source /www/env/orange_env/bin/activate 91 | ``` 92 | 93 | 进去虚拟环境之后,前面会出现一个括号,里面是你虚拟环境的名字: 94 | 95 | ```bash 96 | (orange_env) [root@iz2ze1cvux96riiwfh05qqz ~]# 97 | ``` 98 | 99 | 在虚拟环境中安装uwsgi: 100 | 101 | ```bash 102 | pip install uwsgi 103 | ``` 104 | 105 | **退出虚拟环境:** 106 | 107 | ```bash 108 | deactivate 109 | ``` 110 | 111 | 再次在主环境中安装uwsgi: 112 | 113 | ```bash 114 | pip install uwsgi 115 | ``` 116 | 117 | > 注意:如果你有其他的依赖,比如django,msqlclient这些,记得一定要在虚拟环境里pip安装一下。 118 | 119 | ## 部署过程: 120 | 121 | 找一个你认为比较合适的地方,新建一个文件夹,将你的Django项目上传进去: 122 | 123 | 以我为例: 124 | 125 | ```bash 126 | cd /www 127 | mkdir orange 128 | ``` 129 | 130 | 上传解压操作略,记得是上传项目根目录,就是直接带manage.py的那个目录。 131 | 132 | 新建一个uswgi配置文件,uswgi支持多种配置文件类型,比如yaml,xml,json,ini,这里我选的是xml。 133 | 134 | ```bash 135 | vim mysite.xml #记得mysite.xml 要和你项目的manage.py 在一个目录下。 136 | ``` 137 | 138 | mysite.xml内容如下: 139 | 140 | ```xml 141 | 142 | 127.0.0.1:8080 143 | /www/orange/ 144 | orangeproject.wsgi 145 | 4 146 | uwsgi.log 147 | 148 | ``` 149 | 150 | 151 | 152 | **安装Nginx:** 153 | 154 | Nginx 我之前有写过一系列的基础入门教程,如果对安装启动重启这些不是很熟练的可以看下面这篇文章: 155 | 156 | 链接:[写给后端的Nginx初级入门教程:实战篇](https://juejin.im/post/5db8f8c3f265da4d3e173c62) 157 | 158 | 查看nginx 配置文件路径: 159 | 160 | ```bash 161 | nginx -t 162 | ``` 163 | 164 | 记得备份nginx之前的配置文件,然后把之前的配置全部删了,直接加入下面内容: 165 | 166 | ```conf 167 | worker_processes 1; 168 | events { 169 | worker_connections 1024; 170 | } 171 | http { 172 | include mime.types; 173 | default_type application/octet-stream; 174 | sendfile on; 175 | server { 176 | listen 8000; 177 | server_name 你的域名; 178 | charset utf-8; 179 | location / { 180 | include uwsgi_params; 181 | uwsgi_pass 127.0.0.1:8080; 182 | uwsgi_param UWSGI_SCRIPT orangeproject.wsgi; 183 | uwsgi_param UWSGI_CHDIR /www/orange/; 184 | 185 | } 186 | location /static/ { 187 | alias /www/orange/transfer/static/; 188 | } 189 | } 190 | } 191 | ``` 192 | 193 | > 注意模块名要保持一致,而且你nginx监听的端口不能和你django启动的端口一样,要不uswgi会因为nginx占用端口启动失败。alias /www/orange/transfer/static/; 这个是你的静态文件地址,css,img这些。 194 | 195 | 检查nginx是否配置成功: 196 | 197 | ```bash 198 | nginx -t 199 | ``` 200 | 201 | 重启nginx: 202 | 203 | ```bash 204 | nginx -s reload 205 | ``` 206 | 207 | 之后,再次进入我们的虚拟环境orange_env中,启动我们的uwsgi服务器: 208 | 209 | ```bash 210 | cd /www/orange 211 | uwsgi -x mysite.xml 212 | ``` 213 | 214 | 然后打开我们的本地浏览器,输入:域名:8000,备案过的可以改nginx配置文件成80。 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /废话设计模式系列/命令模式/设计模式Java语言实现之命令模式.md: -------------------------------------------------------------------------------- 1 | # 设计模式Java语言实现之命令模式 2 | 3 | ## 前言: 4 | 5 | 6 | 7 | > 作者:韩数 8 | > 9 | > Github:https://github.com/hanshuaikang 10 | > 11 | > 时间:2019-01-26 12 | > 13 | > JDK版本:1.8 14 | 15 | 16 | 17 | ## 概述: 18 | 19 | > 命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。 20 | 21 | ## 应用场景: 22 | 23 | > 日志记录,撤销操作,队列请求 24 | 25 | ## 优/缺点: 26 | 27 | > 优点: 28 | > 29 | > 1、降低了系统耦合度。 30 | > 2、新的命令可以很容易添加到系统中去 31 | > 32 | > 缺点: 33 | > 34 | > 1.使用命令模式可能会导致某些系统有过多的具体命令类。 35 | 36 | 37 | 38 | ## 前提引入: 39 | 40 | 最近大家都在搞智能家居,构建自己的生态,阿呆的老板身为中国五十万强公司的老板,岂能没有嗅到这其中的商机呢?于是让连忙阿呆停下手中的工作,毕竟作为公司唯一的技术人员,阿呆还是很受重用的。 41 | 42 | 老板;阿呆啊,你来公司已经有两年了吧,你摸着自己的良心给我说,我待你如何? 43 | 44 | 阿呆:那没得说,对我简直不能再好了,有好几次我都觉得好像我是您失散多年的父,不对,儿子一样。(冷汗...不会要开除我吧) 45 | 46 | 老板:你能这样说我就放心了,是这样,最近我看新闻说这个智能家居是一个新的潮流,常常跟你讲,我们要懂得抓住未来,你要是有我一半的智慧,也早就成为公司的二把手了。是这样的,我们公司三个员工,在管理上占据了我相当大的精力了,如果再来一家公司要我管理的话,那是真的两个脑袋也管不过来啊。所以我打算成立一个做智能家居的新公司,我看那个什么公司来着,小麦?就做的挺好的嘛。 47 | 48 | 阿呆:是小米。 49 | 50 | 老板:对对,小米,考虑到我们自身的情况确实和小米差点,所以新公司小木集团,我打算派你去管理。 51 | 52 | 阿呆:真的吗(感动中...虽然老板平时喜欢拖欠工资,把人当苦力,天天九九六,加班不给钱,.....但没想到老板组建分公司第一个想到的就是自己,放心吧老板,我阿呆追随你到天涯海角,你跑路我跟你跑!),那研发费用,还要招新员工的钱? 53 | 54 | 老板:等你盈利之前,就先从你工资里扣吧。 55 | 56 | 阿呆:吐血 57 | 58 | 59 | 60 | 好尬..... 61 | 62 | 63 | 64 | 阿呆看着自己那点工资,心想啥也做不出来啊,别说做家居了,买家居的钱都没有。那做什么呢,做电视吧,算了吧,做个遥控器还差不多? !!! 就做遥控器! 65 | 66 | 小木牌智能遥控器,一键配对,按键功能随心搭配,一个遥控器在手,去哪看电视都不愁! 67 | 68 | 69 | 70 | 那么问题来了,既然要做到遥控器每个按钮的功能可以更换,那势必不能按照传统的方法来开发了,有过之前的教训,阿呆已经明白,写一个类把所有功能写死是万万不能的,那应该怎么做呢,这时候就该我们的命令模式派上用场了,当然,这里直接举例代码说明是不太容易帮助我们刚学习设计模式的小伙伴们理解的,大家不妨这样想: 71 | 72 | 在一个军队中,将军突然渴了,于是想到张三是负责接水的,就让张三送水来喝,饿了,想起来厨师是李四,于是就让李四去做饭送来吃,当然,记住两个人的名字对于将军来说并非难事,那要是将军是个事儿多的人呢,可能就要记住成千上百个人的名字和他们对应的职务了,这对于将军来说肯定不能把精力花在记名字这件事情上,否则就会造成打仗打不好,这个时候我们引入管家这个概念,因为管家不需要打仗,所以管家有足够多的精力去记名字,名字过多时,我们也可以设立多个管家来解决,所有当将军饿的时候,只需要说一句"我饿了,送饭来"就行了,管家听到之后,于是把李四叫过来把饭做好,然后给将军享用。在这个例子中,**将军是不知道李四是负责做饭的,李四也不知道将军的存在,所以即使换了别的人做将军,或者换了别的人做饭,只要管家还在,对于整个系统就是没有影响的,所以降低了将军和李四之间的耦合。因为有了管家这个角色的存在,我们甚至可以做到,李四把饭做好的时候,管家记录李四做的什么饭(应用场景中的日志记录),饭做的不好吃让李四回去重新做(撤销,更改等操作)**,同理,命令模式也是这样,调用者只负责发送命令,而不需要关心谁来具体执行业务逻辑。 73 | 74 | 看代码: 75 | 76 | 77 | 78 | ## 代码实战: 79 | 80 | ### Receiver接口:接收者接口 81 | 82 | ```java 83 | 84 | /*** 85 | * 86 | * @author 韩数 87 | * 定义业务实现的行为。 88 | * 89 | */ 90 | public interface Receiver { 91 | //业务逻辑接口,接收者知道业务逻辑的具体实现方法 92 | public void action(); 93 | 94 | } 95 | ``` 96 | 97 | ### UpReceiverImpl类:接收者遥控器打开功能实现 98 | 99 | ```java 100 | 101 | /*** 102 | * 103 | * @author 韩数 104 | * 业务逻辑的具体实现,在这里主要是指遥控器的开操作。 105 | * 106 | */ 107 | public class UpReceiverImpl implements Receiver{ 108 | 109 | @Override 110 | public void action() { 111 | // TODO Auto-generated method stub 112 | System.out.println("打开电视!"); 113 | 114 | } 115 | 116 | } 117 | 118 | ``` 119 | 120 | ### Command:命令接口,定义命令的行为: 121 | 122 | ```java 123 | /*** 124 | * 125 | * @author 韩数 126 | * 命令接口,定义命令的行为,比如撤销,更改等操作都适合在这里定义。 127 | * 128 | */ 129 | public interface Command { 130 | 131 | /*** 132 | * 命令执行的方法 133 | */ 134 | public void execute(); 135 | 136 | 137 | 138 | } 139 | 140 | ``` 141 | 142 | ### ConcreteCommand:命令的具体实现类: 143 | 144 | ```java 145 | /**** 146 | * 147 | * @author 韩数 148 | * 具体的命令实现类,在这里完成Reciver和Command之间的绑定。 149 | * 150 | */ 151 | public class ConcreteCommand implements Command { 152 | 153 | private Receiver receiver; 154 | 155 | public ConcreteCommand(Receiver receiver) { 156 | this.receiver = receiver; 157 | } 158 | 159 | //实际的业务逻辑是委托给Reciver对象来执行的。 160 | @Override 161 | public void execute() { 162 | // TODO Auto-generated method stub 163 | receiver.action(); 164 | 165 | } 166 | 167 | } 168 | ``` 169 | 170 | ### Invoker:调用者 171 | 172 | ```java 173 | /*** 174 | * 175 | * @author 韩数 176 | * 调用者,调用者不知道是具体的服务提供方是谁,只知道对应的Command命令,通过命令中封装的具体服务实例,从而实现了代码间的解耦合。 177 | * 178 | */ 179 | public class Invoker { 180 | 181 | private Command command; 182 | 183 | public void setCommand(Command command) { 184 | this.command = command; 185 | } 186 | 187 | public void action() { 188 | command.execute(); 189 | } 190 | 191 | 192 | } 193 | 194 | ``` 195 | 196 | ### 测试: 197 | 198 | ```java 199 | /*** 200 | * 201 | * @author 客户端 202 | * 模拟一个命令模式执行的流程 203 | * 204 | */ 205 | public class Client { 206 | 207 | public static void main(String[] args) { 208 | 209 | Receiver receiver = new UpReceiverImpl(); 210 | Command command = new ConcreteCommand(receiver); 211 | Invoker invoker = new Invoker(); 212 | invoker.setCommand(command); 213 | invoker.action(); 214 | 215 | 216 | 217 | } 218 | 219 | } 220 | ``` 221 | 222 | 223 | 224 | **OUTPUT:** 225 | 226 | 打开电视! 227 | 228 | 229 | 230 | ## 总结: 231 | 232 | 以上就是命令模式的一个简单的实现了,至于如何实现操作的撤销,更改,记录等操作,我这里更多的是想小伙伴们自己去动脑实现,技术的养成并非一朝一夕,要经过自己的思考才能得到真正的理解,本系列笔记目前已经开源至**github**,如果需要本文的电子版markdown笔记和配套代码,可以关注微信公众号**码上marson** 或者移步github下载即可。 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HanShu-Note 2 | 叮叮叮铛,韩数的学习笔记(哆啦A梦配音) 3 | 4 | 大家好,我是韩数,这里收录了我的大多数学习笔记,包括Nginx,JAVA,Springboot,SpringCloud,Docker等后端常用的技术,大多数文章都经过markdown精心排版,总之八个字: 5 | 6 | **思路清奇,写法风骚** 7 | 8 | 后续预告: 9 | 10 | Spark 大数据初级教程 11 | 12 | 面向后端的Docker提升教程:高级篇 13 | 14 | 面向后端的Mysql初级入门教程等 15 | 16 | 17 | 18 | **写给后端的Nginx初级入门教程**: 19 | 20 | [写给后端的Nginx初级入门教程:基础篇](https://github.com/hanshuaikang/HanShu-Note/blob/master/Nginx%E5%88%9D%E7%BA%A7%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/%E5%86%99%E7%BB%99%E5%90%8E%E7%AB%AF%E7%9A%84Nginx%E5%88%9D%E7%BA%A7%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%E5%9F%BA%E7%A1%80%E7%AF%87.md) 21 | 22 | [写给后端的Nginx初级入门教程:实战篇](https://github.com/hanshuaikang/HanShu-Note/blob/master/Nginx%E5%88%9D%E7%BA%A7%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/%E5%86%99%E7%BB%99%E5%90%8E%E7%AB%AF%E7%9A%84Nginx%E5%88%9D%E7%BA%A7%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%E5%AE%9E%E6%88%98%E7%AF%87.md) 23 | 24 | **面向后端的Docker初级入门教程:** 25 | 26 | [面向后端的Docker初级入门教程:基础篇](https://github.com/hanshuaikang/HanShu-Note/blob/master/Docker%E5%88%9D%E7%BA%A7%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/%E9%9D%A2%E5%90%91%E5%88%9D%E5%AD%A6%E8%80%85%E7%9A%84docker%E5%AD%A6%E4%B9%A0%E6%95%99%E7%A8%8B%EF%BC%9A%E5%9F%BA%E7%A1%80%E7%AF%87.md) 27 | 28 | [Docker 三要素 :镜像、容器和仓库](https://github.com/hanshuaikang/HanShu-Note/blob/master/Docker%E5%88%9D%E7%BA%A7%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/Docker%20%E4%B8%89%E8%A6%81%E7%B4%A0%20%EF%BC%9A%E9%95%9C%E5%83%8F%E3%80%81%E5%AE%B9%E5%99%A8%E5%92%8C%E4%BB%93%E5%BA%93.md) 29 | 30 | [面向后端的Docker初级入门教程:实战篇](https://github.com/hanshuaikang/HanShu-Note/blob/master/Docker初级入门教程/面向后端的Docker初级入门教程:实战篇(上).md) 31 | 32 | [面向后端的Docker初级入门教程:DockerFile 命令详解](https://github.com/hanshuaikang/HanShu-Note/blob/master/Docker%E5%88%9D%E7%BA%A7%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/%E5%86%99%E7%BB%99%E5%90%8E%E7%AB%AF%E7%9A%84Docker%E5%88%9D%E7%BA%A7%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%9ADockerFile%20%E5%91%BD%E4%BB%A4%E8%AF%A6%E8%A7%A3.md) 33 | 34 | **Spring Boot系列笔记:** 35 | 36 | [SpringBoot +Mybatis 实现多数据源配置(两篇)](https://github.com/hanshuaikang/HanShu-Note/blob/master/SpringBoot%E7%B3%BB%E5%88%97%E7%AC%94%E8%AE%B0/mybatis%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90%E9%85%8D%E7%BD%AE/SpringBoot%20%2B%20Mybatis%E4%BD%BF%E7%94%A8AOP%E5%AE%9E%E7%8E%B0%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2%E6%95%B0%E6%8D%AE%E6%BA%90.md) 37 | 38 | [Springboot 之定义全局异常处理](https://github.com/hanshuaikang/HanShu-Note/blob/master/SpringBoot%E7%B3%BB%E5%88%97%E7%AC%94%E8%AE%B0/%E5%85%A8%E5%B1%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86/Springboot%E4%B9%8B%E8%87%AA%E5%AE%9A%E4%B9%89%E5%85%A8%E5%B1%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86.md) 39 | 40 | [SpringBoot 嵌入式Web 容器](https://github.com/hanshuaikang/HanShu-Note/blob/master/SpringBoot%E7%B3%BB%E5%88%97%E7%AC%94%E8%AE%B0/%E5%B5%8C%E5%85%A5%E5%BC%8Fweb%E5%AE%B9%E5%99%A8/%E5%B5%8C%E5%85%A5%E5%BC%8Fweb%E5%AE%B9%E5%99%A8.md) 41 | 42 | [Springboot 之自定义starter](https://github.com/hanshuaikang/HanShu-Note/blob/master/SpringBoot%E7%B3%BB%E5%88%97%E7%AC%94%E8%AE%B0/%E8%87%AA%E5%AE%9A%E4%B9%89starter/Springboot%20%E4%B9%8B%E5%88%9B%E5%BB%BA%E8%87%AA%E5%AE%9A%E4%B9%89starter.md) 43 | 44 | [Springboot 之自定义注解扫描器](https://github.com/hanshuaikang/HanShu-Note/blob/master/SpringBoot%E7%B3%BB%E5%88%97%E7%AC%94%E8%AE%B0/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E8%A7%A3%E6%89%AB%E6%8F%8F%E5%99%A8/Spring%20Boot%E4%B9%8B%E5%AE%9A%E4%B9%89%E6%B3%A8%E8%A7%A3%E6%89%AB%E6%8F%8F%E5%99%A8.md) 45 | 46 | **SpringCloud系列笔记:** 47 | 48 | [Hystrix 快速入门](https://github.com/hanshuaikang/HanShu-Note/blob/master/SpringCloud%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/Hystrix/Hystrix%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90(%E4%B8%80)%EF%BC%9A%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8.md) 49 | 50 | [万字长文浅析微服务Ribbon负载均衡源码](https://github.com/hanshuaikang/HanShu-Note/blob/master/SpringCloud%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/Ribbon/%E4%B8%87%E5%AD%97%E9%95%BF%E6%96%87%E6%B5%85%E6%9E%90%E5%BE%AE%E6%9C%8D%E5%8A%A1Ribbon%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E6%BA%90%E7%A0%81.md) 51 | 52 | **JVM系列笔记**: 53 | 54 | [一个对象的死亡证明](https://github.com/hanshuaikang/HanShu-Note/blob/master/Jvm%E7%BB%88%E6%9E%81%E5%A5%A5%E4%B9%89%E7%B3%BB%E5%88%97/%E4%B8%80%E4%B8%AAJava%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%AD%BB%E4%BA%A1%E8%AF%81%E6%98%8E.md) 55 | 56 | [jvm内存回收终极奥义:垃圾收集算法](https://github.com/hanshuaikang/HanShu-Note/blob/master/Jvm%E7%BB%88%E6%9E%81%E5%A5%A5%E4%B9%89%E7%B3%BB%E5%88%97/jvm%E5%86%85%E5%AD%98%E5%9B%9E%E6%94%B6%E7%BB%88%E6%9E%81%E5%A5%A5%E4%B9%89%EF%BC%9A%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E7%AE%97%E6%B3%95.md) 57 | 58 | [Jvm内存回收终极奥义:垃圾回收器](https://github.com/hanshuaikang/HanShu-Note/blob/master/Jvm%E7%BB%88%E6%9E%81%E5%A5%A5%E4%B9%89%E7%B3%BB%E5%88%97/Jvm%E5%86%85%E5%AD%98%E5%9B%9E%E6%94%B6%E7%BB%88%E6%9E%81%E5%A5%A5%E4%B9%89%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8.md) 59 | 60 | **JAVA系列笔记:** 61 | 62 | [到底什么是应用上下文?](https://github.com/hanshuaikang/HanShu-Note/blob/master/java%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/%E5%88%B0%E5%BA%95%E4%BB%80%E4%B9%88%E6%98%AF%E5%BA%94%E7%94%A8%E4%B8%8A%E4%B8%8B%E6%96%87%EF%BC%9F.md) 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /Jvm终极奥义系列/Jvm内存回收终极奥义垃圾收集器.md: -------------------------------------------------------------------------------- 1 | # Jvm内存回收终极奥义:垃圾收集器 2 | 3 | ## 前言: 4 | 5 | 之前在[一个java对象的死亡证明](https://juejin.im/post/5d5a00b7f265da03bd051b59)我们讲了一个对象是如何被判定为死亡的,并在之后的 [Jvm内存回收终极奥义:垃圾收集算法](https://juejin.im/post/5d5ca0fbf265da03e921cdde)详细的介绍了目前较为流行的四种垃圾回收算法,后来各大虚拟机充分验证了这四种算法的可行性,所谓有需求就会有市场,本着**回收垃圾,掌握核心科技**的强大信念,垃圾公司应运而生了(你确定不是在骂人?),垃圾公司一口气推出了覆盖各大场景的七种垃圾回收器供广大消费者选择,不费吹灰之力占领了百分之90的市场份额,公司成立一天,火速上市,而竞争者们则被安排去了非洲,故事到这里,JVM宇宙正式形成了。 6 | 7 | 是的,一本正经的胡说八道。 8 | 9 | 这七种产品分别为: 10 | 11 | - 单线程垃圾收集小王子:**Serial收集器** 12 | - 多线程垃圾收集小王子:**ParNew 收集器** 13 | - 吞吐量优先小王子:**Parallel Scavenge收集器** 14 | - 单线程垃圾收集老王子 :**Serial Old收集器** 15 | - 多线程垃圾收集老王子:**ParNew Old 收集器** 16 | - 垃圾收集器明日之星:**CMS收集器** 17 | - 来自未来的次世代收集器:**G1收集器** 18 | 19 | 凭着在垃圾回收领域的优秀表现,无数次拯救了内存危机,它们被后人称为**垃圾回收者联盟** 20 | 21 | 22 | 23 | ## Serial收集器: 24 | 25 | 首先第一个上场的是Serial收集器,它是所有收集器里面年龄最大的收集器,在jdk1.3之前那物质贫瘠的时代,Serial收集器就像IE一样是唯一的选择,Serial的垃圾收集方式有点类似于我们平常的保洁阿姨,收集垃圾的时候,会先告诉在场的所有线程: 26 | 27 | **你,你,你,别动,刚扫完** 28 | 29 | Serial收集器工作的时候会暂停掉其他工作线程,直到它收集结束,这对很多应用是难以接受的,比如你再打一款游戏java荣耀,正准备团战呢,Serial收集器站出来说,弟弟们先往边上靠靠,我要来收集垃圾了,给你暂停个五分钟,谁受得了,下面是Serial收集器的运行过程,其中新生代使用的是**复制算法**,老生代使用**标记-整理算法** 30 | 31 |  32 | 33 | 读者看到这里肯定会说了,这垃圾收集器也太垃圾了吧,其实不然,Serial的专一(单线程)收集在只有一个内核的系统中因为不需要去和其他回收线程进行交互,反而效率更高一点。 34 | 35 | 36 | 37 | ## ParNew 收集器: 38 | 39 | ParNew 收集器呢,其实就是Serial收集器的多线程版本,其中用到的收集算法,Stop the word(停顿类型STW,暂停其他线程),对象分配规则,回收策略几乎都与Serial收集器一模一样,但青出于蓝而胜于蓝,ParNew在实行垃圾回收的时候是采用多线程的,当然,这并不足以使它立足于java虚拟机的理由,它最大的优势就是和CMS关系特好,几乎是拜把子的兄弟,俩人合作起来默契十足,而这些,Serial 和 Parallel Scavenge收集器就只能相形见绌了,可见,一个好的队友是多么重要。ParNew的工作过程如下图所示: 40 | 41 |  42 | 43 | 44 | 45 | 你这图看着有点斜啊,大家不要在意这些细节,逃 46 | 47 | 但是parNew收集器并不是说因为加了多线程就完爆Serial收集器了,当只有一个内核的时候,ParNew的效率就通常没有Serial收集器高,原因在于一个小房间一个打扫起来比十个人扫起来快,因为没有互相沟通的成本,但随着内核个数的增加,ParNew的优势才会逐渐体现出来。 48 | 49 | 50 | 51 | ## Parallel Scavenge收集器: 52 | 53 | 巧了,Parallel Scavenge是一个新生代收集器,和ParNew收集器在多线程,复制算法又几乎差不多,那他究竟在哪了,没错,是Idea,当别的垃圾收集器都在拼命减少用户线程停顿时间的时候,它另辟蹊径,选择了**吞吐量**优先。 54 | 55 | PS:吞吐量 = 用户运行代码时间/(用户运行代码时间+垃圾收集器时间) 56 | 57 | 吞吐量越高意味着用户代码运行的时间就越高,这里引用深入理解jvm虚拟机中的原文,感觉我怎么说也没作者说得好。 58 | 59 | > 停顿时间越短越适合需要与用户交互的程序,良好的相应速度能提升用户体验,而高吞吐量则可以高效的利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。 60 | 61 | 存在感有点低,没有图,,, 62 | 63 | 64 | 65 | ## Serial Old收集器: 66 | 67 | Serial Old收集器是Serial收集器的老年代版本,它同样是单线程的收集器,使用**标记-整理算法**,它主要工作在Client模式,两点呢,由于Parallel Scavenge收集器和Serial和cms收集器关系都不太好,所有它们两个成了好朋友,一个新生代,一个老年代,所以可以配合使用,第二个是作为CMS收集器的后备预案。它的工作图如下所示: 68 | 69 |  70 | 71 | ## ParNew Old 收集器: 72 | 73 | 同样的,ParNew Old收集器则是ParNew收集器的老年代版本了,使用**多线程**和**标记-整理算法**,由于上文我们说到,Parallel Scavenge收集器和CMS收集器尤其不合,所以如果一旦选择了Parallel Scavenge收集器作为新生代收集器的话,那也就意味着老年代收集器我们只能选Serial Old收集器,而Serial Old收集器属于单线程处理器,难免会有着性能上的问题,所以这就比较蛋疼了,简直就是捆绑销售,这一切直到ParNew Old 收集器的面世才得到解决,至此,Parallel Scavenge收集器+ParNew Old 收集器才算是真正的实现了吞吐量优先的至臻组合。 74 | 75 | 下图是ParNew Old 收集器运行示意图: 76 | 77 |  78 | 79 | ## CMS收集器: 80 | 81 | CMS收集器,全称Concurrent Mark Sweep 收集器,和大多数收集器类似,也是致力于缩短线程停顿时间的垃圾收集器。本身采用的是进阶版的**标记-清除算法**: 82 | 83 | 主要分为四个流程: 84 | 85 | 1. 初始标记 86 | 2. 并发标记 87 | 3. 重新标记 88 | 4. 并发清除 89 | 90 | 其中并发标记和并发清除这两个做真正工作的占据了整个垃圾回收过程的绝大多数时间。初始标记可以说是先来初略的看一眼,花不了多次时间,重新标记则是看看哪个被标记的改动了,也是很快就完成的。 91 | 92 | 执行流程如下: 93 | 94 |  95 | 96 | 虽然CMS收集器是今日之星收集器,但是也并非完全没有缺陷,其中有三个比较明显的问题。 97 | 98 | - 第一个就是CMS收集器使用的标记-清除算法会导致大量的空间碎片,起原因我已经在上篇文章垃圾收集算法有说明,不了解的朋友可以去看一下。 99 | - 第二个则是对CPU资源非常敏感:大家想,流程图那么多线程,当CPU多的时候还能接受,我家底厚,你吃得多也差不多能养起,可是当CPU数量比较少的时候,就相当于一个母亲养一堆嗷嗷待哺的孩子,就会很吃力,相应的也会导致我们应用性能的下降。 100 | - 第三个就是没办法回收“浮动垃圾”:这个浮动垃圾是什么意思呢,大家这么理解,你在家扫地呢,你弟弟在旁边嗑瓜子,当你扫到你弟弟那边的时候,你边扫它边往你扫干净的地方仍,但是,虚拟机规定你一次只能扫一次,没法回去,所以你得等下一次扫的时候在处理这些垃圾。所以,CMS无法回收哪些线程在回收的过程中因为运行而产生的新垃圾。 101 | 102 | ## G1收集器: 103 | 104 | G1收集器我称它为**次世代垃圾收集器**,一款来自未来的垃圾收集器,真实的讲呢,就是G1是当今垃圾收集器发展的最前沿的成果之一,但是,由于一些问题没有解决所以迟迟不能上市,直到JDK7才正式加入jvm虚拟机大家庭,JDK9才作为默认的垃圾回收器, 105 | 106 | G1的出现不是工程师们觉得垃圾回收器太少再随便加个,根据我们以往的历史经验,一款新技术的诞生必定是为了弥补旧技术的不足,G1同样是为了弥补CMS垃圾收集器的一些问题而诞生的,相对于CMS垃圾回收器,G1的主要提升主要在如下几个方面: 107 | 108 | - G1在压缩空间方面有优势 109 | - G1通过将内存空间分成区域(Region)的方式避免内存碎片问题 Eden, Survivor, Old区不再固定、在内存使用效率上来说更灵活 110 | - G1可以通过设置预期停顿时间(Pause Time)来控制垃圾收集时间避免应用雪崩现象 111 | - G1在回收内存后会马上同时做合并空闲内存的工作、而CMS默认是在STW(stop the world)的时候做 112 | - G1会在Young GC中使用、而CMS只能在O区使用 113 | 114 | 115 | 116 | 而相较于其他的垃圾收集器而言,G1主要有如下四个特点: 117 | 118 | - **并行与并发**:能更好的利用多CPU的优势。 119 | - **分代收集**:这个就厉害了,强大到没队友,可以管理整个GC堆,一个人carry全内存。 120 | - **空间整合**:薛定谔的垃圾回收算法,整体看是基于标记-整理算法实现的,局部看又像是基于复制算法实现的,但无论使用它们两个中的哪个,都几乎不会产生空间内存碎片。 121 | - 可预测的停顿:有点像AI,会建立可预测的停顿时间模型,可以有计划的避免在整个Java堆实现全区域的垃圾收集。 122 | 123 | 而G1是实现流程相对来说还是很复杂的,在这里我们就不多加叙述了,想要了解的同学可以去搜索相关资料。 124 | 125 | 和CMS相同,G1的垃圾回收过程也主要分为四个阶段: 126 | 127 | 1. 初始标记 128 | 2. 并发标记 129 | 3. 最终标记 130 | 4. 筛选回收 131 | 132 | 流程图如下: 133 | 134 |  135 | 136 | 137 | 138 | 139 | 140 | ## 总结: 141 | 142 | 本篇文章我们较为初略的说了说jvm七种垃圾收集器,各有所长,没有绝对的吊打,只有场景的选择。合适的场景用合适的垃圾收集器,才会使我们的应用性能得到改善。 143 | 144 | 我是韩数,关注我,有你好果子吃(哼) 145 | 146 | 更多文章请前去我的github,欢迎大家star: 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | https://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | import java.io.File; 21 | import java.io.FileInputStream; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.net.URL; 25 | import java.nio.channels.Channels; 26 | import java.nio.channels.ReadableByteChannel; 27 | import java.util.Properties; 28 | 29 | public class MavenWrapperDownloader { 30 | 31 | /** 32 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 33 | */ 34 | private static final String DEFAULT_DOWNLOAD_URL = 35 | "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; 36 | 37 | /** 38 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 39 | * use instead of the default one. 40 | */ 41 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 42 | ".mvn/wrapper/maven-wrapper.properties"; 43 | 44 | /** 45 | * Path where the maven-wrapper.jar will be saved to. 46 | */ 47 | private static final String MAVEN_WRAPPER_JAR_PATH = 48 | ".mvn/wrapper/maven-wrapper.jar"; 49 | 50 | /** 51 | * Name of the property which should be used to override the default download url for the wrapper. 52 | */ 53 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 54 | 55 | public static void main(String args[]) { 56 | System.out.println("- Downloader started"); 57 | File baseDirectory = new File(args[0]); 58 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 59 | 60 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 61 | // wrapperUrl parameter. 62 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 63 | String url = DEFAULT_DOWNLOAD_URL; 64 | if (mavenWrapperPropertyFile.exists()) { 65 | FileInputStream mavenWrapperPropertyFileInputStream = null; 66 | try { 67 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 68 | Properties mavenWrapperProperties = new Properties(); 69 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 70 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 71 | } catch (IOException e) { 72 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 73 | } finally { 74 | try { 75 | if (mavenWrapperPropertyFileInputStream != null) { 76 | mavenWrapperPropertyFileInputStream.close(); 77 | } 78 | } catch (IOException e) { 79 | // Ignore ... 80 | } 81 | } 82 | } 83 | System.out.println("- Downloading from: : " + url); 84 | 85 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 86 | if (!outputFile.getParentFile().exists()) { 87 | if (!outputFile.getParentFile().mkdirs()) { 88 | System.out.println( 89 | "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 90 | } 91 | } 92 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 93 | try { 94 | downloadFileFromURL(url, outputFile); 95 | System.out.println("Done"); 96 | System.exit(0); 97 | } catch (Throwable e) { 98 | System.out.println("- Error downloading"); 99 | e.printStackTrace(); 100 | System.exit(1); 101 | } 102 | } 103 | 104 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 105 | URL website = new URL(urlString); 106 | ReadableByteChannel rbc; 107 | rbc = Channels.newChannel(website.openStream()); 108 | FileOutputStream fos = new FileOutputStream(destination); 109 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 110 | fos.close(); 111 | rbc.close(); 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /废话设计模式系列/MVC设计模式/设计模式java语言实现之MVC设计模式.md: -------------------------------------------------------------------------------- 1 | # 设计模式java语言实现(七)之MVC设计模式 2 | 3 | ### 前言: 4 | 5 | 作者:韩数 6 | 7 | Github:https://github.com/hanshuaikang 8 | 9 | 本篇文章电子版和配套代码下载地址:https://github.com/hanshuaikang/design-pattern-java 10 | 11 | 时间:2019-07-31 12 | 13 | Jdk版本:1.8 14 | 15 | ### MVC设计模式定义: 16 | 17 | MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。 18 | 19 | 其中MV将传统的应用分为三层,分别为Model(模型),View(视图) Controller(控制器),其中: 20 | 21 | - **Model(模型)** - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。 22 | - **View(视图)** - 视图代表模型包含的数据的可视化,例如表单、网页等。 23 | - **Controller(控制器)** - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。 24 | 25 | ### 优缺点: 26 | 27 | **优点**:简化开发,提高代码复用,降低代码耦合,部署快,容易维护,可以做到在不修改任何后端逻辑的情况下修改前端视图。 28 | 29 | **缺点**:不适用于小项目,会增加项目的复杂性,会增加代码量。 30 | 31 | ### 应用场景: 32 | 33 | java 的SpringMVC struts 框架 python 的flask框架都对mvc设计模式提供了显著的支持。 34 | 35 | ### 微剧场: 36 | 37 | 为了庆祝韩数今天王者荣耀终于上了钻石,阿呆决定在小区里面做一个黑板报状告天下,由于阿呆小时候受过严格的训练,很快韩数上荣耀钻石的事情就路人皆知。每次韩数走在街上,路人无不投来羡慕的眼光。同样的,在作者韩数的巧妙安排下,阿呆画黑板报的水平在方圆五米名声大噪(不这样的话这篇文章写不下去了),于是就有很多铂金菜弟弟慕名而来,想请阿呆去他们小区画黑板报。刚开始的适合来找的人比较少,所以阿呆一个人做文案,设计,绘画倒还忙的过来,但随着需求量的增加,阿呆一个人已经忙不过来了,尽管每个黑板报的内容都大同小异,无非都是一些**"总有一天我也会像韩数大人那样成为荣耀钻石的!"**这些一听就很搞笑的豪言壮语。但是阿呆仍然都要每次自己重复整个流程。 38 | 39 | 每当夜深人静的时候阿呆就会趁着月光独自思考,怎么样才能提高做黑板报的效率呢?当然以阿呆的智商自己是想不出来的,这点光看前面的文章就可以猜到。于是阿呆找到了我这个世界上最聪明最善良最智慧的宝藏男孩寻求帮助,扭捏了很久我告诉他家传绝学,**MVC设计模式。** 40 | 41 | 传统的黑板报生产方式,都是一个人负责所有流程,这当然没有什么问题,可是大家想,随着韩数荣耀钻石名气的增加,越来越多的人都会来找阿呆做黑板报,这样的需求量单靠阿呆一个人必然是无法处理完的,所以往往呢,阿呆就会找来很多做黑板报的人去对接,因为每个人要负责做黑板报的整个流程,这样就会导致什么问题呢,如果只有20个黑板报制作者,但是却有100个人要做黑板报,即使全员出动,仍然是有80个人是需要等待的,传统的思路解决这样的问题的方案就是把黑板报制作者增加到100的人,看过我前面文章的小伙伴肯定已经明白,这么硬刚是不行的,为了解决这个问题呢,MVC设计模式应运而生了。 42 | 43 | 44 | 45 | /*** 46 | 47 | 之前写了一段mvc设计模式改造之后的黑板生产流程,写完发现不太理想,大家直接看代码吧。 48 | 49 | ***/ 50 | 51 | ### 代码实战: 52 | 53 | 首先 新建一个黑板类作为我们的Model层,负责数据的存储和处理。 54 | 55 | ```java 56 | //黑板报作为我们的的Model层 57 | public class BlackBoard { 58 | 59 | 60 | private String content;//黑板报的内容 61 | private String style;//黑板报的风格 62 | 63 | 64 | public String getContent() { 65 | return content; 66 | } 67 | public void setContent(String content) { 68 | this.content = content; 69 | } 70 | public String getStyle() { 71 | return style; 72 | } 73 | public void setStyle(String style) { 74 | this.style = style; 75 | } 76 | 77 | 78 | 79 | } 80 | 81 | ``` 82 | 83 | 然后新建一个Draw类,作为我们的view层,用于数据的显示。 84 | 85 | ```java 86 | public class Draw { 87 | public void show(BlackBoard b) { 88 | System.out.println("这是一个"+b.getStyle()+"的黑板报内容是:"+b.getContent()); 89 | } 90 | 91 | } 92 | ``` 93 | 94 | 新建一个Controller类,作为我们的控制层,用于控制数据的存储和显示 95 | 96 | ```java 97 | public class Controller { 98 | 99 | private BlackBoard blackBoard = new BlackBoard(); 100 | private Draw draw; 101 | 102 | 103 | public void setDraw(Draw draw) { 104 | this.draw = draw; 105 | } 106 | 107 | public void setContent(String content) { 108 | blackBoard.setContent(content); 109 | } 110 | 111 | public void setStyle(String style) { 112 | blackBoard.setStyle(style); 113 | } 114 | 115 | 116 | public void show() { 117 | draw.show(blackBoard); 118 | 119 | } 120 | 121 | 122 | } 123 | ``` 124 | 125 | **测试:** 126 | 127 | ```java 128 | //测试实例,前端给Controller层提交属性,Controller交给model层保存,然后再由view层打印出来。 129 | public class MVCTest { 130 | 131 | public static void main(String[] args) { 132 | 133 | Controller c = new Controller(); 134 | Draw d = new Draw(); 135 | c.setContent("葬爱家族"); 136 | c.setStyle("杀马特风格的"); 137 | c.setDraw(d); 138 | c.show(); 139 | 140 | //当需要改风格的时候 141 | c.setStyle("唯美风格的"); 142 | c.show(); 143 | 144 | 145 | } 146 | } 147 | ``` 148 | 149 | 150 | 151 | **输出**: 152 | 153 | ```text 154 | 这是一个杀马特风格的的黑板报内容是:葬爱家族 155 | 这是一个唯美风格的的黑板报内容是:葬爱家族 156 | ``` 157 | 158 | 159 | 160 | 同样的:**可以做到在不修改任何后端逻辑的情况下修改前端视图** 也就很容易实现了,我们可以新建一个view类替换 或者 直接修改原来的view代码就好,而不会对model层和controller层产生任何影响。从而大大降低了我们代码之间的耦合。 161 | 162 | ```java 163 | public class Draw { 164 | public void show(BlackBoard b) { 165 | System.out.println("这是一个新的黑板报,风格是"+b.getStyle()+"内容是:"+b.getContent()); 166 | } 167 | 168 | } 169 | ``` 170 | 171 | 172 | 173 | 很多同学看到这里依然没有办法去理解MVC设计模式相较于传统的方式具体提升在哪里,那么我们现在以传统的非MVC方式来实现这个黑板报例子: 174 | 175 | ```java 176 | public class tradition { 177 | 178 | private String content;//黑板报的内容 179 | private String style;//黑板报的风格 180 | 181 | 182 | public String getContent() { 183 | return content; 184 | } 185 | public void setContent(String content) { 186 | this.content = content; 187 | } 188 | public String getStyle() { 189 | return style; 190 | } 191 | public void setStyle(String style) { 192 | this.style = style; 193 | } 194 | 195 | private void show() { 196 | System.out.println("这是一个"+ style +"的黑板报内容是:"+content); 197 | 198 | } 199 | 200 | } 201 | ``` 202 | 203 | 204 | 205 | 这个时候很多同学纳闷了,心理卧槽,我怎么感觉传统的更好???! 206 | 207 | 所以对于小项目来说,使用mvc模式就只是存粹提高代码量了,并没有办法起到很好的作用,但是大家不妨这样想,如果把model view controller 所有的逻辑都封装到一个类中,当项目很大的时候,势必会造成单个代码文件十分臃肿,每次修改代码都要修改整个文件,带来维护上的难度,而且同样的,如果此刻来了一个新的类,也需要某些通用的操作,那么他实例化一个黑板类去调用它的保存数据对应的方法显然是不合适的。所以一般项目比较大的时候,我们通常会使用MVC设计模式,把一些数据库操作封装到model层中,作为我们的共用类,所有controller都可以调用,同样比如我们需要修改数据库存储的逻辑,也同样只需要单独修改model类对应的方法内部的代码就好了。做过java web开发的小伙伴们,SpringMVC中的service层,controller层,和我们前端的jsp/html,就是使用的MVC设计模式。 208 | 209 | 210 | 211 | 本期笔记没有总结,我们下篇文章再见! 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | -------------------------------------------------------------------------------- /废话设计模式系列/工厂模式/设计模式之工厂模式三连发(下).md: -------------------------------------------------------------------------------- 1 | # 设计模式二连发之工厂模式(下) 2 | 3 | ## 上文回顾: 4 | 5 | **简单工厂模式核心代码:** 6 | 7 | ```java 8 | public class CarFactory { 9 | 10 | public Car getCar(String name){ 11 | if("Porsche".endsWith(name)){ 12 | return new PorscheCar(); 13 | } 14 | else if("Bugatti".endsWith(name)){ 15 | return new BugattiCar(); 16 | } 17 | return new PorscheCar(); 18 | 19 | } 20 | 21 | } 22 | ``` 23 | 24 | 同时,和我们之前系列文章不止一次的强调的那样,如果把要维护对象的数量提升到一个很高的量级,即我们简单工厂模式需要维护成千上万的对象,那么我们程序肯定会爆炸的。 25 | 26 | 这么一想,的确是这样,当维护对象比较多的时候确实会比较麻烦,所以天才的科学家们呢肯定不会放任这个问题坐视不管的,于是简单工厂模式plus升级版本诞生了,不过我个人感觉工厂方法模式并没有解决最本质的问题,可以说是换汤不换药,披着工厂方法模式的单例模式。 27 | 28 | 我刚才说了什么?工厂方法模式?我想起来了,今天要写工厂方法模式。 29 | 30 | 合着你刚才说半天都不知道要写啥啊?! 31 | 32 | 走,走,让你来说相声来了? 33 | 34 | 对,我们下面来看工厂方法模式 35 | 36 | ## 工厂方法模式定义: 37 | 38 | 工厂方法模式是简单工厂的进一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。 39 | 40 | 这里就不设什么微剧场了,我相信天才的读者们看到代码就自然了解了。 41 | 42 | ### 应用场景: 43 | 44 | - 客户端不知道要创建出来的类是什么,但是知道对应的工厂是什么。 45 | - 客户端可以通过子类来指定创建对应的对象 46 | 47 | ### 代码实战: 48 | 49 | 我们在原来简单工厂模式实例的基础上,新增一个Factory接口类,用于规范我们工厂的行为。 50 | 51 | ```java 52 | public interface Factory { 53 | 54 | public Car getCar(); 55 | 56 | } 57 | 58 | ``` 59 | 60 | 然后编写他们的相关实现: 61 | 62 | ```java 63 | //布加迪 64 | public class BugattiFactory implements Factory { 65 | 66 | @Override 67 | public Car getCar() { 68 | // TODO Auto-generated method stub 69 | return new BugattiCar(); 70 | } 71 | 72 | } 73 | //保时捷 74 | public class PorscheFactory implements Factory{ 75 | 76 | @Override 77 | public Car getCar() { 78 | // TODO Auto-generated method stub 79 | return new PorscheCar(); 80 | } 81 | 82 | } 83 | 84 | 85 | ``` 86 | 87 | ### 测试: 88 | 89 | ```java 90 | public class Test { 91 | 92 | public static void main(String[] args) { 93 | 94 | Factory bugattiFactory = new BugattiFactory(); 95 | Car car = bugattiFactory.getCar(); 96 | car.description(); 97 | 98 | Factory porschFactory = new PorscheFactory(); 99 | car = porschFactory.getCar(); 100 | car.description(); 101 | } 102 | 103 | } 104 | 105 | ``` 106 | 107 | ### 输出: 108 | 109 | ```text 110 | 布加迪跑车 111 | 保时捷汽车 112 | ``` 113 | 114 | 115 | 116 | 这..这..这看起来有点像那个什么,那个什么来着,单例,单例模式? 117 | 118 | 的确是有点像的,确实在设计模式里面有很多模式之间是运作流程是有点类似的,这点在代理模式和装饰者模式之间的争论有所提及,感兴趣的朋友可以看一下我的那篇笔记。 119 | 120 | 当然,工厂模式这么看起来确实是有点尴尬了,原来一个工厂造所有车,但是型号多了维护不过来,现在是每一个型号都建了一个单独的工厂生产,这么一来型号多了工厂就会变得有点多,万一我同时买个十几种不同型号的车,那岂不是要跑去十多个工厂?太累了,不行,不行,难道就没有更好的方法了吗? 121 | 122 | 叮叮叮叮,当当当当 123 | 124 | **抽象工厂模式**!(请读者自行脑补哆啦A梦的语气)。 125 | 126 | ## 抽象工厂模式: 127 | 128 | **抽象工厂模式(**Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 129 | 130 | 在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。 131 | 132 | 工厂模式发展到抽象工厂模式,才真的有点设计模式家族的样子,抽象工厂设计模式在工厂模式上再加一层,做一个超级工厂,从超级工厂获取工厂,然后在从工厂中获取具体的对象。 133 | 134 | 头有点晕,不废话,码上看代码 135 | 136 | 广告,滚。 137 | 138 | 在看代码前我们先来说一下抽象工厂模式的应用场景: 139 | 140 | - 和工厂方法一样客户端不需要知道它所创建的对象的类 141 | - 需要一组对象共同完成某种功能时。并且可能存在多组对象完成不同功能的情况。 142 | - 代码结构稳定,不需要频繁修改和扩展产品功能。 143 | 144 | ### 应用实例: 145 | 146 | - 王者荣耀换皮肤,一次换一套 147 | - QQ换皮肤,一套一次换 148 | - 英雄联盟换皮肤,换一套一次 149 | 150 | 官方吐槽:除了换皮肤你就不能换点别的????!! 151 | 152 | 不能 153 | 154 | 155 | 156 | ### 代码实战 157 | 158 | 现在我们让客户的需求更加刁钻一些,客户现在需要不同的颜色的法拉利跑车。 159 | 160 | 首先声明一个Color接口类,用于规范我们颜色的行为。 161 | 162 | ```java 163 | 164 | public interface Color { 165 | void name(); 166 | } 167 | 168 | ``` 169 | 170 | 编写相关的实现 171 | 172 | ```java 173 | 174 | public class Red implements Color{ 175 | 176 | @Override 177 | public void name() { 178 | System.out.println("是红色的"); 179 | } 180 | 181 | } 182 | 183 | public class Blue implements Color{ 184 | 185 | @Override 186 | public void name() { 187 | System.out.println("是蓝色的"); 188 | 189 | } 190 | 191 | } 192 | 193 | 194 | ``` 195 | 196 | 然后创建我们的抽象工厂类: 197 | 198 | ```java 199 | public abstract class Factory { 200 | public abstract void getCar(); 201 | public abstract void getColor(); 202 | 203 | 204 | } 205 | ``` 206 | 207 | 编写相关实现: 208 | 209 | ```java 210 | //红色法拉利 211 | public class RedPorscheFactory extends Factory { 212 | 213 | @Override 214 | public Car getCar() { 215 | // TODO Auto-generated method stub 216 | return new PorscheCar(); 217 | } 218 | 219 | @Override 220 | public Color getColor() { 221 | // TODO Auto-generated method stub 222 | return new Red(); 223 | } 224 | 225 | 226 | } 227 | 228 | //蓝色布加迪 229 | public class BlueBugattiFactory extends Factory { 230 | 231 | @Override 232 | public Car getCar() { 233 | // TODO Auto-generated method stub 234 | return new BugattiCar(); 235 | } 236 | 237 | @Override 238 | public Color getColor() { 239 | // TODO Auto-generated method stub 240 | return new Blue(); 241 | } 242 | 243 | } 244 | 245 | 246 | 247 | ``` 248 | 249 | 最后为了体现超级工厂这个概念,我们新增一个超级工厂类,当然部分场景下是需要的,本例属于可有可无的范围。 250 | 251 | ```java 252 | public class BigFactory { 253 | 254 | public static Factory getFactory(String name){ 255 | if("bugatti".endsWith(name)){ 256 | return new BlueBugattiFactory(); 257 | }else if("porsche".endsWith(name)){ 258 | return new RedPorscheFactory(); 259 | } 260 | return null; 261 | 262 | } 263 | 264 | } 265 | 266 | ``` 267 | 268 | ### 测试: 269 | 270 | ```java 271 | public class Test { 272 | 273 | public static void main(String[] args) { 274 | Factory factory = BigFactory.getFactory("bugatti"); 275 | Car car = factory.getCar(); 276 | Color color = factory.getColor(); 277 | car.description(); 278 | color.name(); 279 | } 280 | 281 | } 282 | 283 | ``` 284 | 285 | ### 输出: 286 | 287 | ```java 288 | 布加迪跑车 289 | 是蓝色的 290 | ``` 291 | 292 | 由此可见,抽象工厂模式维护的是一套对象,那么什么时候会用到抽象工厂模式呢? 293 | 294 | 王者荣耀换皮肤的时候。 295 | 296 | 大家想,王者荣耀英雄换皮肤的时候,是不是一换一整套,这不就像当我们的产品需要向多个不同操作平台适配的时候,而编写相应的一套实现。比如面向liunx的叫linuxFactory,面向win的叫winFactory等等。 297 | 298 | 这个时候,有的读者可能会想到,是不是有点像模板设计模式,不尽然,虽然两者在职责上有重叠的部分,都是根据不同情况相应地提供不同实现,但是实现方式却不一样,工厂模式不关心是那个类具体实现了Car接口,也不关心是哪个实体类代表了法拉利,获取这个类的途径是通过工厂获取,使用者只需要知道对应的工厂是什么,而完全不需要care相关细节。 299 | 300 | 所以说呢,设计模式学到最后往往就可以把握到万千设计模式中那不变的一部分,因为工厂方法模式实在是没有什么可说的,所以笔记由原来计划的三篇缩减到了两篇,通过这两篇笔记呢,我知道这些笔记可能虽然没有什么人看,但是看的那几个人应该以及初步掌握了工厂模式的精髓了,并且能在之后的开发中能有所应用。 301 | 302 | 最后,我是韩数,欢迎关注我,我们下篇笔记再见。 303 | 304 | 305 | 306 | 307 | 308 | -------------------------------------------------------------------------------- /Vue系列学习笔记/vue 使用 vue-pdf 实现文件在线预览.md: -------------------------------------------------------------------------------- 1 | # vue 使用 vue-pdf 实现pdf在线预览 2 | 3 | ## 背景 4 | 之前的demo增加了图片预览,于是今天下午追完番剧就突然想到能不能把pdf在线预览也做了,说干就干,刚开始查了很多教程,我发现很多人都在说什么pdf.js这个库,这当然没什么问题,pdf.js的确可以非常完美的实现pdf在线预览的过程,但是感觉这样直接进去有点不太优雅,感觉硬拼凑进去的,应该还是尽量用VUE体系之内的东西,于是多方查阅资料,发现有vue-pdf这个组件,虽然说它没有原生那样强大,比如不支持pdf 复制,打印会乱码,但是我感觉已经足以满足我的需求了。本篇笔记循序渐进,从基础的demo,到一个可用的程度,文末列出了大家在实际使用的过程中可能会遇到的问题和解决方案。 5 | 6 | ## 安装: 7 | 这个没有啥背景知识可讲,我们直接跳到安装环节,vue-pdf 和其他vue组件的安装并无不同,打开命令行,敲入: 8 | ```bash 9 | npm install --save vue-pdf 10 | ``` 11 | 注意路径。别在桌面调出来个终端安装了,这种直接打回去重学Vue。 12 | 13 | ## vue-pdf 初体验: 14 | 安装完之后,使用vue-pdf非常简单,和其他的组件并没有什么不同,上代码: 15 | 16 | 首先我们需要引入这个组件: 17 | ```html 18 | 30 | ``` 31 | 然后在页面使用vue-pdf,只需要添加标签: 32 | ```html 33 | 34 | 35 | 38 | 39 | 40 | 41 | ``` 42 | 重启你的项目,访问这个界面,你大概率会发现pdf已经成功显示在你的界面上了。这没有任何问题,但是,正当你准备拿起一根烟,点上,伴着舒适的《美丽的梭罗河》,欣赏你成功的杰作的时候,你会发现,我**擦,为啥只有一页**,当玻璃杯碰在一起,满世界都是梦破碎的声音。 43 | 44 | 所以,这只是初体验,如果你的pdf只有一页,这样写当然没什么问题,但是当我们呢pdf 有很多页的时候,你会发现,这行不通了。所以,接下来,我们来看看怎么让它显示多页。 45 | 46 | ## vue-pdf 渐入佳境: 47 | 其实,想要显示多页也没那么复杂,你每次就显示一页,我,直接v-for 循环,直接显示完,简单粗暴。 48 | 49 | 页面代码: 50 | ```html 51 | 52 | 53 | 54 | 55 | 56 | 57 | 86 | 87 | ``` 88 | 各个属性: 89 | - url :pdf 文件的路径,可以是本地路径,也可以是在线路径。 90 | - numPages : pdf 文件总页数。 91 | 92 | getNumPages 计算总页数,顺便给url和numPages赋值。 93 | 94 | 唯一需要大家注意的是这句: 95 | ```js 96 | this.getNumPages("http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf") 97 | ``` 98 | 注意啊,这句不一定非要写到mounted里面,你想写哪就写哪,比如你前端请求后端,后端返回一个pdf 的url,在那里写就行,写在你需要的地方。 99 | 100 | ## vue-pdf 轻车熟路: 101 | 102 | 很多人看到这,就这,就这?万一,我pdf有一千页,我浏览器还不得裂开,我追求的是那种在微醺的下午,一页一页的翻看的感觉,你能给我吗? 103 | 104 | 我不能,才怪,保证满足你。 105 | 106 | ```html 107 | 108 | 109 | 110 | 上一页 111 | 下一页 112 | {{pageNum}}/{{pageTotalNum}} 113 | 顺时针 114 | 逆时针 115 | 116 | 125 | 126 | 127 | 128 | ``` 129 | 接下来,我们一一介绍这些都是个啥。 130 | 131 | 参数介绍: 132 | - `page `: 当前显示的页数,比如第一页page=1 133 | - `rotate` : 旋转角度,比如0就是不旋转,+90,-90 就是水平旋转。 134 | - `progress` :当前页面的加载进度,范围是0-1 ,等于1的时候代表当前页已经完全加载完成了。 135 | - `page-loaded` :页面加载成功的回调函数,不咋能用到。 136 | - `num-pages` :总页数 137 | - `error` :加载错误的回调 138 | - `link-clicked `:单机pdf内的链接会触发。 139 | 140 | 其他: 141 | - print 这个是打印函数。 142 | 注意:谷歌浏览器会出现乱码,这个和字体有关系。 143 | 144 | 来,js代码走一个: 145 | 146 | ```js 147 | 198 | ``` 199 | 其他骚操作: 200 | ```js 201 | // 打印全部 202 | pdfPrintAll() { 203 | this.$refs.pdf.print() 204 | }, 205 | // 打印指定部分 206 | pdfPrint() { 207 | this.$refs.pdf.print(100, [1, 2]) 208 | }, 209 | ``` 210 | 211 | 212 | 213 | 具体样式什么的我就不贴出来了,这些都不是重点,完全可以改成自己喜欢的。 214 | 215 | 成品展示: 216 |  217 | 218 | ## 其他问题以及解决方案: 219 | 220 | ### 跨域问题: 221 | 网上用pdf.js 很多都会遇到跨域问题,这个我今天实际应用到自己的项目里面了,我服务端设置了跨域,所以没有出现跨域的问题,如果出现跨域需要修改你后端的请求头。 222 | 223 | ### 打印界面字符乱码: 224 | 这个我倒是碰到了,谷歌浏览器打印的时候,预览界面真的变成了 真·方块字 ,全是方块。这个问题是因为你pdf中使用了自定义字体导致的,具体解决方案如下: 225 | 226 | 首先,找到这个文件:node_modules/vue-pdf/src/pdfjsWrapper.js 227 | 228 | 然后根据github上这个issue,其中红色的是要删掉的,绿色的是要加上去的,照着改就可以解决了。 229 | 230 | 地址: https://github.com/FranckFreiburger/vue-pdf/pull/130/commits/253f6186ff0676abf9277786087dda8d95dd8ea7 231 | 232 | 根据我的实际测试,是可以解决打印乱码的问题的。 233 | 234 | 非常感谢能读到这里的朋友,你们的支持和关注是我坚持高质量分享下去的动力。 235 | 236 | 相关代码和文档已经上传至本人github。一定要点个**star**啊啊啊啊啊啊啊 237 | 238 | **万水千山总是情,给个star行不行** 239 | 240 | [韩数的开发笔记](https://github.com/hanshuaikang/HanShu-Note) 241 | 242 | 欢迎点赞,关注我,**有你好果子吃**(滑稽) 243 | 244 | 245 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/mybatis多数据源配置/code/MybatisDemo/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/全局异常处理/code/Global_Exception/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /SpringBoot系列笔记/自定义starter/Springboot 之创建自定义starter.md: -------------------------------------------------------------------------------- 1 | ## Springboot 之创建自定义starter 2 | 3 | ## 前言: 4 | 5 | Springboot的出现极大的简化了开发人员的配置,而这之中的一大利器便是springboot的starter,starter是springboot的核心组成部分,springboot官方同时也为开发人员封装了各种各样方便好用的starter模块,例如: 6 | 7 | - spring-boot-starter-web//spring MVC相关 8 | - spring-boot-starter-aop //切面编程相关 9 | - spring-boot-starter-cache //缓存相关 10 | 11 | starter的出现极大的帮助开发者们从繁琐的框架配置中解放出来,从而更专注于业务代码,而springboot能做的不仅仅停留于此,当面对一些特殊的情况时,我们可以使用我们自定义的**springboot starter**。 12 | 13 | 在创建我们自定义的starter之前呢,我们先看看官方是怎么说的: 14 | 15 | - **模块** 16 | 17 | 在springboot官方文档中,特别提到,我们需要创建两个module ,其中一个是**autoconfigure module** 一个是 **starter module** ,其中 starter module 依赖 autoconfigure module 18 | 19 | 但是,网上仍然有很多blog在说这块的时候其实会发现他们其实只用了一个module,这当然并没有错,这点官方也有说明: 20 | 21 | ```text 22 | You may combine the auto-configuration code and the dependency management in a single module if you do not need to separate those two concerns 23 | 24 | //如果不需要将自动配置代码和依赖项管理分离开来,则可以将它们组合到一个模块中。 25 | ``` 26 | 27 | 28 | 29 | - **命名规范** 30 | 31 | springboot 官方建议springboot官方推出的starter 以spring-boot-starter-xxx的格式来命名,第三方开发者自定义的starter则以xxxx-spring-boot-starter的规则来命名,事实上,很多开发者在自定义starter的时候往往会忽略这个东西(因为不看官方文档很难知道这件事情。同时也不会造成其他的后果,主要是显得不够专业)。 32 | 33 | 34 | 35 | ## 看看官方的starter 36 | 37 | 了解了这两点之后,那么下面让我们一块去探索spingboot starter的奥秘吧。 38 | 39 | 按照springboot官方给的思路,starter的核心module应该是autoconfigure,所以我们直接去看spring-boot-autoconfigure里面的内容。 40 | 41 | 当Spring Boot启动时,它会在类路径中查找名为spring.factories的文件。该文件位于META-INF目录中。打开spring.factories文件,文件内容太多了,为了避免我水篇幅,我们只看其中的一部分: 42 | 43 | ```factories 44 | # Auto Configure 45 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 46 | org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ 47 | org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ 48 | org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ 49 | org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ 50 | org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ 51 | ``` 52 | 53 | 我们可以发现一些比较眼熟的单词,比如Aop,Rabbit,Cache ,当springboot启动的时候,将会尝试加载这些配置类,如果该路径下存在该类的话,则将运行它,并初始化与该配置类相关的bean。 54 | 55 | 点开一个看看: 56 | 57 | ```java 58 | @Configuration 59 | @ConditionalOnClass({RabbitTemplate.class, Channel.class}) 60 | @EnableConfigurationProperties({RabbitProperties.class}) 61 | @Import({RabbitAnnotationDrivenConfiguration.class}) 62 | public class RabbitAutoConfiguration { 63 | 64 | //...代码略.. 65 | } 66 | ``` 67 | 68 | 我们先来了解一下这几个注解: 69 | 70 | **@ConditionalOnClass** :条件注解,当classpath下发现该类的情况下进行自动配置。 71 | 72 | **@EnableConfigurationProperties**:外部化配置 73 | 74 | **@Import** :引入其他的配置类 75 | 76 | 77 | 78 | 当然,这并不是一种通用的套路,查看其他的配置类,我们会发现其标注的注解往往也是有所区别的。 79 | 80 | 81 | 82 | ## 自定义自己的starter 83 | 84 | 首先我们新建一个maven项目,引入以下依赖: 85 | 86 | ```xml 87 | 88 | 89 | org.springframework.boot 90 | spring-boot-autoconfigure 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-dependencies 99 | 2.1.0.RELEASE 100 | pom 101 | import 102 | 103 | 104 | 105 | ``` 106 | 107 | 然后我们创建一个person类,用作后期我们测试的bean 108 | 109 | ```java 110 | public class Person { 111 | 112 | //属性 113 | private int age; 114 | private String name; 115 | private String gender; 116 | 117 | /*此处省略getter and setter and toStering*/ 118 | 119 | } 120 | ``` 121 | 122 | 123 | 124 | 然后我们也创建一个PersonConfigProperties来完成我们属性的注入 125 | 126 | ```java 127 | 128 | @ConfigurationProperties(prefix = "mystarter.config.student") 129 | public class PersonConfigProperties { 130 | 131 | 132 | private String name; 133 | private int age; 134 | private String gender; 135 | 136 | /* 137 | 其他的配置信息。。。。 138 | */ 139 | 140 | /*此处省略getter and setter and toStering*/ 141 | } 142 | 143 | ``` 144 | 145 | 146 | 147 | 最后创建我们的自动配置类MyStarterAutoConfiguration.java 148 | 149 | ```java 150 | @Configuration 151 | @EnableConfigurationProperties(PersonConfigProperties.class) 152 | @ConditionalOnClass(Person.class) 153 | public class MyStarterAutoConfiguration { 154 | 155 | 156 | @Bean 157 | @ConditionalOnProperty(prefix = "mystarter.config", name = "enable", havingValue = "true") 158 | public Person defaultStudent(PersonConfigProperties personConfigProperties) { 159 | Person person = new Person(); 160 | person.setAge(personConfigProperties.getAge()); 161 | person.setName(personConfigProperties.getName()); 162 | person.setGender(personConfigProperties.getGender()); 163 | return person; 164 | } 165 | } 166 | ``` 167 | 168 | 169 | 170 | 我感觉这是不是做好了? 171 | 172 | **我不要你觉得,我要我觉得** 173 | 174 | 最后我们最重要的一步: 175 | 176 | 在resourecs文件目录下创建META-INF,并创建我们自己的spring.factories,并把我们的 MyStarterAutoConfiguration添加进去 177 | 178 | ```text 179 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 180 | com.jdkcb.mystarter.config.MyStarterAutoConfiguration 181 | ``` 182 | 183 | 最后打包成jar包,在我们新的项目里面测试: 184 | 185 | ## 测试: 186 | 187 | 引入我们的starter,当然也可以在本地直接引入我们的my-spring-boot-starter项目 188 | 189 | ```xml 190 | 191 | com.jdkcb 192 | my-spring-boot-starter 193 | 0.0.1-SNAPSHOT 194 | system 195 | ${project.basedir}/src/main/resources/lib/my-spring-boot-starter-0.0.1-SNAPSHOT.jar 196 | 197 | ``` 198 | 199 | 在application.properties配置文件中添加我们相应的配置 200 | 201 | ```properties 202 | mystarter.config.enable=true 203 | mystarter.config.person.name=小明 204 | mystarter.config.person.age=5 205 | mystarter.config.person.gender=男 206 | ``` 207 | 208 | 209 | 210 | 新建一个测试的Controller: 211 | 212 | ```java 213 | @RestController 214 | public class TestController { 215 | 216 | @Autowired 217 | private Person person; 218 | 219 | @RequestMapping("/getPerson") 220 | private Person getStudent() { 221 | return person; 222 | } 223 | 224 | } 225 | 226 | ``` 227 | 228 | 启动项目,在浏览器地址栏输入 http://127.0.0.1:8080/getPerson ,结果如下 229 | 230 | ```json 231 | {"age":5,"name":"小明","gender":"男"} 232 | ``` 233 | 234 | 大功告成~ 235 | 236 | 最后的最后,大家好,我是韩数,哼,关注我,有你好果子吃(叉腰)。 237 | 238 | 记得点个赞再走哦~ -------------------------------------------------------------------------------- /Docker初级入门教程/写给后端的Docker初级入门教程:DockerFile 命令详解.md: -------------------------------------------------------------------------------- 1 | # 面向后端的Docker初级入门教程:DockerFile 命令详解 2 | 3 | 在上一篇文章[面向后端的Docker初级入门教程:实战篇](https://juejin.im/post/5dabb85ff265da5b6e0a48e7)最后我们有提到用DockerFile来构建和定制属于我们自己的镜像,因为时间和篇幅问题,上一篇文章对DockerFile只做了一个简单的介绍和使用,并没有对DockerFile具体的指令进行详细的介绍和解释,本篇,作为上一篇实战篇的额外补充篇,我们将从DockerFile基础的命令入手,一步一步的去构建一个属于我们自己的镜像出来。 4 | 5 | ## DockerFile介绍: 6 | 7 | Dockerfile是由一系列命令和参数构成的脚本,一个Dockerfile里面包含了构建整个image的完整命令。Docker通过docker build执行Dockerfile中的一系列命令自动构建image。 8 | 9 | ## 实例: 10 | 11 | 这里我们仍然选择我们上一篇使用的在centos基础上定制我们自己的镜像为本章的代码实例,代码如下: 12 | 13 | ```shell 14 | FROM centos //继承至centos 15 | ENV mypath /tmp //设置环境变量 16 | WORKDIR $mypath //指定工作目录 17 | 18 | RUN yum -y install vim //执行yum命令安装vim 19 | RUN yum -y install net-tools //执行yum命令安装net-tools 20 | 21 | EXPOSE 80 //对外默认暴露的端口是80 22 | CMD /bin/bash //CMD 容器启动命令,在运行容器的时候会自动执行这行命令,比如当我们 docker run -it centos 的时候,就会直接进入bash 23 | ``` 24 | 25 | 之后再通过docker build 命令编译该DockerFile便可以得到一个属于自己的镜像了。 26 | 27 | ```shell 28 | 然后编译该镜像 29 | docker build -f ./DockerFile -t mycentos:1.3. 30 | -t 新镜像名字:版本 31 | -f 文件 -d 文件夹 32 | ``` 33 | 34 | 运行该镜像会发现vim和net-tools在我们新的容器中已经可以正常使用了。 35 | 36 | 接下来呢,我们将从FROM命令开始逐行介绍,最终完成对DockerFile常用命令的了解和掌握。 37 | 38 | ## 常用命令: 39 | 40 | ### FROM命令: 41 | 42 | 既然我们是在原有的centos镜像的基础上做定制,那么我们的新镜像也一定是需要以centos这个镜像为基础的,而FROM命令则代表了这个意思,在DockerFile中,基础镜像是必须指定的,FROM指令的作用就是指定基础镜像,因此一个DockerFile中,FROM是必备的指令,而且就像java,python的import关键字一样,在DockerFile中,**FROM指令必须放在第一条指令的位置** 43 | 44 | 当然,这个时候可能有朋友会问了,我要是不想在其他的镜像上定制镜像怎么办呢,没问题啊,Docker 提供了scratch 这个虚拟镜像,如果你选择 FROM scratch 的话,则意味着你不以任何镜像为基础,接下来所写的指令将作为镜像的第一层开始存在,当然,在某些情况下,比如linux下静态编译的程序,运行的时候不需要操作系统提供运行时的支持,这个时候FROM scratch 是没有问题的,反而会大幅降低我们的镜像体积。 45 | 46 | ### ENV指令 47 | 48 | 功能:**设置环境变量** 49 | 50 | 同样的,DockerFile也提供了两种格式: 51 | 52 | - ENV key value 53 | - ENV key1=value1 key2=value2 54 | 55 | 这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如 RUN, 还是运行时的应用,都可以直接使用这里定义的环境变量。 56 | 57 | 可以看到我们示例中使用ENV设置mypath变量之后,在下一行WORKDIR则使用到了mypath这个变量 58 | 59 | ```shell 60 | ENV mypath /tmp //设置环境变量 61 | WORKDIR $mypath //指定工作目录 62 | ``` 63 | 64 | ### WORKDIR 指令: 65 | 66 | 功能,**指定工作目录** 67 | 68 | 格式为:WORKDIR 工作目录路径,如果这个目录不存在的话,WORKDIR则会帮助我们创建这个目录。 69 | 70 | 设置过工作目录之后,当我们启动容器,会直接进入该工作目录 71 | 72 | ```shell 73 | [root@8081304919c9 tmp]# 74 | ``` 75 | 76 | ### RUN命令: 77 | 78 | **RUN 指令是用来执行命令行命令的**。由于命令行的强大能力,RUN 指令也是在定制镜像时是较为常用的指令之一。 79 | 80 | RUN命令的格式一共有两种,分别是: 81 | 82 | - Shell 格式 83 | 84 | RUN 命令,就像直接在命令行中输入命令一样,比如RUN yum -y install vim就是使用的这种格式 85 | 86 | - exec 格式 87 | 88 | RUN["可执行文件","参数1","参数2"],感觉就像调用函数一样 89 | 90 | 就像我们在上一篇文章中说过的那样,DockerFile中每一条指令都会建立一层,比如我们上面执行过下面这条命令 91 | 92 | ```shell 93 | RUN yum -y install vim 94 | ``` 95 | 96 | 执行结束之后,则调用commit提交这一层的修改,使之构成一个新的镜像,怎么样,是不是豁然开朗了呢。 97 | 98 | 并没有 99 | 100 | 那好吧 101 | 102 | 同样的,Dockerfile 支持 Shell 类的行尾添加 \ 的命令换行方式,以 及行首 # 进行注释的格式。良好的格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。 103 | 104 | > 提示: 105 | > 106 | > 如果使用apt方式安装的话,最后不要忘记清理掉额外产生的apt缓存文件,如果不清理的话会让我们的镜像显得非常臃肿。因为DockerFile生成一层新的镜像的时候,并不会删除上一层镜像所残留的文件。 107 | 108 | ### EXPOSE指令: 109 | 110 | 功能:**声明端口** 111 | 112 | 格式: EXPOSE 端口1 端口2 113 | 114 | EXPOSE 指令是声明运行时容器提供服务端口,这当然只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。这样声明主要是为了方便后期我们配置端口映射。 115 | 116 | ### CMD指令: 117 | 118 | 之前介绍容器的时候曾经说过,Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器主进程的启动命令的。 119 | 120 | 同样的,DockerFile也为我们提供了两种格式来使用CMD命令: 121 | 122 | - shell 格式:CMD 命令 123 | - exec 格式:CMD ["可执行文件", "参数 1", "参数 2"...] 124 | 125 | 示例中,我们使用的是第一种: 126 | 127 | ```shell 128 | CMD /bin/bash 129 | ``` 130 | 131 | 这条指令带来的效果就是,**当我们通过run -it 启动命令的时候,容器会自动执行/bin/bash,centos默认也是CMD /bin/bash,所以当我们运行centos镜像的时候,会自动进入bash环境里面。** 132 | 133 | 当然,我们也可以通过运行时指定命令的方式来体换默认的命令,比如: 134 | 135 | ```shell 136 | docker run -it centos cat /etc/os-release 137 | ``` 138 | 139 | 这样当我们运行镜像的时候,cat /etc/os-release就会替代默认的CMD /bin/bash 输出系统的版本信息了。 140 | 141 | 如果使用 shell 格式的话, 实际的命令会被包装为 sh -c 的参数的形式进行执行。 142 | 143 | 比如: 144 | 145 | ```shell 146 | CMD echo $HOME 147 | ``` 148 | 149 | 在实际执行中,会将其变更为 150 | 151 | ```shell 152 | CMD [ "sh", "-c", "echo $HOME" ] 153 | ``` 154 | 155 | 当然还有很多初学者特别容易犯的问题,就是去启动后台服务,比如: 156 | 157 | ```shell 158 | CMD service nginx start 159 | ``` 160 | 161 | 这样子去用,会发现容器运行了一会就自动退出了。 162 | 163 | 所以,????? 164 | 165 | 我们之前不止一次的提醒过,**容器不是虚拟机,容器就是进程**,容器内的应用都应该以前台运行,而不是像虚拟机,物理机那样去运行后台服务,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。 166 | 167 | 怎么理解呢?想想偶像剧,容器是女主角,主进程是男主角 168 | 169 | 你走了,我也不活了(撕心裂肺大哭),大概就是这么个意思。 170 | 171 | 正如我们前面所提出的,实际上CMD service nginx start 最终会被理解为: 172 | 173 | ```shell 174 | CMD [ "sh", "-c", "service nginx start"] 175 | ``` 176 | 177 | 在这里,我们主进程实际就是sh,当我们service nginx start执行完毕之后,那么sh自然就会退出了,主进程退出,容器自然就会相应的停止。争取的做法是直接执行nginx可执行文件,并且声明以前台的形式运行: 178 | 179 | ```shell 180 | CMD ["nginx", "-g", "daemon off;"] 181 | ``` 182 | 183 | 到这里,我们示例中所涉及到的命令已经讲完了,当然,这并不够,Docker中仍然有很多命令是我们使用比较频繁的,下面我们的部分作为补充,讲一下其他常用的DockerFile命令。 184 | 185 | ### **COPY** 命令: 186 | 187 | 功能:**复制文件** 188 | 189 | Docker依旧提供了两种格式供我们选择: 190 | 191 | - COPY [--chown=:] <源路径>... <目标路径> 192 | - COPY [--chown=:] ["<源路径 1>",... "<目标路径>"] 193 | 194 | 到这里大家其实会发现,Docker提供的两种格式其实都是差不多的用法,一种类似于命令行,一种则类似于函数调用。 195 | 196 | 第一种例如(将package.json拷贝到/usr/src/app/目录下): 197 | 198 | ```shell 199 | COPY package.json /usr/src/app/ 200 | ``` 201 | 202 | 其次,目标路径 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径 ,工作目录可以用 WORKDIR 指令来指定,如果需要改变文件所属的用户或者用户组,可以加上--chown 选项。 203 | 204 | > 需要注意的是,使用 COPY 指 令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这 个特性对于镜像定制很有用。 205 | 206 | ### ADD命令: 207 | 208 | ADD命令可以理解为COPY命令的高级版,格式和用法与COPY几乎一致,ADD在COPY的基础上增加了一些功能,比如源路径可以是一个URL链接,当你这么用的时候,Docker会尝试着先将该URL代表的文件下载下来,然后复制到目标目录上去,其他的则是在COPY的基础上增加了解压缩之类的操作,码字码的手疼,需要了解的朋友可以去官网查看相关的文档,这里我就不延申了。 209 | 210 | ### VOLUME 定义匿名卷: 211 | 212 | 在上一篇中,我们有讲容器卷这个概念,为了防止运行时用户忘记 将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些 目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运 行,不会向容器存储层写入大量数据。 213 | 214 | 例如: 215 | 216 | ```shell 217 | VOLUME /data 218 | ``` 219 | 220 | 运行时通过-v参数即可以覆盖默认的匿名卷设置。 221 | 222 | ### USER 命令: 223 | 224 | 功能:**指定当前用户** 225 | 226 | 格式:**USER 用户名:用户组** 227 | 228 | USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份。当然,和 WORKDIR 一样,USER 只是帮助你切换到指定用户 而已,这个用户必须是事先建立好的,否则无法切换。 229 | 230 | 当然这个大前提是,你的User用户是事先存在好的。 231 | 232 | ## 完结撒花? 233 | 234 | 不知不觉间,Docker系列初级入门教程已经发到了第四篇,篇幅也到了一万多字,前三篇文章加起来在掘金上慢慢有了大概1500左右的阅读量,我知道这点对于很多掘金大佬来说只是微不足道的一点,但对于现阶段的我来说已经非常满足了,从来没有想到过有一天自己也可以通过分享去帮助到别人,正如我之前通过别人的技术博客学习那样。 235 | 236 | 这个系列完结了吗?我想初级篇应该是完结了,但是Nginx的初级入门教程,即将到来的Mysql,Netty等等并没有,由于目前尚未毕业,还没有接受过工作的毒打,所以只能尽自己的能力去写一些基础的入门教程,所以完结了吗?并没有,技术之路永无止境,只要我们一直在坚持学习,我想,我们可以一直继续下去。 237 | 238 | 感谢掘金,大家好,我是韩数,我们下期文章再见! 239 | 240 | 最后,相关笔记已经同步开源至Github(**欢迎star**): 241 | https://github.com/hanshuaikang/HanShu-Note 242 | 243 | 一定要记得给个star哦。 244 | 245 | -------------------------------------------------------------------------------- /Nginx初级入门教程/写给后端的Nginx初级入门教程Nginx原理初探.md: -------------------------------------------------------------------------------- 1 | # 写给后端的Nginx初级入门教程:Nginx原理初探 2 | 3 | 在上一篇文章[写给后端的Nginx初级入门教程:配置高可用集群](https://juejin.im/post/5dbcf063e51d456f00066264) 中,我们使用keepalived实现了我们Nginx服务器的高可用,防止因为Nginx服务器挂掉导致整个应用挂掉的情况的发生。而Nginx作为当下最受欢迎的web服务器软件之一,能做到如今的地位也并不是没有原因,优秀的性能表现,可伸缩性,可修改性的设计,同时可移植跨平台的运行以及非常低的故障率,那Nginx整体架构又是如何设计的呢?本篇文章呢,由于要了解Nginx核心技术需要较深的技术内力,我没有(大哭),所以本次我们将轻轻的揭开Nginx神秘面纱的一角,去探知一下Nginx内部是如何运作与设计的。 4 | 5 | ## Nginx的特性 6 | 7 | Nginx能做到现在,并且被如此广泛的企业所采用,一定是有很多技能点点满了的,经过查阅相关资料,前辈们一共总结出来六点Nginx服务器相对于其他类型的web服务器软件做的更加优秀的地方,它们分别是: 8 | 9 | - 性能 10 | - 可伸缩性 11 | - 简单性 12 | - 可修改性 13 | - 可见性 14 | - 可移植性 15 | 16 | ### 性能: 17 | 18 | 性能我想不必多说,这是Nginx能混到现在的核心资本,即使Nginx在其他方面做的很优秀,如果性能比不上其他的web服务器,在这个大家比较看重性能的时代,Nginx有较大概率会受到冷遇。而Nginx和传统的程序不一样的是,其他程序比如游戏可能会需要计算性能,图形渲染,网络渲染性能,而Nginx作为一款web服务器,就只能在网络领域和别人一决雌雄了。 19 | 20 | 而Nginx在网络性能这块做了大量的工作,包括使用事件驱动架构配合请求的多阶段异步处理,以及Master-workers机制的使用,都保证了在高并发场景下,Nginx的出色性能表现。 21 | 22 | ### 可伸缩性: 23 | 24 | 可伸缩性也可以理解为可扩展性,可以理解为比如谷歌浏览器的插件,火狐浏览器的插件(没想到什么合适的例子),Nginx支持添加相关的模块来增强我们的服务,同时优秀的模块化设计允许我们定制或者采用第三方开发的模块来提升我们Nginx的服务。 25 | 26 | ### 简单性: 27 | 28 | 简单性通常指的是组件的简单程度,每个组件越简单,就会越容易理解和实现,也更容易被验证。当然开发Nginx组件不能随心所欲,同时要遵循Nginx模块开发统一的规范,而模块接口非常简单,具有很高的灵活性。 29 | 30 | ### 可修改性: 31 | 32 | Nginx基于BSD开源,这意味当Nginx某些功能不能满足我们其他的额外需求时,我们可以修改它的代码来达到我们的业务要求。同时Nginx也支持在我们不重启,停止服务的前提下,修改我们web服务器的某些配置。 33 | 34 | ### 可见性: 35 | 36 | 可见性呢,就是我们整个应用对使用者的透明程度,开放程度。Nginx 有 **http_stub_status_module** 来实现基础的可见性,可以让我们了解到Nginx 当前一共建立了多少个链接,处理了多少个请求等等,这些监控参数可以让运维人员更好的了解Nginx服务运行的状况,并及时的做出调整。比如当 Reading + Writing 数值比较高的时候,就意味着我们当前的应用并发量还是比较大的。 37 | 38 | ### 可移植性: 39 | 40 | 由于Nginx是基于C语言开发的,这意味着Nginx可以在多个平台上运行,同时Nginx重新封装了日志,各种数据结构等工具软件,而且核心代码皆采用与操作系统无关代码的实现方式,而涉及到与操作系统的交互,Nginx则为不同操作系统提供了各自独立的实现,这点其实和java虚拟机有着异曲同工之妙。 41 | 42 | 处理的说完了这些,Nginx又是如何实现这些骚操作的呢?接下来我们**浅入Nginx内部,从模块设计,事件驱动,请求处理,进程管理**四个方面来简单的了解Nginx内部是如何设计得如此高效的。 43 | 44 | ## 优秀的模块化设计: 45 | 46 | Nginx和java一样,java呢是除了少数基本类型之外,其他一切皆为对象,Nginx也是如此,除了少部分核心代码之外,其他的皆为模块,Nginx模块遵循着同样的设计规范(ngx_module-t),设计规范中只要求了最核心的几个实现,初始化,退出,以及配置,这样做的好处和java接口类一样,**在给了模块设计者充分自由的同时,又有效的避免了模块设计者乱来。** 47 | 48 | 同样的,规范(ngx_module-t)中允许我们自定义服务类型,比如在之前的[实战篇](https://juejin.im/post/5db8f8c3f265da4d3e173c62) 配置详解那部分,我们就主要说了Nginx的 **全局块,events块,http块**。而这些就属于我们Nginx的模块类型。比如http模块就只负责相关的http请求的处理,而关于事件的处理则交给events模块处理。 49 | 50 | 同时Nginx也引入了核心模块的概念,目前Nginx一共有六个核心模块,用来处理我们常见的日志(ngx_errlog_module),事件(ngx_events_module),安全(ngx_openssl_module),网络(ngx_http_module,邮件(ngx_mail_module,核心代码(ngx_mail_module)这样做有什么好处呢,这意味Nginx非模块的代码,比如Nginx的核心代码,只关注怎么调用这六个模块进行相应的处理就可以了,完全不需要管它们是怎么实现的。同样的,Nginx框架不会约束核心模块的接口和功能。这种简洁,灵活的设计为Nginx实现动态可扩展性,动态可配置性。动态可定制性带来了极大的便利。这段话怎么理解呢,这样理解: 51 | 52 | **不管黑猫白猫,能抓住耗子的就是好猫。**Nginx核心模块不管你是怎么实现的,只要实现就行。所以核心模块的实现才可以充足的发挥。当然,这一切也是需要遵守相关的规范的,但是规范只是极少的一部分。 53 | 54 | ## 事件驱动架构: 55 | 56 | 在了解Nginx的事件驱动架构前,我们先看一下传统的web服务器是如何工作的,接下来进入小剧场: 57 | 58 | 报,报tomcat大王,一个请求过来了! 59 | 60 | 这样啊,你派一个线程跟着,防止它有什么小动作,记住,等请求结束离开之后,再让那个线程回来。 61 | 62 | 在传统的web服务器中,一个请求往往会分配一个独立的线程去处理,直到该线程结束,这当然没有什么问题,可是如果该请求请求到一半又想去读一下文件,这个时候就会造成IO阻塞,我们线程就只能在那干等着它处理完,而请求来到请求走的这个过程,线程都始终占用着系统资源,直到请求结束线程被销毁才会释放资源,当然,如果请求刷的一下就处理完了这没有什么问题,但是如果请求一下子处理了几分钟,几十分钟,这谁顶得住,并发量稍微高一点线程数就达到最大值了。 63 | 64 | 当然,以上只是举例,tomcat在7之后就支持NIO异步IO处理了,tomcat8在linux环境中已经默认开启NIO模式了。 65 | 66 | 而Nginx不一样在哪呢,传统的web服务器往往是事件消费者独自占用一个进程资源,而Nginx的事件消费者只是被事件分发者短期调用而已。比如在传统的web服务器来了,当TCP建立链接的时候发生一个事件,然后链接之后较给一个进程去处理消费,这其中比如读写操作什么的都是这一个进程始终如一的去完成的。而Nginx不一样在哪里呢: 67 | 68 | 比如当tcp连接事件来的时候,会首先被我们事件收集者,分发者收到,然后事件分发者将这个事件交给,记住,交给仅仅处理tcp链接的消费者去处理,而tcp读事件和tcp连接消费者一点关系都没有,当读事件来的时候,就分发给只负责读事件的事件消费者,而每个事件消费者的处理都是刷的一下非常快的就处理完了,所有的事件消费者只是事件分发者进程的短期调用而已,**这种设计使得网络性能,用户感知和请求时延都得到了提升,每个用户的请求都会得到及时的响应,整个服务器的网络吞吐量都会由于事件的及时响应而增大。** 69 | 70 | 如果200个请求到达传统的web服务器,将会分配两百个线程去处理,如果传统的web服务器最大只要两百个线程的话,后面的用户就只有等待前面的请求完成,而Nginx则是两百个请求发起链接,连接事件消费者只把连接事件处理了,这样第201个请求来的时候,由于tcp连接事件消费者已经处理完了,所以第201个请求也可以瞬间得到连接成功的响应。 71 | 72 | 太牛X了。 73 | 74 | 当然,这样也有弊端,就是我们的事件消费者进程不能阻塞和休眠,比如请求来了,你负责连接的事件消费者阻塞主了,那我的事件分发者就得一直等你处理完,或者负责连接的事件消费者因为太闲进程睡着了,每次调用相应事件消费者的时候还得先把它唤醒,这都是不能忍得。所以Nginx的整体实现难度要比传统的web服务器高很多。 75 | 76 | Nginx事件处理大致图如下(画的有点丑): 77 | 78 |  79 | 80 | 81 | 82 | ## 请求的多阶段异步处理: 83 | 84 | 既然说了多阶段,在Nginx能够把单个请求分割成多个阶段的也只有事件驱动机制了,所以请求的多阶段异步处理实际上就是基于Nginx本身的事件驱动架构实现的。 85 | 86 | 比如获取静态文件的HTTP请求就可以划分为以下七个阶段: 87 | 88 | | 阶段 | 触发事件 | 89 | | :------------------------------------------------------- | ------------------------------------------------------------ | 90 | | 建立tcp连接 | 接收到tcp中的SYN包 | 91 | | 开始接收用户请求 | 接收到TCP中的ACK包表示连接建立成功 | 92 | | 接收到用户请求并分析已经接收到的请求是否完整 | 接收到用户的数据包 | 93 | | 接收到完整的用户请求后开始处理用户请求 | 接受到用户的数据包 | 94 | | 由目标静态文件中读取部分内容,并直接发送给用户 | 接收到用户的数据包,或者接收到TCP中的ACK包表示用户已经接收到上次发送的数据包,TCP滑动窗口向前滑动。 | 95 | | 对于非keep-alive请求,再发送完静态文件之后主动关闭连接。 | 接收到TCP中的ACK包表示用户已经收到之前发送的所有数据包。 | 96 | | 由于用户关闭连接而结束请求 | 接收到TCP中的FIN包。 | 97 | 98 | 当然,对于很多计算机网络基础较差的同学不是特别明白也没有关系,我们这篇文章并不是去分析Nginx在做到这些是如何具体去实现的,而是去宏观的了解Nginx具体用了一种什么样的思路去设计的。 99 | 100 | 大家这样去理解,每个响应的事件都会有对应的专门的事件消费者去处理,由于是单一的任务,这对于每一个事件消费者来说都是相对容易的且处理迅速的,负责连接的事件消费者处理过之后可以马上投入到下一个连接事件的处理中,这样可以使得我们每个进程都一直在马不停蹄的全速工作,在高并发的情况下就很少有进程休眠这种情况的发送,因为在高并发的场景下,每个进程要处理的事件是非常多,哪有功夫去睡觉。而传统的web服务器,一旦出现进程休眠,对于用户的感知就是请求的响应变慢了,而在高并发的场景下,由于一个请求对应一个进程,这个时候,如果进程不够了,系统就会去创建更多的进程,进程间的切换都会占用相当多的操作系统的资源,从而导致我们网络性能的下降。 101 | 102 | 而对于这点,Nginx是这样处理的,比如在使用send调用发送数据给用户时,如果使用阻塞socket句柄,当send在向操作系统内核发出数据包之后就必须把当前进程休眠,直到数据成功发送之后才能醒来。而Nginx是这样处理的,把send这个过程分成两个阶段 103 | 104 | 1. 向操作系统内核发出数据包,不等待结果 105 | 2. send结果返回。 106 | 107 | 也就是你发吧,我先干别的事儿,发完了通过事件告诉我,我再来处理数据包的事儿。 108 | 109 | 而在大文件中,也可以把阻塞的方法分解成多个阶段的方法调用,比如在没有开启异步IO的情况下,把1000M 的文件处理成1000份,每份1M,处理完这1M,马上处理其他的事情,然后再回来接着依次处理剩下的999M,这样的好处是,每次处理1m,我们都有一定的间隔去处理其他事务,而不是一下子处理1000M,干等着。 110 | 111 | 如果实在没有办法把阻塞的操作拆分成多个阶段处理,Nginx便会派一个新的进程去单独处理这个阻塞方法,完成之后再发送完成事件通知。这样虽然方法是阻塞的,但是由于是额外的进程再处理,从而不影响其他的请求处理。 112 | 113 | ## 管理进程+多工作进程的设计: 114 | 115 | Nginx采用master - worker 机制,这样对于每个worker进程来说,由于是独立的进程,所以也避免了锁带来的额外开销,如果有多个CPU的情况下,多个worker进程占用不同的CPU核心来工作,提高了网络性能,降低了请求的平均时延,毕竟再怎么说,十个进程也要比一个进程处理起来快一点。 116 | 117 | 而我们master进程并不针对请求做处理,主要是用来管理和监控我们其他的worker进程,所以master并不会占用特别多的资源,同时还能做到worker之间的负载均衡,比如请求来的时候,优先分配给压力较小的worker进程去处理。同样的,比如我们单个worker进程挂了,由于进程之间是独立的,所以并不会影响到其他worker进程的处理。提高了整个系统的可靠性,降低了由于单个进程挂掉导致整个应用挂掉的风险。 118 | 119 | 如图所示: 120 | 121 |  122 | 123 | ## 下面开始技术总结: 124 | 125 | 今天呢,作为写给后端的Nginx初级入门教程最后一篇,原理篇呢,我们通过对Nginx架构的设计的简单探索非常简单的了解了Nginx内部是如何设计和工作的,总的来说,本篇文章内容较为浅显,对代码层面上的分析几乎没有,第一呢,考虑到这是一篇初级入门教程,所以并没有在代码设计上做很深的分析,更多的是架构设计,实现思路上面的宏观解释,至少让我们在不了解代码实现之前可以粗略的知道Nginx是如何实现的,第二个则是Nginx源码太过复杂,不是我这样的菜鸟可以分析透彻的(这个是主要原因),非常感谢对Nginx初级入门系列的支持,你们的阅读和点赞是我坚持分享下去的不竭动力! 126 | 127 | -------------------------------------------------------------------------------- /Spring学习笔记/事件驱动编程/Spring 事件驱动编程.md: -------------------------------------------------------------------------------- 1 | # Spring 事件驱动编程 2 | 3 | 谈到Spring 事件驱动模型,我想大家都不陌生,事件驱动模型,通常也可以说是观察者设计模式,对观察者设计模式不熟悉的朋友可以看我之前写的笔记,[设计模式java语言实现之观察者模式](https://zhuanlan.zhihu.com/p/56032704),在java事件驱动的支持中,EventBus做移动端开发的朋友应该都比较了解,其实,java本身也自带了对事件驱动的支持,但是大部分都是用于我们的客户端开发,比如GUI ,Swing这些,而Spring 则在java的基础上,扩展了对事件驱动的支持。 4 | 5 | 不说废话,直接上代码 6 | 7 | ## 1.代码实战 8 | 9 | 首先,我们新建一个类NotifyEvent 继承ApplicationEvent,用于封装我们事件额外的信息,这里则是String类型的msg,用于记录详细的事件内容。 10 | 11 | ```java 12 | public class NotifyEvent extends ApplicationEvent { 13 | 14 | private String msg; 15 | 16 | public NotifyEvent(Object source, String msg) { 17 | super(source); 18 | this.msg = msg; 19 | } 20 | 21 | public String getMsg() { 22 | return msg; 23 | } 24 | } 25 | 26 | ``` 27 | 28 | 其中,ApplicationEvent 是一个抽象类,扩展了java本身的EventObject 类,每一个继承了ApplicationEvent的子类都表示一类事件,可以携带数据。 29 | 30 | 然后新建一个NotifyPublisher用于我们事件的发布工作,该类实现了ApplicationContextAware并重写了setApplicationContext 方法,这一步的目的是可以获取我们Spring的应用上下文,因为事件的发布是需要应用上下文来做的,不了解应用上下文的同学可以去看我的另外一篇笔记:[到底什么是上下文?](https://juejin.im/post/5d8ebf8ef265da5b633cc90f) 31 | 32 | ```java 33 | @Component //声明成组件,为了后期注入方便 34 | public class NotifyPublisher implements ApplicationContextAware { 35 | 36 | private ApplicationContext ctx; //应用上下文 37 | 38 | @Override 39 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 40 | this.ctx= applicationContext; 41 | } 42 | 43 | // 发布一个消息,这里大家可以根据不同的状态实现发布不同的事件,我这里就只写了一个事件类,所以if else 44 | //都发布NotifyEvent事件。 45 | public void publishEvent(int status, String msg) { 46 | if (status == 0) { 47 | ctx.publishEvent(new NotifyEvent(this, msg)); 48 | } else { 49 | ctx.publishEvent(new NotifyEvent(this,msg)) ; 50 | } 51 | } 52 | } 53 | 54 | ``` 55 | 56 | 最后一步就是实现一个类作为事件的订阅者啦,当事件发布时,会通知订阅者,然后订阅者做相关的处理,比如新用户注册发送事件自动发送欢迎邮件等等。同时,Spring 4.2 版本更新的EventListener,可以很方便帮助我们实现事件与方法的绑定,只需要在目标方法上加上EventListener即可。 57 | 58 | ```java 59 | @Component 60 | public class NotifyListener { 61 | 62 | @EventListener 63 | //参数NotifyEvent ,当有NotifyEvent 类型的事件发生时,交给sayHello方法处理 64 | public void sayHello(NotifyEvent notifyEvent){ 65 | System.out.println("收到事件:"+notifyEvent.getMsg()); 66 | } 67 | 68 | } 69 | 70 | ``` 71 | 72 | **测试:**编写我们的测试类TestController。 73 | 74 | ```java 75 | @RestController 76 | public class TestController { 77 | 78 | @Autowired 79 | private NotifyPublisher notifyPublisher; 80 | 81 | @GetMapping("/sayHello") 82 | public String sayHello(){ 83 | notifyPublisher.publishEvent(1, "我发布了一个事件"); 84 | return "Hello Word"; 85 | 86 | } 87 | 88 | } 89 | 90 | 91 | ``` 92 | 93 | 启动我们的应用,在浏览器中输入http://127.0.0.1:8080/sayHello,控制台输出: 94 | 95 | ```text 96 | 2019-09-28 16:55:51.902 INFO 716 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 12 ms 97 | 收到事件:我发布了一个事件 98 | ``` 99 | 100 | 101 | 102 | 划个知识点: 103 | 104 | > 如果一个新的事件继承了NotifyEvent,当我们推送NotifyEvent类型的事件时,NotifyEvent和其子类的监听器都可以收到该事件。 105 | 106 | 107 | 108 | 完了吗,还没有,日常除了听到过事件驱动编程,偶尔还会见到异步事件驱动编程这几个字,同样的Spring 也提供了@Async 注解来实现异步事件的消费。用起来也很简单,只需要在 @EventListener上加上@Async 就好了。 109 | 110 | ## 2. Spring 异步事件实现: 111 | 112 | **代码如下:** 113 | 114 | ```java 115 | @Component 116 | public class NotifyListener { 117 | 118 | @Async 119 | @EventListener 120 | public void sayHello(NotifyEvent notifyEvent){ 121 | System.out.println("收到事件:"+notifyEvent.getMsg()); 122 | } 123 | 124 | } 125 | 126 | ``` 127 | 128 | 最后配置一个线程池 129 | 130 | ```java 131 | @Configuration 132 | @EnableAsync 133 | public class AysncListenerConfig implements AsyncConfigurer { 134 | 135 | /** 136 | * 获取异步线程池执行对象 137 | * 138 | * @return 139 | */ 140 | @Override 141 | @Bean(name = "taskExecutor") 142 | public Executor getAsyncExecutor() { 143 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 144 | executor.initialize(); 145 | executor.setCorePoolSize(10); //核心线程数 146 | executor.setMaxPoolSize(20); //最大线程数 147 | executor.setQueueCapacity(1000); //队列大小 148 | executor.setKeepAliveSeconds(300); //线程最大空闲时间 149 | executor.setThreadNamePrefix("ics-Executor-"); ////指定用于新创建的线程名称的前缀。 150 | executor.setRejectedExecutionHandler( 151 | new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略 152 | return new ExceptionHandlingAsyncTaskExecutor(executor); 153 | } 154 | 155 | 156 | 157 | } 158 | ``` 159 | 160 | 161 | 162 | ```java 163 | public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor { 164 | 165 | private AsyncTaskExecutor executor; 166 | 167 | public ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) { 168 | this.executor = executor; 169 | } 170 | 171 | //用独立的线程来包装,@Async其本质就是如此 172 | public void execute(Runnable task) { 173 | executor.execute(createWrappedRunnable(task)); 174 | } 175 | 176 | public void execute(Runnable task, long startTimeout) { 177 | //用独立的线程来包装,@Async其本质就是如此 178 | executor.execute(createWrappedRunnable(task), startTimeout); 179 | } 180 | 181 | 182 | public Future submit(Runnable task) { return executor.submit(createWrappedRunnable(task)); 183 | //用独立的线程来包装,@Async其本质就是如此。 184 | } 185 | 186 | 187 | public Future submit(final Callable task) { 188 | //用独立的线程来包装,@Async其本质就是如此。 189 | return executor.submit(createCallable(task)); 190 | } 191 | 192 | 193 | private Callable createCallable(final Callable task) { 194 | return new Callable(){ 195 | 196 | @Override 197 | public Object call() throws Exception { 198 | try { 199 | return task.call(); 200 | } catch (Exception ex) { 201 | handle(ex); 202 | throw ex; 203 | } 204 | } 205 | }; 206 | } 207 | 208 | 209 | private Runnable createWrappedRunnable(final Runnable task) { 210 | return new Runnable() { 211 | public void run() { 212 | try { 213 | task.run(); 214 | } catch (Exception ex) { 215 | handle(ex); 216 | } 217 | } 218 | }; 219 | } 220 | private void handle(Exception ex) { 221 | //具体的异常逻辑处理的地方 222 | System.err.println("Error during @Async execution: " + ex); 223 | } 224 | } 225 | ``` 226 | 227 | 228 | 229 | **测试:**编写我们的测试类TestController。 230 | 231 | ```text 232 | 2019-09-28 16:55:51.902 INFO 716 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 12 ms 233 | 收到事件:我发布了一个事件 234 | ``` 235 | 236 | 237 | 238 | 大功告成啦。 239 | 240 | 相关电子版笔记已经开源至github(欢迎star哦): 241 | 242 | 243 | 244 | --------------------------------------------------------------------------------