From 24d5837864f2623851ddd9e8f93a657c9dd84cab Mon Sep 17 00:00:00 2001
From: aJream <563664686@qq.com>
Date: Fri, 17 Sep 2021 11:02:23 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E7=AB=A0=EF=BC=9A?=
=?UTF-8?q?=E8=B4=A8=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
db.json | 2 +-
public/service-worker.js.map | 2 +-
.../\350\264\250\346\225\260.md" | 38 +++++++++++++++++++
3 files changed, 40 insertions(+), 2 deletions(-)
create mode 100644 "source/_posts/\347\256\227\346\263\225/\350\264\250\346\225\260.md"
diff --git a/db.json b/db.json
index 38075aa1..34c5435a 100644
--- a/db.json
+++ b/db.json
@@ -1 +1 @@
-{"meta":{"version":1,"warehouse":"4.0.0"},"models":{"Asset":[{"_id":"themes/butterfly/source/css/copyright.css","path":"css/copyright.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/background.css","path":"css/background.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/custom.css","path":"css/custom.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/icon.css","path":"css/icon.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/index.min.css","path":"css/index.min.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/mouse.css","path":"css/mouse.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/index.styl","path":"css/index.styl","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/side.css","path":"css/side.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/var.styl","path":"css/var.styl","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/JetBrainsMono-Medium.woff2","path":"fonts/JetBrainsMono-Medium.woff2","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/ZhuZiAWan.woff2","path":"fonts/ZhuZiAWan.woff2","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/ajLangMan.ttf","path":"fonts/ajLangMan.ttf","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/loving.ttf","path":"fonts/loving.ttf","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/shishangblack.ttf","path":"fonts/shishangblack.ttf","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/01.jpg","path":"img/01.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/02.jpg","path":"img/02.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/404.jpg","path":"img/404.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/34.jpg","path":"img/34.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/articles.png","path":"img/articles.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/algolia.svg","path":"img/algolia.svg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/articles2.png","path":"img/articles2.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/avatar1.jpg","path":"img/avatar1.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/avatar2.jpg","path":"img/avatar2.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/banner.png","path":"img/banner.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/blissed01.jpg","path":"img/blissed01.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/categories.png","path":"img/categories.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/favicon.png","path":"img/favicon.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/friend_404.gif","path":"img/friend_404.gif","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/huiji.png","path":"img/huiji.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/mybatis.png","path":"img/mybatis.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/mybatis1.png","path":"img/mybatis1.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/pcbimg.png","path":"img/pcbimg.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/pcbimg2.png","path":"img/pcbimg2.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/plane.png","path":"img/plane.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/plane1.png","path":"img/plane1.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/main.js","path":"js/main.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/tw_cn.js","path":"js/tw_cn.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/utils.js","path":"js/utils.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/LICENSE","path":"live2d-widget/LICENSE","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/README.md","path":"live2d-widget/README.md","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/autoload.js","path":"live2d-widget/autoload.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/live2d.min.js","path":"live2d-widget/live2d.min.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/waifu-tips.js","path":"live2d-widget/waifu-tips.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/waifu-tips.json","path":"live2d-widget/waifu-tips.json","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/waifu.css","path":"live2d-widget/waifu.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/AppStarting.ani","path":"mouse/AppStarting.ani","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Arrow.cur","path":"mouse/Arrow.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Cross.cur","path":"mouse/Cross.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Hand.cur","path":"mouse/Hand.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Handwriting.cur","path":"mouse/Handwriting.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Help.cur","path":"mouse/Help.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/IBeam.cur","path":"mouse/IBeam.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/NO.cur","path":"mouse/NO.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeAll.cur","path":"mouse/SizeAll.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeNESW.cur","path":"mouse/SizeNESW.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeNWSE.cur","path":"mouse/SizeNWSE.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeNS.cur","path":"mouse/SizeNS.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeWE.cur","path":"mouse/SizeWE.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/UpArrow.cur","path":"mouse/UpArrow.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Wait.ani","path":"mouse/Wait.ani","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/search/algolia.js","path":"js/search/algolia.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/search/local-search.js","path":"js/search/local-search.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-1.png","path":"live2d-widget/assets/screenshot-1.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-2.png","path":"live2d-widget/assets/screenshot-2.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-3.png","path":"live2d-widget/assets/screenshot-3.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/demo/demo.html","path":"live2d-widget/demo/demo.html","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/demo/login.html","path":"live2d-widget/demo/login.html","modified":0,"renderable":1}],"Cache":[{"_id":"source/link/index.md","hash":"b523cebc9646f9e71dced10c15286446b5ec05f9","modified":1629177172461},{"_id":"source/_data/link.yml","hash":"ac42ffbb8934c77eaaa451aaaee0e997edfbef39","modified":1630841898931},{"_id":"source/about/index.md","hash":"2cbe86fa0f91f1ec4e9e9f50d2622ba6fb334c4b","modified":1629470228214},{"_id":"source/categories/index.md","hash":"85594ad9ae83a45a6fd1f1bb812d1c348f0e0d04","modified":1629199613838},{"_id":"source/notes/index.md","hash":"cd1eca6299ad86555ea010220c23a582e1d1d689","modified":1630456117545},{"_id":"source/poems/index.md","hash":"4a8f9bd31f5b9f30a41e5c168294ff8006cfe2c0","modified":1630495317769},{"_id":"source/music/index.md","hash":"18c94b16f0d06e13daf5574fba544e4beee1888e","modified":1631436778221},{"_id":"source/tags/index.md","hash":"7d44ddaac9bce3ef3d0747699ad557271b21e2e2","modified":1629177106636},{"_id":"source/web-links/index.md","hash":"ed38c45ed78d5406a00f6c1c76fe9f98b03e6403","modified":1630461912181},{"_id":"source/_posts/51单片机/51单片机学习笔记(一).md","hash":"baa93866bf0443a9487ceadf241ea3e6191c644a","modified":1631277953371},{"_id":"source/_posts/51单片机/51单片机学习笔记(七).md","hash":"5ab18e79564dd583a3ff8a2bf3d9003ff862e599","modified":1631277926554},{"_id":"source/_posts/51单片机/51单片机学习笔记(三).md","hash":"b77cb8e4e58ba7affe617b7147b698d826fc3b2d","modified":1631277931265},{"_id":"source/_posts/51单片机/51单片机学习笔记(九).md","hash":"0fb139f3222ec409ca81bb298615c252bb79f6c4","modified":1631277912950},{"_id":"source/_posts/51单片机/51单片机学习笔记(二).md","hash":"c2df9e5ec32fba24ee910a0d2c234e5deafe1f76","modified":1631277906012},{"_id":"source/_posts/51单片机/51单片机学习笔记(五).md","hash":"6429fd26f9e6ab42720347db1992a5fbc97d2f5f","modified":1631277942618},{"_id":"source/manifest.json","hash":"c06ff8a12fb81cfe47befc9d3941aacfae49ddff","modified":1631611477371},{"_id":"source/_posts/51单片机/51单片机学习笔记(八).md","hash":"b5b3eac1a371a6578b27d7852d2fa707dac5c42d","modified":1631277895652},{"_id":"source/_posts/Hexo/Hexo添加百度统计分析.md","hash":"7b581d3c1adea4dee8d792cf805bdcc9f875b4c4","modified":1630841698490},{"_id":"source/_posts/51单片机/51单片机学习笔记(六).md","hash":"bed0d77a7c301947aa63f142220c27b865207a18","modified":1631277919484},{"_id":"source/_posts/51单片机/51单片机学习笔记(四).md","hash":"121d5f350d59cbce348b1d505f5a39a62036f1e4","modified":1631277936194},{"_id":"source/_posts/Hexo/butterfly主题配置.md","hash":"642f98cc33649a81685e660a687364d9f0e4d338","modified":1631806136062},{"_id":"source/_posts/Java面试/Java垃圾回收算法.md","hash":"c3b1129a2cbc588037e603bf4d9fba0116158a03","modified":1631347297796},{"_id":"source/_posts/Hexo/github Actions自动部署.md","hash":"ffb2b229f96e063815bcb43494cd15ccac5d9a5b","modified":1631806189622},{"_id":"source/_posts/Maven/1-maven基本概念.md","hash":"b721b4c3648699ce80e70b838843d2413027b96a","modified":1631242769812},{"_id":"source/_posts/Hexo/标签外挂.md","hash":"6187523670c82eb272c8f40fdee46b4467e3a6c1","modified":1631806122688},{"_id":"source/_posts/Maven/3-maven+IDEA创建webapp.md","hash":"24ef7a6e34687f49bf679e80d4e3cb75b8b98f2d","modified":1631242813294},{"_id":"source/_posts/Java面试/java面试题.md","hash":"47f820465287ffb7f9cf553b0be66aacf31c5d43","modified":1630720868705},{"_id":"source/_posts/MySQL/4-MySQL事务.md","hash":"9762f26985acb72368eb4a7ea8b6c6eba05c5faf","modified":1631106363208},{"_id":"source/_posts/MySQL/1-数据库认识.md","hash":"69ef32bfb52656fc614d7bb34317c45bed43af3a","modified":1631242870897},{"_id":"source/_posts/MySQL/2-MySQL基础.md","hash":"2b07760222f7dc7d425384768eda92c763fa4ac7","modified":1631106379369},{"_id":"source/_posts/MySQL/5-索引.md","hash":"54678b3e8b7c0424178ba9bc356dbed1b535187e","modified":1631106357536},{"_id":"source/_posts/MySQL/3-数据管理.md","hash":"526b0eec8b3c745a9543b8d5802cd52c4e9bda2b","modified":1631242915065},{"_id":"source/_posts/Spring/1-spring框架介绍.md","hash":"4c12fd3d702fbdde0e3c78008d84883b134df0b1","modified":1631243841676},{"_id":"source/_posts/MySQL/6-权限-备份.md","hash":"47af43803556a42b669ca47bba616a7853203e1d","modified":1631106351506},{"_id":"source/_posts/Spring/10-注解实现自动装配.md","hash":"696a8643c333620c9eafd1f3dc9b6ac54ce84639","modified":1631243823959},{"_id":"source/_posts/Spring/12-静态代理.md","hash":"ce877429ec005160d28d09a33bf1f16e364aa935","modified":1631243812129},{"_id":"source/_posts/MySQL/7-JDBC.md","hash":"305788488fc48dbb74c73517ba15c2eed0eec6e7","modified":1631242990248},{"_id":"source/_posts/Spring/11-使用注解开发.md","hash":"8adf385ed6593347df06ec5dd923c6c7f1d345bd","modified":1631243818357},{"_id":"source/_posts/Spring/2-SpringIOC.md","hash":"e3a00f743c9a3dd346761d2a800c5998de95d8b8","modified":1631243889108},{"_id":"source/_posts/Spring/14-springAOP.md","hash":"fe9e27a79ea54944c5895bfdfce143c3186b47f9","modified":1631243778937},{"_id":"source/_posts/Spring/3-ioc控制反转.md","hash":"5d6dc86b0671061d8a92a8bc50e8632c279dfbe5","modified":1631243873109},{"_id":"source/_posts/Spring/13-动态代理.md","hash":"47b762cb652146dd068fe76f4080ce9decaf8cff","modified":1631243800037},{"_id":"source/_posts/Spring/7-spring容器注入.md","hash":"1e72586920062aeb3c7c39258470b53555578d47","modified":1631243912070},{"_id":"source/_posts/Spring/5-ioc创建对象方式.md","hash":"724e4210c04ef41ce123f7026151aec89a4e66dd","modified":1631243900605},{"_id":"source/_posts/Spring/4-HelloSpring程序.md","hash":"bbe75d0e4aa6cb9ed065b771be37c69392227620","modified":1631243881840},{"_id":"source/_posts/Spring/6-spring配置说明.md","hash":"4c06ce7bab72f94b6e46774f1773997b4d76f090","modified":1631243904710},{"_id":"source/_posts/Spring/8-命名空间和作用域.md","hash":"9401035baf7cd2bd15f608b48730e70ed02a1640","modified":1631243833986},{"_id":"source/_posts/Mybatis/mybatis关联查询(四).md","hash":"b6c78531032c3896d7c1f20086e27416f4c7731e","modified":1630314135636},{"_id":"source/_posts/Spring/9-bean自动装配.md","hash":"b7608f63c78c92f5df4342d582c77c22ca3b1980","modified":1631243829012},{"_id":"source/_posts/Mybatis/mybatis动态sql(七).md","hash":"5b5cccaf4b436fa4516447fb86afe1b34eb638c1","modified":1630248324576},{"_id":"source/_posts/Mybatis/mybatis延迟加载(六).md","hash":"6c245ee1328b1492dfcbf53448f4b7d400f0fdd6","modified":1630331393564},{"_id":"source/_posts/Mybatis/mybatis的Mapper详解(三).md","hash":"9d1cf80be6c69ef5f3f6fe840940d50453e8e09c","modified":1630248305560},{"_id":"source/_posts/Mybatis/mybatis简单使用(二).md","hash":"a5588151a36c8144206562e2a711af39ac9ba893","modified":1630248299444},{"_id":"source/_posts/Mybatis/mybatis简单介绍与使用(一).md","hash":"2a1b3741be4325b72ce72b46c99943c7988e564d","modified":1630248286760},{"_id":"source/_posts/Mybatis/mybatis逆向工程(五).md","hash":"80fadcf88c09c5e70a0e43a7883c4a905176efaf","modified":1630248315143},{"_id":"source/_posts/esp8266/0-Arduino配置esp8266开发环境.md","hash":"1ae7dabb617ed7af563ab4fc971fa2ff3346bc7b","modified":1631346932130},{"_id":"source/_posts/esp8266/1-接入点模式-无线终端模式.md","hash":"d6b50a08f00138a630722ab9cefa7804e28bdac6","modified":1631243986998},{"_id":"source/_posts/esp8266/2-网络服务器.md","hash":"a145f1fbefcc624a4080db65fb41cffef88f575f","modified":1631243997136},{"_id":"source/_posts/esp8266/3-闪存文件系统.md","hash":"665075ac149ce26c7ae45cc1632d5b5247155fb7","modified":1631346924239},{"_id":"source/_posts/esp8266/5-Stream数据流.md","hash":"2059838c8bbbd5ae5338438f4e59189252a82a5b","modified":1631346878317},{"_id":"source/_posts/esp8266/4-NodeMCU作为客户端使用.md","hash":"32dd90d0f0a098bf778d9df7c338094137a509b3","modified":1631346862484},{"_id":"source/_posts/esp8266/8-OTA上传代码.md","hash":"c135b08812c2e0646f859d788d9bf45aa9250b91","modified":1631346901650},{"_id":"source/_posts/esp8266/MQTT篇(一)-认识MQTT与基本操作.md","hash":"92e8dafacbff0a5f3c229d36984db24054b6cbcc","modified":1631346808088},{"_id":"source/_posts/esp8266/7-多任务执行.md","hash":"fe76210a836b07eebc72e106657bd31e9c1211f0","modified":1631346890150},{"_id":"source/_posts/esp8266/6-WiFi配网.md","hash":"d422ebb4c9ded7c59d0dd7511b19e3d88bcae27a","modified":1631346886084},{"_id":"source/_posts/esp8266/MQTT篇(三)-主题.md","hash":"a058bd9738090459b257442d7b58abb338ed4f72","modified":1630248886346},{"_id":"source/_posts/esp8266/MQTT篇(二)-发布、订阅、取消订阅.md","hash":"d19e95d87777a83680af4eabe4b46f9139907966","modified":1630248882648},{"_id":"source/_posts/SpringMVC/springMVC(二)HelloMVC.md","hash":"f8e912976546f72f47b177bf009c4047c35d9f91","modified":1630838038666},{"_id":"source/_posts/SpringMVC/springMVC(三)使用注解开发.md","hash":"7e2d4406b2a227668dec925352eaf45ec626b550","modified":1630838043284},{"_id":"source/_posts/SpringMVC/springMVC(一)快速入门.md","hash":"47df9392752049d5b2e0a1d89f39784a70694268","modified":1630838027417},{"_id":"source/_posts/SpringMVC/springMVC(四)Controller与restful.md","hash":"0c393bcd4bd730b37abdc8a0bd284732cc479384","modified":1630838047415},{"_id":"source/_posts/SpringMVC/springMVC(六)数据处理.md","hash":"ecfa53b64252b8a777f8a12b80535836366ebb4d","modified":1630838056597},{"_id":"source/_posts/javaEE/3-ServletRequest对象.md","hash":"12b2f1398cbf09df5b3d75cf4e6635367791655c","modified":1631243407722},{"_id":"source/_posts/SpringMVC/springMVC(五)结果跳转方式.md","hash":"4ae1e8827eafb617ef1747747666088b1c643675","modified":1630838051953},{"_id":"source/_posts/javaEE/2-Servlet生命周期.md","hash":"48a296346a3c6e86079a15682d8d935f52f0fb07","modified":1631243355138},{"_id":"source/_posts/javaEE/1-Servlet.md","hash":"c7270b3dccaa9fbac4bb7a880da86fd54eedb857","modified":1631243338332},{"_id":"source/_posts/javaEE/IDEA配置Tomcat.md","hash":"57335e93f1c10a450b18f759c99607235c5fdf1d","modified":1631243461852},{"_id":"source/_posts/javaEE/http.md","hash":"cd4021433ae1ac20e4dd6f1f9a40316f0c69b01d","modified":1630677215404},{"_id":"source/_posts/javaEE/4-请求转发.md","hash":"51c6485851dfc005dcc187e62a5c18147059b6ef","modified":1631243421610},{"_id":"source/_posts/javaEE/Tomcat基础.md","hash":"de9787f7bf7a2f41fcc6a2f4df6fef969e73d1f0","modified":1630671686646},{"_id":"source/_posts/javaSE/10-装箱与拆箱.md","hash":"b70216ab8145f5f31a461c09e0981606264097f2","modified":1630251080436},{"_id":"source/_posts/javaSE/1-类与对象.md","hash":"cd815d389512cc1ea6ee5596c3abccea4160df9b","modified":1631243135295},{"_id":"source/_posts/javaSE/11-数字与字符串相互转换.md","hash":"4f1b133b565eae5977800de91d6eb8266621f826","modified":1630251084118},{"_id":"source/_posts/javaSE/12-字符串操作.md","hash":"658bf9a88b0d6dd97e82c1e11ae37a9b9f84caae","modified":1630251087482},{"_id":"source/_posts/javaSE/14-接口与继承.md","hash":"4198da5d1d74a47f2fa49b09141631318db96c23","modified":1630251095517},{"_id":"source/_posts/javaSE/13-日期.md","hash":"ae9aaed18184ef39f36ef26152111df220172c65","modified":1630251091919},{"_id":"source/_posts/javaSE/15-异常处理.md","hash":"2ec30d857c147fdacc21419556f48f9e78fa671c","modified":1630251098885},{"_id":"source/_posts/javaSE/16-文件IO.md","hash":"bcd6604db1fa6f0c96b03a07b2b9bdfbbcc28d9f","modified":1630251102266},{"_id":"source/_posts/javaSE/17-容器.md","hash":"3cb845c57d11ceb506bf2eb3c321d8a7a56abb07","modified":1630251116883},{"_id":"source/_posts/javaSE/17-集合框架.md","hash":"9d1e5fd4a725c71ad667d51d2dc638152be6c3a6","modified":1630251105529},{"_id":"source/_posts/javaSE/18-泛型.md","hash":"286cc161aff3cf7577cf60d64eeaef6aa861eea7","modified":1630251121869},{"_id":"source/_posts/javaSE/2.2-访问修饰符.md","hash":"b01d48e5e16a4016638f2ae7f977152b30fc6ef7","modified":1630251038950},{"_id":"source/_posts/javaSE/19-多线程系列.md","hash":"74a44f6b72f52971feefc14012f22f1eed5dd49f","modified":1630251125689},{"_id":"source/_posts/javaSE/2.1-类和对象的属性初始化.md","hash":"64bb8acbe190e10d7a13e568920368ff3bd4c94b","modified":1630251034836},{"_id":"source/_posts/javaSE/21-注解.md","hash":"948bf85840b582a6d5333bf6dd56a010e5324c49","modified":1630251134568},{"_id":"source/_posts/javaSE/3.1-数据类型.md","hash":"3a380b105bf644c6905b3d32a4382dd495c26f0b","modified":1630251043790},{"_id":"source/_posts/javaSE/3.2-数组.md","hash":"2cf0a2ca05c216f0f83842ce9654d81a73d86321","modified":1630251047232},{"_id":"source/_posts/javaSE/20-反射机制.md","hash":"115db83be1b9e6a83c1b005757dc670a5a49b8d2","modified":1630251130453},{"_id":"source/_posts/javaSE/4-变量.md","hash":"abc5bd6ca9258e7b14570e03c66d1520cb59d483","modified":1630251050603},{"_id":"source/_posts/javaSE/5-操作符.md","hash":"1ec84eddc3febdebc9540dd7c0718361ec2ec8a3","modified":1630251053821},{"_id":"source/_posts/javaSE/6-输入语句.md","hash":"e2995606574b6e504d1202ff261537f53f95718d","modified":1630251057135},{"_id":"source/_posts/javaSE/7-条件语句.md","hash":"e43786948b7a87e74960b8459b30eea0bfd81df5","modified":1630312166817},{"_id":"source/_posts/javaSE/8-循环语句.md","hash":"7797c7d3f5730b6507c9e83b2b15720320075b00","modified":1630251071601},{"_id":"source/_posts/win10/win10任务栏透明.md","hash":"daa7cba90b6693dddc8f378234a96d31d1a90c28","modified":1631024957913},{"_id":"source/_posts/javaSE/9-package包.md","hash":"e71ddfcdc911f3455aa514c0458974a2a9c84bb7","modified":1630251074685},{"_id":"source/_posts/win10/win10创建系统还原节点.md","hash":"d88958fa074d91459ad9df888b6dc46ab0fbe3ab","modified":1631112351921},{"_id":"source/_posts/win10/win10主题美化.md","hash":"834b1c6baa1802570ca526f151a8ab8dd4e27f51","modified":1630834352290},{"_id":"source/_posts/stm32/MDK_Keil卸载已经安装的pack.md","hash":"50804d67534ad7f9b9214ff801d2a1bc3579b271","modified":1631278107623},{"_id":"source/_posts/stm32/stm32标准外设库介绍.md","hash":"49b69166a379f02c46ca6d5bce694e707600e515","modified":1631417937863},{"_id":"source/_posts/stm32/keil新建库函数版本的project.md","hash":"bc5ec136879bdfbd73eeadc5d26fd83502fcef36","modified":1631285270686},{"_id":"source/_posts/stm32/stm32点亮led灯程序.md","hash":"47e58ef7caa184c1e68294bbfd6c3fc02cfdd9eb","modified":1631411594761},{"_id":"source/_posts/杂七杂八/gitee图床配置.md","hash":"71a1ffcb7d8384734563cff8f2acdbcf9c99ed04","modified":1631536238795},{"_id":"source/_posts/网络安全/古典加密算法.md","hash":"c07a4af38074ebd217f0926bfc94ff12d06e020d","modified":1631802714376},{"_id":"source/_posts/网络通信/计算机网络的产生.md","hash":"a8774c3ea7a7502a6f19109ac7a9c46adce82e81","modified":1631498410634},{"_id":"source/_posts/电磁场/矢量场.md","hash":"071499e078c59f02d2813c38988c15dc6f53ad34","modified":1631092459444},{"_id":"source/_posts/网络通信/网络协议.md","hash":"821f48c6c4d29fd76d7ce17b784bd6e63b7d492c","modified":1631498054362},{"_id":"themes/butterfly/README_CN.md","hash":"459d6f3200863021bee1fe72a719aef236fb4090","modified":1627578032000},{"_id":"themes/butterfly/LICENSE","hash":"1128f8f91104ba9ef98d37eea6523a888dcfa5de","modified":1627578032000},{"_id":"themes/butterfly/package.json","hash":"c18b118422237b964063874a099945b79573feca","modified":1627578032000},{"_id":"themes/butterfly/languages/en.yml","hash":"cd333235ff1648a6bf58dfafc81f2c57672a15a5","modified":1627578032000},{"_id":"themes/butterfly/languages/default.yml","hash":"7ca673fb629ea74f5ba5e75b4f0f95248cfb5090","modified":1627578032000},{"_id":"themes/butterfly/languages/zh-CN.yml","hash":"741e522b2387f94764a73844e7b084cc7e927c54","modified":1627578032000},{"_id":"themes/butterfly/_config.yml.bak","hash":"8609065adff452353410b14cced654a0a7430914","modified":1629765958049},{"_id":"themes/butterfly/languages/zh-TW.yml","hash":"79a50c40d9f5463f1fa42aa870ac6b8b84540412","modified":1627578032000},{"_id":"themes/butterfly/layout/archive.pug","hash":"bd62286afb64a51c97e800c5945620d51605d5fa","modified":1627578032000},{"_id":"themes/butterfly/layout/category.pug","hash":"60c1b795b6e227b5dd81963b51d29d1b81d0bf49","modified":1627578032000},{"_id":"themes/butterfly/layout/index.pug","hash":"201b6f40da9a60ef76e4f51c5dd1440114dcb303","modified":1630593644328},{"_id":"themes/butterfly/layout/page.pug","hash":"82aa988527a11835e7ac86ce4f23b8cd20014dfa","modified":1627578032000},{"_id":"themes/butterfly/layout/tag.pug","hash":"0440f42569df2676273c026a92384fa7729bc4e9","modified":1627578032000},{"_id":"themes/butterfly/README.md","hash":"cedd13fcd8c75a68742265dd8eced4087e940ffd","modified":1627578032000},{"_id":"themes/butterfly/scripts/year.js","hash":"a72100c58fc177fc1ec123ece67bb216a32911b7","modified":1629471168674},{"_id":"themes/butterfly/.github/stale.yml","hash":"05a55a87fa7f122c59683e41c8b2e37e79f688f0","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/404.pug","hash":"7d378e328a53cc99d5acc9682dce53f5eb61537d","modified":1627578032000},{"_id":"themes/butterfly/.github/ISSUE_TEMPLATE/feature_request.md","hash":"f6867a2f0417fe89a0f2008730ee19dd38422021","modified":1627578032000},{"_id":"themes/butterfly/layout/post.pug","hash":"8d398c8925182699d9f2b9f1b727f06228488312","modified":1627578032000},{"_id":"themes/butterfly/.github/ISSUE_TEMPLATE/custom.md","hash":"eff495eb1584cf4586e33c76e8b2fa6a469a179b","modified":1627578032000},{"_id":"themes/butterfly/.github/ISSUE_TEMPLATE/bug_report.md","hash":"476802922b774b679225102ac30a9d9183394701","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/footer.pug","hash":"02390a5b6ae1f57497b22ba2e6be9f13cfb7acac","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/additional-js.pug","hash":"4156224c47bfc2482281ac4e4df701c30476ff00","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head.pug","hash":"1377952022ee0a9eaa7a2fd1098f1571efc468d9","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/layout.pug","hash":"6f2608c4d93d3d10ae6b2cd7f8918f303f024321","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/sidebar.pug","hash":"8dafc2dcd8c33f70a546fee443f0b6d80b3cd243","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/pagination.pug","hash":"0b80f04950bd0fe5e6c4e7b7559adf4d0ce28436","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/rightside.pug","hash":"2d0453adf92a3fd3466cf0793f14685d17b8b51d","modified":1627578032000},{"_id":"themes/butterfly/scripts/events/init.js","hash":"018aa446265fe627301b1d53d7cba4f4ff1960ac","modified":1627578032000},{"_id":"themes/butterfly/scripts/events/404.js","hash":"83cd7f73225ccad123afbd526ce1834eb1eb6a6d","modified":1627578032000},{"_id":"themes/butterfly/scripts/filters/post_lazyload.js","hash":"4cc2d517195c8779471d326ada09f9371cbad4dd","modified":1627578032000},{"_id":"themes/butterfly/scripts/events/welcome.js","hash":"d575137c8779e50422c2416f4d0832fdea346ee6","modified":1627578032000},{"_id":"themes/butterfly/scripts/filters/random_cover.js","hash":"9821872007cf57efae4b728dc575ef9d004547bb","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/aside_archives.js","hash":"2ec66513d5322f185d2071acc052978ba9415a8e","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/page.js","hash":"c6611d97087c51845cb1ab4821696a62fa33daeb","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/inject_head_js.js","hash":"65f2442e04c4defd16e7c1e67701d3bb41d9577a","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/aside_categories.js","hash":"e00efdb5d02bc5c6eb4159e498af69fa61a7dbb9","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/button.js","hash":"b816ded1451f28c7c54151ffe6c259b110253ae3","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/related_post.js","hash":"54b9324e3506dcc9c9991ef5e11e37e66e21594f","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/hide.js","hash":"f33858ffb9e88191e644796e11d2f901eb332308","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/gallery.js","hash":"94826ea6bcc4d2304199adae12c4e2b272caf529","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/inlineImg.js","hash":"a43ee2c7871bdd93cb6beb804429e404570f7929","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/mermaid.js","hash":"35f073021db93699fcac9ef351e26c59c31aadf7","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/label.js","hash":"03b2afef41d02bd1045c89578a02402c28356006","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/tabs.js","hash":"6c6e415623d0fd39da016d9e353bb4f5cca444f5","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/note.js","hash":"c16c6eb058af2b36bcd583b2591076c7ebdd51ad","modified":1627578032000},{"_id":"themes/butterfly/source/css/copyright.css","hash":"40f1b088daeaca4fdfe81e77024dd863aa5e7c1c","modified":1629187161084},{"_id":"themes/butterfly/source/css/background.css","hash":"24beaed4bf1eb5f2157743f0805e209e320374c0","modified":1631178433645},{"_id":"themes/butterfly/source/css/icon.css","hash":"d93c1e972e3569c9a08fedf3f54a1fe28f2b036f","modified":1631435783049},{"_id":"themes/butterfly/source/css/custom.css","hash":"662ddd3067ea440c511faeb917a5a0a49f38dda6","modified":1631535464054},{"_id":"themes/butterfly/source/css/index.styl","hash":"861998e4ac67a59529a8245a9130d68f826c9c12","modified":1627578032000},{"_id":"themes/butterfly/source/css/index.min.css","hash":"706e9fbfd4eace05e57d1d414b610f72a267480e","modified":1631105037871},{"_id":"themes/butterfly/.github/workflows/publish.yml","hash":"05857c2f265246d8de00e31037f2720709540c09","modified":1627578032000},{"_id":"themes/butterfly/source/css/mouse.css","hash":"a2d961444557cd7a72dc1d8e26c6f051e916249e","modified":1630154901393},{"_id":"themes/butterfly/source/css/side.css","hash":"5bce6787fa34f53e43a96a5c2700b510fb137b0d","modified":1630154791413},{"_id":"themes/butterfly/source/css/var.styl","hash":"40c3f64422205a24e68ce1ad8fe8163f24fdd525","modified":1627578032000},{"_id":"themes/butterfly/source/fonts/JetBrainsMono-Medium.woff2","hash":"f31bdf436d68c69503e96e7ca691a90ffa4918a8","modified":1630301034059},{"_id":"themes/butterfly/source/img/404.jpg","hash":"fb4489bc1d30c93d28f7332158c1c6c1416148de","modified":1627578032000},{"_id":"themes/butterfly/source/img/articles.png","hash":"f6654630e0f6f18497475b34e607f63a79f09140","modified":1630247726978},{"_id":"themes/butterfly/source/img/articles2.png","hash":"44f2e90e928bf1ae3fd91a85b70b5b1d07a91519","modified":1630247920935},{"_id":"themes/butterfly/source/img/algolia.svg","hash":"ec119560b382b2624e00144ae01c137186e91621","modified":1627578032000},{"_id":"themes/butterfly/source/img/avatar1.jpg","hash":"466d1b7cd0573e57e7db294c5f63c55ee6ca4296","modified":1630311556297},{"_id":"themes/butterfly/source/img/favicon.png","hash":"3cf89864b4f6c9b532522a4d260a2e887971c92d","modified":1627578032000},{"_id":"themes/butterfly/source/img/avatar2.jpg","hash":"4a7143ede9561ace432c2ac963b406428b296756","modified":1630313302374},{"_id":"themes/butterfly/source/img/mybatis.png","hash":"1e685804796630baba037812e3beab62936e1227","modified":1630246612746},{"_id":"themes/butterfly/source/img/huiji.png","hash":"f33b6927a0124ce7ad10638b96ee3be9b55c555e","modified":1630246963906},{"_id":"themes/butterfly/source/img/plane.png","hash":"fcd0b34a638838dcae8cbf17f34c51f973a0c02b","modified":1628982336217},{"_id":"themes/butterfly/source/img/pcbimg.png","hash":"72afcf1951d00c206d235facf6e09d16802c6ba1","modified":1630248671451},{"_id":"themes/butterfly/source/img/pcbimg2.png","hash":"511c4a15ad0f382adcbd3e1ff86dca3ead5d2df1","modified":1630487183495},{"_id":"themes/butterfly/source/img/mybatis1.png","hash":"d40a9abf2799be2e830868845e7287e884d8688d","modified":1630248123122},{"_id":"themes/butterfly/source/js/main.js","hash":"8ef2821ceb92d81aa0f8c02ee932f094adcafb2b","modified":1627578032000},{"_id":"themes/butterfly/source/js/tw_cn.js","hash":"00053ce73210274b3679f42607edef1206eebc68","modified":1627578032000},{"_id":"themes/butterfly/source/js/utils.js","hash":"8319b59c26ce8cd2b0ae7d030c4912215148fa92","modified":1627578032000},{"_id":"themes/butterfly/source/live2d-widget/autoload.js","hash":"e4364dd681c0a6d5bf64b50b94c3fa656303aa26","modified":1630559474816},{"_id":"themes/butterfly/source/live2d-widget/waifu-tips.json","hash":"cc68124da17a8547f26ebca0c0596d20dea37238","modified":1629353672566},{"_id":"themes/butterfly/source/live2d-widget/waifu-tips.js","hash":"2684e315f9aeb882055f541f943c959bd3afbe34","modified":1629353672566},{"_id":"themes/butterfly/source/img/plane1.png","hash":"c5713f3d92ddcd45d2b9a494f78b0f5b0e4a7466","modified":1628982320662},{"_id":"themes/butterfly/source/live2d-widget/waifu.css","hash":"003ddb4a25b804a9ce99c6f365142fa382dd0e75","modified":1629355252618},{"_id":"themes/butterfly/source/live2d-widget/LICENSE","hash":"1eba7caf09a39110ad2f542e3ed8700d1a69c6d3","modified":1629353672556},{"_id":"themes/butterfly/source/mouse/Cross.cur","hash":"6d6d6f975164d58db643a84ed238375fcbdac98d","modified":1564159338221},{"_id":"themes/butterfly/source/mouse/Handwriting.cur","hash":"195c938abc7f3e6d1f2e6301c46ba1e42ff99a4f","modified":1564159347886},{"_id":"themes/butterfly/source/live2d-widget/README.md","hash":"665054726a60b77a8f5e5b96527b8f1a4f166e30","modified":1629353672556},{"_id":"themes/butterfly/source/mouse/Help.cur","hash":"bf59506b7a65e2a5bb8c07b9b3c8daa6b90b0b7d","modified":1564159364356},{"_id":"themes/butterfly/source/mouse/Arrow.cur","hash":"1ab6471c728291cf66ba813b09135d48a06e74c3","modified":1564159324877},{"_id":"themes/butterfly/source/mouse/IBeam.cur","hash":"c96a55e806892deb9c1866c1aa33f98627d6dadf","modified":1564159375581},{"_id":"themes/butterfly/source/mouse/SizeAll.cur","hash":"b8ccca4354a8c11d61b7281818004933bdc32e01","modified":1564159388444},{"_id":"themes/butterfly/source/mouse/AppStarting.ani","hash":"e334f5489e63167adbd44f1116ffcfc72e84f718","modified":1564159318638},{"_id":"themes/butterfly/source/mouse/Hand.cur","hash":"46d6d4e0a8a79e054df35bcee1a53bb3c4b1ec8b","modified":1564159343139},{"_id":"themes/butterfly/source/mouse/SizeWE.cur","hash":"915ae8029726e0cf7b8f523fff059a96148cfc5c","modified":1564159419424},{"_id":"themes/butterfly/source/mouse/SizeNWSE.cur","hash":"25666d52fb83373286dc0b815387be41a52a26ac","modified":1564159413351},{"_id":"themes/butterfly/source/mouse/NO.cur","hash":"709b03659cd3be203e9d1c449d675e866847b957","modified":1564159381269},{"_id":"themes/butterfly/source/mouse/SizeNESW.cur","hash":"253f4e4c0da9b85757596c09187a79204fd9f07b","modified":1564159398663},{"_id":"themes/butterfly/layout/includes/head/Open_Graph.pug","hash":"6c41f49a3e682067533dd9384e6e4511fc3a1349","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/google_adsense.pug","hash":"95a37e92b39c44bcbea4be7e29ddb3921c5b8220","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/config_site.pug","hash":"889ef16fa34a39e5533bc170e62f20f3450cc522","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/analytics.pug","hash":"90d01b88d0f406d00184960b1afe9230aec2ebe6","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/config.pug","hash":"4def0aab9e2172ad1f29abd1535d8e08ff23aa0b","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/preconnect.pug","hash":"e55f8bdb876d5429a908498db1307b94094c0d06","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/pwa.pug","hash":"3d492cfe645d37c94d30512e0b230b0a09913148","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/noscript.pug","hash":"d16ad2ee0ff5751fd7f8a5ce1b83935518674977","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/site_verification.pug","hash":"e2e8d681f183f00ce5ee239c42d2e36b3744daad","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/nav.pug","hash":"c205b9fd72b2fe19e6d15c5b5ab0fb38c653032e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/social.pug","hash":"0d953e51d04a9294a64153c89c20f491a9ec42d4","modified":1627578032000},{"_id":"themes/butterfly/source/mouse/SizeNS.cur","hash":"4c02aa53675e758f61de6e13a5cf764b41e129f5","modified":1564159406614},{"_id":"themes/butterfly/layout/includes/loading/loading-js.pug","hash":"4cfcf0100e37ce91864703cd44f1cb99cb5493ea","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/loading/loading.pug","hash":"5276937fbcceb9d62879dc47be880cd469a27349","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/index.pug","hash":"65fa23680af0daf64930a399c2f2ca37809a8149","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/menu_item.pug","hash":"24370508ee87f14418e8f06e9d79ad8c52a342c4","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/post-info.pug","hash":"92f81a437c9db49f7ebcf608bc09488ecdb55a21","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/page/categories.pug","hash":"1f30952fed73dec21b42e2e30b7fe2e84618d2e4","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/page/default-page.pug","hash":"dbec869c62135695495703a29ad7655e9965d461","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/page/flink.pug","hash":"b53a2d4f9c37b375a4446d2273dcfb7712d91b3e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/page/tags.pug","hash":"93d4ebc7dc8228c7a10ddeb5a553d0dcdabbe145","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/mixins/article-sort.pug","hash":"bfd20a845dc4ac8d30792692954f3f404bb25ea0","modified":1629471437777},{"_id":"themes/butterfly/layout/includes/post/reward.pug","hash":"5b404356f311d2ee36478291ca3553210867b738","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/mixins/post-ui.pug","hash":"4c3c5cb69b3aead8c232cb0fbc251929f28aad75","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/aplayer.pug","hash":"292646dfab135973b09f0fa9e3931e83da2ed30e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/pangu.pug","hash":"d5fec7dedc52ab23865fb4db002755e9bdaadc9f","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/post/post-copyright - 副本.pug","hash":"88e3b611b03149665e4113cfa39595c1a3fca7e5","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/post/post-copyright.pug","hash":"2ed4573caa447dd9eac1bf859a09e3618f7dc2c8","modified":1629186546309},{"_id":"themes/butterfly/layout/includes/third-party/prismjs.pug","hash":"1fbecfd299068f90d727f0c8c65e2a792fa6e3e2","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_ad.pug","hash":"60dc48a7b5d89c2a49123c3fc5893ab9c57dd225","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_archives.pug","hash":"86897010fe71503e239887fd8f6a4f5851737be9","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/effect.pug","hash":"b9d54a01d7c2a7a183cb7209e99430ce7fea1fe3","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_announcement.pug","hash":"3d8e3706a056389176f55dd21956aabc78046761","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/pjax.pug","hash":"933cb710d2dbcea25c6426a57c6f49d2f48b792c","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_bottom_self.pug","hash":"13dc8ce922e2e2332fe6ad5856ebb5dbf9ea4444","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_categories.pug","hash":"d1a416d0a8a7916d0b1a41d73adc66f8c811e493","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_newest_comment.pug","hash":"27afd2274bd5f2cbbf1bad9f0afe2b2b72c213ca","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/subtitle.pug","hash":"d50e5c22cd6bc3c378bc581918136746cfa3447f","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_post_toc.pug","hash":"ae9336bf31cdad08ff586ead4295912a96563c76","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_tags.pug","hash":"438aea3e713ed16b7559b9a80a9c5ec0221263df","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_top_self.pug","hash":"ae67c6d4130a6c075058a9c1faea1648bcc6f83e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_author.pug","hash":"0366c658cdcff839aa1df2e2d252a03a53fd427e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_recent_post.pug","hash":"9c1229af6ab48961021886882c473514101fba21","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/index.pug","hash":"b5525891f6affd02c1ea3b2327c026882efe428b","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_webinfo.pug","hash":"0612aaee878f33ea8d3da0293c7dc3b6cd871466","modified":1627578032000},{"_id":"themes/butterfly/source/css/_global/function.styl","hash":"eda47f3e807a466ba8275627ea936c5100c43818","modified":1627578032000},{"_id":"themes/butterfly/source/css/_global/index.styl","hash":"e211efbd1952d4b1b881287aa43423133c1d166e","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/highlight.styl","hash":"85e72c70a0cef29e40be1968f5d23b06c6f8e3aa","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/theme.styl","hash":"fa7a4c1685f391d60ed863e869b9604b59746c27","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/404.styl","hash":"b0488ceacde74af139d66c8db5cb36cc21737b9b","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/archives.styl","hash":"6874adc2e276443f354bbe50d0072e9bec37243c","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/categories.styl","hash":"e554549f0a0ae85362f0b0e8687981741f486f6b","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/common.styl","hash":"97fec1e814f88237862f4d800a35362b802f6625","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/tags.styl","hash":"9a881c031f463c486bd25248c2814fd09f97892b","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/flink.styl","hash":"2cc49d3f6a6beb9f7bff93e292f88aa5681da1d0","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/homepage.styl","hash":"7c4152162a03aa8331a783df5695e4ebbb816a8c","modified":1627578032000},{"_id":"themes/butterfly/source/css/_mode/darkmode.styl","hash":"4e629f510b73f998ab208b739c5bd7dcd168d1a7","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/chat.styl","hash":"29f48f9370f245e6e575b5836bccf47eb5688d8b","modified":1627578032000},{"_id":"themes/butterfly/source/css/_mode/readmode.styl","hash":"f59a9a0059d5261251bdd6de45aa97dd2d11e633","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/aside.styl","hash":"7feb755ca7c22da36bbad11e74ecd95fdcf3b879","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/categoryBar.styl","hash":"7e31831eef19bd1957b5b6b5a957c64f20ef703d","modified":1630592669826},{"_id":"themes/butterfly/source/css/_layout/footer.styl","hash":"dd8cdf639ba2b726437c77fa7aa8d5edbabe8f9b","modified":1627578032000},{"_id":"themes/butterfly/source/mouse/UpArrow.cur","hash":"2e65c3437b457ddf6429a096d5921ea962703843","modified":1564159425136},{"_id":"themes/butterfly/source/css/_layout/comments.styl","hash":"f1b63892baafa48ab872bc79671d57aafd511f6c","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/head.styl","hash":"98235fcda3b87ad6f7e91eafbed94d0d6ae847ca","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/loading.styl","hash":"7d18a7be9cfea65091de3ef00014063d2d649912","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/pagination.styl","hash":"90fe01c968696a9f791cb2b84fca621cbbb56f47","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/post - 副本.styl","hash":"d748951d9fbcd04dda839085af78b01b8fa04cba","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/relatedposts.styl","hash":"0551c5893d1589a3d17ce161e50ecb1d724cc6e8","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/rightside.styl","hash":"7a072589e6097dbe942783131964f2372fdf1eb6","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/post.styl","hash":"72536fe1f905d0719896fd38c91d20040e3bc1c9","modified":1629187118515},{"_id":"themes/butterfly/source/css/_layout/reward.styl","hash":"ea1ba40dd5954c2ed718a126336fb7f94da4e66f","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/hexo.styl","hash":"d0386ba6d8d63afc72b9673e8f3e89df6446ffc2","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/third-party.styl","hash":"978c397d0966eaf9e6e2afd13866f8f4900b509f","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/sidebar.styl","hash":"2c5fb77c448ce0a734040c8ce532b28fed688899","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/button.styl","hash":"1c3f9d7efc3b9dfcfa8926a1132d0c44ffc7d4b2","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/label.styl","hash":"f741e85295ce15c70a6027ec15a542636dd5dcca","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/hide.styl","hash":"21964fdd6d74ffbea519418bab65024aee5f3736","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/inlineImg.styl","hash":"df9d405c33a9a68946b530410f64096bcb72560c","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/gallery.styl","hash":"53ecae272e16223a436c497abbf25dd5f0fc4aaa","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/note.styl","hash":"86fee274a62f7f034547342930f445c47378eb55","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/tabs.styl","hash":"1756791581c0ec51cb03353a09dac4778d944349","modified":1627578032000},{"_id":"themes/butterfly/source/css/_search/index.styl","hash":"f168f5c669978f633abe118cdcc4a12cfc883c01","modified":1627578032000},{"_id":"themes/butterfly/source/css/_search/local-search.styl","hash":"6befe4c51b86d0c1de130beeecad9e28d6442713","modified":1627578032000},{"_id":"themes/butterfly/source/css/_third-party/normalize.min.css","hash":"2c18a1c9604af475b4749def8f1959df88d8b276","modified":1627578032000},{"_id":"themes/butterfly/source/css/_search/algolia.styl","hash":"917e0e399e117217184ca63d3eb5c4843bcccf7b","modified":1627578032000},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-1.png","hash":"4bd4d97a1fcaa5deb3cea1c0a102a895a15af32f","modified":1629353672556},{"_id":"themes/butterfly/source/js/search/local-search.js","hash":"459e2541afda483916d16fce4aaa56b41bcd42ba","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/fb.pug","hash":"7848ec58c6ec03243abf80a3b22b4dc10f3edf53","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/disqus.pug","hash":"d85c3737b5c9548553a78b757a7698df126a52cf","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/index.pug","hash":"e3bf847553515174f6085df982f0623e9783db7a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/valine.pug","hash":"bba9871f446c10ffcc8fa9023f5a2eb701a86bae","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/waline.pug","hash":"400ce038548d6f9ddb486150c724c87b6923a88b","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/chatra.pug","hash":"481cd5053bafb1a19f623554a27d3aa077ea59c3","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/twikoo.pug","hash":"ef1b2b5b980d6aeaa5d06b97d1afc9644b155a16","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/index.pug","hash":"3f05f8311ae559d768ee3d0925e84ed767c314d3","modified":1627578032000},{"_id":"themes/butterfly/source/js/search/algolia.js","hash":"65b45e61586f7e66c3f338370bfd9daadd71a4b7","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/daovoice.pug","hash":"cfe63e7d26a6665df6aa32ca90868ad48e05ec04","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/crisp.pug","hash":"76634112c64023177260d1317ae39cef2a68e35f","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/gitter.pug","hash":"d1d2474420bf4edc2e43ccdff6f92b8b082143df","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/tidio.pug","hash":"24a926756c2300b9c561aaab6bd3a71fdd16e16d","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/disqusjs.pug","hash":"2e52c64e89f16267596a8465841dd46f51820982","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/disqus.pug","hash":"a111407fdcafcf1099e26ffa69786f8822c5d9fb","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/facebook_comments.pug","hash":"c46a932257212f82e4a9974fbbc5de8878c8b383","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/gitalk.pug","hash":"0b7571919e8ad51285deda56a1868fccf8c563d7","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/index.pug","hash":"da9813f8dc0d388869c15413cf056012cfb69e1a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/js.pug","hash":"bafb3d5710824caa59a56017afb058fd2b4eac65","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/livere.pug","hash":"52ea8aa26b84d3ad38ae28cdf0f163e9ca8dced7","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/utterances.pug","hash":"b871ea208e36398b4d668db9a9a0b61c79415381","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/twikoo.pug","hash":"16378d8646ea3f4ac99c18f0296dd85b13f9d775","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/math/index.pug","hash":"b8ae5fd7d74e1edcef21f5004fc96147e064d219","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/valine.pug","hash":"2b45fe09d5b591dca156b76dae99981f8d8e1c61","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/math/katex.pug","hash":"f9b00ead54573ba6e6eb33481588af144aab648d","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/math/mermaid.pug","hash":"3f3a3cd8bea2103dedd754f767aca5cb84d5f586","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/waline.pug","hash":"36f3c603d2a2ecddaa6d2675a89d76ad94968f72","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/index.pug","hash":"f6506ccfd1ce994b9e53aa95588d0b6dbad11411","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/math/mathjax.pug","hash":"a47d8f9f593091cc91192c0c49deaa2c0d2317fd","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/disqus-comment.pug","hash":"b443d6b16baf3ea250041342cc0361a42a412b7f","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/github-issues.pug","hash":"34088a15655704d12e9b1807b47b3f6a860c9eec","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/search/algolia.pug","hash":"d8f59e94eafc669c49349561dc5bbea3915aecb7","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/twikoo-comment.pug","hash":"cb38ffe911023092a90a28f2ba8317a92b22cd0c","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/search/index.pug","hash":"da3b9437d061ee68dbc383057db5c73034c49605","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/share/add-this.pug","hash":"2980f1889226ca981aa23b8eb1853fde26dcf89a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/search/local-search.pug","hash":"613280d61b8ab9612014ec016ae3d3698d36fd1a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/valine.pug","hash":"59b4c26a827ace5a54855881d199977103ff6f50","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/share/index.pug","hash":"4c4a9c15215ae8ac5eadb0e086b278f76db9ee92","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/waline.pug","hash":"a2bc2601b7e0ae5caf1fc51a07390562d928620a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/share/addtoany.pug","hash":"309f51bc5302e72fc469d54c577fbcfe57fb07a8","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/share/share-js.pug","hash":"006acc91ce25fc7c7d778ca043e970f57dc46b83","modified":1627578032000},{"_id":"themes/butterfly/source/live2d-widget/demo/demo.html","hash":"a26089ebcf2606b5b38e71a1e68d64f9344cac1a","modified":1629353672556},{"_id":"themes/butterfly/source/css/_highlight/prismjs/index.styl","hash":"e0e7065124ef0d99f8322a47bc47838982e04ad0","modified":1630509500097},{"_id":"themes/butterfly/source/css/_highlight/prismjs/line-number.styl","hash":"8970cc1916c982b64a1478792b2822d1d31e276d","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/highlight/diff.styl","hash":"8c0726fb8d9a497d2f900b0be2845efaa68e3d87","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/highlight/index.styl","hash":"89cbcc8e087788ecec18b5fa58710afacdb7d080","modified":1627578032000},{"_id":"themes/butterfly/source/live2d-widget/demo/login.html","hash":"1fd46a102ccf4e89a483b85ef4bd79a563563836","modified":1629353672556},{"_id":"themes/butterfly/source/img/friend_404.gif","hash":"8d2d0ebef70a8eb07329f57e645889b0e420fa48","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/prismjs/diff.styl","hash":"5972c61f5125068cbe0af279a0c93a54847fdc3b","modified":1627578032000},{"_id":"themes/butterfly/source/mouse/Wait.ani","hash":"5a7278ded553b61a248ec549b9964b7e2930cecf","modified":1564159429851},{"_id":"themes/butterfly/source/live2d-widget/live2d.min.js","hash":"61a09b54a472b4d0541b7c50afb3dfdd172e062f","modified":1629353672566},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-3.png","hash":"f89c6fdf44008e7d3c7a1aafe65dc39b212edb6c","modified":1629353672556},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-2.png","hash":"76b844653136184c573b3632e12296e008a7ff5c","modified":1629353672556},{"_id":"themes/butterfly/source/img/02.jpg","hash":"52276fbc7427eaf3facebf1e7cf9d99dad1664e8","modified":1630244205652},{"_id":"themes/butterfly/source/img/banner.png","hash":"e0b6d5d65d72bdfeb1b1bcfd1af340088d50fdfb","modified":1629110564595},{"_id":"themes/butterfly/source/img/01.jpg","hash":"455aff642579a632713b90c58edcd89f1ea01aef","modified":1629129110293},{"_id":"themes/butterfly/source/img/34.jpg","hash":"10b2ddfbe7f03c4513ace7a2855a47f907478d55","modified":1630244370887},{"_id":"themes/butterfly/source/img/blissed01.jpg","hash":"932db4b0cb8a297a4f9c1fd48af59183db742584","modified":1630244182581},{"_id":"themes/butterfly/source/img/categories.png","hash":"1829d3667c9fb36d729057a2fb26f12b662c86e4","modified":1629111056814},{"_id":"themes/butterfly/source/fonts/shishangblack.ttf","hash":"b5dda0beb2618829e41553d0dc656afbc78af523","modified":1630200705629},{"_id":"themes/butterfly/source/fonts/ajLangMan.ttf","hash":"c79e63ca78049f8273c0de7b5c65898c1dd8557f","modified":1629378758891},{"_id":"themes/butterfly/source/fonts/loving.ttf","hash":"786ab7558a8a2cd5de1ac71a1c6c19bfeff17e95","modified":1630200693598},{"_id":"themes/butterfly/source/fonts/ZhuZiAWan.woff2","hash":"a8512c6d5d70f5fc098a58a01a0ad720aa211c2a","modified":1631094979211},{"_id":"public/manifest.json","hash":"e5653018c633c03092c691caf2b17921c60fe633","modified":1631633856066},{"_id":"public/search.xml","hash":"6c5cefd2b310b7656e3e459e7b0e7403496dd38a","modified":1631633856066},{"_id":"public/404.html","hash":"58e406587db3548d33d3249886af5dfd2184989b","modified":1631633856066},{"_id":"public/about/index.html","hash":"f996050c1b8d596c9e3eb62b844982d16c705354","modified":1631633856066},{"_id":"public/link/index.html","hash":"3b9a75e10a18bd16eeb4cc20b268ccdc1154cb20","modified":1631633856066},{"_id":"public/music/index.html","hash":"06b0e469a43d9c6106c6c747688d2c70ffaed9df","modified":1631633856066},{"_id":"public/categories/index.html","hash":"118251e34b7c66b9fffb5c11e190b8a502340816","modified":1631633856066},{"_id":"public/notes/index.html","hash":"2a8ebaaa7b34885bf46975ce47271c15376a2c4b","modified":1631633856066},{"_id":"public/tags/index.html","hash":"f27805e42397220cc3da0d6c75998ebde260ffe1","modified":1631633856066},{"_id":"public/poems/index.html","hash":"43bf0ab218e30404765cf9c6856609221af24068","modified":1631633856066},{"_id":"public/posts/ee4b2b4d.html","hash":"8d72917cfce0212eb47acd0741fa39f2b6f463c9","modified":1631633856066},{"_id":"public/web-links/index.html","hash":"d535c4b5620fad3790ba92c6ee0641255846c328","modified":1631633856066},{"_id":"public/posts/88ad757.html","hash":"e2c6b65699d1ae25bb7326977c592940610bea1c","modified":1631633856066},{"_id":"public/posts/8b75069d.html","hash":"1d88cc36293dcdc8aaed858046a09f9ca9299bf0","modified":1631633856066},{"_id":"public/posts/1cd3002f.html","hash":"4233127e9c2c23aec8e5a96562a10729d76dcdd3","modified":1631633856066},{"_id":"public/posts/7dc1e032.html","hash":"dd8bdb3613e5ada905a2303e4f0a20dd70bc3ad6","modified":1631633856066},{"_id":"public/posts/f8dae6c5.html","hash":"1958ecfe80d2ab434268ebb6d92eab79269b420f","modified":1631633856066},{"_id":"public/posts/1294a4bd.html","hash":"48dd307b9e356f06fa0f34705d134ff0404f7573","modified":1631633856066},{"_id":"public/posts/190b30be.html","hash":"0a6066123a28e82d23c126603dd94029f5b7a860","modified":1631633856066},{"_id":"public/posts/a4c109bd.html","hash":"8b62e61ea8c3d924a7899598ee3e90b082b94702","modified":1631633856066},{"_id":"public/posts/54667693.html","hash":"6eb8e590c7c95e6b7a4b1c41f67e71e395af019a","modified":1631633856066},{"_id":"public/posts/401491fc.html","hash":"3f36bb80cfce6539e641a434a813461e982300ab","modified":1631633856066},{"_id":"public/posts/3de0f4a2.html","hash":"70d982cabab55ff24be5dee1668a0064b36427db","modified":1631633856066},{"_id":"public/posts/54a4218.html","hash":"3f61abfc0f2e9c6f1e5818e88f8144c60b815c8c","modified":1631633856066},{"_id":"public/posts/446faab7.html","hash":"f9bb5a58323e604583e6972b4327cd007b1068d7","modified":1631633856066},{"_id":"public/posts/ecec7d3f.html","hash":"f7a6f90bd0bd011ecff7a1ae78e8b4a5237c0999","modified":1631633856066},{"_id":"public/posts/1eb33081.html","hash":"752450393018314ba436dd83174188cec39a814c","modified":1631633856066},{"_id":"public/posts/57ac54fe.html","hash":"840bf899083f71fbaad61d0924b1483082f2a7e7","modified":1631633856066},{"_id":"public/posts/b8426075.html","hash":"04afceb2bf3a32a62bec0e50d7834ac94c2bc864","modified":1631633856066},{"_id":"public/posts/a4682555.html","hash":"c74ff055138550853d2b10c4c2786f86f2f1b90a","modified":1631633856066},{"_id":"public/posts/890c8bdb.html","hash":"5a095ce108ea77900ff3f1dede8b8c3e9aa110cc","modified":1631633856066},{"_id":"public/posts/c8eb4af9.html","hash":"1015211fc45492a3cc728dc820119856fe769505","modified":1631633856066},{"_id":"public/posts/dc7f8ae1.html","hash":"dd08a553296eca1cd503fafdfec55b2de37191fc","modified":1631633856066},{"_id":"public/posts/6e81c43e.html","hash":"9dff365ca5e2ca3018c9e24b4c1883a47dca57a8","modified":1631633856066},{"_id":"public/posts/3b32a20d.html","hash":"0cde59bc14487f5e042ef11ebab2939318469ad9","modified":1631633856066},{"_id":"public/posts/81ecdf4a.html","hash":"2a19a3f511e557417d497c8fcdf6550f0c0774aa","modified":1631633856066},{"_id":"public/posts/ba43a8dc.html","hash":"6212939774785dc57ce61487108b65aca471a997","modified":1631633856066},{"_id":"public/posts/f218ca42.html","hash":"15848001e001d4294889a0192f352aedf901a53a","modified":1631633856066},{"_id":"public/posts/ab69c7ec.html","hash":"0464e8ae29728e7233613df9f1b7b32ac471213f","modified":1631633856066},{"_id":"public/posts/78903d87.html","hash":"1abf47c7de03d672c2b052b0fcd47d89985cdc5e","modified":1631633856066},{"_id":"public/posts/17528481.html","hash":"635f9377b2af5bbd1c10086cb56b7af7fbfa374f","modified":1631633856066},{"_id":"public/posts/b836e65.html","hash":"899c0ee9cc3a85afb1f7a468acf90110a378639f","modified":1631633856066},{"_id":"public/posts/c2d1d169.html","hash":"ab4f3b09ca9fe2d5cec97d4dc79aa3848185ecb1","modified":1631633856066},{"_id":"public/posts/5dd9c9e3.html","hash":"f30ded54e22b1fceead36f2e2a5dd5c3e019a3ca","modified":1631633856066},{"_id":"public/posts/c4d68191.html","hash":"18d8daa44348d26765dc9c712348e31f8a0be08b","modified":1631633856066},{"_id":"public/posts/c27dd6ec.html","hash":"cf0de529f9f84bc11beb034ea11b1776af6f8365","modified":1631633856066},{"_id":"public/posts/383e39e3.html","hash":"021dfc87842ab104e9c0d30865925e1c267933c2","modified":1631633856066},{"_id":"public/posts/46cd19c8.html","hash":"026c49121699da6c7fc2f088b31ee4c37199361f","modified":1631633856066},{"_id":"public/posts/9e0a8624.html","hash":"f374ce85021f993616b98cbcb0f732d92fa56668","modified":1631633856066},{"_id":"public/posts/894e18ee.html","hash":"4d0f636e99cfc36b3744679343a46adc32da2cce","modified":1631633856066},{"_id":"public/posts/adfcabaa.html","hash":"35c9c2f7c7e6f3ac48d5c6d9fa5ca600027d413f","modified":1631633856066},{"_id":"public/posts/73ba8569.html","hash":"986c05360940fbd4b8bd2bde272c0d5f3a665f75","modified":1631633856066},{"_id":"public/posts/1b6a7930.html","hash":"b500d5c03f2cd0eb749287a9cd3358e45dc2c756","modified":1631633856066},{"_id":"public/posts/c25015c5.html","hash":"1d683c51cbc3bafb2ef0c86de378eac6a540cddb","modified":1631633856066},{"_id":"public/posts/baad5185.html","hash":"38344a849c2cb8105f352d24a5f50e8a13a6520b","modified":1631633856066},{"_id":"public/posts/2d7f83ef.html","hash":"671a3d3c537097a02c6433796db424da86beb86f","modified":1631633856066},{"_id":"public/posts/d150c3f1.html","hash":"c1c8faaadbfa1dbc74f6aa851f67ba90aca2817e","modified":1631633856066},{"_id":"public/posts/2080031a.html","hash":"8c6c1e6d3d1d6168b715f00829bfdbec4d40e569","modified":1631633856066},{"_id":"public/posts/27153554.html","hash":"fd9c69f99d550f23f4022eaa14a422278c7f5d76","modified":1631633856066},{"_id":"public/posts/4dc0a7a8.html","hash":"9e5ce406873762f90541189bcb77718568d4be69","modified":1631633856066},{"_id":"public/posts/89cf4bca.html","hash":"01069c939864e7b1e4c2952faa2d42c2d571e684","modified":1631633856066},{"_id":"public/posts/a1a1eb5.html","hash":"57cd8daacd3ff4d4e0a132d13a0a52475f261d4c","modified":1631633856066},{"_id":"public/posts/be39d4e8.html","hash":"bee976b1bf03fac9080b30a939c8f1fa9c775246","modified":1631633856066},{"_id":"public/posts/26c04051.html","hash":"45f844b331497a4c93752c8ee47dea07c82aef5f","modified":1631633856066},{"_id":"public/posts/38b3e585.html","hash":"b182f51537f07fcf9e8c1207d33d187cdc0c1985","modified":1631633856066},{"_id":"public/posts/39f00355.html","hash":"2e4539e7c805a0449b314be125dbd82343fe575e","modified":1631633856066},{"_id":"public/posts/f68104ec.html","hash":"fba07f1b3374a419d3fc34f3519bc77e1a262950","modified":1631633856066},{"_id":"public/posts/3feecb5e.html","hash":"8bd533a97d3d7c3f351745c0894f5fa623d17377","modified":1631633856066},{"_id":"public/posts/7f2244ba.html","hash":"b6733cbc60db2edcd6e54a1eea52afc59e88c911","modified":1631633856066},{"_id":"public/posts/d8642fb9.html","hash":"ca62d808348a78f38227ac0e69cdb939c67f0204","modified":1631633856066},{"_id":"public/posts/3fb3b16d.html","hash":"813ec5ece159676140fec6d31021763f0765f117","modified":1631633856066},{"_id":"public/posts/d959138b.html","hash":"7b572164e53e13a48e1c6329abf6549fe17ca910","modified":1631633856066},{"_id":"public/posts/59e405f3.html","hash":"fd6e165bb6b6c2bab7183b91e71c41a85991f7e7","modified":1631633856066},{"_id":"public/posts/752d4bdb.html","hash":"0dcb911913475c61dd4df6b86effa2e2fb50bd06","modified":1631633856066},{"_id":"public/posts/3afd7950.html","hash":"6154496d361851690e16f347f75a63f997634b04","modified":1631633856066},{"_id":"public/posts/530cfffd.html","hash":"d5f009bd083d94f216f9084caf80bc75b2e38983","modified":1631633856066},{"_id":"public/posts/3f4cddeb.html","hash":"dc99bec7ffa43bcde7b8d315dac042fb0c9573b5","modified":1631633856066},{"_id":"public/posts/1a57c6b6.html","hash":"7d290b2df71f7a7bc589c62f80b2584412b55fc9","modified":1631633856066},{"_id":"public/posts/d29e4d17.html","hash":"62dac627c7b813865909b4da59e9c2d78aa27348","modified":1631633856066},{"_id":"public/posts/2a3cf99e.html","hash":"656b6ce8849f4497a362d04801c2cc8c01ceabe5","modified":1631633856066},{"_id":"public/posts/683d1a78.html","hash":"1b0c01572589a576b1a062bab76674f6cffe4e02","modified":1631633856066},{"_id":"public/posts/d709dda.html","hash":"ce9df1dfc884deeaa312e3eaf9293051e57c7c9b","modified":1631633856066},{"_id":"public/posts/39aef30f.html","hash":"2ab0b61161357d59b42e9282294f8a67690decb2","modified":1631633856066},{"_id":"public/posts/b9bb0bd5.html","hash":"8882192a09fa15f9bb58649c084b61d387dd8587","modified":1631633856066},{"_id":"public/posts/af3c95f0.html","hash":"9d89b51dbee638317273dd94a302bc503f2c9f63","modified":1631633856066},{"_id":"public/posts/bdfc8ea1.html","hash":"3ab6e1c5ca7b13791351c1abba88b9d002919a6f","modified":1631633856066},{"_id":"public/posts/79c0e989.html","hash":"9c9e72fc2d4d01597e00352faadea3d10d85103b","modified":1631633856066},{"_id":"public/posts/52e2375.html","hash":"d03264d3de06bdb89eb0c0a33db3475b1cd1b569","modified":1631633856066},{"_id":"public/posts/374aa28c.html","hash":"301d8688b2c88c5f1ffa1fe435e8732aeb10e65a","modified":1631633856066},{"_id":"public/posts/1a9b2152.html","hash":"be3bcfb2c1a8c4b5e3820e24b61a379e5412f86a","modified":1631633856066},{"_id":"public/posts/4609d38b.html","hash":"907eff716d4015e52f359d9201de10527b8c2593","modified":1631633856066},{"_id":"public/posts/e7ba7594.html","hash":"f8a5d307059fa0d275228864c3eceecccaaf763d","modified":1631633856066},{"_id":"public/posts/345d8e17.html","hash":"cf57b589bc3f9745b92e1d75f66941e6f2da1bce","modified":1631633856066},{"_id":"public/posts/170e1590.html","hash":"6d4845a6fb049215d8f320892387e4993d4deb95","modified":1631633856066},{"_id":"public/posts/c9f91dab.html","hash":"75fe236ecffedfdb0d9ca19dc445b3b7815bbed6","modified":1631633856066},{"_id":"public/posts/c5d59ae.html","hash":"0dbfcdd4761f1020e8bc6a9ed4435fabc2f59353","modified":1631633856066},{"_id":"public/posts/e0bbd322.html","hash":"cf01d97e38f8aa6198854d446c0e0b57fb90bf7f","modified":1631633856066},{"_id":"public/posts/c0626013.html","hash":"928bd3cbdb1292f4a25b0b142367ef3800cfd261","modified":1631633856066},{"_id":"public/posts/9dc8e4af.html","hash":"c912829f4478f39e6d1a2c7de26a6329b0c50dea","modified":1631633856066},{"_id":"public/posts/4f0e7b50.html","hash":"ec56a4d038522edb58d9fef79e3b4c8313de3c8f","modified":1631633856066},{"_id":"public/posts/f5c95c.html","hash":"3878be1111006dfab543cf265d933e10ec3d9b1d","modified":1631633856066},{"_id":"public/posts/ab09b476.html","hash":"53c83764ad4aa611379f4d980349e2b580178ca2","modified":1631633856066},{"_id":"public/posts/b3cec184.html","hash":"625e998ef8e74a970ea998a04baee33b5b59c2b2","modified":1631633856066},{"_id":"public/posts/3aac01ca.html","hash":"58b9c1ace0157361d90d5fd95050a228c4185ad9","modified":1631633856066},{"_id":"public/posts/5e64b19a.html","hash":"0930d5ed8b1496d2da1a7aefea2f79ff04f83a3e","modified":1631633856066},{"_id":"public/posts/38b36403.html","hash":"4c158a9b59335c192ddf512b514c7f59d49cd635","modified":1631633856066},{"_id":"public/posts/94566522.html","hash":"46947a24b755977cb6287517dbc6883f2e29c9aa","modified":1631633856066},{"_id":"public/posts/b54f36d8.html","hash":"15857f756806f2b7b5f68a1dd7db8e18c22cfbda","modified":1631633856066},{"_id":"public/posts/753dfa3e.html","hash":"516213b5208a2e98096a6851eabf579083b5595e","modified":1631633856066},{"_id":"public/posts/63036c3c.html","hash":"d65b1b4675a9a7569fce7f534106de633dfccdc1","modified":1631633856066},{"_id":"public/posts/8a45e6a3.html","hash":"e2e873dfab3d9757d86f87bb78e2571524dd1cd4","modified":1631633856066},{"_id":"public/posts/68f2c599.html","hash":"9d9f42efb5244e6bd19232c59025fe165523ad35","modified":1631633856066},{"_id":"public/posts/c63fec2a.html","hash":"74ec8ba9f848be9fab846e232034eefdd3593ab3","modified":1631633856066},{"_id":"public/posts/52da8eea.html","hash":"c3002fea62a2bacaf72433b2a04c35be31d8f780","modified":1631633856066},{"_id":"public/posts/51a2ab89.html","hash":"312e59ffe8193710fd26a3c9de94dcb91c54cbd9","modified":1631633856066},{"_id":"public/posts/4515cdfc.html","hash":"7055bc1ea114a702b30ba2fb498c476788cb988f","modified":1631633856066},{"_id":"public/posts/52b44ec7.html","hash":"dda522576c0659a99584a597af2ad9302a5cd863","modified":1631633856066},{"_id":"public/archives/index.html","hash":"d63799f0f677d7c83c84d6ca525367a0fef7f0a2","modified":1631633856066},{"_id":"public/archives/page/2/index.html","hash":"97b487f9dc7a26267090e0e0aa0926cd6554f47e","modified":1631633856066},{"_id":"public/archives/page/3/index.html","hash":"9ec1c1771331caff2e752d663843fbf47d9aeec5","modified":1631633856066},{"_id":"public/archives/page/4/index.html","hash":"81fdd2d50278aad738dc6e08854a8383daa1e507","modified":1631633856066},{"_id":"public/archives/page/5/index.html","hash":"9c3714a0b98b49d94468153530deefd13bbae5d7","modified":1631633856066},{"_id":"public/archives/page/6/index.html","hash":"bfb7543d14a274c93e96c69a4bd50f9c32b8d083","modified":1631633856066},{"_id":"public/archives/page/7/index.html","hash":"68b36bd61b4d4964e7d0964862fe9a40dabee5fd","modified":1631633856066},{"_id":"public/archives/page/8/index.html","hash":"5134324fd8056f4ca2a5a89c972dd9a0e1a1bb1f","modified":1631633856066},{"_id":"public/archives/page/9/index.html","hash":"3e0953b96746f3fdefac23c46bc7b92eb9de82c6","modified":1631633856066},{"_id":"public/archives/page/10/index.html","hash":"4bb0eee002c1c0be43e00351f8768cea330393e6","modified":1631633856066},{"_id":"public/archives/page/11/index.html","hash":"243cdbe7c3510b0e67fa837103ef8895bcabcb75","modified":1631633856066},{"_id":"public/archives/2021/index.html","hash":"aed4dff4f7eabba3f598b8366d22cdce7e303aa7","modified":1631633856066},{"_id":"public/archives/2021/page/2/index.html","hash":"8d92a763323c7e1e651817967dbfc04fa349f287","modified":1631633856066},{"_id":"public/archives/2021/page/3/index.html","hash":"0ef2d816fd7dafe857cab558945632323566c9d2","modified":1631633856066},{"_id":"public/archives/2021/page/4/index.html","hash":"6e0523af12e62414b2ee617e6788ceddd292763f","modified":1631633856066},{"_id":"public/archives/2021/page/5/index.html","hash":"9368fa0707de91160d921d3fe526dc8464fe8e9c","modified":1631633856066},{"_id":"public/archives/2021/page/6/index.html","hash":"9096c0a325a74f257d889d8c59811f0570c677e8","modified":1631633856066},{"_id":"public/archives/2021/page/7/index.html","hash":"c145880d3746c753a7568cc9450eca5f141c4e3b","modified":1631633856066},{"_id":"public/archives/2021/page/8/index.html","hash":"13c3c686fb758361db669f05bbc1f796dd72aef0","modified":1631633856066},{"_id":"public/archives/2021/page/9/index.html","hash":"f2c9de409a465c904cef11b8e8eb9c24f4bf44bd","modified":1631633856066},{"_id":"public/archives/2021/page/10/index.html","hash":"fa8c30c321463828963cc63a12ab2b52221bee87","modified":1631633856066},{"_id":"public/archives/2021/page/11/index.html","hash":"a5ed686ffc885a3c30e66532b5a3605bd19dd18a","modified":1631633856066},{"_id":"public/archives/2021/02/page/2/index.html","hash":"d7ba72d3ec237b08868f6da77d0cc9adf03b2a16","modified":1631633856066},{"_id":"public/archives/2021/02/index.html","hash":"0ccee5fa890a0afe16f6fc70269d8bfeb2a802e7","modified":1631633856066},{"_id":"public/archives/2021/02/page/3/index.html","hash":"6234cc5534859f5cb625375d19c50197af44d080","modified":1631633856066},{"_id":"public/archives/2021/04/index.html","hash":"3d2cfbfe660476858f18df5d3ef6b5dbcbc9e5ea","modified":1631633856066},{"_id":"public/archives/2021/05/index.html","hash":"c7e87b5418e60e671c08b3215b11ded7c2ff0bea","modified":1631633856066},{"_id":"public/archives/2021/06/index.html","hash":"520feefe0bbf26618f87443db4f82dacbd1dc588","modified":1631633856066},{"_id":"public/archives/2021/07/index.html","hash":"fae42ce426a1e6b6131ef1e5d978522bada35050","modified":1631633856066},{"_id":"public/archives/2021/07/page/2/index.html","hash":"bf4259c12b5736065d91e88d600cd57a173d8a59","modified":1631633856066},{"_id":"public/archives/2021/08/index.html","hash":"4378387e033c92810936da38d83ef5e7ec2f80e9","modified":1631633856066},{"_id":"public/archives/2021/08/page/2/index.html","hash":"ea75b482085cea17b3c33d90fad21b5c0bdfac51","modified":1631633856066},{"_id":"public/archives/2021/08/page/3/index.html","hash":"9b820787545efebbdef7ad12916ba4e5a9e26c00","modified":1631633856066},{"_id":"public/archives/2021/09/index.html","hash":"1be93dd46feb5ef99ef3c0c1eddbf56150638306","modified":1631633856066},{"_id":"public/archives/2021/09/page/2/index.html","hash":"e0909354dfada8ba1400bba0cdf5793de875df85","modified":1631633856066},{"_id":"public/categories/硬件学习/index.html","hash":"6a513b2a67c272a769bb6878eba5665fc167c0de","modified":1631633856066},{"_id":"public/categories/硬件学习/page/2/index.html","hash":"e9be192a35daa186ca3887a5a4543ac72b8bf49b","modified":1631633856066},{"_id":"public/categories/硬件学习/page/3/index.html","hash":"035bc3639d947d047fdded4dee18e3aef129947f","modified":1631633856066},{"_id":"public/categories/硬件学习/51单片机/index.html","hash":"f7af8ae920e8f5322dc8b98535279adb7d767a8b","modified":1631633856066},{"_id":"public/categories/Hexo/index.html","hash":"41825b4793ba33e31f0a795bfb423cf8739a724e","modified":1631633856066},{"_id":"public/categories/java/index.html","hash":"7c969a7ded506e20259de863de0cc68cd87478dc","modified":1631633856066},{"_id":"public/categories/java/page/2/index.html","hash":"3742eb83c1d8810af0a92ad62995dd1d2c254c50","modified":1631633856066},{"_id":"public/categories/java/page/3/index.html","hash":"db4f3020f34b31b06ed847037ebf4ab51ccf3a53","modified":1631633856066},{"_id":"public/categories/java/page/4/index.html","hash":"366da5957d294c82e392ade1a3db201b442feafb","modified":1631633856066},{"_id":"public/categories/java/page/5/index.html","hash":"2527d33a961f30c84ddf60e9a4409661f982d90e","modified":1631633856066},{"_id":"public/categories/java/page/6/index.html","hash":"bbaf8f7136f06bf796c5b158409e2375e1d48f9c","modified":1631633856066},{"_id":"public/categories/java/page/7/index.html","hash":"9a250026ace06bf39aedfb8590f4b9a9f07c1857","modified":1631633856066},{"_id":"public/categories/java/spring/index.html","hash":"4f42cea914f8cfcee7d4ea4958981d9b8c316067","modified":1631633856066},{"_id":"public/categories/java/spring/page/2/index.html","hash":"d731115057fcfc84b3043a452c0394e38dbc1fe8","modified":1631633856066},{"_id":"public/categories/java/java面试/index.html","hash":"ddb813a2c2a140287becfd75254266db228e3517","modified":1631633856066},{"_id":"public/categories/java/Maven/index.html","hash":"02e396a361742fe8247c33d00b8316c26d729025","modified":1631633856066},{"_id":"public/categories/java/Mybatis/index.html","hash":"52163b938fc3e972740512866de07be376ad2727","modified":1631633856066},{"_id":"public/categories/java/MySQL/index.html","hash":"0cc0c99dae4e1e2a82d9409fac856bd6aff0f754","modified":1631633856066},{"_id":"public/categories/硬件学习/esp8266/index.html","hash":"0bfa22368fd6da59eaabbb2a8c12ba1c89583c3a","modified":1631633856066},{"_id":"public/categories/硬件学习/物联网开发/index.html","hash":"2ed8a94c707cef5e188d594250a312b56c9a101d","modified":1631633856066},{"_id":"public/categories/java/springMVC/index.html","hash":"29c74e1664fb0ff84d5b3ca0f807f4c27965da58","modified":1631633856066},{"_id":"public/categories/java/javaEE/index.html","hash":"94402c66d020a587e916f3239a158cf0e08ccdd3","modified":1631633856066},{"_id":"public/categories/java/javaSE/index.html","hash":"3b5ea63a00cb05334ca9666cc3b07c736b6893e1","modified":1631633856066},{"_id":"public/categories/java/javaSE/page/2/index.html","hash":"c2a80c3af933daaf85b6703e2e3ea4da3cdac239","modified":1631633856066},{"_id":"public/categories/java/javaSE/page/3/index.html","hash":"690743c4d0cd73ecb6589e283232de9386db51b0","modified":1631633856066},{"_id":"public/categories/win10/index.html","hash":"9e86a6126de017f8134c2ec648b2ac36fb149814","modified":1631633856066},{"_id":"public/categories/硬件学习/stm32/index.html","hash":"1a67a33a38c3b171c7351568abb12cae1babc303","modified":1631633856066},{"_id":"public/categories/杂七杂八/index.html","hash":"438a4d1d0df06c74c0ed89264052ce97e913fa62","modified":1631633856066},{"_id":"public/categories/电磁场/index.html","hash":"02ba820d83001fe395004b9c39db5c947d123b67","modified":1631633856066},{"_id":"public/categories/网络通信/index.html","hash":"abafb1eace5d749d0d6fb907f726178201ccb74c","modified":1631633856066},{"_id":"public/categories/网络安全/index.html","hash":"ad7729956076715a209fb189782ec355fd5235b6","modified":1631633856066},{"_id":"public/categories/电磁场/矢量/index.html","hash":"f2a13ddb98d5f868c6c7469e26a7579958d5e6af","modified":1631633856066},{"_id":"public/index.html","hash":"3c7f2494f84dfa0fe16cd98d54ce6472b38e62a4","modified":1631633856066},{"_id":"public/page/2/index.html","hash":"d75c2b63057b80bae4d0174a62e01fff63dbc4b4","modified":1631633856066},{"_id":"public/page/3/index.html","hash":"419ebe03ae47eade03c63a99c43b12b235da77eb","modified":1631633856066},{"_id":"public/page/4/index.html","hash":"318efec312561a848d6fd2acb63d56637223cbb9","modified":1631633856066},{"_id":"public/page/5/index.html","hash":"11cf71182f0b050d05f07a4cf849bfa7985a5bc8","modified":1631633856066},{"_id":"public/page/6/index.html","hash":"2429e3eeac682aaeb4fea5b93c0d9b1fc26cad96","modified":1631633856066},{"_id":"public/page/8/index.html","hash":"0d27acda4c595eff4f073fb76363abd23766dd04","modified":1631633856066},{"_id":"public/page/7/index.html","hash":"cd5ad163189ab7ed849e35817bac94450eb72193","modified":1631633856066},{"_id":"public/page/9/index.html","hash":"3ef6fff0220a9aa0393b7211b82d9db61d6d9383","modified":1631633856066},{"_id":"public/page/10/index.html","hash":"c0bca5813654a92b10dc13bf0b975c820a41c593","modified":1631633856066},{"_id":"public/page/11/index.html","hash":"664f388f714426c7fc44542fd5f2acec530dba7a","modified":1631633856066},{"_id":"public/tags/51单片机/index.html","hash":"0548d7173c6b349dd77ae6ee429ac64b018d0905","modified":1631633856066},{"_id":"public/tags/Hexo/index.html","hash":"213a235becac8754f46baf7e8ff0c7c222e62847","modified":1631633856066},{"_id":"public/tags/Java面试/index.html","hash":"5f8055c1576891d5db2d2d217e27862143d22128","modified":1631633856066},{"_id":"public/tags/Maven/index.html","hash":"9d9adcbc7d74da9265116289163737f36876889e","modified":1631633856066},{"_id":"public/tags/MySQL/index.html","hash":"a177013670eb01837aa1a3de4dad74887b831997","modified":1631633856066},{"_id":"public/tags/spring/index.html","hash":"4dcbf62f17a585ef4a97884db24a0103d31978a8","modified":1631633856066},{"_id":"public/tags/spring/page/2/index.html","hash":"7affd12b7d8bc048a9060926fa909d8efc18e338","modified":1631633856066},{"_id":"public/tags/Mybatis/index.html","hash":"754a292da7ddc8e687974835ab90f8d4be187f74","modified":1631633856066},{"_id":"public/tags/esp8266/index.html","hash":"0264f427ca96d137607c2d682d4ff8778f324115","modified":1631633856066},{"_id":"public/tags/esp8266/page/2/index.html","hash":"629ca0e4fa7183e4af14370db512fd6afbdcb0bb","modified":1631633856066},{"_id":"public/tags/物联网/index.html","hash":"9309f73eb4c1552a0484eb8704b32420ccd4e842","modified":1631633856066},{"_id":"public/tags/springMVC/index.html","hash":"f531a754d87db249e79c5fd5f52b3e04db1c3135","modified":1631633856066},{"_id":"public/tags/Servlet/index.html","hash":"dfcb78a4427f5afb79d184ccfacdb55395aea2d5","modified":1631633856066},{"_id":"public/tags/Tomcat/index.html","hash":"a32b17c0384ec973148ebd31c0e40f841e8af7ea","modified":1631633856066},{"_id":"public/tags/javaSE/page/2/index.html","hash":"a33c49319f09534adb289ae8361f1612668d98e8","modified":1631633856066},{"_id":"public/tags/javaSE/page/3/index.html","hash":"deb72538264430394c65e91c0e5001e8a9939c88","modified":1631633856066},{"_id":"public/tags/javaSE/index.html","hash":"e0f9f158422ed596b1850cfea6bcaefce4d4c8fb","modified":1631633856066},{"_id":"public/tags/Http/index.html","hash":"a0143c82839fd1c5fd160b8e3bf14708e7f1bf28","modified":1631633856066},{"_id":"public/tags/win10/index.html","hash":"5c10df9f6c64f89352297e573b16277067e870af","modified":1631633856066},{"_id":"public/tags/MDK-keil5/index.html","hash":"9fc5a415ee288ed150092f6166684c6c7647a233","modified":1631633856066},{"_id":"public/tags/stm32/index.html","hash":"2a3cac3efadf3e057293efd9e4a5c0f2fabc399a","modified":1631633856066},{"_id":"public/tags/图床/index.html","hash":"038ae4bea02f30e45606f15a1fc3b91d8c955aaf","modified":1631633856066},{"_id":"public/tags/矢量/index.html","hash":"4dc07818b6f6ce8d5d971cd6802c6b3a5d2b26c7","modified":1631633856066},{"_id":"public/tags/网络安全/index.html","hash":"e4ce6e5fbbd10007db75288b9f3e3fb0e5fa599a","modified":1631633856066},{"_id":"public/tags/网络通信/index.html","hash":"e54eabdca13bb05f903b757364afaf24232e56b0","modified":1631633856066},{"_id":"public/img/404.jpg","hash":"fb4489bc1d30c93d28f7332158c1c6c1416148de","modified":1631633856066},{"_id":"public/img/algolia.svg","hash":"ec119560b382b2624e00144ae01c137186e91621","modified":1631633856066},{"_id":"public/img/articles.png","hash":"f6654630e0f6f18497475b34e607f63a79f09140","modified":1631633856066},{"_id":"public/img/articles2.png","hash":"44f2e90e928bf1ae3fd91a85b70b5b1d07a91519","modified":1631633856066},{"_id":"public/img/avatar1.jpg","hash":"466d1b7cd0573e57e7db294c5f63c55ee6ca4296","modified":1631633856066},{"_id":"public/img/avatar2.jpg","hash":"4a7143ede9561ace432c2ac963b406428b296756","modified":1631633856066},{"_id":"public/img/favicon.png","hash":"3cf89864b4f6c9b532522a4d260a2e887971c92d","modified":1631633856066},{"_id":"public/img/friend_404.gif","hash":"8d2d0ebef70a8eb07329f57e645889b0e420fa48","modified":1631633856066},{"_id":"public/img/huiji.png","hash":"f33b6927a0124ce7ad10638b96ee3be9b55c555e","modified":1631633856066},{"_id":"public/img/mybatis.png","hash":"1e685804796630baba037812e3beab62936e1227","modified":1631633856066},{"_id":"public/img/mybatis1.png","hash":"d40a9abf2799be2e830868845e7287e884d8688d","modified":1631633856066},{"_id":"public/img/pcbimg.png","hash":"72afcf1951d00c206d235facf6e09d16802c6ba1","modified":1631633856066},{"_id":"public/img/pcbimg2.png","hash":"511c4a15ad0f382adcbd3e1ff86dca3ead5d2df1","modified":1631633856066},{"_id":"public/img/plane.png","hash":"fcd0b34a638838dcae8cbf17f34c51f973a0c02b","modified":1631633856066},{"_id":"public/img/plane1.png","hash":"c5713f3d92ddcd45d2b9a494f78b0f5b0e4a7466","modified":1631633856066},{"_id":"public/live2d-widget/LICENSE","hash":"1eba7caf09a39110ad2f542e3ed8700d1a69c6d3","modified":1631633856066},{"_id":"public/mouse/AppStarting.ani","hash":"e334f5489e63167adbd44f1116ffcfc72e84f718","modified":1631633856066},{"_id":"public/mouse/Arrow.cur","hash":"1ab6471c728291cf66ba813b09135d48a06e74c3","modified":1631633856066},{"_id":"public/mouse/Cross.cur","hash":"6d6d6f975164d58db643a84ed238375fcbdac98d","modified":1631633856066},{"_id":"public/mouse/Hand.cur","hash":"46d6d4e0a8a79e054df35bcee1a53bb3c4b1ec8b","modified":1631633856066},{"_id":"public/mouse/Handwriting.cur","hash":"195c938abc7f3e6d1f2e6301c46ba1e42ff99a4f","modified":1631633856066},{"_id":"public/mouse/Help.cur","hash":"bf59506b7a65e2a5bb8c07b9b3c8daa6b90b0b7d","modified":1631633856066},{"_id":"public/mouse/IBeam.cur","hash":"c96a55e806892deb9c1866c1aa33f98627d6dadf","modified":1631633856066},{"_id":"public/mouse/SizeAll.cur","hash":"b8ccca4354a8c11d61b7281818004933bdc32e01","modified":1631633856066},{"_id":"public/mouse/NO.cur","hash":"709b03659cd3be203e9d1c449d675e866847b957","modified":1631633856066},{"_id":"public/mouse/SizeNWSE.cur","hash":"25666d52fb83373286dc0b815387be41a52a26ac","modified":1631633856066},{"_id":"public/mouse/SizeNESW.cur","hash":"253f4e4c0da9b85757596c09187a79204fd9f07b","modified":1631633856066},{"_id":"public/mouse/SizeWE.cur","hash":"915ae8029726e0cf7b8f523fff059a96148cfc5c","modified":1631633856066},{"_id":"public/mouse/UpArrow.cur","hash":"2e65c3437b457ddf6429a096d5921ea962703843","modified":1631633856066},{"_id":"public/mouse/SizeNS.cur","hash":"4c02aa53675e758f61de6e13a5cf764b41e129f5","modified":1631633856066},{"_id":"public/live2d-widget/assets/screenshot-1.png","hash":"4bd4d97a1fcaa5deb3cea1c0a102a895a15af32f","modified":1631633856066},{"_id":"public/assets/algolia/algoliasearchLite.min.js","hash":"284416885e4e80e27fa4eae6fc305f4de15b914c","modified":1631633856066},{"_id":"public/fonts/JetBrainsMono-Medium.woff2","hash":"f31bdf436d68c69503e96e7ca691a90ffa4918a8","modified":1631633856066},{"_id":"public/mouse/Wait.ani","hash":"5a7278ded553b61a248ec549b9964b7e2930cecf","modified":1631633856066},{"_id":"public/assets/algolia/algoliasearchLite.js","hash":"e56ad6b82caf69066de545201014291fc961635e","modified":1631633856066},{"_id":"public/assets/algolia/algoliasearch.min.js","hash":"a3b131a9a47ccc16f4dd8988fabb6d306548db2f","modified":1631633856066},{"_id":"public/css/copyright.css","hash":"45bc0d0d4889daabae8b6e0c129617fcc212b77b","modified":1631633856066},{"_id":"public/css/background.css","hash":"573aeea20ddcc77fa2b835ebba66e40b90635fb3","modified":1631633856066},{"_id":"public/css/custom.css","hash":"43a20c653bba7892c704bf7654d60cfdcfe4b40d","modified":1631633856066},{"_id":"public/css/icon.css","hash":"d93c1e972e3569c9a08fedf3f54a1fe28f2b036f","modified":1631633856066},{"_id":"public/css/mouse.css","hash":"72d8fe42bc9b8896dabcbd163323841d6ee565a8","modified":1631633856066},{"_id":"public/css/side.css","hash":"770be1a4d7eed917770462cc8d361ead355985c5","modified":1631633856066},{"_id":"public/css/var.css","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1631633856066},{"_id":"public/js/utils.js","hash":"8319b59c26ce8cd2b0ae7d030c4912215148fa92","modified":1631633856066},{"_id":"public/live2d-widget/README.html","hash":"f4d5ae45aedcc8a1e6661dd7db64ca241c3758a5","modified":1631633856066},{"_id":"public/live2d-widget/autoload.js","hash":"e6540c988c9c2d6f57fabe4073469042fd4e088c","modified":1631633856066},{"_id":"public/live2d-widget/waifu-tips.js","hash":"f8bdc91fa1e51d46dc18e04174c63034d68299bf","modified":1631633856066},{"_id":"public/live2d-widget/waifu-tips.json","hash":"a3f9d4d832cd0948cd21385e7e8c62db377ba7d4","modified":1631633856066},{"_id":"public/live2d-widget/waifu.css","hash":"0613696628c20499c6f72bcec6b6780594443b0d","modified":1631633856066},{"_id":"public/js/search/algolia.js","hash":"65b45e61586f7e66c3f338370bfd9daadd71a4b7","modified":1631633856066},{"_id":"public/js/search/local-search.js","hash":"459e2541afda483916d16fce4aaa56b41bcd42ba","modified":1631633856066},{"_id":"public/live2d-widget/demo/login.html","hash":"9512df47f6516c0013153095693cba1992744f4b","modified":1631633856066},{"_id":"public/live2d-widget/demo/demo.html","hash":"2617b8aa4c4728d38a8f062465304e5433056abc","modified":1631633856066},{"_id":"public/css/index.css","hash":"09267c4348aec68b0104ec3ec2abb3fb5f1d067c","modified":1631633856066},{"_id":"public/live2d-widget/assets/screenshot-3.png","hash":"f89c6fdf44008e7d3c7a1aafe65dc39b212edb6c","modified":1631633856066},{"_id":"public/js/tw_cn.js","hash":"00053ce73210274b3679f42607edef1206eebc68","modified":1631633856066},{"_id":"public/js/main.js","hash":"8ef2821ceb92d81aa0f8c02ee932f094adcafb2b","modified":1631633856066},{"_id":"public/assets/algolia/algoliasearch.js","hash":"6948fcdf071e4983e784e8c458cf201536f77792","modified":1631633856066},{"_id":"public/css/index.min.css","hash":"fbc05a25b9ffcb535debd43ba244c96e909498c1","modified":1631633856066},{"_id":"public/live2d-widget/assets/screenshot-2.png","hash":"76b844653136184c573b3632e12296e008a7ff5c","modified":1631633856066},{"_id":"public/img/02.jpg","hash":"52276fbc7427eaf3facebf1e7cf9d99dad1664e8","modified":1631633856066},{"_id":"public/img/banner.png","hash":"e0b6d5d65d72bdfeb1b1bcfd1af340088d50fdfb","modified":1631633856066},{"_id":"public/img/01.jpg","hash":"455aff642579a632713b90c58edcd89f1ea01aef","modified":1631633856066},{"_id":"public/img/34.jpg","hash":"10b2ddfbe7f03c4513ace7a2855a47f907478d55","modified":1631633856066},{"_id":"public/img/blissed01.jpg","hash":"932db4b0cb8a297a4f9c1fd48af59183db742584","modified":1631633856066},{"_id":"public/live2d-widget/live2d.min.js","hash":"94383fb61de815c3f806ae3f11026bbcfe384082","modified":1631633856066},{"_id":"public/img/categories.png","hash":"1829d3667c9fb36d729057a2fb26f12b662c86e4","modified":1631633856066},{"_id":"public/fonts/loving.ttf","hash":"786ab7558a8a2cd5de1ac71a1c6c19bfeff17e95","modified":1631633856066},{"_id":"public/fonts/shishangblack.ttf","hash":"b5dda0beb2618829e41553d0dc656afbc78af523","modified":1631633856066},{"_id":"public/fonts/ajLangMan.ttf","hash":"c79e63ca78049f8273c0de7b5c65898c1dd8557f","modified":1631633856066},{"_id":"public/fonts/ZhuZiAWan.woff2","hash":"a8512c6d5d70f5fc098a58a01a0ad720aa211c2a","modified":1631633856066}],"Category":[{"name":"硬件学习","_id":"cktk8o6o10004akve0nho022p"},{"name":"51单片机","parent":"cktk8o6o10004akve0nho022p","_id":"cktk8o6oe000sakvef4qjae8o"},{"name":"Hexo","_id":"cktk8o6ov001sakve83h63fm7"},{"name":"java","_id":"cktk8o6pc002lakve2iyjem8n"},{"name":"spring","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qe0055akvegob69vva"},{"name":"java面试","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qh005bakveacjzeuio"},{"name":"Maven","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qs0068akve66aj2w3e"},{"name":"Mybatis","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r30076akve2siqh8cu"},{"name":"MySQL","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r5007eakve6g5i7k0f"},{"name":"esp8266","parent":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rl008wakveg62hemot"},{"name":"物联网开发","parent":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rz00a5akvehafeesla"},{"name":"springMVC","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6s900beakvegzr10ttv"},{"name":"javaEE","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sj00cnakvedfi1cpk5"},{"name":"javaSE","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sq00dhakve6e73glbu"},{"name":"win10","_id":"cktk8o6td00fzakve55x24m4x"},{"name":"stm32","parent":"cktk8o6o10004akve0nho022p","_id":"cktk8o6te00g7akvedfi64j0n"},{"name":"杂七杂八","_id":"cktk8o6th00gkakve2e1eb0kc"},{"name":"电磁场","_id":"cktk8o6th00goakve1pju20c4"},{"name":"网络通信","_id":"cktk8o6th00grakve7iws2msc"},{"name":"网络安全","_id":"cktk8o6ti00gtakve51ks4u05"},{"name":"矢量","parent":"cktk8o6th00goakve1pju20c4","_id":"cktk8o6ti00gwakve0x2ufj97"}],"Data":[{"_id":"link","data":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}],"Page":[{"_content":"{\n \"name\": \"string\",\n \"short_name\": \"Junzhou\",\n \"theme_color\": \"#49b1f5\",\n \"background_color\": \"#49b1f5\",\n \"display\": \"standalone\",\n \"scope\": \"/\",\n \"start_url\": \"/\",\n \"icons\": [\n {\n \"src\": \"images/pwaicons/36.png\",\n \"sizes\": \"36x36\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/48.png\",\n \"sizes\": \"48x48\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/72.png\",\n \"sizes\": \"72x72\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/96.png\",\n \"sizes\": \"96x96\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/144.png\",\n \"sizes\": \"144x144\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/192.png\",\n \"sizes\": \"192x192\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/512.png\",\n \"sizes\": \"512x512\",\n \"type\": \"image/png\"\n }\n ],\n \"splash_pages\": null\n }","source":"manifest.json","raw":"{\n \"name\": \"string\",\n \"short_name\": \"Junzhou\",\n \"theme_color\": \"#49b1f5\",\n \"background_color\": \"#49b1f5\",\n \"display\": \"standalone\",\n \"scope\": \"/\",\n \"start_url\": \"/\",\n \"icons\": [\n {\n \"src\": \"images/pwaicons/36.png\",\n \"sizes\": \"36x36\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/48.png\",\n \"sizes\": \"48x48\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/72.png\",\n \"sizes\": \"72x72\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/96.png\",\n \"sizes\": \"96x96\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/144.png\",\n \"sizes\": \"144x144\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/192.png\",\n \"sizes\": \"192x192\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/512.png\",\n \"sizes\": \"512x512\",\n \"type\": \"image/png\"\n }\n ],\n \"splash_pages\": null\n }","date":"2021-09-14T09:24:37.372Z","updated":"2021-09-14T09:24:37.371Z","path":"manifest.json","layout":"false","title":"","comments":1,"_id":"cktk8o6nt0000akvebb85cs9q","content":"{\"name\":\"string\",\"short_name\":\"Junzhou\",\"theme_color\":\"#49b1f5\",\"background_color\":\"#49b1f5\",\"display\":\"standalone\",\"scope\":\"/\",\"start_url\":\"/\",\"icons\":[{\"src\":\"images/pwaicons/36.png\",\"sizes\":\"36x36\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/48.png\",\"sizes\":\"48x48\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/72.png\",\"sizes\":\"72x72\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/96.png\",\"sizes\":\"96x96\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/144.png\",\"sizes\":\"144x144\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/192.png\",\"sizes\":\"192x192\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/512.png\",\"sizes\":\"512x512\",\"type\":\"image/png\"}],\"splash_pages\":null}","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"{\"name\":\"string\",\"short_name\":\"Junzhou\",\"theme_color\":\"#49b1f5\",\"background_color\":\"#49b1f5\",\"display\":\"standalone\",\"scope\":\"/\",\"start_url\":\"/\",\"icons\":[{\"src\":\"images/pwaicons/36.png\",\"sizes\":\"36x36\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/48.png\",\"sizes\":\"48x48\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/72.png\",\"sizes\":\"72x72\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/96.png\",\"sizes\":\"96x96\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/144.png\",\"sizes\":\"144x144\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/192.png\",\"sizes\":\"192x192\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/512.png\",\"sizes\":\"512x512\",\"type\":\"image/png\"}],\"splash_pages\":null}"},{"title":"关于","date":"2021-08-20T14:32:50.000Z","type":"about","_content":"\n2021暑假利用Hexo框架,使用butterfly主题,参考网上大神们做的个人博客,用来做笔记。。。\n\n\n\n嗯。。。。。。。\n就这样!!!!!","source":"about/index.md","raw":"---\ntitle: 关于\ndate: 2021-08-20 22:32:50\ntype: \"about\"\n---\n\n2021暑假利用Hexo框架,使用butterfly主题,参考网上大神们做的个人博客,用来做笔记。。。\n\n\n\n嗯。。。。。。。\n就这样!!!!!","updated":"2021-08-20T14:37:08.214Z","path":"about/index.html","comments":1,"layout":"page","_id":"cktk8o6ny0002akve6dacfapw","content":"
2021暑假利用Hexo框架,使用butterfly主题,参考网上大神们做的个人博客,用来做笔记。。。
\n嗯。。。。。。。 就这样!!!!!
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":"2021暑假利用Hexo框架,使用butterfly主题,参考网上大神们做的个人博客,用来做笔记。。。
\n嗯。。。。。。。 就这样!!!!!
\n"},{"title":"友情链接","date":"2021-08-17T05:12:28.000Z","type":"link","_content":"","source":"link/index.md","raw":"---\ntitle: 友情链接\ndate: 2021-08-17 13:12:28\ntype: link\n---\n","updated":"2021-08-17T05:12:52.461Z","path":"link/index.html","comments":1,"layout":"page","_id":"cktk8o6o20006akve6elihxpj","content":"","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":""},{"title":"音乐","date":"2021-08-17T13:17:27.000Z","type":"music","_content":"\n\n{% tabs 音乐, -1 %}\n\n[网易云音乐-我的音乐](https://music.163.com/#/my/m/music/playlist?id=5322139721)\n\n{% folding 歌单1 %}\n{% meting \"6925236447\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n{% folding 歌单2021-09 %}\n{% meting \"6952136420\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n{% folding 歌单2021-08 %}\n{% meting \"6916454908\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n{% folding 歌单2021-06 %}\n{% meting \"6826165381\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-05 %}\n{% meting \"6749189366\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-04[2] %}\n{% meting \"6718667049\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n{% folding 歌单2021-04[1] %}\n{% meting \"6678848086\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-03%}\n{% meting \"6649685822\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[酷狗音乐](https://www.kugou.com/)\n\n{% folding 大杂烩%}\n{% meting \"37118256\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" %}\n{% meting \"38928927\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" %}\n{% meting \"12869806\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[QQ音乐](https://y.qq.com/)\n\nQQ音乐的音乐id获取:点击分享歌曲获取链接,在链接中的songid就是\n\n{% folding 歌单1%}\n{% meting \"8133710813\" \"tencent\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[酷我音乐](http://www.kuwo.cn/)\n\n{% folding 展开%}\n\n\n\n{% endfolding %}\n\n\n\n\n\n\n芳华慢-封茗囧菌\n{% audio https://webfs.ali.kugou.com/202109120929/fdccfb42189820d5c6cac2e9ec9f7e8b/KGTX/CLTX001/4e38a814fe40ae680ab67ced203487a5.mp3 %}\n\n\n\n{% endtabs %}\n\n\n\n\n\n\n","source":"music/index.md","raw":"---\ntitle: 音乐\ndate: 2021-08-17 21:17:27\ntype: \"music\"\n---\n\n\n{% tabs 音乐, -1 %}\n\n[网易云音乐-我的音乐](https://music.163.com/#/my/m/music/playlist?id=5322139721)\n\n{% folding 歌单1 %}\n{% meting \"6925236447\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n{% folding 歌单2021-09 %}\n{% meting \"6952136420\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n{% folding 歌单2021-08 %}\n{% meting \"6916454908\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n{% folding 歌单2021-06 %}\n{% meting \"6826165381\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-05 %}\n{% meting \"6749189366\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-04[2] %}\n{% meting \"6718667049\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n{% folding 歌单2021-04[1] %}\n{% meting \"6678848086\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-03%}\n{% meting \"6649685822\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[酷狗音乐](https://www.kugou.com/)\n\n{% folding 大杂烩%}\n{% meting \"37118256\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" %}\n{% meting \"38928927\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" %}\n{% meting \"12869806\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[QQ音乐](https://y.qq.com/)\n\nQQ音乐的音乐id获取:点击分享歌曲获取链接,在链接中的songid就是\n\n{% folding 歌单1%}\n{% meting \"8133710813\" \"tencent\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[酷我音乐](http://www.kuwo.cn/)\n\n{% folding 展开%}\n\n\n\n{% endfolding %}\n\n\n\n\n\n\n芳华慢-封茗囧菌\n{% audio https://webfs.ali.kugou.com/202109120929/fdccfb42189820d5c6cac2e9ec9f7e8b/KGTX/CLTX001/4e38a814fe40ae680ab67ced203487a5.mp3 %}\n\n\n\n{% endtabs %}\n\n\n\n\n\n\n","updated":"2021-09-12T08:52:58.221Z","path":"music/index.html","comments":1,"layout":"page","_id":"cktk8o6o40008akve8p3pgh18","content":"网易云音乐-我的音乐
\n
歌单1 \n \n \n\n
歌单2021-09 \n \n \n\n
歌单2021-08 \n \n \n\n\n
歌单2021-06 \n \n \n\n\n\n
歌单2021-05 \n \n \n\n\n\n
歌单2021-04[2] \n \n \n\n\n
歌单2021-04[1] \n \n \n\n\n\n
歌单2021-03 \n \n QQ音乐
\n
QQ音乐的音乐id获取:点击分享歌曲获取链接,在链接中的songid就是
\n
歌单1 \n \n 酷我音乐 <!— <div id=”aplayer-jyZgFIvs” class=”aplayer aplayer-tag-marker meting-tag-marker” data-id=”72510362” data-server=”kuwo” data-type=”yinyue” data-mode=”circulation” data-autoplay=”false” data-mutex=”true” data-listmaxheight=”340px” data-preload=”auto” data-theme=”#3F51B5”
\n
></div> -->\n
展开 \n \n \n
\n 芳华慢-封茗囧菌
\n
Your browser does not support the audio tag. \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"网易云音乐-我的音乐
\n
歌单1 \n \n \n\n
歌单2021-09 \n \n \n\n
歌单2021-08 \n \n \n\n\n
歌单2021-06 \n \n \n\n\n\n
歌单2021-05 \n \n \n\n\n\n
歌单2021-04[2] \n \n \n\n\n
歌单2021-04[1] \n \n \n\n\n\n
歌单2021-03 \n \n QQ音乐
\n
QQ音乐的音乐id获取:点击分享歌曲获取链接,在链接中的songid就是
\n
歌单1 \n \n 酷我音乐 <!— <div id=”aplayer-jyZgFIvs” class=”aplayer aplayer-tag-marker meting-tag-marker” data-id=”72510362” data-server=”kuwo” data-type=”yinyue” data-mode=”circulation” data-autoplay=”false” data-mutex=”true” data-listmaxheight=”340px” data-preload=”auto” data-theme=”#3F51B5”
\n
></div> -->\n
展开 \n \n \n
\n 芳华慢-封茗囧菌
\n
Your browser does not support the audio tag. \n"},{"title":"分类","date":"2021-08-17T05:10:35.000Z","type":"categories","_content":"","source":"categories/index.md","raw":"---\ntitle: 分类\ndate: 2021-08-17 13:10:35\ntype: \"categories\"\n# top_img: false\n---\n","updated":"2021-08-17T11:26:53.838Z","path":"categories/index.html","comments":1,"layout":"page","_id":"cktk8o6o5000aakve8jlidwd2","content":"","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":""},{"title":"笔记","date":"2021-09-01T00:27:26.000Z","type":"notes","_content":"","source":"notes/index.md","raw":"---\ntitle: 笔记\ndate: 2021-09-01 08:27:26\ntype: \"notes\"\n---\n","updated":"2021-09-01T00:28:37.545Z","path":"notes/index.html","comments":1,"layout":"page","_id":"cktk8o6o9000fakveeaeogkyt","content":"","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":""},{"title":"标签","date":"2021-08-17T05:11:25.000Z","type":"tags","_content":"","source":"tags/index.md","raw":"---\ntitle: 标签\ndate: 2021-08-17 13:11:25\ntype: \"tags\"\n---\n","updated":"2021-08-17T05:11:46.636Z","path":"tags/index.html","comments":1,"layout":"page","_id":"cktk8o6oa000iakve6bb9guiq","content":"","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":""},{"title":"诗歌","date":"2021-09-01T05:19:34.000Z","type":"poems","_content":"\n{% folding blue, 水调歌头 %}\n{% poem 水调歌头,苏轼 %}\n丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。\n明月几时有?把酒问青天。\n不知天上宫阙,今夕是何年?\n我欲乘风归去,又恐琼楼玉宇,高处不胜寒。\n起舞弄清影,何似在人间?\n\n转朱阁,低绮户,照无眠。\n不应有恨,何事长向别时圆?\n人有悲欢离合,月有阴晴圆缺,此事古难全。\n但愿人长久,千里共婵娟。\n{% endpoem %}\n{%endfolding%}\n\n{% folding blue, 古诗十九首 %}\n{% poem 迢迢牵牛星 %}\n迢迢牵牛星,皎皎河汉女。\n纤纤擢素手,札札弄机杼。\n终日不成章,泣涕零如雨。\n河汉清且浅,相去复几许?\n盈盈一水间,脉脉不得语。\n{% endpoem %}\n\n{% poem 行行重行行 %}\n行行重行行,与君生别离。\n相去万余里,各在天一涯。\n道路阻且长,会面安可知?\n胡马依北风,越鸟巢南枝。\n相去日已远,衣带日已缓。\n浮云蔽白日,游子不顾反。\n思君令人老,岁月忽已晚。\n弃捐勿复道,努力加餐饭\n{% endpoem %}\n\n\n{%endfolding%}\n\n","source":"poems/index.md","raw":"---\ntitle: 诗歌\ndate: 2021-09-01 13:19:34\ntype: \"poems\"\n---\n\n{% folding blue, 水调歌头 %}\n{% poem 水调歌头,苏轼 %}\n丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。\n明月几时有?把酒问青天。\n不知天上宫阙,今夕是何年?\n我欲乘风归去,又恐琼楼玉宇,高处不胜寒。\n起舞弄清影,何似在人间?\n\n转朱阁,低绮户,照无眠。\n不应有恨,何事长向别时圆?\n人有悲欢离合,月有阴晴圆缺,此事古难全。\n但愿人长久,千里共婵娟。\n{% endpoem %}\n{%endfolding%}\n\n{% folding blue, 古诗十九首 %}\n{% poem 迢迢牵牛星 %}\n迢迢牵牛星,皎皎河汉女。\n纤纤擢素手,札札弄机杼。\n终日不成章,泣涕零如雨。\n河汉清且浅,相去复几许?\n盈盈一水间,脉脉不得语。\n{% endpoem %}\n\n{% poem 行行重行行 %}\n行行重行行,与君生别离。\n相去万余里,各在天一涯。\n道路阻且长,会面安可知?\n胡马依北风,越鸟巢南枝。\n相去日已远,衣带日已缓。\n浮云蔽白日,游子不顾反。\n思君令人老,岁月忽已晚。\n弃捐勿复道,努力加餐饭\n{% endpoem %}\n\n\n{%endfolding%}\n\n","updated":"2021-09-01T11:21:57.769Z","path":"poems/index.html","comments":1,"layout":"page","_id":"cktk8o6oc000nakve8s3d8v6m","content":" 水调歌头 \n \n
水调歌头
苏轼
丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。 明月几时有?把酒问青天。 不知天上宫阙,今夕是何年? 我欲乘风归去,又恐琼楼玉宇,高处不胜寒。 起舞弄清影,何似在人间?
转朱阁,低绮户,照无眠。 不应有恨,何事长向别时圆? 人有悲欢离合,月有阴晴圆缺,此事古难全。 但愿人长久,千里共婵娟。
\n
\n \n 古诗十九首 \n \n
迢迢牵牛星
迢迢牵牛星,皎皎河汉女。 纤纤擢素手,札札弄机杼。 终日不成章,泣涕零如雨。 河汉清且浅,相去复几许? 盈盈一水间,脉脉不得语。
行行重行行
行行重行行,与君生别离。 相去万余里,各在天一涯。 道路阻且长,会面安可知? 胡马依北风,越鸟巢南枝。 相去日已远,衣带日已缓。 浮云蔽白日,游子不顾反。 思君令人老,岁月忽已晚。 弃捐勿复道,努力加餐饭
\n
\n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":" 水调歌头 \n \n
水调歌头
苏轼
丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。 明月几时有?把酒问青天。 不知天上宫阙,今夕是何年? 我欲乘风归去,又恐琼楼玉宇,高处不胜寒。 起舞弄清影,何似在人间?
转朱阁,低绮户,照无眠。 不应有恨,何事长向别时圆? 人有悲欢离合,月有阴晴圆缺,此事古难全。 但愿人长久,千里共婵娟。
\n
\n \n 古诗十九首 \n \n
迢迢牵牛星
迢迢牵牛星,皎皎河汉女。 纤纤擢素手,札札弄机杼。 终日不成章,泣涕零如雨。 河汉清且浅,相去复几许? 盈盈一水间,脉脉不得语。
行行重行行
行行重行行,与君生别离。 相去万余里,各在天一涯。 道路阻且长,会面安可知? 胡马依北风,越鸟巢南枝。 相去日已远,衣带日已缓。 浮云蔽白日,游子不顾反。 思君令人老,岁月忽已晚。 弃捐勿复道,努力加餐饭
\n
\n \n"},{"title":"网站链接","date":"2021-09-01T01:38:54.000Z","type":"web_links","_content":"\n\n{% folding blue, 工具网站 %}\n\n\n\n\n1. [百度脑图-便捷的思维工具](https://naotu.baidu.com/home)\n2. [【GitMind官网】- 免费在线思维导图软件](https://gitmind.cn/)\n3. [程序员工具 - 聚够网](http://tool.ggo.net/)\n4. [程序员常用工具箱 -TOOLFK](https://www.toolfk.com/)\n5. [json解析](http://sojson.com/)\n6. [微词云 - 制作 - 设计页](https://www.weiciyun.com/u/create#/?k=kecplc6u)\n7. [在线生成透明ICO图标——ICO图标制作](http://www.ico51.cn/)\n8. [math-latex](https://webdemo.myscript.com/views/math/index.html#)\n9. [LaTeX在线:吴文中数学公式编辑器](https://latex.91maths.com/)\n10. [638+ 演示文稿模板 - 模板素材库 - Canva可画](https://www.canva.cn/templates/search/5ryU56S65paH56i_X64/)\n11. [ppt模板 - 学堂PPT](https://xtppt.cn/PPTMB/YQPPT/1792.html)\n12. [JetBrains](https://www.jetbrains.com/)\n13. [GitHub 下载文件加速](https://gh.api.99988866.xyz/)\n14. [博客文章图片生成](https://nav.rdonly.com/laboratory/bgimage/backimage.html)\n15. [徽标生成工具](https://shields.io/)\n\n\n{% endfolding %}","source":"web-links/index.md","raw":"---\ntitle: 网站链接\ndate: 2021-09-01 09:38:54\ntype: \"web_links\"\n---\n\n\n{% folding blue, 工具网站 %}\n\n\n\n\n1. [百度脑图-便捷的思维工具](https://naotu.baidu.com/home)\n2. [【GitMind官网】- 免费在线思维导图软件](https://gitmind.cn/)\n3. [程序员工具 - 聚够网](http://tool.ggo.net/)\n4. [程序员常用工具箱 -TOOLFK](https://www.toolfk.com/)\n5. [json解析](http://sojson.com/)\n6. [微词云 - 制作 - 设计页](https://www.weiciyun.com/u/create#/?k=kecplc6u)\n7. [在线生成透明ICO图标——ICO图标制作](http://www.ico51.cn/)\n8. [math-latex](https://webdemo.myscript.com/views/math/index.html#)\n9. [LaTeX在线:吴文中数学公式编辑器](https://latex.91maths.com/)\n10. [638+ 演示文稿模板 - 模板素材库 - Canva可画](https://www.canva.cn/templates/search/5ryU56S65paH56i_X64/)\n11. [ppt模板 - 学堂PPT](https://xtppt.cn/PPTMB/YQPPT/1792.html)\n12. [JetBrains](https://www.jetbrains.com/)\n13. [GitHub 下载文件加速](https://gh.api.99988866.xyz/)\n14. [博客文章图片生成](https://nav.rdonly.com/laboratory/bgimage/backimage.html)\n15. [徽标生成工具](https://shields.io/)\n\n\n{% endfolding %}","updated":"2021-09-01T02:05:12.181Z","path":"web-links/index.html","comments":1,"layout":"page","_id":"cktk8o6od000qakveak0t3zck","content":" 工具网站 \n \n ","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":" 工具网站 \n \n "}],"Post":[{"title":"51单片机学习笔记(一)","description":"点亮第一个led灯、流水灯、蜂鸣器、数码管、按键","abbrlink":"9e0a8624","date":"2021-08-17T16:00:00.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n## 点亮第一个led灯\n\n\n\n1. 新建工程\n\n\t![image-20210821203338653](https://gitee.com/ajream/images/raw/master/img/20210821203342_image-20210821203338653.png)\n\n2. 选择固件\n\n\t![image-20210821203543242](https://gitee.com/ajream/images/raw/master/img/20210821203544_image-20210821203543242.png)\n\n\t![image-20210821203717166](https://gitee.com/ajream/images/raw/master/img/20210821203718_image-20210821203717166.png)\n\n3. 新建代码源文件\n\n\t![image-20210821203932209](https://gitee.com/ajream/images/raw/master/img/20210821203933_image-20210821203932209.png)\n\n\t![image-20210821204130367](https://gitee.com/ajream/images/raw/master/img/20210821204131_image-20210821204130367.png)\n\n\t![image-20210821204227781](https://gitee.com/ajream/images/raw/master/img/20210821204229_image-20210821204227781.png)\n\n4. 添加如下代码\n\n\t```c\n\t#include //引用51头文件\n\t\n\tsbit LED = P1^1;\n\t\n\tvoid main()\n\t{\n\t\tLED = 0;\t//点亮LED2\t\n\t}\n\t```\n5. 编译前要先配置,让编译器在编译完成后输出hex文件,hex文件用来烧录进单片机里面\n\n ![image-20210821204723747](https://gitee.com/ajream/images/raw/master/img/20210821204724_image-20210821204723747.png)\n6. 编译\n\n ![image-20210821205109242](https://gitee.com/ajream/images/raw/master/img/20210821205110_image-20210821205109242.png)\n\n7. 上传(烧录)\n\n ![image-20210821205434569](https://gitee.com/ajream/images/raw/master/img/20210821205435_image-20210821205434569.png)\n\n\n\n到此第一个点灯小程序结束\n\n\n\n\n\n## 流水灯\n\n\n\n按照之前的步骤再建立一个新工程,写下如下代码:\n\n```c\n#include \t //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nuchar temp; //LED灯相关变量\n\n//延时函数\nvoid delay(uint z)\n{\n\tuint x, y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()\n{\n\ttemp = 0xfe;\n\tP1 = temp; //1111 1110 初值LED1亮\n\tdelay(1000);//毫秒级延时 100毫秒\n\twhile(1)\n\t{\n\t\ttemp = _cror_(temp, 1);//循环左移\n\t\tP1 = temp;//移位完成后赋值给P1 每个一个灯点亮\n\t\tdelay(1000);//毫秒级延时 100毫秒\n\t}\t\n} \n```\n\n说明:\n\n- `intrins.h` 头文件包含移位函数 :`右移_cror_()`、 `左移_corl_()`\n\n\n\n## 蜂鸣器\n\n蜂鸣器分有源与无源蜂鸣器\n\n- 有源指内部自带震荡源,只要提供电压即可响,但由于其震荡源震动频率固定,因此只能产生一种频率的声音\n- 无源蜂鸣器指内部没有震荡源,需要提供变化的电压(方波)才能让其响\n\n一个蜂鸣器的简单驱动电路:\n\n![image-20210821210804064](https://gitee.com/ajream/images/raw/master/img/20210821210805_image-20210821210804064.png)\n\n```c\n#include \t//包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n\nsbit beep = P2^3;//蜂鸣器\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()\n{\n\twhile(1)\n\t{\n\t\tbeep = ~beep;//\t蜂鸣器发出滴滴声\n\t\tdelay(100);\n\t}\t\n} \n```\n\n\n\n\n\n## 数码管\n\n\n\n### 原理\n\n单个数码管结构原理:\n\n![image-20210821211542277](https://gitee.com/ajream/images/raw/master/img/20210821211543_image-20210821211542277.png)\n\n\n\n多个数码管:\n\n![image-20210821211818619](https://gitee.com/ajream/images/raw/master/img/20210821211819_image-20210821211818619.png)\n\n说明:\n\n74HC573是个锁存器,原理如下:\n\n![image-20210821212211630](https://gitee.com/ajream/images/raw/master/img/20210821212212_image-20210821212211630.png)\n\n当控制端LE为高电平时,输出端Q的数据随输入端D的数据变化而变化\n\n当控制端LE为低电平时,输入端D的数据变化,Q端会保持之前的状态\n\n\n\n在这幅图\n\n![image-20210821211818619](https://gitee.com/ajream/images/raw/master/img/20210821211819_image-20210821211818619.png)\n\n\n\n用到2片74HC573模块,上面一片是用来控制哪一个数码管使用,下面那一片用来控制显示的字符\n\n在这8个数码管中,左边是低位,右边是高位,这是共阴极数码管,比如位选输入“0xFE”(即`1111 1110`),最左边这个数码管就被选中了。\n\n数码管的每一段都接到地,给对应的段提供高电平就能点亮它,其数码表如下\n\n```c\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F\n// 0 1 2 3\t4\t 5\t 6 7 8 9 \n0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x40, 0x00\n//A B C D E F H L - 熄灭\n```\n\n\n\n### 数码管静态显示\n\n```c\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管位选\n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110 选通最左边的数码管\n\tWE = 0;//锁存位选数据\n\n\tDU = 1;//打开段选锁存器\n\tP0 = 0X5b;//1101 1011 显示“1”\n\tDU = 0;//锁存段选数据\n\t\n\twhile(1);\n} \n```\n\n\n\n### 数码管动态显示\n\n要让多个数码管同时显示,如果用静态显示的方式,每个数码管都要8根线连接,n个数码管就要 n*8根线,比较耗费资源,因此使用动态扫描的方式进行显示,即**快速扫描**每一个数码管,分别让它们进行显示,只要足够快,由于视觉暂留效应,人的大脑会以为这是同时显示,实际上每个时刻只有一个数码管进行了显示\n\n\n\n这个代码用来控制3个数码管来显示一个三位数\n\n```c\n#include //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n//设计一个函数用于显示三位的数字number\nvoid display(uchar number)\n{\n\tuchar bai, shi, ge;\n\tbai = number / 100; \t\t//百位\n\tshi = number % 100 / 10;\t//十位\n\tge = number % 10;\t\t\t//个位\n\t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[bai];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第二位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFD; //1111 1101\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[shi];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第三位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFB; //1111 1011\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[ge];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n}\n\nvoid main()//main函数自身会循环\n{\t\n\twhile(1)\n\t{\n\t\tdisplay(185); //数码管显示185\n\t}\t\n} \n```\n\n\n\n\n\n\n\n## 键盘\n\n非编码键盘分为独立键盘和矩阵键盘\n\n### 独立键盘\n\n\n\n![image-20210821222715524](https://gitee.com/ajream/images/raw/master/img/20210821222718_image-20210821222715524.png)\n\n对于独立键盘而言,例如,当S2按键按下时,P30被拉低,变为低电平,因此要判断S2是否被按下,只需要不断检测\n\nP30是否为低电平即可\n\n> 【注意】\n>\n> 1. 按键消抖,最简单的是延时10~20ms后再检查其状态是否变化\n> 2. 松手检测\n\n```c\n/*********************************************************************************\n \t按下开发板S2按键数码管值+1,最大到9\n\t按下S3按下,值-1,最小减到0\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit key_s2 = P3^0;//独立按键S2\nsbit key_s3 = P3^1;//独立按键S3\nuchar num;//数码管显示的值\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\n\n//毫秒级延时函数定义\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\twhile(1)\n\t{\n\t\tif(key_s2 == 0)//判断S2是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s2 == 0)\n\t\t\t{\n\t\t\t\tif(num != 9)//如果值不等于9则+1,功能把值限定为小于9\n\t\t\t\tnum++;\n\t\t\t\twhile(!key_s2);//松手检测\n\t\t\t}\t\n\t\t}\n\t\tif(key_s3 == 0)//判断S3是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s3 == 0)\n\t\t\t{\n\t\t\t\tif(num > 0)\t//如果大于0则执行减一\n\t\t\t\t\tnum--;\n\t\t\t\twhile(!key_s3);//松手检测\n\t\t\t}\t\n\t\t}\n\t\t//松手之后刷新显示\n\t\tDU = 1;//打开段选锁存器\n\t\tP0 = tabel[num];//\n\t\tDU = 0;//锁存段选数据\n\t}\t\n} \n```\n\n\n\n### 矩阵键盘\n\n判断矩阵按键中某个按键是否按下可采用**先列扫描再行扫描**的方式\n\n1. 列扫描:先给P37, P36, P35, P34高电平,同时给P33,P32,P31,P30低电平,即传送 “0xF0”(11110000)过去,如果【S6】(或S10/S14/S18)按下,则P34就会被拉低,P3的引脚数据变为“0xE0”(1110 0000),S7按下,P35被拉低,变为“0xD1”(1101 0000),因此只要判断P3这一变量的数据变化即可判断是哪一列被按下了;\n2. 行扫描:给P3发送“0x0F”(0000 1111),如果是【S6】按下,则P30被拉低,P3数据变为“0x0E”(0000 1110),如果是【S10】按下,P3数据变为“0x0D”(0000 1101),这样就可以判断出是哪个按键按下。\n\n\n\n```c\n/*********************************************************************************\n* 【程序功能】: \t4*4矩阵键盘与4位独立键盘识别\t\t \t\t\t \t\t\t \n* 【使用说明】: \t按下矩阵键盘和独立键盘任意键,数码管显示相应数值\n\t\t\t\t 初始显示“-”横\n**********************************************************************************/\n#include //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nuchar num;//数码管显示的值\nuchar KeyValue = 20;//按键值 显示-\n\n//共阴数码管段选表\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x37, 0x3E, 0x40, 0x00 }; \n//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,H,L,,n,u,-,熄灭\n\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid KeyScan()\n{\n\t//4*4矩阵键盘扫描\n\tP3 = 0XF0;//列扫描\n\tif(P3 != 0XF0)//判断按键是否被按下\n\t{\n\t\tdelay(10);//软件消抖10ms\n\t\tif(P3 != 0XF0)//判断按键是否被按下\n\t\t{\n\t\t\tswitch(P3) //判断那一列被按下\n\t\t\t{\n\t\t\t\tcase 0xe0:\tKeyValue = 0;\tbreak;//第一列被按下\n\t\t\t\tcase 0xd0:\tKeyValue = 1;\tbreak;//第二列被按下\n\t\t\t\tcase 0xb0:\tKeyValue = 2;\tbreak;//第三列被按下\n\t\t\t\tcase 0x70:\tKeyValue = 3;\tbreak;//第四列被按下 \n\t\t\t}\n\t\t\tP3 = 0X0F;//行扫描\n\t\t\tswitch(P3) //判断那一行被按下\n\t\t\t{\n\t\t\t\tcase 0x0e:\tKeyValue = KeyValue;\tbreak;//第一行被按下\n\t\t\t\tcase 0x0d:\tKeyValue = KeyValue + 4;\tbreak;//第二行被按下\n\t\t\t\tcase 0x0b:\tKeyValue = KeyValue + 8;\tbreak;//第三行被按下\n\t\t\t\tcase 0x07:\tKeyValue = KeyValue + 12;\tbreak;//第四行被按下 \n\t\t\t}\n\t\t\twhile(P3 != 0X0F);//松手检测\t\n\t\t}\n\t}\n \n\tP3 = 0XFF;//独立按键扫描\n\tif(P3 != 0XFF)\n\t{\n\t\tdelay(10);//软件消抖10ms\n\t\tif(P3 != 0XFF)\n\t\t{\n\t\t\tswitch(P3) //判断那一行被按下\n\t\t\t{\n\t\t\t\tcase 0xfe:\tKeyValue = 16;\tbreak;//S2被按下\n\t\t\t\tcase 0xfd:\tKeyValue = 17;\tbreak;//S3被按下\n\t\t\t\tcase 0xfb:\tKeyValue = 18;\tbreak;//S4被按下\n\t\t\t\tcase 0xf7:\tKeyValue = 19;\tbreak;//S5被按下 \n\t\t\t}\n\t\t\twhile(P3 != 0XFF);//松手检测\t\t\t\n\t\t}\t\n\t}\n\n}\n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\n\tDU = 1;//打开段选锁存器\n\twhile(1)\n\t{\n\t\tKeyScan();//20个按键键盘扫描\n\t\tP0 = tabel[KeyValue];//显示按键值\n\t}\t\n} \n```\n\n\n\n> 总结:先列扫描,再行扫描;或者先行扫描,再列扫描\n\n\n\n\n\n","source":"_posts/51单片机/51单片机学习笔记(一).md","raw":"---\ntitle: 51单片机学习笔记(一)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 点亮第一个led灯、流水灯、蜂鸣器、数码管、按键\nabbrlink: 9e0a8624\ndate: 2021-08-18 00:00:00\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n## 点亮第一个led灯\n\n\n\n1. 新建工程\n\n\t![image-20210821203338653](https://gitee.com/ajream/images/raw/master/img/20210821203342_image-20210821203338653.png)\n\n2. 选择固件\n\n\t![image-20210821203543242](https://gitee.com/ajream/images/raw/master/img/20210821203544_image-20210821203543242.png)\n\n\t![image-20210821203717166](https://gitee.com/ajream/images/raw/master/img/20210821203718_image-20210821203717166.png)\n\n3. 新建代码源文件\n\n\t![image-20210821203932209](https://gitee.com/ajream/images/raw/master/img/20210821203933_image-20210821203932209.png)\n\n\t![image-20210821204130367](https://gitee.com/ajream/images/raw/master/img/20210821204131_image-20210821204130367.png)\n\n\t![image-20210821204227781](https://gitee.com/ajream/images/raw/master/img/20210821204229_image-20210821204227781.png)\n\n4. 添加如下代码\n\n\t```c\n\t#include //引用51头文件\n\t\n\tsbit LED = P1^1;\n\t\n\tvoid main()\n\t{\n\t\tLED = 0;\t//点亮LED2\t\n\t}\n\t```\n5. 编译前要先配置,让编译器在编译完成后输出hex文件,hex文件用来烧录进单片机里面\n\n ![image-20210821204723747](https://gitee.com/ajream/images/raw/master/img/20210821204724_image-20210821204723747.png)\n6. 编译\n\n ![image-20210821205109242](https://gitee.com/ajream/images/raw/master/img/20210821205110_image-20210821205109242.png)\n\n7. 上传(烧录)\n\n ![image-20210821205434569](https://gitee.com/ajream/images/raw/master/img/20210821205435_image-20210821205434569.png)\n\n\n\n到此第一个点灯小程序结束\n\n\n\n\n\n## 流水灯\n\n\n\n按照之前的步骤再建立一个新工程,写下如下代码:\n\n```c\n#include \t //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nuchar temp; //LED灯相关变量\n\n//延时函数\nvoid delay(uint z)\n{\n\tuint x, y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()\n{\n\ttemp = 0xfe;\n\tP1 = temp; //1111 1110 初值LED1亮\n\tdelay(1000);//毫秒级延时 100毫秒\n\twhile(1)\n\t{\n\t\ttemp = _cror_(temp, 1);//循环左移\n\t\tP1 = temp;//移位完成后赋值给P1 每个一个灯点亮\n\t\tdelay(1000);//毫秒级延时 100毫秒\n\t}\t\n} \n```\n\n说明:\n\n- `intrins.h` 头文件包含移位函数 :`右移_cror_()`、 `左移_corl_()`\n\n\n\n## 蜂鸣器\n\n蜂鸣器分有源与无源蜂鸣器\n\n- 有源指内部自带震荡源,只要提供电压即可响,但由于其震荡源震动频率固定,因此只能产生一种频率的声音\n- 无源蜂鸣器指内部没有震荡源,需要提供变化的电压(方波)才能让其响\n\n一个蜂鸣器的简单驱动电路:\n\n![image-20210821210804064](https://gitee.com/ajream/images/raw/master/img/20210821210805_image-20210821210804064.png)\n\n```c\n#include \t//包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n\nsbit beep = P2^3;//蜂鸣器\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()\n{\n\twhile(1)\n\t{\n\t\tbeep = ~beep;//\t蜂鸣器发出滴滴声\n\t\tdelay(100);\n\t}\t\n} \n```\n\n\n\n\n\n## 数码管\n\n\n\n### 原理\n\n单个数码管结构原理:\n\n![image-20210821211542277](https://gitee.com/ajream/images/raw/master/img/20210821211543_image-20210821211542277.png)\n\n\n\n多个数码管:\n\n![image-20210821211818619](https://gitee.com/ajream/images/raw/master/img/20210821211819_image-20210821211818619.png)\n\n说明:\n\n74HC573是个锁存器,原理如下:\n\n![image-20210821212211630](https://gitee.com/ajream/images/raw/master/img/20210821212212_image-20210821212211630.png)\n\n当控制端LE为高电平时,输出端Q的数据随输入端D的数据变化而变化\n\n当控制端LE为低电平时,输入端D的数据变化,Q端会保持之前的状态\n\n\n\n在这幅图\n\n![image-20210821211818619](https://gitee.com/ajream/images/raw/master/img/20210821211819_image-20210821211818619.png)\n\n\n\n用到2片74HC573模块,上面一片是用来控制哪一个数码管使用,下面那一片用来控制显示的字符\n\n在这8个数码管中,左边是低位,右边是高位,这是共阴极数码管,比如位选输入“0xFE”(即`1111 1110`),最左边这个数码管就被选中了。\n\n数码管的每一段都接到地,给对应的段提供高电平就能点亮它,其数码表如下\n\n```c\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F\n// 0 1 2 3\t4\t 5\t 6 7 8 9 \n0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x40, 0x00\n//A B C D E F H L - 熄灭\n```\n\n\n\n### 数码管静态显示\n\n```c\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管位选\n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110 选通最左边的数码管\n\tWE = 0;//锁存位选数据\n\n\tDU = 1;//打开段选锁存器\n\tP0 = 0X5b;//1101 1011 显示“1”\n\tDU = 0;//锁存段选数据\n\t\n\twhile(1);\n} \n```\n\n\n\n### 数码管动态显示\n\n要让多个数码管同时显示,如果用静态显示的方式,每个数码管都要8根线连接,n个数码管就要 n*8根线,比较耗费资源,因此使用动态扫描的方式进行显示,即**快速扫描**每一个数码管,分别让它们进行显示,只要足够快,由于视觉暂留效应,人的大脑会以为这是同时显示,实际上每个时刻只有一个数码管进行了显示\n\n\n\n这个代码用来控制3个数码管来显示一个三位数\n\n```c\n#include //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n//设计一个函数用于显示三位的数字number\nvoid display(uchar number)\n{\n\tuchar bai, shi, ge;\n\tbai = number / 100; \t\t//百位\n\tshi = number % 100 / 10;\t//十位\n\tge = number % 10;\t\t\t//个位\n\t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[bai];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第二位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFD; //1111 1101\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[shi];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第三位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFB; //1111 1011\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[ge];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n}\n\nvoid main()//main函数自身会循环\n{\t\n\twhile(1)\n\t{\n\t\tdisplay(185); //数码管显示185\n\t}\t\n} \n```\n\n\n\n\n\n\n\n## 键盘\n\n非编码键盘分为独立键盘和矩阵键盘\n\n### 独立键盘\n\n\n\n![image-20210821222715524](https://gitee.com/ajream/images/raw/master/img/20210821222718_image-20210821222715524.png)\n\n对于独立键盘而言,例如,当S2按键按下时,P30被拉低,变为低电平,因此要判断S2是否被按下,只需要不断检测\n\nP30是否为低电平即可\n\n> 【注意】\n>\n> 1. 按键消抖,最简单的是延时10~20ms后再检查其状态是否变化\n> 2. 松手检测\n\n```c\n/*********************************************************************************\n \t按下开发板S2按键数码管值+1,最大到9\n\t按下S3按下,值-1,最小减到0\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit key_s2 = P3^0;//独立按键S2\nsbit key_s3 = P3^1;//独立按键S3\nuchar num;//数码管显示的值\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\n\n//毫秒级延时函数定义\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\twhile(1)\n\t{\n\t\tif(key_s2 == 0)//判断S2是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s2 == 0)\n\t\t\t{\n\t\t\t\tif(num != 9)//如果值不等于9则+1,功能把值限定为小于9\n\t\t\t\tnum++;\n\t\t\t\twhile(!key_s2);//松手检测\n\t\t\t}\t\n\t\t}\n\t\tif(key_s3 == 0)//判断S3是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s3 == 0)\n\t\t\t{\n\t\t\t\tif(num > 0)\t//如果大于0则执行减一\n\t\t\t\t\tnum--;\n\t\t\t\twhile(!key_s3);//松手检测\n\t\t\t}\t\n\t\t}\n\t\t//松手之后刷新显示\n\t\tDU = 1;//打开段选锁存器\n\t\tP0 = tabel[num];//\n\t\tDU = 0;//锁存段选数据\n\t}\t\n} \n```\n\n\n\n### 矩阵键盘\n\n判断矩阵按键中某个按键是否按下可采用**先列扫描再行扫描**的方式\n\n1. 列扫描:先给P37, P36, P35, P34高电平,同时给P33,P32,P31,P30低电平,即传送 “0xF0”(11110000)过去,如果【S6】(或S10/S14/S18)按下,则P34就会被拉低,P3的引脚数据变为“0xE0”(1110 0000),S7按下,P35被拉低,变为“0xD1”(1101 0000),因此只要判断P3这一变量的数据变化即可判断是哪一列被按下了;\n2. 行扫描:给P3发送“0x0F”(0000 1111),如果是【S6】按下,则P30被拉低,P3数据变为“0x0E”(0000 1110),如果是【S10】按下,P3数据变为“0x0D”(0000 1101),这样就可以判断出是哪个按键按下。\n\n\n\n```c\n/*********************************************************************************\n* 【程序功能】: \t4*4矩阵键盘与4位独立键盘识别\t\t \t\t\t \t\t\t \n* 【使用说明】: \t按下矩阵键盘和独立键盘任意键,数码管显示相应数值\n\t\t\t\t 初始显示“-”横\n**********************************************************************************/\n#include //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nuchar num;//数码管显示的值\nuchar KeyValue = 20;//按键值 显示-\n\n//共阴数码管段选表\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x37, 0x3E, 0x40, 0x00 }; \n//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,H,L,,n,u,-,熄灭\n\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid KeyScan()\n{\n\t//4*4矩阵键盘扫描\n\tP3 = 0XF0;//列扫描\n\tif(P3 != 0XF0)//判断按键是否被按下\n\t{\n\t\tdelay(10);//软件消抖10ms\n\t\tif(P3 != 0XF0)//判断按键是否被按下\n\t\t{\n\t\t\tswitch(P3) //判断那一列被按下\n\t\t\t{\n\t\t\t\tcase 0xe0:\tKeyValue = 0;\tbreak;//第一列被按下\n\t\t\t\tcase 0xd0:\tKeyValue = 1;\tbreak;//第二列被按下\n\t\t\t\tcase 0xb0:\tKeyValue = 2;\tbreak;//第三列被按下\n\t\t\t\tcase 0x70:\tKeyValue = 3;\tbreak;//第四列被按下 \n\t\t\t}\n\t\t\tP3 = 0X0F;//行扫描\n\t\t\tswitch(P3) //判断那一行被按下\n\t\t\t{\n\t\t\t\tcase 0x0e:\tKeyValue = KeyValue;\tbreak;//第一行被按下\n\t\t\t\tcase 0x0d:\tKeyValue = KeyValue + 4;\tbreak;//第二行被按下\n\t\t\t\tcase 0x0b:\tKeyValue = KeyValue + 8;\tbreak;//第三行被按下\n\t\t\t\tcase 0x07:\tKeyValue = KeyValue + 12;\tbreak;//第四行被按下 \n\t\t\t}\n\t\t\twhile(P3 != 0X0F);//松手检测\t\n\t\t}\n\t}\n \n\tP3 = 0XFF;//独立按键扫描\n\tif(P3 != 0XFF)\n\t{\n\t\tdelay(10);//软件消抖10ms\n\t\tif(P3 != 0XFF)\n\t\t{\n\t\t\tswitch(P3) //判断那一行被按下\n\t\t\t{\n\t\t\t\tcase 0xfe:\tKeyValue = 16;\tbreak;//S2被按下\n\t\t\t\tcase 0xfd:\tKeyValue = 17;\tbreak;//S3被按下\n\t\t\t\tcase 0xfb:\tKeyValue = 18;\tbreak;//S4被按下\n\t\t\t\tcase 0xf7:\tKeyValue = 19;\tbreak;//S5被按下 \n\t\t\t}\n\t\t\twhile(P3 != 0XFF);//松手检测\t\t\t\n\t\t}\t\n\t}\n\n}\n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\n\tDU = 1;//打开段选锁存器\n\twhile(1)\n\t{\n\t\tKeyScan();//20个按键键盘扫描\n\t\tP0 = tabel[KeyValue];//显示按键值\n\t}\t\n} \n```\n\n\n\n> 总结:先列扫描,再行扫描;或者先行扫描,再列扫描\n\n\n\n\n\n","slug":"51单片机/51单片机学习笔记(一)","published":1,"updated":"2021-09-10T12:45:53.371Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6nw0001akve96rfdnwk","content":"点亮第一个led灯 \n新建工程
\n
\n \n选择固件
\n
\n
\n \n新建代码源文件
\n
\n
\n
\n \n添加如下代码
\n 1 2 3 4 5 6 7 8 #include <reg52.h> sbit LED = P1^1 ; void main () {\tLED = 0 ;\t }
\n编译前要先配置,让编译器在编译完成后输出hex文件,hex文件用来烧录进单片机里面
\n
\n \n编译
\n
\n \n上传(烧录)
\n
\n \n \n到此第一个点灯小程序结束
\n流水灯 按照之前的步骤再建立一个新工程,写下如下代码:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <reg52.h> \t #include <intrins.h> #define uint unsigned int #define uchar unsigned char uchar temp; void delay (uint z) {\tuint x, y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void main () {\ttemp = 0xfe ; \tP1 = temp; \tdelay(1000 ); \twhile (1 ) \t{ \t\ttemp = _cror_(temp, 1 ); \t\tP1 = temp; \t\tdelay(1000 ); \t}\t }
\n说明:
\n\nintrins.h
头文件包含移位函数 :右移_cror_()
、 左移_corl_()
\n \n蜂鸣器 蜂鸣器分有源与无源蜂鸣器
\n\n有源指内部自带震荡源,只要提供电压即可响,但由于其震荡源震动频率固定,因此只能产生一种频率的声音 \n无源蜂鸣器指内部没有震荡源,需要提供变化的电压(方波)才能让其响 \n \n一个蜂鸣器的简单驱动电路:
\n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <reg52.h> \t #include <intrins.h> #define uint unsigned int sbit beep = P2^3 ; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void main () {\twhile (1 ) \t{ \t\tbeep = ~beep; \t\tdelay(100 ); \t}\t }
\n数码管 原理 单个数码管结构原理:
\n
\n多个数码管:
\n
\n说明:
\n74HC573是个锁存器,原理如下:
\n
\n当控制端LE为高电平时,输出端Q的数据随输入端D的数据变化而变化
\n当控制端LE为低电平时,输入端D的数据变化,Q端会保持之前的状态
\n在这幅图
\n
\n用到2片74HC573模块,上面一片是用来控制哪一个数码管使用,下面那一片用来控制显示的字符
\n在这8个数码管中,左边是低位,右边是高位,这是共阴极数码管,比如位选输入“0xFE”(即1111 1110
),最左边这个数码管就被选中了。
\n数码管的每一段都接到地,给对应的段提供高电平就能点亮它,其数码表如下
\n1 2 3 4 0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F 0x77 , 0x7C , 0x39 , 0x5E , 0x79 , 0x71 , 0x76 , 0x38 , 0x40 , 0x00
\n数码管静态显示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; void main () {\tWE = 1 ; \tP0 = 0XFE ; \tWE = 0 ; \tDU = 1 ; \tP0 = 0X5b ; \tDU = 0 ; \t \twhile (1 ); }
\n数码管动态显示 要让多个数码管同时显示,如果用静态显示的方式,每个数码管都要8根线连接,n个数码管就要 n8根线,比较耗费资源,因此使用动态扫描的方式进行显示,即*快速扫描 每一个数码管,分别让它们进行显示,只要足够快,由于视觉暂留效应,人的大脑会以为这是同时显示,实际上每个时刻只有一个数码管进行了显示
\n这个代码用来控制3个数码管来显示一个三位数
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; uchar code tabel[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F ,}; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void display (uchar number) {\tuchar bai, shi, ge; \tbai = number / 100 ; \t\t \tshi = number % 100 / 10 ;\t \tge = number % 10 ;\t\t\t \t\t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = 0XFE ; \tWE = 0 ; \t \tDU = 1 ; \tP0 = tabel[bai]; \tDU = 0 ; \tdelay(5 ); \t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = 0XFD ; \tWE = 0 ; \t \tDU = 1 ; \tP0 = tabel[shi]; \tDU = 0 ; \tdelay(5 ); \t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = 0XFB ; \tWE = 0 ; \t \tDU = 1 ; \tP0 = tabel[ge]; \tDU = 0 ; \tdelay(5 ); } void main () {\t\twhile (1 ) \t{ \t\tdisplay(185 ); \t}\t }
\n键盘 非编码键盘分为独立键盘和矩阵键盘
\n独立键盘
\n对于独立键盘而言,例如,当S2按键按下时,P30被拉低,变为低电平,因此要判断S2是否被按下,只需要不断检测
\nP30是否为低电平即可
\n\n【注意】
\n\n按键消抖,最简单的是延时10~20ms后再检查其状态是否变化 \n松手检测 \n \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 55 56 57 58 59 60 61 62 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; sbit key_s2 = P3^0 ; sbit key_s3 = P3^1 ; uchar num; uchar code tabel[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F ,}; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void main () {\tWE = 1 ; \tP0 = 0XFE ; \tWE = 0 ; \t \twhile (1 ) \t{ \t\tif (key_s2 == 0 ) \t\t{ \t\t\tdelay(20 ); \t\t\tif (key_s2 == 0 ) \t\t\t{ \t\t\t\tif (num != 9 ) \t\t\t\tnum++; \t\t\t\twhile (!key_s2); \t\t\t}\t \t\t} \t\tif (key_s3 == 0 ) \t\t{ \t\t\tdelay(20 ); \t\t\tif (key_s3 == 0 ) \t\t\t{ \t\t\t\tif (num > 0 )\t \t\t\t\t\tnum--; \t\t\t\twhile (!key_s3); \t\t\t}\t \t\t} \t\t \t\tDU = 1 ; \t\tP0 = tabel[num]; \t\tDU = 0 ; \t}\t }
\n矩阵键盘 判断矩阵按键中某个按键是否按下可采用先列扫描再行扫描 的方式
\n\n列扫描:先给P37, P36, P35, P34高电平,同时给P33,P32,P31,P30低电平,即传送 “0xF0”(11110000)过去,如果【S6】(或S10/S14/S18)按下,则P34就会被拉低,P3的引脚数据变为“0xE0”(1110 0000),S7按下,P35被拉低,变为“0xD1”(1101 0000),因此只要判断P3这一变量的数据变化即可判断是哪一列被按下了; \n行扫描:给P3发送“0x0F”(0000 1111),如果是【S6】按下,则P30被拉低,P3数据变为“0x0E”(0000 1110),如果是【S10】按下,P3数据变为“0x0D”(0000 1101),这样就可以判断出是哪个按键按下。 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; uchar num; uchar KeyValue = 20 ; uchar code tabel[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F , 0x77 , 0x7C , 0x39 , 0x5E , 0x79 , 0x71 , 0x76 , 0x38 , 0x37 , 0x3E , 0x40 , 0x00 }; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void KeyScan () {\t \tP3 = 0XF0 ; \tif (P3 != 0XF0 ) \t{ \t\tdelay(10 ); \t\tif (P3 != 0XF0 ) \t\t{ \t\t\tswitch (P3) \t\t\t{ \t\t\t\tcase 0xe0 :\tKeyValue = 0 ;\tbreak ; \t\t\t\tcase 0xd0 :\tKeyValue = 1 ;\tbreak ; \t\t\t\tcase 0xb0 :\tKeyValue = 2 ;\tbreak ; \t\t\t\tcase 0x70 :\tKeyValue = 3 ;\tbreak ; \t\t\t} \t\t\tP3 = 0X0F ; \t\t\tswitch (P3) \t\t\t{ \t\t\t\tcase 0x0e :\tKeyValue = KeyValue;\tbreak ; \t\t\t\tcase 0x0d :\tKeyValue = KeyValue + 4 ;\tbreak ; \t\t\t\tcase 0x0b :\tKeyValue = KeyValue + 8 ;\tbreak ; \t\t\t\tcase 0x07 :\tKeyValue = KeyValue + 12 ;\tbreak ; \t\t\t} \t\t\twhile (P3 != 0X0F ); \t\t} \t} \tP3 = 0XFF ; \tif (P3 != 0XFF ) \t{ \t\tdelay(10 ); \t\tif (P3 != 0XFF ) \t\t{ \t\t\tswitch (P3) \t\t\t{ \t\t\t\tcase 0xfe :\tKeyValue = 16 ;\tbreak ; \t\t\t\tcase 0xfd :\tKeyValue = 17 ;\tbreak ; \t\t\t\tcase 0xfb :\tKeyValue = 18 ;\tbreak ; \t\t\t\tcase 0xf7 :\tKeyValue = 19 ;\tbreak ; \t\t\t} \t\t\twhile (P3 != 0XFF ); \t\t}\t \t} } void main () {\tWE = 1 ; \tP0 = 0XFE ; \tWE = 0 ; \tDU = 1 ; \twhile (1 ) \t{ \t\tKeyScan(); \t\tP0 = tabel[KeyValue]; \t}\t }
\n\n总结:先列扫描,再行扫描;或者先行扫描,再列扫描
\n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"点亮第一个led灯 \n新建工程
\n
\n \n选择固件
\n
\n
\n \n新建代码源文件
\n
\n
\n
\n \n添加如下代码
\n 1 2 3 4 5 6 7 8 #include <reg52.h> sbit LED = P1^1 ; void main () {\tLED = 0 ;\t }
\n编译前要先配置,让编译器在编译完成后输出hex文件,hex文件用来烧录进单片机里面
\n
\n \n编译
\n
\n \n上传(烧录)
\n
\n \n \n到此第一个点灯小程序结束
\n流水灯 按照之前的步骤再建立一个新工程,写下如下代码:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <reg52.h> \t #include <intrins.h> #define uint unsigned int #define uchar unsigned char uchar temp; void delay (uint z) {\tuint x, y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void main () {\ttemp = 0xfe ; \tP1 = temp; \tdelay(1000 ); \twhile (1 ) \t{ \t\ttemp = _cror_(temp, 1 ); \t\tP1 = temp; \t\tdelay(1000 ); \t}\t }
\n说明:
\n\nintrins.h
头文件包含移位函数 :右移_cror_()
、 左移_corl_()
\n \n蜂鸣器 蜂鸣器分有源与无源蜂鸣器
\n\n有源指内部自带震荡源,只要提供电压即可响,但由于其震荡源震动频率固定,因此只能产生一种频率的声音 \n无源蜂鸣器指内部没有震荡源,需要提供变化的电压(方波)才能让其响 \n \n一个蜂鸣器的简单驱动电路:
\n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <reg52.h> \t #include <intrins.h> #define uint unsigned int sbit beep = P2^3 ; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void main () {\twhile (1 ) \t{ \t\tbeep = ~beep; \t\tdelay(100 ); \t}\t }
\n数码管 原理 单个数码管结构原理:
\n
\n多个数码管:
\n
\n说明:
\n74HC573是个锁存器,原理如下:
\n
\n当控制端LE为高电平时,输出端Q的数据随输入端D的数据变化而变化
\n当控制端LE为低电平时,输入端D的数据变化,Q端会保持之前的状态
\n在这幅图
\n
\n用到2片74HC573模块,上面一片是用来控制哪一个数码管使用,下面那一片用来控制显示的字符
\n在这8个数码管中,左边是低位,右边是高位,这是共阴极数码管,比如位选输入“0xFE”(即1111 1110
),最左边这个数码管就被选中了。
\n数码管的每一段都接到地,给对应的段提供高电平就能点亮它,其数码表如下
\n1 2 3 4 0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F 0x77 , 0x7C , 0x39 , 0x5E , 0x79 , 0x71 , 0x76 , 0x38 , 0x40 , 0x00
\n数码管静态显示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; void main () {\tWE = 1 ; \tP0 = 0XFE ; \tWE = 0 ; \tDU = 1 ; \tP0 = 0X5b ; \tDU = 0 ; \t \twhile (1 ); }
\n数码管动态显示 要让多个数码管同时显示,如果用静态显示的方式,每个数码管都要8根线连接,n个数码管就要 n8根线,比较耗费资源,因此使用动态扫描的方式进行显示,即*快速扫描 每一个数码管,分别让它们进行显示,只要足够快,由于视觉暂留效应,人的大脑会以为这是同时显示,实际上每个时刻只有一个数码管进行了显示
\n这个代码用来控制3个数码管来显示一个三位数
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; uchar code tabel[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F ,}; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void display (uchar number) {\tuchar bai, shi, ge; \tbai = number / 100 ; \t\t \tshi = number % 100 / 10 ;\t \tge = number % 10 ;\t\t\t \t\t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = 0XFE ; \tWE = 0 ; \t \tDU = 1 ; \tP0 = tabel[bai]; \tDU = 0 ; \tdelay(5 ); \t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = 0XFD ; \tWE = 0 ; \t \tDU = 1 ; \tP0 = tabel[shi]; \tDU = 0 ; \tdelay(5 ); \t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = 0XFB ; \tWE = 0 ; \t \tDU = 1 ; \tP0 = tabel[ge]; \tDU = 0 ; \tdelay(5 ); } void main () {\t\twhile (1 ) \t{ \t\tdisplay(185 ); \t}\t }
\n键盘 非编码键盘分为独立键盘和矩阵键盘
\n独立键盘
\n对于独立键盘而言,例如,当S2按键按下时,P30被拉低,变为低电平,因此要判断S2是否被按下,只需要不断检测
\nP30是否为低电平即可
\n\n【注意】
\n\n按键消抖,最简单的是延时10~20ms后再检查其状态是否变化 \n松手检测 \n \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 55 56 57 58 59 60 61 62 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; sbit key_s2 = P3^0 ; sbit key_s3 = P3^1 ; uchar num; uchar code tabel[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F ,}; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void main () {\tWE = 1 ; \tP0 = 0XFE ; \tWE = 0 ; \t \twhile (1 ) \t{ \t\tif (key_s2 == 0 ) \t\t{ \t\t\tdelay(20 ); \t\t\tif (key_s2 == 0 ) \t\t\t{ \t\t\t\tif (num != 9 ) \t\t\t\tnum++; \t\t\t\twhile (!key_s2); \t\t\t}\t \t\t} \t\tif (key_s3 == 0 ) \t\t{ \t\t\tdelay(20 ); \t\t\tif (key_s3 == 0 ) \t\t\t{ \t\t\t\tif (num > 0 )\t \t\t\t\t\tnum--; \t\t\t\twhile (!key_s3); \t\t\t}\t \t\t} \t\t \t\tDU = 1 ; \t\tP0 = tabel[num]; \t\tDU = 0 ; \t}\t }
\n矩阵键盘 判断矩阵按键中某个按键是否按下可采用先列扫描再行扫描 的方式
\n\n列扫描:先给P37, P36, P35, P34高电平,同时给P33,P32,P31,P30低电平,即传送 “0xF0”(11110000)过去,如果【S6】(或S10/S14/S18)按下,则P34就会被拉低,P3的引脚数据变为“0xE0”(1110 0000),S7按下,P35被拉低,变为“0xD1”(1101 0000),因此只要判断P3这一变量的数据变化即可判断是哪一列被按下了; \n行扫描:给P3发送“0x0F”(0000 1111),如果是【S6】按下,则P30被拉低,P3数据变为“0x0E”(0000 1110),如果是【S10】按下,P3数据变为“0x0D”(0000 1101),这样就可以判断出是哪个按键按下。 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; uchar num; uchar KeyValue = 20 ; uchar code tabel[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F , 0x77 , 0x7C , 0x39 , 0x5E , 0x79 , 0x71 , 0x76 , 0x38 , 0x37 , 0x3E , 0x40 , 0x00 }; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void KeyScan () {\t \tP3 = 0XF0 ; \tif (P3 != 0XF0 ) \t{ \t\tdelay(10 ); \t\tif (P3 != 0XF0 ) \t\t{ \t\t\tswitch (P3) \t\t\t{ \t\t\t\tcase 0xe0 :\tKeyValue = 0 ;\tbreak ; \t\t\t\tcase 0xd0 :\tKeyValue = 1 ;\tbreak ; \t\t\t\tcase 0xb0 :\tKeyValue = 2 ;\tbreak ; \t\t\t\tcase 0x70 :\tKeyValue = 3 ;\tbreak ; \t\t\t} \t\t\tP3 = 0X0F ; \t\t\tswitch (P3) \t\t\t{ \t\t\t\tcase 0x0e :\tKeyValue = KeyValue;\tbreak ; \t\t\t\tcase 0x0d :\tKeyValue = KeyValue + 4 ;\tbreak ; \t\t\t\tcase 0x0b :\tKeyValue = KeyValue + 8 ;\tbreak ; \t\t\t\tcase 0x07 :\tKeyValue = KeyValue + 12 ;\tbreak ; \t\t\t} \t\t\twhile (P3 != 0X0F ); \t\t} \t} \tP3 = 0XFF ; \tif (P3 != 0XFF ) \t{ \t\tdelay(10 ); \t\tif (P3 != 0XFF ) \t\t{ \t\t\tswitch (P3) \t\t\t{ \t\t\t\tcase 0xfe :\tKeyValue = 16 ;\tbreak ; \t\t\t\tcase 0xfd :\tKeyValue = 17 ;\tbreak ; \t\t\t\tcase 0xfb :\tKeyValue = 18 ;\tbreak ; \t\t\t\tcase 0xf7 :\tKeyValue = 19 ;\tbreak ; \t\t\t} \t\t\twhile (P3 != 0XFF ); \t\t}\t \t} } void main () {\tWE = 1 ; \tP0 = 0XFE ; \tWE = 0 ; \tDU = 1 ; \twhile (1 ) \t{ \t\tKeyScan(); \t\tP0 = tabel[KeyValue]; \t}\t }
\n\n总结:先列扫描,再行扫描;或者先行扫描,再列扫描
\n \n"},{"title":"51单片机学习笔记(七)","description":"红外通信","abbrlink":"c2d1d169","date":"2021-08-20T08:32:44.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n## 红外通信\n\n### 红外遥控电路组成\n\n一般而言,红外遥控系统由发射装置 和接收装置 两大部分组成\n\n\n\n发射装置(例如遥控器)主要包括:\n\n- 键盘电路\n- 红外编码芯片\n- 电源\n- 红外发射电路\n\n\n\n红外接收设备:\n\n- 可由红外接收电路\n- 红外解码芯片\n- 电源\n- 应用电路组成\n\n\n\n![image-20210824165320963](https://gitee.com/ajream/images/raw/master/img/20210824165322_image-20210824165320963.png)\n\n\n\n\n\n### 信号调制和解调\n\n\n\n为了使信号更好的传输,一般会把信号进行调制使频率变高,接收到后再通过解调等一系列步骤还原信号\n\n\n\n\n\n### NEC协议\n\n\n\nNEC 标准下的编码表示\n\n1. 引导码高电平约 9000us 左右,低电平约4500us 左右\n\n2. 用户码16 位,数据码16 位,共32位\n\n ![image-20210824165820317](https://gitee.com/ajream/images/raw/master/img/20210824165821_image-20210824165820317.png)\n\n \n\n3. 数据0 是用“高电平约 560us + 低电平约 560us ”表示\n\n4. 数据1 是用“高电平约 560us + 低电平约 1680us”表示\n\n ![image-20210824165845177](https://gitee.com/ajream/images/raw/master/img/20210824165846_image-20210824165845177.png)\n\n\n\n\n\n\n\n## 例子\n\n\n\n```c\n/*\nNEC协议红外通信\n单片机解码后通过串口以9600的比特率发送出去\n*/\n\n#include \n\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit IR = P3^2; //定义红外脉冲数据接口\t外部中断O输入口\n\nuchar IRtime; \t\t//检测红外高电平持续时间(脉宽)\nuchar IRcord[4]; //此数组用于储存分离出来的4个字节的数据(用户码2个字节+键值码2个字节)\nuchar IRdata[33]; //此数组用于储存红外的33位数据(第一位为引导码用户码16+键值码16)\nbit IRpro_ok, IRok; //第一个用于红外接收4个字节完毕。IRok用为检测脉宽完毕\n\nvoid init()\t //初始化定时器0 和外部中断0\n{\n\tTMOD = 0x22; //定时器0和定时器1工作方式2,8位自动重装\n\tTH0 = 0x00; //高8位装入0那么定时器溢出一次的时间是256个机器周期\n\tTL0 = 0x00;\n\tEA = 1; //总中断\n\tET0 = 1;\t //定时器0中断\n\tTR0 = 1; //启动定时器0\n\n\tIT0 = 1;\t //设置外部中断0为跳沿触发方式,来一个下降沿触发一次\n\tEX0 = 1;\t //启动外部中断0\n\n\tTH1 = 0xfd; //此溢出率为波特率9600\n\tTL1 = 0xfd;\n\tTR1 = 1; //启动定时器1\n\tSM1 = 1; //设置串口工作方式1,10位异步收发器\n}\n\nvoid time0() interrupt 1 //定义定时器0\n{\n\tIRtime++; \t\t\t //检测脉宽,1次为278us\n}\n\nvoid int0() interrupt 0\t \t\t//定义外部中断0\n{\n\tstatic uchar i;\t \t\t\t//\t声明静态变量(在跳出函数后在回来执行的时候不会丢失数值)i用于把33次高电平的持续时间存入IRdata\n\tstatic bit startflag;\t\t//开始储存脉宽标志位\n\tif(startflag)\t \t\t\t//开始接收脉宽检测\n\t{\n\t\tif( (IRtime < 53) && (IRtime >= 32) ) /*判断是否是引导码,底电平9000us+高4500us\t\n\t\t这个自己可以算我以11.0592来算了NEC协议的引导码低8000-10000+高4000-5000 \n\t\t如果已经接收了引导码那么i不会被置0就会开始依次存入脉宽*/\n\t\t\ti = 0;\t\t\t\t //如果是引导码那么执行i=0把他存到IRdata的第一个位\n\t\tIRdata[i] = IRtime; \t\t //以T0的溢出次数来计算脉宽,把这个时间存到数组里面到后面判断\n\t\tIRtime = 0;\t\t\t\t //计数清零,下一个下降沿的时候在存入脉宽\n\t\ti++; \t\t\t\t\t //计数脉宽存入的次数\n\t\tif(i == 33) \t\t\t\t //如果存入34次 数组的下标是从0开始i等于33表示执行了34次\n\t\t{\n\t\t \tIRok = 1;\t\t\t\t //那么表示脉宽检测完毕\n\t\t\ti = 0; \t\t\t\t //把脉宽计数清零准备下次存入\n\t\t}\n\t}\n\telse\t\t \n\t{\n\t\tIRtime = 0; \t\t\t\t //引导码开始进入把脉宽计数清零开始计数\n\t\tstartflag = 1;\t\t\t //开始处理标志位置1\n\t}\n}\n\nvoid IRcordpro() \t\t\t\t //提取它的33次脉宽进行数据解码\n{\n\tuchar i, j, k, cord, value;\t/*i用于处理4个字节,j用于处理一个字节中每一位,k用于33次脉宽中的哪一位\n\tcord用于取出脉宽的时间判断是否符合1的脉宽时间*/\n\tk = 1; \t\t\t\t\t\t//从第一位脉宽开始取,丢弃引导码脉宽\n\tfor(i = 0; i < 4; i++)\n\t{\n\t\tfor(j = 0; j < 8; j++)\n\t\t{\n\t\t\tcord = IRdata[k];\t //把脉宽存入cord\n\t\t\tif(cord > 5)\t \t\t//如果脉宽大于我11.0592的t0溢出率为约278us*5=1390那么判断为1\n\t\t\tvalue = value | 0x80;\t/*接收的时候是先接收最低位,\n\t\t\t把最低位先放到value的最高位在和0x08按位或一下\n\t\t\t这样不会改变valua的其他位的数值只会让他最高位为1*/\n\t\t\tif(j < 7)\n\t\t\t{\n\t\t\t\tvalue = value >> 1;\t//value位左移依次接收8位数据。\n\t\t\t}\n\t\t\tk++;\t\t\t\t//每执行一次脉宽位加1\n\t\t}\n\t\tIRcord[i] = value;\t //每处理完一个字节把它放入IRcord数组中。\n\t\tvalue = 0; \t\t\t //清零value方便下次在存入数据\n\t}\n\tIRpro_ok = 1;\t\t\t\t //接收完4个字节后IRpro ok置1表示红外解码完成\t\n}\n\n\nvoid main()\n{\n\tuchar i;\n\tinit();\t//执行初始化定时器0和外部中断0\n\twhile(1)\t//大循环\n\t{\n\t\tif(IRok) //判断脉宽是否检测完毕 \n\t\t{ \n\t\t\tIRcordpro();//根据脉宽解码出4个字节的数据\n\t\t\tIRok = 0;\t//重新等待脉宽检测\n\t\t\tif(IRpro_ok) //判断是否解码完毕 \n\t\t\t{\n\t\t\t\tfor(i = 0; i < 4; i++) \n\t\t\t\t{\t \n\t\t\t\t\tSBUF = IRcord[i];\n\t\t\t\t\twhile(!TI);\n\t\t\t\t\tTI = 0;\n\t\t\t\t}\n\t\t\t\tIRpro_ok = 0;\n\t\t\t}\n\t\t}\t\n\t}\n}\n```\n\n","source":"_posts/51单片机/51单片机学习笔记(七).md","raw":"---\ntitle: 51单片机学习笔记(七)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 红外通信\nabbrlink: c2d1d169\ndate: 2021-08-20 16:32:44\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n## 红外通信\n\n### 红外遥控电路组成\n\n一般而言,红外遥控系统由发射装置 和接收装置 两大部分组成\n\n\n\n发射装置(例如遥控器)主要包括:\n\n- 键盘电路\n- 红外编码芯片\n- 电源\n- 红外发射电路\n\n\n\n红外接收设备:\n\n- 可由红外接收电路\n- 红外解码芯片\n- 电源\n- 应用电路组成\n\n\n\n![image-20210824165320963](https://gitee.com/ajream/images/raw/master/img/20210824165322_image-20210824165320963.png)\n\n\n\n\n\n### 信号调制和解调\n\n\n\n为了使信号更好的传输,一般会把信号进行调制使频率变高,接收到后再通过解调等一系列步骤还原信号\n\n\n\n\n\n### NEC协议\n\n\n\nNEC 标准下的编码表示\n\n1. 引导码高电平约 9000us 左右,低电平约4500us 左右\n\n2. 用户码16 位,数据码16 位,共32位\n\n ![image-20210824165820317](https://gitee.com/ajream/images/raw/master/img/20210824165821_image-20210824165820317.png)\n\n \n\n3. 数据0 是用“高电平约 560us + 低电平约 560us ”表示\n\n4. 数据1 是用“高电平约 560us + 低电平约 1680us”表示\n\n ![image-20210824165845177](https://gitee.com/ajream/images/raw/master/img/20210824165846_image-20210824165845177.png)\n\n\n\n\n\n\n\n## 例子\n\n\n\n```c\n/*\nNEC协议红外通信\n单片机解码后通过串口以9600的比特率发送出去\n*/\n\n#include \n\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit IR = P3^2; //定义红外脉冲数据接口\t外部中断O输入口\n\nuchar IRtime; \t\t//检测红外高电平持续时间(脉宽)\nuchar IRcord[4]; //此数组用于储存分离出来的4个字节的数据(用户码2个字节+键值码2个字节)\nuchar IRdata[33]; //此数组用于储存红外的33位数据(第一位为引导码用户码16+键值码16)\nbit IRpro_ok, IRok; //第一个用于红外接收4个字节完毕。IRok用为检测脉宽完毕\n\nvoid init()\t //初始化定时器0 和外部中断0\n{\n\tTMOD = 0x22; //定时器0和定时器1工作方式2,8位自动重装\n\tTH0 = 0x00; //高8位装入0那么定时器溢出一次的时间是256个机器周期\n\tTL0 = 0x00;\n\tEA = 1; //总中断\n\tET0 = 1;\t //定时器0中断\n\tTR0 = 1; //启动定时器0\n\n\tIT0 = 1;\t //设置外部中断0为跳沿触发方式,来一个下降沿触发一次\n\tEX0 = 1;\t //启动外部中断0\n\n\tTH1 = 0xfd; //此溢出率为波特率9600\n\tTL1 = 0xfd;\n\tTR1 = 1; //启动定时器1\n\tSM1 = 1; //设置串口工作方式1,10位异步收发器\n}\n\nvoid time0() interrupt 1 //定义定时器0\n{\n\tIRtime++; \t\t\t //检测脉宽,1次为278us\n}\n\nvoid int0() interrupt 0\t \t\t//定义外部中断0\n{\n\tstatic uchar i;\t \t\t\t//\t声明静态变量(在跳出函数后在回来执行的时候不会丢失数值)i用于把33次高电平的持续时间存入IRdata\n\tstatic bit startflag;\t\t//开始储存脉宽标志位\n\tif(startflag)\t \t\t\t//开始接收脉宽检测\n\t{\n\t\tif( (IRtime < 53) && (IRtime >= 32) ) /*判断是否是引导码,底电平9000us+高4500us\t\n\t\t这个自己可以算我以11.0592来算了NEC协议的引导码低8000-10000+高4000-5000 \n\t\t如果已经接收了引导码那么i不会被置0就会开始依次存入脉宽*/\n\t\t\ti = 0;\t\t\t\t //如果是引导码那么执行i=0把他存到IRdata的第一个位\n\t\tIRdata[i] = IRtime; \t\t //以T0的溢出次数来计算脉宽,把这个时间存到数组里面到后面判断\n\t\tIRtime = 0;\t\t\t\t //计数清零,下一个下降沿的时候在存入脉宽\n\t\ti++; \t\t\t\t\t //计数脉宽存入的次数\n\t\tif(i == 33) \t\t\t\t //如果存入34次 数组的下标是从0开始i等于33表示执行了34次\n\t\t{\n\t\t \tIRok = 1;\t\t\t\t //那么表示脉宽检测完毕\n\t\t\ti = 0; \t\t\t\t //把脉宽计数清零准备下次存入\n\t\t}\n\t}\n\telse\t\t \n\t{\n\t\tIRtime = 0; \t\t\t\t //引导码开始进入把脉宽计数清零开始计数\n\t\tstartflag = 1;\t\t\t //开始处理标志位置1\n\t}\n}\n\nvoid IRcordpro() \t\t\t\t //提取它的33次脉宽进行数据解码\n{\n\tuchar i, j, k, cord, value;\t/*i用于处理4个字节,j用于处理一个字节中每一位,k用于33次脉宽中的哪一位\n\tcord用于取出脉宽的时间判断是否符合1的脉宽时间*/\n\tk = 1; \t\t\t\t\t\t//从第一位脉宽开始取,丢弃引导码脉宽\n\tfor(i = 0; i < 4; i++)\n\t{\n\t\tfor(j = 0; j < 8; j++)\n\t\t{\n\t\t\tcord = IRdata[k];\t //把脉宽存入cord\n\t\t\tif(cord > 5)\t \t\t//如果脉宽大于我11.0592的t0溢出率为约278us*5=1390那么判断为1\n\t\t\tvalue = value | 0x80;\t/*接收的时候是先接收最低位,\n\t\t\t把最低位先放到value的最高位在和0x08按位或一下\n\t\t\t这样不会改变valua的其他位的数值只会让他最高位为1*/\n\t\t\tif(j < 7)\n\t\t\t{\n\t\t\t\tvalue = value >> 1;\t//value位左移依次接收8位数据。\n\t\t\t}\n\t\t\tk++;\t\t\t\t//每执行一次脉宽位加1\n\t\t}\n\t\tIRcord[i] = value;\t //每处理完一个字节把它放入IRcord数组中。\n\t\tvalue = 0; \t\t\t //清零value方便下次在存入数据\n\t}\n\tIRpro_ok = 1;\t\t\t\t //接收完4个字节后IRpro ok置1表示红外解码完成\t\n}\n\n\nvoid main()\n{\n\tuchar i;\n\tinit();\t//执行初始化定时器0和外部中断0\n\twhile(1)\t//大循环\n\t{\n\t\tif(IRok) //判断脉宽是否检测完毕 \n\t\t{ \n\t\t\tIRcordpro();//根据脉宽解码出4个字节的数据\n\t\t\tIRok = 0;\t//重新等待脉宽检测\n\t\t\tif(IRpro_ok) //判断是否解码完毕 \n\t\t\t{\n\t\t\t\tfor(i = 0; i < 4; i++) \n\t\t\t\t{\t \n\t\t\t\t\tSBUF = IRcord[i];\n\t\t\t\t\twhile(!TI);\n\t\t\t\t\tTI = 0;\n\t\t\t\t}\n\t\t\t\tIRpro_ok = 0;\n\t\t\t}\n\t\t}\t\n\t}\n}\n```\n\n","slug":"51单片机/51单片机学习笔记(七)","published":1,"updated":"2021-09-10T12:45:26.554Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6nz0003akve5a7o3s93","content":"红外通信 红外遥控电路组成 一般而言,红外遥控系统由发射装置 和接收装置 两大部分组成
\n发射装置(例如遥控器)主要包括:
\n\n键盘电路 \n红外编码芯片 \n电源 \n红外发射电路 \n \n红外接收设备:
\n\n可由红外接收电路 \n红外解码芯片 \n电源 \n应用电路组成 \n \n
\n信号调制和解调 为了使信号更好的传输,一般会把信号进行调制使频率变高,接收到后再通过解调等一系列步骤还原信号
\nNEC协议 NEC 标准下的编码表示
\n\n引导码高电平约 9000us 左右,低电平约4500us 左右
\n \n用户码16 位,数据码16 位,共32位
\n
\n \n \n\n数据0 是用“高电平约 560us + 低电平约 560us ”表示
\n \n数据1 是用“高电平约 560us + 低电平约 1680us”表示
\n
\n \n \n例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 #include <reg52.h> typedef unsigned char INT8U;typedef unsigned char uchar;typedef unsigned int INT16U;typedef unsigned int uint;sbit IR = P3^2 ; uchar IRtime; \t\t uchar IRcord[4 ]; uchar IRdata[33 ]; bit IRpro_ok, IRok; void init () \t {\tTMOD = 0x22 ; \tTH0 = 0x00 ; \tTL0 = 0x00 ; \tEA = 1 ; \tET0 = 1 ;\t \tTR0 = 1 ; \tIT0 = 1 ;\t \tEX0 = 1 ;\t \tTH1 = 0xfd ; \tTL1 = 0xfd ; \tTR1 = 1 ; \tSM1 = 1 ; } void time0 () interrupt 1 {\tIRtime++; \t\t\t } void int0 () interrupt 0\t \t\t {\tstatic uchar i;\t \t\t\t \tstatic bit startflag;\t\t \tif (startflag)\t \t\t\t \t{ \t\tif ( (IRtime < 53 ) && (IRtime >= 32 ) ) \t\t\ti = 0 ;\t\t\t\t \t\tIRdata[i] = IRtime; \t\t \t\tIRtime = 0 ;\t\t\t\t \t\ti++; \t\t\t\t\t \t\tif (i == 33 ) \t\t\t\t \t\t{ \t\t \tIRok = 1 ;\t\t\t\t \t\t\ti = 0 ; \t\t\t\t \t\t} \t} \telse \t\t \t{ \t\tIRtime = 0 ; \t\t\t\t \t\tstartflag = 1 ;\t\t\t \t} } void IRcordpro () \t\t\t\t {\tuchar i, j, k, cord, value;\t \tk = 1 ; \t\t\t\t\t\t \tfor (i = 0 ; i < 4 ; i++) \t{ \t\tfor (j = 0 ; j < 8 ; j++) \t\t{ \t\t\tcord = IRdata[k];\t \t\t\tif (cord > 5 )\t \t\t \t\t\tvalue = value | 0x80 ;\t \t\t\tif (j < 7 ) \t\t\t{ \t\t\t\tvalue = value >> 1 ;\t \t\t\t} \t\t\tk++;\t\t\t\t \t\t} \t\tIRcord[i] = value;\t \t\tvalue = 0 ; \t\t\t \t} \tIRpro_ok = 1 ;\t\t\t\t } void main () {\tuchar i; \tinit();\t \twhile (1 )\t \t{ \t\tif (IRok) \t\t{ \t\t\tIRcordpro(); \t\t\tIRok = 0 ;\t \t\t\tif (IRpro_ok) \t\t\t{ \t\t\t\tfor (i = 0 ; i < 4 ; i++) \t\t\t\t{\t \t\t\t\t\tSBUF = IRcord[i]; \t\t\t\t\twhile (!TI); \t\t\t\t\tTI = 0 ; \t\t\t\t} \t\t\t\tIRpro_ok = 0 ; \t\t\t} \t\t}\t \t} }
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"红外通信 红外遥控电路组成 一般而言,红外遥控系统由发射装置 和接收装置 两大部分组成
\n发射装置(例如遥控器)主要包括:
\n\n键盘电路 \n红外编码芯片 \n电源 \n红外发射电路 \n \n红外接收设备:
\n\n可由红外接收电路 \n红外解码芯片 \n电源 \n应用电路组成 \n \n
\n信号调制和解调 为了使信号更好的传输,一般会把信号进行调制使频率变高,接收到后再通过解调等一系列步骤还原信号
\nNEC协议 NEC 标准下的编码表示
\n\n引导码高电平约 9000us 左右,低电平约4500us 左右
\n \n用户码16 位,数据码16 位,共32位
\n
\n \n \n\n数据0 是用“高电平约 560us + 低电平约 560us ”表示
\n \n数据1 是用“高电平约 560us + 低电平约 1680us”表示
\n
\n \n \n例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 #include <reg52.h> typedef unsigned char INT8U;typedef unsigned char uchar;typedef unsigned int INT16U;typedef unsigned int uint;sbit IR = P3^2 ; uchar IRtime; \t\t uchar IRcord[4 ]; uchar IRdata[33 ]; bit IRpro_ok, IRok; void init () \t {\tTMOD = 0x22 ; \tTH0 = 0x00 ; \tTL0 = 0x00 ; \tEA = 1 ; \tET0 = 1 ;\t \tTR0 = 1 ; \tIT0 = 1 ;\t \tEX0 = 1 ;\t \tTH1 = 0xfd ; \tTL1 = 0xfd ; \tTR1 = 1 ; \tSM1 = 1 ; } void time0 () interrupt 1 {\tIRtime++; \t\t\t } void int0 () interrupt 0\t \t\t {\tstatic uchar i;\t \t\t\t \tstatic bit startflag;\t\t \tif (startflag)\t \t\t\t \t{ \t\tif ( (IRtime < 53 ) && (IRtime >= 32 ) ) \t\t\ti = 0 ;\t\t\t\t \t\tIRdata[i] = IRtime; \t\t \t\tIRtime = 0 ;\t\t\t\t \t\ti++; \t\t\t\t\t \t\tif (i == 33 ) \t\t\t\t \t\t{ \t\t \tIRok = 1 ;\t\t\t\t \t\t\ti = 0 ; \t\t\t\t \t\t} \t} \telse \t\t \t{ \t\tIRtime = 0 ; \t\t\t\t \t\tstartflag = 1 ;\t\t\t \t} } void IRcordpro () \t\t\t\t {\tuchar i, j, k, cord, value;\t \tk = 1 ; \t\t\t\t\t\t \tfor (i = 0 ; i < 4 ; i++) \t{ \t\tfor (j = 0 ; j < 8 ; j++) \t\t{ \t\t\tcord = IRdata[k];\t \t\t\tif (cord > 5 )\t \t\t \t\t\tvalue = value | 0x80 ;\t \t\t\tif (j < 7 ) \t\t\t{ \t\t\t\tvalue = value >> 1 ;\t \t\t\t} \t\t\tk++;\t\t\t\t \t\t} \t\tIRcord[i] = value;\t \t\tvalue = 0 ; \t\t\t \t} \tIRpro_ok = 1 ;\t\t\t\t } void main () {\tuchar i; \tinit();\t \twhile (1 )\t \t{ \t\tif (IRok) \t\t{ \t\t\tIRcordpro(); \t\t\tIRok = 0 ;\t \t\t\tif (IRpro_ok) \t\t\t{ \t\t\t\tfor (i = 0 ; i < 4 ; i++) \t\t\t\t{\t \t\t\t\t\tSBUF = IRcord[i]; \t\t\t\t\twhile (!TI); \t\t\t\t\tTI = 0 ; \t\t\t\t} \t\t\t\tIRpro_ok = 0 ; \t\t\t} \t\t}\t \t} }
\n"},{"title":"51单片机学习笔记(九)","description":"LCD1602液晶显示器基本操作使用","abbrlink":"17528481","date":"2021-08-21T06:32:19.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n## 简单介绍\n\n### 主要参数\n\n显示容量:16X2个字符\n\n芯片工作电压:4.5~5.5V, 最佳工作电压5.0V(2.0mA)\n\n\n\n\n\n### 引脚说明\n\nRS:为0时是指令输入,也就是表示显示在哪个“格子”的(1602有16X2共32个\"格子\");\n\n\t为1时表示数据输入,即每个”格子“显示的内容\n\nR/W:为1时是读,0是写;\n\nE:读写数据时要置1\n\nD0~D7:数据输入输出\n\n![image-20210825154248774](https://gitee.com/ajream/images/raw/master/img/20210825154252_image-20210825154248774.png)\n\n\n\n\n\n## 基本操作\n\n### 读写操作\n\n- 读状态,输入:RS=0, RW=1, E=0; \n\n 此时输出数据D0~D7为状态字,每一位都有各自的状态;\n\n 其中D0-D6表示当前数据地址指针的数值,D7的状态表示读写操作使能,如果是1表示禁止读写,0表示允许读写;\n\n ![image-20210825155602088](https://gitee.com/ajream/images/raw/master/img/20210825155603_image-20210825155602088.png)\n\n- 写指令(即告诉1602要显示的位置):RS=0, RW=0, E=高脉冲,D0-D7为指令码\n- 读数据:RS=1, RW=1, E=1, 此时D0-D7输出数据\n- 写数据:RS=1, RW=0, D0-D7表示要写入的数据,E=高脉冲\n\n\n\n```c\nvoid Read_Busy() //读状态判断是芯片否处于”忙“状态\n{\n\tuchar busy;\n\tP0 = 0xff;\n\tRS = 0;\n\tRW = 1;\n\tdo\n\t{\n\t\tEN = 1;\n\t\tbusy = P0;\n\t\tEN = 0;\n\t}while(busy & 0x80);\n}\n//写LCD1602命令一个字节\nvoid Write_Cmd(uchar cmd)\n{\n Read_Busy();//判断忙\n RS = 0;\n RW = 0;\n P0 = cmd;\n EN = 1;\n EN = 0;\n}\n//写一个字节数据\nvoid Write_Dat(uchar dat)\n{\n\tRead_Busy();\n\tRS = 1;\n\tRW = 0;\n\tP0 = dat;\n\tEN = 1;\n\tEN = 0;\n}\n```\n\n\n\n\n\n### RAM地址映射\n\n控制器内部自带了80X8位(即80Bytes)的RAM缓冲区,对应关系如下图:\n\n![image-20210825160547241](https://gitee.com/ajream/images/raw/master/img/20210825160548_image-20210825160547241.png)\n\n注意:第10——27和50——67的地址上的字符是无法显示的,只能显示前面16X2个地址上所存放的字符\n\n\n\n### 指令说明\n\n初始化设置:\n\n1. 显示模式设置:指令码0x38(二进制是00111000),表示设置16X2显示,5X7点阵,8位数据接口\n\n2. 开/关光标设置:(配置时要转为16进制)\n\n 指令码(二进制形式)`00001DCB`\n\n - D=1开显示,D=0关显示\n\n - C=1显示光标,C=0不显示光标\n - B=1光标闪烁,B=0不闪烁\n\n 指令码(二进制形式)`000001NS`\n\n - N=1当读或写一个字符后地址和光标 自动+1,N=0则为自动-1;\n\n - S=1当写入一个字符整屏显示左移(N=1)或右移(N=0)达到光标不动屏幕移动的效果;\n\n S=0当写入一个字符,屏幕显示不移动\n\n3. 数据控制\n\n 控制器内部有地址指针,通过发送指令 `80H+地址码` (地址码范围是0——27H,40H——67H) 即可访问地址上的数据,进而进行设置\n\n4. 其他指令:\n\n ![image-20210825162336247](https://gitee.com/ajream/images/raw/master/img/20210825162337_image-20210825162336247.png)\n\n\n\n\n\n### 时序图\n\n\n\n读操作时序图\n\n\n\n![image-20210825162506774](https://gitee.com/ajream/images/raw/master/img/20210825162508_image-20210825162506774.png)\n\n\n\n写操作时序图\n\n![image-20210825162527365](https://gitee.com/ajream/images/raw/master/img/20210825162528_image-20210825162527365.png)\n\n\n\n时序参数\n\n\n\n![image-20210825162600974](https://gitee.com/ajream/images/raw/master/img/20210825162602_image-20210825162600974.png)\n\n\n\n\n\n## 例子\n\n控制LCD1602显示字符12345\n\n\n\n```c\n#include \n\ntypedef unsigned char uchar;\ntypedef unsigned int uint;\n\nsbit RS = P3^5;\nsbit RW = P3^6;\nsbit EN = P3^4;\n\n//判断液晶忙,如果忙则等待\nvoid Read_Busy()\n{\n\tuchar busy;\n\tP0 = 0xff;\n\tRS = 0;\n\tRW = 1;\n\tdo\n\t{\n\t\tEN = 1;\n\t\tbusy = P0;\n\t\tEN = 0;\n\t}while(busy & 0x80);\n}\n//写LCD1602命令一个字节\nvoid Write_Cmd(uchar cmd)\n{\n\tRead_Busy();//判断忙\n\tRS = 0;\n\tRW = 0;\n\tP0 = cmd;\n\tEN = 1;\n\tEN = 0;\n}\n//写一个字节数据\nvoid Write_Dat(uchar dat)\n{\n\tRead_Busy();\n\tRS = 1;\n\tRW = 0;\n\tP0 = dat;\n\tEN = 1;\n\tEN = 0;\n}\nvoid main()\n{\n\tWrite_Cmd(0x38);//设置16*2显示\n\tWrite_Cmd(0x0f);//开显示,显示光标,光标闪烁\n\tWrite_Cmd(0x01);//清屏\n\n\tWrite_Cmd(0x06);//地址指针移位命令\n\tWrite_Cmd(0x80 | 0x06);//显示地址\n\n\tWrite_Dat(1 + '0'); //写入的字符要转换位ASCII码\n\tWrite_Dat(2 + '0');\n\tWrite_Dat(3 + '0');\n\tWrite_Dat(4 + '0');\n\tWrite_Dat(5 + '0');\n\twhile(1);\n}\n```\n\n","source":"_posts/51单片机/51单片机学习笔记(九).md","raw":"---\ntitle: 51单片机学习笔记(九)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: LCD1602液晶显示器基本操作使用\nabbrlink: '17528481'\ndate: 2021-08-21 14:32:19\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n## 简单介绍\n\n### 主要参数\n\n显示容量:16X2个字符\n\n芯片工作电压:4.5~5.5V, 最佳工作电压5.0V(2.0mA)\n\n\n\n\n\n### 引脚说明\n\nRS:为0时是指令输入,也就是表示显示在哪个“格子”的(1602有16X2共32个\"格子\");\n\n\t为1时表示数据输入,即每个”格子“显示的内容\n\nR/W:为1时是读,0是写;\n\nE:读写数据时要置1\n\nD0~D7:数据输入输出\n\n![image-20210825154248774](https://gitee.com/ajream/images/raw/master/img/20210825154252_image-20210825154248774.png)\n\n\n\n\n\n## 基本操作\n\n### 读写操作\n\n- 读状态,输入:RS=0, RW=1, E=0; \n\n 此时输出数据D0~D7为状态字,每一位都有各自的状态;\n\n 其中D0-D6表示当前数据地址指针的数值,D7的状态表示读写操作使能,如果是1表示禁止读写,0表示允许读写;\n\n ![image-20210825155602088](https://gitee.com/ajream/images/raw/master/img/20210825155603_image-20210825155602088.png)\n\n- 写指令(即告诉1602要显示的位置):RS=0, RW=0, E=高脉冲,D0-D7为指令码\n- 读数据:RS=1, RW=1, E=1, 此时D0-D7输出数据\n- 写数据:RS=1, RW=0, D0-D7表示要写入的数据,E=高脉冲\n\n\n\n```c\nvoid Read_Busy() //读状态判断是芯片否处于”忙“状态\n{\n\tuchar busy;\n\tP0 = 0xff;\n\tRS = 0;\n\tRW = 1;\n\tdo\n\t{\n\t\tEN = 1;\n\t\tbusy = P0;\n\t\tEN = 0;\n\t}while(busy & 0x80);\n}\n//写LCD1602命令一个字节\nvoid Write_Cmd(uchar cmd)\n{\n Read_Busy();//判断忙\n RS = 0;\n RW = 0;\n P0 = cmd;\n EN = 1;\n EN = 0;\n}\n//写一个字节数据\nvoid Write_Dat(uchar dat)\n{\n\tRead_Busy();\n\tRS = 1;\n\tRW = 0;\n\tP0 = dat;\n\tEN = 1;\n\tEN = 0;\n}\n```\n\n\n\n\n\n### RAM地址映射\n\n控制器内部自带了80X8位(即80Bytes)的RAM缓冲区,对应关系如下图:\n\n![image-20210825160547241](https://gitee.com/ajream/images/raw/master/img/20210825160548_image-20210825160547241.png)\n\n注意:第10——27和50——67的地址上的字符是无法显示的,只能显示前面16X2个地址上所存放的字符\n\n\n\n### 指令说明\n\n初始化设置:\n\n1. 显示模式设置:指令码0x38(二进制是00111000),表示设置16X2显示,5X7点阵,8位数据接口\n\n2. 开/关光标设置:(配置时要转为16进制)\n\n 指令码(二进制形式)`00001DCB`\n\n - D=1开显示,D=0关显示\n\n - C=1显示光标,C=0不显示光标\n - B=1光标闪烁,B=0不闪烁\n\n 指令码(二进制形式)`000001NS`\n\n - N=1当读或写一个字符后地址和光标 自动+1,N=0则为自动-1;\n\n - S=1当写入一个字符整屏显示左移(N=1)或右移(N=0)达到光标不动屏幕移动的效果;\n\n S=0当写入一个字符,屏幕显示不移动\n\n3. 数据控制\n\n 控制器内部有地址指针,通过发送指令 `80H+地址码` (地址码范围是0——27H,40H——67H) 即可访问地址上的数据,进而进行设置\n\n4. 其他指令:\n\n ![image-20210825162336247](https://gitee.com/ajream/images/raw/master/img/20210825162337_image-20210825162336247.png)\n\n\n\n\n\n### 时序图\n\n\n\n读操作时序图\n\n\n\n![image-20210825162506774](https://gitee.com/ajream/images/raw/master/img/20210825162508_image-20210825162506774.png)\n\n\n\n写操作时序图\n\n![image-20210825162527365](https://gitee.com/ajream/images/raw/master/img/20210825162528_image-20210825162527365.png)\n\n\n\n时序参数\n\n\n\n![image-20210825162600974](https://gitee.com/ajream/images/raw/master/img/20210825162602_image-20210825162600974.png)\n\n\n\n\n\n## 例子\n\n控制LCD1602显示字符12345\n\n\n\n```c\n#include \n\ntypedef unsigned char uchar;\ntypedef unsigned int uint;\n\nsbit RS = P3^5;\nsbit RW = P3^6;\nsbit EN = P3^4;\n\n//判断液晶忙,如果忙则等待\nvoid Read_Busy()\n{\n\tuchar busy;\n\tP0 = 0xff;\n\tRS = 0;\n\tRW = 1;\n\tdo\n\t{\n\t\tEN = 1;\n\t\tbusy = P0;\n\t\tEN = 0;\n\t}while(busy & 0x80);\n}\n//写LCD1602命令一个字节\nvoid Write_Cmd(uchar cmd)\n{\n\tRead_Busy();//判断忙\n\tRS = 0;\n\tRW = 0;\n\tP0 = cmd;\n\tEN = 1;\n\tEN = 0;\n}\n//写一个字节数据\nvoid Write_Dat(uchar dat)\n{\n\tRead_Busy();\n\tRS = 1;\n\tRW = 0;\n\tP0 = dat;\n\tEN = 1;\n\tEN = 0;\n}\nvoid main()\n{\n\tWrite_Cmd(0x38);//设置16*2显示\n\tWrite_Cmd(0x0f);//开显示,显示光标,光标闪烁\n\tWrite_Cmd(0x01);//清屏\n\n\tWrite_Cmd(0x06);//地址指针移位命令\n\tWrite_Cmd(0x80 | 0x06);//显示地址\n\n\tWrite_Dat(1 + '0'); //写入的字符要转换位ASCII码\n\tWrite_Dat(2 + '0');\n\tWrite_Dat(3 + '0');\n\tWrite_Dat(4 + '0');\n\tWrite_Dat(5 + '0');\n\twhile(1);\n}\n```\n\n","slug":"51单片机/51单片机学习笔记(九)","published":1,"updated":"2021-09-10T12:45:12.950Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6o30007akve3mey9pm8","content":"简单介绍 主要参数 显示容量:16X2个字符
\n芯片工作电压:4.5~5.5V, 最佳工作电压5.0V(2.0mA)
\n引脚说明 RS:为0时是指令输入,也就是表示显示在哪个“格子”的(1602有16X2共32个”格子”);
\n 为1时表示数据输入,即每个”格子“显示的内容
\nR/W:为1时是读,0是写;
\nE:读写数据时要置1
\nD0~D7:数据输入输出
\n
\n基本操作 读写操作 \n读状态,输入:RS=0, RW=1, E=0;
\n此时输出数据D0~D7为状态字,每一位都有各自的状态;
\n其中D0-D6表示当前数据地址指针的数值,D7的状态表示读写操作使能,如果是1表示禁止读写,0表示允许读写;
\n
\n \n写指令(即告诉1602要显示的位置):RS=0, RW=0, E=高脉冲,D0-D7为指令码
\n \n读数据:RS=1, RW=1, E=1, 此时D0-D7输出数据 \n写数据:RS=1, RW=0, D0-D7表示要写入的数据,E=高脉冲 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 void Read_Busy () {\tuchar busy; \tP0 = 0xff ; \tRS = 0 ; \tRW = 1 ; \tdo \t{ \t\tEN = 1 ; \t\tbusy = P0; \t\tEN = 0 ; \t}while (busy & 0x80 ); } void Write_Cmd (uchar cmd) { Read_Busy(); RS = 0 ; RW = 0 ; P0 = cmd; EN = 1 ; EN = 0 ; } void Write_Dat (uchar dat) {\tRead_Busy(); \tRS = 1 ; \tRW = 0 ; \tP0 = dat; \tEN = 1 ; \tEN = 0 ; }
\nRAM地址映射 控制器内部自带了80X8位(即80Bytes)的RAM缓冲区,对应关系如下图:
\n
\n注意:第10——27和50——67的地址上的字符是无法显示的,只能显示前面16X2个地址上所存放的字符
\n指令说明 初始化设置:
\n\n显示模式设置:指令码0x38(二进制是00111000),表示设置16X2显示,5X7点阵,8位数据接口
\n \n开/关光标设置:(配置时要转为16进制)
\n指令码(二进制形式)00001DCB
\n\nD=1开显示,D=0关显示
\n \nC=1显示光标,C=0不显示光标
\n \nB=1光标闪烁,B=0不闪烁 \n \n指令码(二进制形式)000001NS
\n\n \n数据控制
\n控制器内部有地址指针,通过发送指令 80H+地址码
(地址码范围是0——27H,40H——67H) 即可访问地址上的数据,进而进行设置
\n \n其他指令:
\n
\n \n \n时序图 读操作时序图
\n
\n写操作时序图
\n
\n时序参数
\n
\n例子 控制LCD1602显示字符12345
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 55 56 57 58 59 #include <reg52.h> typedef unsigned char uchar;typedef unsigned int uint;sbit RS = P3^5 ; sbit RW = P3^6 ; sbit EN = P3^4 ; void Read_Busy () {\tuchar busy; \tP0 = 0xff ; \tRS = 0 ; \tRW = 1 ; \tdo \t{ \t\tEN = 1 ; \t\tbusy = P0; \t\tEN = 0 ; \t}while (busy & 0x80 ); } void Write_Cmd (uchar cmd) {\tRead_Busy(); \tRS = 0 ; \tRW = 0 ; \tP0 = cmd; \tEN = 1 ; \tEN = 0 ; } void Write_Dat (uchar dat) {\tRead_Busy(); \tRS = 1 ; \tRW = 0 ; \tP0 = dat; \tEN = 1 ; \tEN = 0 ; } void main () {\tWrite_Cmd(0x38 ); \tWrite_Cmd(0x0f ); \tWrite_Cmd(0x01 ); \tWrite_Cmd(0x06 ); \tWrite_Cmd(0x80 | 0x06 ); \tWrite_Dat(1 + '0' ); \tWrite_Dat(2 + '0' ); \tWrite_Dat(3 + '0' ); \tWrite_Dat(4 + '0' ); \tWrite_Dat(5 + '0' ); \twhile (1 ); }
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"简单介绍 主要参数 显示容量:16X2个字符
\n芯片工作电压:4.5~5.5V, 最佳工作电压5.0V(2.0mA)
\n引脚说明 RS:为0时是指令输入,也就是表示显示在哪个“格子”的(1602有16X2共32个”格子”);
\n 为1时表示数据输入,即每个”格子“显示的内容
\nR/W:为1时是读,0是写;
\nE:读写数据时要置1
\nD0~D7:数据输入输出
\n
\n基本操作 读写操作 \n读状态,输入:RS=0, RW=1, E=0;
\n此时输出数据D0~D7为状态字,每一位都有各自的状态;
\n其中D0-D6表示当前数据地址指针的数值,D7的状态表示读写操作使能,如果是1表示禁止读写,0表示允许读写;
\n
\n \n写指令(即告诉1602要显示的位置):RS=0, RW=0, E=高脉冲,D0-D7为指令码
\n \n读数据:RS=1, RW=1, E=1, 此时D0-D7输出数据 \n写数据:RS=1, RW=0, D0-D7表示要写入的数据,E=高脉冲 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 void Read_Busy () {\tuchar busy; \tP0 = 0xff ; \tRS = 0 ; \tRW = 1 ; \tdo \t{ \t\tEN = 1 ; \t\tbusy = P0; \t\tEN = 0 ; \t}while (busy & 0x80 ); } void Write_Cmd (uchar cmd) { Read_Busy(); RS = 0 ; RW = 0 ; P0 = cmd; EN = 1 ; EN = 0 ; } void Write_Dat (uchar dat) {\tRead_Busy(); \tRS = 1 ; \tRW = 0 ; \tP0 = dat; \tEN = 1 ; \tEN = 0 ; }
\nRAM地址映射 控制器内部自带了80X8位(即80Bytes)的RAM缓冲区,对应关系如下图:
\n
\n注意:第10——27和50——67的地址上的字符是无法显示的,只能显示前面16X2个地址上所存放的字符
\n指令说明 初始化设置:
\n\n显示模式设置:指令码0x38(二进制是00111000),表示设置16X2显示,5X7点阵,8位数据接口
\n \n开/关光标设置:(配置时要转为16进制)
\n指令码(二进制形式)00001DCB
\n\nD=1开显示,D=0关显示
\n \nC=1显示光标,C=0不显示光标
\n \nB=1光标闪烁,B=0不闪烁 \n \n指令码(二进制形式)000001NS
\n\n \n数据控制
\n控制器内部有地址指针,通过发送指令 80H+地址码
(地址码范围是0——27H,40H——67H) 即可访问地址上的数据,进而进行设置
\n \n其他指令:
\n
\n \n \n时序图 读操作时序图
\n
\n写操作时序图
\n
\n时序参数
\n
\n例子 控制LCD1602显示字符12345
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 55 56 57 58 59 #include <reg52.h> typedef unsigned char uchar;typedef unsigned int uint;sbit RS = P3^5 ; sbit RW = P3^6 ; sbit EN = P3^4 ; void Read_Busy () {\tuchar busy; \tP0 = 0xff ; \tRS = 0 ; \tRW = 1 ; \tdo \t{ \t\tEN = 1 ; \t\tbusy = P0; \t\tEN = 0 ; \t}while (busy & 0x80 ); } void Write_Cmd (uchar cmd) {\tRead_Busy(); \tRS = 0 ; \tRW = 0 ; \tP0 = cmd; \tEN = 1 ; \tEN = 0 ; } void Write_Dat (uchar dat) {\tRead_Busy(); \tRS = 1 ; \tRW = 0 ; \tP0 = dat; \tEN = 1 ; \tEN = 0 ; } void main () {\tWrite_Cmd(0x38 ); \tWrite_Cmd(0x0f ); \tWrite_Cmd(0x01 ); \tWrite_Cmd(0x06 ); \tWrite_Cmd(0x80 | 0x06 ); \tWrite_Dat(1 + '0' ); \tWrite_Dat(2 + '0' ); \tWrite_Dat(3 + '0' ); \tWrite_Dat(4 + '0' ); \tWrite_Dat(5 + '0' ); \twhile (1 ); }
\n"},{"title":"51单片机学习笔记(三)","description":"串口通信","abbrlink":"383e39e3","date":"2021-08-19T05:17:20.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n# 串口通信\n\n## 原理简介\n\n通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式\n\n**并行通信**\n\n并行通信通常是将数据字节的各位用多条数据线同时进行传送\n\n特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。\n\n![image-20210822131931271](https://gitee.com/ajream/images/raw/master/img/20210822131937_image-20210822131931271.png)\n\n**串行通信**\n\n串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。\n\n![image-20210822132211661](https://gitee.com/ajream/images/raw/master/img/20210822132216_image-20210822132211661.png)\n\n**同步、异步通信**\n\n串行通信又分 同步、异步两种\n\n同步:接收方与发送方时钟一致\n\n异步:双方使用各自的时钟,但应该尽可能保持一致\n\n**数据传输方向**\n\n单工:一台设备只能收/发\n\n半双工:同一时间只能收或发\n\n全双工:可同时进行收发\n\n![image-20210822134038629](https://gitee.com/ajream/images/raw/master/img/20210822134041_image-20210822134038629.png)\n\n**奇偶校验**\n\n在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。\n\n- 奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;\n\n- 偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。\n\n**代码和校验**\n\n代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。\n\n**循环冗余校验**\n\n这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。\n\n\n\n## 使用串口进行数据发送和接收\n\n### 初始化配置\n\n1. **打开总中断和串口中断**\n\n ![image-20210822141906412](https://gitee.com/ajream/images/raw/master/img/20210822141909_image-20210822141906412.png)\n2. **选择串口工作方式(配置串口控制寄存器SCON)**不懂就先选方式1\n\n ![image-20210822142218968](https://gitee.com/ajream/images/raw/master/img/20210822142220_image-20210822142218968.png)\n3. **允许串口接收(配置串口控制寄存器SCON)**\n\n ![image-20210822142735027](https://gitee.com/ajream/images/raw/master/img/20210822142736_image-20210822142735027.png)\n\n ![image-20210822150203890](https://gitee.com/ajream/images/raw/master/img/20210822150205_image-20210822150203890.png)\n4. **配置波特率**\n\n 假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD\n\n ![image-20210822143113859](https://gitee.com/ajream/images/raw/master/img/20210822143115_image-20210822143113859.png)\n\n 代码如下:\n\n ```c\n void UARTInit()\n {\n \tEA = 1;\t//打开总中断\n \tES = 1; //打开串口中断\n \tSM0 = 0;\tSM1 = 1; //串口工作方式1,8位UART波特率可变\n \tREN = 1;//串口允许接收\n \n \tTR1 = 1;//启动定时器1\n \tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装\n \tTH1 = 0xfd;\n \tTL1 = 0xfd;//设置比特率9600\n }\n ```\n\n > 说明:TMOD |= 1, 使用了 `|=` 运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式\n >\n > ![image-20210822100638172](https://gitee.com/ajream/images/raw/master/img/20210822100641_image-20210822100638172.png)\n >\n > \n >\n > 这是T0的初始化:\n >\n > ```c\n > void T0_init()\n > {\n > \tEA = 1;\t//打开总中断\n > \tET0 = 1;//打开定时器0中断\n > \tTR0 = 1;\t //启动定时器0\n > \tREN = 1;//允许串口接收\n > \tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n > \tTH0 = 0xED;\n > \tTL0 = 0xFF; //定时5ms\n > }\n > \n > void UARTInit(){\n > //......\n > }\n > ```\n\n \n\n \n\n### 发送、接收数据\n\n\n\n![image-20210822145358670](https://gitee.com/ajream/images/raw/master/img/20210822145359_image-20210822145358670.png)\n\n接收:通过读取接收缓冲器SBUF中的数据\n\n发送:通过往发送缓冲器SBUF写入数据\n\n发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99\n\nRI、TI看这2幅图,如下\n\n![image-20210822142735027](https://gitee.com/ajream/images/raw/master/img/20210822142736_image-20210822142735027.png)\n\n![image-20210822150203890](https://gitee.com/ajream/images/raw/master/img/20210822150205_image-20210822150203890.png)\n\n```c\nvoid UART() interrupt 4\n{\n\tuchar temp;\n\tif(RI)\t\t\t\t\t//判断接收是否完成\n\t{\n\t\tnum = SBUF;\t\t\t//读SBUF,读出串口接收到的数据\n\t\tRI = 0;\t\t\t\t//软件清零接收标志位\t\n\t\ttemp = num;\n\t\tSBUF = ++temp; \t//写SBUF,把要发送的数据送给发送缓存器\n\t}\n\tif(TI)\t\t\t\t\t//判断是否发送完成\n\t\tTI = 0;\t\t\t\t//清零发送完成标志位\t\n} \n```\n\n\n\n\n\n完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出\n\n```c\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nuchar num;//数码管显示的值\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid T0_init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tREN = 1;//允许串口接收\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n\n//串口初始化\nvoid UART_init()\n{\n\tEA = 1;\t//打开总中断\n\tES = 1; //打开串口中断\n\tSM0 = 0;\tSM1 = 1;//串口工作方式1,8位UART波特率可变\n\tREN = 1;//串口允许接收\n\tTR1 = 1;//启动定时器1\n\tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装\n\tTH1 = 0xfd;\n\tTL1 = 0xfd;//设置比特率9600\n}\nvoid main()//main函数自身会循环\n{\t\n\tT0_init();//定时器0初始化\n\tUART_init();//串口初始化\n\twhile(1);\t\n} \n\n//定时器0中断函数\nvoid t0() interrupt 1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n}\n\n//串口中断函数\nvoid UART() interrupt 4\n{\n\tuchar temp;\n\tif(RI)//判断接收是否完成\n\t{\n\t\tnum = SBUF;//读SBUF,读出串口接收到的数据\n\t\tRI = 0;//软件清零接收标志位\t\n\t\ttemp = num;//\n\t\tSBUF = ++temp;//写SBUF,把要发送的数据送给发送缓存器\n\t}\n\tif(TI)//判断是否发送完成\n\t\tTI = 0;//清零发送完成标志位\t\n} \n```\n\n","source":"_posts/51单片机/51单片机学习笔记(三).md","raw":"---\ntitle: 51单片机学习笔记(三)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 串口通信\nabbrlink: 383e39e3\ndate: 2021-08-19 13:17:20\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n# 串口通信\n\n## 原理简介\n\n通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式\n\n**并行通信**\n\n并行通信通常是将数据字节的各位用多条数据线同时进行传送\n\n特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。\n\n![image-20210822131931271](https://gitee.com/ajream/images/raw/master/img/20210822131937_image-20210822131931271.png)\n\n**串行通信**\n\n串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。\n\n![image-20210822132211661](https://gitee.com/ajream/images/raw/master/img/20210822132216_image-20210822132211661.png)\n\n**同步、异步通信**\n\n串行通信又分 同步、异步两种\n\n同步:接收方与发送方时钟一致\n\n异步:双方使用各自的时钟,但应该尽可能保持一致\n\n**数据传输方向**\n\n单工:一台设备只能收/发\n\n半双工:同一时间只能收或发\n\n全双工:可同时进行收发\n\n![image-20210822134038629](https://gitee.com/ajream/images/raw/master/img/20210822134041_image-20210822134038629.png)\n\n**奇偶校验**\n\n在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。\n\n- 奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;\n\n- 偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。\n\n**代码和校验**\n\n代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。\n\n**循环冗余校验**\n\n这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。\n\n\n\n## 使用串口进行数据发送和接收\n\n### 初始化配置\n\n1. **打开总中断和串口中断**\n\n ![image-20210822141906412](https://gitee.com/ajream/images/raw/master/img/20210822141909_image-20210822141906412.png)\n2. **选择串口工作方式(配置串口控制寄存器SCON)**不懂就先选方式1\n\n ![image-20210822142218968](https://gitee.com/ajream/images/raw/master/img/20210822142220_image-20210822142218968.png)\n3. **允许串口接收(配置串口控制寄存器SCON)**\n\n ![image-20210822142735027](https://gitee.com/ajream/images/raw/master/img/20210822142736_image-20210822142735027.png)\n\n ![image-20210822150203890](https://gitee.com/ajream/images/raw/master/img/20210822150205_image-20210822150203890.png)\n4. **配置波特率**\n\n 假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD\n\n ![image-20210822143113859](https://gitee.com/ajream/images/raw/master/img/20210822143115_image-20210822143113859.png)\n\n 代码如下:\n\n ```c\n void UARTInit()\n {\n \tEA = 1;\t//打开总中断\n \tES = 1; //打开串口中断\n \tSM0 = 0;\tSM1 = 1; //串口工作方式1,8位UART波特率可变\n \tREN = 1;//串口允许接收\n \n \tTR1 = 1;//启动定时器1\n \tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装\n \tTH1 = 0xfd;\n \tTL1 = 0xfd;//设置比特率9600\n }\n ```\n\n > 说明:TMOD |= 1, 使用了 `|=` 运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式\n >\n > ![image-20210822100638172](https://gitee.com/ajream/images/raw/master/img/20210822100641_image-20210822100638172.png)\n >\n > \n >\n > 这是T0的初始化:\n >\n > ```c\n > void T0_init()\n > {\n > \tEA = 1;\t//打开总中断\n > \tET0 = 1;//打开定时器0中断\n > \tTR0 = 1;\t //启动定时器0\n > \tREN = 1;//允许串口接收\n > \tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n > \tTH0 = 0xED;\n > \tTL0 = 0xFF; //定时5ms\n > }\n > \n > void UARTInit(){\n > //......\n > }\n > ```\n\n \n\n \n\n### 发送、接收数据\n\n\n\n![image-20210822145358670](https://gitee.com/ajream/images/raw/master/img/20210822145359_image-20210822145358670.png)\n\n接收:通过读取接收缓冲器SBUF中的数据\n\n发送:通过往发送缓冲器SBUF写入数据\n\n发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99\n\nRI、TI看这2幅图,如下\n\n![image-20210822142735027](https://gitee.com/ajream/images/raw/master/img/20210822142736_image-20210822142735027.png)\n\n![image-20210822150203890](https://gitee.com/ajream/images/raw/master/img/20210822150205_image-20210822150203890.png)\n\n```c\nvoid UART() interrupt 4\n{\n\tuchar temp;\n\tif(RI)\t\t\t\t\t//判断接收是否完成\n\t{\n\t\tnum = SBUF;\t\t\t//读SBUF,读出串口接收到的数据\n\t\tRI = 0;\t\t\t\t//软件清零接收标志位\t\n\t\ttemp = num;\n\t\tSBUF = ++temp; \t//写SBUF,把要发送的数据送给发送缓存器\n\t}\n\tif(TI)\t\t\t\t\t//判断是否发送完成\n\t\tTI = 0;\t\t\t\t//清零发送完成标志位\t\n} \n```\n\n\n\n\n\n完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出\n\n```c\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nuchar num;//数码管显示的值\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid T0_init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tREN = 1;//允许串口接收\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n\n//串口初始化\nvoid UART_init()\n{\n\tEA = 1;\t//打开总中断\n\tES = 1; //打开串口中断\n\tSM0 = 0;\tSM1 = 1;//串口工作方式1,8位UART波特率可变\n\tREN = 1;//串口允许接收\n\tTR1 = 1;//启动定时器1\n\tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装\n\tTH1 = 0xfd;\n\tTL1 = 0xfd;//设置比特率9600\n}\nvoid main()//main函数自身会循环\n{\t\n\tT0_init();//定时器0初始化\n\tUART_init();//串口初始化\n\twhile(1);\t\n} \n\n//定时器0中断函数\nvoid t0() interrupt 1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n}\n\n//串口中断函数\nvoid UART() interrupt 4\n{\n\tuchar temp;\n\tif(RI)//判断接收是否完成\n\t{\n\t\tnum = SBUF;//读SBUF,读出串口接收到的数据\n\t\tRI = 0;//软件清零接收标志位\t\n\t\ttemp = num;//\n\t\tSBUF = ++temp;//写SBUF,把要发送的数据送给发送缓存器\n\t}\n\tif(TI)//判断是否发送完成\n\t\tTI = 0;//清零发送完成标志位\t\n} \n```\n\n","slug":"51单片机/51单片机学习笔记(三)","published":1,"updated":"2021-09-10T12:45:31.265Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6o40009akvehm657ia2","content":"串口通信 原理简介 通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式
\n并行通信
\n并行通信通常是将数据字节的各位用多条数据线同时进行传送
\n特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。
\n
\n串行通信
\n串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。
\n
\n同步、异步通信
\n串行通信又分 同步、异步两种
\n同步:接收方与发送方时钟一致
\n异步:双方使用各自的时钟,但应该尽可能保持一致
\n数据传输方向
\n单工:一台设备只能收/发
\n半双工:同一时间只能收或发
\n全双工:可同时进行收发
\n
\n奇偶校验
\n在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。
\n\n代码和校验
\n代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。
\n循环冗余校验
\n这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。
\n使用串口进行数据发送和接收 初始化配置 \n打开总中断和串口中断
\n
\n \n选择串口工作方式(配置串口控制寄存器SCON) 不懂就先选方式1
\n
\n \n允许串口接收(配置串口控制寄存器SCON)
\n
\n
\n \n配置波特率
\n假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD
\n
\n代码如下:
\n1 2 3 4 5 6 7 8 9 10 11 12 void UARTInit () {\tEA = 1 ;\t \tES = 1 ; \tSM0 = 0 ;\tSM1 = 1 ; \tREN = 1 ; \tTR1 = 1 ; \tTMOD |= 0x20 ; \tTH1 = 0xfd ; \tTL1 = 0xfd ; }
\n\n说明:TMOD |= 1, 使用了 |=
运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式
\n
\n这是T0的初始化:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 void T0_init () {\tEA = 1 ;\t \tET0 = 1 ; \tTR0 = 1 ;\t \tREN = 1 ; \tTMOD |= 0X01 ; \tTH0 = 0xED ; \tTL0 = 0xFF ; } void UARTInit () {}
\n \n \n \n发送、接收数据
\n接收:通过读取接收缓冲器SBUF中的数据
\n发送:通过往发送缓冲器SBUF写入数据
\n发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99
\nRI、TI看这2幅图,如下
\n
\n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 void UART () interrupt 4 {\tuchar temp; \tif (RI)\t\t\t\t\t \t{ \t\tnum = SBUF;\t\t\t \t\tRI = 0 ;\t\t\t\t \t\ttemp = num; \t\tSBUF = ++temp; \t \t} \tif (TI)\t\t\t\t\t \t\tTI = 0 ;\t\t\t\t }
\n完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; uchar num; uchar code SMGduan[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F ,}; uchar code SMGwei[] = {0xfe , 0xfd , 0xfb }; void display (uchar i) {\tstatic uchar wei; \t\t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = SMGwei[wei]; \tWE = 0 ; \tswitch (wei) \t{ \t\tcase 0 : DU = 1 ; P0 = SMGduan[i / 100 ]; DU = 0 ; break ; \t\tcase 1 : DU = 1 ; P0 = SMGduan[i % 100 / 10 ]; DU = 0 ; break ;\t \t\tcase 2 : DU = 1 ; P0 = SMGduan[i % 10 ]; DU = 0 ; break ;\t\t \t} \twei++; \tif (wei == 3 ) \t\twei = 0 ; } void T0_init () {\tEA = 1 ;\t \tET0 = 1 ; \tTR0 = 1 ;\t \tREN = 1 ; \tTMOD |= 0X01 ; \tTH0 = 0xED ; \tTL0 = 0xFF ; } void UART_init () {\tEA = 1 ;\t \tES = 1 ; \tSM0 = 0 ;\tSM1 = 1 ; \tREN = 1 ; \tTR1 = 1 ; \tTMOD |= 0x20 ; \tTH1 = 0xfd ; \tTL1 = 0xfd ; } void main () {\t\tT0_init(); \tUART_init(); \twhile (1 );\t } void t0 () interrupt 1 {\tTH0 = 0xED ; \tTL0 = 0xFF ; \tdisplay(num); } void UART () interrupt 4 {\tuchar temp; \tif (RI) \t{ \t\tnum = SBUF; \t\tRI = 0 ; \t\ttemp = num; \t\tSBUF = ++temp; \t} \tif (TI) \t\tTI = 0 ; }
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"串口通信 原理简介 通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式
\n并行通信
\n并行通信通常是将数据字节的各位用多条数据线同时进行传送
\n特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。
\n
\n串行通信
\n串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。
\n
\n同步、异步通信
\n串行通信又分 同步、异步两种
\n同步:接收方与发送方时钟一致
\n异步:双方使用各自的时钟,但应该尽可能保持一致
\n数据传输方向
\n单工:一台设备只能收/发
\n半双工:同一时间只能收或发
\n全双工:可同时进行收发
\n
\n奇偶校验
\n在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。
\n\n代码和校验
\n代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。
\n循环冗余校验
\n这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。
\n使用串口进行数据发送和接收 初始化配置 \n打开总中断和串口中断
\n
\n \n选择串口工作方式(配置串口控制寄存器SCON) 不懂就先选方式1
\n
\n \n允许串口接收(配置串口控制寄存器SCON)
\n
\n
\n \n配置波特率
\n假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD
\n
\n代码如下:
\n1 2 3 4 5 6 7 8 9 10 11 12 void UARTInit () {\tEA = 1 ;\t \tES = 1 ; \tSM0 = 0 ;\tSM1 = 1 ; \tREN = 1 ; \tTR1 = 1 ; \tTMOD |= 0x20 ; \tTH1 = 0xfd ; \tTL1 = 0xfd ; }
\n\n说明:TMOD |= 1, 使用了 |=
运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式
\n
\n这是T0的初始化:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 void T0_init () {\tEA = 1 ;\t \tET0 = 1 ; \tTR0 = 1 ;\t \tREN = 1 ; \tTMOD |= 0X01 ; \tTH0 = 0xED ; \tTL0 = 0xFF ; } void UARTInit () {}
\n \n \n \n发送、接收数据
\n接收:通过读取接收缓冲器SBUF中的数据
\n发送:通过往发送缓冲器SBUF写入数据
\n发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99
\nRI、TI看这2幅图,如下
\n
\n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 void UART () interrupt 4 {\tuchar temp; \tif (RI)\t\t\t\t\t \t{ \t\tnum = SBUF;\t\t\t \t\tRI = 0 ;\t\t\t\t \t\ttemp = num; \t\tSBUF = ++temp; \t \t} \tif (TI)\t\t\t\t\t \t\tTI = 0 ;\t\t\t\t }
\n完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; uchar num; uchar code SMGduan[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F ,}; uchar code SMGwei[] = {0xfe , 0xfd , 0xfb }; void display (uchar i) {\tstatic uchar wei; \t\t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = SMGwei[wei]; \tWE = 0 ; \tswitch (wei) \t{ \t\tcase 0 : DU = 1 ; P0 = SMGduan[i / 100 ]; DU = 0 ; break ; \t\tcase 1 : DU = 1 ; P0 = SMGduan[i % 100 / 10 ]; DU = 0 ; break ;\t \t\tcase 2 : DU = 1 ; P0 = SMGduan[i % 10 ]; DU = 0 ; break ;\t\t \t} \twei++; \tif (wei == 3 ) \t\twei = 0 ; } void T0_init () {\tEA = 1 ;\t \tET0 = 1 ; \tTR0 = 1 ;\t \tREN = 1 ; \tTMOD |= 0X01 ; \tTH0 = 0xED ; \tTL0 = 0xFF ; } void UART_init () {\tEA = 1 ;\t \tES = 1 ; \tSM0 = 0 ;\tSM1 = 1 ; \tREN = 1 ; \tTR1 = 1 ; \tTMOD |= 0x20 ; \tTH1 = 0xfd ; \tTL1 = 0xfd ; } void main () {\t\tT0_init(); \tUART_init(); \twhile (1 );\t } void t0 () interrupt 1 {\tTH0 = 0xED ; \tTL0 = 0xFF ; \tdisplay(num); } void UART () interrupt 4 {\tuchar temp; \tif (RI) \t{ \t\tnum = SBUF; \t\tRI = 0 ; \t\ttemp = num; \t\tSBUF = ++temp; \t} \tif (TI) \t\tTI = 0 ; }
\n"},{"title":"51单片机学习笔记(二)","description":"定时-计数器、中断系统","abbrlink":"46cd19c8","date":"2021-08-19T00:57:20.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n# 定时计数器\n\n51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)\n\n这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。\n\n当工作在**定时模式**时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。\n可以算出工作在定时模式时最高单次定时时间:65535*1.085us\n\n当工作在**计数器模式**时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1\n\n\n## 使用定时-计数器\n### (一)启动定时计数器\n\nT0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)\n\n![image-20210822095401344](https://gitee.com/ajream/images/raw/master/img/20210822095407_image-20210822095401344.png)\n\n> 总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)\n\n### (二)设定定时计数器的工作模式\n\n这一步由寄存器TMOD(不可位寻址 )控制\n\n![image-20210822100638172](https://gitee.com/ajream/images/raw/master/img/20210822100641_image-20210822100638172.png)\n\n第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数\n\n第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式\n\nM1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:\n\n| M1 | M0 | 工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位) |\n| ---- | ---- | ------------------------------------------------------------ |\n| 0 | 0 | 13位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:$2^{13}-1=8191$ |\n| 0 | 1 | 16位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535 |\n| 1 | 0 | 8位自动重装载方式,溢出时自动将THx的值装载到TLx里面 |\n| 1 | 1 | 如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到($2^8-1=255$),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出) |\n\n### (三)查询定时计数器是否溢出\n\n上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0\n\n\n\n```c\n/******************************************************************\n* 【程序功能】: \t定时器0工作模式1 16位定时模式,数码管动态显示0-10,秒表。\t\n*******************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid display(uchar i)\n{\n\tuchar bai, shi, ge;\n\tbai = i / 100; //236 / 100 = 2\n\tshi = i % 100 / 10;\t//236 % 100 / 10 = 3\n\tge = i % 10;//236 % 10 =6\n\t\n\t//第一位数码管 \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[bai];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第二位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFD; //1111 1101\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[shi];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第三位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFB; //1111 1011\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[ge];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n}\n\n//定时器T0初始化\nvoid T0_init()\n{\n\tTR0 = 1;\t //启动定时器0\n\tTMOD = 0X01; //定时器工作模式1,16位定时器计数模式\n\tTH0 = 0x4b;\n\tTL0 = 0xfd; //定时50ms\n //0x4bfd即19453,共计数65535-19453+1 = 46083次,耗时46083*1.085us = 50 ms\n}\n\nvoid main()//main函数自身会循环\n{\t\n\tuchar mSec, Sec;//毫秒和秒储存变量\n\tT0_init();//定时器T0初始化\n\twhile(1)\n\t{\n\t\tif(TF0 == 1)//判断是否溢出\n\t\t{\n\t\t\tTF0 = 0;//软件清零溢出标志位\n\t\t\tTH0 = 0x4b;\n\t\t\tTL0 = 0xfd; //定时50ms\n\t\t\tmSec++;//50ms到\n\t\t\tif(mSec == 20)\n\t\t\t{\n\t\t\t\tmSec = 0;\n\t\t\t\tSec++;//1秒时间到\n\t\t\t}\t\t\t\t\t\n\t\t}\n\t\tdisplay(Sec); //数码管显示函数\n\t\tif(Sec > 10)\n\t\t\tSec = 0;//秒清零 \n\t}\t\n} \n```\n\n方式1初值计算方法,假如定时t(单位:us),使用T0:\n\n则:\n\n```\nTH0 = (65535 - t / 1.085) / 256\nTL0 = (65535 - t / 1.085) % 256\n四舍五入取整后转换为16进制\n```\n\n\n\n\n\n\n# 中断系统\n\n主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断\n\n![image-20210822084113315](https://gitee.com/ajream/images/raw/master/img/20210822084120_image-20210822084113315.png)\n\n引起CPU中断的根源,称为**中断源**。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。\n\n51子系列存在5个中断源:\n**外部中断源2个**\n\n1. INT0——由P3.2端口线引入,低电平或下降沿引起。\n\n2. INT1——由P3.3端口线引入,低电平或下降沿引起。\n\n 这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制\n\n**内部中断源3个**\n\n1. T0——定时器/计数器0中断,由T0回零溢出引起。\n2. T1——定时器/计数器1中断,由T1回零溢出引起。\n3. TI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。\n \n 这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。\n\n## 使用中断\n\n如何使用中断,有3大步骤:\n\n### (一)允许产生中断\n\n要允许产生中断首先要配置中断允许寄存器IE和XICON\n\n(**可位寻址**表示每一位都已经用变量定义好了,比如第7位是EA)\n\n![image-20210822091845225](https://gitee.com/ajream/images/raw/master/img/20210822091850_image-20210822091845225.png)\n\n> 总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断\n\n### (二)设置什么时候响应中断\n\n什么时候响应中断由控制寄存器TCON控制\n\n![image-20210822092158946](https://gitee.com/ajream/images/raw/master/img/20210822092202_image-20210822092158946.png)\n\nTFx:当计数值溢出时,就会向cpu发出中断请求,然后**自动**进入对应的**中断处理函数**,然后TFx由硬件自动清0\n\n其它IEx, ITx也差不多是这样\n\n### (三)产生中断后你要干什么——中断处理函数\n\n中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:\n\n格式如下:\n\n```c\nvoid 函数名() interrupt 中断入口号 //中断处理函数,加关键字interrupt和入口号0/1/2/3/4\n{\n //中断处理语句\n}\n\n```\n\n\n\n> 中断优先级:\n>\n> 如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念\n>\n> 中断优先级级别越高,就越先被执行 \n>\n> ![image-20210822120231820](https://gitee.com/ajream/images/raw/master/img/20210822120238_image-20210822120231820.png)\n\n\n\n```c\n/*\n定时器0, 工作模式1, 16位定时计数\n数码管动态显示0-10,每隔1秒显示一次\n*/\n\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit key_s2 = P3^0;//独立按键S2\nsbit key_s3 = P3^1;//独立按键S3\nuchar num;//数码管显示的值\nuchar mSec, Sec;//毫秒和秒储存变量\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid T0_init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD = 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n\nvoid main() //主程序执行按键扫描\n{\t\n\tT0_init();//定时器0初始化\n\twhile(1)\n\t{\n\t\tif(key_s2 == 0)//判断S2是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s2 == 0)\n\t\t\t{\n\t\t\t\tif(num != 120)\n\t\t\t\tnum++;\n\t\t\t\twhile(!key_s2);//松手检测\n\t\t\t}\t\n\t\t}\n\t\tif(key_s3 == 0)//判断S3是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s3 == 0)\n\t\t\t{\n\t\t\t\tif(num > 0)\n\t\t\t\t\tnum--;\n\t\t\t\twhile(!key_s3);//松手检测\n\t\t\t}\t\n\t\t}\n\t}\t\n} \n\n//定时器0中断处理函数\nvoid timer0() interrupt 1 //T0中断入口为1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示s\n} \n```\n\n","source":"_posts/51单片机/51单片机学习笔记(二).md","raw":"---\ntitle: 51单片机学习笔记(二)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 定时-计数器、中断系统\nabbrlink: 46cd19c8\ndate: 2021-08-19 08:57:20\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n# 定时计数器\n\n51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)\n\n这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。\n\n当工作在**定时模式**时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。\n可以算出工作在定时模式时最高单次定时时间:65535*1.085us\n\n当工作在**计数器模式**时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1\n\n\n## 使用定时-计数器\n### (一)启动定时计数器\n\nT0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)\n\n![image-20210822095401344](https://gitee.com/ajream/images/raw/master/img/20210822095407_image-20210822095401344.png)\n\n> 总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)\n\n### (二)设定定时计数器的工作模式\n\n这一步由寄存器TMOD(不可位寻址 )控制\n\n![image-20210822100638172](https://gitee.com/ajream/images/raw/master/img/20210822100641_image-20210822100638172.png)\n\n第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数\n\n第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式\n\nM1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:\n\n| M1 | M0 | 工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位) |\n| ---- | ---- | ------------------------------------------------------------ |\n| 0 | 0 | 13位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:$2^{13}-1=8191$ |\n| 0 | 1 | 16位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535 |\n| 1 | 0 | 8位自动重装载方式,溢出时自动将THx的值装载到TLx里面 |\n| 1 | 1 | 如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到($2^8-1=255$),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出) |\n\n### (三)查询定时计数器是否溢出\n\n上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0\n\n\n\n```c\n/******************************************************************\n* 【程序功能】: \t定时器0工作模式1 16位定时模式,数码管动态显示0-10,秒表。\t\n*******************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid display(uchar i)\n{\n\tuchar bai, shi, ge;\n\tbai = i / 100; //236 / 100 = 2\n\tshi = i % 100 / 10;\t//236 % 100 / 10 = 3\n\tge = i % 10;//236 % 10 =6\n\t\n\t//第一位数码管 \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[bai];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第二位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFD; //1111 1101\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[shi];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第三位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFB; //1111 1011\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[ge];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n}\n\n//定时器T0初始化\nvoid T0_init()\n{\n\tTR0 = 1;\t //启动定时器0\n\tTMOD = 0X01; //定时器工作模式1,16位定时器计数模式\n\tTH0 = 0x4b;\n\tTL0 = 0xfd; //定时50ms\n //0x4bfd即19453,共计数65535-19453+1 = 46083次,耗时46083*1.085us = 50 ms\n}\n\nvoid main()//main函数自身会循环\n{\t\n\tuchar mSec, Sec;//毫秒和秒储存变量\n\tT0_init();//定时器T0初始化\n\twhile(1)\n\t{\n\t\tif(TF0 == 1)//判断是否溢出\n\t\t{\n\t\t\tTF0 = 0;//软件清零溢出标志位\n\t\t\tTH0 = 0x4b;\n\t\t\tTL0 = 0xfd; //定时50ms\n\t\t\tmSec++;//50ms到\n\t\t\tif(mSec == 20)\n\t\t\t{\n\t\t\t\tmSec = 0;\n\t\t\t\tSec++;//1秒时间到\n\t\t\t}\t\t\t\t\t\n\t\t}\n\t\tdisplay(Sec); //数码管显示函数\n\t\tif(Sec > 10)\n\t\t\tSec = 0;//秒清零 \n\t}\t\n} \n```\n\n方式1初值计算方法,假如定时t(单位:us),使用T0:\n\n则:\n\n```\nTH0 = (65535 - t / 1.085) / 256\nTL0 = (65535 - t / 1.085) % 256\n四舍五入取整后转换为16进制\n```\n\n\n\n\n\n\n# 中断系统\n\n主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断\n\n![image-20210822084113315](https://gitee.com/ajream/images/raw/master/img/20210822084120_image-20210822084113315.png)\n\n引起CPU中断的根源,称为**中断源**。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。\n\n51子系列存在5个中断源:\n**外部中断源2个**\n\n1. INT0——由P3.2端口线引入,低电平或下降沿引起。\n\n2. INT1——由P3.3端口线引入,低电平或下降沿引起。\n\n 这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制\n\n**内部中断源3个**\n\n1. T0——定时器/计数器0中断,由T0回零溢出引起。\n2. T1——定时器/计数器1中断,由T1回零溢出引起。\n3. TI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。\n \n 这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。\n\n## 使用中断\n\n如何使用中断,有3大步骤:\n\n### (一)允许产生中断\n\n要允许产生中断首先要配置中断允许寄存器IE和XICON\n\n(**可位寻址**表示每一位都已经用变量定义好了,比如第7位是EA)\n\n![image-20210822091845225](https://gitee.com/ajream/images/raw/master/img/20210822091850_image-20210822091845225.png)\n\n> 总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断\n\n### (二)设置什么时候响应中断\n\n什么时候响应中断由控制寄存器TCON控制\n\n![image-20210822092158946](https://gitee.com/ajream/images/raw/master/img/20210822092202_image-20210822092158946.png)\n\nTFx:当计数值溢出时,就会向cpu发出中断请求,然后**自动**进入对应的**中断处理函数**,然后TFx由硬件自动清0\n\n其它IEx, ITx也差不多是这样\n\n### (三)产生中断后你要干什么——中断处理函数\n\n中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:\n\n格式如下:\n\n```c\nvoid 函数名() interrupt 中断入口号 //中断处理函数,加关键字interrupt和入口号0/1/2/3/4\n{\n //中断处理语句\n}\n\n```\n\n\n\n> 中断优先级:\n>\n> 如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念\n>\n> 中断优先级级别越高,就越先被执行 \n>\n> ![image-20210822120231820](https://gitee.com/ajream/images/raw/master/img/20210822120238_image-20210822120231820.png)\n\n\n\n```c\n/*\n定时器0, 工作模式1, 16位定时计数\n数码管动态显示0-10,每隔1秒显示一次\n*/\n\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit key_s2 = P3^0;//独立按键S2\nsbit key_s3 = P3^1;//独立按键S3\nuchar num;//数码管显示的值\nuchar mSec, Sec;//毫秒和秒储存变量\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid T0_init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD = 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n\nvoid main() //主程序执行按键扫描\n{\t\n\tT0_init();//定时器0初始化\n\twhile(1)\n\t{\n\t\tif(key_s2 == 0)//判断S2是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s2 == 0)\n\t\t\t{\n\t\t\t\tif(num != 120)\n\t\t\t\tnum++;\n\t\t\t\twhile(!key_s2);//松手检测\n\t\t\t}\t\n\t\t}\n\t\tif(key_s3 == 0)//判断S3是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s3 == 0)\n\t\t\t{\n\t\t\t\tif(num > 0)\n\t\t\t\t\tnum--;\n\t\t\t\twhile(!key_s3);//松手检测\n\t\t\t}\t\n\t\t}\n\t}\t\n} \n\n//定时器0中断处理函数\nvoid timer0() interrupt 1 //T0中断入口为1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示s\n} \n```\n\n","slug":"51单片机/51单片机学习笔记(二)","published":1,"updated":"2021-09-10T12:45:06.012Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6o6000bakve2lmd5swc","content":"定时计数器 51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)
\n这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。
\n当工作在定时模式 时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。 可以算出工作在定时模式时最高单次定时时间:65535*1.085us
\n当工作在计数器模式 时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1
\n使用定时-计数器 (一)启动定时计数器 T0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)
\n
\n\n总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)
\n \n(二)设定定时计数器的工作模式 这一步由寄存器TMOD(不可位寻址 )控制
\n
\n第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数
\n第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式
\nM1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:
\n\n
\n\n\nM1 \nM0 \n工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位) \n \n \n\n\n0 \n0 \n13位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:$2^{13}-1=8191$ \n \n\n0 \n1 \n16位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535 \n \n\n1 \n0 \n8位自动重装载方式,溢出时自动将THx的值装载到TLx里面 \n \n\n1 \n1 \n如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到($2^8-1=255$),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出) \n \n \n
\n
\n(三)查询定时计数器是否溢出 上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; uchar code tabel[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F ,}; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void display (uchar i) {\tuchar bai, shi, ge; \tbai = i / 100 ; \tshi = i % 100 / 10 ;\t \tge = i % 10 ; \t \t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = 0XFE ; \tWE = 0 ; \t \tDU = 1 ; \tP0 = tabel[bai]; \tDU = 0 ; \tdelay(5 ); \t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = 0XFD ; \tWE = 0 ; \t \tDU = 1 ; \tP0 = tabel[shi]; \tDU = 0 ; \tdelay(5 ); \t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = 0XFB ; \tWE = 0 ; \t \tDU = 1 ; \tP0 = tabel[ge]; \tDU = 0 ; \tdelay(5 ); } void T0_init () {\tTR0 = 1 ;\t \tTMOD = 0X01 ; \tTH0 = 0x4b ; \tTL0 = 0xfd ; } void main () {\t\tuchar mSec, Sec; \tT0_init(); \twhile (1 ) \t{ \t\tif (TF0 == 1 ) \t\t{ \t\t\tTF0 = 0 ; \t\t\tTH0 = 0x4b ; \t\t\tTL0 = 0xfd ; \t\t\tmSec++; \t\t\tif (mSec == 20 ) \t\t\t{ \t\t\t\tmSec = 0 ; \t\t\t\tSec++; \t\t\t}\t\t\t\t\t \t\t} \t\tdisplay(Sec); \t\tif (Sec > 10 ) \t\t\tSec = 0 ; \t}\t }
\n方式1初值计算方法,假如定时t(单位:us),使用T0:
\n则:
\n1 2 3 TH0 = (65535 - t / 1.085) / 256 TL0 = (65535 - t / 1.085) % 256 四舍五入取整后转换为16进制
\n中断系统 主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断
\n
\n引起CPU中断的根源,称为中断源 。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。
\n51子系列存在5个中断源:外部中断源2个
\n\nINT0——由P3.2端口线引入,低电平或下降沿引起。
\n \nINT1——由P3.3端口线引入,低电平或下降沿引起。
\n 这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制
\n \n \n内部中断源3个
\n\nT0——定时器/计数器0中断,由T0回零溢出引起。 \nT1——定时器/计数器1中断,由T1回零溢出引起。 \nTI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。
\n 这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。
\n \n \n使用中断 如何使用中断,有3大步骤:
\n(一)允许产生中断 要允许产生中断首先要配置中断允许寄存器IE和XICON
\n(可位寻址 表示每一位都已经用变量定义好了,比如第7位是EA)
\n
\n\n总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断
\n \n(二)设置什么时候响应中断 什么时候响应中断由控制寄存器TCON控制
\n
\nTFx:当计数值溢出时,就会向cpu发出中断请求,然后自动 进入对应的中断处理函数 ,然后TFx由硬件自动清0
\n其它IEx, ITx也差不多是这样
\n(三)产生中断后你要干什么——中断处理函数 中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:
\n格式如下:
\n1 2 3 4 5 void 函数名() interrupt 中断入口号 { }
\n\n中断优先级:
\n如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念
\n中断优先级级别越高,就越先被执行
\n
\n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; sbit key_s2 = P3^0 ; sbit key_s3 = P3^1 ; uchar num; uchar mSec, Sec; uchar code SMGduan[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F ,}; uchar code SMGwei[] = {0xfe , 0xfd , 0xfb }; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void display (uchar i) {\tstatic uchar wei; \t\t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = SMGwei[wei]; \tWE = 0 ; \tswitch (wei) \t{ \t\tcase 0 : DU = 1 ; P0 = SMGduan[i / 100 ]; DU = 0 ; break ; \t\tcase 1 : DU = 1 ; P0 = SMGduan[i % 100 / 10 ]; DU = 0 ; break ;\t \t\tcase 2 : DU = 1 ; P0 = SMGduan[i % 10 ]; DU = 0 ; break ;\t\t \t} \twei++; \tif (wei == 3 ) \t\twei = 0 ; } void T0_init () {\tEA = 1 ;\t \tET0 = 1 ; \tTR0 = 1 ;\t \tTMOD = 0X01 ; \tTH0 = 0xED ; \tTL0 = 0xFF ; } void main () {\t\tT0_init(); \twhile (1 ) \t{ \t\tif (key_s2 == 0 ) \t\t{ \t\t\tdelay(20 ); \t\t\tif (key_s2 == 0 ) \t\t\t{ \t\t\t\tif (num != 120 ) \t\t\t\tnum++; \t\t\t\twhile (!key_s2); \t\t\t}\t \t\t} \t\tif (key_s3 == 0 ) \t\t{ \t\t\tdelay(20 ); \t\t\tif (key_s3 == 0 ) \t\t\t{ \t\t\t\tif (num > 0 ) \t\t\t\t\tnum--; \t\t\t\twhile (!key_s3); \t\t\t}\t \t\t} \t}\t } void timer0 () interrupt 1 {\tTH0 = 0xED ; \tTL0 = 0xFF ; \tdisplay(num); }
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"定时计数器 51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)
\n这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。
\n当工作在定时模式 时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。 可以算出工作在定时模式时最高单次定时时间:65535*1.085us
\n当工作在计数器模式 时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1
\n使用定时-计数器 (一)启动定时计数器 T0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)
\n
\n\n总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)
\n \n(二)设定定时计数器的工作模式 这一步由寄存器TMOD(不可位寻址 )控制
\n
\n第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数
\n第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式
\nM1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:
\n\n
\n\n\nM1 \nM0 \n工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位) \n \n \n\n\n0 \n0 \n13位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:$2^{13}-1=8191$ \n \n\n0 \n1 \n16位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535 \n \n\n1 \n0 \n8位自动重装载方式,溢出时自动将THx的值装载到TLx里面 \n \n\n1 \n1 \n如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到($2^8-1=255$),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出) \n \n \n
\n
\n(三)查询定时计数器是否溢出 上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; uchar code tabel[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F ,}; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void display (uchar i) {\tuchar bai, shi, ge; \tbai = i / 100 ; \tshi = i % 100 / 10 ;\t \tge = i % 10 ; \t \t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = 0XFE ; \tWE = 0 ; \t \tDU = 1 ; \tP0 = tabel[bai]; \tDU = 0 ; \tdelay(5 ); \t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = 0XFD ; \tWE = 0 ; \t \tDU = 1 ; \tP0 = tabel[shi]; \tDU = 0 ; \tdelay(5 ); \t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = 0XFB ; \tWE = 0 ; \t \tDU = 1 ; \tP0 = tabel[ge]; \tDU = 0 ; \tdelay(5 ); } void T0_init () {\tTR0 = 1 ;\t \tTMOD = 0X01 ; \tTH0 = 0x4b ; \tTL0 = 0xfd ; } void main () {\t\tuchar mSec, Sec; \tT0_init(); \twhile (1 ) \t{ \t\tif (TF0 == 1 ) \t\t{ \t\t\tTF0 = 0 ; \t\t\tTH0 = 0x4b ; \t\t\tTL0 = 0xfd ; \t\t\tmSec++; \t\t\tif (mSec == 20 ) \t\t\t{ \t\t\t\tmSec = 0 ; \t\t\t\tSec++; \t\t\t}\t\t\t\t\t \t\t} \t\tdisplay(Sec); \t\tif (Sec > 10 ) \t\t\tSec = 0 ; \t}\t }
\n方式1初值计算方法,假如定时t(单位:us),使用T0:
\n则:
\n1 2 3 TH0 = (65535 - t / 1.085) / 256 TL0 = (65535 - t / 1.085) % 256 四舍五入取整后转换为16进制
\n中断系统 主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断
\n
\n引起CPU中断的根源,称为中断源 。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。
\n51子系列存在5个中断源:外部中断源2个
\n\nINT0——由P3.2端口线引入,低电平或下降沿引起。
\n \nINT1——由P3.3端口线引入,低电平或下降沿引起。
\n 这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制
\n \n \n内部中断源3个
\n\nT0——定时器/计数器0中断,由T0回零溢出引起。 \nT1——定时器/计数器1中断,由T1回零溢出引起。 \nTI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。
\n 这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。
\n \n \n使用中断 如何使用中断,有3大步骤:
\n(一)允许产生中断 要允许产生中断首先要配置中断允许寄存器IE和XICON
\n(可位寻址 表示每一位都已经用变量定义好了,比如第7位是EA)
\n
\n\n总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断
\n \n(二)设置什么时候响应中断 什么时候响应中断由控制寄存器TCON控制
\n
\nTFx:当计数值溢出时,就会向cpu发出中断请求,然后自动 进入对应的中断处理函数 ,然后TFx由硬件自动清0
\n其它IEx, ITx也差不多是这样
\n(三)产生中断后你要干什么——中断处理函数 中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:
\n格式如下:
\n1 2 3 4 5 void 函数名() interrupt 中断入口号 { }
\n\n中断优先级:
\n如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念
\n中断优先级级别越高,就越先被执行
\n
\n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit DU = P2^6 ; sbit WE = P2^7 ; sbit key_s2 = P3^0 ; sbit key_s3 = P3^1 ; uchar num; uchar mSec, Sec; uchar code SMGduan[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F ,}; uchar code SMGwei[] = {0xfe , 0xfd , 0xfb }; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void display (uchar i) {\tstatic uchar wei; \t\t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = SMGwei[wei]; \tWE = 0 ; \tswitch (wei) \t{ \t\tcase 0 : DU = 1 ; P0 = SMGduan[i / 100 ]; DU = 0 ; break ; \t\tcase 1 : DU = 1 ; P0 = SMGduan[i % 100 / 10 ]; DU = 0 ; break ;\t \t\tcase 2 : DU = 1 ; P0 = SMGduan[i % 10 ]; DU = 0 ; break ;\t\t \t} \twei++; \tif (wei == 3 ) \t\twei = 0 ; } void T0_init () {\tEA = 1 ;\t \tET0 = 1 ; \tTR0 = 1 ;\t \tTMOD = 0X01 ; \tTH0 = 0xED ; \tTL0 = 0xFF ; } void main () {\t\tT0_init(); \twhile (1 ) \t{ \t\tif (key_s2 == 0 ) \t\t{ \t\t\tdelay(20 ); \t\t\tif (key_s2 == 0 ) \t\t\t{ \t\t\t\tif (num != 120 ) \t\t\t\tnum++; \t\t\t\twhile (!key_s2); \t\t\t}\t \t\t} \t\tif (key_s3 == 0 ) \t\t{ \t\t\tdelay(20 ); \t\t\tif (key_s3 == 0 ) \t\t\t{ \t\t\t\tif (num > 0 ) \t\t\t\t\tnum--; \t\t\t\twhile (!key_s3); \t\t\t}\t \t\t} \t}\t } void timer0 () interrupt 1 {\tTH0 = 0xED ; \tTL0 = 0xFF ; \tdisplay(num); }
\n"},{"title":"51单片机学习笔记(八)","description":"DS1302日历时钟芯片","abbrlink":"b836e65","date":"2021-08-20T09:32:44.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n## DS1302\n\n### 芯片功能\n\nDS1302是美国DALLAS推出的一款高性能、低功耗的日历时钟芯片。\n\nDS1302是一种串行接口的实时时钟,芯片内部具有可编程的日历时钟和31个字节的静态RAM,日历时钟可以自动进行闰年补偿,计时准确,接口简单,使用方便\n\n工作电压范围2.5~5.5V,芯片自身还具有对备用电池进行涓流充电功能,可有效延长备用电池的使用寿命。\n\nDS1302用于数据记录,能实现数据与该数据出现的时间同时记录,广泛应用于测量系统中。\n\n\n\n### 引脚\n\n\n\n![image-20210824170629438](https://gitee.com/ajream/images/raw/master/img/20210824170630_image-20210824170629438.png)\n\n\n\n- VCC1:主电源\n\n- VCC2:备用电源\n\n 当VCC2大于VCC1+0.2V时,由VCC2向DS1302供电否则由VCC1向DS1302供电\n\n- SCLK:串行时钟输入端,控制数据输入与输出\n\n- I/O :双向输入线\n\n- CE:使能端,CE为高时允许读写DS1302数据,为低时禁止读写\n\n\n\n\n\n### 应用电路\n\n开发板实际电路如下:\n\n![image-20210824170855223](https://gitee.com/ajream/images/raw/master/img/20210824170856_image-20210824170855223.png)\n\n说明:\n\nDS1302和单片机的IO连接只需3条线:\n\n- CE数据传输使能端、SCLK串行时钟输入端、I/O串行数据端\n\n (开发板上把这3个引脚分别接到了j5排针上同时通过4.7K电阻上拉到VCC,我们实际使用DS1302芯片时需要用杜邦线把j5和相应的单片机IO链接上)\n\n此外\n\n- X1、X2晶振引脚外接32.768KHZ圆形晶振,给时钟芯片提供工作频率\n\n- VCC2接的开发上的系统电源,VCC1接的预留电池座\n\n- DS1302第4脚接的系统GND\n\n\n\n### DS1302的寄存器\n\n\n\n\n\n![image-20210824174037791](https://gitee.com/ajream/images/raw/master/img/20210824174040_image-20210824174037791.png)\n\n\n\n如图所示,时钟日历包含在 7 个读/写寄存器内,读/写寄存器中的数据是 BCD 码。(4位BCD码最大表示9,不是15, 例如用BCD码表示10为 `0001 0000`)\n\n\n\n- 秒寄存器(81h、80h)的BIT7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。\n\n- 小时寄存器(85h、84h)的BIT7用于定义DS1302是运行于12小时模式还是24小时模式,当为1时,选择12小时模式,此时BIT5为AM/PM位,在24小时模式时此位为小时数据位。\n\n- 写保护寄存器(8Fh、8Eh)的BIT7是写保护位(WP),其它7位均为0。在任何对时钟或RAM读写操作之前,WP位必须为0。当WP位为1时,不能对任何时钟日历寄存器或RAM进行写操作。\n\n \n\n使用对应指令即可读取或写入数据到相应寄存器,比如读取秒,用指令`81h`,读取分,用 `83h`, ......\n\n\n\n\n\n\n\n突发模式:\n\n突发模式可以指定任 何的时钟/日历或者 RAM 寄存器为突发模式,和以前一样,第 6 位指定时钟或 RAM 而 0 位指定读或写。\n\n突发模式的实质是指一次传送多个字节的时钟信号和 RAM 数据。如下图所示:\n\n![image-20210824174700324](https://gitee.com/ajream/images/raw/master/img/20210824174701_image-20210824174700324.png)\n\n也就是说,如果发送`BFh`指令,DS1302会一次性把年月周日、时分秒发送过来,不需要像前面一个个指令来获取时间\n\n\n\n\n\n### DS1302时序\n\n\n\nDS1302读写数据时序,数据的传输是从最低位开始(BIT0)。\n\n数据是以位(BIT)为单位依次写入或读出,读写数据操作中SCLK上升沿时执行写入数据,下降沿时执行读出数据。\n\n\n\n**读数据:**CE端从低到高的一个上升沿开始允许开始读数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期读出数据。\n\n![image-20210824175336808](https://gitee.com/ajream/images/raw/master/img/20210824175338_image-20210824175336808.png)\n\n\n\n**写数据:**CE端从低到高的一个上升沿开始允许开始写数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期写入数据。\n\n![image-20210824175358215](https://gitee.com/ajream/images/raw/master/img/20210824175359_image-20210824175358215.png)\n\n\n\n\n\n\n\n## 例子\n\n数码管显示DS1302时钟\n\n```c\n#include \n#include \n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit TSCLK = P1^0;//时钟线 接到P10上用杜邦线\nsbit TIO = P1^1;//数据线,接到P11上\nsbit TRST = P1^2;//使能端,接到P12上\nsbit DU = P2^6; //数码管段选\nsbit WE = P2^7; //数码管位选\n/*====================================\n共阴极数码管段选码\n====================================*/\nuchar code table[]={ \n//0\t\t1\t 2 3 4 5 6 7 8\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00\n };\n\n/*====================================\n数码管位选码\n====================================*/\n\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位\nuchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数:void Delay_Ms(INT16U ms)\n参数:ms,毫秒延时形参\n描述:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid Delay_Ms(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n\nvoid Display(uchar Hour, Min, Sec)\n{\t\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Hour/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[0];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Hour%10]|0x80; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[1];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Min/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[2];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Min%10]|0x80; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[3];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Sec/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[4];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Sec%10]; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[5];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n\n}\n\n//写DS1302数据\nvoid Write_DS1302_DAT(uchar cmd, uchar dat)\n{\n\tuchar i;\n\tTRST = 0; //拉低使能端\n\tTSCLK = 0;//拉低数据总线\n\tTRST = 1; //拉高使能端,产生上升沿开始写数据\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = cmd & 0x01; //写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tcmd >>=1;\t\t //右移一位\n\t}\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = dat & 0x01; //写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tdat >>= 1;\t\t //右移一位\n\t}\n}\n//读DS1302数据\nuchar Read_DS1302_DAT(uchar cmd)\n{\n\tuchar i, dat;\n\tTRST = 0; //拉低使能端\n\tTSCLK = 0; //拉低数据总线\n\tTRST = 1; //拉高使能端,产生上升沿开始写数据\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = cmd & 0x01;//写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tcmd >>=1;\t\t //右移一位\n\t}\n\tfor(i = 0; i < 8; i++)//每次读1位,读8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线,产生下降沿,DS1302把数据放到TIO上\n\t\tdat >>= 1;\t\t //右移一位\n\t\tif(TIO)\tdat |= 0x80;//读取数据,从最低位开始\n\t\tTSCLK = 1;\t\t\t//拉高时钟总线,以备下一次产生下降沿\n\t}\n\treturn dat;\t//返回读出数据\n}\n\n//数据转BCD码\nuchar Dat_Chg_BCD(uchar dat)\n{\n\tuchar dat1, dat2;\n\tdat1 = dat / 10;\n\tdat2 = dat % 10;\n\tdat2 = dat2 + dat1 * 16;\n\treturn dat2;\n}\n\n//BCD码转换为数据\nuchar BCD_Chg_Dat(uchar dat)\n{\n\tuchar dat1, dat2;\n\tdat1 = dat / 16;\n\tdat2 = dat % 16;\n\tdat2 = dat2 + dat1 * 10;\n\treturn dat2;\n}\n\nvoid main()\n{\n\tuchar i;\n\tuchar Sec, Min, Hour;\n\tWrite_DS1302_DAT(0x8e, 0);//清除写保护\n\tWrite_DS1302_DAT(0x80, Dat_Chg_BCD(30));//30秒(并且进行BCD码转换)\n\tWrite_DS1302_DAT(0x82, Dat_Chg_BCD(15));//15分\n\tWrite_DS1302_DAT(0x84, Dat_Chg_BCD(19));//19时\n\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护\n\twhile(1)\n\t{\n\t\tWrite_DS1302_DAT(0x8e, 0); //清除写保护\n\t\tSec = BCD_Chg_Dat(Read_DS1302_DAT(0x81));//读秒寄存器(并且进行BCD码转换)\n\t\tMin\t= BCD_Chg_Dat(Read_DS1302_DAT(0x83));//读分寄存器\n\t\tHour = BCD_Chg_Dat(Read_DS1302_DAT(0x85));//读时寄存器\n\t\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护\n\t\tfor(i = 0; i < 50; i++)\t//循环显示时钟\n\t\t\tDisplay(Hour, Min, Sec);\n\n\t}\n}\n```\n\n","source":"_posts/51单片机/51单片机学习笔记(八).md","raw":"---\ntitle: 51单片机学习笔记(八)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: DS1302日历时钟芯片\nabbrlink: b836e65\ndate: 2021-08-20 17:32:44\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n## DS1302\n\n### 芯片功能\n\nDS1302是美国DALLAS推出的一款高性能、低功耗的日历时钟芯片。\n\nDS1302是一种串行接口的实时时钟,芯片内部具有可编程的日历时钟和31个字节的静态RAM,日历时钟可以自动进行闰年补偿,计时准确,接口简单,使用方便\n\n工作电压范围2.5~5.5V,芯片自身还具有对备用电池进行涓流充电功能,可有效延长备用电池的使用寿命。\n\nDS1302用于数据记录,能实现数据与该数据出现的时间同时记录,广泛应用于测量系统中。\n\n\n\n### 引脚\n\n\n\n![image-20210824170629438](https://gitee.com/ajream/images/raw/master/img/20210824170630_image-20210824170629438.png)\n\n\n\n- VCC1:主电源\n\n- VCC2:备用电源\n\n 当VCC2大于VCC1+0.2V时,由VCC2向DS1302供电否则由VCC1向DS1302供电\n\n- SCLK:串行时钟输入端,控制数据输入与输出\n\n- I/O :双向输入线\n\n- CE:使能端,CE为高时允许读写DS1302数据,为低时禁止读写\n\n\n\n\n\n### 应用电路\n\n开发板实际电路如下:\n\n![image-20210824170855223](https://gitee.com/ajream/images/raw/master/img/20210824170856_image-20210824170855223.png)\n\n说明:\n\nDS1302和单片机的IO连接只需3条线:\n\n- CE数据传输使能端、SCLK串行时钟输入端、I/O串行数据端\n\n (开发板上把这3个引脚分别接到了j5排针上同时通过4.7K电阻上拉到VCC,我们实际使用DS1302芯片时需要用杜邦线把j5和相应的单片机IO链接上)\n\n此外\n\n- X1、X2晶振引脚外接32.768KHZ圆形晶振,给时钟芯片提供工作频率\n\n- VCC2接的开发上的系统电源,VCC1接的预留电池座\n\n- DS1302第4脚接的系统GND\n\n\n\n### DS1302的寄存器\n\n\n\n\n\n![image-20210824174037791](https://gitee.com/ajream/images/raw/master/img/20210824174040_image-20210824174037791.png)\n\n\n\n如图所示,时钟日历包含在 7 个读/写寄存器内,读/写寄存器中的数据是 BCD 码。(4位BCD码最大表示9,不是15, 例如用BCD码表示10为 `0001 0000`)\n\n\n\n- 秒寄存器(81h、80h)的BIT7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。\n\n- 小时寄存器(85h、84h)的BIT7用于定义DS1302是运行于12小时模式还是24小时模式,当为1时,选择12小时模式,此时BIT5为AM/PM位,在24小时模式时此位为小时数据位。\n\n- 写保护寄存器(8Fh、8Eh)的BIT7是写保护位(WP),其它7位均为0。在任何对时钟或RAM读写操作之前,WP位必须为0。当WP位为1时,不能对任何时钟日历寄存器或RAM进行写操作。\n\n \n\n使用对应指令即可读取或写入数据到相应寄存器,比如读取秒,用指令`81h`,读取分,用 `83h`, ......\n\n\n\n\n\n\n\n突发模式:\n\n突发模式可以指定任 何的时钟/日历或者 RAM 寄存器为突发模式,和以前一样,第 6 位指定时钟或 RAM 而 0 位指定读或写。\n\n突发模式的实质是指一次传送多个字节的时钟信号和 RAM 数据。如下图所示:\n\n![image-20210824174700324](https://gitee.com/ajream/images/raw/master/img/20210824174701_image-20210824174700324.png)\n\n也就是说,如果发送`BFh`指令,DS1302会一次性把年月周日、时分秒发送过来,不需要像前面一个个指令来获取时间\n\n\n\n\n\n### DS1302时序\n\n\n\nDS1302读写数据时序,数据的传输是从最低位开始(BIT0)。\n\n数据是以位(BIT)为单位依次写入或读出,读写数据操作中SCLK上升沿时执行写入数据,下降沿时执行读出数据。\n\n\n\n**读数据:**CE端从低到高的一个上升沿开始允许开始读数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期读出数据。\n\n![image-20210824175336808](https://gitee.com/ajream/images/raw/master/img/20210824175338_image-20210824175336808.png)\n\n\n\n**写数据:**CE端从低到高的一个上升沿开始允许开始写数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期写入数据。\n\n![image-20210824175358215](https://gitee.com/ajream/images/raw/master/img/20210824175359_image-20210824175358215.png)\n\n\n\n\n\n\n\n## 例子\n\n数码管显示DS1302时钟\n\n```c\n#include \n#include \n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit TSCLK = P1^0;//时钟线 接到P10上用杜邦线\nsbit TIO = P1^1;//数据线,接到P11上\nsbit TRST = P1^2;//使能端,接到P12上\nsbit DU = P2^6; //数码管段选\nsbit WE = P2^7; //数码管位选\n/*====================================\n共阴极数码管段选码\n====================================*/\nuchar code table[]={ \n//0\t\t1\t 2 3 4 5 6 7 8\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00\n };\n\n/*====================================\n数码管位选码\n====================================*/\n\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位\nuchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数:void Delay_Ms(INT16U ms)\n参数:ms,毫秒延时形参\n描述:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid Delay_Ms(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n\nvoid Display(uchar Hour, Min, Sec)\n{\t\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Hour/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[0];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Hour%10]|0x80; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[1];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Min/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[2];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Min%10]|0x80; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[3];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Sec/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[4];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Sec%10]; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[5];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n\n}\n\n//写DS1302数据\nvoid Write_DS1302_DAT(uchar cmd, uchar dat)\n{\n\tuchar i;\n\tTRST = 0; //拉低使能端\n\tTSCLK = 0;//拉低数据总线\n\tTRST = 1; //拉高使能端,产生上升沿开始写数据\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = cmd & 0x01; //写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tcmd >>=1;\t\t //右移一位\n\t}\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = dat & 0x01; //写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tdat >>= 1;\t\t //右移一位\n\t}\n}\n//读DS1302数据\nuchar Read_DS1302_DAT(uchar cmd)\n{\n\tuchar i, dat;\n\tTRST = 0; //拉低使能端\n\tTSCLK = 0; //拉低数据总线\n\tTRST = 1; //拉高使能端,产生上升沿开始写数据\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = cmd & 0x01;//写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tcmd >>=1;\t\t //右移一位\n\t}\n\tfor(i = 0; i < 8; i++)//每次读1位,读8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线,产生下降沿,DS1302把数据放到TIO上\n\t\tdat >>= 1;\t\t //右移一位\n\t\tif(TIO)\tdat |= 0x80;//读取数据,从最低位开始\n\t\tTSCLK = 1;\t\t\t//拉高时钟总线,以备下一次产生下降沿\n\t}\n\treturn dat;\t//返回读出数据\n}\n\n//数据转BCD码\nuchar Dat_Chg_BCD(uchar dat)\n{\n\tuchar dat1, dat2;\n\tdat1 = dat / 10;\n\tdat2 = dat % 10;\n\tdat2 = dat2 + dat1 * 16;\n\treturn dat2;\n}\n\n//BCD码转换为数据\nuchar BCD_Chg_Dat(uchar dat)\n{\n\tuchar dat1, dat2;\n\tdat1 = dat / 16;\n\tdat2 = dat % 16;\n\tdat2 = dat2 + dat1 * 10;\n\treturn dat2;\n}\n\nvoid main()\n{\n\tuchar i;\n\tuchar Sec, Min, Hour;\n\tWrite_DS1302_DAT(0x8e, 0);//清除写保护\n\tWrite_DS1302_DAT(0x80, Dat_Chg_BCD(30));//30秒(并且进行BCD码转换)\n\tWrite_DS1302_DAT(0x82, Dat_Chg_BCD(15));//15分\n\tWrite_DS1302_DAT(0x84, Dat_Chg_BCD(19));//19时\n\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护\n\twhile(1)\n\t{\n\t\tWrite_DS1302_DAT(0x8e, 0); //清除写保护\n\t\tSec = BCD_Chg_Dat(Read_DS1302_DAT(0x81));//读秒寄存器(并且进行BCD码转换)\n\t\tMin\t= BCD_Chg_Dat(Read_DS1302_DAT(0x83));//读分寄存器\n\t\tHour = BCD_Chg_Dat(Read_DS1302_DAT(0x85));//读时寄存器\n\t\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护\n\t\tfor(i = 0; i < 50; i++)\t//循环显示时钟\n\t\t\tDisplay(Hour, Min, Sec);\n\n\t}\n}\n```\n\n","slug":"51单片机/51单片机学习笔记(八)","published":1,"updated":"2021-09-10T12:44:55.652Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6o9000gakve5fnphajo","content":"DS1302 芯片功能 DS1302是美国DALLAS推出的一款高性能、低功耗的日历时钟芯片。
\nDS1302是一种串行接口的实时时钟,芯片内部具有可编程的日历时钟和31个字节的静态RAM,日历时钟可以自动进行闰年补偿,计时准确,接口简单,使用方便
\n工作电压范围2.5~5.5V,芯片自身还具有对备用电池进行涓流充电功能,可有效延长备用电池的使用寿命。
\nDS1302用于数据记录,能实现数据与该数据出现的时间同时记录,广泛应用于测量系统中。
\n引脚
\n\n应用电路 开发板实际电路如下:
\n
\n说明:
\nDS1302和单片机的IO连接只需3条线:
\n\n此外
\n\nDS1302的寄存器
\n如图所示,时钟日历包含在 7 个读/写寄存器内,读/写寄存器中的数据是 BCD 码。(4位BCD码最大表示9,不是15, 例如用BCD码表示10为 0001 0000
)
\n\n秒寄存器(81h、80h)的BIT7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。
\n \n小时寄存器(85h、84h)的BIT7用于定义DS1302是运行于12小时模式还是24小时模式,当为1时,选择12小时模式,此时BIT5为AM/PM位,在24小时模式时此位为小时数据位。
\n \n写保护寄存器(8Fh、8Eh)的BIT7是写保护位(WP),其它7位均为0。在任何对时钟或RAM读写操作之前,WP位必须为0。当WP位为1时,不能对任何时钟日历寄存器或RAM进行写操作。
\n \n \n使用对应指令即可读取或写入数据到相应寄存器,比如读取秒,用指令81h
,读取分,用 83h
, ……
\n突发模式:
\n突发模式可以指定任 何的时钟/日历或者 RAM 寄存器为突发模式,和以前一样,第 6 位指定时钟或 RAM 而 0 位指定读或写。
\n突发模式的实质是指一次传送多个字节的时钟信号和 RAM 数据。如下图所示:
\n
\n也就是说,如果发送BFh
指令,DS1302会一次性把年月周日、时分秒发送过来,不需要像前面一个个指令来获取时间
\nDS1302时序 DS1302读写数据时序,数据的传输是从最低位开始(BIT0)。
\n数据是以位(BIT)为单位依次写入或读出,读写数据操作中SCLK上升沿时执行写入数据,下降沿时执行读出数据。
\n读数据: CE端从低到高的一个上升沿开始允许开始读数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期读出数据。
\n
\n写数据: CE端从低到高的一个上升沿开始允许开始写数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期写入数据。
\n
\n例子 数码管显示DS1302时钟
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 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 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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 #include <reg52.h> #include <intrins.h> #define MAIN_Fosc\t\t11059200UL\t typedef unsigned char INT8U;typedef unsigned char uchar;typedef unsigned int INT16U;typedef unsigned int uint;sbit TSCLK = P1^0 ; sbit TIO = P1^1 ; sbit TRST = P1^2 ; sbit DU = P2^6 ; sbit WE = P2^7 ; uchar code table[]={ 0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F ,0x6F , 0x77 , 0x7C , 0x39 , 0x5E , 0x79 , 0x71 , 0x40 , 0x80 , 0x00 }; \t\t\t\t uchar code T_COM[] = {0xfe , 0xfd , 0xfb , 0xf7 , 0xef , 0xdf , 0xbf , 0x7f }; void Delay_Ms (INT16U ms) { INT16U i; \t do { \t i = MAIN_Fosc / 96000 ; \t\t while (--i); }while (--ms); } void Display (uchar Hour, Min, Sec) {\t\t\tDU = 0 ;\t\t\t\t\t\t\t \tP0 = table[Hour/10 ];\t\t \tDU = 1 ;\t\t\t\t\t\t \tDU = 0 ;\t\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tP0 = T_COM[0 ];\t\t\t\t \tWE = 1 ;\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tDelay_Ms(3 ); \tDU = 0 ; \tP0 = table[Hour%10 ]|0x80 ; \tDU = 1 ; \tDU = 0 ; \tWE = 0 ; \tP0 = T_COM[1 ];\t\t\t \tWE = 1 ; \tWE = 0 ; \tDelay_Ms(3 ); \tDU = 0 ;\t\t\t\t\t\t\t \tP0 = table[Min/10 ];\t\t \tDU = 1 ;\t\t\t\t\t\t \tDU = 0 ;\t\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tP0 = T_COM[2 ];\t\t\t\t \tWE = 1 ;\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tDelay_Ms(3 ); \tDU = 0 ; \tP0 = table[Min%10 ]|0x80 ; \tDU = 1 ; \tDU = 0 ; \tWE = 0 ; \tP0 = T_COM[3 ];\t\t\t \tWE = 1 ; \tWE = 0 ; \tDelay_Ms(3 ); \tDU = 0 ;\t\t\t\t\t\t\t \tP0 = table[Sec/10 ];\t\t \tDU = 1 ;\t\t\t\t\t\t \tDU = 0 ;\t\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tP0 = T_COM[4 ];\t\t\t\t \tWE = 1 ;\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tDelay_Ms(3 ); \tDU = 0 ; \tP0 = table[Sec%10 ]; \tDU = 1 ; \tDU = 0 ; \tWE = 0 ; \tP0 = T_COM[5 ];\t\t\t \tWE = 1 ; \tWE = 0 ; \tDelay_Ms(3 ); } void Write_DS1302_DAT (uchar cmd, uchar dat) {\tuchar i; \tTRST = 0 ; \tTSCLK = 0 ; \tTRST = 1 ; \tfor (i = 0 ; i < 8 ; i++) \t{ \t\tTSCLK = 0 ;\t\t \t\tTIO = cmd & 0x01 ; \t\tTSCLK = 1 ;\t\t \t\tcmd >>=1 ;\t\t \t} \tfor (i = 0 ; i < 8 ; i++) \t{ \t\tTSCLK = 0 ;\t\t \t\tTIO = dat & 0x01 ; \t\tTSCLK = 1 ;\t\t \t\tdat >>= 1 ;\t\t \t} } uchar Read_DS1302_DAT (uchar cmd) {\tuchar i, dat; \tTRST = 0 ; \tTSCLK = 0 ; \tTRST = 1 ; \tfor (i = 0 ; i < 8 ; i++) \t{ \t\tTSCLK = 0 ;\t\t \t\tTIO = cmd & 0x01 ; \t\tTSCLK = 1 ;\t\t \t\tcmd >>=1 ;\t\t \t} \tfor (i = 0 ; i < 8 ; i++) \t{ \t\tTSCLK = 0 ;\t\t \t\tdat >>= 1 ;\t\t \t\tif (TIO)\tdat |= 0x80 ; \t\tTSCLK = 1 ;\t\t\t \t} \treturn dat;\t } uchar Dat_Chg_BCD (uchar dat) {\tuchar dat1, dat2; \tdat1 = dat / 10 ; \tdat2 = dat % 10 ; \tdat2 = dat2 + dat1 * 16 ; \treturn dat2; } uchar BCD_Chg_Dat (uchar dat) {\tuchar dat1, dat2; \tdat1 = dat / 16 ; \tdat2 = dat % 16 ; \tdat2 = dat2 + dat1 * 10 ; \treturn dat2; } void main () {\tuchar i; \tuchar Sec, Min, Hour; \tWrite_DS1302_DAT(0x8e , 0 ); \tWrite_DS1302_DAT(0x80 , Dat_Chg_BCD(30 )); \tWrite_DS1302_DAT(0x82 , Dat_Chg_BCD(15 )); \tWrite_DS1302_DAT(0x84 , Dat_Chg_BCD(19 )); \tWrite_DS1302_DAT(0x8e , 0x80 ); \twhile (1 ) \t{ \t\tWrite_DS1302_DAT(0x8e , 0 ); \t\tSec = BCD_Chg_Dat(Read_DS1302_DAT(0x81 )); \t\tMin\t= BCD_Chg_Dat(Read_DS1302_DAT(0x83 )); \t\tHour = BCD_Chg_Dat(Read_DS1302_DAT(0x85 )); \t\tWrite_DS1302_DAT(0x8e , 0x80 ); \t\tfor (i = 0 ; i < 50 ; i++)\t \t\t\tDisplay(Hour, Min, Sec); \t} }
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"DS1302 芯片功能 DS1302是美国DALLAS推出的一款高性能、低功耗的日历时钟芯片。
\nDS1302是一种串行接口的实时时钟,芯片内部具有可编程的日历时钟和31个字节的静态RAM,日历时钟可以自动进行闰年补偿,计时准确,接口简单,使用方便
\n工作电压范围2.5~5.5V,芯片自身还具有对备用电池进行涓流充电功能,可有效延长备用电池的使用寿命。
\nDS1302用于数据记录,能实现数据与该数据出现的时间同时记录,广泛应用于测量系统中。
\n引脚
\n\n应用电路 开发板实际电路如下:
\n
\n说明:
\nDS1302和单片机的IO连接只需3条线:
\n\n此外
\n\nDS1302的寄存器
\n如图所示,时钟日历包含在 7 个读/写寄存器内,读/写寄存器中的数据是 BCD 码。(4位BCD码最大表示9,不是15, 例如用BCD码表示10为 0001 0000
)
\n\n秒寄存器(81h、80h)的BIT7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。
\n \n小时寄存器(85h、84h)的BIT7用于定义DS1302是运行于12小时模式还是24小时模式,当为1时,选择12小时模式,此时BIT5为AM/PM位,在24小时模式时此位为小时数据位。
\n \n写保护寄存器(8Fh、8Eh)的BIT7是写保护位(WP),其它7位均为0。在任何对时钟或RAM读写操作之前,WP位必须为0。当WP位为1时,不能对任何时钟日历寄存器或RAM进行写操作。
\n \n \n使用对应指令即可读取或写入数据到相应寄存器,比如读取秒,用指令81h
,读取分,用 83h
, ……
\n突发模式:
\n突发模式可以指定任 何的时钟/日历或者 RAM 寄存器为突发模式,和以前一样,第 6 位指定时钟或 RAM 而 0 位指定读或写。
\n突发模式的实质是指一次传送多个字节的时钟信号和 RAM 数据。如下图所示:
\n
\n也就是说,如果发送BFh
指令,DS1302会一次性把年月周日、时分秒发送过来,不需要像前面一个个指令来获取时间
\nDS1302时序 DS1302读写数据时序,数据的传输是从最低位开始(BIT0)。
\n数据是以位(BIT)为单位依次写入或读出,读写数据操作中SCLK上升沿时执行写入数据,下降沿时执行读出数据。
\n读数据: CE端从低到高的一个上升沿开始允许开始读数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期读出数据。
\n
\n写数据: CE端从低到高的一个上升沿开始允许开始写数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期写入数据。
\n
\n例子 数码管显示DS1302时钟
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 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 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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 #include <reg52.h> #include <intrins.h> #define MAIN_Fosc\t\t11059200UL\t typedef unsigned char INT8U;typedef unsigned char uchar;typedef unsigned int INT16U;typedef unsigned int uint;sbit TSCLK = P1^0 ; sbit TIO = P1^1 ; sbit TRST = P1^2 ; sbit DU = P2^6 ; sbit WE = P2^7 ; uchar code table[]={ 0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F ,0x6F , 0x77 , 0x7C , 0x39 , 0x5E , 0x79 , 0x71 , 0x40 , 0x80 , 0x00 }; \t\t\t\t uchar code T_COM[] = {0xfe , 0xfd , 0xfb , 0xf7 , 0xef , 0xdf , 0xbf , 0x7f }; void Delay_Ms (INT16U ms) { INT16U i; \t do { \t i = MAIN_Fosc / 96000 ; \t\t while (--i); }while (--ms); } void Display (uchar Hour, Min, Sec) {\t\t\tDU = 0 ;\t\t\t\t\t\t\t \tP0 = table[Hour/10 ];\t\t \tDU = 1 ;\t\t\t\t\t\t \tDU = 0 ;\t\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tP0 = T_COM[0 ];\t\t\t\t \tWE = 1 ;\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tDelay_Ms(3 ); \tDU = 0 ; \tP0 = table[Hour%10 ]|0x80 ; \tDU = 1 ; \tDU = 0 ; \tWE = 0 ; \tP0 = T_COM[1 ];\t\t\t \tWE = 1 ; \tWE = 0 ; \tDelay_Ms(3 ); \tDU = 0 ;\t\t\t\t\t\t\t \tP0 = table[Min/10 ];\t\t \tDU = 1 ;\t\t\t\t\t\t \tDU = 0 ;\t\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tP0 = T_COM[2 ];\t\t\t\t \tWE = 1 ;\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tDelay_Ms(3 ); \tDU = 0 ; \tP0 = table[Min%10 ]|0x80 ; \tDU = 1 ; \tDU = 0 ; \tWE = 0 ; \tP0 = T_COM[3 ];\t\t\t \tWE = 1 ; \tWE = 0 ; \tDelay_Ms(3 ); \tDU = 0 ;\t\t\t\t\t\t\t \tP0 = table[Sec/10 ];\t\t \tDU = 1 ;\t\t\t\t\t\t \tDU = 0 ;\t\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tP0 = T_COM[4 ];\t\t\t\t \tWE = 1 ;\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tDelay_Ms(3 ); \tDU = 0 ; \tP0 = table[Sec%10 ]; \tDU = 1 ; \tDU = 0 ; \tWE = 0 ; \tP0 = T_COM[5 ];\t\t\t \tWE = 1 ; \tWE = 0 ; \tDelay_Ms(3 ); } void Write_DS1302_DAT (uchar cmd, uchar dat) {\tuchar i; \tTRST = 0 ; \tTSCLK = 0 ; \tTRST = 1 ; \tfor (i = 0 ; i < 8 ; i++) \t{ \t\tTSCLK = 0 ;\t\t \t\tTIO = cmd & 0x01 ; \t\tTSCLK = 1 ;\t\t \t\tcmd >>=1 ;\t\t \t} \tfor (i = 0 ; i < 8 ; i++) \t{ \t\tTSCLK = 0 ;\t\t \t\tTIO = dat & 0x01 ; \t\tTSCLK = 1 ;\t\t \t\tdat >>= 1 ;\t\t \t} } uchar Read_DS1302_DAT (uchar cmd) {\tuchar i, dat; \tTRST = 0 ; \tTSCLK = 0 ; \tTRST = 1 ; \tfor (i = 0 ; i < 8 ; i++) \t{ \t\tTSCLK = 0 ;\t\t \t\tTIO = cmd & 0x01 ; \t\tTSCLK = 1 ;\t\t \t\tcmd >>=1 ;\t\t \t} \tfor (i = 0 ; i < 8 ; i++) \t{ \t\tTSCLK = 0 ;\t\t \t\tdat >>= 1 ;\t\t \t\tif (TIO)\tdat |= 0x80 ; \t\tTSCLK = 1 ;\t\t\t \t} \treturn dat;\t } uchar Dat_Chg_BCD (uchar dat) {\tuchar dat1, dat2; \tdat1 = dat / 10 ; \tdat2 = dat % 10 ; \tdat2 = dat2 + dat1 * 16 ; \treturn dat2; } uchar BCD_Chg_Dat (uchar dat) {\tuchar dat1, dat2; \tdat1 = dat / 16 ; \tdat2 = dat % 16 ; \tdat2 = dat2 + dat1 * 10 ; \treturn dat2; } void main () {\tuchar i; \tuchar Sec, Min, Hour; \tWrite_DS1302_DAT(0x8e , 0 ); \tWrite_DS1302_DAT(0x80 , Dat_Chg_BCD(30 )); \tWrite_DS1302_DAT(0x82 , Dat_Chg_BCD(15 )); \tWrite_DS1302_DAT(0x84 , Dat_Chg_BCD(19 )); \tWrite_DS1302_DAT(0x8e , 0x80 ); \twhile (1 ) \t{ \t\tWrite_DS1302_DAT(0x8e , 0 ); \t\tSec = BCD_Chg_Dat(Read_DS1302_DAT(0x81 )); \t\tMin\t= BCD_Chg_Dat(Read_DS1302_DAT(0x83 )); \t\tHour = BCD_Chg_Dat(Read_DS1302_DAT(0x85 )); \t\tWrite_DS1302_DAT(0x8e , 0x80 ); \t\tfor (i = 0 ; i < 50 ; i++)\t \t\t\tDisplay(Hour, Min, Sec); \t} }
\n"},{"title":"Hexo添加百度统计分析","description":"Hexo添加百度统计分析","abbrlink":"c8eb4af9","date":"2021-08-29T02:20:29.000Z","swiper_index":6,"cover":"https://gitee.com/ajream/images/raw/master/img/20210905193431_butterfly.png","_content":"\n\n1. 注册登录\n登录百度统计的[官方网站](https://tongji.baidu.com/web/welcome/login)\n\n2. 添加网站,填写相关信息\n\n ![image-20210830205228402](https://gitee.com/ajream/images/raw/master/img/20210830205231_image-20210830205228402.png)\n\n3. 获取代码![image-20210830205615284](https://gitee.com/ajream/images/raw/master/img/20210830205616_image-20210830205615284.png)\n\n 将上面这串字符填在主题的配置文件`_config.butterfly.yml`中:\n\n ```yml\n # Baidu Analytics\n # https://tongji.baidu.com/web/welcome/login\n baidu_analytics: #填在这里\n \n ```\n\n \n\n","source":"_posts/Hexo/Hexo添加百度统计分析.md","raw":"---\ntitle: Hexo添加百度统计分析\ntags:\n - Hexo\ncategories:\n - Hexo\ndescription: Hexo添加百度统计分析\nabbrlink: c8eb4af9\ndate: 2021-08-29 10:20:29\nswiper_index: 6\ncover: https://gitee.com/ajream/images/raw/master/img/20210905193431_butterfly.png\n---\n\n\n1. 注册登录\n登录百度统计的[官方网站](https://tongji.baidu.com/web/welcome/login)\n\n2. 添加网站,填写相关信息\n\n ![image-20210830205228402](https://gitee.com/ajream/images/raw/master/img/20210830205231_image-20210830205228402.png)\n\n3. 获取代码![image-20210830205615284](https://gitee.com/ajream/images/raw/master/img/20210830205616_image-20210830205615284.png)\n\n 将上面这串字符填在主题的配置文件`_config.butterfly.yml`中:\n\n ```yml\n # Baidu Analytics\n # https://tongji.baidu.com/web/welcome/login\n baidu_analytics: #填在这里\n \n ```\n\n \n\n","slug":"Hexo/Hexo添加百度统计分析","published":1,"updated":"2021-09-05T11:34:58.490Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ob000jakve6uur2o8z","content":"\n注册登录 登录百度统计的官方网站
\n \n添加网站,填写相关信息
\n
\n \n获取代码
\n将上面这串字符填在主题的配置文件_config.butterfly.yml
中:
\n \n \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"\n注册登录 登录百度统计的官方网站
\n \n添加网站,填写相关信息
\n
\n \n获取代码
\n将上面这串字符填在主题的配置文件_config.butterfly.yml
中:
\n \n \n \n"},{"title":"51单片机学习笔记(六)","description":"DS18B20温度传感器介绍","abbrlink":"5dd9c9e3","date":"2021-08-20T07:02:48.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n\n\n## DS18B20介绍\n\nDS18B20数字温度传感器是DALLAS公司生产的单总线器件 ,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。\n\n\n\n### 特点\n\n1. 通信采用1-Wire接口\n2. n每个DS18B20都有唯一的64位序列码储存在板载ROM中\n3. 无需外部元件\n4. 可从数据线供电,电源范围为3.0V ~ 5.5V。\n5. 可测量的温度范围在-55℃ ~ +125℃\n6. 在-10~+85℃范围内精确度为±0.5℃\n7. 温度计分辨率可设置为9~12位,12位时分辨率对应为0.0625℃\n\n\n\n\n\n![image-20210824153229536](https://gitee.com/ajream/images/raw/master/img/20210824153232_image-20210824153229536.png)\n\n\n\n## 单总线\n\nDS18B20采用`1-wire Bus`传输数据,所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。\n\n单总线信号类型: 复位脉冲、存在脉冲、写0、写1、读0、读1。\n\n所有这些信号除存在脉冲由DS18B20发出的以外,其他信号都由总线控制器发出。并且,数据传输总是从最低有效位开始。\n\n\n\n\n\n### 初始化时序\n\n\n\n初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。\n\n主机和DS18B20做任何通讯前都需要对其初始化。\n\n初始化期间:\n\n1. 总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位\n\n2. 释放总线,等到15-60us,此时18B20将返回一个60~240us之间的低电平存在信号\n\n\n\n![image-20210824154325250](https://gitee.com/ajream/images/raw/master/img/20210824154326_image-20210824154325250.png)\n\n\n\n\n\n### 写时序\n\n写时序有2种写法:写0时序和写1时序。总线控制器通过写 1 时序写逻辑 1 到 DS18B20,写 0 时序写逻辑 0 到 DS18B20。\n\n所有写时序必须持续 >=60us,包括:两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。\n\n\n\n初始化写时序:\n\n总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。\n\n当总线被释放的时候,5K 的上拉电阻将拉高总线。因此,如果总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。\n\n\n\n进行写时序:\n\n总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采 样。如果线上是高电平,就是写1。如果线上是低电平,就是写0\n\n\n\n![image-20210824160214829](https://gitee.com/ajream/images/raw/master/img/20210824160216_image-20210824160214829.png)\n\n\n\n\n\n\n\n\n\n### 读时序\n\n1. 先把总线拉低至少1us\n2. 在15us内选取1us进行读数据\n3. 在拉低的15us(包括上面2点所说的1us)内传输的数据是有效的\n4. 然后总线被释放\n5. 总时间必须>=60us\n\n![image-20210824161252256](https://gitee.com/ajream/images/raw/master/img/20210824161255_image-20210824161252256.png)\n\n\n\n读1的详细时序:\n\nTINIT,TRC 和 TSAMPLE 之和必须小于 15us\n\n\n\n![image-20210824162105674](https://gitee.com/ajream/images/raw/master/img/20210824162107_image-20210824162105674.png)\n\n\n\n推荐的读1时序:\n\n系统时间可以用下面办法达到最大:\n\nTINIT 和 TRC 保持时间尽可能小;把控制器采样时间放 到 15us 周期的最后。\n\n\n\n![image-20210824162319244](https://gitee.com/ajream/images/raw/master/img/20210824162320_image-20210824162319244.png)\n\n\n\n\n\n\n\n## 数据获取\n\n\n\n通过单线总线端口访问DS18B20的协议如下:\n\n\n\n1. 初始化\n\n2. ROM操作指令\n\n3. DS18B20功能指令、温度转换命令、读取暂存器命令\n\n\n\n部分常用ROM指令如下:\n\n![image-20210824163430400](https://gitee.com/ajream/images/raw/master/img/20210824163431_image-20210824163430400.png)\n\n\n\n\n\n## 例子\n\n```c\n#include \n#include \n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit DS = P2^2; //DS18B20单总线\nsbit DU = P2^6; //数码管段选\nsbit WE = P2^7; //数码管位选\n/*====================================\n共阴极数码管段选码\n====================================*/\nuchar code table[]={ \n//0\t\t1\t 2 3 4 5 6 7 8\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00\n };\n\n/*====================================\n数码管位选码\n====================================*/\n\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位\nuchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数:void Delay_Ms(INT16U ms)\n参数:ms,毫秒延时形参\n描述:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid Delay_Ms(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n/*us延时函数,执行一次US--所需6.5us进入一次函数需要11.95us*/\nvoid Delay_us(uchar us)\n{\n\twhile(us--);\t\n}\n/*====================================\n函数:void Display(INT16U Value)\n参数:Value,显示值 取值0-65535\n描述:共阴极数码管显示函数可显示一个字节的数\n====================================*/\nvoid Display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型\n{\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\tP0 = table[Value/100];\t\t//数码管显示百位\n\tDU = 1;\t\t\t\t\t\t\t//打开段选\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tP0 = T_COM[0];\t\t\t\t //第一位数码管\n\tWE = 1;\t\t\t\t\t\t//打开位选\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Value%100/10]|0x80; //显示十位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[1];\t\t\t //第二位数码管\n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Value%10];\t\t//显示个位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[2];\t\t\t\t//第三位数码管\n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n}\n/*单总线初始化时序*/\nbit ds_init()\n{\n\tbit i;\n\tDS = 1;\n\t_nop_();\n\tDS = 0;\n\tDelay_us(75); //拉低总线499.45us 挂接在总线上的18B20将会全部被复位\n\tDS = 1; //释放总线\n\tDelay_us(4); //延时37.95us 等待18B20发回存在信号\n\ti = DS;\n\tDelay_us(20); //141.95us\n\tDS = 1;\n\t_nop_();\n\treturn (i);\n}\n/*写一个字节*/\nvoid write_byte(uchar dat)\n{\n\tuchar i;\n\tfor(i=0;i<8;i++)\n\t{\n\t\tDS = 0;\n\t\t_nop_();//产生些时序\n\t\tDS = dat & 0x01;\n\t\tDelay_us(10);//76.95us\n\t\tDS = 1; //释放总线准备下一次数据写入\n\t\t_nop_();\n\t\tdat >>= 1;\n\t}\n}\n\nuchar read_byte()\n{\n\tuchar i, j, dat;\n\tfor(i=0;i<8;i++)\n\t{\n\t\tDS = 0;\n\t\t_nop_();//产生读时序\n\t\tDS = 1;\n\t\t_nop_();//释放总线\n\t\tj = DS;\n\t\tDelay_us(10);//76.95us\n\t\tDS = 1;\n\t\t_nop_();\n\t\tdat = (j<<7)|(dat>>1);\t\n\t}\n\treturn (dat);\n}\nvoid main()\n{\n\tuint i;\n\tuchar L, M;\n/*\tds_init();//初始化DS18B20\n\twrite_byte(0xcc);//发送跳跃ROM指令\n\twrite_byte(0x4e);//写暂存器指令\n\twrite_byte(0x7f);\n\twrite_byte(0xf7);\n\twrite_byte(0x1f);//配置工作在9位模式下\n\tds_init();//初始化DS18B20\n\twrite_byte(0xcc);//发送跳跃ROM指令 \n\twrite_byte(0x48);*/\n\twhile(1)\n\t{\n\t\tds_init();//初始化DS18B20\n\t\twrite_byte(0xcc);//发送跳跃ROM指令\n\t\twrite_byte(0x44);//发送温度转换指令\n\t\tds_init();//初始化DS18B20\n\t\twrite_byte(0xcc);//发送跳跃ROM指令\n\t\twrite_byte(0xbe);//读取DS18B20暂存器值\n\t\tL = read_byte();\n\t\tM = read_byte();\n\t\ti = M;\n\t\ti <<= 8;\n\t\ti |= L;\t\t\t\t\t\t\n\t\ti = i * 0.0625 * 10 + 0.5;\n\t\tDisplay(i);\n\t}\n}\n```\n\n","source":"_posts/51单片机/51单片机学习笔记(六).md","raw":"---\ntitle: 51单片机学习笔记(六)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: DS18B20温度传感器介绍\nabbrlink: 5dd9c9e3\ndate: 2021-08-20 15:02:48\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n\n\n## DS18B20介绍\n\nDS18B20数字温度传感器是DALLAS公司生产的单总线器件 ,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。\n\n\n\n### 特点\n\n1. 通信采用1-Wire接口\n2. n每个DS18B20都有唯一的64位序列码储存在板载ROM中\n3. 无需外部元件\n4. 可从数据线供电,电源范围为3.0V ~ 5.5V。\n5. 可测量的温度范围在-55℃ ~ +125℃\n6. 在-10~+85℃范围内精确度为±0.5℃\n7. 温度计分辨率可设置为9~12位,12位时分辨率对应为0.0625℃\n\n\n\n\n\n![image-20210824153229536](https://gitee.com/ajream/images/raw/master/img/20210824153232_image-20210824153229536.png)\n\n\n\n## 单总线\n\nDS18B20采用`1-wire Bus`传输数据,所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。\n\n单总线信号类型: 复位脉冲、存在脉冲、写0、写1、读0、读1。\n\n所有这些信号除存在脉冲由DS18B20发出的以外,其他信号都由总线控制器发出。并且,数据传输总是从最低有效位开始。\n\n\n\n\n\n### 初始化时序\n\n\n\n初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。\n\n主机和DS18B20做任何通讯前都需要对其初始化。\n\n初始化期间:\n\n1. 总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位\n\n2. 释放总线,等到15-60us,此时18B20将返回一个60~240us之间的低电平存在信号\n\n\n\n![image-20210824154325250](https://gitee.com/ajream/images/raw/master/img/20210824154326_image-20210824154325250.png)\n\n\n\n\n\n### 写时序\n\n写时序有2种写法:写0时序和写1时序。总线控制器通过写 1 时序写逻辑 1 到 DS18B20,写 0 时序写逻辑 0 到 DS18B20。\n\n所有写时序必须持续 >=60us,包括:两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。\n\n\n\n初始化写时序:\n\n总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。\n\n当总线被释放的时候,5K 的上拉电阻将拉高总线。因此,如果总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。\n\n\n\n进行写时序:\n\n总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采 样。如果线上是高电平,就是写1。如果线上是低电平,就是写0\n\n\n\n![image-20210824160214829](https://gitee.com/ajream/images/raw/master/img/20210824160216_image-20210824160214829.png)\n\n\n\n\n\n\n\n\n\n### 读时序\n\n1. 先把总线拉低至少1us\n2. 在15us内选取1us进行读数据\n3. 在拉低的15us(包括上面2点所说的1us)内传输的数据是有效的\n4. 然后总线被释放\n5. 总时间必须>=60us\n\n![image-20210824161252256](https://gitee.com/ajream/images/raw/master/img/20210824161255_image-20210824161252256.png)\n\n\n\n读1的详细时序:\n\nTINIT,TRC 和 TSAMPLE 之和必须小于 15us\n\n\n\n![image-20210824162105674](https://gitee.com/ajream/images/raw/master/img/20210824162107_image-20210824162105674.png)\n\n\n\n推荐的读1时序:\n\n系统时间可以用下面办法达到最大:\n\nTINIT 和 TRC 保持时间尽可能小;把控制器采样时间放 到 15us 周期的最后。\n\n\n\n![image-20210824162319244](https://gitee.com/ajream/images/raw/master/img/20210824162320_image-20210824162319244.png)\n\n\n\n\n\n\n\n## 数据获取\n\n\n\n通过单线总线端口访问DS18B20的协议如下:\n\n\n\n1. 初始化\n\n2. ROM操作指令\n\n3. DS18B20功能指令、温度转换命令、读取暂存器命令\n\n\n\n部分常用ROM指令如下:\n\n![image-20210824163430400](https://gitee.com/ajream/images/raw/master/img/20210824163431_image-20210824163430400.png)\n\n\n\n\n\n## 例子\n\n```c\n#include \n#include \n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit DS = P2^2; //DS18B20单总线\nsbit DU = P2^6; //数码管段选\nsbit WE = P2^7; //数码管位选\n/*====================================\n共阴极数码管段选码\n====================================*/\nuchar code table[]={ \n//0\t\t1\t 2 3 4 5 6 7 8\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00\n };\n\n/*====================================\n数码管位选码\n====================================*/\n\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位\nuchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数:void Delay_Ms(INT16U ms)\n参数:ms,毫秒延时形参\n描述:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid Delay_Ms(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n/*us延时函数,执行一次US--所需6.5us进入一次函数需要11.95us*/\nvoid Delay_us(uchar us)\n{\n\twhile(us--);\t\n}\n/*====================================\n函数:void Display(INT16U Value)\n参数:Value,显示值 取值0-65535\n描述:共阴极数码管显示函数可显示一个字节的数\n====================================*/\nvoid Display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型\n{\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\tP0 = table[Value/100];\t\t//数码管显示百位\n\tDU = 1;\t\t\t\t\t\t\t//打开段选\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tP0 = T_COM[0];\t\t\t\t //第一位数码管\n\tWE = 1;\t\t\t\t\t\t//打开位选\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Value%100/10]|0x80; //显示十位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[1];\t\t\t //第二位数码管\n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Value%10];\t\t//显示个位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[2];\t\t\t\t//第三位数码管\n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n}\n/*单总线初始化时序*/\nbit ds_init()\n{\n\tbit i;\n\tDS = 1;\n\t_nop_();\n\tDS = 0;\n\tDelay_us(75); //拉低总线499.45us 挂接在总线上的18B20将会全部被复位\n\tDS = 1; //释放总线\n\tDelay_us(4); //延时37.95us 等待18B20发回存在信号\n\ti = DS;\n\tDelay_us(20); //141.95us\n\tDS = 1;\n\t_nop_();\n\treturn (i);\n}\n/*写一个字节*/\nvoid write_byte(uchar dat)\n{\n\tuchar i;\n\tfor(i=0;i<8;i++)\n\t{\n\t\tDS = 0;\n\t\t_nop_();//产生些时序\n\t\tDS = dat & 0x01;\n\t\tDelay_us(10);//76.95us\n\t\tDS = 1; //释放总线准备下一次数据写入\n\t\t_nop_();\n\t\tdat >>= 1;\n\t}\n}\n\nuchar read_byte()\n{\n\tuchar i, j, dat;\n\tfor(i=0;i<8;i++)\n\t{\n\t\tDS = 0;\n\t\t_nop_();//产生读时序\n\t\tDS = 1;\n\t\t_nop_();//释放总线\n\t\tj = DS;\n\t\tDelay_us(10);//76.95us\n\t\tDS = 1;\n\t\t_nop_();\n\t\tdat = (j<<7)|(dat>>1);\t\n\t}\n\treturn (dat);\n}\nvoid main()\n{\n\tuint i;\n\tuchar L, M;\n/*\tds_init();//初始化DS18B20\n\twrite_byte(0xcc);//发送跳跃ROM指令\n\twrite_byte(0x4e);//写暂存器指令\n\twrite_byte(0x7f);\n\twrite_byte(0xf7);\n\twrite_byte(0x1f);//配置工作在9位模式下\n\tds_init();//初始化DS18B20\n\twrite_byte(0xcc);//发送跳跃ROM指令 \n\twrite_byte(0x48);*/\n\twhile(1)\n\t{\n\t\tds_init();//初始化DS18B20\n\t\twrite_byte(0xcc);//发送跳跃ROM指令\n\t\twrite_byte(0x44);//发送温度转换指令\n\t\tds_init();//初始化DS18B20\n\t\twrite_byte(0xcc);//发送跳跃ROM指令\n\t\twrite_byte(0xbe);//读取DS18B20暂存器值\n\t\tL = read_byte();\n\t\tM = read_byte();\n\t\ti = M;\n\t\ti <<= 8;\n\t\ti |= L;\t\t\t\t\t\t\n\t\ti = i * 0.0625 * 10 + 0.5;\n\t\tDisplay(i);\n\t}\n}\n```\n\n","slug":"51单片机/51单片机学习笔记(六)","published":1,"updated":"2021-09-10T12:45:19.484Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6od000oakveeb9m24v2","content":"DS18B20介绍 DS18B20数字温度传感器是DALLAS公司生产的单总线器件 ,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。
\n特点 \n通信采用1-Wire接口 \nn每个DS18B20都有唯一的64位序列码储存在板载ROM中 \n无需外部元件 \n可从数据线供电,电源范围为3.0V ~ 5.5V。 \n可测量的温度范围在-55℃ ~ +125℃ \n在-10~+85℃范围内精确度为±0.5℃ \n温度计分辨率可设置为9~12位,12位时分辨率对应为0.0625℃ \n \n
\n单总线 DS18B20采用1-wire Bus
传输数据,所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。
\n单总线信号类型: 复位脉冲、存在脉冲、写0、写1、读0、读1。
\n所有这些信号除存在脉冲由DS18B20发出的以外,其他信号都由总线控制器发出。并且,数据传输总是从最低有效位开始。
\n初始化时序 初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。
\n主机和DS18B20做任何通讯前都需要对其初始化。
\n初始化期间:
\n\n总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位
\n \n释放总线,等到15-60us,此时18B20将返回一个60~240us之间的低电平存在信号
\n \n \n
\n写时序 写时序有2种写法:写0时序和写1时序。总线控制器通过写 1 时序写逻辑 1 到 DS18B20,写 0 时序写逻辑 0 到 DS18B20。
\n所有写时序必须持续 >=60us,包括:两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。
\n初始化写时序:
\n总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。
\n当总线被释放的时候,5K 的上拉电阻将拉高总线。因此,如果总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。
\n进行写时序:
\n总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采 样。如果线上是高电平,就是写1。如果线上是低电平,就是写0
\n
\n读时序 \n先把总线拉低至少1us \n在15us内选取1us进行读数据 \n在拉低的15us(包括上面2点所说的1us)内传输的数据是有效的 \n然后总线被释放 \n总时间必须>=60us \n \n
\n读1的详细时序:
\nTINIT,TRC 和 TSAMPLE 之和必须小于 15us
\n
\n推荐的读1时序:
\n系统时间可以用下面办法达到最大:
\nTINIT 和 TRC 保持时间尽可能小;把控制器采样时间放 到 15us 周期的最后。
\n
\n数据获取 通过单线总线端口访问DS18B20的协议如下:
\n\n初始化
\n \nROM操作指令
\n \nDS18B20功能指令、温度转换命令、读取暂存器命令
\n \n \n部分常用ROM指令如下:
\n
\n例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 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 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 161 162 163 164 165 166 167 168 169 170 171 172 #include <reg52.h> #include <intrins.h> #define MAIN_Fosc\t\t11059200UL\t typedef unsigned char INT8U;typedef unsigned char uchar;typedef unsigned int INT16U;typedef unsigned int uint;sbit DS = P2^2 ; sbit DU = P2^6 ; sbit WE = P2^7 ; uchar code table[]={ 0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F ,0x6F , 0x77 , 0x7C , 0x39 , 0x5E , 0x79 , 0x71 , 0x40 , 0x80 , 0x00 }; \t\t\t\t uchar code T_COM[] = {0xfe , 0xfd , 0xfb , 0xf7 , 0xef , 0xdf , 0xbf , 0x7f }; void Delay_Ms (INT16U ms) { INT16U i; \t do { \t i = MAIN_Fosc / 96000 ; \t\t while (--i); }while (--ms); } void Delay_us (uchar us) {\twhile (us--);\t } void Display (INT16U Value) \t\t\t {\t\tDU = 0 ;\t\t\t\t\t\t\t \tP0 = table[Value/100 ];\t\t \tDU = 1 ;\t\t\t\t\t\t\t \tDU = 0 ;\t\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tP0 = T_COM[0 ];\t\t\t\t \tWE = 1 ;\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tDelay_Ms(3 ); \tDU = 0 ; \tP0 = table[Value%100 /10 ]|0x80 ; \tDU = 1 ; \tDU = 0 ; \tWE = 0 ; \tP0 = T_COM[1 ];\t\t\t \tWE = 1 ; \tWE = 0 ; \tDelay_Ms(3 ); \tDU = 0 ; \tP0 = table[Value%10 ];\t\t \tDU = 1 ; \tDU = 0 ; \tWE = 0 ; \tP0 = T_COM[2 ];\t\t\t\t \tWE = 1 ; \tWE = 0 ; \tDelay_Ms(3 ); } bit ds_init () {\tbit i; \tDS = 1 ; \t_nop_(); \tDS = 0 ; \tDelay_us(75 ); \tDS = 1 ; \tDelay_us(4 ); \ti = DS; \tDelay_us(20 ); \tDS = 1 ; \t_nop_(); \treturn (i); } void write_byte (uchar dat) {\tuchar i; \tfor (i=0 ;i<8 ;i++) \t{ \t\tDS = 0 ; \t\t_nop_(); \t\tDS = dat & 0x01 ; \t\tDelay_us(10 ); \t\tDS = 1 ; \t\t_nop_(); \t\tdat >>= 1 ; \t} } uchar read_byte () {\tuchar i, j, dat; \tfor (i=0 ;i<8 ;i++) \t{ \t\tDS = 0 ; \t\t_nop_(); \t\tDS = 1 ; \t\t_nop_(); \t\tj = DS; \t\tDelay_us(10 ); \t\tDS = 1 ; \t\t_nop_(); \t\tdat = (j<<7 )|(dat>>1 );\t \t} \treturn (dat); } void main () {\tuint i; \tuchar L, M; \twhile (1 ) \t{ \t\tds_init(); \t\twrite_byte(0xcc ); \t\twrite_byte(0x44 ); \t\tds_init(); \t\twrite_byte(0xcc ); \t\twrite_byte(0xbe ); \t\tL = read_byte(); \t\tM = read_byte(); \t\ti = M; \t\ti <<= 8 ; \t\ti |= L;\t\t\t\t\t\t \t\ti = i * 0.0625 * 10 + 0.5 ; \t\tDisplay(i); \t} }
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"DS18B20介绍 DS18B20数字温度传感器是DALLAS公司生产的单总线器件 ,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。
\n特点 \n通信采用1-Wire接口 \nn每个DS18B20都有唯一的64位序列码储存在板载ROM中 \n无需外部元件 \n可从数据线供电,电源范围为3.0V ~ 5.5V。 \n可测量的温度范围在-55℃ ~ +125℃ \n在-10~+85℃范围内精确度为±0.5℃ \n温度计分辨率可设置为9~12位,12位时分辨率对应为0.0625℃ \n \n
\n单总线 DS18B20采用1-wire Bus
传输数据,所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。
\n单总线信号类型: 复位脉冲、存在脉冲、写0、写1、读0、读1。
\n所有这些信号除存在脉冲由DS18B20发出的以外,其他信号都由总线控制器发出。并且,数据传输总是从最低有效位开始。
\n初始化时序 初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。
\n主机和DS18B20做任何通讯前都需要对其初始化。
\n初始化期间:
\n\n总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位
\n \n释放总线,等到15-60us,此时18B20将返回一个60~240us之间的低电平存在信号
\n \n \n
\n写时序 写时序有2种写法:写0时序和写1时序。总线控制器通过写 1 时序写逻辑 1 到 DS18B20,写 0 时序写逻辑 0 到 DS18B20。
\n所有写时序必须持续 >=60us,包括:两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。
\n初始化写时序:
\n总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。
\n当总线被释放的时候,5K 的上拉电阻将拉高总线。因此,如果总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。
\n进行写时序:
\n总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采 样。如果线上是高电平,就是写1。如果线上是低电平,就是写0
\n
\n读时序 \n先把总线拉低至少1us \n在15us内选取1us进行读数据 \n在拉低的15us(包括上面2点所说的1us)内传输的数据是有效的 \n然后总线被释放 \n总时间必须>=60us \n \n
\n读1的详细时序:
\nTINIT,TRC 和 TSAMPLE 之和必须小于 15us
\n
\n推荐的读1时序:
\n系统时间可以用下面办法达到最大:
\nTINIT 和 TRC 保持时间尽可能小;把控制器采样时间放 到 15us 周期的最后。
\n
\n数据获取 通过单线总线端口访问DS18B20的协议如下:
\n\n初始化
\n \nROM操作指令
\n \nDS18B20功能指令、温度转换命令、读取暂存器命令
\n \n \n部分常用ROM指令如下:
\n
\n例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 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 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 161 162 163 164 165 166 167 168 169 170 171 172 #include <reg52.h> #include <intrins.h> #define MAIN_Fosc\t\t11059200UL\t typedef unsigned char INT8U;typedef unsigned char uchar;typedef unsigned int INT16U;typedef unsigned int uint;sbit DS = P2^2 ; sbit DU = P2^6 ; sbit WE = P2^7 ; uchar code table[]={ 0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F ,0x6F , 0x77 , 0x7C , 0x39 , 0x5E , 0x79 , 0x71 , 0x40 , 0x80 , 0x00 }; \t\t\t\t uchar code T_COM[] = {0xfe , 0xfd , 0xfb , 0xf7 , 0xef , 0xdf , 0xbf , 0x7f }; void Delay_Ms (INT16U ms) { INT16U i; \t do { \t i = MAIN_Fosc / 96000 ; \t\t while (--i); }while (--ms); } void Delay_us (uchar us) {\twhile (us--);\t } void Display (INT16U Value) \t\t\t {\t\tDU = 0 ;\t\t\t\t\t\t\t \tP0 = table[Value/100 ];\t\t \tDU = 1 ;\t\t\t\t\t\t\t \tDU = 0 ;\t\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tP0 = T_COM[0 ];\t\t\t\t \tWE = 1 ;\t\t\t\t\t\t \tWE = 0 ;\t\t\t\t\t\t \tDelay_Ms(3 ); \tDU = 0 ; \tP0 = table[Value%100 /10 ]|0x80 ; \tDU = 1 ; \tDU = 0 ; \tWE = 0 ; \tP0 = T_COM[1 ];\t\t\t \tWE = 1 ; \tWE = 0 ; \tDelay_Ms(3 ); \tDU = 0 ; \tP0 = table[Value%10 ];\t\t \tDU = 1 ; \tDU = 0 ; \tWE = 0 ; \tP0 = T_COM[2 ];\t\t\t\t \tWE = 1 ; \tWE = 0 ; \tDelay_Ms(3 ); } bit ds_init () {\tbit i; \tDS = 1 ; \t_nop_(); \tDS = 0 ; \tDelay_us(75 ); \tDS = 1 ; \tDelay_us(4 ); \ti = DS; \tDelay_us(20 ); \tDS = 1 ; \t_nop_(); \treturn (i); } void write_byte (uchar dat) {\tuchar i; \tfor (i=0 ;i<8 ;i++) \t{ \t\tDS = 0 ; \t\t_nop_(); \t\tDS = dat & 0x01 ; \t\tDelay_us(10 ); \t\tDS = 1 ; \t\t_nop_(); \t\tdat >>= 1 ; \t} } uchar read_byte () {\tuchar i, j, dat; \tfor (i=0 ;i<8 ;i++) \t{ \t\tDS = 0 ; \t\t_nop_(); \t\tDS = 1 ; \t\t_nop_(); \t\tj = DS; \t\tDelay_us(10 ); \t\tDS = 1 ; \t\t_nop_(); \t\tdat = (j<<7 )|(dat>>1 );\t \t} \treturn (dat); } void main () {\tuint i; \tuchar L, M; \twhile (1 ) \t{ \t\tds_init(); \t\twrite_byte(0xcc ); \t\twrite_byte(0x44 ); \t\tds_init(); \t\twrite_byte(0xcc ); \t\twrite_byte(0xbe ); \t\tL = read_byte(); \t\tM = read_byte(); \t\ti = M; \t\ti <<= 8 ; \t\ti |= L;\t\t\t\t\t\t \t\ti = i * 0.0625 * 10 + 0.5 ; \t\tDisplay(i); \t} }
\n"},{"title":"Github Actions自动部署","date":"2021-09-13T12:13:46.000Z","description":"使用Github的Actions功能自动部署Hexo博客","cover":"https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png","abbrlink":"88ad757","_content":"\n\n\n## 写在前面\n\n\n\n\n\n没用过Actions的建议细看,好好理解其工作原理和流程\n\n本文大部分参考 [糖果屋] 的教程,详见[糖果屋-使用Github Action实现全自动部署](https://akilar.top/posts/f752c86d/), 感谢大佬!\n\n## Github Actions简介\n\nGithub Actions 可以理解为一个自动化脚本,当它检测到某个仓库发生变化后,自动执行脚本语句\n\n在这里可以让脚本自动执行 `hexo cl && hexo g -d` 三连操作\n\n\n\n\n\n\n\n## 获取令牌(Token)\n\n为了确保交由 `Github Action` 来持续部署时,`Github Action` 具备足够的权限来进行 `hexo deploy` 操作[因为远程服务器的git不能使用ssh方式推送],需要先获取 `Token`,博主分别在 `Github`、`Gitee`、`Coding` 处部署了静态页面,所以也就需要获取这三处的 `Token`。\n\n当我们获取了token后,在 `_config.yml`文件的 `deploy` 项就可以使用了\n\n{%tabs test,-1%}\n\n\n\n访问 [Github-> 头像(右上角)->Settings->Developer Settings->Personal access tokens](https://github.com/settings/tokens)->generate new token, 创建的 `Token` 名称随意,但必须勾选 repo 项。\n\n![image-20210913220005116](https://gitee.com/ajream/images/raw/master/img/20210913220008_image-20210913220005116.png)\n\n![image-20210913220422469](https://gitee.com/ajream/images/raw/master/img/20210913220423_image-20210913220422469.png)\n\n\n\n{% note warning modern %}\n\n`token` 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n访问 [Gitee-> 头像(右上角)-> 设置 -> 私人令牌](https://gitee.com/profile/personal_access_tokens) -> 生成新令牌\n\n![image-20210913221257282](https://gitee.com/ajream/images/raw/master/img/20210913221258_image-20210913221257282.png)\n\n\n\n{% note warning modern %}\n\n`token` 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n\n\n\n\n访问 [Coding](https://coding.net/)-> 头像(右上角)-> 个人账户设置 -> 访问令牌 -> 新建令牌。\n\n![image-20210913221822733](https://gitee.com/ajream/images/raw/master/img/20210913221824_image-20210913221822733.png)\n\n\n\n\n\n![image-20210913221754924](https://gitee.com/ajream/images/raw/master/img/20210913221756_image-20210913221754924.png)\n\n\n\n{% note warning modern %}\n\n注意coding平台与Github、Gitee不同,它生成的令牌还有【令牌用户名】,并且只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`和`令牌用户名`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n\n\n{%endtabs%}\n\n\n\n\n\n\n\n## 创建存放源码的私有仓库\n\n{%folding blue, 展开%}\n\n我们需要创建一个用来存放 `Hexo` 博客源码的私有仓库 `[SourceRepo]`,这点在 [Win10](https://akilar.top/posts/6ef63e2d/) 的 `Hexo` 博客搭建教程中有提到。为了保持教程的连贯,此处再写一遍。\n[![img](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/PZ7wmiuX8ae9rVK.png)](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/PZ7wmiuX8ae9rVK.png)\n\n\n创建完成后,需要把博客的源码 `push` 到这里。首先获取远程仓库地址,此处虽然 `SSH` 和 `HTTPS` 均可。`SSH` 在绑定过 `ssh key` 的设备上无需再输入密码,`HTTPS` 则需要输入密码,但是 `SSH` 偶尔会遇到端口占用的情况。请自主选择。\n[![img](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/mpdUYJKcfWLQ2Eg.png)](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/mpdUYJKcfWLQ2Eg.png)\n\n\n\n{%note default modern%}\n\n这里之所以是私有仓库,是因为在接下来的配置中会用到 `Token`,如果 `Token` 被盗用,别人可以肆意操作你的 github 仓库内容,为了避免这一风险,才选择的博客源码闭源。\n\n{%endnote%}\n\n{%endfolding%}\n\n\n\n\n\n## 配置 deploy 项\n\n\n\n打开站点配置文件 `[Blogroot]/_config.yml`, 找到 `deploy` 配置项,使用之前生成的 `[SiteToken]` 和各个站点仓库 `URL` 来组装地址。\n\n\n\n```yml\ndeploy:\n- type: git\n repo:\n gitHub: https://[GithubUsername]:[GithubToken]@github.com/[GithubUsername]/[GithubBlogRepo].git[,branch]\n gitee: https://[Gitee用户名]:[GiteeToken]@gitee.com/[GiteeUsername]/[GiteeBlogRepo].git[,branch]\n coding: https://[令牌用户名]:[CodingToken]@e.coding.net/[CodingUsername]/[CodingBlogRepo].git[,branch]\n # [,branch]为可选项,表示部署的分支\n #2020年10月后github新建仓库默认分支改为main,注意更改\n```\n\n\n\n{%note default modern%}\n\n注意:Gitee的用户名如果含有大写字母,要改为小写 ; coding的【令牌用户名】是生成令牌时显示出来的,如果没有注意,要重新生成一次\n\n{%endnote%}\n\n\n\n参考我个人的写法:\n\n\n\n```yml\ndeploy:\n type: git\n repo: \n github: https://aJream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@github.com/aJream/aJream.github.io.git,master\n gitee: https://ajream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@gitee.com/ajream/ajream.git,master\n coding: https://xxxxxxxxxx:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@e.coding.net/ajream/page/blog1.git,master\n```\n\n\n\n\n\n## 配置 Github Action\n\n\n\n在 `[Blogroot]` 新建`.github` 文件夹,注意开头是有个`.` 的。然后在`.github` 内新建 `workflows` 文件夹,再在 `workflows` 文件夹内新建 `autodeploy.yml`, 在 `[Blogroot]/.github/workflows/autodeploy.yml` 里面输入\n\n```yml\n# 当有改动推送到master分支时,启动Action\nname: 自动部署\n\non:\n push:\n branches:\n - master #2020年10月后github新建仓库默认分支改为main,注意更改\n\n release:\n types:\n - published\n\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n - name: 检查分支\n uses: actions/checkout@v2\n with:\n ref: master #2020年10月后github新建仓库默认分支改为main,注意更改\n\n - name: 安装 Node\n uses: actions/setup-node@v1\n with:\n node-version: \"12.x\"\n\n - name: 安装 Hexo\n run: |\n export TZ='Asia/Shanghai'\n npm install hexo-cli -g\n\n - name: 缓存 Hexo\n uses: actions/cache@v1\n id: cache\n with:\n path: node_modules\n key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}\n\n - name: 安装依赖\n if: steps.cache.outputs.cache-hit != 'true'\n run: |\n npm install --save\n\n - name: 生成静态文件\n run: |\n hexo clean\n hexo generate\n\n - name: 部署\n run: |\n git config --global user.name \"[GithubUsername]\"\n git config --global user.email \"[GithubEmail]\"\n git clone https://github.com/[GithubUsername]/[GithubBlogRepo].git .deploy_git\n # 此处务必用HTTPS链接。SSH链接可能有权限报错的隐患\n # =====注意.deploy_git前面有个空格=====\n # 这行指令的目的是clone博客静态文件仓库,防止Hexo推送时覆盖整个静态文件仓库,而是只推送有更改的文件\n hexo deploy\n```\n\n\n\n{%note info modern%}\n\n注意看代码里面的注释\n\n{%endnote%}\n\n\n\n\n\n## 重新设置远程仓库和分支\n\n\n\n{%tabs gggg, -1%}\n\n\n\n1. 添加屏蔽项\n 因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。\n 打开 `[Blogroot]/.gitignore`, 输入以下内容:\n\n ```sh\n .DS_Store\n Thumbs.db\n db.json\n *.log\n node_modules/\n public/\n .deploy*/\n .deploy_git*/\n .idea\n themes/butterfly/.git\n ```\n\n 如果不是 `butterfly` 主题,记得替换最后一行内容为你自己当前使用的主题\n\n2. 提交源码到刚才新建的私有仓库 \n\n 在博客根目录 [Blogroot] 下启动终端,使用 git 指令重设仓库地址。这样在新建仓库,我们仍旧可以保留珍贵的 commit history,便于版本回滚。\n\n ```sh\n git remote rm origin # 删除原有仓库链接\n git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为新的存放源码的github私有仓库\n git checkout -b master # 切换到master分支,\n #2020年10月后github新建仓库默认分支改为main,注意更改\n # 如果不是,后面的所有设置的分支记得保持一致\n git add .\n git commit -m \"github action update\"\n git push origin master\n \n #2020年10月后github新建仓库默认分支改为main,注意更改\n ```\n\n\n\n> 可能遇到的 bug\n> 因为 `butterfly` 主题文件夹下的`.git` 文件夹的存在,那么主题文件夹会被识别子项目。从而无法被上传到源码仓库。若是遇到添加屏蔽项,但是还是无法正常上传主题文件夹的情况。请先将本地源码中的 `themes` 文件夹移动到别的目录下。然后 `commit` 一次。接着将 `themes` 文件夹移动回来,再 `commit` 一次。\n\n\n\n\n\n\n\n\n\n1. 删除或者先把 `[Blogroot]/themes/butterfly/.git` 移动到非博客文件夹目录下,原因是主题文件夹下的`.git` 文件夹的存在会导致其被识别成子项目,从而无法被上传到源码仓库。\n\n2. 在博客根目录 `[Blogroot]` 路径下运行指令\n\n ```sh\n git init #初始化\n git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为存放源码的github私有仓库\n git checkout -b master # 切换到master分支,\n #2020年10月后github新建仓库默认分支改为main,注意更改\n # 如果不是,后面的所有设置的分支记得保持一致\n ```\n\n3. 添加屏蔽项\n 因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。\n 打开 `[Blogroot]/.gitignore`, 输入以下内容:\n\n ```sh\n .DS_Store\n Thumbs.db\n db.json\n *.log\n node_modules/\n public/\n .deploy*/\n .deploy_git*/\n .idea\n themes/butterfly/.git # 如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题\n ```\n\n\n\n完成后就可以用git指令进行提交到Github源码仓库,Actions监测到仓库发生改变会自动部署\n\n```sh\ngit add .\ngit commit -m \"github action update\"\ngit push origin master\n#2020年10月后github新建仓库默认分支改为main,注意更改\n```\n\n\n\n此时你的主题文件夹若已经被正常上传,并且你也添加了主题文件夹下的.git 文件夹的屏蔽项。那可以考虑把第二步移走或删除的`.git` 放回来,用作以后升级。(不禁怀疑真的有人会去用这个方式来升级吗)\n\n\n\n{%endtabs%}\n\n\n\n\n\n## 查看部署情况\n\n![image-20210913232006065](https://gitee.com/ajream/images/raw/master/img/20210913232007_image-20210913232006065.png)\n\n\n\n\n\n点击正在运行的workflow就可以查看部署情况\n\n![image-20210913233828678](https://gitee.com/ajream/images/raw/master/img/20210913233832_image-20210913233828678.png)\n\n\n\n![image-20210913234018254](https://gitee.com/ajream/images/raw/master/img/20210913234019_image-20210913234018254.png)\n\n## 我遇到的Bug\n\n\n\nBug:\n\n**Spawn Failed**\n\n原因主要就是配置deploy项 时仓库的git链接没有正确配置,比如 :\n\n1. gitee仓库的用户名使用大写就不行,要改为小写(平台上的用户名不用改,只需要改链接中的)\n2. 配置coding时没有仔细看教程,用户名必须要使用生成 `token` 时系统同时生成的令牌用户名 \n\n\n\n\n\n**看板娘不能显示**\n\n删除 `BlogRoot/themes/butterfly/source/live2d-widget` 下的 `.git` 文件\n\n然后执行:\n\n```\ngit rm --cached /themes/butterfly/source/live2d-widget\ngit add .\ngit commit -m \"修复看板娘不能正常显示\"\ngit push origin master\n```\n\n \n\n","source":"_posts/Hexo/github Actions自动部署.md","raw":"---\ntitle: Github Actions自动部署\ndate: '2021-09-13 20:13:46'\ntags:\n - Hexo\ncategories:\n - Hexo\ndescription: 使用Github的Actions功能自动部署Hexo博客\ncover: https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png\nabbrlink: 88ad757\n---\n\n\n\n## 写在前面\n\n\n\n\n\n没用过Actions的建议细看,好好理解其工作原理和流程\n\n本文大部分参考 [糖果屋] 的教程,详见[糖果屋-使用Github Action实现全自动部署](https://akilar.top/posts/f752c86d/), 感谢大佬!\n\n## Github Actions简介\n\nGithub Actions 可以理解为一个自动化脚本,当它检测到某个仓库发生变化后,自动执行脚本语句\n\n在这里可以让脚本自动执行 `hexo cl && hexo g -d` 三连操作\n\n\n\n\n\n\n\n## 获取令牌(Token)\n\n为了确保交由 `Github Action` 来持续部署时,`Github Action` 具备足够的权限来进行 `hexo deploy` 操作[因为远程服务器的git不能使用ssh方式推送],需要先获取 `Token`,博主分别在 `Github`、`Gitee`、`Coding` 处部署了静态页面,所以也就需要获取这三处的 `Token`。\n\n当我们获取了token后,在 `_config.yml`文件的 `deploy` 项就可以使用了\n\n{%tabs test,-1%}\n\n\n\n访问 [Github-> 头像(右上角)->Settings->Developer Settings->Personal access tokens](https://github.com/settings/tokens)->generate new token, 创建的 `Token` 名称随意,但必须勾选 repo 项。\n\n![image-20210913220005116](https://gitee.com/ajream/images/raw/master/img/20210913220008_image-20210913220005116.png)\n\n![image-20210913220422469](https://gitee.com/ajream/images/raw/master/img/20210913220423_image-20210913220422469.png)\n\n\n\n{% note warning modern %}\n\n`token` 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n访问 [Gitee-> 头像(右上角)-> 设置 -> 私人令牌](https://gitee.com/profile/personal_access_tokens) -> 生成新令牌\n\n![image-20210913221257282](https://gitee.com/ajream/images/raw/master/img/20210913221258_image-20210913221257282.png)\n\n\n\n{% note warning modern %}\n\n`token` 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n\n\n\n\n访问 [Coding](https://coding.net/)-> 头像(右上角)-> 个人账户设置 -> 访问令牌 -> 新建令牌。\n\n![image-20210913221822733](https://gitee.com/ajream/images/raw/master/img/20210913221824_image-20210913221822733.png)\n\n\n\n\n\n![image-20210913221754924](https://gitee.com/ajream/images/raw/master/img/20210913221756_image-20210913221754924.png)\n\n\n\n{% note warning modern %}\n\n注意coding平台与Github、Gitee不同,它生成的令牌还有【令牌用户名】,并且只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`和`令牌用户名`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n\n\n{%endtabs%}\n\n\n\n\n\n\n\n## 创建存放源码的私有仓库\n\n{%folding blue, 展开%}\n\n我们需要创建一个用来存放 `Hexo` 博客源码的私有仓库 `[SourceRepo]`,这点在 [Win10](https://akilar.top/posts/6ef63e2d/) 的 `Hexo` 博客搭建教程中有提到。为了保持教程的连贯,此处再写一遍。\n[![img](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/PZ7wmiuX8ae9rVK.png)](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/PZ7wmiuX8ae9rVK.png)\n\n\n创建完成后,需要把博客的源码 `push` 到这里。首先获取远程仓库地址,此处虽然 `SSH` 和 `HTTPS` 均可。`SSH` 在绑定过 `ssh key` 的设备上无需再输入密码,`HTTPS` 则需要输入密码,但是 `SSH` 偶尔会遇到端口占用的情况。请自主选择。\n[![img](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/mpdUYJKcfWLQ2Eg.png)](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/mpdUYJKcfWLQ2Eg.png)\n\n\n\n{%note default modern%}\n\n这里之所以是私有仓库,是因为在接下来的配置中会用到 `Token`,如果 `Token` 被盗用,别人可以肆意操作你的 github 仓库内容,为了避免这一风险,才选择的博客源码闭源。\n\n{%endnote%}\n\n{%endfolding%}\n\n\n\n\n\n## 配置 deploy 项\n\n\n\n打开站点配置文件 `[Blogroot]/_config.yml`, 找到 `deploy` 配置项,使用之前生成的 `[SiteToken]` 和各个站点仓库 `URL` 来组装地址。\n\n\n\n```yml\ndeploy:\n- type: git\n repo:\n gitHub: https://[GithubUsername]:[GithubToken]@github.com/[GithubUsername]/[GithubBlogRepo].git[,branch]\n gitee: https://[Gitee用户名]:[GiteeToken]@gitee.com/[GiteeUsername]/[GiteeBlogRepo].git[,branch]\n coding: https://[令牌用户名]:[CodingToken]@e.coding.net/[CodingUsername]/[CodingBlogRepo].git[,branch]\n # [,branch]为可选项,表示部署的分支\n #2020年10月后github新建仓库默认分支改为main,注意更改\n```\n\n\n\n{%note default modern%}\n\n注意:Gitee的用户名如果含有大写字母,要改为小写 ; coding的【令牌用户名】是生成令牌时显示出来的,如果没有注意,要重新生成一次\n\n{%endnote%}\n\n\n\n参考我个人的写法:\n\n\n\n```yml\ndeploy:\n type: git\n repo: \n github: https://aJream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@github.com/aJream/aJream.github.io.git,master\n gitee: https://ajream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@gitee.com/ajream/ajream.git,master\n coding: https://xxxxxxxxxx:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@e.coding.net/ajream/page/blog1.git,master\n```\n\n\n\n\n\n## 配置 Github Action\n\n\n\n在 `[Blogroot]` 新建`.github` 文件夹,注意开头是有个`.` 的。然后在`.github` 内新建 `workflows` 文件夹,再在 `workflows` 文件夹内新建 `autodeploy.yml`, 在 `[Blogroot]/.github/workflows/autodeploy.yml` 里面输入\n\n```yml\n# 当有改动推送到master分支时,启动Action\nname: 自动部署\n\non:\n push:\n branches:\n - master #2020年10月后github新建仓库默认分支改为main,注意更改\n\n release:\n types:\n - published\n\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n - name: 检查分支\n uses: actions/checkout@v2\n with:\n ref: master #2020年10月后github新建仓库默认分支改为main,注意更改\n\n - name: 安装 Node\n uses: actions/setup-node@v1\n with:\n node-version: \"12.x\"\n\n - name: 安装 Hexo\n run: |\n export TZ='Asia/Shanghai'\n npm install hexo-cli -g\n\n - name: 缓存 Hexo\n uses: actions/cache@v1\n id: cache\n with:\n path: node_modules\n key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}\n\n - name: 安装依赖\n if: steps.cache.outputs.cache-hit != 'true'\n run: |\n npm install --save\n\n - name: 生成静态文件\n run: |\n hexo clean\n hexo generate\n\n - name: 部署\n run: |\n git config --global user.name \"[GithubUsername]\"\n git config --global user.email \"[GithubEmail]\"\n git clone https://github.com/[GithubUsername]/[GithubBlogRepo].git .deploy_git\n # 此处务必用HTTPS链接。SSH链接可能有权限报错的隐患\n # =====注意.deploy_git前面有个空格=====\n # 这行指令的目的是clone博客静态文件仓库,防止Hexo推送时覆盖整个静态文件仓库,而是只推送有更改的文件\n hexo deploy\n```\n\n\n\n{%note info modern%}\n\n注意看代码里面的注释\n\n{%endnote%}\n\n\n\n\n\n## 重新设置远程仓库和分支\n\n\n\n{%tabs gggg, -1%}\n\n\n\n1. 添加屏蔽项\n 因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。\n 打开 `[Blogroot]/.gitignore`, 输入以下内容:\n\n ```sh\n .DS_Store\n Thumbs.db\n db.json\n *.log\n node_modules/\n public/\n .deploy*/\n .deploy_git*/\n .idea\n themes/butterfly/.git\n ```\n\n 如果不是 `butterfly` 主题,记得替换最后一行内容为你自己当前使用的主题\n\n2. 提交源码到刚才新建的私有仓库 \n\n 在博客根目录 [Blogroot] 下启动终端,使用 git 指令重设仓库地址。这样在新建仓库,我们仍旧可以保留珍贵的 commit history,便于版本回滚。\n\n ```sh\n git remote rm origin # 删除原有仓库链接\n git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为新的存放源码的github私有仓库\n git checkout -b master # 切换到master分支,\n #2020年10月后github新建仓库默认分支改为main,注意更改\n # 如果不是,后面的所有设置的分支记得保持一致\n git add .\n git commit -m \"github action update\"\n git push origin master\n \n #2020年10月后github新建仓库默认分支改为main,注意更改\n ```\n\n\n\n> 可能遇到的 bug\n> 因为 `butterfly` 主题文件夹下的`.git` 文件夹的存在,那么主题文件夹会被识别子项目。从而无法被上传到源码仓库。若是遇到添加屏蔽项,但是还是无法正常上传主题文件夹的情况。请先将本地源码中的 `themes` 文件夹移动到别的目录下。然后 `commit` 一次。接着将 `themes` 文件夹移动回来,再 `commit` 一次。\n\n\n\n\n\n\n\n\n\n1. 删除或者先把 `[Blogroot]/themes/butterfly/.git` 移动到非博客文件夹目录下,原因是主题文件夹下的`.git` 文件夹的存在会导致其被识别成子项目,从而无法被上传到源码仓库。\n\n2. 在博客根目录 `[Blogroot]` 路径下运行指令\n\n ```sh\n git init #初始化\n git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为存放源码的github私有仓库\n git checkout -b master # 切换到master分支,\n #2020年10月后github新建仓库默认分支改为main,注意更改\n # 如果不是,后面的所有设置的分支记得保持一致\n ```\n\n3. 添加屏蔽项\n 因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。\n 打开 `[Blogroot]/.gitignore`, 输入以下内容:\n\n ```sh\n .DS_Store\n Thumbs.db\n db.json\n *.log\n node_modules/\n public/\n .deploy*/\n .deploy_git*/\n .idea\n themes/butterfly/.git # 如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题\n ```\n\n\n\n完成后就可以用git指令进行提交到Github源码仓库,Actions监测到仓库发生改变会自动部署\n\n```sh\ngit add .\ngit commit -m \"github action update\"\ngit push origin master\n#2020年10月后github新建仓库默认分支改为main,注意更改\n```\n\n\n\n此时你的主题文件夹若已经被正常上传,并且你也添加了主题文件夹下的.git 文件夹的屏蔽项。那可以考虑把第二步移走或删除的`.git` 放回来,用作以后升级。(不禁怀疑真的有人会去用这个方式来升级吗)\n\n\n\n{%endtabs%}\n\n\n\n\n\n## 查看部署情况\n\n![image-20210913232006065](https://gitee.com/ajream/images/raw/master/img/20210913232007_image-20210913232006065.png)\n\n\n\n\n\n点击正在运行的workflow就可以查看部署情况\n\n![image-20210913233828678](https://gitee.com/ajream/images/raw/master/img/20210913233832_image-20210913233828678.png)\n\n\n\n![image-20210913234018254](https://gitee.com/ajream/images/raw/master/img/20210913234019_image-20210913234018254.png)\n\n## 我遇到的Bug\n\n\n\nBug:\n\n**Spawn Failed**\n\n原因主要就是配置deploy项 时仓库的git链接没有正确配置,比如 :\n\n1. gitee仓库的用户名使用大写就不行,要改为小写(平台上的用户名不用改,只需要改链接中的)\n2. 配置coding时没有仔细看教程,用户名必须要使用生成 `token` 时系统同时生成的令牌用户名 \n\n\n\n\n\n**看板娘不能显示**\n\n删除 `BlogRoot/themes/butterfly/source/live2d-widget` 下的 `.git` 文件\n\n然后执行:\n\n```\ngit rm --cached /themes/butterfly/source/live2d-widget\ngit add .\ngit commit -m \"修复看板娘不能正常显示\"\ngit push origin master\n```\n\n \n\n","slug":"Hexo/github Actions自动部署","published":1,"updated":"2021-09-16T15:29:49.622Z","_id":"cktk8o6oe000rakve4wdsbjyw","comments":1,"layout":"post","photos":[],"link":"","content":"写在前面 \n没用过Actions的建议细看,好好理解其工作原理和流程
\n本文大部分参考 [糖果屋] 的教程,详见糖果屋-使用Github Action实现全自动部署 , 感谢大佬!
\nGithub Actions简介 \nGithub Actions 可以理解为一个自动化脚本,当它检测到某个仓库发生变化后,自动执行脚本语句
\n在这里可以让脚本自动执行 hexo cl && hexo g -d
三连操作
\n获取令牌(Token) \n为了确保交由 Github Action
来持续部署时,Github Action
具备足够的权限来进行 hexo deploy
操作[因为远程服务器的git不能使用ssh方式推送],需要先获取 Token
,博主分别在 Github
、Gitee
、Coding
处部署了静态页面,所以也就需要获取这三处的 Token
。
\n当我们获取了token后,在 _config.yml
文件的 deploy
项就可以使用了
\n\n创建存放源码的私有仓库 \n 展开 \n \n
我们需要创建一个用来存放 Hexo
博客源码的私有仓库 [SourceRepo]
,这点在 Win10 的 Hexo
博客搭建教程中有提到。为了保持教程的连贯,此处再写一遍。
创建完成后,需要把博客的源码 push
到这里。首先获取远程仓库地址,此处虽然 SSH
和 HTTPS
均可。SSH
在绑定过 ssh key
的设备上无需再输入密码,HTTPS
则需要输入密码,但是 SSH
偶尔会遇到端口占用的情况。请自主选择。
这里之所以是私有仓库,是因为在接下来的配置中会用到 Token
,如果 Token
被盗用,别人可以肆意操作你的 github 仓库内容,为了避免这一风险,才选择的博客源码闭源。
\n
\n \n配置 deploy 项 \n打开站点配置文件 [Blogroot]/_config.yml
, 找到 deploy
配置项,使用之前生成的 [SiteToken]
和各个站点仓库 URL
来组装地址。
\n1 2 3 4 5 6 7 8 deploy: - type: git repo: gitHub: https://[GithubUsername]:[GithubToken]@github.com/[GithubUsername]/[GithubBlogRepo].git[,branch] gitee: https://[Gitee用户名]:[GiteeToken]@gitee.com/[GiteeUsername]/[GiteeBlogRepo].git[,branch] coding: https://[令牌用户名]:[CodingToken]@e.coding.net/[CodingUsername]/[CodingBlogRepo].git[,branch]
\n注意:Gitee的用户名如果含有大写字母,要改为小写 ; coding的【令牌用户名】是生成令牌时显示出来的,如果没有注意,要重新生成一次
\n
\n参考我个人的写法:
\n1 2 3 4 5 6 deploy: type: git repo: github: https://aJream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@github.com/aJream/aJream.github.io.git,master gitee: https://ajream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@gitee.com/ajream/ajream.git,master coding: https://xxxxxxxxxx:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@e.coding.net/ajream/page/blog1.git,master
\n配置 Github Action \n在 [Blogroot]
新建.github
文件夹,注意开头是有个.
的。然后在.github
内新建 workflows
文件夹,再在 workflows
文件夹内新建 autodeploy.yml
, 在 [Blogroot]/.github/workflows/autodeploy.yml
里面输入
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 55 56 57 name: 自动部署 on: push: branches: - master release: types: - published jobs: deploy: runs-on: ubuntu-latest steps: - name: 检查分支 uses: actions/checkout@v2 with: ref: master - name: 安装 Node uses: actions/setup-node@v1 with: node-version: "12.x" - name: 安装 Hexo run: | export TZ='Asia/Shanghai' npm install hexo-cli -g - name: 缓存 Hexo uses: actions/cache@v1 id: cache with: path: node_modules key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}} - name: 安装依赖 if: steps.cache.outputs.cache-hit != 'true' run: | npm install --save - name: 生成静态文件 run: | hexo clean hexo generate - name: 部署 run: | git config --global user.name "[GithubUsername]" git config --global user.email "[GithubEmail]" git clone https://github.com/[GithubUsername]/[GithubBlogRepo].git .deploy_git # 此处务必用HTTPS链接。SSH链接可能有权限报错的隐患 # =====注意.deploy_git前面有个空格===== # 这行指令的目的是clone博客静态文件仓库,防止Hexo推送时覆盖整个静态文件仓库,而是只推送有更改的文件 hexo deploy
\n\n重新设置远程仓库和分支 \n曾经用git管理过博源码 第一次用git管理博客源码 \n\n添加屏蔽项 \n因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。 \n打开 [Blogroot]/.gitignore
, 输入以下内容:
\n1 2 3 4 5 6 7 8 9 10 .DS_Store Thumbs.db db.json *.log node_modules/ public/ .deploy*/ .deploy_git*/ .idea themes/butterfly/.git
\n如果不是 butterfly
主题,记得替换最后一行内容为你自己当前使用的主题
\n \n\n提交源码到刚才新建的私有仓库
\n在博客根目录 [Blogroot] 下启动终端,使用 git 指令重设仓库地址。这样在新建仓库,我们仍旧可以保留珍贵的 commit history,便于版本回滚。
\n1 2 3 4 5 6 7 8 9 10 git remote rm origin git remote add origin git@github.com:[Github用户名]/[私有仓库名].git git checkout -b master git add . git commit -m "github action update" git push origin master
\n \n \n
\n可能遇到的 bug \n因为 butterfly
主题文件夹下的.git
文件夹的存在,那么主题文件夹会被识别子项目。从而无法被上传到源码仓库。若是遇到添加屏蔽项,但是还是无法正常上传主题文件夹的情况。请先将本地源码中的 themes
文件夹移动到别的目录下。然后 commit
一次。接着将 themes
文件夹移动回来,再 commit
一次。
\n \n\n删除或者先把 [Blogroot]/themes/butterfly/.git
移动到非博客文件夹目录下,原因是主题文件夹下的.git
文件夹的存在会导致其被识别成子项目,从而无法被上传到源码仓库。
\n \n\n在博客根目录 [Blogroot]
路径下运行指令
\n1 2 3 4 5 git init git remote add origin git@github.com:[Github用户名]/[私有仓库名].git git checkout -b master
\n \n\n添加屏蔽项 \n因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。 \n打开 [Blogroot]/.gitignore
, 输入以下内容:
\n1 2 3 4 5 6 7 8 9 10 .DS_Store Thumbs.db db.json *.log node_modules/ public/ .deploy*/ .deploy_git*/ .idea themes/butterfly/.git
\n \n \n
完成后就可以用git指令进行提交到Github源码仓库,Actions监测到仓库发生改变会自动部署
\n
1 2 3 4 git add . git commit -m "github action update" git push origin master
\n
此时你的主题文件夹若已经被正常上传,并且你也添加了主题文件夹下的.git 文件夹的屏蔽项。那可以考虑把第二步移走或删除的.git
放回来,用作以后升级。(不禁怀疑真的有人会去用这个方式来升级吗)
\n查看部署情况 \n
\n点击正在运行的workflow就可以查看部署情况
\n
\n
\n我遇到的Bug \nBug:
\nSpawn Failed
\n原因主要就是配置deploy项 时仓库的git链接没有正确配置,比如 :
\n\ngitee仓库的用户名使用大写就不行,要改为小写(平台上的用户名不用改,只需要改链接中的) \n配置coding时没有仔细看教程,用户名必须要使用生成 token
时系统同时生成的令牌用户名 \n \n看板娘不能显示
\n删除 BlogRoot/themes/butterfly/source/live2d-widget
下的 .git
文件
\n然后执行:
\n1 2 3 4 git rm --cached /themes/butterfly/source/live2d-widget git add . git commit -m "修复看板娘不能正常显示" git push origin master
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"写在前面 \n没用过Actions的建议细看,好好理解其工作原理和流程
\n本文大部分参考 [糖果屋] 的教程,详见糖果屋-使用Github Action实现全自动部署 , 感谢大佬!
\nGithub Actions简介 \nGithub Actions 可以理解为一个自动化脚本,当它检测到某个仓库发生变化后,自动执行脚本语句
\n在这里可以让脚本自动执行 hexo cl && hexo g -d
三连操作
\n获取令牌(Token) \n为了确保交由 Github Action
来持续部署时,Github Action
具备足够的权限来进行 hexo deploy
操作[因为远程服务器的git不能使用ssh方式推送],需要先获取 Token
,博主分别在 Github
、Gitee
、Coding
处部署了静态页面,所以也就需要获取这三处的 Token
。
\n当我们获取了token后,在 _config.yml
文件的 deploy
项就可以使用了
\n\n创建存放源码的私有仓库 \n 展开 \n \n
我们需要创建一个用来存放 Hexo
博客源码的私有仓库 [SourceRepo]
,这点在 Win10 的 Hexo
博客搭建教程中有提到。为了保持教程的连贯,此处再写一遍。
创建完成后,需要把博客的源码 push
到这里。首先获取远程仓库地址,此处虽然 SSH
和 HTTPS
均可。SSH
在绑定过 ssh key
的设备上无需再输入密码,HTTPS
则需要输入密码,但是 SSH
偶尔会遇到端口占用的情况。请自主选择。
这里之所以是私有仓库,是因为在接下来的配置中会用到 Token
,如果 Token
被盗用,别人可以肆意操作你的 github 仓库内容,为了避免这一风险,才选择的博客源码闭源。
\n
\n \n配置 deploy 项 \n打开站点配置文件 [Blogroot]/_config.yml
, 找到 deploy
配置项,使用之前生成的 [SiteToken]
和各个站点仓库 URL
来组装地址。
\n1 2 3 4 5 6 7 8 deploy: - type: git repo: gitHub: https://[GithubUsername]:[GithubToken]@github.com/[GithubUsername]/[GithubBlogRepo].git[,branch] gitee: https://[Gitee用户名]:[GiteeToken]@gitee.com/[GiteeUsername]/[GiteeBlogRepo].git[,branch] coding: https://[令牌用户名]:[CodingToken]@e.coding.net/[CodingUsername]/[CodingBlogRepo].git[,branch]
\n注意:Gitee的用户名如果含有大写字母,要改为小写 ; coding的【令牌用户名】是生成令牌时显示出来的,如果没有注意,要重新生成一次
\n
\n参考我个人的写法:
\n1 2 3 4 5 6 deploy: type: git repo: github: https://aJream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@github.com/aJream/aJream.github.io.git,master gitee: https://ajream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@gitee.com/ajream/ajream.git,master coding: https://xxxxxxxxxx:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@e.coding.net/ajream/page/blog1.git,master
\n配置 Github Action \n在 [Blogroot]
新建.github
文件夹,注意开头是有个.
的。然后在.github
内新建 workflows
文件夹,再在 workflows
文件夹内新建 autodeploy.yml
, 在 [Blogroot]/.github/workflows/autodeploy.yml
里面输入
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 55 56 57 name: 自动部署 on: push: branches: - master release: types: - published jobs: deploy: runs-on: ubuntu-latest steps: - name: 检查分支 uses: actions/checkout@v2 with: ref: master - name: 安装 Node uses: actions/setup-node@v1 with: node-version: "12.x" - name: 安装 Hexo run: | export TZ='Asia/Shanghai' npm install hexo-cli -g - name: 缓存 Hexo uses: actions/cache@v1 id: cache with: path: node_modules key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}} - name: 安装依赖 if: steps.cache.outputs.cache-hit != 'true' run: | npm install --save - name: 生成静态文件 run: | hexo clean hexo generate - name: 部署 run: | git config --global user.name "[GithubUsername]" git config --global user.email "[GithubEmail]" git clone https://github.com/[GithubUsername]/[GithubBlogRepo].git .deploy_git # 此处务必用HTTPS链接。SSH链接可能有权限报错的隐患 # =====注意.deploy_git前面有个空格===== # 这行指令的目的是clone博客静态文件仓库,防止Hexo推送时覆盖整个静态文件仓库,而是只推送有更改的文件 hexo deploy
\n\n重新设置远程仓库和分支 \n曾经用git管理过博源码 第一次用git管理博客源码 \n\n添加屏蔽项 \n因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。 \n打开 [Blogroot]/.gitignore
, 输入以下内容:
\n1 2 3 4 5 6 7 8 9 10 .DS_Store Thumbs.db db.json *.log node_modules/ public/ .deploy*/ .deploy_git*/ .idea themes/butterfly/.git
\n如果不是 butterfly
主题,记得替换最后一行内容为你自己当前使用的主题
\n \n\n提交源码到刚才新建的私有仓库
\n在博客根目录 [Blogroot] 下启动终端,使用 git 指令重设仓库地址。这样在新建仓库,我们仍旧可以保留珍贵的 commit history,便于版本回滚。
\n1 2 3 4 5 6 7 8 9 10 git remote rm origin git remote add origin git@github.com:[Github用户名]/[私有仓库名].git git checkout -b master git add . git commit -m "github action update" git push origin master
\n \n \n
\n可能遇到的 bug \n因为 butterfly
主题文件夹下的.git
文件夹的存在,那么主题文件夹会被识别子项目。从而无法被上传到源码仓库。若是遇到添加屏蔽项,但是还是无法正常上传主题文件夹的情况。请先将本地源码中的 themes
文件夹移动到别的目录下。然后 commit
一次。接着将 themes
文件夹移动回来,再 commit
一次。
\n \n\n删除或者先把 [Blogroot]/themes/butterfly/.git
移动到非博客文件夹目录下,原因是主题文件夹下的.git
文件夹的存在会导致其被识别成子项目,从而无法被上传到源码仓库。
\n \n\n在博客根目录 [Blogroot]
路径下运行指令
\n1 2 3 4 5 git init git remote add origin git@github.com:[Github用户名]/[私有仓库名].git git checkout -b master
\n \n\n添加屏蔽项 \n因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。 \n打开 [Blogroot]/.gitignore
, 输入以下内容:
\n1 2 3 4 5 6 7 8 9 10 .DS_Store Thumbs.db db.json *.log node_modules/ public/ .deploy*/ .deploy_git*/ .idea themes/butterfly/.git
\n \n \n
完成后就可以用git指令进行提交到Github源码仓库,Actions监测到仓库发生改变会自动部署
\n
1 2 3 4 git add . git commit -m "github action update" git push origin master
\n
此时你的主题文件夹若已经被正常上传,并且你也添加了主题文件夹下的.git 文件夹的屏蔽项。那可以考虑把第二步移走或删除的.git
放回来,用作以后升级。(不禁怀疑真的有人会去用这个方式来升级吗)
\n查看部署情况 \n
\n点击正在运行的workflow就可以查看部署情况
\n
\n
\n我遇到的Bug \nBug:
\nSpawn Failed
\n原因主要就是配置deploy项 时仓库的git链接没有正确配置,比如 :
\n\ngitee仓库的用户名使用大写就不行,要改为小写(平台上的用户名不用改,只需要改链接中的) \n配置coding时没有仔细看教程,用户名必须要使用生成 token
时系统同时生成的令牌用户名 \n \n看板娘不能显示
\n删除 BlogRoot/themes/butterfly/source/live2d-widget
下的 .git
文件
\n然后执行:
\n1 2 3 4 git rm --cached /themes/butterfly/source/live2d-widget git add . git commit -m "修复看板娘不能正常显示" git push origin master
\n"},{"title":"51单片机学习笔记(四)","description":"串行通信中的IIC总线工作原理和协议+EEPROM","abbrlink":"c27dd6ec","date":"2021-08-19T08:07:10.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n\n\n目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-wire、I2C和SPI总线\n\n- UART:是以异步方式进行通信(一条数据输入线,一条数据输出线)。\n\n- 1-wire:即单线总线,又叫单总线(只有一条线)。\n\n- I2C:同步串行2线方式进行通信(一条时钟线,一条数据线)。\n\n- SPI:同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。\n\n\n\n## 原理\n\nIIC串行总线,只有两根双向 信号线。一根是数据线SDA,另一根是时钟线SCL\n\n如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据 \n\n![image-20210822161125324](https://gitee.com/ajream/images/raw/master/img/20210822161128_image-20210822161125324.png)\n\n在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。\n\n在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况\n\n\n\n总线“线与关系”:I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,\n\n即`SDA = SDA1 & SDA2 & SDA3 & ...`, `SCL=SCL1 & SCL2 & SCL3 & ...`\n\n![image-20210822161725608](https://gitee.com/ajream/images/raw/master/img/20210822161726_image-20210822161725608.png)\n\n\n\n\n\n### 起始信号和终止信号\n\nSCL线为高电平期间 ,SDA线由高电平向低电平 的变化表示起始信号;\n\nSCL线为高电平期间 ,SDA线由低电平向高电平 的变化表示终止信号。\n\n![image-20210822162305371](https://gitee.com/ajream/images/raw/master/img/20210822162306_image-20210822162305371.png)\n\n### 数据位有效性\n\nSCL为高电平期间,数据线上的数据必须保持稳定\n\n只有SCL信号为低电平期间,SDA状态才允许变化。\n\n![image-20210822162522027](https://gitee.com/ajream/images/raw/master/img/20210822162523_image-20210822162522027.png)\n\n### IIC总线的传送与应答\n\n每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节**最后面**都必须跟随一位应答位(即一帧共有9位)\n\n主机通过从机发出的**应答位**来判断从机是否成功接收数据:\n\n- 从机正忙于其他事情,发出应答1,表示没有收到\n- 当从机空闲可以接收该字节数据时,从机会发出应答0\n\n当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。\n\n\n\n### 数据帧格式\n\n在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用\"0\"表示主机发送数据(T),\"1\"表示主机接收数据(R)。\n\n每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。\n\n### 软件模拟IIC总线传送数据\n\n主机可以采用不带IIC总线接口的单片机,如80C51、STC89C52等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。\n\n为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :\n\n![image-20210822163923932](https://gitee.com/ajream/images/raw/master/img/20210822163925_image-20210822163923932.png)\n\n模拟的代码如下:\n\n```c\n//起始信号\nvoid I2cStart(){\n\tSCL = 1;\n\tSDA = 1;\n\tdelay5us(); //SDA在拉低低之前,应该先保持高电平时间>4us,这里用5us\n\tSDA = 0;\n\tdelay5us();\t //SDA保持低电平时间>4us,这里用5us\n}\n\n//终止信号\nvoid I2cStop(){\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();\n\tSDA = 1;\n\tdelay5us();\n}\n\n```\n\n第3、4个图表示先将SCL拉高5us,然后读取并返回SDA的值,读取后将SCL拉低,代码如下\n\n\n```c\n//主机读取从机的应答\nbit ReadACK(){\n\tSCL = 1;\n\tdelay5us();\n\tif(SDA){\n\t\tSCL = 0;\n\t\treturn(1);\n\t}\n\telse{\n\t\tSCL = 0;\n\t\treturn(0);\n\t}\n}\n\n\n```\n\n\n\n发送数据i,在SCL为低时,将SDA拉高或拉低(根据发送的数据确定,如果发送1,则拉高,反之拉低);然后将SCL拉高,保持5us,再将SCL拉低,这样就发送了一位(bit)数据,最后释放总线\n\n```c\n//主机发送应答\nvoid SendACK(bit i){\n\tSCL = 0;\n \n\tif(i) SDA = 1;\n\telse SDA = 0;\n \n\tSCL = 1;\n\tdelay5us();\n\tSCL = 0; \t //拉低时钟总线\n\tSDA = 1;\t //释放数据总线\n}\n```\n\n\n\n## 串行EEPROM的扩展\n\nAteml公司的AT24C系列:\n\n- AT24C01:128字节(128×8位)\n\n- AT24C02:256字节(256×8位)\n- AT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位)\n- AT24C16:2K字节(2K×8位)\n\n\n\n### 写入过程\n\n**获取地址码:**AT24C系列E2PROM芯片地址的固定部分为1010,而当A2、A1、A0引脚接高、低电平后就得到确定的3位编码。形成的7位编码即为该器件的地址码。例如在开发板中A2, A1, A0都接地,则这7位码为 `1010000`\n\n![image-20210824140820582](https://gitee.com/ajream/images/raw/master/img/20210824140829_image-20210824140820582.png)\n\n**写操作:**单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。\n\n\n\n**传送数据:**\n\n- 这个过程单片机首先发送一个字节被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。\n\n- AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1(表示相对之前写入的位置往后移一位)\n\n- 当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式如下:\n\n ![image-20210822171711924](https://gitee.com/ajream/images/raw/master/img/20210822171714_image-20210822171711924.png)\n \n > 说明:S为起始信号,然后是7位地址码+ `0`(0表示写操作),然后写入第一个数据存放位置的首地址,后面的写入的数据自动往后移动一位,写入1Byte数据后,器件发送应答A确认已写入,最后如果写入完成后发送终止信号P。\n \n- 注意:在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。\n\n\n\n### 读出过程\n\n单片机先发送该器件的7位地址码和写方向位“0”(“伪写” ),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。 \n\n\n\n然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。\n\n当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。 \n\n![image-20210822171925336](https://gitee.com/ajream/images/raw/master/img/20210822171926_image-20210822171925336.png)\n\n\n\n完整代码:\n\n```c\n/*********************************************************************************\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tIIC通信,AT24C02读写数据,数码管显示数据。\t\t \t\t\t\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n#define At24c02ADDR 0XA0 //AT24C02硬件地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1;//I2C时钟总线\nsbit SDA = P2^0;//I2C数据总线\nuchar num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n/*====================================\n函数\t: delay(uint z)\n参数\t:z 延时毫秒设定,取值范围0-65535\n返回值\t:无\n描述\t:12T/Fosc11.0592M毫秒级延时\n====================================*/\nvoid delay(uint z){\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示数值,取值范围0-255\n返回值\t:无\n描述\t:三位共阴数码管动态显示\n====================================*/\nvoid display(uchar i){\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei){\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid timer0Init(){\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:delay5us()\n参数\t:无\n返回值\t:无\n描述\t:5us延时函数\n====================================*/\nvoid delay5us(){\n\t_nop_();\n}\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart(){\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop(){\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK(){\n\tSCL = 0;\t\t\t//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;\t\t\t//拉高,读SDA\n\tdelay5us();\n\tif(SDA){\t\t\t//NOACK\n\t\tSCL = 0;\n\t\treturn(1);\t\t//返回1\n\t}\n\telse{\t\t\t\t//ACK\n\t\tSCL = 0;\n\t\treturn(0);\t\t//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i){\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT){\n\tuchar i; \n\tfor(i=0; i<8; i++){ //分别写8次,每次写1位\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:At24c02Write(uchar ADDR, DAT)\n参数\t:ADDR 单元地址0-255,DAT 需要输入的数据0-255\n返回值\t:无\n描述\t:At24c02指定单元写入一个字节数据\n====================================*/\nvoid At24c02Write(uchar ADDR, DAT){\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK()) //读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(ADDR);//发送储存单元地址字节\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(DAT);//发送一字节数据\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cStop();\t//I2C停止信号\n}\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte(){\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++){ \t//分别读8次,每次读一位\n\t\n\t\tDAT <<= 1; \t\t\t//数据左移1位,准备接收一位\n\t\tSCL = 0; \t\t\t//拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; \t\t\t//拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;\t//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); \t\t\t//返回读出的数据\n}\n\n/*====================================\n函数\t:At24c02Read(uchar ADDR)\n参数\t:ADDR 单元地址\t0-255\n返回值\t:返回指定单元的数据\n描述\t:读AT24C02指定单元内数据\n====================================*/\nuchar At24c02Read(uchar ADDR){\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(ADDR);//I2C发送一个字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环{\t\n\ttimer0Init();//定时器0初始化\n\tEA = 0;//屏蔽中断\n\tAt24c02Write(3, 188);//给第3单元写入数据“188”\n\tdelay(1);//延时等待AT24C02处理\n\tnum = At24c02Read(3);//读出第3单元内数据送给显示变量\n\tif(AckFlag)//当从机非应答\n\t\tP1 = 0;//亮P1所有灯\n\telse\n\t\tP1 = 0XFF;//灭P1所有灯\n\tEA = 1;//开中断\n\twhile(1);\n} \n\n//定时器0中断函数\nvoid timer0() interrupt 1 {\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n} \n```\n\n","source":"_posts/51单片机/51单片机学习笔记(四).md","raw":"---\ntitle: 51单片机学习笔记(四)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 串行通信中的IIC总线工作原理和协议+EEPROM\nabbrlink: c27dd6ec\ndate: 2021-08-19 16:07:10\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n\n\n目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-wire、I2C和SPI总线\n\n- UART:是以异步方式进行通信(一条数据输入线,一条数据输出线)。\n\n- 1-wire:即单线总线,又叫单总线(只有一条线)。\n\n- I2C:同步串行2线方式进行通信(一条时钟线,一条数据线)。\n\n- SPI:同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。\n\n\n\n## 原理\n\nIIC串行总线,只有两根双向 信号线。一根是数据线SDA,另一根是时钟线SCL\n\n如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据 \n\n![image-20210822161125324](https://gitee.com/ajream/images/raw/master/img/20210822161128_image-20210822161125324.png)\n\n在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。\n\n在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况\n\n\n\n总线“线与关系”:I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,\n\n即`SDA = SDA1 & SDA2 & SDA3 & ...`, `SCL=SCL1 & SCL2 & SCL3 & ...`\n\n![image-20210822161725608](https://gitee.com/ajream/images/raw/master/img/20210822161726_image-20210822161725608.png)\n\n\n\n\n\n### 起始信号和终止信号\n\nSCL线为高电平期间 ,SDA线由高电平向低电平 的变化表示起始信号;\n\nSCL线为高电平期间 ,SDA线由低电平向高电平 的变化表示终止信号。\n\n![image-20210822162305371](https://gitee.com/ajream/images/raw/master/img/20210822162306_image-20210822162305371.png)\n\n### 数据位有效性\n\nSCL为高电平期间,数据线上的数据必须保持稳定\n\n只有SCL信号为低电平期间,SDA状态才允许变化。\n\n![image-20210822162522027](https://gitee.com/ajream/images/raw/master/img/20210822162523_image-20210822162522027.png)\n\n### IIC总线的传送与应答\n\n每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节**最后面**都必须跟随一位应答位(即一帧共有9位)\n\n主机通过从机发出的**应答位**来判断从机是否成功接收数据:\n\n- 从机正忙于其他事情,发出应答1,表示没有收到\n- 当从机空闲可以接收该字节数据时,从机会发出应答0\n\n当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。\n\n\n\n### 数据帧格式\n\n在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用\"0\"表示主机发送数据(T),\"1\"表示主机接收数据(R)。\n\n每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。\n\n### 软件模拟IIC总线传送数据\n\n主机可以采用不带IIC总线接口的单片机,如80C51、STC89C52等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。\n\n为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :\n\n![image-20210822163923932](https://gitee.com/ajream/images/raw/master/img/20210822163925_image-20210822163923932.png)\n\n模拟的代码如下:\n\n```c\n//起始信号\nvoid I2cStart(){\n\tSCL = 1;\n\tSDA = 1;\n\tdelay5us(); //SDA在拉低低之前,应该先保持高电平时间>4us,这里用5us\n\tSDA = 0;\n\tdelay5us();\t //SDA保持低电平时间>4us,这里用5us\n}\n\n//终止信号\nvoid I2cStop(){\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();\n\tSDA = 1;\n\tdelay5us();\n}\n\n```\n\n第3、4个图表示先将SCL拉高5us,然后读取并返回SDA的值,读取后将SCL拉低,代码如下\n\n\n```c\n//主机读取从机的应答\nbit ReadACK(){\n\tSCL = 1;\n\tdelay5us();\n\tif(SDA){\n\t\tSCL = 0;\n\t\treturn(1);\n\t}\n\telse{\n\t\tSCL = 0;\n\t\treturn(0);\n\t}\n}\n\n\n```\n\n\n\n发送数据i,在SCL为低时,将SDA拉高或拉低(根据发送的数据确定,如果发送1,则拉高,反之拉低);然后将SCL拉高,保持5us,再将SCL拉低,这样就发送了一位(bit)数据,最后释放总线\n\n```c\n//主机发送应答\nvoid SendACK(bit i){\n\tSCL = 0;\n \n\tif(i) SDA = 1;\n\telse SDA = 0;\n \n\tSCL = 1;\n\tdelay5us();\n\tSCL = 0; \t //拉低时钟总线\n\tSDA = 1;\t //释放数据总线\n}\n```\n\n\n\n## 串行EEPROM的扩展\n\nAteml公司的AT24C系列:\n\n- AT24C01:128字节(128×8位)\n\n- AT24C02:256字节(256×8位)\n- AT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位)\n- AT24C16:2K字节(2K×8位)\n\n\n\n### 写入过程\n\n**获取地址码:**AT24C系列E2PROM芯片地址的固定部分为1010,而当A2、A1、A0引脚接高、低电平后就得到确定的3位编码。形成的7位编码即为该器件的地址码。例如在开发板中A2, A1, A0都接地,则这7位码为 `1010000`\n\n![image-20210824140820582](https://gitee.com/ajream/images/raw/master/img/20210824140829_image-20210824140820582.png)\n\n**写操作:**单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。\n\n\n\n**传送数据:**\n\n- 这个过程单片机首先发送一个字节被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。\n\n- AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1(表示相对之前写入的位置往后移一位)\n\n- 当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式如下:\n\n ![image-20210822171711924](https://gitee.com/ajream/images/raw/master/img/20210822171714_image-20210822171711924.png)\n \n > 说明:S为起始信号,然后是7位地址码+ `0`(0表示写操作),然后写入第一个数据存放位置的首地址,后面的写入的数据自动往后移动一位,写入1Byte数据后,器件发送应答A确认已写入,最后如果写入完成后发送终止信号P。\n \n- 注意:在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。\n\n\n\n### 读出过程\n\n单片机先发送该器件的7位地址码和写方向位“0”(“伪写” ),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。 \n\n\n\n然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。\n\n当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。 \n\n![image-20210822171925336](https://gitee.com/ajream/images/raw/master/img/20210822171926_image-20210822171925336.png)\n\n\n\n完整代码:\n\n```c\n/*********************************************************************************\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tIIC通信,AT24C02读写数据,数码管显示数据。\t\t \t\t\t\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n#define At24c02ADDR 0XA0 //AT24C02硬件地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1;//I2C时钟总线\nsbit SDA = P2^0;//I2C数据总线\nuchar num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n/*====================================\n函数\t: delay(uint z)\n参数\t:z 延时毫秒设定,取值范围0-65535\n返回值\t:无\n描述\t:12T/Fosc11.0592M毫秒级延时\n====================================*/\nvoid delay(uint z){\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示数值,取值范围0-255\n返回值\t:无\n描述\t:三位共阴数码管动态显示\n====================================*/\nvoid display(uchar i){\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei){\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid timer0Init(){\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:delay5us()\n参数\t:无\n返回值\t:无\n描述\t:5us延时函数\n====================================*/\nvoid delay5us(){\n\t_nop_();\n}\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart(){\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop(){\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK(){\n\tSCL = 0;\t\t\t//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;\t\t\t//拉高,读SDA\n\tdelay5us();\n\tif(SDA){\t\t\t//NOACK\n\t\tSCL = 0;\n\t\treturn(1);\t\t//返回1\n\t}\n\telse{\t\t\t\t//ACK\n\t\tSCL = 0;\n\t\treturn(0);\t\t//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i){\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT){\n\tuchar i; \n\tfor(i=0; i<8; i++){ //分别写8次,每次写1位\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:At24c02Write(uchar ADDR, DAT)\n参数\t:ADDR 单元地址0-255,DAT 需要输入的数据0-255\n返回值\t:无\n描述\t:At24c02指定单元写入一个字节数据\n====================================*/\nvoid At24c02Write(uchar ADDR, DAT){\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK()) //读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(ADDR);//发送储存单元地址字节\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(DAT);//发送一字节数据\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cStop();\t//I2C停止信号\n}\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte(){\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++){ \t//分别读8次,每次读一位\n\t\n\t\tDAT <<= 1; \t\t\t//数据左移1位,准备接收一位\n\t\tSCL = 0; \t\t\t//拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; \t\t\t//拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;\t//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); \t\t\t//返回读出的数据\n}\n\n/*====================================\n函数\t:At24c02Read(uchar ADDR)\n参数\t:ADDR 单元地址\t0-255\n返回值\t:返回指定单元的数据\n描述\t:读AT24C02指定单元内数据\n====================================*/\nuchar At24c02Read(uchar ADDR){\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(ADDR);//I2C发送一个字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环{\t\n\ttimer0Init();//定时器0初始化\n\tEA = 0;//屏蔽中断\n\tAt24c02Write(3, 188);//给第3单元写入数据“188”\n\tdelay(1);//延时等待AT24C02处理\n\tnum = At24c02Read(3);//读出第3单元内数据送给显示变量\n\tif(AckFlag)//当从机非应答\n\t\tP1 = 0;//亮P1所有灯\n\telse\n\t\tP1 = 0XFF;//灭P1所有灯\n\tEA = 1;//开中断\n\twhile(1);\n} \n\n//定时器0中断函数\nvoid timer0() interrupt 1 {\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n} \n```\n\n","slug":"51单片机/51单片机学习笔记(四)","published":1,"updated":"2021-09-10T12:45:36.194Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6og000vakve4p1e67aw","content":"目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-wire、I2C和SPI总线
\n\nUART:是以异步方式进行通信(一条数据输入线,一条数据输出线)。
\n \n1-wire:即单线总线,又叫单总线(只有一条线)。
\n \nI2C:同步串行2线方式进行通信(一条时钟线,一条数据线)。
\n \nSPI:同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。
\n \n \n原理 IIC串行总线,只有两根双向 信号线。一根是数据线SDA,另一根是时钟线SCL
\n如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据
\n
\n在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。
\n在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况
\n总线“线与关系”:I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,
\n即SDA = SDA1 & SDA2 & SDA3 & ...
, SCL=SCL1 & SCL2 & SCL3 & ...
\n
\n起始信号和终止信号 SCL线为高电平期间 ,SDA线由高电平向低电平 的变化表示起始信号;
\nSCL线为高电平期间 ,SDA线由低电平向高电平 的变化表示终止信号。
\n
\n数据位有效性 SCL为高电平期间,数据线上的数据必须保持稳定
\n只有SCL信号为低电平期间,SDA状态才允许变化。
\n
\nIIC总线的传送与应答 每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节最后面 都必须跟随一位应答位(即一帧共有9位)
\n主机通过从机发出的应答位 来判断从机是否成功接收数据:
\n\n从机正忙于其他事情,发出应答1,表示没有收到 \n当从机空闲可以接收该字节数据时,从机会发出应答0 \n \n当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。
\n数据帧格式 在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用”0”表示主机发送数据(T),”1”表示主机接收数据(R)。
\n每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
\n软件模拟IIC总线传送数据 主机可以采用不带IIC总线接口的单片机,如80C51、STC89C52等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。
\n为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :
\n
\n模拟的代码如下:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void I2cStart () {\tSCL = 1 ; \tSDA = 1 ; \tdelay5us(); \tSDA = 0 ; \tdelay5us();\t } void I2cStop () {\tSCL = 0 ; \tSDA = 0 ; \tSCL = 1 ; \tdelay5us(); \tSDA = 1 ; \tdelay5us(); }
\n第3、4个图表示先将SCL拉高5us,然后读取并返回SDA的值,读取后将SCL拉低,代码如下
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 bit ReadACK () {\tSCL = 1 ; \tdelay5us(); \tif (SDA){ \t\tSCL = 0 ; \t\treturn (1 ); \t} \telse { \t\tSCL = 0 ; \t\treturn (0 ); \t} }
\n发送数据i,在SCL为低时,将SDA拉高或拉低(根据发送的数据确定,如果发送1,则拉高,反之拉低);然后将SCL拉高,保持5us,再将SCL拉低,这样就发送了一位(bit)数据,最后释放总线
\n1 2 3 4 5 6 7 8 9 10 11 12 void SendACK (bit i) {\tSCL = 0 ; \tif (i) SDA = 1 ; \telse SDA = 0 ; \tSCL = 1 ; \tdelay5us(); \tSCL = 0 ; \t \tSDA = 1 ;\t }
\n串行EEPROM的扩展 Ateml公司的AT24C系列:
\n\nAT24C01:128字节(128×8位)
\n \nAT24C02:256字节(256×8位)
\n \nAT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位) \nAT24C16:2K字节(2K×8位) \n \n写入过程 获取地址码: AT24C系列E2PROM芯片地址的固定部分为1010,而当A2、A1、A0引脚接高、低电平后就得到确定的3位编码。形成的7位编码即为该器件的地址码。例如在开发板中A2, A1, A0都接地,则这7位码为 1010000
\n
\n写操作: 单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。
\n传送数据:
\n\n这个过程单片机首先发送一个字节被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。
\n \nAT24C系列器件片内地址在接收到每一个数据字节地址后自动加1(表示相对之前写入的位置往后移一位)
\n \n当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式如下:
\n
\n\n说明:S为起始信号,然后是7位地址码+ 0
(0表示写操作),然后写入第一个数据存放位置的首地址,后面的写入的数据自动往后移动一位,写入1Byte数据后,器件发送应答A确认已写入,最后如果写入完成后发送终止信号P。
\n \n \n注意:在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。
\n \n \n读出过程 单片机先发送该器件的7位地址码和写方向位“0”(“伪写” ),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。
\n然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。
\n当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。
\n
\n完整代码:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 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 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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char #define At24c02ADDR 0XA0 #define \tI2cRead 1\t\t #define \tI2cWrite 0\t\t sbit DU = P2^6 ; sbit WE = P2^7 ; sbit SCL = P2^1 ; sbit SDA = P2^0 ; uchar num; bit AckFlag; uchar code SMGduan[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F ,}; uchar code SMGwei[] = {0xfe , 0xfd , 0xfb }; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void display (uchar i) {\tstatic uchar wei; \t\t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = SMGwei[wei]; \tWE = 0 ; \tswitch (wei){ \t\tcase 0 : DU = 1 ; P0 = SMGduan[i / 100 ]; DU = 0 ; break ; \t\tcase 1 : DU = 1 ; P0 = SMGduan[i % 100 / 10 ]; DU = 0 ; break ;\t \t\tcase 2 : DU = 1 ; P0 = SMGduan[i % 10 ]; DU = 0 ; break ;\t\t \t} \twei++; \tif (wei == 3 ) \t\twei = 0 ; } void timer0Init () {\tEA = 1 ;\t \tET0 = 1 ; \tTR0 = 1 ;\t \tTMOD |= 0X01 ; \tTH0 = 0xED ; \tTL0 = 0xFF ; } void delay5us () {\t_nop_(); } void I2cStart () {\tSCL = 1 ; \tSDA = 1 ; \tdelay5us(); \tSDA = 0 ; \tdelay5us(); } void I2cStop () {\tSCL = 0 ; \tSDA = 0 ; \tSCL = 1 ; \tdelay5us(); \tSDA = 1 ; \tdelay5us(); } bit ReadACK () {\tSCL = 0 ;\t\t\t \tSCL = 1 ;\t\t\t \tdelay5us(); \tif (SDA){\t\t\t \t\tSCL = 0 ; \t\treturn (1 );\t\t \t} \telse {\t\t\t\t \t\tSCL = 0 ; \t\treturn (0 );\t\t \t} } void SendACK (bit i) {\tSCL = 0 ; \tif (i)\t \t\tSDA = 1 ; \telse \t \t\tSDA = 0 ; \tSCL = 1 ; \tdelay5us(); \tSCL = 0 ; \tSDA = 1 ; } void I2cSendByte (uchar DAT) {\tuchar i; \tfor (i=0 ; i<8 ; i++){ \t\tSCL = 0 ; \t\tif (DAT & 0x80 ) \t\t\tSDA = 1 ; \t\telse \t\t\tSDA = 0 ; \t\tSCL = 1 ;\t \t\tDAT <<= 1 ;\t \t} \tSCL = 0 ; \tSDA = 1 ; } void At24c02Write (uchar ADDR, DAT) {\tI2cStart(); \tI2cSendByte(At24c02ADDR + I2cWrite); \tif (ReadACK()) \t\tAckFlag = 1 ;\t \telse \t\tAckFlag = 0 ;\t \tI2cSendByte(ADDR); \tif (ReadACK()) \t\tAckFlag = 1 ;\t \telse \t\tAckFlag = 0 ;\t \tI2cSendByte(DAT); \tif (ReadACK()) \t\tAckFlag = 1 ;\t \telse \t\tAckFlag = 0 ;\t \tI2cStop();\t } uchar I2cReadByte () {\tuchar i, DAT; \tfor (i=0 ; i<8 ; i++){ \t \t \t\tDAT <<= 1 ; \t\t\t \t\tSCL = 0 ; \t\t\t \t\tSCL = 1 ; \t\t\t \t\tif (SDA) \t\t\tDAT |= 0X01 ;\t \t} \treturn (DAT); \t\t\t } uchar At24c02Read (uchar ADDR) {\tuchar DAT; \tI2cStart(); \tI2cSendByte(At24c02ADDR + I2cWrite); \tif (ReadACK()) \t\tAckFlag = 1 ;\t \telse \t\tAckFlag = 0 ;\t \tI2cSendByte(ADDR); \tReadACK(); \tI2cStart(); \tI2cSendByte(At24c02ADDR + I2cRead); \tif (ReadACK()) \t\tAckFlag = 1 ;\t \telse \t\tAckFlag = 0 ;\t \tDAT = I2cReadByte(); \tSendACK(1 ); \tI2cStop(); \treturn (DAT); \t\t\t } void main () \ttimer0Init () ;\tEA = 0 ; \tAt24c02Write(3 , 188 ); \tdelay(1 ); \tnum = At24c02Read(3 ); \tif (AckFlag) \t\tP1 = 0 ; \telse \t\tP1 = 0XFF ; \tEA = 1 ; \twhile (1 ); } void timer0 () interrupt 1 {\tTH0 = 0xED ; \tTL0 = 0xFF ; \tdisplay(num); }
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-wire、I2C和SPI总线
\n\nUART:是以异步方式进行通信(一条数据输入线,一条数据输出线)。
\n \n1-wire:即单线总线,又叫单总线(只有一条线)。
\n \nI2C:同步串行2线方式进行通信(一条时钟线,一条数据线)。
\n \nSPI:同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。
\n \n \n原理 IIC串行总线,只有两根双向 信号线。一根是数据线SDA,另一根是时钟线SCL
\n如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据
\n
\n在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。
\n在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况
\n总线“线与关系”:I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,
\n即SDA = SDA1 & SDA2 & SDA3 & ...
, SCL=SCL1 & SCL2 & SCL3 & ...
\n
\n起始信号和终止信号 SCL线为高电平期间 ,SDA线由高电平向低电平 的变化表示起始信号;
\nSCL线为高电平期间 ,SDA线由低电平向高电平 的变化表示终止信号。
\n
\n数据位有效性 SCL为高电平期间,数据线上的数据必须保持稳定
\n只有SCL信号为低电平期间,SDA状态才允许变化。
\n
\nIIC总线的传送与应答 每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节最后面 都必须跟随一位应答位(即一帧共有9位)
\n主机通过从机发出的应答位 来判断从机是否成功接收数据:
\n\n从机正忙于其他事情,发出应答1,表示没有收到 \n当从机空闲可以接收该字节数据时,从机会发出应答0 \n \n当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。
\n数据帧格式 在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用”0”表示主机发送数据(T),”1”表示主机接收数据(R)。
\n每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
\n软件模拟IIC总线传送数据 主机可以采用不带IIC总线接口的单片机,如80C51、STC89C52等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。
\n为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :
\n
\n模拟的代码如下:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void I2cStart () {\tSCL = 1 ; \tSDA = 1 ; \tdelay5us(); \tSDA = 0 ; \tdelay5us();\t } void I2cStop () {\tSCL = 0 ; \tSDA = 0 ; \tSCL = 1 ; \tdelay5us(); \tSDA = 1 ; \tdelay5us(); }
\n第3、4个图表示先将SCL拉高5us,然后读取并返回SDA的值,读取后将SCL拉低,代码如下
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 bit ReadACK () {\tSCL = 1 ; \tdelay5us(); \tif (SDA){ \t\tSCL = 0 ; \t\treturn (1 ); \t} \telse { \t\tSCL = 0 ; \t\treturn (0 ); \t} }
\n发送数据i,在SCL为低时,将SDA拉高或拉低(根据发送的数据确定,如果发送1,则拉高,反之拉低);然后将SCL拉高,保持5us,再将SCL拉低,这样就发送了一位(bit)数据,最后释放总线
\n1 2 3 4 5 6 7 8 9 10 11 12 void SendACK (bit i) {\tSCL = 0 ; \tif (i) SDA = 1 ; \telse SDA = 0 ; \tSCL = 1 ; \tdelay5us(); \tSCL = 0 ; \t \tSDA = 1 ;\t }
\n串行EEPROM的扩展 Ateml公司的AT24C系列:
\n\nAT24C01:128字节(128×8位)
\n \nAT24C02:256字节(256×8位)
\n \nAT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位) \nAT24C16:2K字节(2K×8位) \n \n写入过程 获取地址码: AT24C系列E2PROM芯片地址的固定部分为1010,而当A2、A1、A0引脚接高、低电平后就得到确定的3位编码。形成的7位编码即为该器件的地址码。例如在开发板中A2, A1, A0都接地,则这7位码为 1010000
\n
\n写操作: 单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。
\n传送数据:
\n\n这个过程单片机首先发送一个字节被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。
\n \nAT24C系列器件片内地址在接收到每一个数据字节地址后自动加1(表示相对之前写入的位置往后移一位)
\n \n当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式如下:
\n
\n\n说明:S为起始信号,然后是7位地址码+ 0
(0表示写操作),然后写入第一个数据存放位置的首地址,后面的写入的数据自动往后移动一位,写入1Byte数据后,器件发送应答A确认已写入,最后如果写入完成后发送终止信号P。
\n \n \n注意:在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。
\n \n \n读出过程 单片机先发送该器件的7位地址码和写方向位“0”(“伪写” ),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。
\n然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。
\n当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。
\n
\n完整代码:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 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 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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char #define At24c02ADDR 0XA0 #define \tI2cRead 1\t\t #define \tI2cWrite 0\t\t sbit DU = P2^6 ; sbit WE = P2^7 ; sbit SCL = P2^1 ; sbit SDA = P2^0 ; uchar num; bit AckFlag; uchar code SMGduan[]= {0x3F , 0x06 , 0x5B , 0x4F , 0x66 , 0x6D , 0x7D , 0x07 , 0x7F , 0x6F ,}; uchar code SMGwei[] = {0xfe , 0xfd , 0xfb }; void delay (uint z) {\tuint x,y; \tfor (x = z; x > 0 ; x--) \t\tfor (y = 114 ; y > 0 ; y--); \t\t } void display (uchar i) {\tstatic uchar wei; \t\t \tP0 = 0XFF ; \tWE = 1 ; \tP0 = SMGwei[wei]; \tWE = 0 ; \tswitch (wei){ \t\tcase 0 : DU = 1 ; P0 = SMGduan[i / 100 ]; DU = 0 ; break ; \t\tcase 1 : DU = 1 ; P0 = SMGduan[i % 100 / 10 ]; DU = 0 ; break ;\t \t\tcase 2 : DU = 1 ; P0 = SMGduan[i % 10 ]; DU = 0 ; break ;\t\t \t} \twei++; \tif (wei == 3 ) \t\twei = 0 ; } void timer0Init () {\tEA = 1 ;\t \tET0 = 1 ; \tTR0 = 1 ;\t \tTMOD |= 0X01 ; \tTH0 = 0xED ; \tTL0 = 0xFF ; } void delay5us () {\t_nop_(); } void I2cStart () {\tSCL = 1 ; \tSDA = 1 ; \tdelay5us(); \tSDA = 0 ; \tdelay5us(); } void I2cStop () {\tSCL = 0 ; \tSDA = 0 ; \tSCL = 1 ; \tdelay5us(); \tSDA = 1 ; \tdelay5us(); } bit ReadACK () {\tSCL = 0 ;\t\t\t \tSCL = 1 ;\t\t\t \tdelay5us(); \tif (SDA){\t\t\t \t\tSCL = 0 ; \t\treturn (1 );\t\t \t} \telse {\t\t\t\t \t\tSCL = 0 ; \t\treturn (0 );\t\t \t} } void SendACK (bit i) {\tSCL = 0 ; \tif (i)\t \t\tSDA = 1 ; \telse \t \t\tSDA = 0 ; \tSCL = 1 ; \tdelay5us(); \tSCL = 0 ; \tSDA = 1 ; } void I2cSendByte (uchar DAT) {\tuchar i; \tfor (i=0 ; i<8 ; i++){ \t\tSCL = 0 ; \t\tif (DAT & 0x80 ) \t\t\tSDA = 1 ; \t\telse \t\t\tSDA = 0 ; \t\tSCL = 1 ;\t \t\tDAT <<= 1 ;\t \t} \tSCL = 0 ; \tSDA = 1 ; } void At24c02Write (uchar ADDR, DAT) {\tI2cStart(); \tI2cSendByte(At24c02ADDR + I2cWrite); \tif (ReadACK()) \t\tAckFlag = 1 ;\t \telse \t\tAckFlag = 0 ;\t \tI2cSendByte(ADDR); \tif (ReadACK()) \t\tAckFlag = 1 ;\t \telse \t\tAckFlag = 0 ;\t \tI2cSendByte(DAT); \tif (ReadACK()) \t\tAckFlag = 1 ;\t \telse \t\tAckFlag = 0 ;\t \tI2cStop();\t } uchar I2cReadByte () {\tuchar i, DAT; \tfor (i=0 ; i<8 ; i++){ \t \t \t\tDAT <<= 1 ; \t\t\t \t\tSCL = 0 ; \t\t\t \t\tSCL = 1 ; \t\t\t \t\tif (SDA) \t\t\tDAT |= 0X01 ;\t \t} \treturn (DAT); \t\t\t } uchar At24c02Read (uchar ADDR) {\tuchar DAT; \tI2cStart(); \tI2cSendByte(At24c02ADDR + I2cWrite); \tif (ReadACK()) \t\tAckFlag = 1 ;\t \telse \t\tAckFlag = 0 ;\t \tI2cSendByte(ADDR); \tReadACK(); \tI2cStart(); \tI2cSendByte(At24c02ADDR + I2cRead); \tif (ReadACK()) \t\tAckFlag = 1 ;\t \telse \t\tAckFlag = 0 ;\t \tDAT = I2cReadByte(); \tSendACK(1 ); \tI2cStop(); \treturn (DAT); \t\t\t } void main () \ttimer0Init () ;\tEA = 0 ; \tAt24c02Write(3 , 188 ); \tdelay(1 ); \tnum = At24c02Read(3 ); \tif (AckFlag) \t\tP1 = 0 ; \telse \t\tP1 = 0XFF ; \tEA = 1 ; \twhile (1 ); } void timer0 () interrupt 1 {\tTH0 = 0xED ; \tTL0 = 0xFF ; \tdisplay(num); }
\n"},{"title":"java垃圾回收算法","abbrlink":"bdfc8ea1","date":"2021-05-16T03:16:42.000Z","description":"Java垃圾回收原理解析","_content":"\n\n\n# Java垃圾回收算法\n\n[咱们从头到尾说一次 Java 垃圾回收 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/73628158)\n\n## 什么是垃圾(定义)\n\n有两种算法来定义\n\n### 引用计数算法\n\n引用计数算法是通过在对象头中分配一个空间来存储该对象被引用的次数,如果该对象被其他对象引用,则它的引用计数+1,删除一个该对象的引用,则其引用计数-1,当其引用计数为0时,该对象就是垃圾,会被回收。\n\n> **缺点:**循环引用(其计数永远不会为0)\n\n### 可达性分析算法\n\n可达性分析算法(Reachability Analysis):基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。\n\n \n\n\n\n> 可达性算法,解决了引用计数所无法解决的问题-“循环依赖”\n\n## 怎么回收(算法)\n\n### 标记-清除算法\n\n \n\n标记清除算法原理就是,把要回收的内存进行标记,再清除掉这些内存中的无用数据;\n\n缺点:会导致大量碎片化的内存出现,但开辟内存时需要的是整块内存\n\n\n\n### 复制算法\n\n**回收前:**\n\n \n\n**回收后:**\n\n \n\n\n\n原理:把整块内存平均分为两部分,分配内存时,只使用其中一部分。而这一部分内存满即将满时,jvm进行垃圾回收:\n\n1. 首先把存活的对象都复制到另一块空内存上\n2. 再把剩下的可回收的内存一次性进行垃圾回收,得到一块(占总的1/2)内存区域。\n\n优点:解决了标记-清除算法的问题\n\n缺点:同一时间只能使用其中一半的内存\n\n### 标记-整理算法\n\n \n\n标记整理算法(Mark-Compact)标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。\n\n优点:标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。\n\n缺点:标记-整理算法对内存变动频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。\n\n\n\n### 分代收集算法\n\n分代收集算法融合了上述3种基础的算法思想,而产生的针对不同情况所采用不同算法的一套组合拳。\n\n对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。\n\n1. 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法 ,只需要付出少量存活对象的复制成本就可以完成收集。\n2. 老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理 或者标记-整理算法 来进行回收。\n\n\n\n## 内存模型与回收策略\n\n内存模型:\n\n \n\n\n\n可以看到,内存模型分为 :\n\n- Eden\n\n- Survior\n\n- Old\n\n 这3个区,而Survior又分为 From、to两个区\n\n### Eden区\n\nIBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC **更频繁,回收速度也更快**。\n\n> 通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。\n\n### Survivor区\n\nSurvivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。\n\n1、为啥需要Survivor区?\n\n不就是新生代到老年代么,直接 Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。\n\n> 所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。\n\n2、为啥需要俩?\n\n设置两个 Survivor 区最大的好处就是解决内存碎片化。\n\n我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们**只能标记清除**,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。\n\n这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。\n\n### Old 区\n\n老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 --- 整理算法。\n\n除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。\n\n1、大对象\n\n大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。\n\n2、长期存活对象\n\n虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中没经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。\n\n3、动态对象年龄\n\n虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一般,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。\n\n这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。\n\n\n\n","source":"_posts/Java面试/Java垃圾回收算法.md","raw":"---\ntitle: java垃圾回收算法\ntags:\n - Java面试\ncategories:\n - java\n - java面试\nabbrlink: bdfc8ea1\ndate: 2021-05-16 11:16:42\ndescription: Java垃圾回收原理解析\n---\n\n\n\n# Java垃圾回收算法\n\n[咱们从头到尾说一次 Java 垃圾回收 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/73628158)\n\n## 什么是垃圾(定义)\n\n有两种算法来定义\n\n### 引用计数算法\n\n引用计数算法是通过在对象头中分配一个空间来存储该对象被引用的次数,如果该对象被其他对象引用,则它的引用计数+1,删除一个该对象的引用,则其引用计数-1,当其引用计数为0时,该对象就是垃圾,会被回收。\n\n> **缺点:**循环引用(其计数永远不会为0)\n\n### 可达性分析算法\n\n可达性分析算法(Reachability Analysis):基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。\n\n \n\n\n\n> 可达性算法,解决了引用计数所无法解决的问题-“循环依赖”\n\n## 怎么回收(算法)\n\n### 标记-清除算法\n\n \n\n标记清除算法原理就是,把要回收的内存进行标记,再清除掉这些内存中的无用数据;\n\n缺点:会导致大量碎片化的内存出现,但开辟内存时需要的是整块内存\n\n\n\n### 复制算法\n\n**回收前:**\n\n \n\n**回收后:**\n\n \n\n\n\n原理:把整块内存平均分为两部分,分配内存时,只使用其中一部分。而这一部分内存满即将满时,jvm进行垃圾回收:\n\n1. 首先把存活的对象都复制到另一块空内存上\n2. 再把剩下的可回收的内存一次性进行垃圾回收,得到一块(占总的1/2)内存区域。\n\n优点:解决了标记-清除算法的问题\n\n缺点:同一时间只能使用其中一半的内存\n\n### 标记-整理算法\n\n \n\n标记整理算法(Mark-Compact)标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。\n\n优点:标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。\n\n缺点:标记-整理算法对内存变动频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。\n\n\n\n### 分代收集算法\n\n分代收集算法融合了上述3种基础的算法思想,而产生的针对不同情况所采用不同算法的一套组合拳。\n\n对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。\n\n1. 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法 ,只需要付出少量存活对象的复制成本就可以完成收集。\n2. 老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理 或者标记-整理算法 来进行回收。\n\n\n\n## 内存模型与回收策略\n\n内存模型:\n\n \n\n\n\n可以看到,内存模型分为 :\n\n- Eden\n\n- Survior\n\n- Old\n\n 这3个区,而Survior又分为 From、to两个区\n\n### Eden区\n\nIBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC **更频繁,回收速度也更快**。\n\n> 通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。\n\n### Survivor区\n\nSurvivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。\n\n1、为啥需要Survivor区?\n\n不就是新生代到老年代么,直接 Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。\n\n> 所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。\n\n2、为啥需要俩?\n\n设置两个 Survivor 区最大的好处就是解决内存碎片化。\n\n我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们**只能标记清除**,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。\n\n这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。\n\n### Old 区\n\n老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 --- 整理算法。\n\n除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。\n\n1、大对象\n\n大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。\n\n2、长期存活对象\n\n虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中没经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。\n\n3、动态对象年龄\n\n虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一般,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。\n\n这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。\n\n\n\n","slug":"Java面试/Java垃圾回收算法","published":1,"updated":"2021-09-11T08:01:37.796Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6og000xakve4mebdy6p","content":"Java垃圾回收算法 咱们从头到尾说一次 Java 垃圾回收 - 知乎 (zhihu.com)
\n什么是垃圾(定义) 有两种算法来定义
\n引用计数算法 引用计数算法是通过在对象头中分配一个空间来存储该对象被引用的次数,如果该对象被其他对象引用,则它的引用计数+1,删除一个该对象的引用,则其引用计数-1,当其引用计数为0时,该对象就是垃圾,会被回收。
\n\n缺点: 循环引用(其计数永远不会为0)
\n \n可达性分析算法 可达性分析算法(Reachability Analysis):基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。
\n
\n\n可达性算法,解决了引用计数所无法解决的问题-“循环依赖”
\n \n怎么回收(算法) 标记-清除算法
\n标记清除算法原理就是,把要回收的内存进行标记,再清除掉这些内存中的无用数据;
\n缺点:会导致大量碎片化的内存出现,但开辟内存时需要的是整块内存
\n复制算法 回收前:
\n
\n回收后:
\n
\n原理:把整块内存平均分为两部分,分配内存时,只使用其中一部分。而这一部分内存满即将满时,jvm进行垃圾回收:
\n\n首先把存活的对象都复制到另一块空内存上 \n再把剩下的可回收的内存一次性进行垃圾回收,得到一块(占总的1/2)内存区域。 \n \n优点:解决了标记-清除算法的问题
\n缺点:同一时间只能使用其中一半的内存
\n标记-整理算法
\n标记整理算法(Mark-Compact)标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。
\n优点:标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。
\n缺点:标记-整理算法对内存变动频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。
\n分代收集算法 分代收集算法融合了上述3种基础的算法思想,而产生的针对不同情况所采用不同算法的一套组合拳。
\n对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
\n\n在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法 ,只需要付出少量存活对象的复制成本就可以完成收集。 \n老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理 或者标记-整理算法 来进行回收。 \n \n内存模型与回收策略 内存模型:
\n
\n可以看到,内存模型分为 :
\n\nEden区 IBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC 更频繁,回收速度也更快 。
\n\n通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。
\n \nSurvivor区 Survivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。
\n1、为啥需要Survivor区?
\n不就是新生代到老年代么,直接 Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。
\n\n所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。
\n \n2、为啥需要俩?
\n设置两个 Survivor 区最大的好处就是解决内存碎片化。
\n我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们只能标记清除 ,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。
\n这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。
\nOld 区 老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 —- 整理算法。
\n除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。
\n1、大对象
\n大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。
\n2、长期存活对象
\n虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中没经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。
\n3、动态对象年龄
\n虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一般,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。
\n这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"Java垃圾回收算法 咱们从头到尾说一次 Java 垃圾回收 - 知乎 (zhihu.com)
\n什么是垃圾(定义) 有两种算法来定义
\n引用计数算法 引用计数算法是通过在对象头中分配一个空间来存储该对象被引用的次数,如果该对象被其他对象引用,则它的引用计数+1,删除一个该对象的引用,则其引用计数-1,当其引用计数为0时,该对象就是垃圾,会被回收。
\n\n缺点: 循环引用(其计数永远不会为0)
\n \n可达性分析算法 可达性分析算法(Reachability Analysis):基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。
\n
\n\n可达性算法,解决了引用计数所无法解决的问题-“循环依赖”
\n \n怎么回收(算法) 标记-清除算法
\n标记清除算法原理就是,把要回收的内存进行标记,再清除掉这些内存中的无用数据;
\n缺点:会导致大量碎片化的内存出现,但开辟内存时需要的是整块内存
\n复制算法 回收前:
\n
\n回收后:
\n
\n原理:把整块内存平均分为两部分,分配内存时,只使用其中一部分。而这一部分内存满即将满时,jvm进行垃圾回收:
\n\n首先把存活的对象都复制到另一块空内存上 \n再把剩下的可回收的内存一次性进行垃圾回收,得到一块(占总的1/2)内存区域。 \n \n优点:解决了标记-清除算法的问题
\n缺点:同一时间只能使用其中一半的内存
\n标记-整理算法
\n标记整理算法(Mark-Compact)标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。
\n优点:标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。
\n缺点:标记-整理算法对内存变动频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。
\n分代收集算法 分代收集算法融合了上述3种基础的算法思想,而产生的针对不同情况所采用不同算法的一套组合拳。
\n对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
\n\n在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法 ,只需要付出少量存活对象的复制成本就可以完成收集。 \n老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理 或者标记-整理算法 来进行回收。 \n \n内存模型与回收策略 内存模型:
\n
\n可以看到,内存模型分为 :
\n\nEden区 IBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC 更频繁,回收速度也更快 。
\n\n通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。
\n \nSurvivor区 Survivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。
\n1、为啥需要Survivor区?
\n不就是新生代到老年代么,直接 Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。
\n\n所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。
\n \n2、为啥需要俩?
\n设置两个 Survivor 区最大的好处就是解决内存碎片化。
\n我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们只能标记清除 ,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。
\n这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。
\nOld 区 老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 —- 整理算法。
\n除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。
\n1、大对象
\n大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。
\n2、长期存活对象
\n虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中没经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。
\n3、动态对象年龄
\n虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一般,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。
\n这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。
\n"},{"title":"标签外挂","date":"2021-09-08T12:13:46.000Z","abbrlink":"54667693","description":"Hexo博客使用标签外挂","cover":"https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png","_content":"\n## 标签外挂\n\n### note\n{% tabs Tabs %}\n\n```markdown\n{% note simple %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default simple %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary simple %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success simple %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info simple %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning simple %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger simple %}\ndanger 提示块标籤\n{% endnote %}\n\n```\n\n**预览**\n{% note simple %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default simple %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary simple %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success simple %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info simple %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning simple %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger simple %}\ndanger 提示块标籤\n{% endnote %}\n\n\n\n\n```markdown\n{% note modern %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default modern %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary modern %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success modern %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info modern %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning modern %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger modern %}\ndanger 提示块标籤\n{% endnote %}\n\n```\n\n预览\n\n{% note modern %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default modern %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary modern %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success modern %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info modern %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning modern %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger modern %}\ndanger 提示块标籤\n{% endnote %}\n\n\n\n\n\n\n```markdown\n{% note flat %}\n默认 提示块标签\n{% endnote %}\n\n{% note default flat %}\ndefault 提示块标签\n{% endnote %}\n\n{% note primary flat %}\nprimary 提示块标签\n{% endnote %}\n\n{% note success flat %}\nsuccess 提示块标签\n{% endnote %}\n\n{% note info flat %}\ninfo 提示块标签\n{% endnote %}\n\n{% note warning flat %}\nwarning 提示块标签\n{% endnote %}\n\n{% note danger flat %}\ndanger 提示块标签\n{% endnote %}\n\n```\n\n{% note flat %}\n默认 提示块标签\n{% endnote %}\n\n{% note default flat %}\ndefault 提示块标签\n{% endnote %}\n\n{% note primary flat %}\nprimary 提示块标签\n{% endnote %}\n\n{% note success flat %}\nsuccess 提示块标签\n{% endnote %}\n\n{% note info flat %}\ninfo 提示块标签\n{% endnote %}\n\n{% note warning flat %}\nwarning 提示块标签\n{% endnote %}\n\n{% note danger flat %}\ndanger 提示块标签\n{% endnote %}\n\n\n\n{% endtabs %}\n\n\n\n\n\n\n\n\n\n### Tab\n```markdown\n{% tabs Unique name, [index] %}\n\nAny content (support inline tags too).\n\n{% endtabs %}\n\nUnique name : Unique name of tabs block tag without comma.\n Will be used in #id's as prefix for each tab with their index numbers.\n If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes.\n Only for current url of post/page must be unique!\n[index] : Index number of active tab.\n If not specified, first tab (1) will be selected.\n If index is -1, no tab will be selected. It's will be something like spoiler.\n Optional parameter.\n[Tab caption] : Caption of current tab.\n If not caption specified, unique name with tab index suffix will be used as caption of tab.\n If not caption specified, but specified icon, caption will empty.\n Optional parameter.\n[@icon] : FontAwesome icon name (full-name, look like 'fas fa-font')\n Can be specified with or without space; e.g. 'Tab caption @icon' similar to 'Tab caption@icon'.\n Optional parameter.\n\n```\n\n**方案一:默认选择第一个tab**\n\n```markdown\n{% tabs test1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n\n**方案二:默认选择第3个tab**\n\n```markdown\n{% tabs test2, 3 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test2, 3 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n**方案三:没有预设值**\n\n```markdown\n{% tabs test3, -1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test3, -1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n**方案四:自定义每个tab的name和图标icon**\n\n```markdown\n{% tabs test4 %}\n\n**tab名字为第一个Tab**\n\n\n\n**只有图标 没有Tab名字**\n\n\n\n**名字+icon**\n\n{% endtabs %}\n\n```\n\n{% tabs test4 %}\n\n**tab名字为第一个Tab**\n\n\n\n**只有图标 没有Tab名字**\n\n\n\n**名字+icon**\n\n{% endtabs %}\n\n### folding\n\n```\n{% folding 参数(可选), 标题 %}\n\n内容\n\n{% endfolding %}\n\n```\n\n{%tabs t2 -1%}\n\n\n参数位置可以填写颜色和状态,多个参数用空格隔开。\n\n- 颜色\n\nblue, cyan, green, yellow, red\n\n- 状态\n\n状态填写 open 代表默认打开\n\n\n\n\n```markdown\n{% folding 查看图片测试 %}\n\n这是内容\n\n{% endfolding %}\n\n{% folding cyan open, 查看默认打开的折叠框 %}\n\n这是一个默认打开的折叠框。\n\n{% endfolding %}\n\n{% folding green, 查看代码测试 %}\n\n{% endfolding %}\n\n{% folding yellow, 查看列表测试 %}\n\n- haha\n- hehe\n\n{% endfolding %}\n\n{% folding red, 查看嵌套测试 %}\n\n{% folding blue, 查看嵌套测试2 %}\n\n{% folding 查看嵌套测试3 %}\n\nhahaha \n\n{% endfolding %}\n\n{% endfolding %}\n\n{% endfolding %}\n\n```\n\n\n\n{% folding 查看图片测试 %}\n\n这是内容\n\n{% endfolding %}\n\n{% folding cyan open, 查看默认打开的折叠框 %}\n\n这是一个默认打开的折叠框。\n\n{% endfolding %}\n\n{% folding green, 查看代码测试 %}\n\n{% endfolding %}\n\n{% folding yellow, 查看列表测试 %}\n\n- haha\n- hehe\n\n{% endfolding %}\n\n{% folding red, 查看嵌套测试 %}\n\n{% folding blue, 查看嵌套测试2 %}\n\n{% folding 查看嵌套测试3 %}\n\nhahaha \n\n{% endfolding %}\n\n{% endfolding %}\n\n{% endfolding %}\n\n\n{% endtabs %}","source":"_posts/Hexo/标签外挂.md","raw":"---\ntitle: 标签外挂\ndate: '2021-09-08 20:13:46'\nabbrlink: '54667693'\ntags:\n - Hexo\ncategories:\n - Hexo\ndescription: Hexo博客使用标签外挂\ncover: https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png\n---\n\n## 标签外挂\n\n### note\n{% tabs Tabs %}\n\n```markdown\n{% note simple %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default simple %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary simple %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success simple %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info simple %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning simple %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger simple %}\ndanger 提示块标籤\n{% endnote %}\n\n```\n\n**预览**\n{% note simple %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default simple %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary simple %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success simple %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info simple %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning simple %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger simple %}\ndanger 提示块标籤\n{% endnote %}\n\n\n\n\n```markdown\n{% note modern %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default modern %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary modern %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success modern %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info modern %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning modern %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger modern %}\ndanger 提示块标籤\n{% endnote %}\n\n```\n\n预览\n\n{% note modern %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default modern %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary modern %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success modern %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info modern %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning modern %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger modern %}\ndanger 提示块标籤\n{% endnote %}\n\n\n\n\n\n\n```markdown\n{% note flat %}\n默认 提示块标签\n{% endnote %}\n\n{% note default flat %}\ndefault 提示块标签\n{% endnote %}\n\n{% note primary flat %}\nprimary 提示块标签\n{% endnote %}\n\n{% note success flat %}\nsuccess 提示块标签\n{% endnote %}\n\n{% note info flat %}\ninfo 提示块标签\n{% endnote %}\n\n{% note warning flat %}\nwarning 提示块标签\n{% endnote %}\n\n{% note danger flat %}\ndanger 提示块标签\n{% endnote %}\n\n```\n\n{% note flat %}\n默认 提示块标签\n{% endnote %}\n\n{% note default flat %}\ndefault 提示块标签\n{% endnote %}\n\n{% note primary flat %}\nprimary 提示块标签\n{% endnote %}\n\n{% note success flat %}\nsuccess 提示块标签\n{% endnote %}\n\n{% note info flat %}\ninfo 提示块标签\n{% endnote %}\n\n{% note warning flat %}\nwarning 提示块标签\n{% endnote %}\n\n{% note danger flat %}\ndanger 提示块标签\n{% endnote %}\n\n\n\n{% endtabs %}\n\n\n\n\n\n\n\n\n\n### Tab\n```markdown\n{% tabs Unique name, [index] %}\n\nAny content (support inline tags too).\n\n{% endtabs %}\n\nUnique name : Unique name of tabs block tag without comma.\n Will be used in #id's as prefix for each tab with their index numbers.\n If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes.\n Only for current url of post/page must be unique!\n[index] : Index number of active tab.\n If not specified, first tab (1) will be selected.\n If index is -1, no tab will be selected. It's will be something like spoiler.\n Optional parameter.\n[Tab caption] : Caption of current tab.\n If not caption specified, unique name with tab index suffix will be used as caption of tab.\n If not caption specified, but specified icon, caption will empty.\n Optional parameter.\n[@icon] : FontAwesome icon name (full-name, look like 'fas fa-font')\n Can be specified with or without space; e.g. 'Tab caption @icon' similar to 'Tab caption@icon'.\n Optional parameter.\n\n```\n\n**方案一:默认选择第一个tab**\n\n```markdown\n{% tabs test1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n\n**方案二:默认选择第3个tab**\n\n```markdown\n{% tabs test2, 3 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test2, 3 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n**方案三:没有预设值**\n\n```markdown\n{% tabs test3, -1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test3, -1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n**方案四:自定义每个tab的name和图标icon**\n\n```markdown\n{% tabs test4 %}\n\n**tab名字为第一个Tab**\n\n\n\n**只有图标 没有Tab名字**\n\n\n\n**名字+icon**\n\n{% endtabs %}\n\n```\n\n{% tabs test4 %}\n\n**tab名字为第一个Tab**\n\n\n\n**只有图标 没有Tab名字**\n\n\n\n**名字+icon**\n\n{% endtabs %}\n\n### folding\n\n```\n{% folding 参数(可选), 标题 %}\n\n内容\n\n{% endfolding %}\n\n```\n\n{%tabs t2 -1%}\n\n\n参数位置可以填写颜色和状态,多个参数用空格隔开。\n\n- 颜色\n\nblue, cyan, green, yellow, red\n\n- 状态\n\n状态填写 open 代表默认打开\n\n\n\n\n```markdown\n{% folding 查看图片测试 %}\n\n这是内容\n\n{% endfolding %}\n\n{% folding cyan open, 查看默认打开的折叠框 %}\n\n这是一个默认打开的折叠框。\n\n{% endfolding %}\n\n{% folding green, 查看代码测试 %}\n\n{% endfolding %}\n\n{% folding yellow, 查看列表测试 %}\n\n- haha\n- hehe\n\n{% endfolding %}\n\n{% folding red, 查看嵌套测试 %}\n\n{% folding blue, 查看嵌套测试2 %}\n\n{% folding 查看嵌套测试3 %}\n\nhahaha \n\n{% endfolding %}\n\n{% endfolding %}\n\n{% endfolding %}\n\n```\n\n\n\n{% folding 查看图片测试 %}\n\n这是内容\n\n{% endfolding %}\n\n{% folding cyan open, 查看默认打开的折叠框 %}\n\n这是一个默认打开的折叠框。\n\n{% endfolding %}\n\n{% folding green, 查看代码测试 %}\n\n{% endfolding %}\n\n{% folding yellow, 查看列表测试 %}\n\n- haha\n- hehe\n\n{% endfolding %}\n\n{% folding red, 查看嵌套测试 %}\n\n{% folding blue, 查看嵌套测试2 %}\n\n{% folding 查看嵌套测试3 %}\n\nhahaha \n\n{% endfolding %}\n\n{% endfolding %}\n\n{% endfolding %}\n\n\n{% endtabs %}","slug":"Hexo/标签外挂","published":1,"updated":"2021-09-16T15:28:42.688Z","_id":"cktk8o6oi0011akve2ujw438x","comments":1,"layout":"post","photos":[],"link":"","content":"标签外挂 \nnote \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 {% note simple %} 默认 提示块标籤 {% endnote %} {% note default simple %} default 提示块标籤 {% endnote %} {% note primary simple %} primary 提示块标籤 {% endnote %} {% note success simple %} success 提示块标籤 {% endnote %} {% note info simple %} info 提示块标籤 {% endnote %} {% note warning simple %} warning 提示块标籤 {% endnote %} {% note danger simple %} danger 提示块标籤 {% endnote %}
\n
预览
\n
\n
\n
\n
\n
\n
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 {% note modern %} 默认 提示块标籤 {% endnote %} {% note default modern %} default 提示块标籤 {% endnote %} {% note primary modern %} primary 提示块标籤 {% endnote %} {% note success modern %} success 提示块标籤 {% endnote %} {% note info modern %} info 提示块标籤 {% endnote %} {% note warning modern %} warning 提示块标籤 {% endnote %} {% note danger modern %} danger 提示块标籤 {% endnote %}
\n
预览
\n
\n
\n
\n
\n
\n
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 {% note flat %} 默认 提示块标签 {% endnote %} {% note default flat %} default 提示块标签 {% endnote %} {% note primary flat %} primary 提示块标签 {% endnote %} {% note success flat %} success 提示块标签 {% endnote %} {% note info flat %} info 提示块标签 {% endnote %} {% note warning flat %} warning 提示块标签 {% endnote %} {% note danger flat %} danger 提示块标签 {% endnote %}
\n
\n
\n
\n
\n
\n
\n
\nTab \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 {% tabs Unique name, [index] %} <!-- tab [Tab caption] [@icon] --> Any content (support inline tags too). <!-- endtab --> {% endtabs %} Unique name : Unique name of tabs block tag without comma. Will be used in #id's as prefix for each tab with their index numbers. If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes. Only for current url of post/page must be unique! [index] : Index number of active tab. If not specified, first tab (1) will be selected. If index is -1, no tab will be selected. It's will be something like spoiler. Optional parameter. [Tab caption] : Caption of current tab. If not caption specified, unique name with tab index suffix will be used as caption of tab. If not caption specified, but specified icon, caption will empty. Optional parameter. [@icon] : FontAwesome icon name (full-name, look like 'fas fa-font') Can be specified with or without space; e.g. 'Tab caption @icon' similar to 'Tab caption@icon'. Optional parameter.
\n方案一:默认选择第一个tab
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 {% tabs test1 %} <!-- tab --> **This is Tab 1.** <!-- endtab --> <!-- tab --> **This is Tab 2.** <!-- endtab --> <!-- tab --> **This is Tab 3.** <!-- endtab --> {% endtabs %}
\n\n方案二:默认选择第3个tab
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 {% tabs test2, 3 %} <!-- tab --> **This is Tab 1.** <!-- endtab --> <!-- tab --> **This is Tab 2.** <!-- endtab --> <!-- tab --> **This is Tab 3.** <!-- endtab --> {% endtabs %}
\n\n方案三:没有预设值
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 {% tabs test3, -1 %} <!-- tab --> **This is Tab 1.** <!-- endtab --> <!-- tab --> **This is Tab 2.** <!-- endtab --> <!-- tab --> **This is Tab 3.** <!-- endtab --> {% endtabs %}
\n\n方案四:自定义每个tab的name和图标icon
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 {% tabs test4 %} <!-- tab 第一个Tab --> **tab名字为第一个Tab** <!-- endtab --> <!-- tab @fab fa-apple-pay --> **只有图标 没有Tab名字** <!-- endtab --> <!-- tab 炸弹@fas fa-bomb --> **名字+icon** <!-- endtab --> {% endtabs %}
\n\nfolding \n1 2 3 4 5 6 {% folding 参数(可选), 标题 %} 内容 {% endfolding %}
\n参数位置可以填写颜色和状态,多个参数用空格隔开。
\n
\n
blue, cyan, green, yellow, red
\n
\n
状态填写 open 代表默认打开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 {% folding 查看图片测试 %} 这是内容 {% endfolding %} {% folding cyan open, 查看默认打开的折叠框 %} 这是一个默认打开的折叠框。 {% endfolding %} {% folding green, 查看代码测试 %} {% endfolding %} {% folding yellow, 查看列表测试 %} - haha- hehe{% endfolding %} {% folding red, 查看嵌套测试 %} {% folding blue, 查看嵌套测试2 %} {% folding 查看嵌套测试3 %} hahaha <span > <img src ='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/emoji/tieba/%E6%BB%91%E7%A8%BD.png' style ='height:24px' > </span > {% endfolding %} {% endfolding %} {% endfolding %}
查看图片测试 \n \n \n
查看默认打开的折叠框 \n \n \n
查看代码测试 \n \n
</div>\n </details>\n
\n
查看列表测试 \n \n \n
查看嵌套测试 \n \n
查看嵌套测试2 查看嵌套测试3 hahaha
\n
\n ","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"
标签外挂 \n
note \n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 {% note simple %} 默认 提示块标籤 {% endnote %} {% note default simple %} default 提示块标籤 {% endnote %} {% note primary simple %} primary 提示块标籤 {% endnote %} {% note success simple %} success 提示块标籤 {% endnote %} {% note info simple %} info 提示块标籤 {% endnote %} {% note warning simple %} warning 提示块标籤 {% endnote %} {% note danger simple %} danger 提示块标籤 {% endnote %}
\n
预览
\n
\n
\n
\n
\n
\n
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 {% note modern %} 默认 提示块标籤 {% endnote %} {% note default modern %} default 提示块标籤 {% endnote %} {% note primary modern %} primary 提示块标籤 {% endnote %} {% note success modern %} success 提示块标籤 {% endnote %} {% note info modern %} info 提示块标籤 {% endnote %} {% note warning modern %} warning 提示块标籤 {% endnote %} {% note danger modern %} danger 提示块标籤 {% endnote %}
\n
预览
\n
\n
\n
\n
\n
\n
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 {% note flat %} 默认 提示块标签 {% endnote %} {% note default flat %} default 提示块标签 {% endnote %} {% note primary flat %} primary 提示块标签 {% endnote %} {% note success flat %} success 提示块标签 {% endnote %} {% note info flat %} info 提示块标签 {% endnote %} {% note warning flat %} warning 提示块标签 {% endnote %} {% note danger flat %} danger 提示块标签 {% endnote %}
\n
\n
\n
\n
\n
\n
\n
\n
Tab \n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 {% tabs Unique name, [index] %} <!-- tab [Tab caption] [@icon] --> Any content (support inline tags too). <!-- endtab --> {% endtabs %} Unique name : Unique name of tabs block tag without comma. Will be used in #id's as prefix for each tab with their index numbers. If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes. Only for current url of post/page must be unique! [index] : Index number of active tab. If not specified, first tab (1) will be selected. If index is -1, no tab will be selected. It's will be something like spoiler. Optional parameter. [Tab caption] : Caption of current tab. If not caption specified, unique name with tab index suffix will be used as caption of tab. If not caption specified, but specified icon, caption will empty. Optional parameter. [@icon] : FontAwesome icon name (full-name, look like 'fas fa-font') Can be specified with or without space; e.g. 'Tab caption @icon' similar to 'Tab caption@icon'. Optional parameter.
\n
方案一:默认选择第一个tab
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 {% tabs test1 %} <!-- tab --> **This is Tab 1.** <!-- endtab --> <!-- tab --> **This is Tab 2.** <!-- endtab --> <!-- tab --> **This is Tab 3.** <!-- endtab --> {% endtabs %}
\n
\n
方案二:默认选择第3个tab
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 {% tabs test2, 3 %} <!-- tab --> **This is Tab 1.** <!-- endtab --> <!-- tab --> **This is Tab 2.** <!-- endtab --> <!-- tab --> **This is Tab 3.** <!-- endtab --> {% endtabs %}
\n
\n
方案三:没有预设值
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 {% tabs test3, -1 %} <!-- tab --> **This is Tab 1.** <!-- endtab --> <!-- tab --> **This is Tab 2.** <!-- endtab --> <!-- tab --> **This is Tab 3.** <!-- endtab --> {% endtabs %}
\n
\n
方案四:自定义每个tab的name和图标icon
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 {% tabs test4 %} <!-- tab 第一个Tab --> **tab名字为第一个Tab** <!-- endtab --> <!-- tab @fab fa-apple-pay --> **只有图标 没有Tab名字** <!-- endtab --> <!-- tab 炸弹@fas fa-bomb --> **名字+icon** <!-- endtab --> {% endtabs %}
\n
\n
folding \n
1 2 3 4 5 6 {% folding 参数(可选), 标题 %} 内容 {% endfolding %}
\n
参数位置可以填写颜色和状态,多个参数用空格隔开。
\n
\n
blue, cyan, green, yellow, red
\n
\n
状态填写 open 代表默认打开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 {% folding 查看图片测试 %} 这是内容 {% endfolding %} {% folding cyan open, 查看默认打开的折叠框 %} 这是一个默认打开的折叠框。 {% endfolding %} {% folding green, 查看代码测试 %} {% endfolding %} {% folding yellow, 查看列表测试 %} - haha- hehe{% endfolding %} {% folding red, 查看嵌套测试 %} {% folding blue, 查看嵌套测试2 %} {% folding 查看嵌套测试3 %} hahaha <span > <img src ='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/emoji/tieba/%E6%BB%91%E7%A8%BD.png' style ='height:24px' > </span > {% endfolding %} {% endfolding %} {% endfolding %}
查看图片测试 \n \n \n
查看默认打开的折叠框 \n \n \n
查看代码测试 \n \n
</div>\n </details>\n
\n
查看列表测试 \n \n \n
查看嵌套测试 \n \n
查看嵌套测试2 查看嵌套测试3 hahaha
\n
\n "},{"title":"1-maven基本概念","abbrlink":"52e2375","date":"2021-04-17T02:51:47.000Z","description":"maven的基本概念、特性介绍及如何使用","_content":"\n\n# Maven基本概念\n\n## Maven学习内容\n\n
\n\n\n\n## Maven简介\n\nMaven主要用于项目构建、依赖管理、项目信息管理\n\n1. 小型项目\n2. 大型企业项目\n3. 传统瀑布式开发\n4. 流行的敏捷开发\n\n\n\n## 项目构建\n\n\t\t总结一下, 我们会发现, 除了编写源代码, 我们每天有相当一部分时间花在了编\n\n译,运行单元测试, 生成文档, 打包和部署等繁琐且不起眼的工作上, 这就是构建。\n\n\n\n## 项目构建工具\n\n### Ant\n\n最早的构建工具, 基于 IDE, 大概是 2000 年有的, 当时是最流行 java 构建工具,不过它的 XML 脚本编写格式让 XML 文件特别大。 对工程构建过程中的过程控制特别好。\n\n### Maven\n\n- 项目对象模型, 通过其描述信息来管理项目的构建, 报告和文档的软件项目管理工具。 \n- 填补了 Ant 缺点, Maven 第一次支持了从网络上下载的功能, 仍然采用 xml 作为配置文件格式。\n- Maven 专注的是依赖管理, 使用 Java 编写。\n\n### Gradle\n\n- 结合以上两个的优点, 它继承了 Ant 的灵活和 Maven 的生命周期管理, 它最后被 google 作为了 Android 御用管理工具。\n- 它最大的区别是不用 XML 作为配置文件格式, 采用了 DSL 格式, 使得脚本更加简洁。\n\n\n\n## Maven四大特性\n\n### 依赖管理系统(jar项目的多模块)\n\n- Maven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理 ,jar 升级时修改配置文件即可。 \n- 在 Java 世界中, 可以用
groupId 、
artifactId 、
version 组成的Coordination( 坐标)唯一标识一个依赖。 \n- 任何基于 Maven 构建的项目自身也必须定义这三项属性, 生成的包可以是 Jar 包, 也可以是 war 包或者 jar 包。\n\n一个典型的依赖引用如下所示:\n\n```xml\n
\n\tjavax.servlet //包名/项目名\n\tjavax.servlet-api //某一模块名\n\t3.1.0 \t//所需要jar的版本\n \n```\n\n### 多模块构建\n\n 项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 标签来定义一组子模块。 parent POM 不会有什么实际构建产出。 而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。\n\n### 一致的项目结构(不同的IDE项目结构是一致)\n\n Ant 时代大家创建 Java 项目目录时比较随意, 然后通过 Ant 配置指定哪些属于source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion overconfiguration( 约定大于配置)。 其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题。\n\n\n\n\n\n### 一致的构建模型和插件机制\n\n通过 pom 配置 tomcat、 jetty 插件:\n\n```html\n
\n\t\n\torg.mortbay.jetty \n\tmaven-jetty-plugin \n\t6.1.25 \n\t\n\t10 \n\t/test \n \n \n\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","source":"_posts/Maven/1-maven基本概念.md","raw":"---\ntitle: 1-maven基本概念\ntags:\n - Maven\ncategories:\n - - java\n - Maven\nabbrlink: '52e2375'\ndate: 2021-04-17 10:51:47\ndescription: maven的基本概念、特性介绍及如何使用\n---\n\n\n# Maven基本概念\n\n## Maven学习内容\n\n
\n\n\n\n## Maven简介\n\nMaven主要用于项目构建、依赖管理、项目信息管理\n\n1. 小型项目\n2. 大型企业项目\n3. 传统瀑布式开发\n4. 流行的敏捷开发\n\n\n\n## 项目构建\n\n\t\t总结一下, 我们会发现, 除了编写源代码, 我们每天有相当一部分时间花在了编\n\n译,运行单元测试, 生成文档, 打包和部署等繁琐且不起眼的工作上, 这就是构建。\n\n\n\n## 项目构建工具\n\n### Ant\n\n最早的构建工具, 基于 IDE, 大概是 2000 年有的, 当时是最流行 java 构建工具,不过它的 XML 脚本编写格式让 XML 文件特别大。 对工程构建过程中的过程控制特别好。\n\n### Maven\n\n- 项目对象模型, 通过其描述信息来管理项目的构建, 报告和文档的软件项目管理工具。 \n- 填补了 Ant 缺点, Maven 第一次支持了从网络上下载的功能, 仍然采用 xml 作为配置文件格式。\n- Maven 专注的是依赖管理, 使用 Java 编写。\n\n### Gradle\n\n- 结合以上两个的优点, 它继承了 Ant 的灵活和 Maven 的生命周期管理, 它最后被 google 作为了 Android 御用管理工具。\n- 它最大的区别是不用 XML 作为配置文件格式, 采用了 DSL 格式, 使得脚本更加简洁。\n\n\n\n## Maven四大特性\n\n### 依赖管理系统(jar项目的多模块)\n\n- Maven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理 ,jar 升级时修改配置文件即可。 \n- 在 Java 世界中, 可以用
groupId 、
artifactId 、
version 组成的Coordination( 坐标)唯一标识一个依赖。 \n- 任何基于 Maven 构建的项目自身也必须定义这三项属性, 生成的包可以是 Jar 包, 也可以是 war 包或者 jar 包。\n\n一个典型的依赖引用如下所示:\n\n```xml\n
\n\tjavax.servlet //包名/项目名\n\tjavax.servlet-api //某一模块名\n\t3.1.0 \t//所需要jar的版本\n \n```\n\n### 多模块构建\n\n 项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 标签来定义一组子模块。 parent POM 不会有什么实际构建产出。 而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。\n\n### 一致的项目结构(不同的IDE项目结构是一致)\n\n Ant 时代大家创建 Java 项目目录时比较随意, 然后通过 Ant 配置指定哪些属于source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion overconfiguration( 约定大于配置)。 其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题。\n\n\n\n\n\n### 一致的构建模型和插件机制\n\n通过 pom 配置 tomcat、 jetty 插件:\n\n```html\n
\n\t\n\torg.mortbay.jetty \n\tmaven-jetty-plugin \n\t6.1.25 \n\t\n\t10 \n\t/test \n \n \n\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","slug":"Maven/1-maven基本概念","published":1,"updated":"2021-09-10T02:59:29.812Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6oj0013akve45f3gq5i","content":"
Maven基本概念 Maven学习内容
\n
Maven简介 Maven主要用于项目构建、依赖管理、项目信息管理
\n
\n小型项目 \n大型企业项目 \n传统瀑布式开发 \n流行的敏捷开发 \n \n
项目构建 总结一下, 我们会发现, 除了编写源代码, 我们每天有相当一部分时间花在了编
\n
译,运行单元测试, 生成文档, 打包和部署等繁琐且不起眼的工作上, 这就是构建。
\n
项目构建工具 Ant 最早的构建工具, 基于 IDE, 大概是 2000 年有的, 当时是最流行 java 构建工具,不过它的 XML 脚本编写格式让 XML 文件特别大。 对工程构建过程中的过程控制特别好。
\n
Maven \n项目对象模型, 通过其描述信息来管理项目的构建, 报告和文档的软件项目管理工具。 \n填补了 Ant 缺点, Maven 第一次支持了从网络上下载的功能, 仍然采用 xml 作为配置文件格式。 \nMaven 专注的是依赖管理, 使用 Java 编写。 \n \n
Gradle \n结合以上两个的优点, 它继承了 Ant 的灵活和 Maven 的生命周期管理, 它最后被 google 作为了 Android 御用管理工具。 \n它最大的区别是不用 XML 作为配置文件格式, 采用了 DSL 格式, 使得脚本更加简洁。 \n \n
Maven四大特性 依赖管理系统(jar项目的多模块) \nMaven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理 ,jar 升级时修改配置文件即可。 \n在 Java 世界中, 可以用 groupId 、 artifactId 、 version 组成的Coordination( 坐标)唯一标识一个依赖。 \n任何基于 Maven 构建的项目自身也必须定义这三项属性, 生成的包可以是 Jar 包, 也可以是 war 包或者 jar 包。 \n \n
一个典型的依赖引用如下所示:
\n
1 2 3 4 5 <dependency > \t<groupId > javax.servlet</groupId > //包名/项目名 \t<artifactId > javax.servlet-api</artifactId > //某一模块名 \t<version > 3.1.0</version > \t//所需要jar的版本 </dependency >
\n
多模块构建 项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 标签来定义一组子模块。 parent POM 不会有什么实际构建产出。 而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。
\n
一致的项目结构(不同的IDE项目结构是一致) Ant 时代大家创建 Java 项目目录时比较随意, 然后通过 Ant 配置指定哪些属于source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion overconfiguration( 约定大于配置)。 其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题。
\n
一致的构建模型和插件机制 通过 pom 配置 tomcat、 jetty 插件:
\n
1 2 3 4 5 6 7 8 9 10 11 <plugin > \t \t<groupId > org.mortbay.jetty</groupId > \t<artifactId > maven-jetty-plugin</artifactId > \t<version > 6.1.25</version > \t<configuration > \t<scanIntervalSeconds > 10</scanIntervalSeconds > \t<contextPath > /test</contextPath > </configuration > </plugin >
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":"
Maven基本概念 Maven学习内容
\n
Maven简介 Maven主要用于项目构建、依赖管理、项目信息管理
\n
\n小型项目 \n大型企业项目 \n传统瀑布式开发 \n流行的敏捷开发 \n \n
项目构建 总结一下, 我们会发现, 除了编写源代码, 我们每天有相当一部分时间花在了编
\n
译,运行单元测试, 生成文档, 打包和部署等繁琐且不起眼的工作上, 这就是构建。
\n
项目构建工具 Ant 最早的构建工具, 基于 IDE, 大概是 2000 年有的, 当时是最流行 java 构建工具,不过它的 XML 脚本编写格式让 XML 文件特别大。 对工程构建过程中的过程控制特别好。
\n
Maven \n项目对象模型, 通过其描述信息来管理项目的构建, 报告和文档的软件项目管理工具。 \n填补了 Ant 缺点, Maven 第一次支持了从网络上下载的功能, 仍然采用 xml 作为配置文件格式。 \nMaven 专注的是依赖管理, 使用 Java 编写。 \n \n
Gradle \n结合以上两个的优点, 它继承了 Ant 的灵活和 Maven 的生命周期管理, 它最后被 google 作为了 Android 御用管理工具。 \n它最大的区别是不用 XML 作为配置文件格式, 采用了 DSL 格式, 使得脚本更加简洁。 \n \n
Maven四大特性 依赖管理系统(jar项目的多模块) \nMaven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理 ,jar 升级时修改配置文件即可。 \n在 Java 世界中, 可以用 groupId 、 artifactId 、 version 组成的Coordination( 坐标)唯一标识一个依赖。 \n任何基于 Maven 构建的项目自身也必须定义这三项属性, 生成的包可以是 Jar 包, 也可以是 war 包或者 jar 包。 \n \n
一个典型的依赖引用如下所示:
\n
1 2 3 4 5 <dependency > \t<groupId > javax.servlet</groupId > //包名/项目名 \t<artifactId > javax.servlet-api</artifactId > //某一模块名 \t<version > 3.1.0</version > \t//所需要jar的版本 </dependency >
\n
多模块构建 项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 标签来定义一组子模块。 parent POM 不会有什么实际构建产出。 而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。
\n
一致的项目结构(不同的IDE项目结构是一致) Ant 时代大家创建 Java 项目目录时比较随意, 然后通过 Ant 配置指定哪些属于source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion overconfiguration( 约定大于配置)。 其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题。
\n
一致的构建模型和插件机制 通过 pom 配置 tomcat、 jetty 插件:
\n
1 2 3 4 5 6 7 8 9 10 11 <plugin > \t \t<groupId > org.mortbay.jetty</groupId > \t<artifactId > maven-jetty-plugin</artifactId > \t<version > 6.1.25</version > \t<configuration > \t<scanIntervalSeconds > 10</scanIntervalSeconds > \t<contextPath > /test</contextPath > </configuration > </plugin >
\n"},{"title":"3-maven+IDEA创建webapp","abbrlink":"79c0e989","date":"2021-04-17T11:54:41.000Z","description":"如何用idea编辑器、maven工具构建一个webapp","_content":"\n\n\n\n\n### 创建project\n\n![image-20210502143531044](https://gitee.com/ajream/images/raw/master/img/2021-05-0214-35-33_image-20210502143531044.png)\n\n\n\n![image-20210502143822062](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-38-23_image-20210502143822062.png)\n\n\n\n![image-20210502144127624](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-41-29_image-20210502144127624.png)\n\n接着等待下载一些文件\n\n进入工程下文件 `pom.xml`,做以下配置:\n\n修改Java编译器版本为`1.8`(根据自己情况)\n\n```xml\n
\n UTF-8 \n 1.8 \n 1.8 \n \n```\n\n\n\n删除下面两个标签及标签中的内容\n\n```xml\n
\n ...\n \n```\n\n如果使用tomcat服务器,在刚才删除的地方添加下面内容\n\n```xml\n
\n \n org.apache.tomcat.maven \n tomcat7-maven-plugin \n 2.2 \n \n 8088 \n /test \n UTF-8 \n tomcat7 \n \n \n \n```\n\n\n\n然后:\n\n![image-20210502145452277](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-54-53_image-20210502145452277.png)\n\n\n\n等待下载完成后\n\n![image-20210502145735190](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-57-36_image-20210502145735190.png)\n\n![image-20210502145859423](https://gitee.com/ajream/images/raw/master/img/2021-05-0214-59-00_image-20210502145859423.png)\n\n![image-20210502150218246](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-02-19_image-20210502150218246.png)\n\n\n\n\n\n配置完成,点击运行\n\n![image-20210502150451149](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-04-52_image-20210502150451149.png)\n\n![image-20210502150620073](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-06-21_image-20210502150620073.png)\n\n浏览器访问结果:\n\n![image-20210502150700084](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-07-01_image-20210502150700084.png)\n\n\n\n\n\n\n\n\n> 下载的插件在repository的org目录下\n>\n> ```\n> D:\\maven\\apache-maven-3.8.1\\repository\\org\\apache\\tomcat\n> ```","source":"_posts/Maven/3-maven+IDEA创建webapp.md","raw":"---\ntitle: 3-maven+IDEA创建webapp\ntags:\n - Maven\ncategories:\n - - java\n - Maven\nabbrlink: 79c0e989\ndate: 2021-04-17 19:54:41\ndescription: 如何用idea编辑器、maven工具构建一个webapp\n---\n\n\n\n\n\n### 创建project\n\n![image-20210502143531044](https://gitee.com/ajream/images/raw/master/img/2021-05-0214-35-33_image-20210502143531044.png)\n\n\n\n![image-20210502143822062](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-38-23_image-20210502143822062.png)\n\n\n\n![image-20210502144127624](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-41-29_image-20210502144127624.png)\n\n接着等待下载一些文件\n\n进入工程下文件 `pom.xml`,做以下配置:\n\n修改Java编译器版本为`1.8`(根据自己情况)\n\n```xml\n
\n UTF-8 \n 1.8 \n 1.8 \n \n```\n\n\n\n删除下面两个标签及标签中的内容\n\n```xml\n
\n ...\n \n```\n\n如果使用tomcat服务器,在刚才删除的地方添加下面内容\n\n```xml\n
\n \n org.apache.tomcat.maven \n tomcat7-maven-plugin \n 2.2 \n \n 8088 \n /test \n UTF-8 \n tomcat7 \n \n \n \n```\n\n\n\n然后:\n\n![image-20210502145452277](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-54-53_image-20210502145452277.png)\n\n\n\n等待下载完成后\n\n![image-20210502145735190](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-57-36_image-20210502145735190.png)\n\n![image-20210502145859423](https://gitee.com/ajream/images/raw/master/img/2021-05-0214-59-00_image-20210502145859423.png)\n\n![image-20210502150218246](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-02-19_image-20210502150218246.png)\n\n\n\n\n\n配置完成,点击运行\n\n![image-20210502150451149](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-04-52_image-20210502150451149.png)\n\n![image-20210502150620073](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-06-21_image-20210502150620073.png)\n\n浏览器访问结果:\n\n![image-20210502150700084](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-07-01_image-20210502150700084.png)\n\n\n\n\n\n\n\n\n> 下载的插件在repository的org目录下\n>\n> ```\n> D:\\maven\\apache-maven-3.8.1\\repository\\org\\apache\\tomcat\n> ```","slug":"Maven/3-maven+IDEA创建webapp","published":1,"updated":"2021-09-10T03:00:13.294Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ok0018akve39f5g6yx","content":"
创建project
\n
\n
\n
接着等待下载一些文件
\n
进入工程下文件 pom.xml
,做以下配置:
\n
修改Java编译器版本为1.8
(根据自己情况)
\n
1 2 3 4 5 <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <maven.compiler.source > 1.8</maven.compiler.source > <maven.compiler.target > 1.8</maven.compiler.target > </properties >
\n
删除下面两个标签及标签中的内容
\n
1 2 3 <pluginManagement > ... </pluginManagement >
\n
如果使用tomcat服务器,在刚才删除的地方添加下面内容
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 <plugins > <plugin > <groupId > org.apache.tomcat.maven</groupId > <artifactId > tomcat7-maven-plugin</artifactId > <version > 2.2</version > <configuration > <port > 8088</port > <path > /test</path > <uriEncoding > UTF-8</uriEncoding > <server > tomcat7</server > </configuration > </plugin > </plugins >
\n
然后:
\n
\n
等待下载完成后
\n
\n
\n
\n
配置完成,点击运行
\n
\n
\n
浏览器访问结果:
\n
\n
\n下载的插件在repository的org目录下
\n1 D:\\maven\\apache-maven-3.8.1\\repository\\org\\apache\\tomcat
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"
创建project
\n
\n
\n
接着等待下载一些文件
\n
进入工程下文件 pom.xml
,做以下配置:
\n
修改Java编译器版本为1.8
(根据自己情况)
\n
1 2 3 4 5 <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <maven.compiler.source > 1.8</maven.compiler.source > <maven.compiler.target > 1.8</maven.compiler.target > </properties >
\n
删除下面两个标签及标签中的内容
\n
1 2 3 <pluginManagement > ... </pluginManagement >
\n
如果使用tomcat服务器,在刚才删除的地方添加下面内容
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 <plugins > <plugin > <groupId > org.apache.tomcat.maven</groupId > <artifactId > tomcat7-maven-plugin</artifactId > <version > 2.2</version > <configuration > <port > 8088</port > <path > /test</path > <uriEncoding > UTF-8</uriEncoding > <server > tomcat7</server > </configuration > </plugin > </plugins >
\n
然后:
\n
\n
等待下载完成后
\n
\n
\n
\n
配置完成,点击运行
\n
\n
\n
浏览器访问结果:
\n
\n
\n下载的插件在repository的org目录下
\n1 D:\\maven\\apache-maven-3.8.1\\repository\\org\\apache\\tomcat
\n"},{"title":"MySql(2)-MySQL基础","abbrlink":"39aef30f","date":"2021-06-05T04:18:30.000Z","description":"MySQL介绍与SQL语言使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n\n\n# MySQL基础\n\n\n\n## mysql语法\n\n注释:\n\n- 单行注释:`--这是注释`\n\n- 多行注释:\n\n ```mysql\n /*\n 这是注释\n 这是注释\n 这是注释\n 这是注释\n */\n ```\n\n\nmysql语句不区分大小写,建议小写\n\n## 数据库基本操作\n\n操作数据库->操作数据库的表->操作数据库的表中的数据\n\n\n\n连接数据库\n\n```shell\nmysql -uroot -p密码 #-p和密码之间没有空格\n```\n\n\n\n查看全部数据库\n\n```mysql\nshow databases;\n```\n\n使用某个数据库(假设有个数据库为:school)\n\n```mysql\nuse school;\n```\n\n> 如果名称是sql中的一些关键字,要加反引号\n>\n> ```mysql\n> use `user`;\n> ```\n\n查看该数据库所有的表\n\n```mysql\nshow tables;\n```\n\n查看某个表信息(假设在该数据库中有个表为:student):\n\n```mysql\ndescribe student;\n或\ndesc student;\n```\n\n创建数据库school; (中括号 `[]` 表示可选)\n\n```mysql\ncreate database [if not exist] school;\n```\n\n删除数据库:\n\n```mysql\ndrop database [if exist] school;\n```\n\n\n\n## 数据库的(列)数据类型\n\n### 数值\n\n1. tinyint: 1个字节(-127~128)\n2. smallint: 2个字节\n3. mediumint: 3个字节\n4. **int:4个字节**\n5. bigint:8个字节\n6. float:4个字节\n7. double:8个字节\n8. decimal:字符串形式的浮点数(金融计算常用)\n\n### 字符串\n\n1. char:1个字节(0~255)\n2. **varchar:可变字符串 0~65535**\n3. tinytext: 微型文本 2^8-1\n4. **text:文本串 2^16-1**\n\n\n\n\n\n### 时间日期\n\n1. date\t\tYYYY-MM-DD, 日期格式\n2. time HH:mm:ss 时间格式\n3. **datetime** YYYY-MM-DD HH:mm:ss (最常用)\n4. **timestamp** 1970.1.1到现在的毫秒数(较常用)\n\n5. year:年份\n\n\n\n### NULL\n\n没有值,未知\n\n注意,不要使用NULL进行运算,结果为NULL\n\n\n\n## 字段属性(重要)\n\n每一列(称为字段)都有多个属性,用于描述该列的特征\n\n- unsigned\n\n 无符号整数,表示该列不能为负数\n\n\n\n- zerofill\n - 0填充的\n - 不足的位数用0填充,如:int(3); --输入数字5,自动转为 `005`\n\n\n\n- 自增 auto_increment\n\n 自动在一条记录的基础上(默认) `+1`\n\n 必须是整数类型\n\n\n\n\n\n- 非空 NOT NULL\n - 假设为 not null: 如果不给他赋值,会报错\n\n\n\n例子:创建一个表\n\n```mysql\nCREATE TABLE\nIF\n\tNOT EXISTS `student` (\n\t`id` INT ( 4 ) NOT NULL AUTO_INCREMENT COMMENT '学号',\n\t`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名',\n\t`password` VARCHAR ( 30 ) NOT NULL DEFAULT '123456' COMMENT '密码',\n\t`sex` VARCHAR ( 2 ) NOT NULL DEFAULT '保密' COMMENT '性别',\n\t`age` INT ( 3 ) NOT NULL DEFAULT 18 COMMENT '年龄',\n\tPRIMARY KEY ( `id` ) -- 主键\n\t\n\t) ENGINE = INNODB DEFAULT CHARSET = utf8;\n```\n\n\n\n创建表格式( `[]`里面的内容表示可有可无 ):\n\n```mysql\nCREATE TABLE [IF NOT EXISTS] `表名`(\n\t`字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n ...\n `字段名` 列类型 [属性] [索引] [注释]\n)[表类型] [字符集设置] [注释]\n```\n\n\n\n在创建完一个数据库或者表后,可以用以下语句:\n\n```mysql\nSHOW CREATE DATABASE school; -- 查看创建数据库的语句\nSHOW CREATE TABLE student; \t-- 查看创建表的语句\nDESC student; -- 显示表的结构\n```\n\n\n\n\n\n## 数据库引擎\n\n1. INNODB(默认使用)\n2. MYISAM\n\n\n\n| | MYISAM | INNODB |\n| ------------ | ------ | ------------------- |\n| 事务支持 | 不支持 | 支持 |\n| 数据行锁定 | 不支持 | 支持 |\n| 外键约束 | 不支持 | 支持 |\n| 全文索引 | 支持 | 不支持 |\n| 表的空间大小 | 较小 | 较大,为MYISAM的2倍 |\n\n事务\n\n- 是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以\n\n\n\n占用空间:\n\n- InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;\n\n- 而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。\n\n\n\n常用操作:\n\n- MYISAM: 节约空间,速度快\n- INNODB:安全性高,事务处理,多表多用户操作\n\n\n\n物理空间存在位置:\n\n所有数据文件都在data目录下,本质还是文件存储\n\n- MYISAM对应文件:\n - *.frm 表结构定义\n - *.MYD 数据文件\n - *.MYI 索引文件\n- INNODB:只有 *.frm 文件,以及上级目录下ibdata1文件\n\n\n\n## 设置字符集编码\n\n```MySQL\nCHARSET=utf8\n```\n\nMySQL默认字符集 Latin1, 不支持中文\n\n可以在 my.ini文件配置,但发到别人电脑时可能会出错,最好在创建表时也写上\n\n\n\n\n\n## 修改、删除表\n\n### 修改表的结构:ALTER TABLE\n\n修改表名\n\n```MYSQL\nALTER TABLE teacher RENAME AS teacher1;\n```\n\n增加表的字段\n\n```mysql\nALTER TABLE teacher1 ADD age INT(11)\n```\n\n修改表的字段(字段名,字段类型)\n\n```mysql\nALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改l\nALTER TABLE teacher1 CHANGE age age1 int(11) -- 字段重命名\n\n```\n\n删除表的字段\n\n```mysql\nALTER TABLE teacher1 DROP age1; \n```\n\n\n\n### 删除表 DROP TABLE\n\n```mysql\ndrop table [if exists] teacher; -- 一般加上判断,避免出错\n```\n\n\n\n\n\n","source":"_posts/MySQL/2-MySQL基础.md","raw":"---\ntitle: MySql(2)-MySQL基础\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: 39aef30f\ndate: 2021-06-05 12:18:30\ndescription: MySQL介绍与SQL语言使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n\n\n# MySQL基础\n\n\n\n## mysql语法\n\n注释:\n\n- 单行注释:`--这是注释`\n\n- 多行注释:\n\n ```mysql\n /*\n 这是注释\n 这是注释\n 这是注释\n 这是注释\n */\n ```\n\n\nmysql语句不区分大小写,建议小写\n\n## 数据库基本操作\n\n操作数据库->操作数据库的表->操作数据库的表中的数据\n\n\n\n连接数据库\n\n```shell\nmysql -uroot -p密码 #-p和密码之间没有空格\n```\n\n\n\n查看全部数据库\n\n```mysql\nshow databases;\n```\n\n使用某个数据库(假设有个数据库为:school)\n\n```mysql\nuse school;\n```\n\n> 如果名称是sql中的一些关键字,要加反引号\n>\n> ```mysql\n> use `user`;\n> ```\n\n查看该数据库所有的表\n\n```mysql\nshow tables;\n```\n\n查看某个表信息(假设在该数据库中有个表为:student):\n\n```mysql\ndescribe student;\n或\ndesc student;\n```\n\n创建数据库school; (中括号 `[]` 表示可选)\n\n```mysql\ncreate database [if not exist] school;\n```\n\n删除数据库:\n\n```mysql\ndrop database [if exist] school;\n```\n\n\n\n## 数据库的(列)数据类型\n\n### 数值\n\n1. tinyint: 1个字节(-127~128)\n2. smallint: 2个字节\n3. mediumint: 3个字节\n4. **int:4个字节**\n5. bigint:8个字节\n6. float:4个字节\n7. double:8个字节\n8. decimal:字符串形式的浮点数(金融计算常用)\n\n### 字符串\n\n1. char:1个字节(0~255)\n2. **varchar:可变字符串 0~65535**\n3. tinytext: 微型文本 2^8-1\n4. **text:文本串 2^16-1**\n\n\n\n\n\n### 时间日期\n\n1. date\t\tYYYY-MM-DD, 日期格式\n2. time HH:mm:ss 时间格式\n3. **datetime** YYYY-MM-DD HH:mm:ss (最常用)\n4. **timestamp** 1970.1.1到现在的毫秒数(较常用)\n\n5. year:年份\n\n\n\n### NULL\n\n没有值,未知\n\n注意,不要使用NULL进行运算,结果为NULL\n\n\n\n## 字段属性(重要)\n\n每一列(称为字段)都有多个属性,用于描述该列的特征\n\n- unsigned\n\n 无符号整数,表示该列不能为负数\n\n\n\n- zerofill\n - 0填充的\n - 不足的位数用0填充,如:int(3); --输入数字5,自动转为 `005`\n\n\n\n- 自增 auto_increment\n\n 自动在一条记录的基础上(默认) `+1`\n\n 必须是整数类型\n\n\n\n\n\n- 非空 NOT NULL\n - 假设为 not null: 如果不给他赋值,会报错\n\n\n\n例子:创建一个表\n\n```mysql\nCREATE TABLE\nIF\n\tNOT EXISTS `student` (\n\t`id` INT ( 4 ) NOT NULL AUTO_INCREMENT COMMENT '学号',\n\t`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名',\n\t`password` VARCHAR ( 30 ) NOT NULL DEFAULT '123456' COMMENT '密码',\n\t`sex` VARCHAR ( 2 ) NOT NULL DEFAULT '保密' COMMENT '性别',\n\t`age` INT ( 3 ) NOT NULL DEFAULT 18 COMMENT '年龄',\n\tPRIMARY KEY ( `id` ) -- 主键\n\t\n\t) ENGINE = INNODB DEFAULT CHARSET = utf8;\n```\n\n\n\n创建表格式( `[]`里面的内容表示可有可无 ):\n\n```mysql\nCREATE TABLE [IF NOT EXISTS] `表名`(\n\t`字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n ...\n `字段名` 列类型 [属性] [索引] [注释]\n)[表类型] [字符集设置] [注释]\n```\n\n\n\n在创建完一个数据库或者表后,可以用以下语句:\n\n```mysql\nSHOW CREATE DATABASE school; -- 查看创建数据库的语句\nSHOW CREATE TABLE student; \t-- 查看创建表的语句\nDESC student; -- 显示表的结构\n```\n\n\n\n\n\n## 数据库引擎\n\n1. INNODB(默认使用)\n2. MYISAM\n\n\n\n| | MYISAM | INNODB |\n| ------------ | ------ | ------------------- |\n| 事务支持 | 不支持 | 支持 |\n| 数据行锁定 | 不支持 | 支持 |\n| 外键约束 | 不支持 | 支持 |\n| 全文索引 | 支持 | 不支持 |\n| 表的空间大小 | 较小 | 较大,为MYISAM的2倍 |\n\n事务\n\n- 是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以\n\n\n\n占用空间:\n\n- InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;\n\n- 而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。\n\n\n\n常用操作:\n\n- MYISAM: 节约空间,速度快\n- INNODB:安全性高,事务处理,多表多用户操作\n\n\n\n物理空间存在位置:\n\n所有数据文件都在data目录下,本质还是文件存储\n\n- MYISAM对应文件:\n - *.frm 表结构定义\n - *.MYD 数据文件\n - *.MYI 索引文件\n- INNODB:只有 *.frm 文件,以及上级目录下ibdata1文件\n\n\n\n## 设置字符集编码\n\n```MySQL\nCHARSET=utf8\n```\n\nMySQL默认字符集 Latin1, 不支持中文\n\n可以在 my.ini文件配置,但发到别人电脑时可能会出错,最好在创建表时也写上\n\n\n\n\n\n## 修改、删除表\n\n### 修改表的结构:ALTER TABLE\n\n修改表名\n\n```MYSQL\nALTER TABLE teacher RENAME AS teacher1;\n```\n\n增加表的字段\n\n```mysql\nALTER TABLE teacher1 ADD age INT(11)\n```\n\n修改表的字段(字段名,字段类型)\n\n```mysql\nALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改l\nALTER TABLE teacher1 CHANGE age age1 int(11) -- 字段重命名\n\n```\n\n删除表的字段\n\n```mysql\nALTER TABLE teacher1 DROP age1; \n```\n\n\n\n### 删除表 DROP TABLE\n\n```mysql\ndrop table [if exists] teacher; -- 一般加上判断,避免出错\n```\n\n\n\n\n\n","slug":"MySQL/2-MySQL基础","published":1,"updated":"2021-09-08T13:06:19.369Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ol001bakve1lqxghp8","content":"
MySQL基础 mysql语法 注释:
\n
\n单行注释:--这是注释
\n \n多行注释:
\n1 2 3 4 5 6 /* 这是注释 这是注释 这是注释 这是注释 */
\n \n \n
mysql语句不区分大小写,建议小写
\n
数据库基本操作 操作数据库->操作数据库的表->操作数据库的表中的数据
\n
连接数据库
\n
1 mysql -uroot -p密码 #-p和密码之间没有空格
\n
查看全部数据库
\n
\n
使用某个数据库(假设有个数据库为:school)
\n
\n
\n如果名称是sql中的一些关键字,要加反引号
\n \n \n
查看该数据库所有的表
\n
\n
查看某个表信息(假设在该数据库中有个表为:student):
\n
1 2 3 describe student; 或 desc student;
\n
创建数据库school; (中括号 []
表示可选)
\n
1 create database [if not exist] school;
\n
删除数据库:
\n
1 drop database [if exist] school;
\n
数据库的(列)数据类型 数值 \ntinyint: 1个字节(-127~128) \nsmallint: 2个字节 \nmediumint: 3个字节 \nint:4个字节 \nbigint:8个字节 \nfloat:4个字节 \ndouble:8个字节 \ndecimal:字符串形式的浮点数(金融计算常用) \n \n
字符串 \nchar:1个字节(0~255) \nvarchar:可变字符串 0~65535 \ntinytext: 微型文本 2^8-1 \ntext:文本串 2^16-1 \n \n
时间日期 \ndate YYYY-MM-DD, 日期格式 \ntime HH:mm:ss 时间格式 \ndatetime YYYY-MM-DD HH:mm:ss (最常用) \ntimestamp 1970.1.1到现在的毫秒数(较常用)
\n \nyear:年份
\n \n \n
NULL 没有值,未知
\n
注意,不要使用NULL进行运算,结果为NULL
\n
字段属性(重要) 每一列(称为字段)都有多个属性,用于描述该列的特征
\n
\nunsigned
\n无符号整数,表示该列不能为负数
\n \n \n
\nzerofill\n0填充的 \n不足的位数用0填充,如:int(3); —输入数字5,自动转为 005
\n \n \n \n
\n自增 auto_increment
\n自动在一条记录的基础上(默认) +1
\n必须是整数类型
\n \n \n
\n非空 NOT NULL\n假设为 not null: 如果不给他赋值,会报错 \n \n \n \n
例子:创建一个表
\n
1 2 3 4 5 6 7 8 9 10 11 CREATE TABLE IF \tNOT EXISTS `student` ( \t`id` INT ( 4 ) NOT NULL AUTO_INCREMENT COMMENT '学号', \t`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名', \t`password` VARCHAR ( 30 ) NOT NULL DEFAULT '123456' COMMENT '密码', \t`sex` VARCHAR ( 2 ) NOT NULL DEFAULT '保密' COMMENT '性别', \t`age` INT ( 3 ) NOT NULL DEFAULT 18 COMMENT '年龄', \tPRIMARY KEY ( `id` ) -- 主键 \t \t) ENGINE = INNODB DEFAULT CHARSET = utf8;
\n
创建表格式( []
里面的内容表示可有可无 ):
\n
1 2 3 4 5 6 7 8 CREATE TABLE [IF NOT EXISTS] `表名`( \t`字段名` 列类型 [属性] [索引] [注释], `字段名` 列类型 [属性] [索引] [注释], `字段名` 列类型 [属性] [索引] [注释], `字段名` 列类型 [属性] [索引] [注释], ... `字段名` 列类型 [属性] [索引] [注释] )[表类型] [字符集设置] [注释]
\n
在创建完一个数据库或者表后,可以用以下语句:
\n
1 2 3 SHOW CREATE DATABASE school; -- 查看创建数据库的语句 SHOW CREATE TABLE student; \t-- 查看创建表的语句 DESC student; -- 显示表的结构
\n
数据库引擎 \nINNODB(默认使用) \nMYISAM \n \n
\n
\n\n\n \nMYISAM \nINNODB \n \n \n\n\n事务支持 \n不支持 \n支持 \n \n\n数据行锁定 \n不支持 \n支持 \n \n\n外键约束 \n不支持 \n支持 \n \n\n全文索引 \n支持 \n不支持 \n \n\n表的空间大小 \n较小 \n较大,为MYISAM的2倍 \n \n \n
\n
\n
事务
\n
\n是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以 \n \n
占用空间:
\n
\n
常用操作:
\n
\nMYISAM: 节约空间,速度快 \nINNODB:安全性高,事务处理,多表多用户操作 \n \n
物理空间存在位置:
\n
所有数据文件都在data目录下,本质还是文件存储
\n
\nMYISAM对应文件:\n*.frm 表结构定义 \n*.MYD 数据文件 \n*.MYI 索引文件 \n \n \nINNODB:只有 *.frm 文件,以及上级目录下ibdata1文件 \n \n
设置字符集编码 \n
MySQL默认字符集 Latin1, 不支持中文
\n
可以在 my.ini文件配置,但发到别人电脑时可能会出错,最好在创建表时也写上
\n
修改、删除表 修改表的结构:ALTER TABLE 修改表名
\n
1 ALTER TABLE teacher RENAME AS teacher1;
\n
增加表的字段
\n
1 ALTER TABLE teacher1 ADD age INT(11)
\n
修改表的字段(字段名,字段类型)
\n
1 2 3 ALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改l ALTER TABLE teacher1 CHANGE age age1 int(11) -- 字段重命名
\n
删除表的字段
\n
1 ALTER TABLE teacher1 DROP age1;
\n
删除表 DROP TABLE 1 drop table [if exists] teacher; -- 一般加上判断,避免出错
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"
MySQL基础 mysql语法 注释:
\n
\n单行注释:--这是注释
\n \n多行注释:
\n1 2 3 4 5 6 /* 这是注释 这是注释 这是注释 这是注释 */
\n \n \n
mysql语句不区分大小写,建议小写
\n
数据库基本操作 操作数据库->操作数据库的表->操作数据库的表中的数据
\n
连接数据库
\n
1 mysql -uroot -p密码 #-p和密码之间没有空格
\n
查看全部数据库
\n
\n
使用某个数据库(假设有个数据库为:school)
\n
\n
\n如果名称是sql中的一些关键字,要加反引号
\n \n \n
查看该数据库所有的表
\n
\n
查看某个表信息(假设在该数据库中有个表为:student):
\n
1 2 3 describe student; 或 desc student;
\n
创建数据库school; (中括号 []
表示可选)
\n
1 create database [if not exist] school;
\n
删除数据库:
\n
1 drop database [if exist] school;
\n
数据库的(列)数据类型 数值 \ntinyint: 1个字节(-127~128) \nsmallint: 2个字节 \nmediumint: 3个字节 \nint:4个字节 \nbigint:8个字节 \nfloat:4个字节 \ndouble:8个字节 \ndecimal:字符串形式的浮点数(金融计算常用) \n \n
字符串 \nchar:1个字节(0~255) \nvarchar:可变字符串 0~65535 \ntinytext: 微型文本 2^8-1 \ntext:文本串 2^16-1 \n \n
时间日期 \ndate YYYY-MM-DD, 日期格式 \ntime HH:mm:ss 时间格式 \ndatetime YYYY-MM-DD HH:mm:ss (最常用) \ntimestamp 1970.1.1到现在的毫秒数(较常用)
\n \nyear:年份
\n \n \n
NULL 没有值,未知
\n
注意,不要使用NULL进行运算,结果为NULL
\n
字段属性(重要) 每一列(称为字段)都有多个属性,用于描述该列的特征
\n
\nunsigned
\n无符号整数,表示该列不能为负数
\n \n \n
\nzerofill\n0填充的 \n不足的位数用0填充,如:int(3); —输入数字5,自动转为 005
\n \n \n \n
\n自增 auto_increment
\n自动在一条记录的基础上(默认) +1
\n必须是整数类型
\n \n \n
\n非空 NOT NULL\n假设为 not null: 如果不给他赋值,会报错 \n \n \n \n
例子:创建一个表
\n
1 2 3 4 5 6 7 8 9 10 11 CREATE TABLE IF \tNOT EXISTS `student` ( \t`id` INT ( 4 ) NOT NULL AUTO_INCREMENT COMMENT '学号', \t`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名', \t`password` VARCHAR ( 30 ) NOT NULL DEFAULT '123456' COMMENT '密码', \t`sex` VARCHAR ( 2 ) NOT NULL DEFAULT '保密' COMMENT '性别', \t`age` INT ( 3 ) NOT NULL DEFAULT 18 COMMENT '年龄', \tPRIMARY KEY ( `id` ) -- 主键 \t \t) ENGINE = INNODB DEFAULT CHARSET = utf8;
\n
创建表格式( []
里面的内容表示可有可无 ):
\n
1 2 3 4 5 6 7 8 CREATE TABLE [IF NOT EXISTS] `表名`( \t`字段名` 列类型 [属性] [索引] [注释], `字段名` 列类型 [属性] [索引] [注释], `字段名` 列类型 [属性] [索引] [注释], `字段名` 列类型 [属性] [索引] [注释], ... `字段名` 列类型 [属性] [索引] [注释] )[表类型] [字符集设置] [注释]
\n
在创建完一个数据库或者表后,可以用以下语句:
\n
1 2 3 SHOW CREATE DATABASE school; -- 查看创建数据库的语句 SHOW CREATE TABLE student; \t-- 查看创建表的语句 DESC student; -- 显示表的结构
\n
数据库引擎 \nINNODB(默认使用) \nMYISAM \n \n
\n
\n\n\n \nMYISAM \nINNODB \n \n \n\n\n事务支持 \n不支持 \n支持 \n \n\n数据行锁定 \n不支持 \n支持 \n \n\n外键约束 \n不支持 \n支持 \n \n\n全文索引 \n支持 \n不支持 \n \n\n表的空间大小 \n较小 \n较大,为MYISAM的2倍 \n \n \n
\n
\n
事务
\n
\n是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以 \n \n
占用空间:
\n
\n
常用操作:
\n
\nMYISAM: 节约空间,速度快 \nINNODB:安全性高,事务处理,多表多用户操作 \n \n
物理空间存在位置:
\n
所有数据文件都在data目录下,本质还是文件存储
\n
\nMYISAM对应文件:\n*.frm 表结构定义 \n*.MYD 数据文件 \n*.MYI 索引文件 \n \n \nINNODB:只有 *.frm 文件,以及上级目录下ibdata1文件 \n \n
设置字符集编码 \n
MySQL默认字符集 Latin1, 不支持中文
\n
可以在 my.ini文件配置,但发到别人电脑时可能会出错,最好在创建表时也写上
\n
修改、删除表 修改表的结构:ALTER TABLE 修改表名
\n
1 ALTER TABLE teacher RENAME AS teacher1;
\n
增加表的字段
\n
1 ALTER TABLE teacher1 ADD age INT(11)
\n
修改表的字段(字段名,字段类型)
\n
1 2 3 ALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改l ALTER TABLE teacher1 CHANGE age age1 int(11) -- 字段重命名
\n
删除表的字段
\n
1 ALTER TABLE teacher1 DROP age1;
\n
删除表 DROP TABLE 1 drop table [if exists] teacher; -- 一般加上判断,避免出错
\n"},{"title":"MySql(1)-数据库认识","abbrlink":"b9bb0bd5","date":"2021-06-05T02:25:15.000Z","description":"数据库介绍(数据模型、系统结构与组成)","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n# 数据库介绍与认识\n\n\n\n数据库管理系统:DBMS(database manage system)\n\n## 数据模型\n\n- 概念模型:不依赖于任何计算机系统和DBMS的数据模型\n- 逻辑模型:与具体DBMS相关的模型,用于完成具体数据库设计\n\n> 组成要素:\n>\n> - 数据结构\n> - 数据操作\n> - 数据完整性的约束条件\n\n\n\n## 概念模型\n\n概念模型是数据库设计人员进行数据库设计的工具,常用概念模型工具是:**
实体-联系方法 (E-R方法)**\n\n### 实体联系模型\n\n- 实体(Entity):客观世界事物的集合,如学生、教师……\n- 属性(Attribute):一个实体拥有各种属性,如学生有姓名、学号……\n- 码(Key):唯一标识实体的属性集合,如学号\n- 域(Domain):属性的取值范围,如性别有男、女\n\n\n\n> - 实体型\n>\n> - 实体集\n> - 联系\n\n\n\n### 实体联系类型\n\n- 一对一(1:1)\n- 一对多(1:N)\n- 多对多(M:N)\n\n\n\n## 常用数据模型\n\n### 层次模型\n\n用**
树形结构 **来表示各类实体以及实体间的联系的数据模型\n\n1. 数据结构\n - 有且只有一个结点,无父结点,该结点为根结点\n - 根结点外的其他结点有且仅有一个父结点\n2. 表现形式(以多对多为例)\n - 冗余结点法\n - 虚拟结点法\n3. 数据操作和完整性约束\n - 数据操作:增删查改\n - 完整性约束\n4. 数据存储形式\n - 邻接法\n - 链接法\n5. 优缺点\n - 优点:结构简单,查询效率高\n - 缺点:(1)对于非层次结构的数据表示复杂;(2)查询必须从根结点出发\n\n\n\n### 网状模型\n\n用**
网状结构 **来表示各类实体之间的联系的数据模型\n\n1. 数据结构\n - 允许一个以上结点无双亲\n - 一个结点可以有多于一个的双亲\n2. 优缺点:\n - 优点:描述方便,存储效率高\n - 缺点:结构复杂,处理复杂\n\n\n\n### 关系模型\n\n用**
关系 **来表示实体与实体间的联系\n\n1. 数据结构\n\n - 一个关系就是一张二维表,表有表名,表中内容是对应关系模式在某个时刻的值,称为一个关系\n - 元组:表的
一行 称为
元组 ,一个元组可以表示一个实体或实体间的联系\n - 属性:表中的
一列 称为关系的一个
属性 \n - 分量:元组中的一个属性值\n\n
\n\n2. 数据操作:增删查改\n3. 完整性约束:实体完整性,参照完整性、用户自定义完整性\n\n\n\n## 系统结构\n\n### 二级映像功能\n\n- 模式/内模式映像\n- 外模式/模式映像\n\n\n\n## 数据库组成\n\n1. 数据库\n2. DBMS\n3. 应用程序\n4. 数据库管理员(DBA)\n\n\n\n\n\n\n\n\n\n\n\n","source":"_posts/MySQL/1-数据库认识.md","raw":"---\ntitle: MySql(1)-数据库认识\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: b9bb0bd5\ndate: 2021-06-05 10:25:15\ndescription: 数据库介绍(数据模型、系统结构与组成)\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n# 数据库介绍与认识\n\n\n\n数据库管理系统:DBMS(database manage system)\n\n## 数据模型\n\n- 概念模型:不依赖于任何计算机系统和DBMS的数据模型\n- 逻辑模型:与具体DBMS相关的模型,用于完成具体数据库设计\n\n> 组成要素:\n>\n> - 数据结构\n> - 数据操作\n> - 数据完整性的约束条件\n\n\n\n## 概念模型\n\n概念模型是数据库设计人员进行数据库设计的工具,常用概念模型工具是:**
实体-联系方法 (E-R方法)**\n\n### 实体联系模型\n\n- 实体(Entity):客观世界事物的集合,如学生、教师……\n- 属性(Attribute):一个实体拥有各种属性,如学生有姓名、学号……\n- 码(Key):唯一标识实体的属性集合,如学号\n- 域(Domain):属性的取值范围,如性别有男、女\n\n\n\n> - 实体型\n>\n> - 实体集\n> - 联系\n\n\n\n### 实体联系类型\n\n- 一对一(1:1)\n- 一对多(1:N)\n- 多对多(M:N)\n\n\n\n## 常用数据模型\n\n### 层次模型\n\n用**
树形结构 **来表示各类实体以及实体间的联系的数据模型\n\n1. 数据结构\n - 有且只有一个结点,无父结点,该结点为根结点\n - 根结点外的其他结点有且仅有一个父结点\n2. 表现形式(以多对多为例)\n - 冗余结点法\n - 虚拟结点法\n3. 数据操作和完整性约束\n - 数据操作:增删查改\n - 完整性约束\n4. 数据存储形式\n - 邻接法\n - 链接法\n5. 优缺点\n - 优点:结构简单,查询效率高\n - 缺点:(1)对于非层次结构的数据表示复杂;(2)查询必须从根结点出发\n\n\n\n### 网状模型\n\n用**
网状结构 **来表示各类实体之间的联系的数据模型\n\n1. 数据结构\n - 允许一个以上结点无双亲\n - 一个结点可以有多于一个的双亲\n2. 优缺点:\n - 优点:描述方便,存储效率高\n - 缺点:结构复杂,处理复杂\n\n\n\n### 关系模型\n\n用**
关系 **来表示实体与实体间的联系\n\n1. 数据结构\n\n - 一个关系就是一张二维表,表有表名,表中内容是对应关系模式在某个时刻的值,称为一个关系\n - 元组:表的
一行 称为
元组 ,一个元组可以表示一个实体或实体间的联系\n - 属性:表中的
一列 称为关系的一个
属性 \n - 分量:元组中的一个属性值\n\n
\n\n2. 数据操作:增删查改\n3. 完整性约束:实体完整性,参照完整性、用户自定义完整性\n\n\n\n## 系统结构\n\n### 二级映像功能\n\n- 模式/内模式映像\n- 外模式/模式映像\n\n\n\n## 数据库组成\n\n1. 数据库\n2. DBMS\n3. 应用程序\n4. 数据库管理员(DBA)\n\n\n\n\n\n\n\n\n\n\n\n","slug":"MySQL/1-数据库认识","published":1,"updated":"2021-09-10T03:01:10.897Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6oo001gakve5kd5ad6i","content":"
数据库介绍与认识 数据库管理系统:DBMS(database manage system)
\n
数据模型 \n概念模型:不依赖于任何计算机系统和DBMS的数据模型 \n逻辑模型:与具体DBMS相关的模型,用于完成具体数据库设计 \n \n
\n组成要素:
\n\n数据结构 \n数据操作 \n数据完整性的约束条件 \n \n \n
概念模型 概念模型是数据库设计人员进行数据库设计的工具,常用概念模型工具是:实体-联系方法 (E-R方法)
\n
实体联系模型 \n实体(Entity):客观世界事物的集合,如学生、教师…… \n属性(Attribute):一个实体拥有各种属性,如学生有姓名、学号…… \n码(Key):唯一标识实体的属性集合,如学号 \n域(Domain):属性的取值范围,如性别有男、女 \n \n
\n\n \n
实体联系类型 \n一对一(1:1) \n一对多(1:N) \n多对多(M:N) \n \n
常用数据模型 层次模型 用树形结构 来表示各类实体以及实体间的联系的数据模型
\n
\n数据结构\n有且只有一个结点,无父结点,该结点为根结点 \n根结点外的其他结点有且仅有一个父结点 \n \n \n表现形式(以多对多为例)\n \n数据操作和完整性约束\n \n数据存储形式\n \n优缺点\n优点:结构简单,查询效率高 \n缺点:(1)对于非层次结构的数据表示复杂;(2)查询必须从根结点出发 \n \n \n \n
网状模型 用网状结构 来表示各类实体之间的联系的数据模型
\n
\n数据结构\n允许一个以上结点无双亲 \n一个结点可以有多于一个的双亲 \n \n \n优缺点:\n优点:描述方便,存储效率高 \n缺点:结构复杂,处理复杂 \n \n \n \n
关系模型 用关系 来表示实体与实体间的联系
\n
\n数据结构
\n\n一个关系就是一张二维表,表有表名,表中内容是对应关系模式在某个时刻的值,称为一个关系 \n元组:表的一行 称为元组 ,一个元组可以表示一个实体或实体间的联系 \n属性:表中的一列 称为关系的一个属性 \n分量:元组中的一个属性值 \n \n \n \n
\n
\n数据操作:增删查改 \n完整性约束:实体完整性,参照完整性、用户自定义完整性 \n \n
系统结构 二级映像功能 \n
数据库组成 \n数据库 \nDBMS \n应用程序 \n数据库管理员(DBA) \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"
数据库介绍与认识 数据库管理系统:DBMS(database manage system)
\n
数据模型 \n概念模型:不依赖于任何计算机系统和DBMS的数据模型 \n逻辑模型:与具体DBMS相关的模型,用于完成具体数据库设计 \n \n
\n组成要素:
\n\n数据结构 \n数据操作 \n数据完整性的约束条件 \n \n \n
概念模型 概念模型是数据库设计人员进行数据库设计的工具,常用概念模型工具是:实体-联系方法 (E-R方法)
\n
实体联系模型 \n实体(Entity):客观世界事物的集合,如学生、教师…… \n属性(Attribute):一个实体拥有各种属性,如学生有姓名、学号…… \n码(Key):唯一标识实体的属性集合,如学号 \n域(Domain):属性的取值范围,如性别有男、女 \n \n
\n\n \n
实体联系类型 \n一对一(1:1) \n一对多(1:N) \n多对多(M:N) \n \n
常用数据模型 层次模型 用树形结构 来表示各类实体以及实体间的联系的数据模型
\n
\n数据结构\n有且只有一个结点,无父结点,该结点为根结点 \n根结点外的其他结点有且仅有一个父结点 \n \n \n表现形式(以多对多为例)\n \n数据操作和完整性约束\n \n数据存储形式\n \n优缺点\n优点:结构简单,查询效率高 \n缺点:(1)对于非层次结构的数据表示复杂;(2)查询必须从根结点出发 \n \n \n \n
网状模型 用网状结构 来表示各类实体之间的联系的数据模型
\n
\n数据结构\n允许一个以上结点无双亲 \n一个结点可以有多于一个的双亲 \n \n \n优缺点:\n优点:描述方便,存储效率高 \n缺点:结构复杂,处理复杂 \n \n \n \n
关系模型 用关系 来表示实体与实体间的联系
\n
\n数据结构
\n\n一个关系就是一张二维表,表有表名,表中内容是对应关系模式在某个时刻的值,称为一个关系 \n元组:表的一行 称为元组 ,一个元组可以表示一个实体或实体间的联系 \n属性:表中的一列 称为关系的一个属性 \n分量:元组中的一个属性值 \n \n \n \n
\n
\n数据操作:增删查改 \n完整性约束:实体完整性,参照完整性、用户自定义完整性 \n \n
系统结构 二级映像功能 \n
数据库组成 \n数据库 \nDBMS \n应用程序 \n数据库管理员(DBA) \n \n"},{"title":"MySql(4)-MySQL事务","abbrlink":"2a3cf99e","date":"2021-06-08T02:51:44.000Z","description":"MySQL事务原则(原子性、一致性、持久性、隔离性)、事务执行","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n# 事务\n\n## 什么是事务\n\n什么是事务:要么都成功,要么都失败\n\n> 比如:\n>\n> a 转账给 b 100元\n>\n> b 收到 a 的钱 100元\n>\n> 这两者应该:要么都成功,要么都失败\n\n\n\n事务原则:ACID原则->【原子性、一致性、隔离性、持久性】\n\n- 原子性:要么都成功,要么都失败\n- 一致性:事务前后(时间上)数据完整性一致\n- 持久性:事务一旦提交则不可逆\n- 隔离性:多个用户操作一个数据库,数据库为每一个用户开启的事务,应该相互隔离,相互之间不能干扰\n\n\n\n> 隔离导致的一些问题:\n>\n> - 脏读:一个事务读取另一个事务未提交的数据\n> - 不可重复读:在一个事务内读取表中某一数据,多次读取结果不同\n> - 虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致\n\n\n\n\n\n## 事务执行\n\nMySQL默认开启事务自动提交\n\n手动处理事务:\n\n```mysql\nset autocommit = 0 -- 关闭事务自动提交\nstart transaction -- 标记一个事务开始,从这句话后的sql都在一个事务内\n\n/**\n * 数据操作\n */\n\n-- 提交:持久化(成功)\ncommit\n\n-- 回滚:回到原来样子(事务失败)\nrollback\n\n-- 事务结束\nset autocommit = 1 -- 恢复自动提交\n\nsavepoint 保存点名 ; -- 设置事务保存点\nrollback to savepoint 保存点名 ; -- 回到事务保存点\nrelease savepoint 保存点名; -- 撤销保存点\n\n\n\n```\n\n\n\n","source":"_posts/MySQL/4-MySQL事务.md","raw":"---\ntitle: MySql(4)-MySQL事务\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: 2a3cf99e\ndate: 2021-06-08 10:51:44\ndescription: MySQL事务原则(原子性、一致性、持久性、隔离性)、事务执行\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n# 事务\n\n## 什么是事务\n\n什么是事务:要么都成功,要么都失败\n\n> 比如:\n>\n> a 转账给 b 100元\n>\n> b 收到 a 的钱 100元\n>\n> 这两者应该:要么都成功,要么都失败\n\n\n\n事务原则:ACID原则->【原子性、一致性、隔离性、持久性】\n\n- 原子性:要么都成功,要么都失败\n- 一致性:事务前后(时间上)数据完整性一致\n- 持久性:事务一旦提交则不可逆\n- 隔离性:多个用户操作一个数据库,数据库为每一个用户开启的事务,应该相互隔离,相互之间不能干扰\n\n\n\n> 隔离导致的一些问题:\n>\n> - 脏读:一个事务读取另一个事务未提交的数据\n> - 不可重复读:在一个事务内读取表中某一数据,多次读取结果不同\n> - 虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致\n\n\n\n\n\n## 事务执行\n\nMySQL默认开启事务自动提交\n\n手动处理事务:\n\n```mysql\nset autocommit = 0 -- 关闭事务自动提交\nstart transaction -- 标记一个事务开始,从这句话后的sql都在一个事务内\n\n/**\n * 数据操作\n */\n\n-- 提交:持久化(成功)\ncommit\n\n-- 回滚:回到原来样子(事务失败)\nrollback\n\n-- 事务结束\nset autocommit = 1 -- 恢复自动提交\n\nsavepoint 保存点名 ; -- 设置事务保存点\nrollback to savepoint 保存点名 ; -- 回到事务保存点\nrelease savepoint 保存点名; -- 撤销保存点\n\n\n\n```\n\n\n\n","slug":"MySQL/4-MySQL事务","published":1,"updated":"2021-09-08T13:06:03.208Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6oq001jakvebouthuqu","content":"
事务 什么是事务 什么是事务:要么都成功,要么都失败
\n
\n比如:
\na 转账给 b 100元
\nb 收到 a 的钱 100元
\n这两者应该:要么都成功,要么都失败
\n \n
事务原则:ACID原则->【原子性、一致性、隔离性、持久性】
\n
\n原子性:要么都成功,要么都失败 \n一致性:事务前后(时间上)数据完整性一致 \n持久性:事务一旦提交则不可逆 \n隔离性:多个用户操作一个数据库,数据库为每一个用户开启的事务,应该相互隔离,相互之间不能干扰 \n \n
\n隔离导致的一些问题:
\n\n脏读:一个事务读取另一个事务未提交的数据 \n不可重复读:在一个事务内读取表中某一数据,多次读取结果不同 \n虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致 \n \n \n
事务执行 MySQL默认开启事务自动提交
\n
手动处理事务:
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 set autocommit = 0 -- 关闭事务自动提交 start transaction -- 标记一个事务开始,从这句话后的sql都在一个事务内 /** * 数据操作 */ -- 提交:持久化(成功) commit -- 回滚:回到原来样子(事务失败) rollback -- 事务结束 set autocommit = 1 -- 恢复自动提交 savepoint 保存点名 ; -- 设置事务保存点 rollback to savepoint 保存点名 ; -- 回到事务保存点 release savepoint 保存点名; -- 撤销保存点
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"
事务 什么是事务 什么是事务:要么都成功,要么都失败
\n
\n比如:
\na 转账给 b 100元
\nb 收到 a 的钱 100元
\n这两者应该:要么都成功,要么都失败
\n \n
事务原则:ACID原则->【原子性、一致性、隔离性、持久性】
\n
\n原子性:要么都成功,要么都失败 \n一致性:事务前后(时间上)数据完整性一致 \n持久性:事务一旦提交则不可逆 \n隔离性:多个用户操作一个数据库,数据库为每一个用户开启的事务,应该相互隔离,相互之间不能干扰 \n \n
\n隔离导致的一些问题:
\n\n脏读:一个事务读取另一个事务未提交的数据 \n不可重复读:在一个事务内读取表中某一数据,多次读取结果不同 \n虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致 \n \n \n
事务执行 MySQL默认开启事务自动提交
\n
手动处理事务:
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 set autocommit = 0 -- 关闭事务自动提交 start transaction -- 标记一个事务开始,从这句话后的sql都在一个事务内 /** * 数据操作 */ -- 提交:持久化(成功) commit -- 回滚:回到原来样子(事务失败) rollback -- 事务结束 set autocommit = 1 -- 恢复自动提交 savepoint 保存点名 ; -- 设置事务保存点 rollback to savepoint 保存点名 ; -- 回到事务保存点 release savepoint 保存点名; -- 撤销保存点
\n"},{"title":"MySql(3)-数据管理","abbrlink":"d709dda","date":"2021-06-07T02:43:14.000Z","description":"Mysql数据管理,增删查改","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n# MySQL数据管理\n\n## 关系操作符\n\n| 运算符 | 含义 | 例子 | 结果 |\n| :------: | :----: | :--: | :---: |\n| = | 等于 | 5=6 | false |\n| <> 或 != | 不等于 | 5<>6 | true |\n| > | | | |\n| < | | | |\n| <= | | | |\n| >= | | | |\n| !< | 不小于 | | |\n| !> | 不大于 | | |\n\n\n\n## 主键\n\n主键:对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。\n\n\n\n在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,`students`表的两行记录:\n\n| id | class_id | name | gender | score |\n| :--: | :------: | :--: | :----: | :---: |\n| 1 | 1 | 小明 | M | 90 |\n| 2 | 1 | 小红 | F | 95 |\n\n每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。\n\n假设我们把`name`字段作为主键,那么通过名字`小明`或`小红`就能唯一确定一条记录。但是,这么设定,就没法存储**
同名 **的同学了,因为插入相同主键的两条记录是不被允许的。\n\n\n\n对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。\n\n\n\n### 联合主键\n\n关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。\n\n对于联合主键,允许一列有重复,只要不是所有主键列都重复即可\n\n\n\n\n\n## 外键(了解)\n\n\n\n当我们用主键唯一标识记录时,我们就可以在`students`表中确定任意一个学生的记录:\n\n| id | name | other columns... |\n| :--- | :--- | :--------------- |\n| 1 | 小明 | ... |\n| 2 | 小红 | ... |\n\n我们还可以在`classes`表中确定任意一个班级记录:\n\n| id | name | other columns... |\n| :--- | :--- | :--------------- |\n| 1 | 一班 | ... |\n| 2 | 二班 | ... |\n\n但是我们如何确定`students`表的一条记录,例如,`id=1`的小明,属于哪个班级呢?\n\n\n\n由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“
一对多 ”,即一个`classes`的记录可以对应多个`students`表的记录。\n\n\n\n为了表达这种一对多的关系,我们需要在`students`表中加入一列`class_id`,让它的值与`classes`表的某条记录相对应:\n\n| id | class_id | name | other columns... |\n| :--- | :------- | :--- | :--------------- |\n| 1 | 1 | 小明 | ... |\n| 2 | 1 | 小红 | ... |\n| 5 | 2 | 小白 | ... |\n\n这样,我们就可以根据`class_id`这个列直接定位出一个`students`表的记录应该对应到`classes`的哪条记录。\n\n\n\n\n\n给一张表 `students` 添加外键约束:\n\n```SQL\nALTER TABLE students\nADD CONSTRAINT fk_class_id #外键约束的名称`fk_class_id`可以任意\nFOREIGN KEY (class_id) # 指定了`class_id`作为外键\nREFERENCES classes (id); #指定了这个外键将关联到`classes`表的`id`列(即`classes`表的主键)\n```\n\n\n\n\n\n添加了外键的表(`students`)为从表,被外键引用的表(`classes`)为主表,要删除主表前需先删除从表\n\n> 注意:以上外键是数据库级别外键,不推荐使用\n>\n> 最佳实现:\n>\n> - 数据库就是单纯的表,只有行(记录)、列(字段)\n> - 想使用多张表数据,想用外键(用程序如Java语言实现)\n\n\n\n## DML语言(全部记住)\n\n即数据操作语言\n\n- 插入(增)一行(记录):insert into...values...\n\n```mysql\ninsert into `表名`([字段名1,字段名2,...]) values ('值1a', '值1b'), ('值2a', '值2b'), ...;\n\n例:\n\nINSERT INTO `student` ( `id`, `name` )\nVALUES ( 1001, '张三' ), (1002, '李四'), (1003, '王五');\n```\n\n![](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-26-08_image-20210419143255520.png)\n\n- 删除一列(字段):\n - delete\n - truncate\n - 相同:都会删除记录,表的结构和索引、约束不会变\n - 区别:\n 1. truncate 重新设置自增列,计数器会归零;delete不会影响自增\n 2. truncate 不会影响事务\n\n```mysql\n-- 删除一条记录\ndelete from `student` where id = '1001';\n\n-- 删除所有记录(不建议这样写)\ndelete from `student`;\n\n```\n\n```mysql\n-- 清空表的所有记录,但表的结构和索引不变\ntruncate table `student`;\n```\n\n> 对不同引擎的数据库使用`delete`语句后,重启数据库,现象:(了解)\n>\n> - InnoDB:自增列会从1开始(因为保存在内存中)\n> - MyISAM:继续从上一个自增量开始(保存在文件中)\n\n\n\n\n\n- 修改:update...set...\n\n```mysql\n-- 用where指定条件\nupdate `student` set `name`='wangwu' where `id` = `1001`;\n\n-- 不指定条件情况下,会改动所有行(即记录)\nupdate `student` set `name`='wangwu';\n\n-- 修改多个字段, 用逗号隔开\nupdate `student` set `name`='wangwu', `age`=20 where `id` = `1001`;\n\n-- 通过多个条件定位数据, 用逻辑运算符and、or...\nupdate `student` set `name`='wangwu' where `id` = `1001` and `age` = 18;\n```\n\n> 注意 **【=】** 含义:\n>\n> 在set这样的语句后面表示赋值;\n>\n> 在where后面表示关系运算符,表示比较;\n\n\n\n## DQL查询数据(最重要)\n\nDQL(data query language:数据查询语言)\n\n- 所有查询(简单、复杂)都用它\n- 数据库中最核心、重要的语句\n- 使用频率最高的语句\n\n### 基本查询操作 select\n\n1. 查询字段\n\n ```mysql\n -- 查询全部字段 select 字段 from 表\n select * from `student`;\n \n -- 查询指定字段 select 字段1[, 字段2, ...] from 表;\n select name, age from student;\n \n -- 别名,给结果(字段、表。。。)起个名字 as, as也可以省略不写\n select name as `学生姓名`, age as `年龄` from `student` as `s`;\n \n -- 函数concat(a, b): 连接a、b字符串\n select concat('姓名:', name) as 新名字 from student;\n ```\n\n \n\n2. 去重 `distinct`\n\n ```mysql\n -- 去除select后重复的数据,只留下一条\n select distinct `age` from student;\n ```\n\n3. 其他\n\n ```mysql\n -- 查询系统版本\n select version(); -- version()是个函数\n \n -- 计算\n select 100*3-1 as 计算结果;\n \n -- 所有学员考试成绩 +1 分\n select `name`, `grade` + 1 as 提成1分后 from `student`; \n \n -- 查询系统内置变量值\n select @@auto_increment_increment; -- 查询自增步长\n \n ```\n\n数据库的表达式:文本值、列、null、函数、计算表达式、系统变量。。。\n\n```mysql\nselect 表达式 from 表;\n```\n\n\n\n### where语句\n\n检索数据中符合条件的值\n\n| 逻辑运算符 | 语法 | 描述 |\n| :--------- | :------------------ | :-------------- |\n| and `&&` | a and b、 `a&&b` | 2个都为真返回真 |\n| or `||` | a or b 、 `a||b` | 。。。 |\n| not `!` | not a、 `!a` | 。。。 |\n\n```mysql\nSELECT\n\tid AS 学号,\n\t`name` AS 姓名 \nFROM\n\tstudent \nWHERE\n\tgrade > 80 \n\tAND age < 21;\n```\n\n```mysql\nSELECT\n\t`id` AS 学号,\n\t`name` AS 姓名,\n\t`grade` as 成绩\nFROM\n\tstudent \nWHERE\n\tgrade BETWEEN 70 AND 90;\n```\n\n### 模糊查询\n\n| 运算符 | 语法 | 描述 |\n| ----------- | --------------------- | --------------------------------------- |\n| is null | a is null | 如果a是null,则为真 |\n| is not null | a is not null | 如果a不是null,则为真 |\n| between | a between b and c | 若a在b和c之间,结果为真 |\n| like | a like b | sql匹配,若a匹配b,结果为真 |\n| in | a in(a1,a2,a3,a4,...) | 若a是a1,a2,a3,a4...中的一个值,结果为真 |\n\nlike:通常结合 `%`、`_` 来使用\n\n```mysql\n-- 查找以王字开头的name \n-- “%”表示【任意多个】【任意的】字符,“_”表示任意【一个】字符\nSELECT `id`, `name` FROM student\nWHERE `name` LIKE '王%'; -- 表示以【王】字开头\n\n\n-- 查找以王字开头且【名字为2个字】的name\nSELECT `id`, `name` FROM student\nWHERE `name` LIKE '王__'; -- 两个下划线 \n\n\n-- 查找生日不是null的\nSELECT `id`, `name` FROM student\nWHERE `birthday` is not null;\n```\n\n\n\n### 联表查询 join on\n\n同时在2个表中查找数据(必须要有交集)\n\n\n\n
\n\n\n\n```mysql\n/**\n有2张表(都有id、name字段):\n1. `student`:班级学生基本信息\n2. `exam`:年级学生考试信息\n\n【问题:查找出本班级学生的考试信息】\n*/\n\n-- =====================================\n-- 取交集 inner join\nselect s.id as 学号, s.name as 姓名 \nfrom student as s inner join exam as e -- as可以省略\non s.id = e.id;\n\n-- =====================================\n-- 仅左边的表有,left join \nSELECT\n\ts.id 学号, -- 省略as\n\ts.NAME 姓名 \nFROM\n\tstudent s\n\tLEFT JOIN exam e ON s.NAME = e.NAME \nWHERE\n\te.`name` IS NULL;\n-- =====================================\n\n-- 两个表的所有人 \n\n```\n\n> mysql 不支持 full outer join\n>\n\n\n\n\n\n### 自连接及联表查询\n\n自己的表和自己的表连接,核心:1张表拆为2张一样的表\n\n\n\n### 分页和排序\n\n- 排序 **order by**\n\n - 升序用 ASC, 降序用DESC\n\n - order by 表示查询结果怎么排(升序、降序),通过哪个字段排\n\n 比如:\n\n ```mysql\n -- order 字段 ASC 升序\n -- order 字段 DESC 降序\n SELECT * FROM exam ORDER BY `grade` ASC; -- 成绩按升序排\n ```\n\n- 分页 **limit**:可以缓解数据库压力、给人体验更好、瀑布流\n\n - 语法:\n\n ```mysql\n -- limit 起始下标(从0开始) 页面大小\n \n -- 从第0条数据开始,显示2条数据\n SELECT * FROM exam ORDER BY `id` DESC LIMIT 0,2;\n ```\n\n \n\n\n\n### 子查询与嵌套查询\n\nwhere 表达式【这个值是计算出来的】\n\n即:where 后面还跟select语句\n\n\n\n## MySQL常用函数\n\n### 数学运算\n\n```mysql\nabs()\n\nceiling() -- 向上取整 9.1 -> 10\nfloor() -- 向下取整 9.1 -> 9\n\nrand() -- 返回0~1随机数\n\nsign() -- 判断一个数符号 0->0 负数->-1 正数->1\n```\n\n\n\n### 字符串\n\n```mysql\nchar_length() -- 字符串长度\n\nconcat('a', 'b', 'c') -- 字符串连接 返回abc\n\n-- 字符索引从1开始,替换时,把第n1开始的共len个字符替换为str2\ninsert(str1, n1, len, str2)\n-- 如\nselect insert('abcdef', 2, 3, '5555'); -- 返回 a5555ef\n\n-- 将一个字符串str1中的指定子字符串substr替换为新字符串str2\nreplace(str1, substr, str2)\n\n-- 截取指定字符串\nsubstr(str, a, len); -- 截取str中第a个开始,共len个字符的子串\n\n-- 大小写转换\nupper()\nlower()\n\n-- 返回在str第一次出现的字符串substr的索引\ninstr(str, substr) -- 没有则返回0\n\n-- 反转字符串\nreverse(str)\n\n```\n\n### 时间-日期函数\n\n```mysql\n-- 当前日期\nSELECT CURRENT_DATE()\nSELECT CURDATE()\n\nSELECT NOW(); -- 当前时间 2021-04-20 20:05:44\nSELECT LOCALTIME(); -- 本地时间\nSELECT SYSDATE(); -- 系统时间\n\nSELECT YEAR(NOW()); -- 年\nSELECT MONTH(NOW()); -- 月\nSELECT DAY(NOW());\t-- 日\nSELECT HOUR(NOW());\t-- 时\nSELECT MINUTE(NOW()); -- 分\nSELECT SECOND(NOW()); -- 秒\n```\n\n\n\n### 系统函数\n\n```mysql\nSELECT SYSTEM_USER(); -- 当前系统用户\nSELECT USER()\t\t\t-- 当前用户\nSELECT VERSION()\t\t-- 版本号\n```\n\n\n\n### 聚合函数(常用)\n\n```mysql\ncount() -- 计数有多少个记录\n\tselect count(字段) from tablename; -- 忽略所有null值\n\tselect count(*) from tablename; -- 不会忽略null值,计算行数\n\tselect count(1) from tablename; -- 不会忽略null值,与上一个基本相同\n\t\nsum()\n\tselect sum(字段) from tablename;\n\t\navg()\n\tselect avg(字段) from tablename;\n\nmax()\n\tselect max(字段) from tablename;\n\nmin()\n\tselect min(字段) from tablename;\n```\n\n\n\n\n\n> **select**语法总结\n>\n> ![image-20210420202906345](https://gitee.com/ajream/images/raw/master/img/2021-04-20 20-29-07_image-20210420202906345.png)\n\n\n\n## 数据库级别的md5摘要算法\n\n**MD5消息摘要算法**(英语:MD5 Message-Digest Algorithm),一种被广泛使用的【密码散列函数】\n\nmd5不可逆,具体值的md5值是一样的\n\nmd5破解网站破解原理,其背后有一个巨大的字典库,你给它一个md5值,它就会搜索字典中的哪个字符串经过md5转换后与你输入的一样\n\nSQL使用函数 `MD5()`进行加密\n\n\n\n","source":"_posts/MySQL/3-数据管理.md","raw":"---\ntitle: MySql(3)-数据管理\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: d709dda\ndate: 2021-06-07 10:43:14\ndescription: Mysql数据管理,增删查改\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n# MySQL数据管理\n\n## 关系操作符\n\n| 运算符 | 含义 | 例子 | 结果 |\n| :------: | :----: | :--: | :---: |\n| = | 等于 | 5=6 | false |\n| <> 或 != | 不等于 | 5<>6 | true |\n| > | | | |\n| < | | | |\n| <= | | | |\n| >= | | | |\n| !< | 不小于 | | |\n| !> | 不大于 | | |\n\n\n\n## 主键\n\n主键:对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。\n\n\n\n在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,`students`表的两行记录:\n\n| id | class_id | name | gender | score |\n| :--: | :------: | :--: | :----: | :---: |\n| 1 | 1 | 小明 | M | 90 |\n| 2 | 1 | 小红 | F | 95 |\n\n每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。\n\n假设我们把`name`字段作为主键,那么通过名字`小明`或`小红`就能唯一确定一条记录。但是,这么设定,就没法存储**
同名 **的同学了,因为插入相同主键的两条记录是不被允许的。\n\n\n\n对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。\n\n\n\n### 联合主键\n\n关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。\n\n对于联合主键,允许一列有重复,只要不是所有主键列都重复即可\n\n\n\n\n\n## 外键(了解)\n\n\n\n当我们用主键唯一标识记录时,我们就可以在`students`表中确定任意一个学生的记录:\n\n| id | name | other columns... |\n| :--- | :--- | :--------------- |\n| 1 | 小明 | ... |\n| 2 | 小红 | ... |\n\n我们还可以在`classes`表中确定任意一个班级记录:\n\n| id | name | other columns... |\n| :--- | :--- | :--------------- |\n| 1 | 一班 | ... |\n| 2 | 二班 | ... |\n\n但是我们如何确定`students`表的一条记录,例如,`id=1`的小明,属于哪个班级呢?\n\n\n\n由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“
一对多 ”,即一个`classes`的记录可以对应多个`students`表的记录。\n\n\n\n为了表达这种一对多的关系,我们需要在`students`表中加入一列`class_id`,让它的值与`classes`表的某条记录相对应:\n\n| id | class_id | name | other columns... |\n| :--- | :------- | :--- | :--------------- |\n| 1 | 1 | 小明 | ... |\n| 2 | 1 | 小红 | ... |\n| 5 | 2 | 小白 | ... |\n\n这样,我们就可以根据`class_id`这个列直接定位出一个`students`表的记录应该对应到`classes`的哪条记录。\n\n\n\n\n\n给一张表 `students` 添加外键约束:\n\n```SQL\nALTER TABLE students\nADD CONSTRAINT fk_class_id #外键约束的名称`fk_class_id`可以任意\nFOREIGN KEY (class_id) # 指定了`class_id`作为外键\nREFERENCES classes (id); #指定了这个外键将关联到`classes`表的`id`列(即`classes`表的主键)\n```\n\n\n\n\n\n添加了外键的表(`students`)为从表,被外键引用的表(`classes`)为主表,要删除主表前需先删除从表\n\n> 注意:以上外键是数据库级别外键,不推荐使用\n>\n> 最佳实现:\n>\n> - 数据库就是单纯的表,只有行(记录)、列(字段)\n> - 想使用多张表数据,想用外键(用程序如Java语言实现)\n\n\n\n## DML语言(全部记住)\n\n即数据操作语言\n\n- 插入(增)一行(记录):insert into...values...\n\n```mysql\ninsert into `表名`([字段名1,字段名2,...]) values ('值1a', '值1b'), ('值2a', '值2b'), ...;\n\n例:\n\nINSERT INTO `student` ( `id`, `name` )\nVALUES ( 1001, '张三' ), (1002, '李四'), (1003, '王五');\n```\n\n![](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-26-08_image-20210419143255520.png)\n\n- 删除一列(字段):\n - delete\n - truncate\n - 相同:都会删除记录,表的结构和索引、约束不会变\n - 区别:\n 1. truncate 重新设置自增列,计数器会归零;delete不会影响自增\n 2. truncate 不会影响事务\n\n```mysql\n-- 删除一条记录\ndelete from `student` where id = '1001';\n\n-- 删除所有记录(不建议这样写)\ndelete from `student`;\n\n```\n\n```mysql\n-- 清空表的所有记录,但表的结构和索引不变\ntruncate table `student`;\n```\n\n> 对不同引擎的数据库使用`delete`语句后,重启数据库,现象:(了解)\n>\n> - InnoDB:自增列会从1开始(因为保存在内存中)\n> - MyISAM:继续从上一个自增量开始(保存在文件中)\n\n\n\n\n\n- 修改:update...set...\n\n```mysql\n-- 用where指定条件\nupdate `student` set `name`='wangwu' where `id` = `1001`;\n\n-- 不指定条件情况下,会改动所有行(即记录)\nupdate `student` set `name`='wangwu';\n\n-- 修改多个字段, 用逗号隔开\nupdate `student` set `name`='wangwu', `age`=20 where `id` = `1001`;\n\n-- 通过多个条件定位数据, 用逻辑运算符and、or...\nupdate `student` set `name`='wangwu' where `id` = `1001` and `age` = 18;\n```\n\n> 注意 **【=】** 含义:\n>\n> 在set这样的语句后面表示赋值;\n>\n> 在where后面表示关系运算符,表示比较;\n\n\n\n## DQL查询数据(最重要)\n\nDQL(data query language:数据查询语言)\n\n- 所有查询(简单、复杂)都用它\n- 数据库中最核心、重要的语句\n- 使用频率最高的语句\n\n### 基本查询操作 select\n\n1. 查询字段\n\n ```mysql\n -- 查询全部字段 select 字段 from 表\n select * from `student`;\n \n -- 查询指定字段 select 字段1[, 字段2, ...] from 表;\n select name, age from student;\n \n -- 别名,给结果(字段、表。。。)起个名字 as, as也可以省略不写\n select name as `学生姓名`, age as `年龄` from `student` as `s`;\n \n -- 函数concat(a, b): 连接a、b字符串\n select concat('姓名:', name) as 新名字 from student;\n ```\n\n \n\n2. 去重 `distinct`\n\n ```mysql\n -- 去除select后重复的数据,只留下一条\n select distinct `age` from student;\n ```\n\n3. 其他\n\n ```mysql\n -- 查询系统版本\n select version(); -- version()是个函数\n \n -- 计算\n select 100*3-1 as 计算结果;\n \n -- 所有学员考试成绩 +1 分\n select `name`, `grade` + 1 as 提成1分后 from `student`; \n \n -- 查询系统内置变量值\n select @@auto_increment_increment; -- 查询自增步长\n \n ```\n\n数据库的表达式:文本值、列、null、函数、计算表达式、系统变量。。。\n\n```mysql\nselect 表达式 from 表;\n```\n\n\n\n### where语句\n\n检索数据中符合条件的值\n\n| 逻辑运算符 | 语法 | 描述 |\n| :--------- | :------------------ | :-------------- |\n| and `&&` | a and b、 `a&&b` | 2个都为真返回真 |\n| or `||` | a or b 、 `a||b` | 。。。 |\n| not `!` | not a、 `!a` | 。。。 |\n\n```mysql\nSELECT\n\tid AS 学号,\n\t`name` AS 姓名 \nFROM\n\tstudent \nWHERE\n\tgrade > 80 \n\tAND age < 21;\n```\n\n```mysql\nSELECT\n\t`id` AS 学号,\n\t`name` AS 姓名,\n\t`grade` as 成绩\nFROM\n\tstudent \nWHERE\n\tgrade BETWEEN 70 AND 90;\n```\n\n### 模糊查询\n\n| 运算符 | 语法 | 描述 |\n| ----------- | --------------------- | --------------------------------------- |\n| is null | a is null | 如果a是null,则为真 |\n| is not null | a is not null | 如果a不是null,则为真 |\n| between | a between b and c | 若a在b和c之间,结果为真 |\n| like | a like b | sql匹配,若a匹配b,结果为真 |\n| in | a in(a1,a2,a3,a4,...) | 若a是a1,a2,a3,a4...中的一个值,结果为真 |\n\nlike:通常结合 `%`、`_` 来使用\n\n```mysql\n-- 查找以王字开头的name \n-- “%”表示【任意多个】【任意的】字符,“_”表示任意【一个】字符\nSELECT `id`, `name` FROM student\nWHERE `name` LIKE '王%'; -- 表示以【王】字开头\n\n\n-- 查找以王字开头且【名字为2个字】的name\nSELECT `id`, `name` FROM student\nWHERE `name` LIKE '王__'; -- 两个下划线 \n\n\n-- 查找生日不是null的\nSELECT `id`, `name` FROM student\nWHERE `birthday` is not null;\n```\n\n\n\n### 联表查询 join on\n\n同时在2个表中查找数据(必须要有交集)\n\n\n\n
\n\n\n\n```mysql\n/**\n有2张表(都有id、name字段):\n1. `student`:班级学生基本信息\n2. `exam`:年级学生考试信息\n\n【问题:查找出本班级学生的考试信息】\n*/\n\n-- =====================================\n-- 取交集 inner join\nselect s.id as 学号, s.name as 姓名 \nfrom student as s inner join exam as e -- as可以省略\non s.id = e.id;\n\n-- =====================================\n-- 仅左边的表有,left join \nSELECT\n\ts.id 学号, -- 省略as\n\ts.NAME 姓名 \nFROM\n\tstudent s\n\tLEFT JOIN exam e ON s.NAME = e.NAME \nWHERE\n\te.`name` IS NULL;\n-- =====================================\n\n-- 两个表的所有人 \n\n```\n\n> mysql 不支持 full outer join\n>\n\n\n\n\n\n### 自连接及联表查询\n\n自己的表和自己的表连接,核心:1张表拆为2张一样的表\n\n\n\n### 分页和排序\n\n- 排序 **order by**\n\n - 升序用 ASC, 降序用DESC\n\n - order by 表示查询结果怎么排(升序、降序),通过哪个字段排\n\n 比如:\n\n ```mysql\n -- order 字段 ASC 升序\n -- order 字段 DESC 降序\n SELECT * FROM exam ORDER BY `grade` ASC; -- 成绩按升序排\n ```\n\n- 分页 **limit**:可以缓解数据库压力、给人体验更好、瀑布流\n\n - 语法:\n\n ```mysql\n -- limit 起始下标(从0开始) 页面大小\n \n -- 从第0条数据开始,显示2条数据\n SELECT * FROM exam ORDER BY `id` DESC LIMIT 0,2;\n ```\n\n \n\n\n\n### 子查询与嵌套查询\n\nwhere 表达式【这个值是计算出来的】\n\n即:where 后面还跟select语句\n\n\n\n## MySQL常用函数\n\n### 数学运算\n\n```mysql\nabs()\n\nceiling() -- 向上取整 9.1 -> 10\nfloor() -- 向下取整 9.1 -> 9\n\nrand() -- 返回0~1随机数\n\nsign() -- 判断一个数符号 0->0 负数->-1 正数->1\n```\n\n\n\n### 字符串\n\n```mysql\nchar_length() -- 字符串长度\n\nconcat('a', 'b', 'c') -- 字符串连接 返回abc\n\n-- 字符索引从1开始,替换时,把第n1开始的共len个字符替换为str2\ninsert(str1, n1, len, str2)\n-- 如\nselect insert('abcdef', 2, 3, '5555'); -- 返回 a5555ef\n\n-- 将一个字符串str1中的指定子字符串substr替换为新字符串str2\nreplace(str1, substr, str2)\n\n-- 截取指定字符串\nsubstr(str, a, len); -- 截取str中第a个开始,共len个字符的子串\n\n-- 大小写转换\nupper()\nlower()\n\n-- 返回在str第一次出现的字符串substr的索引\ninstr(str, substr) -- 没有则返回0\n\n-- 反转字符串\nreverse(str)\n\n```\n\n### 时间-日期函数\n\n```mysql\n-- 当前日期\nSELECT CURRENT_DATE()\nSELECT CURDATE()\n\nSELECT NOW(); -- 当前时间 2021-04-20 20:05:44\nSELECT LOCALTIME(); -- 本地时间\nSELECT SYSDATE(); -- 系统时间\n\nSELECT YEAR(NOW()); -- 年\nSELECT MONTH(NOW()); -- 月\nSELECT DAY(NOW());\t-- 日\nSELECT HOUR(NOW());\t-- 时\nSELECT MINUTE(NOW()); -- 分\nSELECT SECOND(NOW()); -- 秒\n```\n\n\n\n### 系统函数\n\n```mysql\nSELECT SYSTEM_USER(); -- 当前系统用户\nSELECT USER()\t\t\t-- 当前用户\nSELECT VERSION()\t\t-- 版本号\n```\n\n\n\n### 聚合函数(常用)\n\n```mysql\ncount() -- 计数有多少个记录\n\tselect count(字段) from tablename; -- 忽略所有null值\n\tselect count(*) from tablename; -- 不会忽略null值,计算行数\n\tselect count(1) from tablename; -- 不会忽略null值,与上一个基本相同\n\t\nsum()\n\tselect sum(字段) from tablename;\n\t\navg()\n\tselect avg(字段) from tablename;\n\nmax()\n\tselect max(字段) from tablename;\n\nmin()\n\tselect min(字段) from tablename;\n```\n\n\n\n\n\n> **select**语法总结\n>\n> ![image-20210420202906345](https://gitee.com/ajream/images/raw/master/img/2021-04-20 20-29-07_image-20210420202906345.png)\n\n\n\n## 数据库级别的md5摘要算法\n\n**MD5消息摘要算法**(英语:MD5 Message-Digest Algorithm),一种被广泛使用的【密码散列函数】\n\nmd5不可逆,具体值的md5值是一样的\n\nmd5破解网站破解原理,其背后有一个巨大的字典库,你给它一个md5值,它就会搜索字典中的哪个字符串经过md5转换后与你输入的一样\n\nSQL使用函数 `MD5()`进行加密\n\n\n\n","slug":"MySQL/3-数据管理","published":1,"updated":"2021-09-10T03:01:55.065Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6or001nakved1j01zfv","content":"
MySQL数据管理 关系操作符 \n
\n\n\n运算符 \n含义 \n例子 \n结果 \n \n \n\n\n= \n等于 \n5=6 \nfalse \n \n\n<> 或 != \n不等于 \n5<>6 \ntrue \n \n\n> \n \n \n \n \n\n< \n \n \n \n \n\n<= \n \n \n \n \n\n>= \n \n \n \n \n\n!< \n不小于 \n \n \n \n\n!> \n不大于 \n \n \n \n
\n
\n
主键 主键:对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。
\n
在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,students
表的两行记录:
\n
\n
\n\n\nid \nclass_id \nname \ngender \nscore \n \n \n\n\n1 \n1 \n小明 \nM \n90 \n \n\n2 \n1 \n小红 \nF \n95 \n \n \n
\n
\n
每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。
\n
假设我们把name
字段作为主键,那么通过名字小明
或小红
就能唯一确定一条记录。但是,这么设定,就没法存储同名 的同学了,因为插入相同主键的两条记录是不被允许的。
\n
对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。
\n
联合主键 关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。
\n
对于联合主键,允许一列有重复,只要不是所有主键列都重复即可
\n
外键(了解) 当我们用主键唯一标识记录时,我们就可以在students
表中确定任意一个学生的记录:
\n
\n
\n\n\nid \nname \nother columns… \n \n \n\n\n1 \n小明 \n… \n \n\n2 \n小红 \n… \n \n \n
\n
\n
我们还可以在classes
表中确定任意一个班级记录:
\n
\n
\n\n\nid \nname \nother columns… \n \n \n\n\n1 \n一班 \n… \n \n\n2 \n二班 \n… \n \n \n
\n
\n
但是我们如何确定students
表的一条记录,例如,id=1
的小明,属于哪个班级呢?
\n
由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多 ”,即一个classes
的记录可以对应多个students
表的记录。
\n
为了表达这种一对多的关系,我们需要在students
表中加入一列class_id
,让它的值与classes
表的某条记录相对应:
\n
\n
\n\n\nid \nclass_id \nname \nother columns… \n \n \n\n\n1 \n1 \n小明 \n… \n \n\n2 \n1 \n小红 \n… \n \n\n5 \n2 \n小白 \n… \n \n \n
\n
\n
这样,我们就可以根据class_id
这个列直接定位出一个students
表的记录应该对应到classes
的哪条记录。
\n
给一张表 students
添加外键约束:
\n
1 2 3 4 ALTER TABLE studentsADD CONSTRAINT fk_class_id #外键约束的名称`fk_class_id`可以任意FOREIGN KEY (class_id) # 指定了`class_id`作为外键REFERENCES classes (id); #指定了这个外键将关联到`classes`表的`id`列(即`classes`表的主键)
\n
添加了外键的表(students
)为从表,被外键引用的表(classes
)为主表,要删除主表前需先删除从表
\n
\n注意:以上外键是数据库级别外键,不推荐使用
\n最佳实现:
\n\n数据库就是单纯的表,只有行(记录)、列(字段) \n想使用多张表数据,想用外键(用程序如Java语言实现) \n \n \n
DML语言(全部记住) 即数据操作语言
\n
\n插入(增)一行(记录):insert into…values… \n \n
1 2 3 4 5 6 insert into `表名`([字段名1,字段名2,...]) values ('值1a', '值1b'), ('值2a', '值2b'), ...; 例: INSERT INTO `student` ( `id`, `name` ) VALUES ( 1001, '张三' ), (1002, '李四'), (1003, '王五');
\n
\n
\n删除一列(字段):\ndelete \ntruncate \n相同:都会删除记录,表的结构和索引、约束不会变 \n区别:\ntruncate 重新设置自增列,计数器会归零;delete不会影响自增 \ntruncate 不会影响事务 \n \n \n \n \n \n
1 2 3 4 5 6 -- 删除一条记录 delete from `student` where id = '1001'; -- 删除所有记录(不建议这样写) delete from `student`;
\n
1 2 -- 清空表的所有记录,但表的结构和索引不变 truncate table `student`;
\n
\n对不同引擎的数据库使用delete
语句后,重启数据库,现象:(了解)
\n\nInnoDB:自增列会从1开始(因为保存在内存中) \nMyISAM:继续从上一个自增量开始(保存在文件中) \n \n \n
\n
1 2 3 4 5 6 7 8 9 10 11 -- 用where指定条件 update `student` set `name`='wangwu' where `id` = `1001`; -- 不指定条件情况下,会改动所有行(即记录) update `student` set `name`='wangwu'; -- 修改多个字段, 用逗号隔开 update `student` set `name`='wangwu', `age`=20 where `id` = `1001`; -- 通过多个条件定位数据, 用逻辑运算符and、or... update `student` set `name`='wangwu' where `id` = `1001` and `age` = 18;
\n
\n注意 【=】 含义:
\n在set这样的语句后面表示赋值;
\n在where后面表示关系运算符,表示比较;
\n \n
DQL查询数据(最重要) DQL(data query language:数据查询语言)
\n
\n所有查询(简单、复杂)都用它 \n数据库中最核心、重要的语句 \n使用频率最高的语句 \n \n
基本查询操作 select \n查询字段
\n1 2 3 4 5 6 7 8 9 10 11 -- 查询全部字段 select 字段 from 表 select * from `student`; -- 查询指定字段 select 字段1[, 字段2, ...] from 表; select name, age from student; -- 别名,给结果(字段、表。。。)起个名字 as, as也可以省略不写 select name as `学生姓名`, age as `年龄` from `student` as `s`; -- 函数concat(a, b): 连接a、b字符串 select concat('姓名:', name) as 新名字 from student;
\n \n \n
\n去重 distinct
\n1 2 -- 去除select后重复的数据,只留下一条 select distinct `age` from student;
\n \n其他
\n1 2 3 4 5 6 7 8 9 10 11 12 -- 查询系统版本 select version(); -- version()是个函数 -- 计算 select 100*3-1 as 计算结果; -- 所有学员考试成绩 +1 分 select `name`, `grade` + 1 as 提成1分后 from `student`; -- 查询系统内置变量值 select @@auto_increment_increment; -- 查询自增步长
\n \n \n
数据库的表达式:文本值、列、null、函数、计算表达式、系统变量。。。
\n
\n
where语句 检索数据中符合条件的值
\n
\n
\n\n\n逻辑运算符 \n语法 \n描述 \n \n \n\n\nand &&
\na and b、 a&&b
\n2个都为真返回真 \n \n\nor ` \n \n` \na or b 、 `a \n \nb` \n。。。 \n \n\nnot !
\nnot a、 !a
\n。。。 \n \n \n
\n
\n
1 2 3 4 5 6 7 8 SELECT \tid AS 学号, \t`name` AS 姓名 FROM \tstudent WHERE \tgrade > 80 \tAND age < 21;
\n
1 2 3 4 5 6 7 8 SELECT \t`id` AS 学号, \t`name` AS 姓名, \t`grade` as 成绩 FROM \tstudent WHERE \tgrade BETWEEN 70 AND 90;
\n
模糊查询 \n
\n\n\n运算符 \n语法 \n描述 \n \n \n\n\nis null \na is null \n如果a是null,则为真 \n \n\nis not null \na is not null \n如果a不是null,则为真 \n \n\nbetween \na between b and c \n若a在b和c之间,结果为真 \n \n\nlike \na like b \nsql匹配,若a匹配b,结果为真 \n \n\nin \na in(a1,a2,a3,a4,…) \n若a是a1,a2,a3,a4…中的一个值,结果为真 \n \n \n
\n
\n
like:通常结合 %
、_
来使用
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 -- 查找以王字开头的name -- “%”表示【任意多个】【任意的】字符,“_”表示任意【一个】字符 SELECT `id`, `name` FROM student WHERE `name` LIKE '王%'; -- 表示以【王】字开头 -- 查找以王字开头且【名字为2个字】的name SELECT `id`, `name` FROM student WHERE `name` LIKE '王__'; -- 两个下划线 -- 查找生日不是null的 SELECT `id`, `name` FROM student WHERE `birthday` is not null;
\n
联表查询 join on 同时在2个表中查找数据(必须要有交集)
\n
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 /** 有2张表(都有id、name字段): 1. `student`:班级学生基本信息 2. `exam`:年级学生考试信息 【问题:查找出本班级学生的考试信息】 */ -- ===================================== -- 取交集 inner join select s.id as 学号, s.name as 姓名 from student as s inner join exam as e -- as可以省略 on s.id = e.id; -- ===================================== -- 仅左边的表有,left join SELECT \ts.id 学号, -- 省略as \ts.NAME 姓名 FROM \tstudent s \tLEFT JOIN exam e ON s.NAME = e.NAME WHERE \te.`name` IS NULL; -- ===================================== -- 两个表的所有人
\n
\nmysql 不支持 full outer join
\n \n
自连接及联表查询 自己的表和自己的表连接,核心:1张表拆为2张一样的表
\n
分页和排序 \n
子查询与嵌套查询 where 表达式【这个值是计算出来的】
\n
即:where 后面还跟select语句
\n
MySQL常用函数 数学运算 1 2 3 4 5 6 7 8 abs() ceiling() -- 向上取整 9.1 -> 10 floor() -- 向下取整 9.1 -> 9 rand() -- 返回0~1随机数 sign() -- 判断一个数符号 0->0 负数->-1 正数->1
\n
字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 char_length() -- 字符串长度 concat('a', 'b', 'c') -- 字符串连接 返回abc -- 字符索引从1开始,替换时,把第n1开始的共len个字符替换为str2 insert(str1, n1, len, str2) -- 如 select insert('abcdef', 2, 3, '5555'); -- 返回 a5555ef -- 将一个字符串str1中的指定子字符串substr替换为新字符串str2 replace(str1, substr, str2) -- 截取指定字符串 substr(str, a, len); -- 截取str中第a个开始,共len个字符的子串 -- 大小写转换 upper() lower() -- 返回在str第一次出现的字符串substr的索引 instr(str, substr) -- 没有则返回0 -- 反转字符串 reverse(str)
\n
时间-日期函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -- 当前日期 SELECT CURRENT_DATE() SELECT CURDATE() SELECT NOW(); -- 当前时间 2021-04-20 20:05:44 SELECT LOCALTIME(); -- 本地时间 SELECT SYSDATE(); -- 系统时间 SELECT YEAR(NOW()); -- 年 SELECT MONTH(NOW()); -- 月 SELECT DAY(NOW());\t-- 日 SELECT HOUR(NOW());\t-- 时 SELECT MINUTE(NOW()); -- 分 SELECT SECOND(NOW()); -- 秒
\n
系统函数 1 2 3 SELECT SYSTEM_USER(); -- 当前系统用户 SELECT USER()\t\t\t-- 当前用户 SELECT VERSION()\t\t-- 版本号
\n
聚合函数(常用) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 count() -- 计数有多少个记录 \tselect count(字段) from tablename; -- 忽略所有null值 \tselect count(*) from tablename; -- 不会忽略null值,计算行数 \tselect count(1) from tablename; -- 不会忽略null值,与上一个基本相同 \t sum() \tselect sum(字段) from tablename; \t avg() \tselect avg(字段) from tablename; max() \tselect max(字段) from tablename; min() \tselect min(字段) from tablename;
\n
\nselect 语法总结
\n
\n \n
数据库级别的md5摘要算法 MD5消息摘要算法 (英语:MD5 Message-Digest Algorithm),一种被广泛使用的【密码散列函数】
\n
md5不可逆,具体值的md5值是一样的
\n
md5破解网站破解原理,其背后有一个巨大的字典库,你给它一个md5值,它就会搜索字典中的哪个字符串经过md5转换后与你输入的一样
\n
SQL使用函数 MD5()
进行加密
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"
MySQL数据管理 关系操作符 \n
\n\n\n运算符 \n含义 \n例子 \n结果 \n \n \n\n\n= \n等于 \n5=6 \nfalse \n \n\n<> 或 != \n不等于 \n5<>6 \ntrue \n \n\n> \n \n \n \n \n\n< \n \n \n \n \n\n<= \n \n \n \n \n\n>= \n \n \n \n \n\n!< \n不小于 \n \n \n \n\n!> \n不大于 \n \n \n \n
\n
\n
主键 主键:对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。
\n
在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,students
表的两行记录:
\n
\n
\n\n\nid \nclass_id \nname \ngender \nscore \n \n \n\n\n1 \n1 \n小明 \nM \n90 \n \n\n2 \n1 \n小红 \nF \n95 \n \n \n
\n
\n
每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。
\n
假设我们把name
字段作为主键,那么通过名字小明
或小红
就能唯一确定一条记录。但是,这么设定,就没法存储同名 的同学了,因为插入相同主键的两条记录是不被允许的。
\n
对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。
\n
联合主键 关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。
\n
对于联合主键,允许一列有重复,只要不是所有主键列都重复即可
\n
外键(了解) 当我们用主键唯一标识记录时,我们就可以在students
表中确定任意一个学生的记录:
\n
\n
\n\n\nid \nname \nother columns… \n \n \n\n\n1 \n小明 \n… \n \n\n2 \n小红 \n… \n \n \n
\n
\n
我们还可以在classes
表中确定任意一个班级记录:
\n
\n
\n\n\nid \nname \nother columns… \n \n \n\n\n1 \n一班 \n… \n \n\n2 \n二班 \n… \n \n \n
\n
\n
但是我们如何确定students
表的一条记录,例如,id=1
的小明,属于哪个班级呢?
\n
由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多 ”,即一个classes
的记录可以对应多个students
表的记录。
\n
为了表达这种一对多的关系,我们需要在students
表中加入一列class_id
,让它的值与classes
表的某条记录相对应:
\n
\n
\n\n\nid \nclass_id \nname \nother columns… \n \n \n\n\n1 \n1 \n小明 \n… \n \n\n2 \n1 \n小红 \n… \n \n\n5 \n2 \n小白 \n… \n \n \n
\n
\n
这样,我们就可以根据class_id
这个列直接定位出一个students
表的记录应该对应到classes
的哪条记录。
\n
给一张表 students
添加外键约束:
\n
1 2 3 4 ALTER TABLE studentsADD CONSTRAINT fk_class_id #外键约束的名称`fk_class_id`可以任意FOREIGN KEY (class_id) # 指定了`class_id`作为外键REFERENCES classes (id); #指定了这个外键将关联到`classes`表的`id`列(即`classes`表的主键)
\n
添加了外键的表(students
)为从表,被外键引用的表(classes
)为主表,要删除主表前需先删除从表
\n
\n注意:以上外键是数据库级别外键,不推荐使用
\n最佳实现:
\n\n数据库就是单纯的表,只有行(记录)、列(字段) \n想使用多张表数据,想用外键(用程序如Java语言实现) \n \n \n
DML语言(全部记住) 即数据操作语言
\n
\n插入(增)一行(记录):insert into…values… \n \n
1 2 3 4 5 6 insert into `表名`([字段名1,字段名2,...]) values ('值1a', '值1b'), ('值2a', '值2b'), ...; 例: INSERT INTO `student` ( `id`, `name` ) VALUES ( 1001, '张三' ), (1002, '李四'), (1003, '王五');
\n
\n
\n删除一列(字段):\ndelete \ntruncate \n相同:都会删除记录,表的结构和索引、约束不会变 \n区别:\ntruncate 重新设置自增列,计数器会归零;delete不会影响自增 \ntruncate 不会影响事务 \n \n \n \n \n \n
1 2 3 4 5 6 -- 删除一条记录 delete from `student` where id = '1001'; -- 删除所有记录(不建议这样写) delete from `student`;
\n
1 2 -- 清空表的所有记录,但表的结构和索引不变 truncate table `student`;
\n
\n对不同引擎的数据库使用delete
语句后,重启数据库,现象:(了解)
\n\nInnoDB:自增列会从1开始(因为保存在内存中) \nMyISAM:继续从上一个自增量开始(保存在文件中) \n \n \n
\n
1 2 3 4 5 6 7 8 9 10 11 -- 用where指定条件 update `student` set `name`='wangwu' where `id` = `1001`; -- 不指定条件情况下,会改动所有行(即记录) update `student` set `name`='wangwu'; -- 修改多个字段, 用逗号隔开 update `student` set `name`='wangwu', `age`=20 where `id` = `1001`; -- 通过多个条件定位数据, 用逻辑运算符and、or... update `student` set `name`='wangwu' where `id` = `1001` and `age` = 18;
\n
\n注意 【=】 含义:
\n在set这样的语句后面表示赋值;
\n在where后面表示关系运算符,表示比较;
\n \n
DQL查询数据(最重要) DQL(data query language:数据查询语言)
\n
\n所有查询(简单、复杂)都用它 \n数据库中最核心、重要的语句 \n使用频率最高的语句 \n \n
基本查询操作 select \n查询字段
\n1 2 3 4 5 6 7 8 9 10 11 -- 查询全部字段 select 字段 from 表 select * from `student`; -- 查询指定字段 select 字段1[, 字段2, ...] from 表; select name, age from student; -- 别名,给结果(字段、表。。。)起个名字 as, as也可以省略不写 select name as `学生姓名`, age as `年龄` from `student` as `s`; -- 函数concat(a, b): 连接a、b字符串 select concat('姓名:', name) as 新名字 from student;
\n \n \n
\n去重 distinct
\n1 2 -- 去除select后重复的数据,只留下一条 select distinct `age` from student;
\n \n其他
\n1 2 3 4 5 6 7 8 9 10 11 12 -- 查询系统版本 select version(); -- version()是个函数 -- 计算 select 100*3-1 as 计算结果; -- 所有学员考试成绩 +1 分 select `name`, `grade` + 1 as 提成1分后 from `student`; -- 查询系统内置变量值 select @@auto_increment_increment; -- 查询自增步长
\n \n \n
数据库的表达式:文本值、列、null、函数、计算表达式、系统变量。。。
\n
\n
where语句 检索数据中符合条件的值
\n
\n
\n\n\n逻辑运算符 \n语法 \n描述 \n \n \n\n\nand &&
\na and b、 a&&b
\n2个都为真返回真 \n \n\nor ` \n \n` \na or b 、 `a \n \nb` \n。。。 \n \n\nnot !
\nnot a、 !a
\n。。。 \n \n \n
\n
\n
1 2 3 4 5 6 7 8 SELECT \tid AS 学号, \t`name` AS 姓名 FROM \tstudent WHERE \tgrade > 80 \tAND age < 21;
\n
1 2 3 4 5 6 7 8 SELECT \t`id` AS 学号, \t`name` AS 姓名, \t`grade` as 成绩 FROM \tstudent WHERE \tgrade BETWEEN 70 AND 90;
\n
模糊查询 \n
\n\n\n运算符 \n语法 \n描述 \n \n \n\n\nis null \na is null \n如果a是null,则为真 \n \n\nis not null \na is not null \n如果a不是null,则为真 \n \n\nbetween \na between b and c \n若a在b和c之间,结果为真 \n \n\nlike \na like b \nsql匹配,若a匹配b,结果为真 \n \n\nin \na in(a1,a2,a3,a4,…) \n若a是a1,a2,a3,a4…中的一个值,结果为真 \n \n \n
\n
\n
like:通常结合 %
、_
来使用
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 -- 查找以王字开头的name -- “%”表示【任意多个】【任意的】字符,“_”表示任意【一个】字符 SELECT `id`, `name` FROM student WHERE `name` LIKE '王%'; -- 表示以【王】字开头 -- 查找以王字开头且【名字为2个字】的name SELECT `id`, `name` FROM student WHERE `name` LIKE '王__'; -- 两个下划线 -- 查找生日不是null的 SELECT `id`, `name` FROM student WHERE `birthday` is not null;
\n
联表查询 join on 同时在2个表中查找数据(必须要有交集)
\n
\n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 /** 有2张表(都有id、name字段): 1. `student`:班级学生基本信息 2. `exam`:年级学生考试信息 【问题:查找出本班级学生的考试信息】 */ -- ===================================== -- 取交集 inner join select s.id as 学号, s.name as 姓名 from student as s inner join exam as e -- as可以省略 on s.id = e.id; -- ===================================== -- 仅左边的表有,left join SELECT \ts.id 学号, -- 省略as \ts.NAME 姓名 FROM \tstudent s \tLEFT JOIN exam e ON s.NAME = e.NAME WHERE \te.`name` IS NULL; -- ===================================== -- 两个表的所有人
\n
\nmysql 不支持 full outer join
\n \n
自连接及联表查询 自己的表和自己的表连接,核心:1张表拆为2张一样的表
\n
分页和排序 \n
子查询与嵌套查询 where 表达式【这个值是计算出来的】
\n
即:where 后面还跟select语句
\n
MySQL常用函数 数学运算 1 2 3 4 5 6 7 8 abs() ceiling() -- 向上取整 9.1 -> 10 floor() -- 向下取整 9.1 -> 9 rand() -- 返回0~1随机数 sign() -- 判断一个数符号 0->0 负数->-1 正数->1
\n
字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 char_length() -- 字符串长度 concat('a', 'b', 'c') -- 字符串连接 返回abc -- 字符索引从1开始,替换时,把第n1开始的共len个字符替换为str2 insert(str1, n1, len, str2) -- 如 select insert('abcdef', 2, 3, '5555'); -- 返回 a5555ef -- 将一个字符串str1中的指定子字符串substr替换为新字符串str2 replace(str1, substr, str2) -- 截取指定字符串 substr(str, a, len); -- 截取str中第a个开始,共len个字符的子串 -- 大小写转换 upper() lower() -- 返回在str第一次出现的字符串substr的索引 instr(str, substr) -- 没有则返回0 -- 反转字符串 reverse(str)
\n
时间-日期函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -- 当前日期 SELECT CURRENT_DATE() SELECT CURDATE() SELECT NOW(); -- 当前时间 2021-04-20 20:05:44 SELECT LOCALTIME(); -- 本地时间 SELECT SYSDATE(); -- 系统时间 SELECT YEAR(NOW()); -- 年 SELECT MONTH(NOW()); -- 月 SELECT DAY(NOW());\t-- 日 SELECT HOUR(NOW());\t-- 时 SELECT MINUTE(NOW()); -- 分 SELECT SECOND(NOW()); -- 秒
\n
系统函数 1 2 3 SELECT SYSTEM_USER(); -- 当前系统用户 SELECT USER()\t\t\t-- 当前用户 SELECT VERSION()\t\t-- 版本号
\n
聚合函数(常用) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 count() -- 计数有多少个记录 \tselect count(字段) from tablename; -- 忽略所有null值 \tselect count(*) from tablename; -- 不会忽略null值,计算行数 \tselect count(1) from tablename; -- 不会忽略null值,与上一个基本相同 \t sum() \tselect sum(字段) from tablename; \t avg() \tselect avg(字段) from tablename; max() \tselect max(字段) from tablename; min() \tselect min(字段) from tablename;
\n
\nselect 语法总结
\n
\n \n
数据库级别的md5摘要算法 MD5消息摘要算法 (英语:MD5 Message-Digest Algorithm),一种被广泛使用的【密码散列函数】
\n
md5不可逆,具体值的md5值是一样的
\n
md5破解网站破解原理,其背后有一个巨大的字典库,你给它一个md5值,它就会搜索字典中的哪个字符串经过md5转换后与你输入的一样
\n
SQL使用函数 MD5()
进行加密
\n"},{"title":"MySql(5)-索引","abbrlink":"683d1a78","date":"2021-06-09T03:53:17.000Z","description":"MySQL数据库索引介绍与使用,索引可以提高查询效率","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n# 索引\n\n**索引是**帮助MySQL高效获取数据的**数据结构**\n\n## 索引分类\n\n- 主键索引(primary key)\n\n - 唯一的标识,主键不可重复,只能有一个列为主键\n- 唯一索引(unique key)\n - 避免重复的列出现,唯一索引可以重复,多个列均可以标识为唯一索引\n- 常规索引(key/index)\n - 默认的,index、key关键字来设置\n- 全文索引(fulltext)\n\n\n\n\n\n## 索引使用\n\n1. 在创建表的时候给字段增加索引\n2. 创建完毕后,增加索引\n\n```mysql\n-- 显示所有索引信息\nshow index from tableA;\n\n-- 增加一个全文索引\nalter table tableA add fulltext index `studentName`;\n\n-- 分析sql执行状况 explain\nexplain select * from tableA;\n\n```\n\n\n\n\n\n## 索引使用原则\n\n- 不是越多越好\n- 不要对进程变动数据加索引\n- 小数据量的表不要添加索引\n- 索引一般加在常用来查询的字段\n\n\n\n## 索引数据结构\n\n- hash类型\n- B-tree类型(innoDB默认索引类型)\n\n\n\n\n\n\n\n\n\n\n\n\n\n","source":"_posts/MySQL/5-索引.md","raw":"---\ntitle: MySql(5)-索引\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: 683d1a78\ndate: 2021-06-09 11:53:17\ndescription: MySQL数据库索引介绍与使用,索引可以提高查询效率\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n# 索引\n\n**索引是**帮助MySQL高效获取数据的**数据结构**\n\n## 索引分类\n\n- 主键索引(primary key)\n\n - 唯一的标识,主键不可重复,只能有一个列为主键\n- 唯一索引(unique key)\n - 避免重复的列出现,唯一索引可以重复,多个列均可以标识为唯一索引\n- 常规索引(key/index)\n - 默认的,index、key关键字来设置\n- 全文索引(fulltext)\n\n\n\n\n\n## 索引使用\n\n1. 在创建表的时候给字段增加索引\n2. 创建完毕后,增加索引\n\n```mysql\n-- 显示所有索引信息\nshow index from tableA;\n\n-- 增加一个全文索引\nalter table tableA add fulltext index `studentName`;\n\n-- 分析sql执行状况 explain\nexplain select * from tableA;\n\n```\n\n\n\n\n\n## 索引使用原则\n\n- 不是越多越好\n- 不要对进程变动数据加索引\n- 小数据量的表不要添加索引\n- 索引一般加在常用来查询的字段\n\n\n\n## 索引数据结构\n\n- hash类型\n- B-tree类型(innoDB默认索引类型)\n\n\n\n\n\n\n\n\n\n\n\n\n\n","slug":"MySQL/5-索引","published":1,"updated":"2021-09-08T13:05:57.536Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ou001qakve1mo0aal1","content":"
索引 索引是 帮助MySQL高效获取数据的数据结构
\n
索引分类 \n主键索引(primary key)
\n\n唯一的标识,主键不可重复,只能有一个列为主键 \n \n \n唯一索引(unique key)\n避免重复的列出现,唯一索引可以重复,多个列均可以标识为唯一索引 \n \n \n常规索引(key/index)\n \n全文索引(fulltext) \n \n
索引使用 \n在创建表的时候给字段增加索引 \n创建完毕后,增加索引 \n \n
1 2 3 4 5 6 7 8 9 -- 显示所有索引信息 show index from tableA; -- 增加一个全文索引 alter table tableA add fulltext index `studentName`; -- 分析sql执行状况 explain explain select * from tableA;
\n
索引使用原则 \n不是越多越好 \n不要对进程变动数据加索引 \n小数据量的表不要添加索引 \n索引一般加在常用来查询的字段 \n \n
索引数据结构 \nhash类型 \nB-tree类型(innoDB默认索引类型) \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"
索引 索引是 帮助MySQL高效获取数据的数据结构
\n
索引分类 \n主键索引(primary key)
\n\n唯一的标识,主键不可重复,只能有一个列为主键 \n \n \n唯一索引(unique key)\n避免重复的列出现,唯一索引可以重复,多个列均可以标识为唯一索引 \n \n \n常规索引(key/index)\n \n全文索引(fulltext) \n \n
索引使用 \n在创建表的时候给字段增加索引 \n创建完毕后,增加索引 \n \n
1 2 3 4 5 6 7 8 9 -- 显示所有索引信息 show index from tableA; -- 增加一个全文索引 alter table tableA add fulltext index `studentName`; -- 分析sql执行状况 explain explain select * from tableA;
\n
索引使用原则 \n不是越多越好 \n不要对进程变动数据加索引 \n小数据量的表不要添加索引 \n索引一般加在常用来查询的字段 \n \n
索引数据结构 \nhash类型 \nB-tree类型(innoDB默认索引类型) \n \n"},{"title":"MySql(6)-权限-备份","abbrlink":"d29e4d17","date":"2021-06-10T03:37:39.000Z","description":"关于数据库一些注意事项,如权限管理,备份管理,数据库设计、SQL注入问题","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n\n## 权限管理和备份\n\n\n\n```mysql\n-- 创建用户 create user 用户名 identified by '密码'\ncreate user 'kjn' identified by '123456';\n\n-- 修改当前用户密码\nset password = password('98765');\n\n-- 修改指定用户密码\nset password for '用户名' = password('98765');\n\n-- 重命名用户 rename ... to ...\nrename user 原来名字 to 新名字;\n\n-- 授权用户全部权限 [all privileges]->除了给别人授权,什么都能干\ngrant all privileges on *.* to 用户名;\n\n-- 查看权限\nshow grants for 用户名;\n\n\n-- 撤销权限\nrevoke all privileges on *.* from 用户名;\n\n\n```\n\n\n\n\n\n## 数据库备份\n\n备份方式\n\n- 直接拷贝物理文件\n\n- 在软件中导出\n\n navicat右键点击表导出\n\n- cmd命令行导出\n\n ```mysql\n -- mysqldump -h本机服务\n mysqldump -hlocalhost -u用户名 -p密码 数据库名 表名 > 导出路径/导出文件名\n ```\n\n- 导入(mysql命令行)\n\n ```mysql\n -- 导入D:/a.sql\n mysql > source d:/a.sql;\n ```\n\n \n\n## 规范数据库设计\n\n数据库比较复杂时就要设计\n\n**良好的数据库设计:**\n\n- 节省内存空间\n- 保证数据库完整性\n- 方便开发系统\n\n**软件开发中:**\n\n- 分析需求:分析业务和需要处理的数据库的需求\n- 概要设计:设计关系图E-R图\n\n\n\n**设计数据库步骤**(个人博客)\n\n- 收集信息,分析需求\n - 用户表(用户登录、注销、个人信息,写博客,创建分支)\n - 分类表(文章分类、谁创建的)\n - 文章表\n - 评论表\n - 粉丝表\n - 自定义表(系统信息、某个关键字、字段)\n - 说说表(用户id、内容、发表时间)\n- 标识实体(把需求落实到每个字段)\n- 标识实体之间关系\n - 写博客:user->blog\n - 创建分类:user->catagory\n - 关注:user->user\n - 粉丝链接:links\n - 评论:user->user-blog\n\n\n\n### 三大范式\n\n> 为什么需要数据规范化\n\n- 信息重写\n- 更新异常\n- 插入异常\n - 无法正常显示信息\n- 删除异常\n - 丢失有效信息\n\n\n\n#### 三大范式\n\n1. **第一范式(1NF)**\n\n 原子性:保持列不可再分\n\n2. **第二范式(2NF)**\n\n 前提:满足1NF\n\n 每张表只描述一件事\n\n3. **第三范式(3NF)**\n\n 前提:满足1NF与2NF\n\n 确保数据表中每一列数据都和主键直接相关,而不能间接相关\n\n\n\n#### 规范性和性能问题\n\n关联查询的表不能超过3张表\n\n- 考虑商业化的需求和目标(成本、用户体验),数据库性能更重要\n- 在规范性能的问题时候,需要适当考虑一下规范性\n- 可以故意给某些表增加一些冗余的字段(多表查询变为单表查询)\n- 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)\n\n\n\n\n\n## SQL注入问题\n\nsql存在漏洞,会被攻击,导致数据泄露\n\n\n\n\n\n\n\n## PrepareStatement对象\n\n防止SQL注入\n\n","source":"_posts/MySQL/6-权限-备份.md","raw":"---\ntitle: MySql(6)-权限-备份\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: d29e4d17\ndate: 2021-06-10 11:37:39\ndescription: 关于数据库一些注意事项,如权限管理,备份管理,数据库设计、SQL注入问题\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n\n## 权限管理和备份\n\n\n\n```mysql\n-- 创建用户 create user 用户名 identified by '密码'\ncreate user 'kjn' identified by '123456';\n\n-- 修改当前用户密码\nset password = password('98765');\n\n-- 修改指定用户密码\nset password for '用户名' = password('98765');\n\n-- 重命名用户 rename ... to ...\nrename user 原来名字 to 新名字;\n\n-- 授权用户全部权限 [all privileges]->除了给别人授权,什么都能干\ngrant all privileges on *.* to 用户名;\n\n-- 查看权限\nshow grants for 用户名;\n\n\n-- 撤销权限\nrevoke all privileges on *.* from 用户名;\n\n\n```\n\n\n\n\n\n## 数据库备份\n\n备份方式\n\n- 直接拷贝物理文件\n\n- 在软件中导出\n\n navicat右键点击表导出\n\n- cmd命令行导出\n\n ```mysql\n -- mysqldump -h本机服务\n mysqldump -hlocalhost -u用户名 -p密码 数据库名 表名 > 导出路径/导出文件名\n ```\n\n- 导入(mysql命令行)\n\n ```mysql\n -- 导入D:/a.sql\n mysql > source d:/a.sql;\n ```\n\n \n\n## 规范数据库设计\n\n数据库比较复杂时就要设计\n\n**良好的数据库设计:**\n\n- 节省内存空间\n- 保证数据库完整性\n- 方便开发系统\n\n**软件开发中:**\n\n- 分析需求:分析业务和需要处理的数据库的需求\n- 概要设计:设计关系图E-R图\n\n\n\n**设计数据库步骤**(个人博客)\n\n- 收集信息,分析需求\n - 用户表(用户登录、注销、个人信息,写博客,创建分支)\n - 分类表(文章分类、谁创建的)\n - 文章表\n - 评论表\n - 粉丝表\n - 自定义表(系统信息、某个关键字、字段)\n - 说说表(用户id、内容、发表时间)\n- 标识实体(把需求落实到每个字段)\n- 标识实体之间关系\n - 写博客:user->blog\n - 创建分类:user->catagory\n - 关注:user->user\n - 粉丝链接:links\n - 评论:user->user-blog\n\n\n\n### 三大范式\n\n> 为什么需要数据规范化\n\n- 信息重写\n- 更新异常\n- 插入异常\n - 无法正常显示信息\n- 删除异常\n - 丢失有效信息\n\n\n\n#### 三大范式\n\n1. **第一范式(1NF)**\n\n 原子性:保持列不可再分\n\n2. **第二范式(2NF)**\n\n 前提:满足1NF\n\n 每张表只描述一件事\n\n3. **第三范式(3NF)**\n\n 前提:满足1NF与2NF\n\n 确保数据表中每一列数据都和主键直接相关,而不能间接相关\n\n\n\n#### 规范性和性能问题\n\n关联查询的表不能超过3张表\n\n- 考虑商业化的需求和目标(成本、用户体验),数据库性能更重要\n- 在规范性能的问题时候,需要适当考虑一下规范性\n- 可以故意给某些表增加一些冗余的字段(多表查询变为单表查询)\n- 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)\n\n\n\n\n\n## SQL注入问题\n\nsql存在漏洞,会被攻击,导致数据泄露\n\n\n\n\n\n\n\n## PrepareStatement对象\n\n防止SQL注入\n\n","slug":"MySQL/6-权限-备份","published":1,"updated":"2021-09-08T13:05:51.506Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ow001uakve6hqegt4b","content":"
权限管理和备份 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 -- 创建用户 create user 用户名 identified by '密码' create user 'kjn' identified by '123456'; -- 修改当前用户密码 set password = password('98765'); -- 修改指定用户密码 set password for '用户名' = password('98765'); -- 重命名用户 rename ... to ... rename user 原来名字 to 新名字; -- 授权用户全部权限 [all privileges]->除了给别人授权,什么都能干 grant all privileges on *.* to 用户名; -- 查看权限 show grants for 用户名; -- 撤销权限 revoke all privileges on *.* from 用户名;
\n
数据库备份 备份方式
\n
\n直接拷贝物理文件
\n \n在软件中导出
\nnavicat右键点击表导出
\n \ncmd命令行导出
\n1 2 -- mysqldump -h本机服务 mysqldump -hlocalhost -u用户名 -p密码 数据库名 表名 > 导出路径/导出文件名
\n \n导入(mysql命令行)
\n1 2 -- 导入D:/a.sql mysql > source d:/a.sql;
\n \n \n
规范数据库设计 数据库比较复杂时就要设计
\n
良好的数据库设计:
\n
\n节省内存空间 \n保证数据库完整性 \n方便开发系统 \n \n
软件开发中:
\n
\n分析需求:分析业务和需要处理的数据库的需求 \n概要设计:设计关系图E-R图 \n \n
设计数据库步骤 (个人博客)
\n
\n收集信息,分析需求\n用户表(用户登录、注销、个人信息,写博客,创建分支) \n分类表(文章分类、谁创建的) \n文章表 \n评论表 \n粉丝表 \n自定义表(系统信息、某个关键字、字段) \n说说表(用户id、内容、发表时间) \n \n \n标识实体(把需求落实到每个字段) \n标识实体之间关系\n写博客:user->blog \n创建分类:user->catagory \n关注:user->user \n粉丝链接:links \n评论:user->user-blog \n \n \n \n
三大范式 \n为什么需要数据规范化
\n \n
\n信息重写 \n更新异常 \n插入异常\n \n删除异常\n \n \n
三大范式 \n第一范式(1NF)
\n原子性:保持列不可再分
\n \n第二范式(2NF)
\n前提:满足1NF
\n每张表只描述一件事
\n \n第三范式(3NF)
\n前提:满足1NF与2NF
\n确保数据表中每一列数据都和主键直接相关,而不能间接相关
\n \n \n
规范性和性能问题 关联查询的表不能超过3张表
\n
\n考虑商业化的需求和目标(成本、用户体验),数据库性能更重要 \n在规范性能的问题时候,需要适当考虑一下规范性 \n可以故意给某些表增加一些冗余的字段(多表查询变为单表查询) \n故意增加一些计算列(从大数据量降低为小数据量的查询:索引) \n \n
SQL注入问题 sql存在漏洞,会被攻击,导致数据泄露
\n
PrepareStatement对象 防止SQL注入
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"
权限管理和备份 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 -- 创建用户 create user 用户名 identified by '密码' create user 'kjn' identified by '123456'; -- 修改当前用户密码 set password = password('98765'); -- 修改指定用户密码 set password for '用户名' = password('98765'); -- 重命名用户 rename ... to ... rename user 原来名字 to 新名字; -- 授权用户全部权限 [all privileges]->除了给别人授权,什么都能干 grant all privileges on *.* to 用户名; -- 查看权限 show grants for 用户名; -- 撤销权限 revoke all privileges on *.* from 用户名;
\n
数据库备份 备份方式
\n
\n直接拷贝物理文件
\n \n在软件中导出
\nnavicat右键点击表导出
\n \ncmd命令行导出
\n1 2 -- mysqldump -h本机服务 mysqldump -hlocalhost -u用户名 -p密码 数据库名 表名 > 导出路径/导出文件名
\n \n导入(mysql命令行)
\n1 2 -- 导入D:/a.sql mysql > source d:/a.sql;
\n \n \n
规范数据库设计 数据库比较复杂时就要设计
\n
良好的数据库设计:
\n
\n节省内存空间 \n保证数据库完整性 \n方便开发系统 \n \n
软件开发中:
\n
\n分析需求:分析业务和需要处理的数据库的需求 \n概要设计:设计关系图E-R图 \n \n
设计数据库步骤 (个人博客)
\n
\n收集信息,分析需求\n用户表(用户登录、注销、个人信息,写博客,创建分支) \n分类表(文章分类、谁创建的) \n文章表 \n评论表 \n粉丝表 \n自定义表(系统信息、某个关键字、字段) \n说说表(用户id、内容、发表时间) \n \n \n标识实体(把需求落实到每个字段) \n标识实体之间关系\n写博客:user->blog \n创建分类:user->catagory \n关注:user->user \n粉丝链接:links \n评论:user->user-blog \n \n \n \n
三大范式 \n为什么需要数据规范化
\n \n
\n信息重写 \n更新异常 \n插入异常\n \n删除异常\n \n \n
三大范式 \n第一范式(1NF)
\n原子性:保持列不可再分
\n \n第二范式(2NF)
\n前提:满足1NF
\n每张表只描述一件事
\n \n第三范式(3NF)
\n前提:满足1NF与2NF
\n确保数据表中每一列数据都和主键直接相关,而不能间接相关
\n \n \n
规范性和性能问题 关联查询的表不能超过3张表
\n
\n考虑商业化的需求和目标(成本、用户体验),数据库性能更重要 \n在规范性能的问题时候,需要适当考虑一下规范性 \n可以故意给某些表增加一些冗余的字段(多表查询变为单表查询) \n故意增加一些计算列(从大数据量降低为小数据量的查询:索引) \n \n
SQL注入问题 sql存在漏洞,会被攻击,导致数据泄露
\n
PrepareStatement对象 防止SQL注入
\n"},{"title":"MySql(7)-JDBC","abbrlink":"3f4cddeb","date":"2021-06-11T04:54:16.000Z","description":"Java中使用MySQL数据库,即JDBC","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n# JDBC(重点)\n\n\n\n## idea连接MySQL数据库\n\n![image-20210812215126308](https://gitee.com/ajream/images/raw/master/img/2021-08-1221-51-30_image-20210812215126308.png)\n\n上面操作完成后只会显示一个数据库mysql,要显示其他数据库,按下面操作:\n\n![image-20210812215624487](https://gitee.com/ajream/images/raw/master/img/2021-08-1221-56-25_image-20210812215624487.png)\n\n\n\n\n\n## 数据库驱动\n\n驱动:声卡、显卡、数据库驱动\n\n
\n\n\n\n程序通过数据库驱动操作数据库\n\n\n\n## JDBC\n\n**JDBC** (Java DataBase Connection) 是通过JAVA访问数据库;\n\nSUN公司为了简化开发人员对数据库统一的操作,提供了Java操作数据库的规范,简称 `JDBC`\n\n对于开发人员来说,我们只需要掌握 JDBC 接口即可\n\n
\n\n\n\n\n\n**编写程序步骤:**\n\n1. 加载驱动\n2. 连接数据库(要有连接的服务器地址、用户名、密码) `DriverManager`\n3. 获取SQL对象\n4. 执行SQL语句,获取返回结果\n5. 释放连接\n\n```java\npackage com.jdbcTest;\n\nimport java.sql.*;\n\npublic class FirstJdbcDemo {\n public static void main(String[] args) throws ClassNotFoundException,SQLException {\n // 1.加载驱动\n Class.forName(\"com.mysql.cj.jdbc.Driver\"); //固定写法,mysql5版本没有【cj】\n\n // 2.链接信息url\n String url = \"jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSl=true\";\n String username = \"root\";\n String password = \"admin\";\n\n // 3. 连接数据库\n Connection connection = DriverManager.getConnection(url, username, password);\n\n // 获取SQL对象\n Statement statement = connection.createStatement();\n\n // 执行SQL语句,获取返回的数据\n String sql = \"SELECT * FROM `student`\";\n ResultSet resultSet = statement.executeQuery(sql); //执行\n\n while(resultSet.next()){\n System.out.println(\"id: \" + resultSet.getObject(\"id\"));\n System.out.println(\"name: \" + resultSet.getObject(\"name\"));\n System.out.println(\"email: \" + resultSet.getObject(\"email\"));\n System.out.println(\"=============================\");\n }\n\n //6. 释放连接\n resultSet.close();\n statement.close();\n connection.close();\n\n }\n\n}\n\n```\n\n\n\n> url:\n>\n> ```java\n> url = \"jdbc:mysql://localhost:3306/school?useUnicode=true&charactorEncoding=utf8&useSSl=true\";\n> ```\n\n\n\n> connection 代表数据库\n\n\n\n> Statement 执行SQL语句的对象\n\n\n\n> ResultSet 获得查询结果集,有以下方法\n>\n> - next():移动到下一个记录\n> - getObject(): 获取结果(不知道字段类型时)\n> - getString(): 获取字符串字段(知道字段类型)\n\n\n\n\n\n","source":"_posts/MySQL/7-JDBC.md","raw":"---\ntitle: MySql(7)-JDBC\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: 3f4cddeb\ndate: 2021-06-11 12:54:16\ndescription: Java中使用MySQL数据库,即JDBC\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n# JDBC(重点)\n\n\n\n## idea连接MySQL数据库\n\n![image-20210812215126308](https://gitee.com/ajream/images/raw/master/img/2021-08-1221-51-30_image-20210812215126308.png)\n\n上面操作完成后只会显示一个数据库mysql,要显示其他数据库,按下面操作:\n\n![image-20210812215624487](https://gitee.com/ajream/images/raw/master/img/2021-08-1221-56-25_image-20210812215624487.png)\n\n\n\n\n\n## 数据库驱动\n\n驱动:声卡、显卡、数据库驱动\n\n
\n\n\n\n程序通过数据库驱动操作数据库\n\n\n\n## JDBC\n\n**JDBC** (Java DataBase Connection) 是通过JAVA访问数据库;\n\nSUN公司为了简化开发人员对数据库统一的操作,提供了Java操作数据库的规范,简称 `JDBC`\n\n对于开发人员来说,我们只需要掌握 JDBC 接口即可\n\n
\n\n\n\n\n\n**编写程序步骤:**\n\n1. 加载驱动\n2. 连接数据库(要有连接的服务器地址、用户名、密码) `DriverManager`\n3. 获取SQL对象\n4. 执行SQL语句,获取返回结果\n5. 释放连接\n\n```java\npackage com.jdbcTest;\n\nimport java.sql.*;\n\npublic class FirstJdbcDemo {\n public static void main(String[] args) throws ClassNotFoundException,SQLException {\n // 1.加载驱动\n Class.forName(\"com.mysql.cj.jdbc.Driver\"); //固定写法,mysql5版本没有【cj】\n\n // 2.链接信息url\n String url = \"jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSl=true\";\n String username = \"root\";\n String password = \"admin\";\n\n // 3. 连接数据库\n Connection connection = DriverManager.getConnection(url, username, password);\n\n // 获取SQL对象\n Statement statement = connection.createStatement();\n\n // 执行SQL语句,获取返回的数据\n String sql = \"SELECT * FROM `student`\";\n ResultSet resultSet = statement.executeQuery(sql); //执行\n\n while(resultSet.next()){\n System.out.println(\"id: \" + resultSet.getObject(\"id\"));\n System.out.println(\"name: \" + resultSet.getObject(\"name\"));\n System.out.println(\"email: \" + resultSet.getObject(\"email\"));\n System.out.println(\"=============================\");\n }\n\n //6. 释放连接\n resultSet.close();\n statement.close();\n connection.close();\n\n }\n\n}\n\n```\n\n\n\n> url:\n>\n> ```java\n> url = \"jdbc:mysql://localhost:3306/school?useUnicode=true&charactorEncoding=utf8&useSSl=true\";\n> ```\n\n\n\n> connection 代表数据库\n\n\n\n> Statement 执行SQL语句的对象\n\n\n\n> ResultSet 获得查询结果集,有以下方法\n>\n> - next():移动到下一个记录\n> - getObject(): 获取结果(不知道字段类型时)\n> - getString(): 获取字符串字段(知道字段类型)\n\n\n\n\n\n","slug":"MySQL/7-JDBC","published":1,"updated":"2021-09-10T03:03:10.248Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ox001xakvegmtv0lsp","content":"
JDBC(重点) idea连接MySQL数据库
\n
上面操作完成后只会显示一个数据库mysql,要显示其他数据库,按下面操作:
\n
\n
数据库驱动 驱动:声卡、显卡、数据库驱动
\n
\n
程序通过数据库驱动操作数据库
\n
JDBC JDBC (Java DataBase Connection) 是通过JAVA访问数据库;
\n
SUN公司为了简化开发人员对数据库统一的操作,提供了Java操作数据库的规范,简称 JDBC
\n
对于开发人员来说,我们只需要掌握 JDBC 接口即可
\n
\n
编写程序步骤:
\n
\n加载驱动 \n连接数据库(要有连接的服务器地址、用户名、密码) DriverManager
\n获取SQL对象 \n执行SQL语句,获取返回结果 \n释放连接 \n \n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 package com.jdbcTest;import java.sql.*;public class FirstJdbcDemo { public static void main (String[] args) throws ClassNotFoundException,SQLException { Class.forName("com.mysql.cj.jdbc.Driver" ); String url = "jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSl=true" ; String username = "root" ; String password = "admin" ; Connection connection = DriverManager.getConnection(url, username, password); Statement statement = connection.createStatement(); String sql = "SELECT * FROM `student`" ; ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()){ System.out.println("id: " + resultSet.getObject("id" )); System.out.println("name: " + resultSet.getObject("name" )); System.out.println("email: " + resultSet.getObject("email" )); System.out.println("=============================" ); } resultSet.close(); statement.close(); connection.close(); } }
\n
\nurl:
\n1 url = "jdbc:mysql://localhost:3306/school?useUnicode=true&charactorEncoding=utf8&useSSl=true" ;
\nconnection 代表数据库
\nStatement 执行SQL语句的对象
\nResultSet 获得查询结果集,有以下方法
\n\nnext():移动到下一个记录 \ngetObject(): 获取结果(不知道字段类型时) \ngetString(): 获取字符串字段(知道字段类型) \n \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"
JDBC(重点) idea连接MySQL数据库
\n
上面操作完成后只会显示一个数据库mysql,要显示其他数据库,按下面操作:
\n
\n
数据库驱动 驱动:声卡、显卡、数据库驱动
\n
\n
程序通过数据库驱动操作数据库
\n
JDBC JDBC (Java DataBase Connection) 是通过JAVA访问数据库;
\n
SUN公司为了简化开发人员对数据库统一的操作,提供了Java操作数据库的规范,简称 JDBC
\n
对于开发人员来说,我们只需要掌握 JDBC 接口即可
\n
\n
编写程序步骤:
\n
\n加载驱动 \n连接数据库(要有连接的服务器地址、用户名、密码) DriverManager
\n获取SQL对象 \n执行SQL语句,获取返回结果 \n释放连接 \n \n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 package com.jdbcTest;import java.sql.*;public class FirstJdbcDemo { public static void main (String[] args) throws ClassNotFoundException,SQLException { Class.forName("com.mysql.cj.jdbc.Driver" ); String url = "jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSl=true" ; String username = "root" ; String password = "admin" ; Connection connection = DriverManager.getConnection(url, username, password); Statement statement = connection.createStatement(); String sql = "SELECT * FROM `student`" ; ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()){ System.out.println("id: " + resultSet.getObject("id" )); System.out.println("name: " + resultSet.getObject("name" )); System.out.println("email: " + resultSet.getObject("email" )); System.out.println("=============================" ); } resultSet.close(); statement.close(); connection.close(); } }
\n
\nurl:
\n1 url = "jdbc:mysql://localhost:3306/school?useUnicode=true&charactorEncoding=utf8&useSSl=true" ;
\nconnection 代表数据库
\nStatement 执行SQL语句的对象
\nResultSet 获得查询结果集,有以下方法
\n\nnext():移动到下一个记录 \ngetObject(): 获取结果(不知道字段类型时) \ngetString(): 获取字符串字段(知道字段类型) \n \n \n"},{"title":"Spring(1)-框架介绍","abbrlink":"3afd7950","date":"2021-07-23T04:11:32.000Z","description":"Java的spring框架介绍","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# spring框架介绍\n\n\n\n## spring是什么\n\n1. spring框架是一个开源JavaEE应用程序\n\n2. 主要核心是IOC(控制反转/依赖注入)和AOP(面向切面编程),除此还有一些如springJDBC+事务等的东西\n3. spring是基于分布式的应用程序\n - 基于轻量级框架\n - 配置管理\n - Bean对象实例化-IOC\n - 集成第三方的框架\n - mybatis\n - springMVC\n - ……\n - 自带服务\n - 邮件mail发送\n - 定时任务\n - 消息处理(异步处理)\n\n\n\n## spring结构\n\n1. Dao层:\n - jdbc操作\n - 对应框架:mybatis\n2. Service层\n - 没有对应框架\n3. controller层\n - servlet(接收请求 响应数据 地址匹配 页面转发)\n - 对应框架:springMVC\n\n\n\n## spring优点\n\n- 控制反转(IOC),面向切面编程(AOP)\n- 轻量,非入侵\n- 支持事务\n\n一句话:spring就是一个轻量级的**控制反转**和**面向切面编程**的框架\n\n\n\n\n\n## spring模块划分\n\n- SpringIOC\n- springAOP\n- springJDBC+事务\n- springWeb\n\n\n\n","source":"_posts/Spring/1-spring框架介绍.md","raw":"---\ntitle: Spring(1)-框架介绍\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 3afd7950\ndate: 2021-07-23 12:11:32\ndescription: Java的spring框架介绍\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# spring框架介绍\n\n\n\n## spring是什么\n\n1. spring框架是一个开源JavaEE应用程序\n\n2. 主要核心是IOC(控制反转/依赖注入)和AOP(面向切面编程),除此还有一些如springJDBC+事务等的东西\n3. spring是基于分布式的应用程序\n - 基于轻量级框架\n - 配置管理\n - Bean对象实例化-IOC\n - 集成第三方的框架\n - mybatis\n - springMVC\n - ……\n - 自带服务\n - 邮件mail发送\n - 定时任务\n - 消息处理(异步处理)\n\n\n\n## spring结构\n\n1. Dao层:\n - jdbc操作\n - 对应框架:mybatis\n2. Service层\n - 没有对应框架\n3. controller层\n - servlet(接收请求 响应数据 地址匹配 页面转发)\n - 对应框架:springMVC\n\n\n\n## spring优点\n\n- 控制反转(IOC),面向切面编程(AOP)\n- 轻量,非入侵\n- 支持事务\n\n一句话:spring就是一个轻量级的**控制反转**和**面向切面编程**的框架\n\n\n\n\n\n## spring模块划分\n\n- SpringIOC\n- springAOP\n- springJDBC+事务\n- springWeb\n\n\n\n","slug":"Spring/1-spring框架介绍","published":1,"updated":"2021-09-10T03:17:21.676Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6oy0020akvegdxh7rqn","content":"
spring框架介绍 spring是什么 \nspring框架是一个开源JavaEE应用程序
\n \n主要核心是IOC(控制反转/依赖注入)和AOP(面向切面编程),除此还有一些如springJDBC+事务等的东西
\n \nspring是基于分布式的应用程序\n基于轻量级框架\n \n集成第三方的框架\nmybatis \nspringMVC \n…… \n \n \n自带服务\n邮件mail发送 \n定时任务 \n消息处理(异步处理) \n \n \n \n \n \n
spring结构 \nDao层:\n \nService层\n \ncontroller层\nservlet(接收请求 响应数据 地址匹配 页面转发) \n对应框架:springMVC \n \n \n \n
spring优点 \n控制反转(IOC),面向切面编程(AOP) \n轻量,非入侵 \n支持事务 \n \n
一句话:spring就是一个轻量级的控制反转 和面向切面编程 的框架
\n
spring模块划分 \nSpringIOC \nspringAOP \nspringJDBC+事务 \nspringWeb \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"
spring框架介绍 spring是什么 \nspring框架是一个开源JavaEE应用程序
\n \n主要核心是IOC(控制反转/依赖注入)和AOP(面向切面编程),除此还有一些如springJDBC+事务等的东西
\n \nspring是基于分布式的应用程序\n基于轻量级框架\n \n集成第三方的框架\nmybatis \nspringMVC \n…… \n \n \n自带服务\n邮件mail发送 \n定时任务 \n消息处理(异步处理) \n \n \n \n \n \n
spring结构 \nDao层:\n \nService层\n \ncontroller层\nservlet(接收请求 响应数据 地址匹配 页面转发) \n对应框架:springMVC \n \n \n \n
spring优点 \n控制反转(IOC),面向切面编程(AOP) \n轻量,非入侵 \n支持事务 \n \n
一句话:spring就是一个轻量级的控制反转 和面向切面编程 的框架
\n
spring模块划分 \nSpringIOC \nspringAOP \nspringJDBC+事务 \nspringWeb \n \n"},{"title":"Spring(10)-注解实现自动装配","abbrlink":"39f00355","date":"2021-07-28T12:52:40.000Z","description":"spring使用注解来完成自动装配","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n\n\n\n# 使用注解实现自动装配\n\n\n\n[点击跳转到工程](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master1)\n\n\n\nspring除了用xml配置文件来实现属性注入以外,还可以使用注解实现注入\n\n```java\n@Autowired //通过byType实现自动装配,而且必须要求这个对象存在\n@Resource\t\t//默认通过byName实现自动装配,如果找不到名字,就通过byType自动装配,2个都不行的话就报错\n```\n\n\n\n### 使用@Autowired注入\n\n要使用注解,首先要在xml文件中提供注解支持\n\n![image-20210801104937636](https://gitee.com/ajream/images/raw/master/img/2021-08-0110-49-42_image-20210801104937636.png)\n\n\n\nbeans.xml\n\n```xml\n\n
\n\n \n\n \n \n \n\n\n \n```\n\n> 注意:使用byType自动装配时,若有多个相同类型的bean,把`primary`属性值设为true说明首先使用该bean\n\n\n\n在`Person`类中:(可以没有setter方法)\n\n```java\npublic class Person {\n @Autowired\n private Cat cat;\n//...\n}\n```\n\n\n\n@Autowired就相当于:\n\n```java\n
\n```\n\n\n\n还可以将 @Autowired 注释应用于传统的 setter 方法,如以下示例所示:\n\n```java\npublic class SimpleMovieLister {\n\n private Cat cat;\n\n @Autowired\n public void setCat(Cat cat) {\n this.cat = cat;\n }\n\n // ...\n}\n```\n\n\n\n由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。因此可以使在bean中配置 primary属性 (下面是官方例子)\n\n```xml\n \n\n \n\n \n```\n\n> primary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个主要 bean,则它成为自动装配的值。\n\n### @Qualifier\n\n\n\n当有多个bean类型相同,但id不同时,可以使用`@Qualifier` 来指定使用哪一个bean\n\n```java\n\npublic class Person {\n @Autowired\n @Qualifier(value = \"cat22\")\n private Cat cat;\n//...\n}\n```\n\n```xml\n\n\n\n\n```\n\n\n\n### @Nullable\n\n字段使用了这个注解,说明该字段可以为 null\n\n### 使用@Resource 注入\n\n@Resource 采用 name 属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。\n\n\n\n```java\n\npublic class Person {\n @Resource(name=\"cat22\")\n private Cat cat;\n//...\n}\n```\n\n```xml\n\n\n\n\n```\n\n","source":"_posts/Spring/10-注解实现自动装配.md","raw":"---\ntitle: Spring(10)-注解实现自动装配\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 39f00355\ndate: 2021-07-28 20:52:40\ndescription: spring使用注解来完成自动装配\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n\n\n\n# 使用注解实现自动装配\n\n\n\n[点击跳转到工程](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master1)\n\n\n\nspring除了用xml配置文件来实现属性注入以外,还可以使用注解实现注入\n\n```java\n@Autowired //通过byType实现自动装配,而且必须要求这个对象存在\n@Resource\t\t//默认通过byName实现自动装配,如果找不到名字,就通过byType自动装配,2个都不行的话就报错\n```\n\n\n\n### 使用@Autowired注入\n\n要使用注解,首先要在xml文件中提供注解支持\n\n![image-20210801104937636](https://gitee.com/ajream/images/raw/master/img/2021-08-0110-49-42_image-20210801104937636.png)\n\n\n\nbeans.xml\n\n```xml\n\n\n\n \n\n \n \n \n\n\n \n```\n\n> 注意:使用byType自动装配时,若有多个相同类型的bean,把`primary`属性值设为true说明首先使用该bean\n\n\n\n在`Person`类中:(可以没有setter方法)\n\n```java\npublic class Person {\n @Autowired\n private Cat cat;\n//...\n}\n```\n\n\n\n@Autowired就相当于:\n\n```java\n\n```\n\n\n\n还可以将 @Autowired 注释应用于传统的 setter 方法,如以下示例所示:\n\n```java\npublic class SimpleMovieLister {\n\n private Cat cat;\n\n @Autowired\n public void setCat(Cat cat) {\n this.cat = cat;\n }\n\n // ...\n}\n```\n\n\n\n由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。因此可以使在bean中配置 primary属性 (下面是官方例子)\n\n```xml\n \n\n \n\n \n```\n\n> primary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个主要 bean,则它成为自动装配的值。\n\n### @Qualifier\n\n\n\n当有多个bean类型相同,但id不同时,可以使用`@Qualifier` 来指定使用哪一个bean\n\n```java\n\npublic class Person {\n @Autowired\n @Qualifier(value = \"cat22\")\n private Cat cat;\n//...\n}\n```\n\n```xml\n\n\n\n\n```\n\n\n\n### @Nullable\n\n字段使用了这个注解,说明该字段可以为 null\n\n### 使用@Resource 注入\n\n@Resource 采用 name 属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。\n\n\n\n```java\n\npublic class Person {\n @Resource(name=\"cat22\")\n private Cat cat;\n//...\n}\n```\n\n```xml\n\n\n\n\n```\n\n","slug":"Spring/10-注解实现自动装配","published":1,"updated":"2021-09-10T03:17:03.959Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6p60024akvegkx6f24q","content":"使用注解实现自动装配 点击跳转到工程
\nspring除了用xml配置文件来实现属性注入以外,还可以使用注解实现注入
\n1 2 @Autowired @Resource \t\t
\n使用@Autowired注入 要使用注解,首先要在xml文件中提供注解支持
\n
\nbeans.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" > <context:annotation-config /> <bean id ="dog" class ="com.ajream.pojo.Dog" /> <bean id ="cat" class ="com.ajream.pojo.Cat" /> <bean id ="person" class ="com.ajream.pojo.Person" /> </beans >
\n\n注意:使用byType自动装配时,若有多个相同类型的bean,把primary
属性值设为true说明首先使用该bean
\n \n在Person
类中:(可以没有setter方法)
\n1 2 3 4 5 public class Person { @Autowired private Cat cat; }
\n@Autowired就相当于:
\n1 <bean class ="com.ajream.pojo.Person" autowire="byType" />
\n还可以将 @Autowired 注释应用于传统的 setter 方法,如以下示例所示:
\n1 2 3 4 5 6 7 8 9 10 11 public class SimpleMovieLister { private Cat cat; @Autowired public void setCat (Cat cat) { this .cat = cat; } }
\n由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。因此可以使在bean中配置 primary属性 (下面是官方例子)
\n1 2 3 4 5 <bean class ="example.SimpleMovieCatalog" primary ="true" /> <bean class ="example.SimpleMovieCatalog" /> <bean id ="movieRecommender" class ="example.MovieRecommender" />
\n\nprimary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个主要 bean,则它成为自动装配的值。
\n \n@Qualifier 当有多个bean类型相同,但id不同时,可以使用@Qualifier
来指定使用哪一个bean
\n1 2 3 4 5 6 7 public class Person { @Autowired @Qualifier(value = "cat22") private Cat cat; }
\n1 2 3 4 <bean id ="cat22" class ="com.ajream.pojo.Cat" /> <bean id ="cat23" class ="com.ajream.pojo.Cat" /> <bean class ="com.ajream.pojo.Person" autowire ="byType" />
\n@Nullable 字段使用了这个注解,说明该字段可以为 null
\n使用@Resource 注入 @Resource 采用 name 属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。
\n1 2 3 4 5 6 public class Person { @Resource(name="cat22") private Cat cat; }
\n1 2 3 4 <bean id ="cat22" class ="com.ajream.pojo.Cat" /> <bean id ="cat23" class ="com.ajream.pojo.Cat" /> <bean class ="com.ajream.pojo.Person" autowire ="byType" />
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"使用注解实现自动装配 点击跳转到工程
\nspring除了用xml配置文件来实现属性注入以外,还可以使用注解实现注入
\n1 2 @Autowired @Resource \t\t
\n使用@Autowired注入 要使用注解,首先要在xml文件中提供注解支持
\n
\nbeans.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" > <context:annotation-config /> <bean id ="dog" class ="com.ajream.pojo.Dog" /> <bean id ="cat" class ="com.ajream.pojo.Cat" /> <bean id ="person" class ="com.ajream.pojo.Person" /> </beans >
\n\n注意:使用byType自动装配时,若有多个相同类型的bean,把primary
属性值设为true说明首先使用该bean
\n \n在Person
类中:(可以没有setter方法)
\n1 2 3 4 5 public class Person { @Autowired private Cat cat; }
\n@Autowired就相当于:
\n1 <bean class ="com.ajream.pojo.Person" autowire="byType" />
\n还可以将 @Autowired 注释应用于传统的 setter 方法,如以下示例所示:
\n1 2 3 4 5 6 7 8 9 10 11 public class SimpleMovieLister { private Cat cat; @Autowired public void setCat (Cat cat) { this .cat = cat; } }
\n由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。因此可以使在bean中配置 primary属性 (下面是官方例子)
\n1 2 3 4 5 <bean class ="example.SimpleMovieCatalog" primary ="true" /> <bean class ="example.SimpleMovieCatalog" /> <bean id ="movieRecommender" class ="example.MovieRecommender" />
\n\nprimary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个主要 bean,则它成为自动装配的值。
\n \n@Qualifier 当有多个bean类型相同,但id不同时,可以使用@Qualifier
来指定使用哪一个bean
\n1 2 3 4 5 6 7 public class Person { @Autowired @Qualifier(value = "cat22") private Cat cat; }
\n1 2 3 4 <bean id ="cat22" class ="com.ajream.pojo.Cat" /> <bean id ="cat23" class ="com.ajream.pojo.Cat" /> <bean class ="com.ajream.pojo.Person" autowire ="byType" />
\n@Nullable 字段使用了这个注解,说明该字段可以为 null
\n使用@Resource 注入 @Resource 采用 name 属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。
\n1 2 3 4 5 6 public class Person { @Resource(name="cat22") private Cat cat; }
\n1 2 3 4 <bean id ="cat22" class ="com.ajream.pojo.Cat" /> <bean id ="cat23" class ="com.ajream.pojo.Cat" /> <bean class ="com.ajream.pojo.Person" autowire ="byType" />
\n"},{"title":"Spring(12)-静态代理","abbrlink":"be39d4e8","date":"2021-07-29T11:42:38.000Z","description":"Java静态代理模式举例介绍","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# 静态代理\n\n## 角色分析\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master)\n\n- 抽象角色:用接口或抽象类解决\n\n ```java\n package com.ajream.demo01;\n \n public interface Rent {\n public void rent();\n }\n \n ```\n\n \n\n- 真实角色:被代理角色,比如房东\n\n ```java\n package com.ajream.demo01;\n \n public class Host implements Rent{\n @Override\n public void rent() {\n System.out.println(\"房东出租了房子\");\n }\n }\n \n ```\n\n \n\n- 代理角色:代理真实角色,一般会有些附属操作\n\n ```java\n package com.ajream.demo01;\n \n public class Proxy implements Rent{\n \n Host host = new Host();\n \n public Proxy() {\n }\n \n public Proxy(Host host) {\n this.host = host;\n }\n \n // 中介帮房东租房子,但中介还会有一些附属操作\n public void rent() {\n host.rent();\n seeHouse();\n qianHeTong();\n }\n \n //中介的附属操作\n public void seeHouse(){\n System.out.println(\"中介带你看房子。。。\");\n }\n \n public void qianHeTong(){\n System.out.println(\"签合同。。。\");\n }\n }\n \n ```\n\n \n\n- 客户:访问代理对象的人\n\n ```java\n package com.ajream.demo01;\n \n import org.junit.Test;\n \n public class Client {\n \n @Test\n public void test(){\n // 找房东租房子\n Host host = new Host();\n \n \n // 客户要租入房子,所以把房东丢给中介,让中介帮忙\n Proxy p = new Proxy(host);\n p.rent();\n }\n \n }\n \n ```\n\n \n\n\n\n> 代理模式好处:\n>\n> 1. 角色操作更加纯粹,不用取关注一些公共业务\n> 2. 公共业务交给代理角色,实现业务分工\n> 3. 公共业务发生扩展时,方便集中管理\n>\n> 缺点:\n>\n> 1. 一个真实角色会产生一个代理角色,代码量翻倍\n\n\n\n## 代理模式实现业务\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master2)\n\n\n\n用代理模式为业务添加日志打印功能\n\n\n\n业务接口\n\n```java\npackage com.ajream.demo02;\n\n//业务接口\npublic interface UserService {\n void add();\n void delete();\n void update();\n void query();\n}\n\n```\n\n\n\n业务实现类(实现增删改查功能)\n\n```java\npackage com.ajream.demo02;\n\n//业务实现\npublic class UserServiceImpl implements UserService {\n @Override\n public void add() {\n System.out.println(\"增加一个用户\");\n }\n\n @Override\n public void delete() {\n System.out.println(\"删除一个用户\");\n }\n\n @Override\n public void update() {\n System.out.println(\"修改一个用户\");\n }\n\n @Override\n public void query() {\n System.out.println(\"查询一个用户\");\n }\n}\n\n```\n\n\n\n\n\n【重点】业务代理类(添加日志打印功能)\n\n```java\npackage com.ajream.demo02;\n\npublic class UserServiceProxy implements UserService{\n\n UserServiceImpl userService;\n\n public UserServiceProxy() {\n }\n\n public void setUserService(UserServiceImpl userService) {\n this.userService = userService;\n }\n\n @Override\n public void add() {\n log(\"add\");\n userService.add();\n }\n\n @Override\n public void delete() {\n log(\"delete\");\n userService.delete();\n }\n\n @Override\n public void update() {\n log(\"update\");\n userService.update();\n }\n\n @Override\n public void query() {\n log(\"query\");\n userService.query();\n }\n\n //代理的额外操作,打印日志\n public void log(String msg){\n System.out.println(\"使用了\" + msg + \"方法\" );\n }\n}\n\n```\n\n\n\n客户\n\n```java\npackage com.ajream.demo02;\n\nimport org.junit.Test;\n\npublic class Client {\n @Test\n public void test1(){\n UserServiceImpl userService = new UserServiceImpl();\n\n UserServiceProxy proxy = new UserServiceProxy();\n proxy.setUserService(userService);\n\n proxy.add();\n }\n}\n\n```\n\n","source":"_posts/Spring/12-静态代理.md","raw":"---\ntitle: Spring(12)-静态代理\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: be39d4e8\ndate: 2021-07-29 19:42:38\ndescription: Java静态代理模式举例介绍\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# 静态代理\n\n## 角色分析\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master)\n\n- 抽象角色:用接口或抽象类解决\n\n ```java\n package com.ajream.demo01;\n \n public interface Rent {\n public void rent();\n }\n \n ```\n\n \n\n- 真实角色:被代理角色,比如房东\n\n ```java\n package com.ajream.demo01;\n \n public class Host implements Rent{\n @Override\n public void rent() {\n System.out.println(\"房东出租了房子\");\n }\n }\n \n ```\n\n \n\n- 代理角色:代理真实角色,一般会有些附属操作\n\n ```java\n package com.ajream.demo01;\n \n public class Proxy implements Rent{\n \n Host host = new Host();\n \n public Proxy() {\n }\n \n public Proxy(Host host) {\n this.host = host;\n }\n \n // 中介帮房东租房子,但中介还会有一些附属操作\n public void rent() {\n host.rent();\n seeHouse();\n qianHeTong();\n }\n \n //中介的附属操作\n public void seeHouse(){\n System.out.println(\"中介带你看房子。。。\");\n }\n \n public void qianHeTong(){\n System.out.println(\"签合同。。。\");\n }\n }\n \n ```\n\n \n\n- 客户:访问代理对象的人\n\n ```java\n package com.ajream.demo01;\n \n import org.junit.Test;\n \n public class Client {\n \n @Test\n public void test(){\n // 找房东租房子\n Host host = new Host();\n \n \n // 客户要租入房子,所以把房东丢给中介,让中介帮忙\n Proxy p = new Proxy(host);\n p.rent();\n }\n \n }\n \n ```\n\n \n\n\n\n> 代理模式好处:\n>\n> 1. 角色操作更加纯粹,不用取关注一些公共业务\n> 2. 公共业务交给代理角色,实现业务分工\n> 3. 公共业务发生扩展时,方便集中管理\n>\n> 缺点:\n>\n> 1. 一个真实角色会产生一个代理角色,代码量翻倍\n\n\n\n## 代理模式实现业务\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master2)\n\n\n\n用代理模式为业务添加日志打印功能\n\n\n\n业务接口\n\n```java\npackage com.ajream.demo02;\n\n//业务接口\npublic interface UserService {\n void add();\n void delete();\n void update();\n void query();\n}\n\n```\n\n\n\n业务实现类(实现增删改查功能)\n\n```java\npackage com.ajream.demo02;\n\n//业务实现\npublic class UserServiceImpl implements UserService {\n @Override\n public void add() {\n System.out.println(\"增加一个用户\");\n }\n\n @Override\n public void delete() {\n System.out.println(\"删除一个用户\");\n }\n\n @Override\n public void update() {\n System.out.println(\"修改一个用户\");\n }\n\n @Override\n public void query() {\n System.out.println(\"查询一个用户\");\n }\n}\n\n```\n\n\n\n\n\n【重点】业务代理类(添加日志打印功能)\n\n```java\npackage com.ajream.demo02;\n\npublic class UserServiceProxy implements UserService{\n\n UserServiceImpl userService;\n\n public UserServiceProxy() {\n }\n\n public void setUserService(UserServiceImpl userService) {\n this.userService = userService;\n }\n\n @Override\n public void add() {\n log(\"add\");\n userService.add();\n }\n\n @Override\n public void delete() {\n log(\"delete\");\n userService.delete();\n }\n\n @Override\n public void update() {\n log(\"update\");\n userService.update();\n }\n\n @Override\n public void query() {\n log(\"query\");\n userService.query();\n }\n\n //代理的额外操作,打印日志\n public void log(String msg){\n System.out.println(\"使用了\" + msg + \"方法\" );\n }\n}\n\n```\n\n\n\n客户\n\n```java\npackage com.ajream.demo02;\n\nimport org.junit.Test;\n\npublic class Client {\n @Test\n public void test1(){\n UserServiceImpl userService = new UserServiceImpl();\n\n UserServiceProxy proxy = new UserServiceProxy();\n proxy.setUserService(userService);\n\n proxy.add();\n }\n}\n\n```\n\n","slug":"Spring/12-静态代理","published":1,"updated":"2021-09-10T03:16:52.129Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6p70028akve23cieqja","content":"静态代理 角色分析 项目地址
\n\n抽象角色:用接口或抽象类解决
\n1 2 3 4 5 6 package com.ajream.demo01;public interface Rent { public void rent () ; }
\n \n \n\n真实角色:被代理角色,比如房东
\n1 2 3 4 5 6 7 8 9 package com.ajream.demo01;public class Host implements Rent { @Override public void rent () { System.out.println("房东出租了房子" ); } }
\n \n \n\n代理角色:代理真实角色,一般会有些附属操作
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.ajream.demo01;public class Proxy implements Rent { Host host = new Host(); public Proxy () { } public Proxy (Host host) { this .host = host; } public void rent () { host.rent(); seeHouse(); qianHeTong(); } public void seeHouse () { System.out.println("中介带你看房子。。。" ); } public void qianHeTong () { System.out.println("签合同。。。" ); } }
\n \n \n\n客户:访问代理对象的人
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.ajream.demo01;import org.junit.Test;public class Client { @Test public void test () { Host host = new Host(); Proxy p = new Proxy(host); p.rent(); } }
\n \n \n\n代理模式好处:
\n\n角色操作更加纯粹,不用取关注一些公共业务 \n公共业务交给代理角色,实现业务分工 \n公共业务发生扩展时,方便集中管理 \n \n缺点:
\n\n一个真实角色会产生一个代理角色,代码量翻倍 \n \n \n代理模式实现业务 项目地址
\n用代理模式为业务添加日志打印功能
\n业务接口
\n1 2 3 4 5 6 7 8 9 10 package com.ajream.demo02;public interface UserService { void add () ; void delete () ; void update () ; void query () ; }
\n业务实现类(实现增删改查功能)
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.ajream.demo02;public class UserServiceImpl implements UserService { @Override public void add () { System.out.println("增加一个用户" ); } @Override public void delete () { System.out.println("删除一个用户" ); } @Override public void update () { System.out.println("修改一个用户" ); } @Override public void query () { System.out.println("查询一个用户" ); } }
\n【重点】业务代理类(添加日志打印功能)
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 package com.ajream.demo02;public class UserServiceProxy implements UserService { UserServiceImpl userService; public UserServiceProxy () { } public void setUserService (UserServiceImpl userService) { this .userService = userService; } @Override public void add () { log("add" ); userService.add(); } @Override public void delete () { log("delete" ); userService.delete(); } @Override public void update () { log("update" ); userService.update(); } @Override public void query () { log("query" ); userService.query(); } public void log (String msg) { System.out.println("使用了" + msg + "方法" ); } }
\n客户
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.ajream.demo02;import org.junit.Test;public class Client { @Test public void test1 () { UserServiceImpl userService = new UserServiceImpl(); UserServiceProxy proxy = new UserServiceProxy(); proxy.setUserService(userService); proxy.add(); } }
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"静态代理 角色分析 项目地址
\n\n抽象角色:用接口或抽象类解决
\n1 2 3 4 5 6 package com.ajream.demo01;public interface Rent { public void rent () ; }
\n \n \n\n真实角色:被代理角色,比如房东
\n1 2 3 4 5 6 7 8 9 package com.ajream.demo01;public class Host implements Rent { @Override public void rent () { System.out.println("房东出租了房子" ); } }
\n \n \n\n代理角色:代理真实角色,一般会有些附属操作
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.ajream.demo01;public class Proxy implements Rent { Host host = new Host(); public Proxy () { } public Proxy (Host host) { this .host = host; } public void rent () { host.rent(); seeHouse(); qianHeTong(); } public void seeHouse () { System.out.println("中介带你看房子。。。" ); } public void qianHeTong () { System.out.println("签合同。。。" ); } }
\n \n \n\n客户:访问代理对象的人
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.ajream.demo01;import org.junit.Test;public class Client { @Test public void test () { Host host = new Host(); Proxy p = new Proxy(host); p.rent(); } }
\n \n \n\n代理模式好处:
\n\n角色操作更加纯粹,不用取关注一些公共业务 \n公共业务交给代理角色,实现业务分工 \n公共业务发生扩展时,方便集中管理 \n \n缺点:
\n\n一个真实角色会产生一个代理角色,代码量翻倍 \n \n \n代理模式实现业务 项目地址
\n用代理模式为业务添加日志打印功能
\n业务接口
\n1 2 3 4 5 6 7 8 9 10 package com.ajream.demo02;public interface UserService { void add () ; void delete () ; void update () ; void query () ; }
\n业务实现类(实现增删改查功能)
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.ajream.demo02;public class UserServiceImpl implements UserService { @Override public void add () { System.out.println("增加一个用户" ); } @Override public void delete () { System.out.println("删除一个用户" ); } @Override public void update () { System.out.println("修改一个用户" ); } @Override public void query () { System.out.println("查询一个用户" ); } }
\n【重点】业务代理类(添加日志打印功能)
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 package com.ajream.demo02;public class UserServiceProxy implements UserService { UserServiceImpl userService; public UserServiceProxy () { } public void setUserService (UserServiceImpl userService) { this .userService = userService; } @Override public void add () { log("add" ); userService.add(); } @Override public void delete () { log("delete" ); userService.delete(); } @Override public void update () { log("update" ); userService.update(); } @Override public void query () { log("query" ); userService.query(); } public void log (String msg) { System.out.println("使用了" + msg + "方法" ); } }
\n客户
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.ajream.demo02;import org.junit.Test;public class Client { @Test public void test1 () { UserServiceImpl userService = new UserServiceImpl(); UserServiceProxy proxy = new UserServiceProxy(); proxy.setUserService(userService); proxy.add(); } }
\n"},{"title":"Spring(11)-使用注解开发","abbrlink":"38b3e585","date":"2021-07-28T12:55:13.000Z","description":"spring使用注解开发,一种还要依赖xml配置文件,另一种可以完全抛弃xml配置文件,使用纯Java来完成属性注入","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# 使用注解开发\n\n## 依然使用xml配置文件来管理\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master3/)\n\nspring4之后,要使用注解开发,必须保证aop的包导入了\n\n![image-20210801135559755](https://gitee.com/ajream/images/raw/master/img/2021-08-0113-56-04_image-20210801135559755.png)\n\n使用注解开发,要导入context约束,提供注解的支持\n\n```xml\n\n\n\n \n\n \n \n \n```\n\n\n\n1. bean\n\n ```java\n @component:\n //组件,在创建的类前面加上这个注解,说明这个类已经被spring管理了,相当于:\n //\n //没有参数时,只能以getBean(\"类名的小写字母\") 来获取bean\n @component(value=\"xxx\")\n //有参数时,可以用getBean(\"xxx\") 来获取bean\n ```\n\n Person类:\n\n ```java\n package com.ajream.pojo;\n \n import org.springframework.stereotype.Component;\n \n @Component\t\t\t\t//没有参数时,只能以getBean(\"person\") 来获取bean\n public class Person {\n public String name = \"张三\";\n }\n \n ```\n\n \n\n2. 属性注入\n\n\t ```java\n @Value(\"李四\"):\n //相当于 \n ```\n\n \n\n ```java\n \n @Component\n public class Person {\n \n @Value(\"李四\")\t\t//注入属性值\n public String name;\n }\n ```\n\n 也可以在setter方法前使用:\n\n ```java\n @Component\n public class Person {\n public String name;\n \n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n }\n ```\n\n \n\n4. 衍生注解\n\n @Component有几个衍生注解,在web中一般按照mvc三层架构划分:\n\n - dao层:@Repository\n - service层:@Service\n - Controller层:@Controller\n\n 这4个注解功能一样的\n\n5. 作用域scope\n\n 单例\n\n ```java\n @Component\n @Scope(value = \"singleton\") \n public class Person {\n public String name;\n \n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n }\n ```\n\n 原型\n\n ```java\n @Scope(value = \"prototype\") \n ```\n\n6. 小结:\n\n xml与注解\n\n - xml:万能\n\n - 注解:不是自己的类用不了,维护相对复杂\n\n xml与注解最佳实践:\n\n - xml用来管理bean\n\n - 注解只负责属性注入\n\n - 注意:要让注解生效,必须开启注解的支持\n\n ```xml\n \n \n ```\n\n \n\n## 使用Java的方式配置spring\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master4/)\n\n在此前一直都使用beans.xml配置文件来配置spring如属性注入,现在可以不使用xml了,将spring配置全权交由Java来配置\n\n用MyConfig类来代替beans.xml:\n\n```java\npackage com.ajream.config;\n\nimport com.ajream.pojo.Person;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class MyConfig {\n\n @Bean\n //相当于\n public Person getPerson1(){ //方法名就是bean的id\n return new Person();\n }\n}\n\n```\n\nPerson类:\n\n```java\npackage com.ajream.pojo;\n\nimport org.springframework.beans.factory.annotation.Value;\n\npublic class Person {\n public String name;\n\n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n}\n\n```\n\nMyTest类\n\n\n\n![image-20210801151413775](https://gitee.com/ajream/images/raw/master/img/2021-08-0115-14-18_image-20210801151413775.png)\n\n\n\n这里不使用xml配置文件了,使用Java配置类 `MyConfig`\n\n```java\nimport com.ajream.config.MyConfig;\nimport com.ajream.pojo.Person;\nimport org.junit.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\n\n\npublic class MyTest {\n\n @Test\n public void test1() {\n //加载配置类\n ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);\n\n Person p = context.getBean(\"getPerson1\", Person.class);\n\n System.out.println(p.getName());\n\n }\n}\n\n```\n\n\n\n由于MyConfig类也是配置类,所以它具有beans.xml的功能,比如:\n\n![image-20210801155244594](https://gitee.com/ajream/images/raw/master/img/2021-08-0115-52-49_image-20210801155244594.png)","source":"_posts/Spring/11-使用注解开发.md","raw":"---\ntitle: Spring(11)-使用注解开发\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 38b3e585\ndate: 2021-07-28 20:55:13\ndescription: spring使用注解开发,一种还要依赖xml配置文件,另一种可以完全抛弃xml配置文件,使用纯Java来完成属性注入\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# 使用注解开发\n\n## 依然使用xml配置文件来管理\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master3/)\n\nspring4之后,要使用注解开发,必须保证aop的包导入了\n\n![image-20210801135559755](https://gitee.com/ajream/images/raw/master/img/2021-08-0113-56-04_image-20210801135559755.png)\n\n使用注解开发,要导入context约束,提供注解的支持\n\n```xml\n\n\n\n \n\n \n \n \n```\n\n\n\n1. bean\n\n ```java\n @component:\n //组件,在创建的类前面加上这个注解,说明这个类已经被spring管理了,相当于:\n //\n //没有参数时,只能以getBean(\"类名的小写字母\") 来获取bean\n @component(value=\"xxx\")\n //有参数时,可以用getBean(\"xxx\") 来获取bean\n ```\n\n Person类:\n\n ```java\n package com.ajream.pojo;\n \n import org.springframework.stereotype.Component;\n \n @Component\t\t\t\t//没有参数时,只能以getBean(\"person\") 来获取bean\n public class Person {\n public String name = \"张三\";\n }\n \n ```\n\n \n\n2. 属性注入\n\n\t ```java\n @Value(\"李四\"):\n //相当于 \n ```\n\n \n\n ```java\n \n @Component\n public class Person {\n \n @Value(\"李四\")\t\t//注入属性值\n public String name;\n }\n ```\n\n 也可以在setter方法前使用:\n\n ```java\n @Component\n public class Person {\n public String name;\n \n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n }\n ```\n\n \n\n4. 衍生注解\n\n @Component有几个衍生注解,在web中一般按照mvc三层架构划分:\n\n - dao层:@Repository\n - service层:@Service\n - Controller层:@Controller\n\n 这4个注解功能一样的\n\n5. 作用域scope\n\n 单例\n\n ```java\n @Component\n @Scope(value = \"singleton\") \n public class Person {\n public String name;\n \n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n }\n ```\n\n 原型\n\n ```java\n @Scope(value = \"prototype\") \n ```\n\n6. 小结:\n\n xml与注解\n\n - xml:万能\n\n - 注解:不是自己的类用不了,维护相对复杂\n\n xml与注解最佳实践:\n\n - xml用来管理bean\n\n - 注解只负责属性注入\n\n - 注意:要让注解生效,必须开启注解的支持\n\n ```xml\n \n \n ```\n\n \n\n## 使用Java的方式配置spring\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master4/)\n\n在此前一直都使用beans.xml配置文件来配置spring如属性注入,现在可以不使用xml了,将spring配置全权交由Java来配置\n\n用MyConfig类来代替beans.xml:\n\n```java\npackage com.ajream.config;\n\nimport com.ajream.pojo.Person;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class MyConfig {\n\n @Bean\n //相当于\n public Person getPerson1(){ //方法名就是bean的id\n return new Person();\n }\n}\n\n```\n\nPerson类:\n\n```java\npackage com.ajream.pojo;\n\nimport org.springframework.beans.factory.annotation.Value;\n\npublic class Person {\n public String name;\n\n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n}\n\n```\n\nMyTest类\n\n\n\n![image-20210801151413775](https://gitee.com/ajream/images/raw/master/img/2021-08-0115-14-18_image-20210801151413775.png)\n\n\n\n这里不使用xml配置文件了,使用Java配置类 `MyConfig`\n\n```java\nimport com.ajream.config.MyConfig;\nimport com.ajream.pojo.Person;\nimport org.junit.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\n\n\npublic class MyTest {\n\n @Test\n public void test1() {\n //加载配置类\n ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);\n\n Person p = context.getBean(\"getPerson1\", Person.class);\n\n System.out.println(p.getName());\n\n }\n}\n\n```\n\n\n\n由于MyConfig类也是配置类,所以它具有beans.xml的功能,比如:\n\n![image-20210801155244594](https://gitee.com/ajream/images/raw/master/img/2021-08-0115-52-49_image-20210801155244594.png)","slug":"Spring/11-使用注解开发","published":1,"updated":"2021-09-10T03:16:58.357Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6p8002cakvedimceatv","content":"使用注解开发 依然使用xml配置文件来管理 项目地址
\nspring4之后,要使用注解开发,必须保证aop的包导入了
\n
\n使用注解开发,要导入context约束,提供注解的支持
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" > <context:annotation-config /> <context:component-scan base-package ="com.ajream.pojo" /> </beans >
\n\nbean
\n1 2 3 4 5 6 @component : @component(value="xxx")
\nPerson类:
\n1 2 3 4 5 6 7 8 9 package com.ajream.pojo;import org.springframework.stereotype.Component;@Component \t\t\t\tpublic class Person { public String name = "张三" ; }
\n \n \n\n属性注入
\n \n \n \n 1 2 3 4 5 6 7 @Component public class Person { @Value("李四") \t\t public String name; }
\n 也可以在setter方法前使用:
\n 1 2 3 4 5 6 7 8 9 @Component public class Person { public String name; @Value("李四") public void setName (String name) { this .name = name; } }
\n\n衍生注解
\n@Component有几个衍生注解,在web中一般按照mvc三层架构划分:
\n\ndao层:@Repository \nservice层:@Service \nController层:@Controller \n \n这4个注解功能一样的
\n \n作用域scope
\n单例
\n1 2 3 4 5 6 7 8 9 10 @Component @Scope(value = "singleton") public class Person { public String name; @Value("李四") public void setName (String name) { this .name = name; } }
\n原型
\n1 @Scope(value = "prototype")
\n \n小结:
\nxml与注解
\n\nxml:万能
\n \n注解:不是自己的类用不了,维护相对复杂
\n \n \nxml与注解最佳实践:
\n\nxml用来管理bean
\n \n注解只负责属性注入
\n \n注意:要让注解生效,必须开启注解的支持
\n1 2 <context:annotation-config /> <context:component-scan base-package ="com.ajream.pojo" />
\n \n \n \n \n使用Java的方式配置spring 项目地址
\n在此前一直都使用beans.xml配置文件来配置spring如属性注入,现在可以不使用xml了,将spring配置全权交由Java来配置
\n用MyConfig类来代替beans.xml:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.ajream.config;import com.ajream.pojo.Person;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration public class MyConfig { @Bean public Person getPerson1 () { return new Person(); } }
\nPerson类:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.ajream.pojo;import org.springframework.beans.factory.annotation.Value;public class Person { public String name; @Value("李四") public void setName (String name) { this .name = name; } public String getName () { return name; } }
\nMyTest类
\n
\n这里不使用xml配置文件了,使用Java配置类 MyConfig
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import com.ajream.config.MyConfig;import com.ajream.pojo.Person;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class MyTest { @Test public void test1 () { ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); Person p = context.getBean("getPerson1" , Person.class); System.out.println(p.getName()); } }
\n由于MyConfig类也是配置类,所以它具有beans.xml的功能,比如:
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"使用注解开发 依然使用xml配置文件来管理 项目地址
\nspring4之后,要使用注解开发,必须保证aop的包导入了
\n
\n使用注解开发,要导入context约束,提供注解的支持
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" > <context:annotation-config /> <context:component-scan base-package ="com.ajream.pojo" /> </beans >
\n\nbean
\n1 2 3 4 5 6 @component : @component(value="xxx")
\nPerson类:
\n1 2 3 4 5 6 7 8 9 package com.ajream.pojo;import org.springframework.stereotype.Component;@Component \t\t\t\tpublic class Person { public String name = "张三" ; }
\n \n \n\n属性注入
\n \n \n \n 1 2 3 4 5 6 7 @Component public class Person { @Value("李四") \t\t public String name; }
\n 也可以在setter方法前使用:
\n 1 2 3 4 5 6 7 8 9 @Component public class Person { public String name; @Value("李四") public void setName (String name) { this .name = name; } }
\n\n衍生注解
\n@Component有几个衍生注解,在web中一般按照mvc三层架构划分:
\n\ndao层:@Repository \nservice层:@Service \nController层:@Controller \n \n这4个注解功能一样的
\n \n作用域scope
\n单例
\n1 2 3 4 5 6 7 8 9 10 @Component @Scope(value = "singleton") public class Person { public String name; @Value("李四") public void setName (String name) { this .name = name; } }
\n原型
\n1 @Scope(value = "prototype")
\n \n小结:
\nxml与注解
\n\nxml:万能
\n \n注解:不是自己的类用不了,维护相对复杂
\n \n \nxml与注解最佳实践:
\n\nxml用来管理bean
\n \n注解只负责属性注入
\n \n注意:要让注解生效,必须开启注解的支持
\n1 2 <context:annotation-config /> <context:component-scan base-package ="com.ajream.pojo" />
\n \n \n \n \n使用Java的方式配置spring 项目地址
\n在此前一直都使用beans.xml配置文件来配置spring如属性注入,现在可以不使用xml了,将spring配置全权交由Java来配置
\n用MyConfig类来代替beans.xml:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.ajream.config;import com.ajream.pojo.Person;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration public class MyConfig { @Bean public Person getPerson1 () { return new Person(); } }
\nPerson类:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.ajream.pojo;import org.springframework.beans.factory.annotation.Value;public class Person { public String name; @Value("李四") public void setName (String name) { this .name = name; } public String getName () { return name; } }
\nMyTest类
\n
\n这里不使用xml配置文件了,使用Java配置类 MyConfig
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import com.ajream.config.MyConfig;import com.ajream.pojo.Person;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class MyTest { @Test public void test1 () { ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); Person p = context.getBean("getPerson1" , Person.class); System.out.println(p.getName()); } }
\n由于MyConfig类也是配置类,所以它具有beans.xml的功能,比如:
\n
\n"},{"title":"Spring(13)-动态代理","abbrlink":"26c04051","date":"2021-07-29T07:53:34.000Z","description":"Java动态代理模式举例介绍","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# 动态代理\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master3)\n\n底层用到`反射`\n\n- 动态代理与静态代理角色一样\n- 动态代理的代理类是动态生成,并非我们直接写好\n- 分类:基于接口和基于类的\n - 基于接口——JDK动态代理\n - 基于类——cglib\n - Java字节码实现——javasist\n\n\n\n需要了解2个类:\n\n- Proxy:调用`newProxyInstance` 方法用于生成代理角色\n\n- InvocationHandler:重写 `invoke` 方法,指明代理角色要处理的功能,详细[查看项目](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master3)\n\n ```java\n //这是一个用于创建代理角色的类,不是代理类\n public class ProxyInvocationHandler implements InvocationHandler {\n \n //指向被代理的接口(即真实角色)\n private Rent r;\n \n public void setRent(Rent r) {\n this.r = r;\n }\n \n //生成代理角色\n public Object getProxy(){\n return Proxy.newProxyInstance(this.getClass().getClassLoader(), r.getClass().getInterfaces(), this);\n }\n \n @Override\n //处理代理实例(即被代理者),并返回结果\n public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n seeHouse();\n fare();\n Object result = method.invoke(r, args);\n return result;\n }\n \n ```\n\n \n\n\n\n动态代理好处:\n\n- 可以使真实角色的操作更加纯粹\n- 公共交给代理角色,实现业务分工\n- 公共业务扩展时,方便集中管理\n- 一个动态代理类可以代理多个类(只要实现的是同一个接口即可)\n\n\n\n","source":"_posts/Spring/13-动态代理.md","raw":"---\ntitle: Spring(13)-动态代理\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 26c04051\ndate: 2021-07-29 15:53:34\ndescription: Java动态代理模式举例介绍\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# 动态代理\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master3)\n\n底层用到`反射`\n\n- 动态代理与静态代理角色一样\n- 动态代理的代理类是动态生成,并非我们直接写好\n- 分类:基于接口和基于类的\n - 基于接口——JDK动态代理\n - 基于类——cglib\n - Java字节码实现——javasist\n\n\n\n需要了解2个类:\n\n- Proxy:调用`newProxyInstance` 方法用于生成代理角色\n\n- InvocationHandler:重写 `invoke` 方法,指明代理角色要处理的功能,详细[查看项目](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master3)\n\n ```java\n //这是一个用于创建代理角色的类,不是代理类\n public class ProxyInvocationHandler implements InvocationHandler {\n \n //指向被代理的接口(即真实角色)\n private Rent r;\n \n public void setRent(Rent r) {\n this.r = r;\n }\n \n //生成代理角色\n public Object getProxy(){\n return Proxy.newProxyInstance(this.getClass().getClassLoader(), r.getClass().getInterfaces(), this);\n }\n \n @Override\n //处理代理实例(即被代理者),并返回结果\n public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n seeHouse();\n fare();\n Object result = method.invoke(r, args);\n return result;\n }\n \n ```\n\n \n\n\n\n动态代理好处:\n\n- 可以使真实角色的操作更加纯粹\n- 公共交给代理角色,实现业务分工\n- 公共业务扩展时,方便集中管理\n- 一个动态代理类可以代理多个类(只要实现的是同一个接口即可)\n\n\n\n","slug":"Spring/13-动态代理","published":1,"updated":"2021-09-10T03:16:40.037Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6p9002fakve7ps4flkp","content":"动态代理 项目地址
\n底层用到反射
\n\n动态代理与静态代理角色一样 \n动态代理的代理类是动态生成,并非我们直接写好 \n分类:基于接口和基于类的\n基于接口——JDK动态代理 \n基于类——cglib \nJava字节码实现——javasist \n \n \n \n需要了解2个类:
\n\n动态代理好处:
\n\n可以使真实角色的操作更加纯粹 \n公共交给代理角色,实现业务分工 \n公共业务扩展时,方便集中管理 \n一个动态代理类可以代理多个类(只要实现的是同一个接口即可) \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"动态代理 项目地址
\n底层用到反射
\n\n动态代理与静态代理角色一样 \n动态代理的代理类是动态生成,并非我们直接写好 \n分类:基于接口和基于类的\n基于接口——JDK动态代理 \n基于类——cglib \nJava字节码实现——javasist \n \n \n \n需要了解2个类:
\n\n动态代理好处:
\n\n可以使真实角色的操作更加纯粹 \n公共交给代理角色,实现业务分工 \n公共业务扩展时,方便集中管理 \n一个动态代理类可以代理多个类(只要实现的是同一个接口即可) \n \n"},{"title":"Spring(14)-springAOP","abbrlink":"a1a1eb5","date":"2021-07-30T05:50:41.000Z","description":"springAOP使用了动态代理模式,其基本使用进入文章详细了解","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# Spring Aop\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-aop/-/tree/master)\n\n## aop实现方式1(实现spring接口)\n\n### 导包\n\n使用之前要导入依赖包\n\n![image-20210803155603975](https://gitee.com/ajream/images/raw/master/img/2021-08-0315-56-08_image-20210803155603975.png)\n\n```xml\n\n \n org.springframework \n spring-context \n 5.2.9.RELEASE \n \n \n junit \n junit \n 4.12 \n \n \n org.aspectj \n aspectjweaver \n 1.9.6 \n \n \n```\n\n\n\n项目结构:\n\n```\nspring1-aop\n├───.idea\n├───src\n│ ├───main\n│ │ ├───java\n│ │ │ └───com\n│ │ │ └───ajream\n│ │ │ ├───log\n│ │ │ └───service\n│ │ └───resources\n│ └───test\n └───java\n```\n\n\n\n### 业务模块service\n\n被代理的接口,包含了要实现的具体功能\n\nUserService.java接口:\n\n```java\npackage com.ajream.service;\n\npublic interface UserService {\n void add();\n void delete();\n void update();\n void query();\n}\n```\n\nUserServiceImpl.java\n\n```java\npackage com.ajream.service;\n\npublic class UserServiceImpl implements UserService {\n @Override\n public void add() {\n System.out.println(\"增加一个用户\");\n }\n\n @Override\n public void delete() {\n System.out.println(\"删除一个用户\");\n }\n\n @Override\n public void update() {\n System.out.println(\"修改一个用户\");\n }\n\n @Override\n public void query() {\n System.out.println(\"查询一个用户\");\n }\n}\n\n```\n\n\n\n### 代理模块\n\n在原有基础上扩展一些功能\n\nLog.java (在实现某个业务(调用某个方法)前,打印一些相关信息)\n\n> 说明:重写MethodBeforeAdvice接口的before方法,说明在切入点之前执行这个方法\n\n```java\npackage com.ajream.log;\n\nimport org.springframework.aop.MethodBeforeAdvice;\n\nimport java.lang.reflect.Method;\n\npublic class Log implements MethodBeforeAdvice {\n @Override\n public void before(Method method, Object[] args, Object target) throws Throwable {\n assert target != null;\n System.out.println(target.getClass().getName() + \"的\" + method.getName() + \"方法被执行了\");\n }\n}\n\n```\n\n\n\nAfterLog.java (在实现某个业务(调用某个方法)后,打印一些相关信息) \n\n> 说明:重写AfterReturningAdvice的 `afterReturning` 方法,说明在切入点之后执行这个方法\n\n```java\npackage com.ajream.log;\n\nimport org.springframework.aop.AfterReturningAdvice;\nimport java.lang.reflect.Method;\n\npublic class AfterLog implements AfterReturningAdvice {\n @Override\n public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {\n System.out.println(\"执行了\" +method.getName()+ \"方法,返回值为:\"+returnValue);\n }\n}\n\n```\n\n\n\nApplicationContext.xml 配置文件\n\n```xml\n\n\n\n \n \n \n\n \n\n \n\n\n \n \n\n \n\n \n```\n\n> 说明:\n>\n> 1. 注意添加aop支持\n>\n> ```\n> xmlns:aop=\"http://www.springframework.org/schema/aop\"\n> http://www.springframework.org/schema/aop\n> https://www.springframework.org/schema/aop/spring-aop.xsd\n> ```\n>\n> 2. aop配置(使用原生spring api接口)\n>\n> ```xml\n> \n> \n> \n> \n> \n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n### 测试\n\nMyTest.java测试\n\n```java\nimport com.ajream.service.UserService;\nimport org.junit.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\npublic class MyTest {\n @Test\n public void test(){\n ApplicationContext context = new ClassPathXmlApplicationContext(\"ApplicationContext.xml\");\n\n //动态代理的是接口\n UserService userService = context.getBean(\"userService\", UserService.class);\n userService.add();\n }\n}\n\n```\n\n\n\n测试结果:\n\n![image-20210808163457649](https://gitee.com/ajream/images/raw/master/img/2021-08-0816-35-03_image-20210808163457649.png)\n\n\n\n## Aop实现方式2(自定义切入类)\n\n主要是aop配置方式不同\n\n之前是使用原生spring api方式,分别实现了`MethodBeforeAdvice`和`AfterReturningAdvice`这两个接口,因此在xml中只需要指明要切入哪个位置,执行什么操作,在执行目标接口的方法时spring就会根据xml配置自动去执行对应操作,如下:\n\n```xml\n\n \n \n \n \n \n \n \n \n```\n\n---\n\n现在改用自定义切入面 方式:\n\n首先添加一个切面DiyPointCut(即一个类,包含了要切入的方法)\n\n\n\n```java\npackage com.ajream.diy;\n\n//自定义切入面\npublic class DiyPointCut {\n public void before(){\n System.out.println(\"=====方法执行前======\");\n }\n\n public void after(){\n System.out.println(\"======方法执行后======\");\n }\n}\n\n```\n\naop配置:\n\n```xml\n\n \n\n\n \n \n \n \n \n \n \n \n \n```\n\n\n\n\n\n\n\n## aop实现方式3(使用注解开发)\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-aop/-/tree/master1)\n\n首先开启aop注解支持\n\n```xml\n\n\n\n \n\n\n \n\n \n \n```\n\n\n\n添加AnnotationPointCut.java类\n\n```java\npackage com.ajream.diy;\n\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.After;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\n\n@Aspect //标注这是一个切面\npublic class AnnotationPointCut {\n\n @Before(\"execution(* com.ajream.service.UserServiceImpl.*(..))\") //切入位置\n public void before(){\n System.out.println(\"=====方法执行前=====\");\n }\n\n @After(\"execution(* com.ajream.service.UserServiceImpl.*(..))\")\n public void after(){\n System.out.println(\"=====方法执行后=====\");\n }\n\n @Around(\"execution(* com.ajream.service.UserServiceImpl.*(..))\") //环绕增强\n public void around(ProceedingJoinPoint jp) throws Throwable {\n System.out.println(\"环绕前\");\n Object proceed = jp.proceed();// 执行方法\n System.out.println(\"执行的方法签名:\" + jp.getSignature());\n System.out.println(\"环绕后\");\n\n }\n}\n\n```\n\n> 注意,如果添加了环绕增强,首先执行环绕增强,执行了proceed方法后才会触发 `@Before` `@After` 这2个注解的内容,所以输出结果如下:\n>\n> ![image-20210808173720981](https://gitee.com/ajream/images/raw/master/img/2021-08-08 17-37-27_image-20210808173720981.png)\n\n","source":"_posts/Spring/14-springAOP.md","raw":"---\ntitle: Spring(14)-springAOP\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: a1a1eb5\ndate: 2021-07-30 13:50:41\ndescription: springAOP使用了动态代理模式,其基本使用进入文章详细了解\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# Spring Aop\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-aop/-/tree/master)\n\n## aop实现方式1(实现spring接口)\n\n### 导包\n\n使用之前要导入依赖包\n\n![image-20210803155603975](https://gitee.com/ajream/images/raw/master/img/2021-08-0315-56-08_image-20210803155603975.png)\n\n```xml\n\n \n org.springframework \n spring-context \n 5.2.9.RELEASE \n \n \n junit \n junit \n 4.12 \n \n \n org.aspectj \n aspectjweaver \n 1.9.6 \n \n \n```\n\n\n\n项目结构:\n\n```\nspring1-aop\n├───.idea\n├───src\n│ ├───main\n│ │ ├───java\n│ │ │ └───com\n│ │ │ └───ajream\n│ │ │ ├───log\n│ │ │ └───service\n│ │ └───resources\n│ └───test\n └───java\n```\n\n\n\n### 业务模块service\n\n被代理的接口,包含了要实现的具体功能\n\nUserService.java接口:\n\n```java\npackage com.ajream.service;\n\npublic interface UserService {\n void add();\n void delete();\n void update();\n void query();\n}\n```\n\nUserServiceImpl.java\n\n```java\npackage com.ajream.service;\n\npublic class UserServiceImpl implements UserService {\n @Override\n public void add() {\n System.out.println(\"增加一个用户\");\n }\n\n @Override\n public void delete() {\n System.out.println(\"删除一个用户\");\n }\n\n @Override\n public void update() {\n System.out.println(\"修改一个用户\");\n }\n\n @Override\n public void query() {\n System.out.println(\"查询一个用户\");\n }\n}\n\n```\n\n\n\n### 代理模块\n\n在原有基础上扩展一些功能\n\nLog.java (在实现某个业务(调用某个方法)前,打印一些相关信息)\n\n> 说明:重写MethodBeforeAdvice接口的before方法,说明在切入点之前执行这个方法\n\n```java\npackage com.ajream.log;\n\nimport org.springframework.aop.MethodBeforeAdvice;\n\nimport java.lang.reflect.Method;\n\npublic class Log implements MethodBeforeAdvice {\n @Override\n public void before(Method method, Object[] args, Object target) throws Throwable {\n assert target != null;\n System.out.println(target.getClass().getName() + \"的\" + method.getName() + \"方法被执行了\");\n }\n}\n\n```\n\n\n\nAfterLog.java (在实现某个业务(调用某个方法)后,打印一些相关信息) \n\n> 说明:重写AfterReturningAdvice的 `afterReturning` 方法,说明在切入点之后执行这个方法\n\n```java\npackage com.ajream.log;\n\nimport org.springframework.aop.AfterReturningAdvice;\nimport java.lang.reflect.Method;\n\npublic class AfterLog implements AfterReturningAdvice {\n @Override\n public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {\n System.out.println(\"执行了\" +method.getName()+ \"方法,返回值为:\"+returnValue);\n }\n}\n\n```\n\n\n\nApplicationContext.xml 配置文件\n\n```xml\n\n\n\n \n \n \n\n \n\n \n\n\n \n \n\n \n\n \n```\n\n> 说明:\n>\n> 1. 注意添加aop支持\n>\n> ```\n> xmlns:aop=\"http://www.springframework.org/schema/aop\"\n> http://www.springframework.org/schema/aop\n> https://www.springframework.org/schema/aop/spring-aop.xsd\n> ```\n>\n> 2. aop配置(使用原生spring api接口)\n>\n> ```xml\n> \n> \n> \n> \n> \n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n### 测试\n\nMyTest.java测试\n\n```java\nimport com.ajream.service.UserService;\nimport org.junit.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\npublic class MyTest {\n @Test\n public void test(){\n ApplicationContext context = new ClassPathXmlApplicationContext(\"ApplicationContext.xml\");\n\n //动态代理的是接口\n UserService userService = context.getBean(\"userService\", UserService.class);\n userService.add();\n }\n}\n\n```\n\n\n\n测试结果:\n\n![image-20210808163457649](https://gitee.com/ajream/images/raw/master/img/2021-08-0816-35-03_image-20210808163457649.png)\n\n\n\n## Aop实现方式2(自定义切入类)\n\n主要是aop配置方式不同\n\n之前是使用原生spring api方式,分别实现了`MethodBeforeAdvice`和`AfterReturningAdvice`这两个接口,因此在xml中只需要指明要切入哪个位置,执行什么操作,在执行目标接口的方法时spring就会根据xml配置自动去执行对应操作,如下:\n\n```xml\n\n \n \n \n \n \n \n \n \n```\n\n---\n\n现在改用自定义切入面 方式:\n\n首先添加一个切面DiyPointCut(即一个类,包含了要切入的方法)\n\n\n\n```java\npackage com.ajream.diy;\n\n//自定义切入面\npublic class DiyPointCut {\n public void before(){\n System.out.println(\"=====方法执行前======\");\n }\n\n public void after(){\n System.out.println(\"======方法执行后======\");\n }\n}\n\n```\n\naop配置:\n\n```xml\n\n \n\n\n \n \n \n \n \n \n \n \n \n```\n\n\n\n\n\n\n\n## aop实现方式3(使用注解开发)\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-aop/-/tree/master1)\n\n首先开启aop注解支持\n\n```xml\n\n\n\n \n\n\n \n\n \n \n```\n\n\n\n添加AnnotationPointCut.java类\n\n```java\npackage com.ajream.diy;\n\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.After;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\n\n@Aspect //标注这是一个切面\npublic class AnnotationPointCut {\n\n @Before(\"execution(* com.ajream.service.UserServiceImpl.*(..))\") //切入位置\n public void before(){\n System.out.println(\"=====方法执行前=====\");\n }\n\n @After(\"execution(* com.ajream.service.UserServiceImpl.*(..))\")\n public void after(){\n System.out.println(\"=====方法执行后=====\");\n }\n\n @Around(\"execution(* com.ajream.service.UserServiceImpl.*(..))\") //环绕增强\n public void around(ProceedingJoinPoint jp) throws Throwable {\n System.out.println(\"环绕前\");\n Object proceed = jp.proceed();// 执行方法\n System.out.println(\"执行的方法签名:\" + jp.getSignature());\n System.out.println(\"环绕后\");\n\n }\n}\n\n```\n\n> 注意,如果添加了环绕增强,首先执行环绕增强,执行了proceed方法后才会触发 `@Before` `@After` 这2个注解的内容,所以输出结果如下:\n>\n> ![image-20210808173720981](https://gitee.com/ajream/images/raw/master/img/2021-08-08 17-37-27_image-20210808173720981.png)\n\n","slug":"Spring/14-springAOP","published":1,"updated":"2021-09-10T03:16:18.937Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pb002jakvefzhpc3tk","content":"Spring Aop 项目地址
\naop实现方式1(实现spring接口) 导包 使用之前要导入依赖包
\n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.9.RELEASE</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.6</version > </dependency > </dependencies >
\n项目结构:
\n1 2 3 4 5 6 7 8 9 10 11 12 spring1-aop ├───.idea ├───src │ ├───main │ │ ├───java │ │ │ └───com │ │ │ └───ajream │ │ │ ├───log │ │ │ └───service │ │ └───resources │ └───test └───java
\n业务模块service 被代理的接口,包含了要实现的具体功能
\nUserService.java接口:
\n1 2 3 4 5 6 7 8 package com.ajream.service;public interface UserService { void add () ; void delete () ; void update () ; void query () ; }
\nUserServiceImpl.java
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.ajream.service;public class UserServiceImpl implements UserService { @Override public void add () { System.out.println("增加一个用户" ); } @Override public void delete () { System.out.println("删除一个用户" ); } @Override public void update () { System.out.println("修改一个用户" ); } @Override public void query () { System.out.println("查询一个用户" ); } }
\n代理模块 在原有基础上扩展一些功能
\nLog.java (在实现某个业务(调用某个方法)前,打印一些相关信息)
\n\n说明:重写MethodBeforeAdvice接口的before方法,说明在切入点之前执行这个方法
\n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.ajream.log;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class Log implements MethodBeforeAdvice { @Override public void before (Method method, Object[] args, Object target) throws Throwable { assert target != null ; System.out.println(target.getClass().getName() + "的" + method.getName() + "方法被执行了" ); } }
\nAfterLog.java (在实现某个业务(调用某个方法)后,打印一些相关信息)
\n\n说明:重写AfterReturningAdvice的 afterReturning
方法,说明在切入点之后执行这个方法
\n \n1 2 3 4 5 6 7 8 9 10 11 12 package com.ajream.log;import org.springframework.aop.AfterReturningAdvice;import java.lang.reflect.Method;public class AfterLog implements AfterReturningAdvice { @Override public void afterReturning (Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了" +method.getName()+ "方法,返回值为:" +returnValue); } }
\nApplicationContext.xml 配置文件
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop ="http://www.springframework.org/schema/aop" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="userService" class ="com.ajream.service.UserServiceImpl" /> <bean id ="log" class ="com.ajream.log.Log" /> <bean id ="afterLog" class ="com.ajream.log.AfterLog" /> <aop:config > <aop:pointcut id ="pointCut" expression ="execution(* com.ajream.service.UserServiceImpl.*(..))" /> <aop:advisor advice-ref ="afterLog" pointcut-ref ="pointCut" /> <aop:advisor advice-ref ="log" pointcut-ref ="pointCut" /> </aop:config > </beans >
\n\n说明:
\n\n注意添加aop支持
\n1 2 3 xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
\n \naop配置(使用原生spring api接口)
\n1 2 3 4 5 6 7 8 9 <aop:config > <aop:pointcut id ="pointCut" expression ="execution(* com.ajream.service.UserServiceImpl.*(..))" /> <aop:advisor advice-ref ="afterLog" pointcut-ref ="pointCut" /> <aop:advisor advice-ref ="log" pointcut-ref ="pointCut" /> </aop:config >
\n \n \n \n测试 MyTest.java测试
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import com.ajream.service.UserService;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyTest { @Test public void test () { ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml" ); UserService userService = context.getBean("userService" , UserService.class); userService.add(); } }
\n测试结果:
\n
\nAop实现方式2(自定义切入类) 主要是aop配置方式不同
\n之前是使用原生spring api方式,分别实现了MethodBeforeAdvice
和AfterReturningAdvice
这两个接口,因此在xml中只需要指明要切入哪个位置,执行什么操作,在执行目标接口的方法时spring就会根据xml配置自动去执行对应操作,如下:
\n1 2 3 4 5 6 7 8 9 <aop:config > <aop:pointcut id ="pointCut" expression ="execution(* com.ajream.service.UserServiceImpl.*(..))" /> <aop:advisor advice-ref ="afterLog" pointcut-ref ="pointCut" /> <aop:advisor advice-ref ="log" pointcut-ref ="pointCut" /> </aop:config >
\n \n现在改用自定义切入面 方式:
\n首先添加一个切面DiyPointCut(即一个类,包含了要切入的方法)
\n1 2 3 4 5 6 7 8 9 10 11 12 13 package com.ajream.diy;public class DiyPointCut { public void before () { System.out.println("=====方法执行前======" ); } public void after () { System.out.println("======方法执行后======" ); } }
\naop配置:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 <bean id ="diy" class ="com.ajream.diy.DiyPointCut" /> <aop:config > <aop:aspect ref ="diy" > <aop:pointcut id ="point" expression ="execution(* com.ajream.service.UserServiceImpl.*(..))" /> <aop:before method ="before" pointcut-ref ="point" /> <aop:after method ="after" pointcut-ref ="point" /> </aop:aspect > </aop:config >
\naop实现方式3(使用注解开发) 项目地址
\n首先开启aop注解支持
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop ="http://www.springframework.org/schema/aop" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="userService" class ="com.ajream.service.UserServiceImpl" /> <bean id ="annotationPointCut" class ="com.ajream.diy.AnnotationPointCut" /> <aop:aspectj-autoproxy /> </beans >
\n添加AnnotationPointCut.java类
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.ajream.diy;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspect public class AnnotationPointCut { @Before("execution(* com.ajream.service.UserServiceImpl.*(..))") public void before () { System.out.println("=====方法执行前=====" ); } @After("execution(* com.ajream.service.UserServiceImpl.*(..))") public void after () { System.out.println("=====方法执行后=====" ); } @Around("execution(* com.ajream.service.UserServiceImpl.*(..))") public void around (ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前" ); Object proceed = jp.proceed(); System.out.println("执行的方法签名:" + jp.getSignature()); System.out.println("环绕后" ); } }
\n\n注意,如果添加了环绕增强,首先执行环绕增强,执行了proceed方法后才会触发 @Before
@After
这2个注解的内容,所以输出结果如下:
\n
\n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"Spring Aop 项目地址
\naop实现方式1(实现spring接口) 导包 使用之前要导入依赖包
\n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.9.RELEASE</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.6</version > </dependency > </dependencies >
\n项目结构:
\n1 2 3 4 5 6 7 8 9 10 11 12 spring1-aop ├───.idea ├───src │ ├───main │ │ ├───java │ │ │ └───com │ │ │ └───ajream │ │ │ ├───log │ │ │ └───service │ │ └───resources │ └───test └───java
\n业务模块service 被代理的接口,包含了要实现的具体功能
\nUserService.java接口:
\n1 2 3 4 5 6 7 8 package com.ajream.service;public interface UserService { void add () ; void delete () ; void update () ; void query () ; }
\nUserServiceImpl.java
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.ajream.service;public class UserServiceImpl implements UserService { @Override public void add () { System.out.println("增加一个用户" ); } @Override public void delete () { System.out.println("删除一个用户" ); } @Override public void update () { System.out.println("修改一个用户" ); } @Override public void query () { System.out.println("查询一个用户" ); } }
\n代理模块 在原有基础上扩展一些功能
\nLog.java (在实现某个业务(调用某个方法)前,打印一些相关信息)
\n\n说明:重写MethodBeforeAdvice接口的before方法,说明在切入点之前执行这个方法
\n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.ajream.log;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class Log implements MethodBeforeAdvice { @Override public void before (Method method, Object[] args, Object target) throws Throwable { assert target != null ; System.out.println(target.getClass().getName() + "的" + method.getName() + "方法被执行了" ); } }
\nAfterLog.java (在实现某个业务(调用某个方法)后,打印一些相关信息)
\n\n说明:重写AfterReturningAdvice的 afterReturning
方法,说明在切入点之后执行这个方法
\n \n1 2 3 4 5 6 7 8 9 10 11 12 package com.ajream.log;import org.springframework.aop.AfterReturningAdvice;import java.lang.reflect.Method;public class AfterLog implements AfterReturningAdvice { @Override public void afterReturning (Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了" +method.getName()+ "方法,返回值为:" +returnValue); } }
\nApplicationContext.xml 配置文件
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop ="http://www.springframework.org/schema/aop" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="userService" class ="com.ajream.service.UserServiceImpl" /> <bean id ="log" class ="com.ajream.log.Log" /> <bean id ="afterLog" class ="com.ajream.log.AfterLog" /> <aop:config > <aop:pointcut id ="pointCut" expression ="execution(* com.ajream.service.UserServiceImpl.*(..))" /> <aop:advisor advice-ref ="afterLog" pointcut-ref ="pointCut" /> <aop:advisor advice-ref ="log" pointcut-ref ="pointCut" /> </aop:config > </beans >
\n\n说明:
\n\n注意添加aop支持
\n1 2 3 xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
\n \naop配置(使用原生spring api接口)
\n1 2 3 4 5 6 7 8 9 <aop:config > <aop:pointcut id ="pointCut" expression ="execution(* com.ajream.service.UserServiceImpl.*(..))" /> <aop:advisor advice-ref ="afterLog" pointcut-ref ="pointCut" /> <aop:advisor advice-ref ="log" pointcut-ref ="pointCut" /> </aop:config >
\n \n \n \n测试 MyTest.java测试
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import com.ajream.service.UserService;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyTest { @Test public void test () { ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml" ); UserService userService = context.getBean("userService" , UserService.class); userService.add(); } }
\n测试结果:
\n
\nAop实现方式2(自定义切入类) 主要是aop配置方式不同
\n之前是使用原生spring api方式,分别实现了MethodBeforeAdvice
和AfterReturningAdvice
这两个接口,因此在xml中只需要指明要切入哪个位置,执行什么操作,在执行目标接口的方法时spring就会根据xml配置自动去执行对应操作,如下:
\n1 2 3 4 5 6 7 8 9 <aop:config > <aop:pointcut id ="pointCut" expression ="execution(* com.ajream.service.UserServiceImpl.*(..))" /> <aop:advisor advice-ref ="afterLog" pointcut-ref ="pointCut" /> <aop:advisor advice-ref ="log" pointcut-ref ="pointCut" /> </aop:config >
\n \n现在改用自定义切入面 方式:
\n首先添加一个切面DiyPointCut(即一个类,包含了要切入的方法)
\n1 2 3 4 5 6 7 8 9 10 11 12 13 package com.ajream.diy;public class DiyPointCut { public void before () { System.out.println("=====方法执行前======" ); } public void after () { System.out.println("======方法执行后======" ); } }
\naop配置:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 <bean id ="diy" class ="com.ajream.diy.DiyPointCut" /> <aop:config > <aop:aspect ref ="diy" > <aop:pointcut id ="point" expression ="execution(* com.ajream.service.UserServiceImpl.*(..))" /> <aop:before method ="before" pointcut-ref ="point" /> <aop:after method ="after" pointcut-ref ="point" /> </aop:aspect > </aop:config >
\naop实现方式3(使用注解开发) 项目地址
\n首先开启aop注解支持
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop ="http://www.springframework.org/schema/aop" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="userService" class ="com.ajream.service.UserServiceImpl" /> <bean id ="annotationPointCut" class ="com.ajream.diy.AnnotationPointCut" /> <aop:aspectj-autoproxy /> </beans >
\n添加AnnotationPointCut.java类
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.ajream.diy;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspect public class AnnotationPointCut { @Before("execution(* com.ajream.service.UserServiceImpl.*(..))") public void before () { System.out.println("=====方法执行前=====" ); } @After("execution(* com.ajream.service.UserServiceImpl.*(..))") public void after () { System.out.println("=====方法执行后=====" ); } @Around("execution(* com.ajream.service.UserServiceImpl.*(..))") public void around (ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前" ); Object proceed = jp.proceed(); System.out.println("执行的方法签名:" + jp.getSignature()); System.out.println("环绕后" ); } }
\n\n注意,如果添加了环绕增强,首先执行环绕增强,执行了proceed方法后才会触发 @Before
@After
这2个注解的内容,所以输出结果如下:
\n
\n \n"},{"title":"Spring(2)-SpringIOC","abbrlink":"752d4bdb","date":"2021-07-24T07:57:27.000Z","description":"springIOC的结构与内容","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# SpringIOC\n\n\n\n![Spring+IOC](https://gitee.com/ajream/images/raw/master/img/2021-07-3021-11-35_Spring+IOC.svg)\n\n\n\n## IOC理论推导\n\n1. userDao接口\n\n userDaoImpl实现类(Mysql, Oracle, ...)\n\n2. userService业务接口\n\n userServiceImpl 业务实现\n\n\n\n![ioc](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-17-37_ioc.svg)\n\n\n\n\n\n","source":"_posts/Spring/2-SpringIOC.md","raw":"---\ntitle: Spring(2)-SpringIOC\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 752d4bdb\ndate: 2021-07-24 15:57:27\ndescription: springIOC的结构与内容\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# SpringIOC\n\n\n\n![Spring+IOC](https://gitee.com/ajream/images/raw/master/img/2021-07-3021-11-35_Spring+IOC.svg)\n\n\n\n## IOC理论推导\n\n1. userDao接口\n\n userDaoImpl实现类(Mysql, Oracle, ...)\n\n2. userService业务接口\n\n userServiceImpl 业务实现\n\n\n\n![ioc](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-17-37_ioc.svg)\n\n\n\n\n\n","slug":"Spring/2-SpringIOC","published":1,"updated":"2021-09-10T03:18:09.108Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pc002makvec2g3cfzf","content":"SpringIOC
\nIOC理论推导 \nuserDao接口
\nuserDaoImpl实现类(Mysql, Oracle, …)
\n \nuserService业务接口
\nuserServiceImpl 业务实现
\n \n \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"SpringIOC
\nIOC理论推导 \nuserDao接口
\nuserDaoImpl实现类(Mysql, Oracle, …)
\n \nuserService业务接口
\nuserServiceImpl 业务实现
\n \n \n
\n"},{"title":"Spring(4)-HelloSpring程序","abbrlink":"59e405f3","date":"2021-07-25T04:48:31.000Z","description":"第一个spring程序","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n# HelloSpring项目\n\n\n\n## 使用maven创建\n\n\n\n创建后目录结构如下:\n\n![image-20210729125943211](https://gitee.com/ajream/images/raw/master/img/2021-07-2912-59-47_image-20210729125943211.png)\n\n\n\n\n\npom.xml:\n\n```xml\n\n\n 4.0.0 \n\n org.ajream \n hello_spring \n 1.0-SNAPSHOT \n \n \n org.springframework \n spring-context \n 5.2.9.RELEASE \n test \n \n \n\n \n 8 \n 8 \n \n\n \n```\n\n\n\n## 代码\n\n1. 在java文件夹下创建`com.xxx.pojo.Hello`类\n\n![image-20210729130640647](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-06-43_image-20210729130640647.png)\n\n```java\npackage com.ajream.pojo;\n\npublic class Hello {\n private String str1; //先不管属性名为什么是str1\n\n public void setStr2(String s){ //先不管为什么函数名设置为setStr2\n this.str1 = s;\n }\n\n public void printStr(){\n System.out.println(\"Hello:->\" + str1);\n }\n}\n\n```\n\n\n\n2. 在resources下创建`beans.xml`配置文件\n\n```xml\n\n\n\n \n\n \n\n \n \n```\n\n> 该文件内容模板可以从spring[官网](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core)中找到\n>\n> ![image-20210729131316578](https://gitee.com/ajream/images/raw/master/img/2021-07-29 13-13-19_image-20210729131316578.png)\n>\n> ```xml\n> \n> xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n> xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n> https://www.springframework.org/schema/beans/spring-beans.xsd\">\n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n\n\n3. 在test/java下创建MyTest类来进行测试输出:\n\n```java\nimport com.ajream.pojo.Hello;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\npublic class MyTest {\n public static void main(String[] args){\n ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\"); //加载xml配置文件\n Hello h = (Hello) context.getBean(\"helloworld\");\n h.printStr();\n }\n}\n\n```\n\n输出:\n\n![image-20210729131704285](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-17-08_image-20210729131704285.png)\n\n\n\n\n\n## 三者之间的关系\n\n![image-20210729134624753](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-46-29_image-20210729134624753.png)\n\n\n\n执行流程:\n\n1. 获取 `beans.xml` 配置文件信息\n2. 根据beans.xml中的id获取bean,创建对象h,并调用了setStr2方法\n3. 调用函数printStr\n\n\n\n\n\n","source":"_posts/Spring/4-HelloSpring程序.md","raw":"---\ntitle: Spring(4)-HelloSpring程序\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 59e405f3\ndate: 2021-07-25 12:48:31\ndescription: 第一个spring程序\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n# HelloSpring项目\n\n\n\n## 使用maven创建\n\n\n\n创建后目录结构如下:\n\n![image-20210729125943211](https://gitee.com/ajream/images/raw/master/img/2021-07-2912-59-47_image-20210729125943211.png)\n\n\n\n\n\npom.xml:\n\n```xml\n\n\n 4.0.0 \n\n org.ajream \n hello_spring \n 1.0-SNAPSHOT \n \n \n org.springframework \n spring-context \n 5.2.9.RELEASE \n test \n \n \n\n \n 8 \n 8 \n \n\n \n```\n\n\n\n## 代码\n\n1. 在java文件夹下创建`com.xxx.pojo.Hello`类\n\n![image-20210729130640647](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-06-43_image-20210729130640647.png)\n\n```java\npackage com.ajream.pojo;\n\npublic class Hello {\n private String str1; //先不管属性名为什么是str1\n\n public void setStr2(String s){ //先不管为什么函数名设置为setStr2\n this.str1 = s;\n }\n\n public void printStr(){\n System.out.println(\"Hello:->\" + str1);\n }\n}\n\n```\n\n\n\n2. 在resources下创建`beans.xml`配置文件\n\n```xml\n\n\n\n \n\n \n\n \n \n```\n\n> 该文件内容模板可以从spring[官网](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core)中找到\n>\n> ![image-20210729131316578](https://gitee.com/ajream/images/raw/master/img/2021-07-29 13-13-19_image-20210729131316578.png)\n>\n> ```xml\n> \n> xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n> xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n> https://www.springframework.org/schema/beans/spring-beans.xsd\">\n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n\n\n3. 在test/java下创建MyTest类来进行测试输出:\n\n```java\nimport com.ajream.pojo.Hello;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\npublic class MyTest {\n public static void main(String[] args){\n ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\"); //加载xml配置文件\n Hello h = (Hello) context.getBean(\"helloworld\");\n h.printStr();\n }\n}\n\n```\n\n输出:\n\n![image-20210729131704285](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-17-08_image-20210729131704285.png)\n\n\n\n\n\n## 三者之间的关系\n\n![image-20210729134624753](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-46-29_image-20210729134624753.png)\n\n\n\n执行流程:\n\n1. 获取 `beans.xml` 配置文件信息\n2. 根据beans.xml中的id获取bean,创建对象h,并调用了setStr2方法\n3. 调用函数printStr\n\n\n\n\n\n","slug":"Spring/4-HelloSpring程序","published":1,"updated":"2021-09-10T03:18:01.840Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pd002qakve0ic15g6q","content":"HelloSpring项目 使用maven创建 创建后目录结构如下:
\n
\npom.xml:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > org.ajream</groupId > <artifactId > hello_spring</artifactId > <version > 1.0-SNAPSHOT</version > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.9.RELEASE</version > <scope > test</scope > </dependency > </dependencies > <properties > <maven.compiler.source > 8</maven.compiler.source > <maven.compiler.target > 8</maven.compiler.target > </properties > </project >
\n代码 \n在java文件夹下创建com.xxx.pojo.Hello
类 \n \n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.ajream.pojo;public class Hello { private String str1; public void setStr2 (String s) { this .str1 = s; } public void printStr () { System.out.println("Hello:->" + str1); } }
\n\n在resources下创建beans.xml
配置文件 \n \n1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="helloworld" class ="com.ajream.pojo.Hello" > <property name ="str2" value ="spring" /> </bean > </beans >
\n\n该文件内容模板可以从spring官网 中找到
\n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="..." class ="..." > </bean > <bean id ="..." class ="..." > </bean > </beans >
\n \n\n在test/java下创建MyTest类来进行测试输出: \n \n1 2 3 4 5 6 7 8 9 10 11 12 import com.ajream.pojo.Hello;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyTest { public static void main (String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml" ); Hello h = (Hello) context.getBean("helloworld" ); h.printStr(); } }
\n输出:
\n
\n三者之间的关系
\n执行流程:
\n\n获取 beans.xml
配置文件信息 \n根据beans.xml中的id获取bean,创建对象h,并调用了setStr2方法 \n调用函数printStr \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"HelloSpring项目 使用maven创建 创建后目录结构如下:
\n
\npom.xml:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > org.ajream</groupId > <artifactId > hello_spring</artifactId > <version > 1.0-SNAPSHOT</version > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.9.RELEASE</version > <scope > test</scope > </dependency > </dependencies > <properties > <maven.compiler.source > 8</maven.compiler.source > <maven.compiler.target > 8</maven.compiler.target > </properties > </project >
\n代码 \n在java文件夹下创建com.xxx.pojo.Hello
类 \n \n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.ajream.pojo;public class Hello { private String str1; public void setStr2 (String s) { this .str1 = s; } public void printStr () { System.out.println("Hello:->" + str1); } }
\n\n在resources下创建beans.xml
配置文件 \n \n1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="helloworld" class ="com.ajream.pojo.Hello" > <property name ="str2" value ="spring" /> </bean > </beans >
\n\n该文件内容模板可以从spring官网 中找到
\n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="..." class ="..." > </bean > <bean id ="..." class ="..." > </bean > </beans >
\n \n\n在test/java下创建MyTest类来进行测试输出: \n \n1 2 3 4 5 6 7 8 9 10 11 12 import com.ajream.pojo.Hello;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyTest { public static void main (String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml" ); Hello h = (Hello) context.getBean("helloworld" ); h.printStr(); } }
\n输出:
\n
\n三者之间的关系
\n执行流程:
\n\n获取 beans.xml
配置文件信息 \n根据beans.xml中的id获取bean,创建对象h,并调用了setStr2方法 \n调用函数printStr \n \n"},{"title":"Spring(5)-ioc创建对象方式","abbrlink":"d8642fb9","date":"2021-07-26T06:58:44.000Z","description":"spring如何使用配置文件来创建对象、注入属性","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n# ioc创建对象方式\n\n\n\n## 构造函数注入\n\nUser.java\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String name;\n\n public User(String name){ //构造函数\n this.name = name;\n }\n\n public String getName(){\n return name;\n }\n\n public void show(){\n System.out.println(\"name: \" + name);\n }\n}\n\n```\n\n\n\n1. 下标指定参数\n\n ```xml\n \n \n \n ```\n\n \n\n2. 变量名指定参数\n\n ```xml\n \n \n \n ```\n\n \n\n3. 类型指定参数(不建议,有可能多个参数的类型相同)\n\n ```xml\n \n \n \n ```\n\n4. 构造函数没有参数的话\n\n ```xml\n \n \n ```\n\n\n\n## set注入\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String name;\n\n public void setName(String name){ // set注入\n this.name = name;\n }\n\n public String getName(){\n return name;\n }\n\n public void show(){\n System.out.println(\"name: \" + name);\n }\n}\n```\n\n\n\n```xml\n\n \n \n```\n\n","source":"_posts/Spring/5-ioc创建对象方式.md","raw":"---\ntitle: Spring(5)-ioc创建对象方式\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: d8642fb9\ndate: 2021-07-26 14:58:44\ndescription: spring如何使用配置文件来创建对象、注入属性\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n# ioc创建对象方式\n\n\n\n## 构造函数注入\n\nUser.java\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String name;\n\n public User(String name){ //构造函数\n this.name = name;\n }\n\n public String getName(){\n return name;\n }\n\n public void show(){\n System.out.println(\"name: \" + name);\n }\n}\n\n```\n\n\n\n1. 下标指定参数\n\n ```xml\n \n \n \n ```\n\n \n\n2. 变量名指定参数\n\n ```xml\n \n \n \n ```\n\n \n\n3. 类型指定参数(不建议,有可能多个参数的类型相同)\n\n ```xml\n \n \n \n ```\n\n4. 构造函数没有参数的话\n\n ```xml\n \n \n ```\n\n\n\n## set注入\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String name;\n\n public void setName(String name){ // set注入\n this.name = name;\n }\n\n public String getName(){\n return name;\n }\n\n public void show(){\n System.out.println(\"name: \" + name);\n }\n}\n```\n\n\n\n```xml\n\n \n \n```\n\n","slug":"Spring/5-ioc创建对象方式","published":1,"updated":"2021-09-10T03:18:20.605Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pe002takvedtbz9vw9","content":"ioc创建对象方式 构造函数注入 User.java
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.ajream.pojo;public class User { private String name; public User (String name) { this .name = name; } public String getName () { return name; } public void show () { System.out.println("name: " + name); } }
\n\n下标指定参数
\n1 2 3 <bean id ="user" class ="com.ajream.pojo.User" > <constructor-arg index ="0" value ="张三" /> </bean >
\n \n \n\n变量名指定参数
\n1 2 3 <bean id ="user" class ="com.ajream.pojo.User" > <constructor-arg name ="name" value ="李四" /> </bean >
\n \n \n\n类型指定参数(不建议,有可能多个参数的类型相同)
\n1 2 3 <bean id ="user" class ="com.ajream.pojo.User" > <constructor-arg type ="String" value ="王五" /> </bean >
\n \n构造函数没有参数的话
\n1 2 <bean id ="user" class ="com.ajream.pojo.User" > </bean >
\n \n \nset注入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.ajream.pojo;public class User { private String name; public void setName (String name) { this .name = name; } public String getName () { return name; } public void show () { System.out.println("name: " + name); } }
\n1 2 3 <bean id ="user" class ="com.ajream.pojo.User" > <property name ="name" value ="李四" /> </bean >
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"ioc创建对象方式 构造函数注入 User.java
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.ajream.pojo;public class User { private String name; public User (String name) { this .name = name; } public String getName () { return name; } public void show () { System.out.println("name: " + name); } }
\n\n下标指定参数
\n1 2 3 <bean id ="user" class ="com.ajream.pojo.User" > <constructor-arg index ="0" value ="张三" /> </bean >
\n \n \n\n变量名指定参数
\n1 2 3 <bean id ="user" class ="com.ajream.pojo.User" > <constructor-arg name ="name" value ="李四" /> </bean >
\n \n \n\n类型指定参数(不建议,有可能多个参数的类型相同)
\n1 2 3 <bean id ="user" class ="com.ajream.pojo.User" > <constructor-arg type ="String" value ="王五" /> </bean >
\n \n构造函数没有参数的话
\n1 2 <bean id ="user" class ="com.ajream.pojo.User" > </bean >
\n \n \nset注入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.ajream.pojo;public class User { private String name; public void setName (String name) { this .name = name; } public String getName () { return name; } public void show () { System.out.println("name: " + name); } }
\n1 2 3 <bean id ="user" class ="com.ajream.pojo.User" > <property name ="name" value ="李四" /> </bean >
\n"},{"title":"Spring(3)-ioc控制反转","abbrlink":"d959138b","date":"2021-07-25T05:25:37.000Z","description":"spring控制反转(IOC)思想举例理解","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n# IOC控制反转思想举例\n\n\n\n## 创建工程\n\n利用Maven创建一个空白工程spring1,其目录结构如下:\n\n![image-20210728212756154](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-27-57_image-20210728212756154.png)\n\n\n\n然后创建以下几个接口和类:\n\n1. Dao层\n2. Service业务层\n3. 用户层(test表示用户测试)\n\n\n![image-20210728213117047](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-31-18_image-20210728213117047.png)\n\n\n\n\n\n## 代码实现\n\n目标:用户要在test1中创建一个对象,调用Dao层的某个方法 `getUser()`来实现某种功能\n\n\n\n\n\n### Dao层\n\nUserDao接口:\n\n```java\npackage com.ajream.Dao;\n\npublic interface UserDao {\n void getUser();\n}\n```\n\nUserDaoImpl:\n\n```java\npackage com.ajream.Dao;\n\npublic class UserDaoImpl implements UserDao{\n @Override\n public void getUser() {\n System.out.println(\"默认获取用户数据。。。\");\n }\n}\n```\n\nUserMysqlDaoImpl:\n\n```java\npackage com.ajream.Dao;\n\npublic class UserMysqlDaoImpl implements UserDao{\n\n @Override\n public void getUser() {\n System.out.println(\"MySQL获取用户数据。。。\");\n }\n}\n\n```\n\n\n\n\n\n\n\n### 没有使用控制反转时的业务层\n\nUserService接口:\n\n```java\npackage com.ajream.Service;\n\npublic interface UserService {\n void getUser();\n}\n```\n\nUserServiceImpl:\n\n![image-20210728215047346](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-50-48_image-20210728215047346.png)\n\n即:\n\n```java\npackage com.ajream.Service;\n\nimport com.ajream.Dao.UserDao;\nimport com.ajream.Dao.UserDaoImpl;\n\npublic class UserServiceImpl implements UserService {\n \n private UserDao userDao = new UserDaoImpl(); //注意此处\n\n public UserServiceImpl() {\n }\n\n public void getUser() {\n this.userDao.getUser();\n }\n}\n```\n\n\n\n> 注意没有使用IOC时第8行代码,这样写的缺点:\n>\n> 这样指定创建了一个对象UserDaoImpl,假如我要创建另一个对象UserMysqlDaoImpl,用户就需要修改业务层这一行代码,例如改为:\n>\n> ```java\n> private UserDao userDao = new UserMysqlDaoImpl();\n> ```\n>\n> 很明显,这样有些不方便,作为用户,应该不需要关心业务的具体实现\n\n\n\n\n\n### 使用控制反转后的业务层\n\nUserService接口不变\n\nUserServiceImpl使用setUser来创建对象:\n\n![image-20210728220123436](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-01-24_image-20210728220123436.png)\n\n\n\n```java\npackage com.ajream.Service;\n\nimport com.ajream.Dao.UserDao;\n\n\npublic class UserServiceImpl implements UserService{\n private UserDao userDao;\n @Override\n public void getUser() {\n userDao.getUser();\n }\n public void setUser(UserDao userDao){\n this.userDao = userDao;\n }\n}\n```\n\n> 用户可以通过调用`setUser()`方法来创建不同对象(只需要传入不同参数即可)\n\n\n\n对比:\n\n![image-20210728221513985](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-15-15_image-20210728221513985.png)\n\n---\n\n\n\ntest1(这里是用户调用业务层实现业务的部分)\n\n- 没有使用IOC时\n\n```java\npackage com.ajream;\n\nimport com.ajream.Dao.UserDaoImpl;\nimport com.ajream.Dao.UserMysqlDaoImpl;\nimport com.ajream.Service.UserService;\nimport com.ajream.Service.UserServiceImpl;\n\npublic class test1 {\n public static void main(String[] args) {\n \n UserService userService = new UserServiceImpl();\n\n userService.getUser();\n }\n}\n\n```\n\n\n\n- 使用了IOC的思想后\n\n```java\npackage com.ajream;\n\nimport com.ajream.Dao.UserDaoImpl;\nimport com.ajream.Dao.UserMysqlDaoImpl;\nimport com.ajream.Service.UserService;\nimport com.ajream.Service.UserServiceImpl;\n\npublic class test1 {\n public static void main(String[] args) {\n \n UserService userService = new UserServiceImpl();\n \n// 通过修改setUser参数来反向创建不同对象\n ((UserServiceImpl) userService).setUser(new UserDaoImpl()); \n userService.getUser();\n \n ((UserServiceImpl) userService).setUser(new UserMysqlDaoImpl());\n userService.getUser();\n }\n}\n\n```\n\n\n\n对比:\n\n![image-20210728221756345](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-17-57_image-20210728221756345.png)\n\n\n\n\n\n## 一张图表示三者之间关系\n\n![ioc](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-17-37_ioc.svg)\n\n\n\n用户希望通过调用业务层来实现各个具体的业务如 `Mysql`、`Oracle`,...\n\n\n\n","source":"_posts/Spring/3-ioc控制反转.md","raw":"---\ntitle: Spring(3)-ioc控制反转\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: d959138b\ndate: 2021-07-25 13:25:37\ndescription: spring控制反转(IOC)思想举例理解\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n# IOC控制反转思想举例\n\n\n\n## 创建工程\n\n利用Maven创建一个空白工程spring1,其目录结构如下:\n\n![image-20210728212756154](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-27-57_image-20210728212756154.png)\n\n\n\n然后创建以下几个接口和类:\n\n1. Dao层\n2. Service业务层\n3. 用户层(test表示用户测试)\n\n\n![image-20210728213117047](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-31-18_image-20210728213117047.png)\n\n\n\n\n\n## 代码实现\n\n目标:用户要在test1中创建一个对象,调用Dao层的某个方法 `getUser()`来实现某种功能\n\n\n\n\n\n### Dao层\n\nUserDao接口:\n\n```java\npackage com.ajream.Dao;\n\npublic interface UserDao {\n void getUser();\n}\n```\n\nUserDaoImpl:\n\n```java\npackage com.ajream.Dao;\n\npublic class UserDaoImpl implements UserDao{\n @Override\n public void getUser() {\n System.out.println(\"默认获取用户数据。。。\");\n }\n}\n```\n\nUserMysqlDaoImpl:\n\n```java\npackage com.ajream.Dao;\n\npublic class UserMysqlDaoImpl implements UserDao{\n\n @Override\n public void getUser() {\n System.out.println(\"MySQL获取用户数据。。。\");\n }\n}\n\n```\n\n\n\n\n\n\n\n### 没有使用控制反转时的业务层\n\nUserService接口:\n\n```java\npackage com.ajream.Service;\n\npublic interface UserService {\n void getUser();\n}\n```\n\nUserServiceImpl:\n\n![image-20210728215047346](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-50-48_image-20210728215047346.png)\n\n即:\n\n```java\npackage com.ajream.Service;\n\nimport com.ajream.Dao.UserDao;\nimport com.ajream.Dao.UserDaoImpl;\n\npublic class UserServiceImpl implements UserService {\n \n private UserDao userDao = new UserDaoImpl(); //注意此处\n\n public UserServiceImpl() {\n }\n\n public void getUser() {\n this.userDao.getUser();\n }\n}\n```\n\n\n\n> 注意没有使用IOC时第8行代码,这样写的缺点:\n>\n> 这样指定创建了一个对象UserDaoImpl,假如我要创建另一个对象UserMysqlDaoImpl,用户就需要修改业务层这一行代码,例如改为:\n>\n> ```java\n> private UserDao userDao = new UserMysqlDaoImpl();\n> ```\n>\n> 很明显,这样有些不方便,作为用户,应该不需要关心业务的具体实现\n\n\n\n\n\n### 使用控制反转后的业务层\n\nUserService接口不变\n\nUserServiceImpl使用setUser来创建对象:\n\n![image-20210728220123436](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-01-24_image-20210728220123436.png)\n\n\n\n```java\npackage com.ajream.Service;\n\nimport com.ajream.Dao.UserDao;\n\n\npublic class UserServiceImpl implements UserService{\n private UserDao userDao;\n @Override\n public void getUser() {\n userDao.getUser();\n }\n public void setUser(UserDao userDao){\n this.userDao = userDao;\n }\n}\n```\n\n> 用户可以通过调用`setUser()`方法来创建不同对象(只需要传入不同参数即可)\n\n\n\n对比:\n\n![image-20210728221513985](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-15-15_image-20210728221513985.png)\n\n---\n\n\n\ntest1(这里是用户调用业务层实现业务的部分)\n\n- 没有使用IOC时\n\n```java\npackage com.ajream;\n\nimport com.ajream.Dao.UserDaoImpl;\nimport com.ajream.Dao.UserMysqlDaoImpl;\nimport com.ajream.Service.UserService;\nimport com.ajream.Service.UserServiceImpl;\n\npublic class test1 {\n public static void main(String[] args) {\n \n UserService userService = new UserServiceImpl();\n\n userService.getUser();\n }\n}\n\n```\n\n\n\n- 使用了IOC的思想后\n\n```java\npackage com.ajream;\n\nimport com.ajream.Dao.UserDaoImpl;\nimport com.ajream.Dao.UserMysqlDaoImpl;\nimport com.ajream.Service.UserService;\nimport com.ajream.Service.UserServiceImpl;\n\npublic class test1 {\n public static void main(String[] args) {\n \n UserService userService = new UserServiceImpl();\n \n// 通过修改setUser参数来反向创建不同对象\n ((UserServiceImpl) userService).setUser(new UserDaoImpl()); \n userService.getUser();\n \n ((UserServiceImpl) userService).setUser(new UserMysqlDaoImpl());\n userService.getUser();\n }\n}\n\n```\n\n\n\n对比:\n\n![image-20210728221756345](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-17-57_image-20210728221756345.png)\n\n\n\n\n\n## 一张图表示三者之间关系\n\n![ioc](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-17-37_ioc.svg)\n\n\n\n用户希望通过调用业务层来实现各个具体的业务如 `Mysql`、`Oracle`,...\n\n\n\n","slug":"Spring/3-ioc控制反转","published":1,"updated":"2021-09-10T03:17:53.109Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pf002xakve102r3c73","content":"IOC控制反转思想举例 创建工程 利用Maven创建一个空白工程spring1,其目录结构如下:
\n
\n然后创建以下几个接口和类:
\n\nDao层 \nService业务层 \n用户层(test表示用户测试) \n \n
\n代码实现 目标:用户要在test1中创建一个对象,调用Dao层的某个方法 getUser()
来实现某种功能
\nDao层 UserDao接口:
\n1 2 3 4 5 package com.ajream.Dao;public interface UserDao { void getUser () ; }
\nUserDaoImpl:
\n1 2 3 4 5 6 7 8 package com.ajream.Dao;public class UserDaoImpl implements UserDao { @Override public void getUser () { System.out.println("默认获取用户数据。。。" ); } }
\nUserMysqlDaoImpl:
\n1 2 3 4 5 6 7 8 9 10 package com.ajream.Dao;public class UserMysqlDaoImpl implements UserDao { @Override public void getUser () { System.out.println("MySQL获取用户数据。。。" ); } }
\n没有使用控制反转时的业务层 UserService接口:
\n1 2 3 4 5 package com.ajream.Service;public interface UserService { void getUser () ; }
\nUserServiceImpl:
\n
\n即:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.ajream.Service;import com.ajream.Dao.UserDao;import com.ajream.Dao.UserDaoImpl;public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl(); public UserServiceImpl () { } public void getUser () { this .userDao.getUser(); } }
\n\n注意没有使用IOC时第8行代码,这样写的缺点:
\n这样指定创建了一个对象UserDaoImpl,假如我要创建另一个对象UserMysqlDaoImpl,用户就需要修改业务层这一行代码,例如改为:
\n1 private UserDao userDao = new UserMysqlDaoImpl();
\n很明显,这样有些不方便,作为用户,应该不需要关心业务的具体实现
\n \n使用控制反转后的业务层 UserService接口不变
\nUserServiceImpl使用setUser来创建对象:
\n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.ajream.Service;import com.ajream.Dao.UserDao;public class UserServiceImpl implements UserService { private UserDao userDao; @Override public void getUser () { userDao.getUser(); } public void setUser (UserDao userDao) { this .userDao = userDao; } }
\n\n用户可以通过调用setUser()
方法来创建不同对象(只需要传入不同参数即可)
\n \n对比:
\n
\n \ntest1(这里是用户调用业务层实现业务的部分)
\n\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.ajream;import com.ajream.Dao.UserDaoImpl;import com.ajream.Dao.UserMysqlDaoImpl;import com.ajream.Service.UserService;import com.ajream.Service.UserServiceImpl;public class test1 { public static void main (String[] args) { UserService userService = new UserServiceImpl(); userService.getUser(); } }
\n\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.ajream;import com.ajream.Dao.UserDaoImpl;import com.ajream.Dao.UserMysqlDaoImpl;import com.ajream.Service.UserService;import com.ajream.Service.UserServiceImpl;public class test1 { public static void main (String[] args) { UserService userService = new UserServiceImpl(); ((UserServiceImpl) userService).setUser(new UserDaoImpl()); userService.getUser(); ((UserServiceImpl) userService).setUser(new UserMysqlDaoImpl()); userService.getUser(); } }
\n对比:
\n
\n一张图表示三者之间关系
\n用户希望通过调用业务层来实现各个具体的业务如 Mysql
、Oracle
,…
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"IOC控制反转思想举例 创建工程 利用Maven创建一个空白工程spring1,其目录结构如下:
\n
\n然后创建以下几个接口和类:
\n\nDao层 \nService业务层 \n用户层(test表示用户测试) \n \n
\n代码实现 目标:用户要在test1中创建一个对象,调用Dao层的某个方法 getUser()
来实现某种功能
\nDao层 UserDao接口:
\n1 2 3 4 5 package com.ajream.Dao;public interface UserDao { void getUser () ; }
\nUserDaoImpl:
\n1 2 3 4 5 6 7 8 package com.ajream.Dao;public class UserDaoImpl implements UserDao { @Override public void getUser () { System.out.println("默认获取用户数据。。。" ); } }
\nUserMysqlDaoImpl:
\n1 2 3 4 5 6 7 8 9 10 package com.ajream.Dao;public class UserMysqlDaoImpl implements UserDao { @Override public void getUser () { System.out.println("MySQL获取用户数据。。。" ); } }
\n没有使用控制反转时的业务层 UserService接口:
\n1 2 3 4 5 package com.ajream.Service;public interface UserService { void getUser () ; }
\nUserServiceImpl:
\n
\n即:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.ajream.Service;import com.ajream.Dao.UserDao;import com.ajream.Dao.UserDaoImpl;public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl(); public UserServiceImpl () { } public void getUser () { this .userDao.getUser(); } }
\n\n注意没有使用IOC时第8行代码,这样写的缺点:
\n这样指定创建了一个对象UserDaoImpl,假如我要创建另一个对象UserMysqlDaoImpl,用户就需要修改业务层这一行代码,例如改为:
\n1 private UserDao userDao = new UserMysqlDaoImpl();
\n很明显,这样有些不方便,作为用户,应该不需要关心业务的具体实现
\n \n使用控制反转后的业务层 UserService接口不变
\nUserServiceImpl使用setUser来创建对象:
\n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.ajream.Service;import com.ajream.Dao.UserDao;public class UserServiceImpl implements UserService { private UserDao userDao; @Override public void getUser () { userDao.getUser(); } public void setUser (UserDao userDao) { this .userDao = userDao; } }
\n\n用户可以通过调用setUser()
方法来创建不同对象(只需要传入不同参数即可)
\n \n对比:
\n
\n \ntest1(这里是用户调用业务层实现业务的部分)
\n\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.ajream;import com.ajream.Dao.UserDaoImpl;import com.ajream.Dao.UserMysqlDaoImpl;import com.ajream.Service.UserService;import com.ajream.Service.UserServiceImpl;public class test1 { public static void main (String[] args) { UserService userService = new UserServiceImpl(); userService.getUser(); } }
\n\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.ajream;import com.ajream.Dao.UserDaoImpl;import com.ajream.Dao.UserMysqlDaoImpl;import com.ajream.Service.UserService;import com.ajream.Service.UserServiceImpl;public class test1 { public static void main (String[] args) { UserService userService = new UserServiceImpl(); ((UserServiceImpl) userService).setUser(new UserDaoImpl()); userService.getUser(); ((UserServiceImpl) userService).setUser(new UserMysqlDaoImpl()); userService.getUser(); } }
\n对比:
\n
\n一张图表示三者之间关系
\n用户希望通过调用业务层来实现各个具体的业务如 Mysql
、Oracle
,…
\n"},{"title":"Spring(7)-spring容器注入","abbrlink":"7f2244ba","date":"2021-07-27T06:39:25.000Z","description":"spring容器类型如何注入","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n\n# spring容器注入\n\n [跳转到项目地址](https://codechina.csdn.net/m0_46079750/spring4/-/tree/master)\n\n\n\n如果是`String` 这种简单类型的数据,spring注入如下:\n\n\n\nUser.java\n\n```java\npublic class User{\n private String name;\n \n public void setName(String name){\n this.name = name;\n }\n \n //...\n}\n```\n\nbeans.xml\n\n```xml\n\n\n\t \n \n\n```\n\n\n\n但是,如果要注入的不是如`String` 这种简单类型数据,而是array、list、map、prop。。。这种容器类型数据,应该如何写bean,下面是模板:\n\n[官方文档说明](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/core.html#beans-inner-beans)\n\n```xml\n\n \n \n \n 《abc》 \n 《defg》 \n 《hijk》 \n \n \n \n \n \n \n administrator@example.org \n support@example.org \n development@example.org \n \n \n \n \n \n \n a list element followed by a reference \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n just some string \n \n \n \n \n```\n\n\n\n例如要往下面这些属性注入值\n\n![image-20210730172726839](https://gitee.com/ajream/images/raw/master/img/2021-07-3017-27-28_image-20210730172726839.png)\n\nbeans.xml:\n\n```xml\n\n\n\n \n \n \n\n \n \n\n \n \n\n\n \n\n\n \n \n 《计算机技术》 \n 《操作系统》 \n 《数据结构》 \n \n \n\n\n \n \n 王者荣耀 \n 少年三国志 \n \n \n\n\n \n \n 打游戏 \n 打羽毛球 \n
\n \n\n\n \n \n \n \n \n \n \n\n\n \n \n administrator@example.org \n 13389453895 \n 456382987 \n \n \n\n \n\n \n```\n\n\n\n\n\n输出预览:\n\n![image-20210730173016148](https://gitee.com/ajream/images/raw/master/img/2021-07-3017-30-17_image-20210730173016148.png)\n\n","source":"_posts/Spring/7-spring容器注入.md","raw":"---\ntitle: Spring(7)-spring容器注入\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 7f2244ba\ndate: 2021-07-27 14:39:25\ndescription: spring容器类型如何注入\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n\n# spring容器注入\n\n [跳转到项目地址](https://codechina.csdn.net/m0_46079750/spring4/-/tree/master)\n\n\n\n如果是`String` 这种简单类型的数据,spring注入如下:\n\n\n\nUser.java\n\n```java\npublic class User{\n private String name;\n \n public void setName(String name){\n this.name = name;\n }\n \n //...\n}\n```\n\nbeans.xml\n\n```xml\n\n\n\t \n \n\n```\n\n\n\n但是,如果要注入的不是如`String` 这种简单类型数据,而是array、list、map、prop。。。这种容器类型数据,应该如何写bean,下面是模板:\n\n[官方文档说明](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/core.html#beans-inner-beans)\n\n```xml\n\n \n \n \n 《abc》 \n 《defg》 \n 《hijk》 \n \n \n \n \n \n \n administrator@example.org \n support@example.org \n development@example.org \n \n \n \n \n \n \n a list element followed by a reference \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n just some string \n \n \n \n \n```\n\n\n\n例如要往下面这些属性注入值\n\n![image-20210730172726839](https://gitee.com/ajream/images/raw/master/img/2021-07-3017-27-28_image-20210730172726839.png)\n\nbeans.xml:\n\n```xml\n\n\n\n \n \n \n\n \n \n\n \n \n\n\n \n\n\n \n \n 《计算机技术》 \n 《操作系统》 \n 《数据结构》 \n \n \n\n\n \n \n 王者荣耀 \n 少年三国志 \n \n \n\n\n \n \n 打游戏 \n 打羽毛球 \n
\n \n\n\n \n \n \n \n \n \n \n\n\n \n \n administrator@example.org \n 13389453895 \n 456382987 \n \n \n\n \n\n \n```\n\n\n\n\n\n输出预览:\n\n![image-20210730173016148](https://gitee.com/ajream/images/raw/master/img/2021-07-3017-30-17_image-20210730173016148.png)\n\n","slug":"Spring/7-spring容器注入","published":1,"updated":"2021-09-10T03:18:32.070Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pg002zakve9wvvbwrm","content":"spring容器注入 跳转到项目地址
\n如果是String
这种简单类型的数据,spring注入如下:
\nUser.java
\n1 2 3 4 5 6 7 8 9 public class User { private String name; public void setName (String name) { this .name = name; } }
\nbeans.xml
\n1 2 3 4 5 <bean id ="user" class ="com.xxx.User" > \t<property name ="name" value ="张三" /> </bean >
\n但是,如果要注入的不是如String
这种简单类型数据,而是array、list、map、prop。。。这种容器类型数据,应该如何写bean,下面是模板:
\n官方文档说明
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 <bean id ="moreComplexObject" class ="example.ComplexObject" > <property name ="books" > <array > <value > 《abc》</value > <value > 《defg》</value > <value > 《hijk》</value > </array > </property > <property name ="adminEmails" > <props > <prop key ="administrator" > administrator@example.org</prop > <prop key ="support" > support@example.org</prop > <prop key ="development" > development@example.org</prop > </props > </property > <property name ="someList" > <list > <value > a list element followed by a reference</value > <ref bean ="myDataSource" /> </list > </property > <property name ="someMap" > <map > <entry key ="an entry" value ="just some string" /> <entry key ="a ref" value-ref ="myDataSource" /> </map > </property > <property name ="someSet" > <set > <value > just some string</value > <ref bean ="myDataSource" /> </set > </property > </bean >
\n例如要往下面这些属性注入值
\n
\nbeans.xml:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="address" class ="com.ajream.pojo.Address" > <property name ="addr" value ="西安" /> </bean > <bean id ="student" class ="com.ajream.pojo.Student" > <property name ="name" value ="王二" /> <property name ="age" value ="30" /> <property name ="address" ref ="address" /> <property name ="books" > <array > <value > 《计算机技术》</value > <value > 《操作系统》</value > <value > 《数据结构》</value > </array > </property > <property name ="games" > <set > <value > 王者荣耀</value > <value > 少年三国志</value > </set > </property > <property name ="hobbies" > <list > <value > 打游戏</value > <value > 打羽毛球</value > </list > </property > <property name ="grade" > <map > <entry key ="高等数学" value ="85" /> <entry key ="大学英语" value ="90" /> <entry key ="java基础" value ="98" /> </map > </property > <property name ="info" > <props > <prop key ="email" > administrator@example.org</prop > <prop key ="phone" > 13389453895</prop > <prop key ="QQ-number" > 456382987</prop > </props > </property > </bean > </beans >
\n输出预览:
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"spring容器注入 跳转到项目地址
\n如果是String
这种简单类型的数据,spring注入如下:
\nUser.java
\n1 2 3 4 5 6 7 8 9 public class User { private String name; public void setName (String name) { this .name = name; } }
\nbeans.xml
\n1 2 3 4 5 <bean id ="user" class ="com.xxx.User" > \t<property name ="name" value ="张三" /> </bean >
\n但是,如果要注入的不是如String
这种简单类型数据,而是array、list、map、prop。。。这种容器类型数据,应该如何写bean,下面是模板:
\n官方文档说明
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 <bean id ="moreComplexObject" class ="example.ComplexObject" > <property name ="books" > <array > <value > 《abc》</value > <value > 《defg》</value > <value > 《hijk》</value > </array > </property > <property name ="adminEmails" > <props > <prop key ="administrator" > administrator@example.org</prop > <prop key ="support" > support@example.org</prop > <prop key ="development" > development@example.org</prop > </props > </property > <property name ="someList" > <list > <value > a list element followed by a reference</value > <ref bean ="myDataSource" /> </list > </property > <property name ="someMap" > <map > <entry key ="an entry" value ="just some string" /> <entry key ="a ref" value-ref ="myDataSource" /> </map > </property > <property name ="someSet" > <set > <value > just some string</value > <ref bean ="myDataSource" /> </set > </property > </bean >
\n例如要往下面这些属性注入值
\n
\nbeans.xml:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="address" class ="com.ajream.pojo.Address" > <property name ="addr" value ="西安" /> </bean > <bean id ="student" class ="com.ajream.pojo.Student" > <property name ="name" value ="王二" /> <property name ="age" value ="30" /> <property name ="address" ref ="address" /> <property name ="books" > <array > <value > 《计算机技术》</value > <value > 《操作系统》</value > <value > 《数据结构》</value > </array > </property > <property name ="games" > <set > <value > 王者荣耀</value > <value > 少年三国志</value > </set > </property > <property name ="hobbies" > <list > <value > 打游戏</value > <value > 打羽毛球</value > </list > </property > <property name ="grade" > <map > <entry key ="高等数学" value ="85" /> <entry key ="大学英语" value ="90" /> <entry key ="java基础" value ="98" /> </map > </property > <property name ="info" > <props > <prop key ="email" > administrator@example.org</prop > <prop key ="phone" > 13389453895</prop > <prop key ="QQ-number" > 456382987</prop > </props > </property > </bean > </beans >
\n输出预览:
\n
\n"},{"title":"Spring(6)-spring配置说明","abbrlink":"3fb3b16d","date":"2021-07-26T05:26:19.000Z","description":"spring一些基本配置","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n\n# spring配置\n\n\n\n## 别名alias\n\nbean可以拥有别名\n\n```xml\n\n```\n\n\n\n例如:\n\n```xml\n\n \n \n \n\n \n```\n\n\n\n使用别名后就可以通过别名来获取bean\n\n```java\n//User user = context.getBean(\"user\", User.class);\nUser user = context.getBean(\"user2\", User.class);\n```\n\n\n\n## bean的配置\n\n```xml\n\n\n \n \n```\n\n\n\n## import\n\n如果有多个 `beans`配置文件,可以用 `import`将多个文件导入到一个文件中\n\n例如几个配置文件如下:\n\n```\nresources\n\t|-applicationcontext.xml\n\t|-beans1.xml\n\t|-beans2.xml\n```\n\n在 `applicationcontext.xml` 中可以使用 `import` 标签导入 另外2个配置文件:\n\n```xml\n\n\n```\n\n> 适用于团队合作时使用\n\n\n\n","source":"_posts/Spring/6-spring配置说明.md","raw":"---\ntitle: Spring(6)-spring配置说明\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 3fb3b16d\ndate: 2021-07-26 13:26:19\ndescription: spring一些基本配置\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n\n# spring配置\n\n\n\n## 别名alias\n\nbean可以拥有别名\n\n```xml\n\n```\n\n\n\n例如:\n\n```xml\n\n \n \n \n\n \n```\n\n\n\n使用别名后就可以通过别名来获取bean\n\n```java\n//User user = context.getBean(\"user\", User.class);\nUser user = context.getBean(\"user2\", User.class);\n```\n\n\n\n## bean的配置\n\n```xml\n\n\n \n \n```\n\n\n\n## import\n\n如果有多个 `beans`配置文件,可以用 `import`将多个文件导入到一个文件中\n\n例如几个配置文件如下:\n\n```\nresources\n\t|-applicationcontext.xml\n\t|-beans1.xml\n\t|-beans2.xml\n```\n\n在 `applicationcontext.xml` 中可以使用 `import` 标签导入 另外2个配置文件:\n\n```xml\n\n\n```\n\n> 适用于团队合作时使用\n\n\n\n","slug":"Spring/6-spring配置说明","published":1,"updated":"2021-09-10T03:18:24.710Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pi0032akve2d4z141s","content":"spring配置 别名alias bean可以拥有别名
\n1 <alias name ="fromName" alias ="toName" />
\n例如:
\n1 2 3 4 5 6 <bean id ="user" class ="com.ajream.pojo.User" > <property name ="name" value ="章三" /> <property name ="age" value ="20" /> </bean > <alias name ="user" alias ="user2" />
\n使用别名后就可以通过别名来获取bean
\n1 2 User user = context.getBean("user2" , User.class);
\nbean的配置 1 2 3 4 5 6 7 8 <bean id ="user" class ="com.ajream.pojo.User" name ="n1,n2;n3 n4" > <property name ="name" value ="章三" /> </bean >
\nimport 如果有多个 beans
配置文件,可以用 import
将多个文件导入到一个文件中
\n例如几个配置文件如下:
\n1 2 3 4 resources \t|-applicationcontext.xml \t|-beans1.xml \t|-beans2.xml
\n在 applicationcontext.xml
中可以使用 import
标签导入 另外2个配置文件:
\n1 2 <import resource ="beans1.xml" /> <import resource ="beans2.xml" />
\n\n适用于团队合作时使用
\n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"spring配置 别名alias bean可以拥有别名
\n1 <alias name ="fromName" alias ="toName" />
\n例如:
\n1 2 3 4 5 6 <bean id ="user" class ="com.ajream.pojo.User" > <property name ="name" value ="章三" /> <property name ="age" value ="20" /> </bean > <alias name ="user" alias ="user2" />
\n使用别名后就可以通过别名来获取bean
\n1 2 User user = context.getBean("user2" , User.class);
\nbean的配置 1 2 3 4 5 6 7 8 <bean id ="user" class ="com.ajream.pojo.User" name ="n1,n2;n3 n4" > <property name ="name" value ="章三" /> </bean >
\nimport 如果有多个 beans
配置文件,可以用 import
将多个文件导入到一个文件中
\n例如几个配置文件如下:
\n1 2 3 4 resources \t|-applicationcontext.xml \t|-beans1.xml \t|-beans2.xml
\n在 applicationcontext.xml
中可以使用 import
标签导入 另外2个配置文件:
\n1 2 <import resource ="beans1.xml" /> <import resource ="beans2.xml" />
\n\n适用于团队合作时使用
\n \n"},{"title":"Spring(9)-bean自动装配","abbrlink":"f68104ec","date":"2021-07-28T09:26:49.000Z","description":"spring自动装配(自动根据一些配置说明来注入属性)的使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n# bean自动装配\n\n\n\n[![spring5](https://codechina.csdn.net/m0_46079750/spring5/-/card.svg)](https://codechina.csdn.net/m0_46079750/spring5)\n\n## 代码\n\n用一个项目来说明,如下:\n\n![image-20210731164147058](https://gitee.com/ajream/images/raw/master/img/2021-07-3116-41-53_image-20210731164147058.png)\n\n其中各个文件代码如下:\n\n\n\nCat:\n\n```java\npackage com.ajream.pojo;\n\npublic class Cat {\n public void shout(){\n System.out.println(\"miao~\");\n }\n}\n```\n\n\n\nDog:\n\n```java\npackage com.ajream.pojo;\n\npublic class Dog {\n public void shout(){\n System.out.println(\"wang~\");\n }\n}\n\n```\n\n\n\nPerson:\n\n```java\npackage com.ajream.pojo;\n\npublic class Person {\n private String name;\n private Cat cat;\n private Dog dog;\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n public Cat getCat() {\n return cat;\n }\n\n public void setCat(Cat cat) {\n this.cat = cat;\n }\n\n public Dog getDog() {\n return dog;\n }\n\n public void setDog(Dog dog) {\n this.dog = dog;\n }\n\n @Override\n public String toString() {\n return \"Person{\" +\n \"name='\" + name + '\\'' +\n \", cat=\" + cat +\n \", dog=\" + dog +\n '}';\n }\n\n}\n\n```\n\nbeans.xml\n\n```xml\n\n\n\n \n \n \n \n \n \n \n \n```\n\n\n\nMyTest:\n\n```java\nimport com.ajream.pojo.Person;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\n\npublic class MyTest {\n\n public static void main(String[] args) {\n \n ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\");\n Person p = context.getBean(\"person\", Person.class);\n\n System.out.println(p.getName());\n p.getCat().shout();\n p.getDog().shout();\n\n }\n\n}\n\n```\n\n\n\n## 分析\n\n主要看 `Person`类和 `beans.xml`文件,在没有使用自动装配时\n\n![image-20210731165352656](https://gitee.com/ajream/images/raw/master/img/2021-07-3116-53-57_image-20210731165352656.png)\n\n\n\n1. 使用 `byName` 自动装配\n\n![image-20210731170102634](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-01-06_image-20210731170102634.png)\n\n\n\n使用自动装配就添加了 `autowire = \"byName\"` 这一配置,要求如下:\n\n![image-20210731170646544](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-06-49_image-20210731170646544.png)\n\nbyName就相当于`name`和`ref`相同的情形(这也是一般情形),而由于`name`又与 `setName` 关联,所以这3个都要保持一致,实际就是代码运行时会自动转换成下面这种情况:\n\n![image-20210731171212541](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-12-15_image-20210731171212541.png)\n\n\n\n\n\n2. 使用 `byType` 自动装配\n\n\n\n对id名无要求,但要求bean类型唯一\n\n![image-20210731171743101](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-17-46_image-20210731171743101.png)\n\n比如下面这种情况就会出错:\n\n![image-20210731172014553](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-20-19_image-20210731172014553.png)\n\n\n\n> 总结:\n>\n> - 使用byName方式的自动装配,bean的id要与set方法保持一致\n> - 使用byType方式的自动装配,bean的类型要唯一,否则就会出错\n\n","source":"_posts/Spring/9-bean自动装配.md","raw":"---\ntitle: Spring(9)-bean自动装配\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: f68104ec\ndate: 2021-07-28 17:26:49\ndescription: spring自动装配(自动根据一些配置说明来注入属性)的使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n# bean自动装配\n\n\n\n[![spring5](https://codechina.csdn.net/m0_46079750/spring5/-/card.svg)](https://codechina.csdn.net/m0_46079750/spring5)\n\n## 代码\n\n用一个项目来说明,如下:\n\n![image-20210731164147058](https://gitee.com/ajream/images/raw/master/img/2021-07-3116-41-53_image-20210731164147058.png)\n\n其中各个文件代码如下:\n\n\n\nCat:\n\n```java\npackage com.ajream.pojo;\n\npublic class Cat {\n public void shout(){\n System.out.println(\"miao~\");\n }\n}\n```\n\n\n\nDog:\n\n```java\npackage com.ajream.pojo;\n\npublic class Dog {\n public void shout(){\n System.out.println(\"wang~\");\n }\n}\n\n```\n\n\n\nPerson:\n\n```java\npackage com.ajream.pojo;\n\npublic class Person {\n private String name;\n private Cat cat;\n private Dog dog;\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n public Cat getCat() {\n return cat;\n }\n\n public void setCat(Cat cat) {\n this.cat = cat;\n }\n\n public Dog getDog() {\n return dog;\n }\n\n public void setDog(Dog dog) {\n this.dog = dog;\n }\n\n @Override\n public String toString() {\n return \"Person{\" +\n \"name='\" + name + '\\'' +\n \", cat=\" + cat +\n \", dog=\" + dog +\n '}';\n }\n\n}\n\n```\n\nbeans.xml\n\n```xml\n\n\n\n \n \n \n \n \n \n \n \n```\n\n\n\nMyTest:\n\n```java\nimport com.ajream.pojo.Person;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\n\npublic class MyTest {\n\n public static void main(String[] args) {\n \n ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\");\n Person p = context.getBean(\"person\", Person.class);\n\n System.out.println(p.getName());\n p.getCat().shout();\n p.getDog().shout();\n\n }\n\n}\n\n```\n\n\n\n## 分析\n\n主要看 `Person`类和 `beans.xml`文件,在没有使用自动装配时\n\n![image-20210731165352656](https://gitee.com/ajream/images/raw/master/img/2021-07-3116-53-57_image-20210731165352656.png)\n\n\n\n1. 使用 `byName` 自动装配\n\n![image-20210731170102634](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-01-06_image-20210731170102634.png)\n\n\n\n使用自动装配就添加了 `autowire = \"byName\"` 这一配置,要求如下:\n\n![image-20210731170646544](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-06-49_image-20210731170646544.png)\n\nbyName就相当于`name`和`ref`相同的情形(这也是一般情形),而由于`name`又与 `setName` 关联,所以这3个都要保持一致,实际就是代码运行时会自动转换成下面这种情况:\n\n![image-20210731171212541](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-12-15_image-20210731171212541.png)\n\n\n\n\n\n2. 使用 `byType` 自动装配\n\n\n\n对id名无要求,但要求bean类型唯一\n\n![image-20210731171743101](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-17-46_image-20210731171743101.png)\n\n比如下面这种情况就会出错:\n\n![image-20210731172014553](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-20-19_image-20210731172014553.png)\n\n\n\n> 总结:\n>\n> - 使用byName方式的自动装配,bean的id要与set方法保持一致\n> - 使用byType方式的自动装配,bean的类型要唯一,否则就会出错\n\n","slug":"Spring/9-bean自动装配","published":1,"updated":"2021-09-10T03:17:09.012Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pj0035akvehrre9dbw","content":"bean自动装配
\n代码 用一个项目来说明,如下:
\n
\n其中各个文件代码如下:
\nCat:
\n1 2 3 4 5 6 7 package com.ajream.pojo;public class Cat { public void shout () { System.out.println("miao~" ); } }
\nDog:
\n1 2 3 4 5 6 7 8 package com.ajream.pojo;public class Dog { public void shout () { System.out.println("wang~" ); } }
\nPerson:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 package com.ajream.pojo;public class Person { private String name; private Cat cat; private Dog dog; public String getName () { return name; } public void setName (String name) { this .name = name; } public Cat getCat () { return cat; } public void setCat (Cat cat) { this .cat = cat; } public Dog getDog () { return dog; } public void setDog (Dog dog) { this .dog = dog; } @Override public String toString () { return "Person{" + "name='" + name + '\\'' + ", cat=" + cat + ", dog=" + dog + '}' ; } }
\nbeans.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="dog" class ="com.ajream.pojo.Dog" /> <bean id ="cat" class ="com.ajream.pojo.Cat" /> <bean id ="person" class ="com.ajream.pojo.Person" > <property name ="name" value ="张三" /> <property name ="cat" ref ="cat" /> <property name ="dog" ref ="dog" /> </bean > </beans >
\nMyTest:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import com.ajream.pojo.Person;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyTest { public static void main (String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml" ); Person p = context.getBean("person" , Person.class); System.out.println(p.getName()); p.getCat().shout(); p.getDog().shout(); } }
\n分析 主要看 Person
类和 beans.xml
文件,在没有使用自动装配时
\n
\n\n使用 byName
自动装配 \n \n
\n使用自动装配就添加了 autowire = "byName"
这一配置,要求如下:
\n
\nbyName就相当于name
和ref
相同的情形(这也是一般情形),而由于name
又与 setName
关联,所以这3个都要保持一致,实际就是代码运行时会自动转换成下面这种情况:
\n
\n\n使用 byType
自动装配 \n \n对id名无要求,但要求bean类型唯一
\n
\n比如下面这种情况就会出错:
\n
\n\n总结:
\n\n使用byName方式的自动装配,bean的id要与set方法保持一致 \n使用byType方式的自动装配,bean的类型要唯一,否则就会出错 \n \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"bean自动装配
\n代码 用一个项目来说明,如下:
\n
\n其中各个文件代码如下:
\nCat:
\n1 2 3 4 5 6 7 package com.ajream.pojo;public class Cat { public void shout () { System.out.println("miao~" ); } }
\nDog:
\n1 2 3 4 5 6 7 8 package com.ajream.pojo;public class Dog { public void shout () { System.out.println("wang~" ); } }
\nPerson:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 package com.ajream.pojo;public class Person { private String name; private Cat cat; private Dog dog; public String getName () { return name; } public void setName (String name) { this .name = name; } public Cat getCat () { return cat; } public void setCat (Cat cat) { this .cat = cat; } public Dog getDog () { return dog; } public void setDog (Dog dog) { this .dog = dog; } @Override public String toString () { return "Person{" + "name='" + name + '\\'' + ", cat=" + cat + ", dog=" + dog + '}' ; } }
\nbeans.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="dog" class ="com.ajream.pojo.Dog" /> <bean id ="cat" class ="com.ajream.pojo.Cat" /> <bean id ="person" class ="com.ajream.pojo.Person" > <property name ="name" value ="张三" /> <property name ="cat" ref ="cat" /> <property name ="dog" ref ="dog" /> </bean > </beans >
\nMyTest:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import com.ajream.pojo.Person;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyTest { public static void main (String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml" ); Person p = context.getBean("person" , Person.class); System.out.println(p.getName()); p.getCat().shout(); p.getDog().shout(); } }
\n分析 主要看 Person
类和 beans.xml
文件,在没有使用自动装配时
\n
\n\n使用 byName
自动装配 \n \n
\n使用自动装配就添加了 autowire = "byName"
这一配置,要求如下:
\n
\nbyName就相当于name
和ref
相同的情形(这也是一般情形),而由于name
又与 setName
关联,所以这3个都要保持一致,实际就是代码运行时会自动转换成下面这种情况:
\n
\n\n使用 byType
自动装配 \n \n对id名无要求,但要求bean类型唯一
\n
\n比如下面这种情况就会出错:
\n
\n\n总结:
\n\n使用byName方式的自动装配,bean的id要与set方法保持一致 \n使用byType方式的自动装配,bean的类型要唯一,否则就会出错 \n \n \n"},{"title":"Spring(8)-命名空间和作用域","abbrlink":"3feecb5e","date":"2021-07-27T09:57:24.000Z","description":"spring的p、c命名空间使用,bean的作用域","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# 命名空间和bean作用域\n\n\n\n## 命名空间\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master/)\n\n### p命名空间\n\n在beans中添加p命名空间的支持\n\n```xml\nxmlns:p=\"http://www.springframework.org/schema/p\"\n```\n\n\n\n以前要对一个属性进行注入,一般是按这种方式:\n\n```xml\n\n \n \n```\n\n使用了p命名空间,可以这样写:\n\n```xml\n \n```\n\np就是 `property` 的简写\n\n### c命名空间\n\n> c代表了 `constructor-arg`,因此c命名空间是通过构造函数来进行注入的\n\n在beans中添加c命名空间的支持\n\n```xml\nxmlns:c=\"http://www.springframework.org/schema/c\"\n```\n\n未使用命名空间:\n\n```xml\n\n \n \n```\n\n使用c命名空间:\n\n```xml\n \n```\n\n\n\n另外,p命名空间和c命名空间还可以一起使用\n\n\n\n## bean作用域\n\n\n\n### 单例模式\n\n(默认使用单例模式)\n\n![](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/images/singleton.png)\n\n说明:\n\n```xml\n\n\n\n\n\n```\n\n用户每次从spring获取的bean,其hashcode 是相同的, \n\n```java\nDefaultAccountService as1 = context.getBean(\"accountService\");\nDefaultAccountService as2 = context.getBean(\"accountService\");\n\n// as1与as2的hashcode相同\n```\n\n\n\n### 原生模式\n\n\n\n![](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/images/prototype.png\"prototype\")\n\n```xml\n\n```\n\n用户每次从spring获取的bean,其hashcode 是不同的\n\n```java\nDefaultAccountService as1 = context.getBean(\"accountService\");\nDefaultAccountService as2 = context.getBean(\"accountService\");\n\n// as1与as2 的 hashcode 不相同\n```\n\n\n\n\n\n\n\n","source":"_posts/Spring/8-命名空间和作用域.md","raw":"---\ntitle: Spring(8)-命名空间和作用域\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 3feecb5e\ndate: 2021-07-27 17:57:24\ndescription: spring的p、c命名空间使用,bean的作用域\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# 命名空间和bean作用域\n\n\n\n## 命名空间\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master/)\n\n### p命名空间\n\n在beans中添加p命名空间的支持\n\n```xml\nxmlns:p=\"http://www.springframework.org/schema/p\"\n```\n\n\n\n以前要对一个属性进行注入,一般是按这种方式:\n\n```xml\n\n \n \n```\n\n使用了p命名空间,可以这样写:\n\n```xml\n \n```\n\np就是 `property` 的简写\n\n### c命名空间\n\n> c代表了 `constructor-arg`,因此c命名空间是通过构造函数来进行注入的\n\n在beans中添加c命名空间的支持\n\n```xml\nxmlns:c=\"http://www.springframework.org/schema/c\"\n```\n\n未使用命名空间:\n\n```xml\n\n \n \n```\n\n使用c命名空间:\n\n```xml\n \n```\n\n\n\n另外,p命名空间和c命名空间还可以一起使用\n\n\n\n## bean作用域\n\n\n\n### 单例模式\n\n(默认使用单例模式)\n\n![](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/images/singleton.png)\n\n说明:\n\n```xml\n\n\n\n\n\n```\n\n用户每次从spring获取的bean,其hashcode 是相同的, \n\n```java\nDefaultAccountService as1 = context.getBean(\"accountService\");\nDefaultAccountService as2 = context.getBean(\"accountService\");\n\n// as1与as2的hashcode相同\n```\n\n\n\n### 原生模式\n\n\n\n![](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/images/prototype.png\"prototype\")\n\n```xml\n\n```\n\n用户每次从spring获取的bean,其hashcode 是不同的\n\n```java\nDefaultAccountService as1 = context.getBean(\"accountService\");\nDefaultAccountService as2 = context.getBean(\"accountService\");\n\n// as1与as2 的 hashcode 不相同\n```\n\n\n\n\n\n\n\n","slug":"Spring/8-命名空间和作用域","published":1,"updated":"2021-09-10T03:17:13.986Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pk0038akved9jw5nm3","content":"命名空间和bean作用域 命名空间 项目地址
\np命名空间 在beans中添加p命名空间的支持
\n1 xmlns:p="http://www.springframework.org/schema/p"
\n以前要对一个属性进行注入,一般是按这种方式:
\n1 2 3 <bean id ="person" class ="com.ajream.pojo.Person" > <property name ="name" value ="张三" /> </bean >
\n使用了p命名空间,可以这样写:
\n1 <bean id ="person" class ="com.ajream.pojo.Person" p:name ="张三" />
\np就是 property
的简写
\nc命名空间 \nc代表了 constructor-arg
,因此c命名空间是通过构造函数来进行注入的
\n \n在beans中添加c命名空间的支持
\n1 xmlns:c="http://www.springframework.org/schema/c"
\n未使用命名空间:
\n1 2 3 <bean id ="person" class ="com.ajream.pojo.Person" > <constructor-arg name ="name" value ="张三" /> </bean >
\n使用c命名空间:
\n1 <bean id ="person" class ="com.ajream.pojo.Person" c:name ="王五" />
\n另外,p命名空间和c命名空间还可以一起使用
\nbean作用域 单例模式 (默认使用单例模式)
\n
\n说明:
\n1 2 3 4 5 <bean id ="accountService" class ="com.something.DefaultAccountService" /> <bean id ="accountService" class ="com.something.DefaultAccountService" scope ="singleton" />
\n用户每次从spring获取的bean,其hashcode 是相同的,
\n1 2 3 4 DefaultAccountService as1 = context.getBean("accountService" ); DefaultAccountService as2 = context.getBean("accountService" );
\n原生模式
\n1 <bean id ="accountService" class ="com.something.DefaultAccountService" scope ="prototype" />
\n用户每次从spring获取的bean,其hashcode 是不同的
\n1 2 3 4 DefaultAccountService as1 = context.getBean("accountService" ); DefaultAccountService as2 = context.getBean("accountService" );
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"命名空间和bean作用域 命名空间 项目地址
\np命名空间 在beans中添加p命名空间的支持
\n1 xmlns:p="http://www.springframework.org/schema/p"
\n以前要对一个属性进行注入,一般是按这种方式:
\n1 2 3 <bean id ="person" class ="com.ajream.pojo.Person" > <property name ="name" value ="张三" /> </bean >
\n使用了p命名空间,可以这样写:
\n1 <bean id ="person" class ="com.ajream.pojo.Person" p:name ="张三" />
\np就是 property
的简写
\nc命名空间 \nc代表了 constructor-arg
,因此c命名空间是通过构造函数来进行注入的
\n \n在beans中添加c命名空间的支持
\n1 xmlns:c="http://www.springframework.org/schema/c"
\n未使用命名空间:
\n1 2 3 <bean id ="person" class ="com.ajream.pojo.Person" > <constructor-arg name ="name" value ="张三" /> </bean >
\n使用c命名空间:
\n1 <bean id ="person" class ="com.ajream.pojo.Person" c:name ="王五" />
\n另外,p命名空间和c命名空间还可以一起使用
\nbean作用域 单例模式 (默认使用单例模式)
\n
\n说明:
\n1 2 3 4 5 <bean id ="accountService" class ="com.something.DefaultAccountService" /> <bean id ="accountService" class ="com.something.DefaultAccountService" scope ="singleton" />
\n用户每次从spring获取的bean,其hashcode 是相同的,
\n1 2 3 4 DefaultAccountService as1 = context.getBean("accountService" ); DefaultAccountService as2 = context.getBean("accountService" );
\n原生模式
\n1 <bean id ="accountService" class ="com.something.DefaultAccountService" scope ="prototype" />
\n用户每次从spring获取的bean,其hashcode 是不同的
\n1 2 3 4 DefaultAccountService as1 = context.getBean("accountService" ); DefaultAccountService as2 = context.getBean("accountService" );
\n"},{"title":"mybatis的Mapper详解(三)","description":"Mapper配置","abbrlink":"f218ca42","date":"2021-08-25T11:12:46.000Z","top_img":null,"cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n## mapper.xml\n\n\n\n在jdbc中,增(insert)、删(delete)、查(select)、改(update)统称为statement\n\n\n\n因此,在mapper中,statement包含这4种标签,在相应的标签内可以使用相应的SQL语句操作数据\n\n\n\n### parameterType: 参数数据类型\n\n- 基本数据类型,int, long, ...\n\n ```xml\n \n delete from t_acount where id = #{id}\n \n ```\n\n \n\n- String类型: `java.lang.String`\n\n ```\n \n select * from t_acount where username=#{username}\n \n ```\n\n- 包装类,如:`java.lang.Integer`, `java.lang.Long`...\n\n- 自定义类类型\n\n ```xml\n \n \tinsert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})\n \n ```\n\n \n\n\n\n\n\n方法中有多个参数,不用写 parameterType,获取参数时不能用参数名,要用下标 `param1`, `param2` ...或者 `arg0`, `arg1` ... 【不同mybatis版本写可能不同,要注意param下标是从1开始的】\n\n例如:\n\n这是方法:\n\n```java\npublic Account findByNameAndAge(name, age);\n```\n\n则mapper如下\n\n```xml\n\n\n select * from t_acount where username=#{param1} and age=#{param2}\n \n```\n\n\n\n\n\n\n\n### resultType:返回类型\n\n用法与parameterType相同\n\n【增删改默认返回int类型,表示影响的行数,不需要指定返回类型是`int`】\n\n- 基本数据类型\n- 包装类\n- String类型\n- 自定义类类型\n\n\n\n\n\n\n\n","source":"_posts/Mybatis/mybatis的Mapper详解(三).md","raw":"---\ntitle: mybatis的Mapper详解(三)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: Mapper配置\nabbrlink: f218ca42\ndate: 2021-08-25 19:12:46\ntop_img: \ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n## mapper.xml\n\n\n\n在jdbc中,增(insert)、删(delete)、查(select)、改(update)统称为statement\n\n\n\n因此,在mapper中,statement包含这4种标签,在相应的标签内可以使用相应的SQL语句操作数据\n\n\n\n### parameterType: 参数数据类型\n\n- 基本数据类型,int, long, ...\n\n ```xml\n \n delete from t_acount where id = #{id}\n \n ```\n\n \n\n- String类型: `java.lang.String`\n\n ```\n \n select * from t_acount where username=#{username}\n \n ```\n\n- 包装类,如:`java.lang.Integer`, `java.lang.Long`...\n\n- 自定义类类型\n\n ```xml\n \n \tinsert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})\n \n ```\n\n \n\n\n\n\n\n方法中有多个参数,不用写 parameterType,获取参数时不能用参数名,要用下标 `param1`, `param2` ...或者 `arg0`, `arg1` ... 【不同mybatis版本写可能不同,要注意param下标是从1开始的】\n\n例如:\n\n这是方法:\n\n```java\npublic Account findByNameAndAge(name, age);\n```\n\n则mapper如下\n\n```xml\n\n\n select * from t_acount where username=#{param1} and age=#{param2}\n \n```\n\n\n\n\n\n\n\n### resultType:返回类型\n\n用法与parameterType相同\n\n【增删改默认返回int类型,表示影响的行数,不需要指定返回类型是`int`】\n\n- 基本数据类型\n- 包装类\n- String类型\n- 自定义类类型\n\n\n\n\n\n\n\n","slug":"Mybatis/mybatis的Mapper详解(三)","published":1,"updated":"2021-08-29T14:45:05.560Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pl003bakve3evtamml","content":"mapper.xml 在jdbc中,增(insert)、删(delete)、查(select)、改(update)统称为statement
\n因此,在mapper中,statement包含这4种标签,在相应的标签内可以使用相应的SQL语句操作数据
\nparameterType: 参数数据类型 \n基本数据类型,int, long, …
\n1 2 3 <delete id ="deleteById" parameterType ="int" > delete from t_acount where id = #{id} </delete >
\n \n \n\n方法中有多个参数,不用写 parameterType,获取参数时不能用参数名,要用下标 param1
, param2
…或者 arg0
, arg1
… 【不同mybatis版本写可能不同,要注意param下标是从1开始的】
\n例如:
\n这是方法:
\n1 public Account findByNameAndAge (name, age) ;
\n则mapper如下
\n1 2 3 4 <select id ="findByNameAndAge" resultType ="com.ajream.entity.Account" > select * from t_acount where username=#{param1} and age=#{param2} </select >
\nresultType:返回类型 用法与parameterType相同
\n【增删改默认返回int类型,表示影响的行数,不需要指定返回类型是int
】
\n\n基本数据类型 \n包装类 \nString类型 \n自定义类类型 \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"mapper.xml 在jdbc中,增(insert)、删(delete)、查(select)、改(update)统称为statement
\n因此,在mapper中,statement包含这4种标签,在相应的标签内可以使用相应的SQL语句操作数据
\nparameterType: 参数数据类型 \n基本数据类型,int, long, …
\n1 2 3 <delete id ="deleteById" parameterType ="int" > delete from t_acount where id = #{id} </delete >
\n \n \n\n方法中有多个参数,不用写 parameterType,获取参数时不能用参数名,要用下标 param1
, param2
…或者 arg0
, arg1
… 【不同mybatis版本写可能不同,要注意param下标是从1开始的】
\n例如:
\n这是方法:
\n1 public Account findByNameAndAge (name, age) ;
\n则mapper如下
\n1 2 3 4 <select id ="findByNameAndAge" resultType ="com.ajream.entity.Account" > select * from t_acount where username=#{param1} and age=#{param2} </select >
\nresultType:返回类型 用法与parameterType相同
\n【增删改默认返回int类型,表示影响的行数,不需要指定返回类型是int
】
\n\n基本数据类型 \n包装类 \nString类型 \n自定义类类型 \n \n"},{"title":"mybatis动态SQL(七)","description":"mybatis动态生成sql语句","abbrlink":"6e81c43e","date":"2021-08-27T10:23:10.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n\n前面我们通过实例讲解了用mybatis对一张表进行的CRUD操作,但是我们发现写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL,稍微不注意,由于引号,空格等缺失可能都会导致错误。\n\n那么怎么去解决这个问题呢?这就是本篇所讲的使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。\n\n\n\n对于这样一张表,要根据 `id/username/passwd/age`来选出指定的一条记录,用sql语句可能会这样写:\n\n```sql\nselect * from t_acount\nwhere id=7 and username=\"xiaoBai\" and passwd=\"12345xb\" and age=24;\n```\n\n![image-20210828220529310](https://gitee.com/ajream/images/raw/master/img/20210828220537_image-20210828220529310.png)\n\n\n\n在Java中需要这样:\n\n接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Account;\n\npublic interface AccountDao {\n Account findByAccount(Account account);\n}\n\n```\n\n\n\n在mapper文件中:\n\n```xml\n\n\n\n \n \n \n \n \n \n \n select * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age}\n \n \n```\n\n\n\n\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AccountDao;\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class AccountDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = AccountDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n Account account = new Account();\n account.setId(7);\n account.setAge(24);\n account.setName(\"xiaoBai\");\n account.setPasswd(\"12345xb\");\n\n AccountDao accountDao = sqlSession.getMapper(AccountDao.class);\n\n Account account1 = accountDao.findByAccount(account);\n System.out.println(account1);\n }\n}\n\n```\n\n\n\n假如,在下面这句代码中\n\n```xml\n\n\tselect * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age}\n \n```\n\n\n\n某一个属性如`passwd` 值为null,那么查询不到任何一条记录,为了避免这种情况,mybatis提供了动态生成sl语句的方式\n\n\n\n\n\n### if标签\n\n```xml\n\n select * from t_acount where\n \n id=#{id}\n \n \n and username=#{name}\n \n \n and passwd=#{passwd}\n \n \n and age=#{age}\n \n \n```\n\n\n\n\n\n这样写我们可以看到,如果 `passwd` 等于 null,那么查询语句为:\n\n```\nselect * from t_acount where id=#{id} and username=#{name} and age=#{age}\n```\n\n但是如果 `id` 为空呢?那么查询语句为:\n\nselect * from t_acount where and username=#{name} and age=#{age}\n\n这是错误的 SQL 语句,如何解决呢?请看下面的 where/if 语句\n\n\n\n### where/if标签\n\n```xml\n\n select * from t_acount\n \n \n id=#{id}\n \n \n and username=#{name}\n \n \n and passwd=#{passwd}\n \n \n and age=#{age}\n \n \n \n```\n\n\n\n这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个`where`,此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。\n\n\n\n\n\n### choose/when-otherwise标签\n\n有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句\n\n```xml\n\n select * from t_acount\n \n \n \n \tid=#{id}\n \n \n \tand username=#{name}\n \n \n \tand passwd=#{passwd}\n \n \n and age=#{age}\n \n \n \n \n```\n\n\n\n也就是说,这里有4个条件:id, name, passwd, age,只能选择一个作为查询条件\n\n- 如果 id 不为空,那么查询语句为:\n\n ```\n select * from t_acount where id=#{id}\n ```\n\n \n\n- 如果 id 为空,那么看name 是否为空,如果不为空,那么语句为:\n\n ```\n select * from user where username=#{name}\n ```\n\n- 如果 username 为空,passwd不为空,那么查询语句为:\n\n ```\n select * from user where passwd=#{passwd}\n ```\n\n- 如果前面3个均为空,那么查询语句为:\n\n ```\n select * from user where age=#{age}\n ```\n\n \n\n\n\n\n\n### trim标签\n\n\n\ntrim标记是一个格式化的标记,可以完成set或者是where标记的功能\n\n用 trim 改写前面的 where-if 语句\n\n```xml\n\n select * from t_acount\n \n \n id=#{id}\n \n \n and username=#{name}\n \n \n and passwd=#{passwd}\n \n \n and age=#{age}\n \n \n \n```\n\nprefix: 表示添加前缀 `where`\n\nprefixOverrides: 去掉第一个`and`或者是`or`\n\n\n\n### set/if标签\n\n\n\n```xml\n\n update t_acount\n \n \n username = #{name},\n \n \n passwd=#{passwd}\n \n \n \n where id=#{id}\n \n```\n\n\n\n这样写,如果第一个条件 name 为空,那么 sql 语句为\n\n```\nupdate t_acount set passwd=#{passwd} where id=#{id}\n```\n\n如果第两个条件都不为空,那么 sql 语句为\n\n```\nupdate t_acount set username=#{name}, passwd=#{passwd} where id=#{id}\n```\n\n\n\n\n\n### sql(片段)标签\n\n\n\n有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。\n\n比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:\n\n```xml\n\n\n \n AND username = #{name}\n \n \n AND passwd = #{passwd}\n \n \n```\n\n\n\n引用sql片段\n\n\n\n```xml\n\n select * from t_acount\n \n \n \n \n \n \n```\n\n\n\n\n\n### foreach标签\n\n 需求:我们需要查询 user 表中 id 分别为1,2,3的用户\n\n sql语句:\n\n```sql\nselect * from user where id=1 or id=2 or id=3;\nselect * from user where id in (1,2,3);\n```\n\n\n\n建立一个 UserVo 类,里面封装一个 `List ids` 的属性\n\n\n\n```java\npackage com.ajream.entity;\n \nimport java.util.List;\nimport lombok.Data;\n\n@Data\npublic class UserVo {\n //封装多个用户的id\n private List ids;\n \n} \n```\n\n用 foreach 来改写 `select * from user where id=1 or id=2 or id=3`\n\n\n\n```xml\n\n select * from user\n \n \n \n id=#{id}\n \n \n \n```\n\n\n\n\n\n用 foreach 来改写 `select * from user where id in (1,2,3)`\n\n```xml\n\n select * from user\n \n \n \n #{id}\n \n \n \n```\n\n\n\n\n\n总结:\n\n动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。\n\n\n\n\n\n","source":"_posts/Mybatis/mybatis动态sql(七).md","raw":"---\ntitle: mybatis动态SQL(七)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: mybatis动态生成sql语句\nabbrlink: 6e81c43e\ndate: 2021-08-27 18:23:10\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n\n前面我们通过实例讲解了用mybatis对一张表进行的CRUD操作,但是我们发现写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL,稍微不注意,由于引号,空格等缺失可能都会导致错误。\n\n那么怎么去解决这个问题呢?这就是本篇所讲的使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。\n\n\n\n对于这样一张表,要根据 `id/username/passwd/age`来选出指定的一条记录,用sql语句可能会这样写:\n\n```sql\nselect * from t_acount\nwhere id=7 and username=\"xiaoBai\" and passwd=\"12345xb\" and age=24;\n```\n\n![image-20210828220529310](https://gitee.com/ajream/images/raw/master/img/20210828220537_image-20210828220529310.png)\n\n\n\n在Java中需要这样:\n\n接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Account;\n\npublic interface AccountDao {\n Account findByAccount(Account account);\n}\n\n```\n\n\n\n在mapper文件中:\n\n```xml\n\n\n\n \n \n \n \n \n \n \n select * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age}\n \n \n```\n\n\n\n\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AccountDao;\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class AccountDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = AccountDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n Account account = new Account();\n account.setId(7);\n account.setAge(24);\n account.setName(\"xiaoBai\");\n account.setPasswd(\"12345xb\");\n\n AccountDao accountDao = sqlSession.getMapper(AccountDao.class);\n\n Account account1 = accountDao.findByAccount(account);\n System.out.println(account1);\n }\n}\n\n```\n\n\n\n假如,在下面这句代码中\n\n```xml\n\n\tselect * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age}\n \n```\n\n\n\n某一个属性如`passwd` 值为null,那么查询不到任何一条记录,为了避免这种情况,mybatis提供了动态生成sl语句的方式\n\n\n\n\n\n### if标签\n\n```xml\n\n select * from t_acount where\n \n id=#{id}\n \n \n and username=#{name}\n \n \n and passwd=#{passwd}\n \n \n and age=#{age}\n \n \n```\n\n\n\n\n\n这样写我们可以看到,如果 `passwd` 等于 null,那么查询语句为:\n\n```\nselect * from t_acount where id=#{id} and username=#{name} and age=#{age}\n```\n\n但是如果 `id` 为空呢?那么查询语句为:\n\nselect * from t_acount where and username=#{name} and age=#{age}\n\n这是错误的 SQL 语句,如何解决呢?请看下面的 where/if 语句\n\n\n\n### where/if标签\n\n```xml\n\n select * from t_acount\n \n \n id=#{id}\n \n \n and username=#{name}\n \n \n and passwd=#{passwd}\n \n \n and age=#{age}\n \n \n \n```\n\n\n\n这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个`where`,此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。\n\n\n\n\n\n### choose/when-otherwise标签\n\n有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句\n\n```xml\n\n select * from t_acount\n \n \n \n \tid=#{id}\n \n \n \tand username=#{name}\n \n \n \tand passwd=#{passwd}\n \n \n and age=#{age}\n \n \n \n \n```\n\n\n\n也就是说,这里有4个条件:id, name, passwd, age,只能选择一个作为查询条件\n\n- 如果 id 不为空,那么查询语句为:\n\n ```\n select * from t_acount where id=#{id}\n ```\n\n \n\n- 如果 id 为空,那么看name 是否为空,如果不为空,那么语句为:\n\n ```\n select * from user where username=#{name}\n ```\n\n- 如果 username 为空,passwd不为空,那么查询语句为:\n\n ```\n select * from user where passwd=#{passwd}\n ```\n\n- 如果前面3个均为空,那么查询语句为:\n\n ```\n select * from user where age=#{age}\n ```\n\n \n\n\n\n\n\n### trim标签\n\n\n\ntrim标记是一个格式化的标记,可以完成set或者是where标记的功能\n\n用 trim 改写前面的 where-if 语句\n\n```xml\n\n select * from t_acount\n \n \n id=#{id}\n \n \n and username=#{name}\n \n \n and passwd=#{passwd}\n \n \n and age=#{age}\n \n \n \n```\n\nprefix: 表示添加前缀 `where`\n\nprefixOverrides: 去掉第一个`and`或者是`or`\n\n\n\n### set/if标签\n\n\n\n```xml\n\n update t_acount\n \n \n username = #{name},\n \n \n passwd=#{passwd}\n \n \n \n where id=#{id}\n \n```\n\n\n\n这样写,如果第一个条件 name 为空,那么 sql 语句为\n\n```\nupdate t_acount set passwd=#{passwd} where id=#{id}\n```\n\n如果第两个条件都不为空,那么 sql 语句为\n\n```\nupdate t_acount set username=#{name}, passwd=#{passwd} where id=#{id}\n```\n\n\n\n\n\n### sql(片段)标签\n\n\n\n有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。\n\n比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:\n\n```xml\n\n\n \n AND username = #{name}\n \n \n AND passwd = #{passwd}\n \n \n```\n\n\n\n引用sql片段\n\n\n\n```xml\n\n select * from t_acount\n \n \n \n \n \n \n```\n\n\n\n\n\n### foreach标签\n\n 需求:我们需要查询 user 表中 id 分别为1,2,3的用户\n\n sql语句:\n\n```sql\nselect * from user where id=1 or id=2 or id=3;\nselect * from user where id in (1,2,3);\n```\n\n\n\n建立一个 UserVo 类,里面封装一个 `List ids` 的属性\n\n\n\n```java\npackage com.ajream.entity;\n \nimport java.util.List;\nimport lombok.Data;\n\n@Data\npublic class UserVo {\n //封装多个用户的id\n private List ids;\n \n} \n```\n\n用 foreach 来改写 `select * from user where id=1 or id=2 or id=3`\n\n\n\n```xml\n\n select * from user\n \n \n \n id=#{id}\n \n \n \n```\n\n\n\n\n\n用 foreach 来改写 `select * from user where id in (1,2,3)`\n\n```xml\n\n select * from user\n \n \n \n #{id}\n \n \n \n```\n\n\n\n\n\n总结:\n\n动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。\n\n\n\n\n\n","slug":"Mybatis/mybatis动态sql(七)","published":1,"updated":"2021-08-29T14:45:24.576Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pm003eakve82ox37f6","content":"前面我们通过实例讲解了用mybatis对一张表进行的CRUD操作,但是我们发现写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL,稍微不注意,由于引号,空格等缺失可能都会导致错误。
\n那么怎么去解决这个问题呢?这就是本篇所讲的使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。
\n对于这样一张表,要根据 id/username/passwd/age
来选出指定的一条记录,用sql语句可能会这样写:
\n1 2 select * from t_acountwhere id= 7 and username= "xiaoBai" and passwd= "12345xb" and age= 24 ;
\n
\n在Java中需要这样:
\n接口
\n1 2 3 4 5 6 7 8 package com.ajream.dao;import com.ajream.entity.Account;public interface AccountDao { Account findByAccount (Account account) ; }
\n在mapper文件中:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.ajream.dao.AccountDao" > <resultMap id ="resultAccount" type ="com.ajream.entity.Account" > <id column ="id" property ="id" /> <result column ="passwd" property ="passwd" /> <result column ="username" property ="name" /> <result column ="age" property ="age" /> </resultMap > <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > select * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age} </select > </mapper >
\n测试
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.ajream.test;import com.ajream.dao.AccountDao;import com.ajream.entity.Account;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.InputStream;public class AccountDaoTest { public static void main (String[] args) { InputStream inputStream = AccountDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); Account account = new Account(); account.setId(7 ); account.setAge(24 ); account.setName("xiaoBai" ); account.setPasswd("12345xb" ); AccountDao accountDao = sqlSession.getMapper(AccountDao.class); Account account1 = accountDao.findByAccount(account); System.out.println(account1); } }
\n假如,在下面这句代码中
\n1 2 3 <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > \tselect * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age} </select >
\n某一个属性如passwd
值为null,那么查询不到任何一条记录,为了避免这种情况,mybatis提供了动态生成sl语句的方式
\nif标签 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > select * from t_acount where <if test ="id!=0" > id=#{id} </if > <if test ="name!=null" > and username=#{name} </if > <if test ="passwd!=null" > and passwd=#{passwd} </if > <if test ="age!=0" > and age=#{age} </if > </select >
\n这样写我们可以看到,如果 passwd
等于 null,那么查询语句为:
\n1 select * from t_acount where id=#{id} and username=#{name} and age=#{age}
\n但是如果 id
为空呢?那么查询语句为:
\nselect * from t_acount where and username=#{name} and age=#{age}
\n这是错误的 SQL 语句,如何解决呢?请看下面的 where/if 语句
\nwhere/if标签 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > select * from t_acount <where > <if test ="id!=0" > id=#{id} </if > <if test ="name!=null" > and username=#{name} </if > <if test ="passwd!=null" > and passwd=#{passwd} </if > <if test ="age!=0" > and age=#{age} </if > </where > </select >
\n这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个where
,此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。
\nchoose/when-otherwise标签 有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > select * from t_acount <where > <choose > <when test ="id != 0" > \tid=#{id} </when > <when test ="name !='' and name != null" > \tand username=#{name} </when > <when test ="passwd!='' and passwd!=null" > \tand passwd=#{passwd} </when > <otherwise > and age=#{age} </otherwise > </choose > </where > </select >
\n也就是说,这里有4个条件:id, name, passwd, age,只能选择一个作为查询条件
\n\n如果 id 不为空,那么查询语句为:
\n1 select * from t_acount where id=#{id}
\n \n \n\ntrim标签 trim标记是一个格式化的标记,可以完成set或者是where标记的功能
\n用 trim 改写前面的 where-if 语句
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > select * from t_acount <trim prefix ="where" prefixOverrides ="and | or" > <if test ="id!=0" > id=#{id} </if > <if test ="name!=null" > and username=#{name} </if > <if test ="passwd!=null" > and passwd=#{passwd} </if > <if test ="age!=0" > and age=#{age} </if > </trim > </select >
\nprefix: 表示添加前缀 where
\nprefixOverrides: 去掉第一个and
或者是or
\nset/if标签 1 2 3 4 5 6 7 8 9 10 11 12 13 <update id ="update" parameterType ="com.ajream.entity.Account" > update t_acount <set > <if test ="name != null and username != ''" > username = #{name}, </if > <if test ="passwd != null and passwd != ''" > passwd=#{passwd} </if > </set > where id=#{id} </update >
\n这样写,如果第一个条件 name 为空,那么 sql 语句为
\n1 update t_acount set passwd=#{passwd} where id=#{id}
\n如果第两个条件都不为空,那么 sql 语句为
\n1 update t_acount set username=#{name}, passwd=#{passwd} where id=#{id}
\nsql(片段)标签 有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
\n比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:
\n1 2 3 4 5 6 7 8 9 <sql id ="selectBySQL" > <if test ="name != null and name != ''" > AND username = #{name} </if > <if test ="passwd != null and passwd != ''" > AND passwd = #{passwd} </if > </sql >
\n引用sql片段
\n1 2 3 4 5 6 7 8 <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > select * from t_acount <trim prefix ="where" prefixOverrides ="and | or" > <include refid ="selectBySQL" /> </trim > </select >
\nforeach标签 需求:我们需要查询 user 表中 id 分别为1,2,3的用户
\n sql语句:
\n1 2 select * from user where id= 1 or id= 2 or id= 3 ;select * from user where id in (1 ,2 ,3 );
\n建立一个 UserVo 类,里面封装一个 List<Integer> ids
的属性
\n1 2 3 4 5 6 7 8 9 10 11 package com.ajream.entity; import java.util.List;import lombok.Data;@Data public class UserVo { private List<Integer> ids; }
\n用 foreach 来改写 select * from user where id=1 or id=2 or id=3
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id ="selectUserByListId" parameterType ="com.ajream.entity.UserVo" resultType ="com.ajream.entity.User" > select * from user <where > <foreach collection ="ids" item ="id" open ="and (" close =")" separator ="or" > id=#{id} </foreach > </where > </select >
\n用 foreach 来改写 select * from user where id in (1,2,3)
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id ="selectUserByListId" parameterType ="com.ys.vo.UserVo" resultType ="com.ys.po.User" > select * from user <where > <foreach collection ="ids" item ="id" open ="and id in (" close =") " separator ="," > #{id} </foreach > </where > </select >
\n总结:
\n动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"前面我们通过实例讲解了用mybatis对一张表进行的CRUD操作,但是我们发现写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL,稍微不注意,由于引号,空格等缺失可能都会导致错误。
\n那么怎么去解决这个问题呢?这就是本篇所讲的使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。
\n对于这样一张表,要根据 id/username/passwd/age
来选出指定的一条记录,用sql语句可能会这样写:
\n1 2 select * from t_acountwhere id= 7 and username= "xiaoBai" and passwd= "12345xb" and age= 24 ;
\n
\n在Java中需要这样:
\n接口
\n1 2 3 4 5 6 7 8 package com.ajream.dao;import com.ajream.entity.Account;public interface AccountDao { Account findByAccount (Account account) ; }
\n在mapper文件中:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.ajream.dao.AccountDao" > <resultMap id ="resultAccount" type ="com.ajream.entity.Account" > <id column ="id" property ="id" /> <result column ="passwd" property ="passwd" /> <result column ="username" property ="name" /> <result column ="age" property ="age" /> </resultMap > <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > select * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age} </select > </mapper >
\n测试
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.ajream.test;import com.ajream.dao.AccountDao;import com.ajream.entity.Account;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.InputStream;public class AccountDaoTest { public static void main (String[] args) { InputStream inputStream = AccountDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); Account account = new Account(); account.setId(7 ); account.setAge(24 ); account.setName("xiaoBai" ); account.setPasswd("12345xb" ); AccountDao accountDao = sqlSession.getMapper(AccountDao.class); Account account1 = accountDao.findByAccount(account); System.out.println(account1); } }
\n假如,在下面这句代码中
\n1 2 3 <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > \tselect * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age} </select >
\n某一个属性如passwd
值为null,那么查询不到任何一条记录,为了避免这种情况,mybatis提供了动态生成sl语句的方式
\nif标签 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > select * from t_acount where <if test ="id!=0" > id=#{id} </if > <if test ="name!=null" > and username=#{name} </if > <if test ="passwd!=null" > and passwd=#{passwd} </if > <if test ="age!=0" > and age=#{age} </if > </select >
\n这样写我们可以看到,如果 passwd
等于 null,那么查询语句为:
\n1 select * from t_acount where id=#{id} and username=#{name} and age=#{age}
\n但是如果 id
为空呢?那么查询语句为:
\nselect * from t_acount where and username=#{name} and age=#{age}
\n这是错误的 SQL 语句,如何解决呢?请看下面的 where/if 语句
\nwhere/if标签 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > select * from t_acount <where > <if test ="id!=0" > id=#{id} </if > <if test ="name!=null" > and username=#{name} </if > <if test ="passwd!=null" > and passwd=#{passwd} </if > <if test ="age!=0" > and age=#{age} </if > </where > </select >
\n这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个where
,此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。
\nchoose/when-otherwise标签 有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > select * from t_acount <where > <choose > <when test ="id != 0" > \tid=#{id} </when > <when test ="name !='' and name != null" > \tand username=#{name} </when > <when test ="passwd!='' and passwd!=null" > \tand passwd=#{passwd} </when > <otherwise > and age=#{age} </otherwise > </choose > </where > </select >
\n也就是说,这里有4个条件:id, name, passwd, age,只能选择一个作为查询条件
\n\n如果 id 不为空,那么查询语句为:
\n1 select * from t_acount where id=#{id}
\n \n \n\ntrim标签 trim标记是一个格式化的标记,可以完成set或者是where标记的功能
\n用 trim 改写前面的 where-if 语句
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > select * from t_acount <trim prefix ="where" prefixOverrides ="and | or" > <if test ="id!=0" > id=#{id} </if > <if test ="name!=null" > and username=#{name} </if > <if test ="passwd!=null" > and passwd=#{passwd} </if > <if test ="age!=0" > and age=#{age} </if > </trim > </select >
\nprefix: 表示添加前缀 where
\nprefixOverrides: 去掉第一个and
或者是or
\nset/if标签 1 2 3 4 5 6 7 8 9 10 11 12 13 <update id ="update" parameterType ="com.ajream.entity.Account" > update t_acount <set > <if test ="name != null and username != ''" > username = #{name}, </if > <if test ="passwd != null and passwd != ''" > passwd=#{passwd} </if > </set > where id=#{id} </update >
\n这样写,如果第一个条件 name 为空,那么 sql 语句为
\n1 update t_acount set passwd=#{passwd} where id=#{id}
\n如果第两个条件都不为空,那么 sql 语句为
\n1 update t_acount set username=#{name}, passwd=#{passwd} where id=#{id}
\nsql(片段)标签 有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
\n比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:
\n1 2 3 4 5 6 7 8 9 <sql id ="selectBySQL" > <if test ="name != null and name != ''" > AND username = #{name} </if > <if test ="passwd != null and passwd != ''" > AND passwd = #{passwd} </if > </sql >
\n引用sql片段
\n1 2 3 4 5 6 7 8 <select id ="findByAccount" parameterType ="com.ajream.entity.Account" resultMap ="resultAccount" > select * from t_acount <trim prefix ="where" prefixOverrides ="and | or" > <include refid ="selectBySQL" /> </trim > </select >
\nforeach标签 需求:我们需要查询 user 表中 id 分别为1,2,3的用户
\n sql语句:
\n1 2 select * from user where id= 1 or id= 2 or id= 3 ;select * from user where id in (1 ,2 ,3 );
\n建立一个 UserVo 类,里面封装一个 List<Integer> ids
的属性
\n1 2 3 4 5 6 7 8 9 10 11 package com.ajream.entity; import java.util.List;import lombok.Data;@Data public class UserVo { private List<Integer> ids; }
\n用 foreach 来改写 select * from user where id=1 or id=2 or id=3
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id ="selectUserByListId" parameterType ="com.ajream.entity.UserVo" resultType ="com.ajream.entity.User" > select * from user <where > <foreach collection ="ids" item ="id" open ="and (" close =")" separator ="or" > id=#{id} </foreach > </where > </select >
\n用 foreach 来改写 select * from user where id in (1,2,3)
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id ="selectUserByListId" parameterType ="com.ys.vo.UserVo" resultType ="com.ys.po.User" > select * from user <where > <foreach collection ="ids" item ="id" open ="and id in (" close =") " separator ="," > #{id} </foreach > </where > </select >
\n总结:
\n动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。
\n"},{"title":"mybatis延迟加载-缓存机制(六)","description":"mybatis通过延迟加载、缓存机制来提高程序运行效率","abbrlink":"3b32a20d","date":"2021-08-27T07:43:16.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n\n\n\n## 延迟加载\n\nMyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。\n\nMyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。\n\n\n\n### 加载时机\n\n直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。\n\n侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。\n\n深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。\n\n{% note info flat %}\n\n注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。\n因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。\n\n{% endnote %}\n\n\n### 侵入式延迟加载\n\n1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置侵入式加载\n\n ```xml\n \n \n \t\n \n ```\n2. 不调用主加载对象时只有一条SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231810.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231811.png)\n\n3. 调用主加载对象的信息时会产生两条SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231812.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231813.png)\n\n\n### 深入式延迟加载\n\n1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置深度加载\n\n ```xml\n \n \n \n \n ```\n2. 调用主加载对象时不会执行第二条加载SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231814.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231915.png)\n\n3. 调用关联对象详细信息时会执行第二次查询\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231916.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231917.png)\n\n\n\n\n\n\n\n## 缓存\n\n\n\nMyBatis拥有自己的缓存结构,可以用来缓解数据库压力,加快查询速度。\n\nMyBatis提供一级缓存和二级缓存的机制。\n\n\n\n### 一级缓存\n\n一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。\n\n当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。\n\n\n\n### 二级缓存\n\n二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件 中的sql语句,多个SqlSession类的实例对象可以共用二级缓存\n\n二级缓存是跨SqlSession的。\n\n一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。\n\n开启二级缓存:要开启二级缓存需要在mybatis配置文件中设置cacheEnabled属性为true\n\n```xml\n \n \n \n```\n\n\n\n![image-20210829001744654](https://gitee.com/ajream/images/raw/master/img/20210829001748_image-20210829001744654.png)\n\n\n\nMyBatis的缓存模式如图所示:\n\n\n\n![image-20210829001805748](https://gitee.com/ajream/images/raw/master/img/20210829001807_image-20210829001805748.png)\n\n\n\n\n\n注意:有些时候在web工程中会将执行查询操作的方法封装在某个Service方法中,当查询万一次后,Service方法结束,此时SqlSession类的实例对下岗就会关闭,一级缓存就会被清空,测试若再次调用Service方法查询同一个信息,新打开一个SqlSession类的实例对象,由于一级缓存区域是空的,因而无法从缓存中获取信息;出现这种情况时,就可以使用二级缓存。\n\n","source":"_posts/Mybatis/mybatis延迟加载(六).md","raw":"---\ntitle: mybatis延迟加载-缓存机制(六)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: mybatis通过延迟加载、缓存机制来提高程序运行效率\nabbrlink: 3b32a20d\ndate: 2021-08-27 15:43:16\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n\n\n\n## 延迟加载\n\nMyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。\n\nMyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。\n\n\n\n### 加载时机\n\n直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。\n\n侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。\n\n深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。\n\n{% note info flat %}\n\n注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。\n因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。\n\n{% endnote %}\n\n\n### 侵入式延迟加载\n\n1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置侵入式加载\n\n ```xml\n \n \n \t\n \n ```\n2. 不调用主加载对象时只有一条SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231810.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231811.png)\n\n3. 调用主加载对象的信息时会产生两条SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231812.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231813.png)\n\n\n### 深入式延迟加载\n\n1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置深度加载\n\n ```xml\n \n \n \n \n ```\n2. 调用主加载对象时不会执行第二条加载SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231814.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231915.png)\n\n3. 调用关联对象详细信息时会执行第二次查询\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231916.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231917.png)\n\n\n\n\n\n\n\n## 缓存\n\n\n\nMyBatis拥有自己的缓存结构,可以用来缓解数据库压力,加快查询速度。\n\nMyBatis提供一级缓存和二级缓存的机制。\n\n\n\n### 一级缓存\n\n一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。\n\n当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。\n\n\n\n### 二级缓存\n\n二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件 中的sql语句,多个SqlSession类的实例对象可以共用二级缓存\n\n二级缓存是跨SqlSession的。\n\n一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。\n\n开启二级缓存:要开启二级缓存需要在mybatis配置文件中设置cacheEnabled属性为true\n\n```xml\n \n \n \n```\n\n\n\n![image-20210829001744654](https://gitee.com/ajream/images/raw/master/img/20210829001748_image-20210829001744654.png)\n\n\n\nMyBatis的缓存模式如图所示:\n\n\n\n![image-20210829001805748](https://gitee.com/ajream/images/raw/master/img/20210829001807_image-20210829001805748.png)\n\n\n\n\n\n注意:有些时候在web工程中会将执行查询操作的方法封装在某个Service方法中,当查询万一次后,Service方法结束,此时SqlSession类的实例对下岗就会关闭,一级缓存就会被清空,测试若再次调用Service方法查询同一个信息,新打开一个SqlSession类的实例对象,由于一级缓存区域是空的,因而无法从缓存中获取信息;出现这种情况时,就可以使用二级缓存。\n\n","slug":"Mybatis/mybatis延迟加载(六)","published":1,"updated":"2021-08-30T13:49:53.564Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pn003hakvebel7gurk","content":"延迟加载 MyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。
\nMyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。
\n加载时机 直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。
\n侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。
\n深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。
\n注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。 因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。
\n
\n侵入式延迟加载 \nMybatis-config.xml大配置文件,首先开启延迟加载,然后再配置侵入式加载
\n1 2 3 4 5 6 7 <setting name ="lazyLoadingEnabled" value ="true" /> \t <setting name ="aggressiveLazyLoading" value ="true" />
\n不调用主加载对象时只有一条SQL
\n
\n
\n \n调用主加载对象的信息时会产生两条SQL
\n
\n
\n \n \n深入式延迟加载 \nMybatis-config.xml大配置文件,首先开启延迟加载,然后再配置深度加载
\n1 2 3 4 5 6 7 <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="aggressiveLazyLoading" value ="false" />
\n调用主加载对象时不会执行第二条加载SQL
\n
\n
\n \n调用关联对象详细信息时会执行第二次查询
\n
\n
\n \n \n缓存 MyBatis拥有自己的缓存结构,可以用来缓解数据库压力,加快查询速度。
\nMyBatis提供一级缓存和二级缓存的机制。
\n一级缓存 一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。
\n当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。
\n二级缓存 二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件 中的sql语句,多个SqlSession类的实例对象可以共用二级缓存
\n二级缓存是跨SqlSession的。
\n一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。
\n开启二级缓存:要开启二级缓存需要在mybatis配置文件中设置cacheEnabled属性为true
\n1 2 3 <settings > <setting name ="cacheEnabled" value ="true" /> </settings >
\n
\nMyBatis的缓存模式如图所示:
\n
\n注意:有些时候在web工程中会将执行查询操作的方法封装在某个Service方法中,当查询万一次后,Service方法结束,此时SqlSession类的实例对下岗就会关闭,一级缓存就会被清空,测试若再次调用Service方法查询同一个信息,新打开一个SqlSession类的实例对象,由于一级缓存区域是空的,因而无法从缓存中获取信息;出现这种情况时,就可以使用二级缓存。
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"延迟加载 MyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。
\nMyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。
\n加载时机 直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。
\n侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。
\n深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。
\n注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。 因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。
\n
\n侵入式延迟加载 \nMybatis-config.xml大配置文件,首先开启延迟加载,然后再配置侵入式加载
\n1 2 3 4 5 6 7 <setting name ="lazyLoadingEnabled" value ="true" /> \t <setting name ="aggressiveLazyLoading" value ="true" />
\n不调用主加载对象时只有一条SQL
\n
\n
\n \n调用主加载对象的信息时会产生两条SQL
\n
\n
\n \n \n深入式延迟加载 \nMybatis-config.xml大配置文件,首先开启延迟加载,然后再配置深度加载
\n1 2 3 4 5 6 7 <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="aggressiveLazyLoading" value ="false" />
\n调用主加载对象时不会执行第二条加载SQL
\n
\n
\n \n调用关联对象详细信息时会执行第二次查询
\n
\n
\n \n \n缓存 MyBatis拥有自己的缓存结构,可以用来缓解数据库压力,加快查询速度。
\nMyBatis提供一级缓存和二级缓存的机制。
\n一级缓存 一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。
\n当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。
\n二级缓存 二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件 中的sql语句,多个SqlSession类的实例对象可以共用二级缓存
\n二级缓存是跨SqlSession的。
\n一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。
\n开启二级缓存:要开启二级缓存需要在mybatis配置文件中设置cacheEnabled属性为true
\n1 2 3 <settings > <setting name ="cacheEnabled" value ="true" /> </settings >
\n
\nMyBatis的缓存模式如图所示:
\n
\n注意:有些时候在web工程中会将执行查询操作的方法封装在某个Service方法中,当查询万一次后,Service方法结束,此时SqlSession类的实例对下岗就会关闭,一级缓存就会被清空,测试若再次调用Service方法查询同一个信息,新打开一个SqlSession类的实例对象,由于一级缓存区域是空的,因而无法从缓存中获取信息;出现这种情况时,就可以使用二级缓存。
\n"},{"title":"mybatis介绍与使用(一)","date":"2021-08-25T02:30:46.000Z","abbrlink":"78903d87","description":"关于mybatis的简介与使用(第一个Mybatis程序)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n## 1、简介\n\n### 什么是 MyBatis\n\n\tMyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。\n\n\n\n\n\n## 2、第一个MyBatis项目\n\n[项目地址mybatis-01](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master/)\n\n### (1)安装库\n\n\t如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:\n\n```xml\n\n mysql \n mysql-connector-java \n 8.0.26 \n \n \n\n\n org.mybatis \n mybatis \n 3.5.6 \n \n\n\n\n junit \n junit \n 4.13 \n test \n \n```\n\n### (2)创建MyBatis工具类\n\n为了获取数据库表中的数据,需要创建一个MyBatis工具类com.ajream.utils.MybatisUtil,用于生成Mysql的会话,通过这个会话,可以get到数据库表中的数据\n\ncom/ajream/utils/MybatisUtil.java\n\n```java\npackage com.ajream.utils;\n\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class MybatisUtil {\n\n private static SqlSessionFactory sqlSessionFactory;\n\n static {\n try {\n //mybatis-config.xml是mybatis配置文件,用于配置MySQL信息\n String resource = \"mybatis-config.xml\"; \n InputStream inputStream = Resources.getResourceAsStream(resource);\n sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);\n }catch (IOException e){\n e.printStackTrace();\n }\n }\n \n //生成Mysql的会话\n public static SqlSession getSqlSesion(){\n return sqlSessionFactory.openSession(); \n }\n\n}\n\n```\n\n\n\n配置文件:mybatis-config.xml (用于配置连接MySQL的基本信息)\n\n```xml\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n```\n\n\n\n\n\n\n\n### (3)存放数据的容器类\n\n这是某个数据库school中student表中的数据\n\n![image-20210813000155615](https://gitee.com/ajream/images/raw/master/img/2021-08-13 00-01-58_image-20210813000155615.png)\n\n\n\n所以这里创建一个User类来表示数据库中的字段\n\ncom/ajream/pojo/User.java\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String id;\n private String name;\n private int age;\n\n public User() {\n }\n\n public User(String id, String name, int age) {\n this.id = id;\n this.name = name;\n this.age = age;\n }\n\n public String getId() {\n return id;\n }\n\n public void setId(String id) {\n this.id = id;\n }\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n public int getAge() {\n return age;\n }\n\n public void setAge(int age) {\n this.age = age;\n }\n \n @Override\n public String toString() {\n return \"User{\" +\n \"id='\" + id + '\\'' +\n \", name='\" + name + '\\'' +\n \", age=\" + age +\n '}';\n }\n}\n\n```\n\n\n\n### (4)创建一个接口\n\n(用于获取并存放拿到的数据)\n\ncom/ajream/dao/UserDao.java\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.pojo.User;\n\nimport java.util.List;\n\npublic interface UserDao {\n List getUserList();\n}\n\n```\n\n\n\n\n\n### (5)实现这个接口\n\n(即用sql语句获取数据)\n\n通常我们会创建一个 `UserDaoImpl` 来实现这个接口,但使用mybatis可以通过配置文件来代理实现:\n\ncom/ajream/dao/UserMapper.xml\n\n```xml\n\n\n\n\n\n \n \n \n select * from school.student\n \n \n\n \n \n```\n\n> Mapper.xml用于将MySQL数据库与Java类相联系起来\n>\n> - insert标签表示插入操作\n> - select标签是查询操作\n> - update是修改操作\n> - delete标签是删除操作\n\n\n\n> 最后要记得在mybatis配置文件中注册mapper.xml文件,即在mybatis-config.xml文件添加:\n>\n> ```xml\n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n\n\n### (6)测试\n\n以上配置完成后,可以写个test测试程序来取出数据库中的数据了\n\n\n\ncom/ajream/dao/UserDaoTest.java\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.pojo.User;\nimport com.ajream.utils.MybatisUtil;\nimport org.apache.ibatis.session.SqlSession;\nimport org.junit.Test;\n\nimport java.util.List;\n\n\npublic class UserDaoTest {\n\n @Test\n public void userDaoTest(){\n\n //获取MySQL会话\n SqlSession sqlSession = MybatisUtil.getSqlSesion();\n \n UserDao userDao = sqlSession.getMapper(UserDao.class);\n \n List userList = userDao.getUserList();\n\n for(User u: userList){\n System.out.println(u);\n }\n\n sqlSession.close();\n }\n}\n\n```\n\n\n\n### (7)注意\n\nidea中只能读取到resources中的xml文件,而”Java“文件夹中的xml文件不能读取到,所以运行上面的测试会报错,要在pom.xml文件中添加如下配置:\n\n```xml\n\n \n \n src/main/resources \n \n **/*.properties \n **/*.xml \n \n true \n \n \n src/main/java \n \n **/*.properties \n **/*.xml \n \n true \n \n \n \n```\n\n\n\n运行结果:\n\n\n\n![image-20210813001800000](https://gitee.com/ajream/images/raw/master/img/2021-08-13 00-18-01_image-20210813001800000.png)\n\n","source":"_posts/Mybatis/mybatis简单介绍与使用(一).md","raw":"---\ntitle: mybatis介绍与使用(一)\ndate: 2021-08-25 10:30:46\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\nabbrlink: 78903d87\ndescription: 关于mybatis的简介与使用(第一个Mybatis程序)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n## 1、简介\n\n### 什么是 MyBatis\n\n\tMyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。\n\n\n\n\n\n## 2、第一个MyBatis项目\n\n[项目地址mybatis-01](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master/)\n\n### (1)安装库\n\n\t如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:\n\n```xml\n\n mysql \n mysql-connector-java \n 8.0.26 \n \n \n\n\n org.mybatis \n mybatis \n 3.5.6 \n \n\n\n\n junit \n junit \n 4.13 \n test \n \n```\n\n### (2)创建MyBatis工具类\n\n为了获取数据库表中的数据,需要创建一个MyBatis工具类com.ajream.utils.MybatisUtil,用于生成Mysql的会话,通过这个会话,可以get到数据库表中的数据\n\ncom/ajream/utils/MybatisUtil.java\n\n```java\npackage com.ajream.utils;\n\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class MybatisUtil {\n\n private static SqlSessionFactory sqlSessionFactory;\n\n static {\n try {\n //mybatis-config.xml是mybatis配置文件,用于配置MySQL信息\n String resource = \"mybatis-config.xml\"; \n InputStream inputStream = Resources.getResourceAsStream(resource);\n sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);\n }catch (IOException e){\n e.printStackTrace();\n }\n }\n \n //生成Mysql的会话\n public static SqlSession getSqlSesion(){\n return sqlSessionFactory.openSession(); \n }\n\n}\n\n```\n\n\n\n配置文件:mybatis-config.xml (用于配置连接MySQL的基本信息)\n\n```xml\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n```\n\n\n\n\n\n\n\n### (3)存放数据的容器类\n\n这是某个数据库school中student表中的数据\n\n![image-20210813000155615](https://gitee.com/ajream/images/raw/master/img/2021-08-13 00-01-58_image-20210813000155615.png)\n\n\n\n所以这里创建一个User类来表示数据库中的字段\n\ncom/ajream/pojo/User.java\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String id;\n private String name;\n private int age;\n\n public User() {\n }\n\n public User(String id, String name, int age) {\n this.id = id;\n this.name = name;\n this.age = age;\n }\n\n public String getId() {\n return id;\n }\n\n public void setId(String id) {\n this.id = id;\n }\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n public int getAge() {\n return age;\n }\n\n public void setAge(int age) {\n this.age = age;\n }\n \n @Override\n public String toString() {\n return \"User{\" +\n \"id='\" + id + '\\'' +\n \", name='\" + name + '\\'' +\n \", age=\" + age +\n '}';\n }\n}\n\n```\n\n\n\n### (4)创建一个接口\n\n(用于获取并存放拿到的数据)\n\ncom/ajream/dao/UserDao.java\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.pojo.User;\n\nimport java.util.List;\n\npublic interface UserDao {\n List getUserList();\n}\n\n```\n\n\n\n\n\n### (5)实现这个接口\n\n(即用sql语句获取数据)\n\n通常我们会创建一个 `UserDaoImpl` 来实现这个接口,但使用mybatis可以通过配置文件来代理实现:\n\ncom/ajream/dao/UserMapper.xml\n\n```xml\n\n\n\n\n\n \n \n \n select * from school.student\n \n \n\n \n \n```\n\n> Mapper.xml用于将MySQL数据库与Java类相联系起来\n>\n> - insert标签表示插入操作\n> - select标签是查询操作\n> - update是修改操作\n> - delete标签是删除操作\n\n\n\n> 最后要记得在mybatis配置文件中注册mapper.xml文件,即在mybatis-config.xml文件添加:\n>\n> ```xml\n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n\n\n### (6)测试\n\n以上配置完成后,可以写个test测试程序来取出数据库中的数据了\n\n\n\ncom/ajream/dao/UserDaoTest.java\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.pojo.User;\nimport com.ajream.utils.MybatisUtil;\nimport org.apache.ibatis.session.SqlSession;\nimport org.junit.Test;\n\nimport java.util.List;\n\n\npublic class UserDaoTest {\n\n @Test\n public void userDaoTest(){\n\n //获取MySQL会话\n SqlSession sqlSession = MybatisUtil.getSqlSesion();\n \n UserDao userDao = sqlSession.getMapper(UserDao.class);\n \n List userList = userDao.getUserList();\n\n for(User u: userList){\n System.out.println(u);\n }\n\n sqlSession.close();\n }\n}\n\n```\n\n\n\n### (7)注意\n\nidea中只能读取到resources中的xml文件,而”Java“文件夹中的xml文件不能读取到,所以运行上面的测试会报错,要在pom.xml文件中添加如下配置:\n\n```xml\n\n \n \n src/main/resources \n \n **/*.properties \n **/*.xml \n \n true \n \n \n src/main/java \n \n **/*.properties \n **/*.xml \n \n true \n \n \n \n```\n\n\n\n运行结果:\n\n\n\n![image-20210813001800000](https://gitee.com/ajream/images/raw/master/img/2021-08-13 00-18-01_image-20210813001800000.png)\n\n","slug":"Mybatis/mybatis简单介绍与使用(一)","published":1,"updated":"2021-08-29T14:44:46.760Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6po003kakveajnw0fjv","content":"1、简介 什么是 MyBatis MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
\n2、第一个MyBatis项目 项目地址mybatis-01
\n(1)安装库 如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.26</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.6</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > <scope > test</scope > </dependency >
\n(2)创建MyBatis工具类 为了获取数据库表中的数据,需要创建一个MyBatis工具类com.ajream.utils.MybatisUtil,用于生成Mysql的会话,通过这个会话,可以get到数据库表中的数据
\ncom/ajream/utils/MybatisUtil.java
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.ajream.utils;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;import java.io.InputStream;public class MybatisUtil { private static SqlSessionFactory sqlSessionFactory; static { try { String resource = "mybatis-config.xml" ; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); }catch (IOException e){ e.printStackTrace(); } } public static SqlSession getSqlSesion () { return sqlSessionFactory.openSession(); } }
\n配置文件:mybatis-config.xml (用于配置连接MySQL的基本信息)
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" /> <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.cj.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/school?useSSL=true& useUnicode=true& characterEncoding=UTF-8" /> <property name ="username" value ="xxx" /> <property name ="password" value ="xxx" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="com/ajream/dao/UserMapper.xml" /> </mappers > </configuration >
\n(3)存放数据的容器类 这是某个数据库school中student表中的数据
\n
\n所以这里创建一个User类来表示数据库中的字段
\ncom/ajream/pojo/User.java
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 package com.ajream.pojo;public class User { private String id; private String name; private int age; public User () { } public User (String id, String name, int age) { this .id = id; this .name = name; this .age = age; } public String getId () { return id; } public void setId (String id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } @Override public String toString () { return "User{" + "id='" + id + '\\'' + ", name='" + name + '\\'' + ", age=" + age + '}' ; } }
\n(4)创建一个接口 (用于获取并存放拿到的数据)
\ncom/ajream/dao/UserDao.java
\n1 2 3 4 5 6 7 8 9 10 package com.ajream.dao;import com.ajream.pojo.User;import java.util.List;public interface UserDao { List<User> getUserList () ; }
\n(5)实现这个接口 (即用sql语句获取数据)
\n通常我们会创建一个 UserDaoImpl
来实现这个接口,但使用mybatis可以通过配置文件来代理实现:
\ncom/ajream/dao/UserMapper.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.ajream.dao.UserDao" > <select id ="getUserList" resultType ="com.ajream.pojo.User" > select * from school.student </select > </mapper >
\n\nMapper.xml用于将MySQL数据库与Java类相联系起来
\n\ninsert标签表示插入操作 \nselect标签是查询操作 \nupdate是修改操作 \ndelete标签是删除操作 \n \n最后要记得在mybatis配置文件中注册mapper.xml文件,即在mybatis-config.xml文件添加:
\n1 2 3 4 <mappers > <mapper resource ="com/ajream/dao/UserMapper.xml" /> </mappers >
\n \n(6)测试 以上配置完成后,可以写个test测试程序来取出数据库中的数据了
\ncom/ajream/dao/UserDaoTest.java
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.ajream.dao;import com.ajream.pojo.User;import com.ajream.utils.MybatisUtil;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.List;public class UserDaoTest { @Test public void userDaoTest () { SqlSession sqlSession = MybatisUtil.getSqlSesion(); UserDao userDao = sqlSession.getMapper(UserDao.class); List<User> userList = userDao.getUserList(); for (User u: userList){ System.out.println(u); } sqlSession.close(); } }
\n(7)注意 idea中只能读取到resources中的xml文件,而”Java“文件夹中的xml文件不能读取到,所以运行上面的测试会报错,要在pom.xml文件中添加如下配置:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <build > <resources > <resource > <directory > src/main/resources</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > <resource > <directory > src/main/java</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > </resources > </build >
\n运行结果:
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"1、简介 什么是 MyBatis MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
\n2、第一个MyBatis项目 项目地址mybatis-01
\n(1)安装库 如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.26</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.6</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > <scope > test</scope > </dependency >
\n(2)创建MyBatis工具类 为了获取数据库表中的数据,需要创建一个MyBatis工具类com.ajream.utils.MybatisUtil,用于生成Mysql的会话,通过这个会话,可以get到数据库表中的数据
\ncom/ajream/utils/MybatisUtil.java
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.ajream.utils;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;import java.io.InputStream;public class MybatisUtil { private static SqlSessionFactory sqlSessionFactory; static { try { String resource = "mybatis-config.xml" ; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); }catch (IOException e){ e.printStackTrace(); } } public static SqlSession getSqlSesion () { return sqlSessionFactory.openSession(); } }
\n配置文件:mybatis-config.xml (用于配置连接MySQL的基本信息)
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" /> <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.cj.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/school?useSSL=true& useUnicode=true& characterEncoding=UTF-8" /> <property name ="username" value ="xxx" /> <property name ="password" value ="xxx" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="com/ajream/dao/UserMapper.xml" /> </mappers > </configuration >
\n(3)存放数据的容器类 这是某个数据库school中student表中的数据
\n
\n所以这里创建一个User类来表示数据库中的字段
\ncom/ajream/pojo/User.java
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 package com.ajream.pojo;public class User { private String id; private String name; private int age; public User () { } public User (String id, String name, int age) { this .id = id; this .name = name; this .age = age; } public String getId () { return id; } public void setId (String id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } @Override public String toString () { return "User{" + "id='" + id + '\\'' + ", name='" + name + '\\'' + ", age=" + age + '}' ; } }
\n(4)创建一个接口 (用于获取并存放拿到的数据)
\ncom/ajream/dao/UserDao.java
\n1 2 3 4 5 6 7 8 9 10 package com.ajream.dao;import com.ajream.pojo.User;import java.util.List;public interface UserDao { List<User> getUserList () ; }
\n(5)实现这个接口 (即用sql语句获取数据)
\n通常我们会创建一个 UserDaoImpl
来实现这个接口,但使用mybatis可以通过配置文件来代理实现:
\ncom/ajream/dao/UserMapper.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.ajream.dao.UserDao" > <select id ="getUserList" resultType ="com.ajream.pojo.User" > select * from school.student </select > </mapper >
\n\nMapper.xml用于将MySQL数据库与Java类相联系起来
\n\ninsert标签表示插入操作 \nselect标签是查询操作 \nupdate是修改操作 \ndelete标签是删除操作 \n \n最后要记得在mybatis配置文件中注册mapper.xml文件,即在mybatis-config.xml文件添加:
\n1 2 3 4 <mappers > <mapper resource ="com/ajream/dao/UserMapper.xml" /> </mappers >
\n \n(6)测试 以上配置完成后,可以写个test测试程序来取出数据库中的数据了
\ncom/ajream/dao/UserDaoTest.java
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.ajream.dao;import com.ajream.pojo.User;import com.ajream.utils.MybatisUtil;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.List;public class UserDaoTest { @Test public void userDaoTest () { SqlSession sqlSession = MybatisUtil.getSqlSesion(); UserDao userDao = sqlSession.getMapper(UserDao.class); List<User> userList = userDao.getUserList(); for (User u: userList){ System.out.println(u); } sqlSession.close(); } }
\n(7)注意 idea中只能读取到resources中的xml文件,而”Java“文件夹中的xml文件不能读取到,所以运行上面的测试会报错,要在pom.xml文件中添加如下配置:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <build > <resources > <resource > <directory > src/main/resources</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > <resource > <directory > src/main/java</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > </resources > </build >
\n运行结果:
\n
\n"},{"title":"mybatis介绍与使用(二)","description":"(一)的例子看不懂可以先看这个,也是一个入门mybatis的例子,该例子逻辑可能更清晰","abbrlink":"ab69c7ec","date":"2021-08-25T06:30:46.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n\n[项目地址mybatis-02](https://codechina.csdn.net/m0_46079750/mybatis-study)\n\n### 新建数据库表\n\n\n\n```sql\nUSE mybatis;\n\nCREATE TABLE t_acount(\n\tid INT PRIMARY KEY auto_increment,\n\tusername VARCHAR(11),\n\tpasswd VARCHAR(11),\n\tage INT\n)\n```\n\n\n\n### 配置mybatis开发环境\n\n\n\n使用maven创建新项目,在pom.xml导入依赖:\n\n```xml\n\n \n mysql \n mysql-connector-java \n 8.0.26 \n \n\n \n org.mybatis \n mybatis \n 3.5.6 \n \n\n \n junit \n junit \n 4.13 \n test \n \n\n \n org.projectlombok \n lombok \n RELEASE \n compile \n \n \n```\n\n\n\n新建数据表对应的Account实体类\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n//@Data可以为类提供读写功能,从而不用写get、set方法; 会为类提供 equals()、hashCode()、toString() 方法。\n//@AllArgsConstructor 自动添加有参构造方法\n//@NoArgsConstructor 添加无参构造方法\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Account {\n private int id;\n private String username;\n private String passwd;\n private int age;\n}\n\n```\n\n\n\n在resources创建mybatis配置文件mybatis-config.xml\n\n首先要导入约束(注意这个与配置文件那个的是不同的)\n\n```xml\n\n\n```\n\nmybatis-config.xml:\n\n```xml\n\n\n\n\n\n\n \n \n\n \n\n \n\n \n \n \n \n \n\n \n \n \n```\n\n到此mybatis开发环境已经搭建好,接下来进行开发\n\n\n\n\n\n### Mybatis开发方式\n\n1. 原生接口方式\n2. mapper代理实现自定义接口方式\n\n\n\n\n\n> **先介绍第一种:原生接口方式**\n\n\n\nmybatis框架需要开发者自定义sql语句,写在Mapper.xml文件\n\n实际中会为每个实体类创建对应mapper.xml来定义sql语句,来管理该实体类对象 \n\n\n\n\n\n(一)创建Mapper文件\n\n因为前面创建的实体类是Account,所以我这里创建AccountMapper.xml文件\n\n\n\n![image-20210826131020613](https://gitee.com/ajream/images/raw/master/img/20210826131024_image-20210826131020613.png)\n\n\n\n\n\n首先也是要导入约束:\n\n```xml\n\n\n```\n\nAccountMapper.xml:\n\n```xml\n\n\n\n\n\n\n\n \n insert into t_acount(id, username, passwd, age) values (#{id},#{username}, #{passwd}, #{age})\n \n \n```\n\n说明:\n\n1. namespace通常设置为文件所在包名+文件名(无后缀)\n\n2. 可以把这个Mapper文件理解成一个java类,类名是 `AccountMapper`, 有一个方法 `save` ,可以传入参数,参数类型是 `com.ajream.entity.Account`\n\n 这个方法的作用是执行insert插入数据操作\n\n\n\n\n\n(二)注册Mapper文件\n\n在mybatis-config.xml配置文件中注册mapper文件 , 添加:\n\n```xml\n\n\t \n \n```\n\n![image-20210826133054400](https://gitee.com/ajream/images/raw/master/img/20210826133057_image-20210826133054400.png)\n\n\n\n\n\n### 编写测试类进行运行\n\n1. 根据mybatis配置文件,使用工厂模式生成sqlSession\n2. 执行sql语句\n\n\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class MyTest {\n public static void main(String[] args) {\n //加载配置文件,获取sqlSession工厂对象\n InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream(\"mybatis02-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n// 通过工厂对象获取sqlSession\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n String statement= \"com.ajream.mapper.AccountMapper.save\"; //用于加载mapper配置文件的save方法\n Account account = new Account(2,\"XiaoZhang\", \"56789xz\", 20);\n sqlSession.insert(statement, account); //执行sql语句\n sqlSession.commit(); //增、删、改 最后必须提交事务\n\n }\n}\n\n```\n\n\n\n\n\n\n\n注意,如果用ideaIDE,由于xml配置文件默认是在resources中的,其他文件夹的无法识别,因此最后要在pom中添加如下语句,让idea能够识别其他文件夹的xml文件\n\n```xml\n\n \n \n src/main/resources \n \n **/*.properties \n **/*.xml \n \n true \n \n \n src/main/java \n \n **/*.properties \n **/*.xml \n \n true \n \n \n \n```\n\n\n\n\n\n如果报错:\n\n```\nCause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效。\n```\n\n在pom.xml中添加语句:\n\n```xml\n\n UTF-8 \n \n```\n\n![image-20210826150549895](https://gitee.com/ajream/images/raw/master/img/20210826150553_image-20210826150549895.png)\n\n\n\n\n\n\n\n> **第二种:Mapper代理实现自定义接口方式(推荐)**\n>\n> [项目地址mybatis-03](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master1)\n\n\n\n(一)创建接口:\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Account;\n\nimport java.util.List;\n\n//增删改查\npublic interface AccountDao {\n int save(Account account);\n int update(Account account);\n int deleteById(int id);\n List findAll();\n Account findById(int id);\n}\n\n```\n\n\n\n\n\n(二)创建接口对应的Mapper.xml文件(通常与接口文件放在同一个文件夹内),通过`insert/update/delete/select`标签来定义接口方法对应的sql语句\n\n\n\n规则:\n\n- namespace为接口的全类名 \n\n- id为接口中对应的方法名\n\n- parameterType为接口中对应方法的参数类型\n\n- resultType为接口对应方法的返回类型\n\n 注意:1-如果方法返回的是集合,则resultType是里面元素的类型\n\n \t 2-添加、修改、删除的返回类型默认都是int类型(表示影响的行数),所以不用指定\n\n\n\n```xml\n\n\n\n \n insert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})\n \n \n update t_acount set username=#{username},age=#{age},passwd=#{password} where id = #{id}\n \n \n delete from t_acount where id = #{id}\n \n\n \n select * from t_acount;\n \n\n \n select * from t_acount where id=#{id}\n \n \n\n\n```\n\n\n\n\n\n(三)注册mapper\n\n```xml\n\n \n \n```\n\n\n\n\n\n(四)测试\n\n\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AccountDao;\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\nimport java.util.List;\n\npublic class MyTest {\n public static void main(String[] args) {\n InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n// 实现接口的代理对象\n AccountDao accountDao = sqlSession.getMapper(AccountDao.class);\n\n// 增\n// accountDao.save(new Account(8, \"小叮当\", \"12345xdj\", 24));\n// sqlSession.commit(); //提交事务\n// 删\n// accountDao.deleteById(4);\n// sqlSession.commit(); //提交事务\n// 查\n// List list = accountDao.findAll();\n// for(Account account: list){\n// System.out.println(account);\n// }\n// Account account = accountDao.findById(2);\n// System.out.println(account);\n\n// 改(先查出要修改的对象,再修改对象的各个属性)\n Account account1 = accountDao.findById(1);\n account1.setUsername(\"小东\");\n account1.setAge(19);\n account1.setPasswd(\"56789xd\");\n\n int result = accountDao.update(account1);\n sqlSession.commit(); //提交事务\n System.out.println(\"修改行数:\" + result + \"修改后的对象:\");\n System.out.println(accountDao.findById(1));\n\n sqlSession.close(); //退出会话\n\n }\n}\n\n```\n\n\n\n这是进行测试的数据表\n\n\n\n![image-20210826203758449](https://gitee.com/ajream/images/raw/master/img/20210826203801_image-20210826203758449.png)\n\n\n\n\n\n","source":"_posts/Mybatis/mybatis简单使用(二).md","raw":"---\ntitle: mybatis介绍与使用(二)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: (一)的例子看不懂可以先看这个,也是一个入门mybatis的例子,该例子逻辑可能更清晰\nabbrlink: ab69c7ec\ndate: 2021-08-25 14:30:46\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n\n[项目地址mybatis-02](https://codechina.csdn.net/m0_46079750/mybatis-study)\n\n### 新建数据库表\n\n\n\n```sql\nUSE mybatis;\n\nCREATE TABLE t_acount(\n\tid INT PRIMARY KEY auto_increment,\n\tusername VARCHAR(11),\n\tpasswd VARCHAR(11),\n\tage INT\n)\n```\n\n\n\n### 配置mybatis开发环境\n\n\n\n使用maven创建新项目,在pom.xml导入依赖:\n\n```xml\n\n \n mysql \n mysql-connector-java \n 8.0.26 \n \n\n \n org.mybatis \n mybatis \n 3.5.6 \n \n\n \n junit \n junit \n 4.13 \n test \n \n\n \n org.projectlombok \n lombok \n RELEASE \n compile \n \n \n```\n\n\n\n新建数据表对应的Account实体类\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n//@Data可以为类提供读写功能,从而不用写get、set方法; 会为类提供 equals()、hashCode()、toString() 方法。\n//@AllArgsConstructor 自动添加有参构造方法\n//@NoArgsConstructor 添加无参构造方法\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Account {\n private int id;\n private String username;\n private String passwd;\n private int age;\n}\n\n```\n\n\n\n在resources创建mybatis配置文件mybatis-config.xml\n\n首先要导入约束(注意这个与配置文件那个的是不同的)\n\n```xml\n\n\n```\n\nmybatis-config.xml:\n\n```xml\n\n\n\n\n\n\n \n \n\n \n\n \n\n \n \n \n \n \n\n \n \n \n```\n\n到此mybatis开发环境已经搭建好,接下来进行开发\n\n\n\n\n\n### Mybatis开发方式\n\n1. 原生接口方式\n2. mapper代理实现自定义接口方式\n\n\n\n\n\n> **先介绍第一种:原生接口方式**\n\n\n\nmybatis框架需要开发者自定义sql语句,写在Mapper.xml文件\n\n实际中会为每个实体类创建对应mapper.xml来定义sql语句,来管理该实体类对象 \n\n\n\n\n\n(一)创建Mapper文件\n\n因为前面创建的实体类是Account,所以我这里创建AccountMapper.xml文件\n\n\n\n![image-20210826131020613](https://gitee.com/ajream/images/raw/master/img/20210826131024_image-20210826131020613.png)\n\n\n\n\n\n首先也是要导入约束:\n\n```xml\n\n\n```\n\nAccountMapper.xml:\n\n```xml\n\n\n\n\n\n\n\n \n insert into t_acount(id, username, passwd, age) values (#{id},#{username}, #{passwd}, #{age})\n \n \n```\n\n说明:\n\n1. namespace通常设置为文件所在包名+文件名(无后缀)\n\n2. 可以把这个Mapper文件理解成一个java类,类名是 `AccountMapper`, 有一个方法 `save` ,可以传入参数,参数类型是 `com.ajream.entity.Account`\n\n 这个方法的作用是执行insert插入数据操作\n\n\n\n\n\n(二)注册Mapper文件\n\n在mybatis-config.xml配置文件中注册mapper文件 , 添加:\n\n```xml\n\n\t \n \n```\n\n![image-20210826133054400](https://gitee.com/ajream/images/raw/master/img/20210826133057_image-20210826133054400.png)\n\n\n\n\n\n### 编写测试类进行运行\n\n1. 根据mybatis配置文件,使用工厂模式生成sqlSession\n2. 执行sql语句\n\n\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class MyTest {\n public static void main(String[] args) {\n //加载配置文件,获取sqlSession工厂对象\n InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream(\"mybatis02-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n// 通过工厂对象获取sqlSession\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n String statement= \"com.ajream.mapper.AccountMapper.save\"; //用于加载mapper配置文件的save方法\n Account account = new Account(2,\"XiaoZhang\", \"56789xz\", 20);\n sqlSession.insert(statement, account); //执行sql语句\n sqlSession.commit(); //增、删、改 最后必须提交事务\n\n }\n}\n\n```\n\n\n\n\n\n\n\n注意,如果用ideaIDE,由于xml配置文件默认是在resources中的,其他文件夹的无法识别,因此最后要在pom中添加如下语句,让idea能够识别其他文件夹的xml文件\n\n```xml\n\n \n \n src/main/resources \n \n **/*.properties \n **/*.xml \n \n true \n \n \n src/main/java \n \n **/*.properties \n **/*.xml \n \n true \n \n \n \n```\n\n\n\n\n\n如果报错:\n\n```\nCause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效。\n```\n\n在pom.xml中添加语句:\n\n```xml\n\n UTF-8 \n \n```\n\n![image-20210826150549895](https://gitee.com/ajream/images/raw/master/img/20210826150553_image-20210826150549895.png)\n\n\n\n\n\n\n\n> **第二种:Mapper代理实现自定义接口方式(推荐)**\n>\n> [项目地址mybatis-03](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master1)\n\n\n\n(一)创建接口:\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Account;\n\nimport java.util.List;\n\n//增删改查\npublic interface AccountDao {\n int save(Account account);\n int update(Account account);\n int deleteById(int id);\n List findAll();\n Account findById(int id);\n}\n\n```\n\n\n\n\n\n(二)创建接口对应的Mapper.xml文件(通常与接口文件放在同一个文件夹内),通过`insert/update/delete/select`标签来定义接口方法对应的sql语句\n\n\n\n规则:\n\n- namespace为接口的全类名 \n\n- id为接口中对应的方法名\n\n- parameterType为接口中对应方法的参数类型\n\n- resultType为接口对应方法的返回类型\n\n 注意:1-如果方法返回的是集合,则resultType是里面元素的类型\n\n \t 2-添加、修改、删除的返回类型默认都是int类型(表示影响的行数),所以不用指定\n\n\n\n```xml\n\n\n\n \n insert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})\n \n \n update t_acount set username=#{username},age=#{age},passwd=#{password} where id = #{id}\n \n \n delete from t_acount where id = #{id}\n \n\n \n select * from t_acount;\n \n\n \n select * from t_acount where id=#{id}\n \n \n\n\n```\n\n\n\n\n\n(三)注册mapper\n\n```xml\n\n \n \n```\n\n\n\n\n\n(四)测试\n\n\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AccountDao;\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\nimport java.util.List;\n\npublic class MyTest {\n public static void main(String[] args) {\n InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n// 实现接口的代理对象\n AccountDao accountDao = sqlSession.getMapper(AccountDao.class);\n\n// 增\n// accountDao.save(new Account(8, \"小叮当\", \"12345xdj\", 24));\n// sqlSession.commit(); //提交事务\n// 删\n// accountDao.deleteById(4);\n// sqlSession.commit(); //提交事务\n// 查\n// List list = accountDao.findAll();\n// for(Account account: list){\n// System.out.println(account);\n// }\n// Account account = accountDao.findById(2);\n// System.out.println(account);\n\n// 改(先查出要修改的对象,再修改对象的各个属性)\n Account account1 = accountDao.findById(1);\n account1.setUsername(\"小东\");\n account1.setAge(19);\n account1.setPasswd(\"56789xd\");\n\n int result = accountDao.update(account1);\n sqlSession.commit(); //提交事务\n System.out.println(\"修改行数:\" + result + \"修改后的对象:\");\n System.out.println(accountDao.findById(1));\n\n sqlSession.close(); //退出会话\n\n }\n}\n\n```\n\n\n\n这是进行测试的数据表\n\n\n\n![image-20210826203758449](https://gitee.com/ajream/images/raw/master/img/20210826203801_image-20210826203758449.png)\n\n\n\n\n\n","slug":"Mybatis/mybatis简单使用(二)","published":1,"updated":"2021-08-29T14:44:59.444Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pp003nakve3uzjgiks","content":"项目地址mybatis-02
\n新建数据库表 1 2 3 4 5 6 7 8 USE mybatis; CREATE TABLE t_acount(\tid INT PRIMARY KEY auto_increment, \tusername VARCHAR (11 ), \tpasswd VARCHAR (11 ), \tage INT )
\n配置mybatis开发环境 使用maven创建新项目,在pom.xml导入依赖:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <dependencies > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.26</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.6</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > <scope > test</scope > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > RELEASE</version > <scope > compile</scope > </dependency > </dependencies >
\n新建数据表对应的Account实体类
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.ajream.entity;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class Account { private int id; private String username; private String passwd; private int age; }
\n在resources创建mybatis配置文件mybatis-config.xml
\n首先要导入约束(注意这个与配置文件那个的是不同的)
\n1 2 3 4 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" >
\nmybatis-config.xml:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <environments default ="test1" > <environment id ="test1" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.cj.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/mybatis?useUnicode=true& characterEncoding=UTF-8" /> <property name ="username" value ="root" /> <property name ="password" value ="admin" /> </dataSource > </environment > </environments > </configuration >
\n到此mybatis开发环境已经搭建好,接下来进行开发
\nMybatis开发方式 \n原生接口方式 \nmapper代理实现自定义接口方式 \n \n\n先介绍第一种:原生接口方式
\n \nmybatis框架需要开发者自定义sql语句,写在Mapper.xml文件
\n实际中会为每个实体类创建对应mapper.xml来定义sql语句,来管理该实体类对象
\n(一)创建Mapper文件
\n因为前面创建的实体类是Account,所以我这里创建AccountMapper.xml文件
\n
\n首先也是要导入约束:
\n1 2 3 4 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
\nAccountMapper.xml:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.ajream.mapper.AccountMapper" > <insert id ="save" parameterType ="com.ajream.entity.Account" > insert into t_acount(id, username, passwd, age) values (#{id},#{username}, #{passwd}, #{age}) </insert > </mapper >
\n说明:
\n\nnamespace通常设置为文件所在包名+文件名(无后缀)
\n \n可以把这个Mapper文件理解成一个java类,类名是 AccountMapper
, 有一个方法 save
,可以传入参数,参数类型是 com.ajream.entity.Account
\n这个方法的作用是执行insert插入数据操作
\n \n \n(二)注册Mapper文件
\n在mybatis-config.xml配置文件中注册mapper文件 , 添加:
\n1 2 3 <mappers > \t<mapper resource ="com/ajream/mapper/AccountMapper.xml" > </mapper > </mappers >
\n
\n编写测试类进行运行 \n根据mybatis配置文件,使用工厂模式生成sqlSession \n执行sql语句 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.ajream.test;import com.ajream.entity.Account;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.InputStream;public class MyTest { public static void main (String[] args) { InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream("mybatis02-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); String statement= "com.ajream.mapper.AccountMapper.save" ; Account account = new Account(2 ,"XiaoZhang" , "56789xz" , 20 ); sqlSession.insert(statement, account); sqlSession.commit(); } }
\n注意,如果用ideaIDE,由于xml配置文件默认是在resources中的,其他文件夹的无法识别,因此最后要在pom中添加如下语句,让idea能够识别其他文件夹的xml文件
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <build > <resources > <resource > <directory > src/main/resources</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > <resource > <directory > src/main/java</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > </resources > </build >
\n如果报错:
\n1 Cause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效。
\n在pom.xml中添加语句:
\n1 2 3 <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > </properties >
\n
\n\n第二种:Mapper代理实现自定义接口方式(推荐)
\n项目地址mybatis-03
\n \n(一)创建接口:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.ajream.dao;import com.ajream.entity.Account;import java.util.List;public interface AccountDao { int save (Account account) ; int update (Account account) ; int deleteById (int id) ; List<Account> findAll () ; Account findById (int id) ; }
\n(二)创建接口对应的Mapper.xml文件(通常与接口文件放在同一个文件夹内),通过insert/update/delete/select
标签来定义接口方法对应的sql语句
\n规则:
\n\nnamespace为接口的全类名
\n \nid为接口中对应的方法名
\n \nparameterType为接口中对应方法的参数类型
\n \nresultType为接口对应方法的返回类型
\n注意:1-如果方法返回的是集合,则resultType是里面元素的类型
\n 2-添加、修改、删除的返回类型默认都是int类型(表示影响的行数),所以不用指定
\n \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.ajream.dao.AccountDao" > <insert id ="save" parameterType ="com.ajream.entity.Account" > insert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age}) </insert > <update id ="update" parameterType ="com.ajream.entity.Account" > update t_acount set username=#{username},age=#{age},passwd=#{password} where id = #{id} </update > <delete id ="deleteById" parameterType ="int" > delete from t_acount where id = #{id} </delete > <select id ="findAll" resultType ="com.ajream.entity.Account" > select * from t_acount; </select > <select id ="findById" resultType ="com.ajream.entity.Account" parameterType ="int" > select * from t_acount where id=#{id} </select > </mapper >
\n(三)注册mapper
\n1 2 3 <mappers > <mapper resource ="com/ajream/dao/AccountDao.xml" /> </mappers >
\n(四)测试
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 package com.ajream.test;import com.ajream.dao.AccountDao;import com.ajream.entity.Account;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.InputStream;import java.util.List;public class MyTest { public static void main (String[] args) { InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); AccountDao accountDao = sqlSession.getMapper(AccountDao.class); Account account1 = accountDao.findById(1 ); account1.setUsername("小东" ); account1.setAge(19 ); account1.setPasswd("56789xd" ); int result = accountDao.update(account1); sqlSession.commit(); System.out.println("修改行数:" + result + "修改后的对象:" ); System.out.println(accountDao.findById(1 )); sqlSession.close(); } }
\n这是进行测试的数据表
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"项目地址mybatis-02
\n新建数据库表 1 2 3 4 5 6 7 8 USE mybatis; CREATE TABLE t_acount(\tid INT PRIMARY KEY auto_increment, \tusername VARCHAR (11 ), \tpasswd VARCHAR (11 ), \tage INT )
\n配置mybatis开发环境 使用maven创建新项目,在pom.xml导入依赖:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <dependencies > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.26</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.6</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > <scope > test</scope > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > RELEASE</version > <scope > compile</scope > </dependency > </dependencies >
\n新建数据表对应的Account实体类
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.ajream.entity;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class Account { private int id; private String username; private String passwd; private int age; }
\n在resources创建mybatis配置文件mybatis-config.xml
\n首先要导入约束(注意这个与配置文件那个的是不同的)
\n1 2 3 4 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" >
\nmybatis-config.xml:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <environments default ="test1" > <environment id ="test1" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.cj.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/mybatis?useUnicode=true& characterEncoding=UTF-8" /> <property name ="username" value ="root" /> <property name ="password" value ="admin" /> </dataSource > </environment > </environments > </configuration >
\n到此mybatis开发环境已经搭建好,接下来进行开发
\nMybatis开发方式 \n原生接口方式 \nmapper代理实现自定义接口方式 \n \n\n先介绍第一种:原生接口方式
\n \nmybatis框架需要开发者自定义sql语句,写在Mapper.xml文件
\n实际中会为每个实体类创建对应mapper.xml来定义sql语句,来管理该实体类对象
\n(一)创建Mapper文件
\n因为前面创建的实体类是Account,所以我这里创建AccountMapper.xml文件
\n
\n首先也是要导入约束:
\n1 2 3 4 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
\nAccountMapper.xml:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.ajream.mapper.AccountMapper" > <insert id ="save" parameterType ="com.ajream.entity.Account" > insert into t_acount(id, username, passwd, age) values (#{id},#{username}, #{passwd}, #{age}) </insert > </mapper >
\n说明:
\n\nnamespace通常设置为文件所在包名+文件名(无后缀)
\n \n可以把这个Mapper文件理解成一个java类,类名是 AccountMapper
, 有一个方法 save
,可以传入参数,参数类型是 com.ajream.entity.Account
\n这个方法的作用是执行insert插入数据操作
\n \n \n(二)注册Mapper文件
\n在mybatis-config.xml配置文件中注册mapper文件 , 添加:
\n1 2 3 <mappers > \t<mapper resource ="com/ajream/mapper/AccountMapper.xml" > </mapper > </mappers >
\n
\n编写测试类进行运行 \n根据mybatis配置文件,使用工厂模式生成sqlSession \n执行sql语句 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.ajream.test;import com.ajream.entity.Account;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.InputStream;public class MyTest { public static void main (String[] args) { InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream("mybatis02-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); String statement= "com.ajream.mapper.AccountMapper.save" ; Account account = new Account(2 ,"XiaoZhang" , "56789xz" , 20 ); sqlSession.insert(statement, account); sqlSession.commit(); } }
\n注意,如果用ideaIDE,由于xml配置文件默认是在resources中的,其他文件夹的无法识别,因此最后要在pom中添加如下语句,让idea能够识别其他文件夹的xml文件
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <build > <resources > <resource > <directory > src/main/resources</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > <resource > <directory > src/main/java</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > </resources > </build >
\n如果报错:
\n1 Cause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效。
\n在pom.xml中添加语句:
\n1 2 3 <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > </properties >
\n
\n\n第二种:Mapper代理实现自定义接口方式(推荐)
\n项目地址mybatis-03
\n \n(一)创建接口:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.ajream.dao;import com.ajream.entity.Account;import java.util.List;public interface AccountDao { int save (Account account) ; int update (Account account) ; int deleteById (int id) ; List<Account> findAll () ; Account findById (int id) ; }
\n(二)创建接口对应的Mapper.xml文件(通常与接口文件放在同一个文件夹内),通过insert/update/delete/select
标签来定义接口方法对应的sql语句
\n规则:
\n\nnamespace为接口的全类名
\n \nid为接口中对应的方法名
\n \nparameterType为接口中对应方法的参数类型
\n \nresultType为接口对应方法的返回类型
\n注意:1-如果方法返回的是集合,则resultType是里面元素的类型
\n 2-添加、修改、删除的返回类型默认都是int类型(表示影响的行数),所以不用指定
\n \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.ajream.dao.AccountDao" > <insert id ="save" parameterType ="com.ajream.entity.Account" > insert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age}) </insert > <update id ="update" parameterType ="com.ajream.entity.Account" > update t_acount set username=#{username},age=#{age},passwd=#{password} where id = #{id} </update > <delete id ="deleteById" parameterType ="int" > delete from t_acount where id = #{id} </delete > <select id ="findAll" resultType ="com.ajream.entity.Account" > select * from t_acount; </select > <select id ="findById" resultType ="com.ajream.entity.Account" parameterType ="int" > select * from t_acount where id=#{id} </select > </mapper >
\n(三)注册mapper
\n1 2 3 <mappers > <mapper resource ="com/ajream/dao/AccountDao.xml" /> </mappers >
\n(四)测试
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 package com.ajream.test;import com.ajream.dao.AccountDao;import com.ajream.entity.Account;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.InputStream;import java.util.List;public class MyTest { public static void main (String[] args) { InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); AccountDao accountDao = sqlSession.getMapper(AccountDao.class); Account account1 = accountDao.findById(1 ); account1.setUsername("小东" ); account1.setAge(19 ); account1.setPasswd("56789xd" ); int result = accountDao.update(account1); sqlSession.commit(); System.out.println("修改行数:" + result + "修改后的对象:" ); System.out.println(accountDao.findById(1 )); sqlSession.close(); } }
\n这是进行测试的数据表
\n
\n"},{"title":"mybatis逆向工程(五)","description":"mybatis框架需要:实体类、自定义mapper接口、mapper.xml,传统开发需要手动创建,而mybatis可以用逆向工程MBG来自动帮助我们创建这3个组件,提高开发效率","abbrlink":"81ecdf4a","date":"2021-08-27T02:22:46.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n\n\n\nmybatis框架需要3个组件:实体类、自定义mapper接口、mapper.xml\n\n传统开发需要手动创建,而mybatis可以用逆向工程MBG(MyBatis Generator)来自动帮助我们创建这3个组件,支持基本CRUD操作(复杂的需要自己写), 提高开发效率\n\n\n\n\n\n如何使用?\n\n\n\n\n\n(1) 需要额外添加依赖\n\n```xml\n\n org.mybatis.generator \n mybatis-generator-maven-plugin \n 1.3.2 \n \n```\n\n\n\n\n\n(2)在resources下创建MBG配置文件GeneratorConfig.xml (文件名可自定义)\n\n\n\n需要配置的信息:\n\n1. jdbcConnection:数据库连接信息\n2. JavaModelGenerator:javaBean(实体类 )生成策略\n3. sqlMapGenerator:sql映射文件 生成策略\n4. javaClientGenerator:配置Mapper接口 生成策略\n5. table :配置目标数据表(tableName: 表名,domainObjectName:javaBean[实体类]的类名)\n\n\n\n首先添加约束\n\n```xml\n\n\n```\n\nGeneratorConfig.xml\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n(3)创建Generator执行类\n\n\n\n```java\npackage com.ajream.test;\n\nimport org.mybatis.generator.api.MyBatisGenerator;\nimport org.mybatis.generator.config.Configuration;\nimport org.mybatis.generator.config.xml.ConfigurationParser;\nimport org.mybatis.generator.exception.InvalidConfigurationException;\nimport org.mybatis.generator.exception.XMLParserException;\nimport org.mybatis.generator.internal.DefaultShellCallback;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n public static void main(String[] args) {\n List warings = new ArrayList();\n boolean overwrite = true;\n String genCig = \"/GeneratorConfig.xml\";\n File configFile = new File(Main.class.getResource(genCig).getFile());\n// File configFile = new File(\"GeneratorConfig.xml\");\n ConfigurationParser configurationParser=new ConfigurationParser(warings);\n Configuration configuration = null;\n try{\n configuration = configurationParser.parseConfiguration(configFile);\n } catch (XMLParserException e) {\n e.printStackTrace();\n } catch (IOException e) {\n e.printStackTrace();\n }\n DefaultShellCallback callback = new DefaultShellCallback(overwrite);\n MyBatisGenerator myBatisGenerator = null;\n try{\n myBatisGenerator = new MyBatisGenerator(configuration,callback,warings);\n } catch (InvalidConfigurationException e) {\n e.printStackTrace();\n }\n\n try{\n myBatisGenerator.generate(null);\n } catch (InterruptedException e) {\n e.printStackTrace();\n } catch (IOException e) {\n e.printStackTrace();\n } catch (SQLException throwables) {\n throwables.printStackTrace();\n }\n\n System.out.println(\"ok...\");\n }\n}\n\n```\n\n\n\n运行即可在指定路径下生成相应组件\n","source":"_posts/Mybatis/mybatis逆向工程(五).md","raw":"---\ntitle: mybatis逆向工程(五)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: >-\n mybatis框架需要:实体类、自定义mapper接口、mapper.xml,传统开发需要手动创建,而mybatis可以用逆向工程MBG来自动帮助我们创建这3个组件,提高开发效率\nabbrlink: 81ecdf4a\ndate: 2021-08-27 10:22:46\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n\n\n\nmybatis框架需要3个组件:实体类、自定义mapper接口、mapper.xml\n\n传统开发需要手动创建,而mybatis可以用逆向工程MBG(MyBatis Generator)来自动帮助我们创建这3个组件,支持基本CRUD操作(复杂的需要自己写), 提高开发效率\n\n\n\n\n\n如何使用?\n\n\n\n\n\n(1) 需要额外添加依赖\n\n```xml\n\n org.mybatis.generator \n mybatis-generator-maven-plugin \n 1.3.2 \n \n```\n\n\n\n\n\n(2)在resources下创建MBG配置文件GeneratorConfig.xml (文件名可自定义)\n\n\n\n需要配置的信息:\n\n1. jdbcConnection:数据库连接信息\n2. JavaModelGenerator:javaBean(实体类 )生成策略\n3. sqlMapGenerator:sql映射文件 生成策略\n4. javaClientGenerator:配置Mapper接口 生成策略\n5. table :配置目标数据表(tableName: 表名,domainObjectName:javaBean[实体类]的类名)\n\n\n\n首先添加约束\n\n```xml\n\n\n```\n\nGeneratorConfig.xml\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n(3)创建Generator执行类\n\n\n\n```java\npackage com.ajream.test;\n\nimport org.mybatis.generator.api.MyBatisGenerator;\nimport org.mybatis.generator.config.Configuration;\nimport org.mybatis.generator.config.xml.ConfigurationParser;\nimport org.mybatis.generator.exception.InvalidConfigurationException;\nimport org.mybatis.generator.exception.XMLParserException;\nimport org.mybatis.generator.internal.DefaultShellCallback;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n public static void main(String[] args) {\n List warings = new ArrayList();\n boolean overwrite = true;\n String genCig = \"/GeneratorConfig.xml\";\n File configFile = new File(Main.class.getResource(genCig).getFile());\n// File configFile = new File(\"GeneratorConfig.xml\");\n ConfigurationParser configurationParser=new ConfigurationParser(warings);\n Configuration configuration = null;\n try{\n configuration = configurationParser.parseConfiguration(configFile);\n } catch (XMLParserException e) {\n e.printStackTrace();\n } catch (IOException e) {\n e.printStackTrace();\n }\n DefaultShellCallback callback = new DefaultShellCallback(overwrite);\n MyBatisGenerator myBatisGenerator = null;\n try{\n myBatisGenerator = new MyBatisGenerator(configuration,callback,warings);\n } catch (InvalidConfigurationException e) {\n e.printStackTrace();\n }\n\n try{\n myBatisGenerator.generate(null);\n } catch (InterruptedException e) {\n e.printStackTrace();\n } catch (IOException e) {\n e.printStackTrace();\n } catch (SQLException throwables) {\n throwables.printStackTrace();\n }\n\n System.out.println(\"ok...\");\n }\n}\n\n```\n\n\n\n运行即可在指定路径下生成相应组件\n","slug":"Mybatis/mybatis逆向工程(五)","published":1,"updated":"2021-08-29T14:45:15.143Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pq003pakveexb0ak5a","content":"mybatis框架需要3个组件:实体类、自定义mapper接口、mapper.xml
\n传统开发需要手动创建,而mybatis可以用逆向工程MBG(MyBatis Generator)来自动帮助我们创建这3个组件,支持基本CRUD操作(复杂的需要自己写), 提高开发效率
\n如何使用?
\n(1) 需要额外添加依赖
\n1 2 3 4 5 <dependency > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-maven-plugin</artifactId > <version > 1.3.2</version > </dependency >
\n(2)在resources下创建MBG配置文件GeneratorConfig.xml (文件名可自定义)
\n需要配置的信息:
\n\njdbcConnection:数据库连接信息 \nJavaModelGenerator:javaBean(实体类 )生成策略 \nsqlMapGenerator:sql映射文件 生成策略 \njavaClientGenerator:配置Mapper接口 生成策略 \ntable :配置目标数据表(tableName: 表名,domainObjectName:javaBean[实体类]的类名) \n \n首先添加约束
\n1 2 3 4 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
\nGeneratorConfig.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" > <generatorConfiguration > <context id ="testTables" targetRuntime ="MyBatis3" > <jdbcConnection driverClass ="com.mysql.cj.jdbc.Driver" connectionURL ="jdbc:mysql://localhost:3306/mybatis?useUnicode=true& characterEncoding=UTF-8" userId ="root" password ="admin" /> <javaModelGenerator targetPackage ="com.ajream.entity" targetProject ="./src/main/java" /> <sqlMapGenerator targetPackage ="com.ajream.dao" targetProject ="./src/main/java" /> <javaClientGenerator type ="XMLMAPPER" targetPackage ="com.ajream.entity" targetProject ="./src/main/java" /> <table tableName ="t_acount" domainObjectName ="Account" /> </context > </generatorConfiguration >
\n(3)创建Generator执行类
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 package com.ajream.test;import org.mybatis.generator.api.MyBatisGenerator;import org.mybatis.generator.config.Configuration;import org.mybatis.generator.config.xml.ConfigurationParser;import org.mybatis.generator.exception.InvalidConfigurationException;import org.mybatis.generator.exception.XMLParserException;import org.mybatis.generator.internal.DefaultShellCallback;import java.io.File;import java.io.IOException;import java.sql.SQLException;import java.util.ArrayList;import java.util.List;public class Main { public static void main (String[] args) { List<String> warings = new ArrayList<String>(); boolean overwrite = true ; String genCig = "/GeneratorConfig.xml" ; File configFile = new File(Main.class.getResource(genCig).getFile()); ConfigurationParser configurationParser=new ConfigurationParser(warings); Configuration configuration = null ; try { configuration = configurationParser.parseConfiguration(configFile); } catch (XMLParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = null ; try { myBatisGenerator = new MyBatisGenerator(configuration,callback,warings); } catch (InvalidConfigurationException e) { e.printStackTrace(); } try { myBatisGenerator.generate(null ); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (SQLException throwables) { throwables.printStackTrace(); } System.out.println("ok..." ); } }
\n运行即可在指定路径下生成相应组件
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"mybatis框架需要3个组件:实体类、自定义mapper接口、mapper.xml
\n传统开发需要手动创建,而mybatis可以用逆向工程MBG(MyBatis Generator)来自动帮助我们创建这3个组件,支持基本CRUD操作(复杂的需要自己写), 提高开发效率
\n如何使用?
\n(1) 需要额外添加依赖
\n1 2 3 4 5 <dependency > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-maven-plugin</artifactId > <version > 1.3.2</version > </dependency >
\n(2)在resources下创建MBG配置文件GeneratorConfig.xml (文件名可自定义)
\n需要配置的信息:
\n\njdbcConnection:数据库连接信息 \nJavaModelGenerator:javaBean(实体类 )生成策略 \nsqlMapGenerator:sql映射文件 生成策略 \njavaClientGenerator:配置Mapper接口 生成策略 \ntable :配置目标数据表(tableName: 表名,domainObjectName:javaBean[实体类]的类名) \n \n首先添加约束
\n1 2 3 4 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
\nGeneratorConfig.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" > <generatorConfiguration > <context id ="testTables" targetRuntime ="MyBatis3" > <jdbcConnection driverClass ="com.mysql.cj.jdbc.Driver" connectionURL ="jdbc:mysql://localhost:3306/mybatis?useUnicode=true& characterEncoding=UTF-8" userId ="root" password ="admin" /> <javaModelGenerator targetPackage ="com.ajream.entity" targetProject ="./src/main/java" /> <sqlMapGenerator targetPackage ="com.ajream.dao" targetProject ="./src/main/java" /> <javaClientGenerator type ="XMLMAPPER" targetPackage ="com.ajream.entity" targetProject ="./src/main/java" /> <table tableName ="t_acount" domainObjectName ="Account" /> </context > </generatorConfiguration >
\n(3)创建Generator执行类
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 package com.ajream.test;import org.mybatis.generator.api.MyBatisGenerator;import org.mybatis.generator.config.Configuration;import org.mybatis.generator.config.xml.ConfigurationParser;import org.mybatis.generator.exception.InvalidConfigurationException;import org.mybatis.generator.exception.XMLParserException;import org.mybatis.generator.internal.DefaultShellCallback;import java.io.File;import java.io.IOException;import java.sql.SQLException;import java.util.ArrayList;import java.util.List;public class Main { public static void main (String[] args) { List<String> warings = new ArrayList<String>(); boolean overwrite = true ; String genCig = "/GeneratorConfig.xml" ; File configFile = new File(Main.class.getResource(genCig).getFile()); ConfigurationParser configurationParser=new ConfigurationParser(warings); Configuration configuration = null ; try { configuration = configurationParser.parseConfiguration(configFile); } catch (XMLParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = null ; try { myBatisGenerator = new MyBatisGenerator(configuration,callback,warings); } catch (InvalidConfigurationException e) { e.printStackTrace(); } try { myBatisGenerator.generate(null ); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (SQLException throwables) { throwables.printStackTrace(); } System.out.println("ok..." ); } }
\n运行即可在指定路径下生成相应组件
\n"},{"title":"esp8266(1)-接入点模式-无线终端模式","abbrlink":"4dc0a7a8","date":"2021-08-02T00:00:00.000Z","description":"esp8266如何开启热点(接入点模式)、如何连接WiFi(无线终端模式)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n\n接入点模式:开发板作为路由器(网关),可以让其他设备连接\n\n无线终端模式:开发板作为终端,可以连接路由器WiFi\n\n\n\n> 说明:此处使用ArduinoIDE来进行开发。\n>\n> 注意:上传代码前必须把“串口监视器” 关闭,否则可能上传出错,因为端口被占用了\n\n\n\n![ESP8266-NodeMCU引脚功能](http://www.taichi-maker.com/wp-content/uploads/2019/02/esp8266_devkit_horizontal-001.png)\n\n\n\n## 1、配置接入点模式\n\n即开启 “热点”\n\n1. 导入 `ESP8266WiFi.h`库\n\n2. 配置WiFi名称(ssid)、密码(passwd)—— `softAP()`\n\n ```c\n const char ssid[] = \"MyWiFi\";\n const char passwd[] = \"12345678\";\n WiFi.softAP(ssid, passwd);\n ```\n\n3. 获取开发板IP\n\n ```c\n WiFi.softAPIP();\n ```\n\n\n\n完整code\n\n```c\n#include \n\nchar ssid[] = \"MyWiFi\";\nchar passwd[] = \"1234567890\";\n\nvoid setup(){\n Serial.begin(9600);\n \n Serial.print(\"wifi ssid: \");\n Serial.println(ssid);\n Serial.print(\"wifi password: \");\n Serial.println(passwd);\n \n WiFi.softAP(ssid, passwd);\t//开启接入点模式\n\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.softAPIP());\t//打印开发板的IP\n}\n\n\nvoid loop(){\n \n}\n```\n\n\n\n\n\n## 2、配置无线终端模式\n\n### 只有1个WiFi\n\n将开发板连接WiFi\n\n```c\n//ssid、passwd均为字符串\nWiFi.mode(WIFI_STA);\nWiFi.begin(ssid, passwd);\n```\n\n\n\n多次尝试连接,直到连接上:\n\n```c\nwhile(WiFi.status() != WL_CONNECTED\t){\n delay(1000);\n //....\n}\n```\n\n\n\n连上WiFi后,获取wifi的ssid、开发板IP地址:\n\n```c\nWiFi.SSID();\t\t//返回字符串\nWiFi.localIP(); //返回字符串\n```\n\n\n\n完整code:\n\n```c\n#include // 本程序使用ESP8266WiFi库\n\nString ssid = \"aaa\"; \nString password = \"12345678\"; \n \n \nvoid setup() {\n Serial.begin(9600); \n WiFi.begin(ssid, password); \n \n int i = 0; \n while (WiFi.status() != WL_CONNECTED) { \n delay(1000); \n Serial.print(i++); Serial.print(' '); \n } \n \n Serial.println(\"\"); \n Serial.print(\"Connecting to \"); \n Serial.println(ssid); \n \n Serial.println(\"IP address: \" ); \n Serial.println(WiFi.localIP()); \n}\n\nvoid loop() { \n}\n```\n\n\n\n### 多个WiFi,自动连接最强信号的\n\n1. 先添加几个待选WiFi\n\n ```c\n //导入库\n #include \n \n // 创建ESP8266WiFiMulti对象\n ESP8266WiFiMulti wifiMulti; \n \n //通过addAP()函数存储WiFi的ssid和密码\n wifiMulti.addAP(\"aaaa\", \"11111111\");\n wifiMulti.addAP(\"bbbb\", \"22222222\");\n wifiMulti.addAP(\"cccc\", \"33333333\");\n \n ```\n\n \n\n2. 多次尝试连接:`wifiMulti.run()`\n\n ```c\n // 将会连接信号最强的那一个WiFi信号。\n while (wifiMulti.run() != WL_CONNECTED) { \n delay(1000); \n Serial.print('...'); \n } \n ```\n\n \n\n\n\n完整code:\n\n```c\n#include \n#include \n\nESP8266WiFiMulti wifiMulti; //创建对象\n\nvoid setup(){\n Serial.begin(9600);\n wifiMulti.addAP(\"aaa\", \"12345678\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(1000);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n \n}\n\nvoid loop(){\n\n}\n```\n\n","source":"_posts/esp8266/1-接入点模式-无线终端模式.md","raw":"---\ntitle: esp8266(1)-接入点模式-无线终端模式\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: 4dc0a7a8\ndate: 2021-08-02 08:00:00\ndescription: esp8266如何开启热点(接入点模式)、如何连接WiFi(无线终端模式)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n\n接入点模式:开发板作为路由器(网关),可以让其他设备连接\n\n无线终端模式:开发板作为终端,可以连接路由器WiFi\n\n\n\n> 说明:此处使用ArduinoIDE来进行开发。\n>\n> 注意:上传代码前必须把“串口监视器” 关闭,否则可能上传出错,因为端口被占用了\n\n\n\n![ESP8266-NodeMCU引脚功能](http://www.taichi-maker.com/wp-content/uploads/2019/02/esp8266_devkit_horizontal-001.png)\n\n\n\n## 1、配置接入点模式\n\n即开启 “热点”\n\n1. 导入 `ESP8266WiFi.h`库\n\n2. 配置WiFi名称(ssid)、密码(passwd)—— `softAP()`\n\n ```c\n const char ssid[] = \"MyWiFi\";\n const char passwd[] = \"12345678\";\n WiFi.softAP(ssid, passwd);\n ```\n\n3. 获取开发板IP\n\n ```c\n WiFi.softAPIP();\n ```\n\n\n\n完整code\n\n```c\n#include \n\nchar ssid[] = \"MyWiFi\";\nchar passwd[] = \"1234567890\";\n\nvoid setup(){\n Serial.begin(9600);\n \n Serial.print(\"wifi ssid: \");\n Serial.println(ssid);\n Serial.print(\"wifi password: \");\n Serial.println(passwd);\n \n WiFi.softAP(ssid, passwd);\t//开启接入点模式\n\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.softAPIP());\t//打印开发板的IP\n}\n\n\nvoid loop(){\n \n}\n```\n\n\n\n\n\n## 2、配置无线终端模式\n\n### 只有1个WiFi\n\n将开发板连接WiFi\n\n```c\n//ssid、passwd均为字符串\nWiFi.mode(WIFI_STA);\nWiFi.begin(ssid, passwd);\n```\n\n\n\n多次尝试连接,直到连接上:\n\n```c\nwhile(WiFi.status() != WL_CONNECTED\t){\n delay(1000);\n //....\n}\n```\n\n\n\n连上WiFi后,获取wifi的ssid、开发板IP地址:\n\n```c\nWiFi.SSID();\t\t//返回字符串\nWiFi.localIP(); //返回字符串\n```\n\n\n\n完整code:\n\n```c\n#include // 本程序使用ESP8266WiFi库\n\nString ssid = \"aaa\"; \nString password = \"12345678\"; \n \n \nvoid setup() {\n Serial.begin(9600); \n WiFi.begin(ssid, password); \n \n int i = 0; \n while (WiFi.status() != WL_CONNECTED) { \n delay(1000); \n Serial.print(i++); Serial.print(' '); \n } \n \n Serial.println(\"\"); \n Serial.print(\"Connecting to \"); \n Serial.println(ssid); \n \n Serial.println(\"IP address: \" ); \n Serial.println(WiFi.localIP()); \n}\n\nvoid loop() { \n}\n```\n\n\n\n### 多个WiFi,自动连接最强信号的\n\n1. 先添加几个待选WiFi\n\n ```c\n //导入库\n #include \n \n // 创建ESP8266WiFiMulti对象\n ESP8266WiFiMulti wifiMulti; \n \n //通过addAP()函数存储WiFi的ssid和密码\n wifiMulti.addAP(\"aaaa\", \"11111111\");\n wifiMulti.addAP(\"bbbb\", \"22222222\");\n wifiMulti.addAP(\"cccc\", \"33333333\");\n \n ```\n\n \n\n2. 多次尝试连接:`wifiMulti.run()`\n\n ```c\n // 将会连接信号最强的那一个WiFi信号。\n while (wifiMulti.run() != WL_CONNECTED) { \n delay(1000); \n Serial.print('...'); \n } \n ```\n\n \n\n\n\n完整code:\n\n```c\n#include \n#include \n\nESP8266WiFiMulti wifiMulti; //创建对象\n\nvoid setup(){\n Serial.begin(9600);\n wifiMulti.addAP(\"aaa\", \"12345678\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(1000);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n \n}\n\nvoid loop(){\n\n}\n```\n\n","slug":"esp8266/1-接入点模式-无线终端模式","published":1,"updated":"2021-09-10T03:19:46.998Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pr003sakve2exe8imp","content":"接入点模式:开发板作为路由器(网关),可以让其他设备连接
\n无线终端模式:开发板作为终端,可以连接路由器WiFi
\n\n说明:此处使用ArduinoIDE来进行开发。
\n注意:上传代码前必须把“串口监视器” 关闭,否则可能上传出错,因为端口被占用了
\n \n
\n1、配置接入点模式 即开启 “热点”
\n\n导入 ESP8266WiFi.h
库
\n \n配置WiFi名称(ssid)、密码(passwd)—— softAP()
\n1 2 3 const char ssid[] = "MyWiFi" ;const char passwd[] = "12345678" ;WiFi.softAP(ssid, passwd);
\n \n获取开发板IP
\n \n \n \n完整code
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <ESP8266WiFi.h> char ssid[] = "MyWiFi" ;char passwd[] = "1234567890" ;void setup () { Serial.begin(9600 ); Serial.print("wifi ssid: " ); Serial.println(ssid); Serial.print("wifi password: " ); Serial.println(passwd); WiFi.softAP(ssid, passwd);\t Serial.print("IP Address: " ); Serial.println(WiFi.softAPIP());\t } void loop () { }
\n2、配置无线终端模式 只有1个WiFi 将开发板连接WiFi
\n1 2 3 WiFi.mode(WIFI_STA); WiFi.begin(ssid, passwd);
\n多次尝试连接,直到连接上:
\n1 2 3 4 while (WiFi.status() != WL_CONNECTED\t){ delay(1000 ); }
\n连上WiFi后,获取wifi的ssid、开发板IP地址:
\n1 2 WiFi.SSID();\t\t WiFi.localIP();
\n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <ESP8266WiFi.h> String ssid = "aaa" ; String password = "12345678" ; void setup () { Serial.begin(9600 ); WiFi.begin(ssid, password); int i = 0 ; while (WiFi.status() != WL_CONNECTED) { delay(1000 ); Serial.print(i++); Serial.print(' ' ); } Serial.println("" ); Serial.print("Connecting to " ); Serial.println(ssid); Serial.println("IP address: " ); Serial.println(WiFi.localIP()); } void loop () { }
\n多个WiFi,自动连接最强信号的 \n先添加几个待选WiFi
\n1 2 3 4 5 6 7 8 9 10 11 #include <ESP8266WiFiMulti.h> ESP8266WiFiMulti wifiMulti; wifiMulti.addAP("aaaa" , "11111111" ); wifiMulti.addAP("bbbb" , "22222222" ); wifiMulti.addAP("cccc" , "33333333" );
\n \n \n\n多次尝试连接:wifiMulti.run()
\n1 2 3 4 5 while (wifiMulti.run() != WL_CONNECTED) { delay(1000 ); Serial.print('...' ); }
\n \n \n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> ESP8266WiFiMulti wifiMulti; void setup () { Serial.begin(9600 ); wifiMulti.addAP("aaa" , "12345678" ); wifiMulti.addAP("bbb" , "12345678" ); wifiMulti.addAP("ccc" , "12345678" ); int i = 0 ; while (wifiMulti.run() != WL_CONNECTED){ delay(1000 ); Serial.print(++i); Serial.print(" " ); } Serial.println("" ); Serial.print("IP Address: " ); Serial.println(WiFi.localIP()); } void loop () {}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"接入点模式:开发板作为路由器(网关),可以让其他设备连接
\n无线终端模式:开发板作为终端,可以连接路由器WiFi
\n\n说明:此处使用ArduinoIDE来进行开发。
\n注意:上传代码前必须把“串口监视器” 关闭,否则可能上传出错,因为端口被占用了
\n \n
\n1、配置接入点模式 即开启 “热点”
\n\n导入 ESP8266WiFi.h
库
\n \n配置WiFi名称(ssid)、密码(passwd)—— softAP()
\n1 2 3 const char ssid[] = "MyWiFi" ;const char passwd[] = "12345678" ;WiFi.softAP(ssid, passwd);
\n \n获取开发板IP
\n \n \n \n完整code
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <ESP8266WiFi.h> char ssid[] = "MyWiFi" ;char passwd[] = "1234567890" ;void setup () { Serial.begin(9600 ); Serial.print("wifi ssid: " ); Serial.println(ssid); Serial.print("wifi password: " ); Serial.println(passwd); WiFi.softAP(ssid, passwd);\t Serial.print("IP Address: " ); Serial.println(WiFi.softAPIP());\t } void loop () { }
\n2、配置无线终端模式 只有1个WiFi 将开发板连接WiFi
\n1 2 3 WiFi.mode(WIFI_STA); WiFi.begin(ssid, passwd);
\n多次尝试连接,直到连接上:
\n1 2 3 4 while (WiFi.status() != WL_CONNECTED\t){ delay(1000 ); }
\n连上WiFi后,获取wifi的ssid、开发板IP地址:
\n1 2 WiFi.SSID();\t\t WiFi.localIP();
\n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <ESP8266WiFi.h> String ssid = "aaa" ; String password = "12345678" ; void setup () { Serial.begin(9600 ); WiFi.begin(ssid, password); int i = 0 ; while (WiFi.status() != WL_CONNECTED) { delay(1000 ); Serial.print(i++); Serial.print(' ' ); } Serial.println("" ); Serial.print("Connecting to " ); Serial.println(ssid); Serial.println("IP address: " ); Serial.println(WiFi.localIP()); } void loop () { }
\n多个WiFi,自动连接最强信号的 \n先添加几个待选WiFi
\n1 2 3 4 5 6 7 8 9 10 11 #include <ESP8266WiFiMulti.h> ESP8266WiFiMulti wifiMulti; wifiMulti.addAP("aaaa" , "11111111" ); wifiMulti.addAP("bbbb" , "22222222" ); wifiMulti.addAP("cccc" , "33333333" );
\n \n \n\n多次尝试连接:wifiMulti.run()
\n1 2 3 4 5 while (wifiMulti.run() != WL_CONNECTED) { delay(1000 ); Serial.print('...' ); }
\n \n \n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> ESP8266WiFiMulti wifiMulti; void setup () { Serial.begin(9600 ); wifiMulti.addAP("aaa" , "12345678" ); wifiMulti.addAP("bbb" , "12345678" ); wifiMulti.addAP("ccc" , "12345678" ); int i = 0 ; while (wifiMulti.run() != WL_CONNECTED){ delay(1000 ); Serial.print(++i); Serial.print(" " ); } Serial.println("" ); Serial.print("IP Address: " ); Serial.println(WiFi.localIP()); } void loop () {}
\n"},{"title":"esp8266(0)-Arduino配置esp8266开发环境","description":"如何用Arduino配置esp8266开发环境","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","abbrlink":"89cf4bca","date":"2021-08-01T00:10:21.000Z","_content":"\n\n\n\n\n### 一般方法(要求网速较好)\n\n\n\n1. 打开ArduinoIDE,在 **文件->首选项** 的附加开发板管理器网址 添加下面地址:\n\n ```http\n http://arduino.esp8266.com/stable/package_esp8266com_index.json\n ```\n\n ![image-20210807195949825](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-16-02_image-20210807195949825.png)\n\n2. 在工具->开发板->开发板管理器,搜索 esp8266,然后选择安装即可\n\n ![image-20210807201352339](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-13-53_image-20210807201352339.png)\n\n\n\n\n\n### 特殊方法(适用于网络不好)\n\n一般方法中如果因为网速过慢,可能在下载过程中会比较慢甚至出错,因此可以自己去GitHub下载对应文件再放到指定文件夹下即可\n\n**过程较为麻烦,不想看的直接看最后:**\n\n1. 去[earlephilhower](https://github.com/earlephilhower/esp-quick-toolchain/releases/)下载下面这4个zip文件:\n\n ```\n x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip\n x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip\n python3-3.7.2.post1-embed-win32v2a.zip\n x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip\n ```\n\n2. 去[esp8266-Arduino](https://github.com/esp8266/Arduino/releases/tag/3.0.2)下载esp8266-3.0.2.zip\n\n ![image-20210807202721141](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-27-22_image-20210807202721141.png)\n\n3. 克隆这个仓库,或者下载zip包\n\n ```git\n https://github.com/esp8266/Arduino.git\n ```\n\n 解压后改名为esp8266,放到目录(没有的文件夹自己新建):\n\n ```\n C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\\n ```\n\n4. 用第2步下载的zip包中的 `libraries` 文件夹来代替第3步下载的文件夹中 的`libraries`\n\n5. 将第一步下载的4个zip包分别重命名:\n\n ```\n x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip --> xtensa-lx106-elf.zip\n x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip --> mkspiffs.zip\n python3-3.7.2.post1-embed-win32v2a.zip --> python3.zip\n x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip --> mklittlefs.zip\n ```\n\n 然后解压到在第三步的 `tool`文件夹中,下面是解压后:\n\n ![image-20210807203943403](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-39-44_image-20210807203943403.png)\n\n\n\n\n\n> 我已经把这5步全部做完,放到压缩包 `esp8266.zip` 中,需要的可以去百度网盘下载【链接: https://pan.baidu.com/s/1UqUJXgvzQecXZjqRe_JLaA 提取码: 6xpa】,或者csdn下载【https://download.csdn.net/download/m0_46079750/20887320】,并解压到 下面的目录即可:\n>\n> ```\n> C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\\n> ```\n\n\n\n","source":"_posts/esp8266/0-Arduino配置esp8266开发环境.md","raw":"---\ntitle: esp8266(0)-Arduino配置esp8266开发环境\ntags:\n - esp8266\ncategories:\n - 硬件学习\n - esp8266\ndescription: 如何用Arduino配置esp8266开发环境\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png'\nabbrlink: 89cf4bca\ndate: 2021-08-01 08:10:21\n---\n\n\n\n\n\n### 一般方法(要求网速较好)\n\n\n\n1. 打开ArduinoIDE,在 **文件->首选项** 的附加开发板管理器网址 添加下面地址:\n\n ```http\n http://arduino.esp8266.com/stable/package_esp8266com_index.json\n ```\n\n ![image-20210807195949825](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-16-02_image-20210807195949825.png)\n\n2. 在工具->开发板->开发板管理器,搜索 esp8266,然后选择安装即可\n\n ![image-20210807201352339](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-13-53_image-20210807201352339.png)\n\n\n\n\n\n### 特殊方法(适用于网络不好)\n\n一般方法中如果因为网速过慢,可能在下载过程中会比较慢甚至出错,因此可以自己去GitHub下载对应文件再放到指定文件夹下即可\n\n**过程较为麻烦,不想看的直接看最后:**\n\n1. 去[earlephilhower](https://github.com/earlephilhower/esp-quick-toolchain/releases/)下载下面这4个zip文件:\n\n ```\n x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip\n x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip\n python3-3.7.2.post1-embed-win32v2a.zip\n x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip\n ```\n\n2. 去[esp8266-Arduino](https://github.com/esp8266/Arduino/releases/tag/3.0.2)下载esp8266-3.0.2.zip\n\n ![image-20210807202721141](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-27-22_image-20210807202721141.png)\n\n3. 克隆这个仓库,或者下载zip包\n\n ```git\n https://github.com/esp8266/Arduino.git\n ```\n\n 解压后改名为esp8266,放到目录(没有的文件夹自己新建):\n\n ```\n C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\\n ```\n\n4. 用第2步下载的zip包中的 `libraries` 文件夹来代替第3步下载的文件夹中 的`libraries`\n\n5. 将第一步下载的4个zip包分别重命名:\n\n ```\n x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip --> xtensa-lx106-elf.zip\n x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip --> mkspiffs.zip\n python3-3.7.2.post1-embed-win32v2a.zip --> python3.zip\n x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip --> mklittlefs.zip\n ```\n\n 然后解压到在第三步的 `tool`文件夹中,下面是解压后:\n\n ![image-20210807203943403](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-39-44_image-20210807203943403.png)\n\n\n\n\n\n> 我已经把这5步全部做完,放到压缩包 `esp8266.zip` 中,需要的可以去百度网盘下载【链接: https://pan.baidu.com/s/1UqUJXgvzQecXZjqRe_JLaA 提取码: 6xpa】,或者csdn下载【https://download.csdn.net/download/m0_46079750/20887320】,并解压到 下面的目录即可:\n>\n> ```\n> C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\\n> ```\n\n\n\n","slug":"esp8266/0-Arduino配置esp8266开发环境","published":1,"updated":"2021-09-11T07:55:32.130Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ps003uakve66vo01gc","content":"\n一般方法(要求网速较好) \n打开ArduinoIDE,在 文件->首选项 的附加开发板管理器网址 添加下面地址:
\n1 http://arduino.esp8266.com/stable/package_esp8266com_index.json
\n
\n \n在工具->开发板->开发板管理器,搜索 esp8266,然后选择安装即可
\n
\n \n \n特殊方法(适用于网络不好) 一般方法中如果因为网速过慢,可能在下载过程中会比较慢甚至出错,因此可以自己去GitHub下载对应文件再放到指定文件夹下即可
\n过程较为麻烦,不想看的直接看最后:
\n\n去earlephilhower 下载下面这4个zip文件:
\n1 2 3 4 x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip python3-3.7.2.post1-embed-win32v2a.zip x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip
\n \n去esp8266-Arduino 下载esp8266-3.0.2.zip
\n
\n \n克隆这个仓库,或者下载zip包
\n1 https://github.com/esp8266/Arduino.git
\n解压后改名为esp8266,放到目录(没有的文件夹自己新建):
\n1 C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\
\n \n用第2步下载的zip包中的 libraries
文件夹来代替第3步下载的文件夹中 的libraries
\n \n将第一步下载的4个zip包分别重命名:
\n1 2 3 4 x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip --> xtensa-lx106-elf.zip x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip --> mkspiffs.zip python3-3.7.2.post1-embed-win32v2a.zip --> python3.zip x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip --> mklittlefs.zip
\n然后解压到在第三步的 tool
文件夹中,下面是解压后:
\n
\n \n \n\n我已经把这5步全部做完,放到压缩包 esp8266.zip
中,需要的可以去百度网盘下载【链接: https://pan.baidu.com/s/1UqUJXgvzQecXZjqRe_JLaA 提取码: 6xpa】,或者csdn下载【https://download.csdn.net/download/m0_46079750/20887320】,并解压到 下面的目录即可:
\n1 C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\
\n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"\n一般方法(要求网速较好) \n打开ArduinoIDE,在 文件->首选项 的附加开发板管理器网址 添加下面地址:
\n1 http://arduino.esp8266.com/stable/package_esp8266com_index.json
\n
\n \n在工具->开发板->开发板管理器,搜索 esp8266,然后选择安装即可
\n
\n \n \n特殊方法(适用于网络不好) 一般方法中如果因为网速过慢,可能在下载过程中会比较慢甚至出错,因此可以自己去GitHub下载对应文件再放到指定文件夹下即可
\n过程较为麻烦,不想看的直接看最后:
\n\n去earlephilhower 下载下面这4个zip文件:
\n1 2 3 4 x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip python3-3.7.2.post1-embed-win32v2a.zip x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip
\n \n去esp8266-Arduino 下载esp8266-3.0.2.zip
\n
\n \n克隆这个仓库,或者下载zip包
\n1 https://github.com/esp8266/Arduino.git
\n解压后改名为esp8266,放到目录(没有的文件夹自己新建):
\n1 C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\
\n \n用第2步下载的zip包中的 libraries
文件夹来代替第3步下载的文件夹中 的libraries
\n \n将第一步下载的4个zip包分别重命名:
\n1 2 3 4 x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip --> xtensa-lx106-elf.zip x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip --> mkspiffs.zip python3-3.7.2.post1-embed-win32v2a.zip --> python3.zip x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip --> mklittlefs.zip
\n然后解压到在第三步的 tool
文件夹中,下面是解压后:
\n
\n \n \n\n我已经把这5步全部做完,放到压缩包 esp8266.zip
中,需要的可以去百度网盘下载【链接: https://pan.baidu.com/s/1UqUJXgvzQecXZjqRe_JLaA 提取码: 6xpa】,或者csdn下载【https://download.csdn.net/download/m0_46079750/20887320】,并解压到 下面的目录即可:
\n1 C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\
\n \n"},{"title":"esp8266(2)-网络服务器","abbrlink":"27153554","date":"2021-08-02T00:20:00.000Z","description":"esp8266作为服务器使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n## 1、建立基本网络服务器\n\n\n\n1. 导入库文件 `ESP8266WebServer.h`\n\n2. 创建服务器对象\n\n ```c\n ESP8266WebServer server(80); //80是端口号\n ```\n\n3. 启动服务器\n\n ```c\n server.begin();\n ```\n\n4. 服务器访问配置\n\n ```c\n // 访问根节点时,调用处理函数 handleRoot\n server.on(\"/\", handleRoot);\n \n //访问不到,即404时,调用处理函数 handleNotFound\n server.onNotFound(handleNotFound);\n ```\n\n5. 访问处理函数\n\n ```c\n void handleRoot(){\n server.send(200, \"text/plain\", \"Hello from ESP8266\");\n }\n ```\n\n ```c\n void handleNotFound(){\n server.send(404, \"text/plain\", \"404: Not found\");\n }\n ```\n\n \n\n6. 循环监听端口\n\n ```c\n void loop(){\n server.handleClient(); //循环监听客户端访问情况\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nvoid setup(){\n Serial.begin(9600);\n server.begin();\n server.on(\"/\", handleRoot);\n server.onNotFound(handleNotFound);\n\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(600);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n \n}\n\n//监听客户端\nvoid loop(){\n server.handleClient();\n}\n\n//访问处理函数\nvoid handleRoot(){\n server.send(200, \"text/plain\", \"Hello esp8266\");\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n```\n\n\n\n## 2、通过网络服务器实现开发板控制\n\n通过网页控制nodeMCU开发板小灯亮灭\n\n步骤1、2、3、6不变,只是第3、4步的服务器访问配置和访问处理函数有些许改动\n\n3. 服务器访问配置\n\n ```c\n server.on(\"/\", HTTP_GET, handleRoot);\n \n /*添加这个控制LED的网页, 使用post方式发送http请求*/\n server.on(\"/LED\", HTTP_POST, handleLED); \n \n server.onNotFound(handleNotFound);\n ```\n\n4. 访问处理函数\n\n ```c\n \n void handleRoot(){\n server.send(200, \"text/html\", \"\");\n }\n ```\n\n > 注意这句html代码:\n >\n > ```html\n > \n > ```\n >\n > 这会生成一个按钮,点击这个按钮会把数据发到\"/LED\"页面\n\n \n\n 下面是“/LED”页面的处理函数:\n\n ```c\n void handleLED(){\n static bool LEDState = LOW;//记录LED此时亮灭状态\n LEDState = !LEDState;\n digitalWrite(LED_BUILTIN, LEDState);\n server.sendHeader(\"Location\", \"/\");\n server.send(303); //303表示将网页重定向\n }\n ```\n\n\n\n完整code\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nvoid setup(){\n Serial.begin(9600);\n\n pinMode(LED_BUILTIN, OUTPUT); //设置内置LED引脚为输出模式\n \n /*服务器配置*/\n server.begin();\n server.on(\"/\", HTTP_GET, handleRoot);\n server.on(\"/LED\", HTTP_POST, handleLED);\n server.onNotFound(handleNotFound);\n\n /*Wifi配置*/\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(600);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n}\n\nvoid loop(){\n server.handleClient();\n}\n\nvoid handleRoot(){\n server.send(200, \"text/html\", \"\");\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n\nvoid handleLED(){\n static bool LEDState = LOW; //记录LED此时亮灭状态\n LEDState = !LEDState;\n digitalWrite(LED_BUILTIN, LEDState);\n server.sendHeader(\"Location\", \"/\");\n server.send(303); //303表示将网页重定向\n}\n```\n\n\n\n## 3、将开发板引脚状态发送到终端网页\n\n将引脚**D3**(已经与flash按键相连,按键按下为低电平)的电平状态显示到网页中\n\n1. 首先在开始时将D3引脚设置为上拉输入模式\n\n ```c\n pinMode(D3, INPUT_PULLUP); \n ```\n\n2. 循环读取引脚状态 `digitalRead()`\n\n ```c\n bool pinState;\n void loop(){\n pinState = digitalRead(D3); // 获取引脚状态\n //......\n }\n ```\n\n \n\n3. 要在根节点下显示引脚状态,所以其访问处理函数为:\n\n ```c\n \n void handleRoot() { \n String displayPinState; // 存储按键状态的字符串变量\n \n if(pinState == HIGH){ // 当按键引脚D3为高电平\n displayPinState = \"Button State: HIGH\"; // 字符串赋值高电平信息\n } else { // 当按键引脚D3为低电平\n displayPinState = \"Button State: LOW\"; // 字符串赋值低电平信息\n }\n esp8266_server.send(200, \"text/plain\", displayPinState); \n // 向浏览器发送按键状态信息 \n }\n ```\n\n \n\n\n\n完整code\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nbool pinState;\n\nvoid setup(){\n Serial.begin(9600);\n\n pinMode(D3, INPUT_PULLUP); //上拉输入模式\n \n /*服务器配置*/\n server.begin();\n server.on(\"/\", HTTP_GET, handleRoot);\n server.onNotFound(handleNotFound);\n\n /*Wifi配置*/\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(800);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n}\n\nvoid loop(){\n server.handleClient();\n pinState = digitalRead(D3);\n}\n\nvoid handleRoot(){\n String displayPinState;\n if(pinState == HIGH){ // 当按键引脚D3为高电平\n displayPinState = \"Button State: HIGH\"; // 字符串赋值高电平信息\n } else { // 当按键引脚D3为低电平\n displayPinState = \"Button State: LOW\"; // 字符串赋值低电平信息\n }\n server.send(200, \"text/plain\", displayPinState);\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n```\n\n> 注意:引脚状态改变后需要手动刷新网页,网页显示才会改变\n\n\n\n\n\n","source":"_posts/esp8266/2-网络服务器.md","raw":"---\ntitle: esp8266(2)-网络服务器\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: '27153554'\ndate: 2021-08-02 08:20:00\ndescription: esp8266作为服务器使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n## 1、建立基本网络服务器\n\n\n\n1. 导入库文件 `ESP8266WebServer.h`\n\n2. 创建服务器对象\n\n ```c\n ESP8266WebServer server(80); //80是端口号\n ```\n\n3. 启动服务器\n\n ```c\n server.begin();\n ```\n\n4. 服务器访问配置\n\n ```c\n // 访问根节点时,调用处理函数 handleRoot\n server.on(\"/\", handleRoot);\n \n //访问不到,即404时,调用处理函数 handleNotFound\n server.onNotFound(handleNotFound);\n ```\n\n5. 访问处理函数\n\n ```c\n void handleRoot(){\n server.send(200, \"text/plain\", \"Hello from ESP8266\");\n }\n ```\n\n ```c\n void handleNotFound(){\n server.send(404, \"text/plain\", \"404: Not found\");\n }\n ```\n\n \n\n6. 循环监听端口\n\n ```c\n void loop(){\n server.handleClient(); //循环监听客户端访问情况\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nvoid setup(){\n Serial.begin(9600);\n server.begin();\n server.on(\"/\", handleRoot);\n server.onNotFound(handleNotFound);\n\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(600);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n \n}\n\n//监听客户端\nvoid loop(){\n server.handleClient();\n}\n\n//访问处理函数\nvoid handleRoot(){\n server.send(200, \"text/plain\", \"Hello esp8266\");\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n```\n\n\n\n## 2、通过网络服务器实现开发板控制\n\n通过网页控制nodeMCU开发板小灯亮灭\n\n步骤1、2、3、6不变,只是第3、4步的服务器访问配置和访问处理函数有些许改动\n\n3. 服务器访问配置\n\n ```c\n server.on(\"/\", HTTP_GET, handleRoot);\n \n /*添加这个控制LED的网页, 使用post方式发送http请求*/\n server.on(\"/LED\", HTTP_POST, handleLED); \n \n server.onNotFound(handleNotFound);\n ```\n\n4. 访问处理函数\n\n ```c\n \n void handleRoot(){\n server.send(200, \"text/html\", \"\");\n }\n ```\n\n > 注意这句html代码:\n >\n > ```html\n > \n > ```\n >\n > 这会生成一个按钮,点击这个按钮会把数据发到\"/LED\"页面\n\n \n\n 下面是“/LED”页面的处理函数:\n\n ```c\n void handleLED(){\n static bool LEDState = LOW;//记录LED此时亮灭状态\n LEDState = !LEDState;\n digitalWrite(LED_BUILTIN, LEDState);\n server.sendHeader(\"Location\", \"/\");\n server.send(303); //303表示将网页重定向\n }\n ```\n\n\n\n完整code\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nvoid setup(){\n Serial.begin(9600);\n\n pinMode(LED_BUILTIN, OUTPUT); //设置内置LED引脚为输出模式\n \n /*服务器配置*/\n server.begin();\n server.on(\"/\", HTTP_GET, handleRoot);\n server.on(\"/LED\", HTTP_POST, handleLED);\n server.onNotFound(handleNotFound);\n\n /*Wifi配置*/\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(600);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n}\n\nvoid loop(){\n server.handleClient();\n}\n\nvoid handleRoot(){\n server.send(200, \"text/html\", \"\");\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n\nvoid handleLED(){\n static bool LEDState = LOW; //记录LED此时亮灭状态\n LEDState = !LEDState;\n digitalWrite(LED_BUILTIN, LEDState);\n server.sendHeader(\"Location\", \"/\");\n server.send(303); //303表示将网页重定向\n}\n```\n\n\n\n## 3、将开发板引脚状态发送到终端网页\n\n将引脚**D3**(已经与flash按键相连,按键按下为低电平)的电平状态显示到网页中\n\n1. 首先在开始时将D3引脚设置为上拉输入模式\n\n ```c\n pinMode(D3, INPUT_PULLUP); \n ```\n\n2. 循环读取引脚状态 `digitalRead()`\n\n ```c\n bool pinState;\n void loop(){\n pinState = digitalRead(D3); // 获取引脚状态\n //......\n }\n ```\n\n \n\n3. 要在根节点下显示引脚状态,所以其访问处理函数为:\n\n ```c\n \n void handleRoot() { \n String displayPinState; // 存储按键状态的字符串变量\n \n if(pinState == HIGH){ // 当按键引脚D3为高电平\n displayPinState = \"Button State: HIGH\"; // 字符串赋值高电平信息\n } else { // 当按键引脚D3为低电平\n displayPinState = \"Button State: LOW\"; // 字符串赋值低电平信息\n }\n esp8266_server.send(200, \"text/plain\", displayPinState); \n // 向浏览器发送按键状态信息 \n }\n ```\n\n \n\n\n\n完整code\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nbool pinState;\n\nvoid setup(){\n Serial.begin(9600);\n\n pinMode(D3, INPUT_PULLUP); //上拉输入模式\n \n /*服务器配置*/\n server.begin();\n server.on(\"/\", HTTP_GET, handleRoot);\n server.onNotFound(handleNotFound);\n\n /*Wifi配置*/\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(800);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n}\n\nvoid loop(){\n server.handleClient();\n pinState = digitalRead(D3);\n}\n\nvoid handleRoot(){\n String displayPinState;\n if(pinState == HIGH){ // 当按键引脚D3为高电平\n displayPinState = \"Button State: HIGH\"; // 字符串赋值高电平信息\n } else { // 当按键引脚D3为低电平\n displayPinState = \"Button State: LOW\"; // 字符串赋值低电平信息\n }\n server.send(200, \"text/plain\", displayPinState);\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n```\n\n> 注意:引脚状态改变后需要手动刷新网页,网页显示才会改变\n\n\n\n\n\n","slug":"esp8266/2-网络服务器","published":1,"updated":"2021-09-10T03:19:57.136Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pt003xakve53qs5vxp","content":"1、建立基本网络服务器 \n导入库文件 ESP8266WebServer.h
\n \n创建服务器对象
\n1 ESP8266WebServer server (80 ) ;
\n \n启动服务器
\n \n \n服务器访问配置
\n1 2 3 4 5 server.on("/" , handleRoot); server.onNotFound(handleNotFound);
\n \n访问处理函数
\n1 2 3 void handleRoot () { server.send(200 , "text/plain" , "Hello from ESP8266" ); }
\n1 2 3 void handleNotFound () { server.send(404 , "text/plain" , "404: Not found" ); }
\n \n \n\n循环监听端口
\n1 2 3 void loop () { server.handleClient(); }
\n \n \n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> #include <ESP8266WebServer.h> ESP8266WiFiMulti wifiMulti; ESP8266WebServer server (80 ) ; void setup () { Serial.begin(9600 ); server.begin(); server.on("/" , handleRoot); server.onNotFound(handleNotFound); wifiMulti.addAP("RBook" , "17191719" ); wifiMulti.addAP("bbb" , "12345678" ); wifiMulti.addAP("ccc" , "12345678" ); int i = 0 ; while (wifiMulti.run() != WL_CONNECTED){ delay(600 ); Serial.print(++i); Serial.print(" " ); } Serial.println("" ); Serial.print("IP Address: " ); Serial.println(WiFi.localIP()); } void loop () { server.handleClient(); } void handleRoot () { server.send(200 , "text/plain" , "Hello esp8266" ); } void handleNotFound () { server.send(404 , "text/plain" , "404 not found" ); }
\n2、通过网络服务器实现开发板控制 通过网页控制nodeMCU开发板小灯亮灭
\n步骤1、2、3、6不变,只是第3、4步的服务器访问配置和访问处理函数有些许改动
\n\n服务器访问配置
\n1 2 3 4 5 6 server.on("/" , HTTP_GET, handleRoot); server.on("/LED" , HTTP_POST, handleLED); server.onNotFound(handleNotFound);
\n \n访问处理函数
\n1 2 3 4 void handleRoot () { server.send(200 , "text/html" , "<form action=\\"/LED\\" method=\\"POST\\"><input type=\\"submit\\" value=\\"Toggle LED\\"></form>" ); }
\n\n注意这句html代码:
\n1 2 3 <form action ="/LED" method ="POST" > <input type ="submit" value ="Toggle LED" > </form >
\n这会生成一个按钮,点击这个按钮会把数据发到”/LED”页面
\n \n \n \n 下面是“/LED”页面的处理函数:
\n 1 2 3 4 5 6 7 void handleLED () { static bool LEDState = LOW; LEDState = !LEDState; digitalWrite(LED_BUILTIN, LEDState); server.sendHeader("Location" , "/" ); server.send(303 ); }
\n完整code
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> #include <ESP8266WebServer.h> ESP8266WiFiMulti wifiMulti; ESP8266WebServer server (80 ) ; void setup () { Serial.begin(9600 ); pinMode(LED_BUILTIN, OUTPUT); server.begin(); server.on("/" , HTTP_GET, handleRoot); server.on("/LED" , HTTP_POST, handleLED); server.onNotFound(handleNotFound); wifiMulti.addAP("RBook" , "17191719" ); wifiMulti.addAP("bbb" , "12345678" ); wifiMulti.addAP("ccc" , "12345678" ); int i = 0 ; while (wifiMulti.run() != WL_CONNECTED){ delay(600 ); Serial.print(++i); Serial.print(" " ); } Serial.println("" ); Serial.print("IP Address: " ); Serial.println(WiFi.localIP()); } void loop () { server.handleClient(); } void handleRoot () { server.send(200 , "text/html" , "<form action=\\"/LED\\" method=\\"POST\\"><input type=\\"submit\\" value=\\"Toggle LED\\"></form>" ); } void handleNotFound () { server.send(404 , "text/plain" , "404 not found" ); } void handleLED () { static bool LEDState = LOW; LEDState = !LEDState; digitalWrite(LED_BUILTIN, LEDState); server.sendHeader("Location" , "/" ); server.send(303 ); }
\n3、将开发板引脚状态发送到终端网页 将引脚D3 (已经与flash按键相连,按键按下为低电平)的电平状态显示到网页中
\n\n首先在开始时将D3引脚设置为上拉输入模式
\n1 pinMode(D3, INPUT_PULLUP);
\n \n循环读取引脚状态 digitalRead()
\n1 2 3 4 5 bool pinState;void loop () { pinState = digitalRead(D3); }
\n \n \n\n要在根节点下显示引脚状态,所以其访问处理函数为:
\n1 2 3 4 5 6 7 8 9 10 11 12 void handleRoot () { String displayPinState; if (pinState == HIGH){ displayPinState = "Button State: HIGH" ; } else { displayPinState = "Button State: LOW" ; } esp8266_server.send(200 , "text/plain" , displayPinState); }
\n \n \n完整code
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> #include <ESP8266WebServer.h> ESP8266WiFiMulti wifiMulti; ESP8266WebServer server (80 ) ; bool pinState;void setup () { Serial.begin(9600 ); pinMode(D3, INPUT_PULLUP); server.begin(); server.on("/" , HTTP_GET, handleRoot); server.onNotFound(handleNotFound); wifiMulti.addAP("RBook" , "17191719" ); wifiMulti.addAP("bbb" , "12345678" ); wifiMulti.addAP("ccc" , "12345678" ); int i = 0 ; while (wifiMulti.run() != WL_CONNECTED){ delay(800 ); Serial.print(++i); Serial.print(" " ); } Serial.println("" ); Serial.print("IP Address: " ); Serial.println(WiFi.localIP()); } void loop () { server.handleClient(); pinState = digitalRead(D3); } void handleRoot () { String displayPinState; if (pinState == HIGH){ displayPinState = "Button State: HIGH" ; } else { displayPinState = "Button State: LOW" ; } server.send(200 , "text/plain" , displayPinState); } void handleNotFound () { server.send(404 , "text/plain" , "404 not found" ); }
\n\n注意:引脚状态改变后需要手动刷新网页,网页显示才会改变
\n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"1、建立基本网络服务器 \n导入库文件 ESP8266WebServer.h
\n \n创建服务器对象
\n1 ESP8266WebServer server (80 ) ;
\n \n启动服务器
\n \n \n服务器访问配置
\n1 2 3 4 5 server.on("/" , handleRoot); server.onNotFound(handleNotFound);
\n \n访问处理函数
\n1 2 3 void handleRoot () { server.send(200 , "text/plain" , "Hello from ESP8266" ); }
\n1 2 3 void handleNotFound () { server.send(404 , "text/plain" , "404: Not found" ); }
\n \n \n\n循环监听端口
\n1 2 3 void loop () { server.handleClient(); }
\n \n \n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> #include <ESP8266WebServer.h> ESP8266WiFiMulti wifiMulti; ESP8266WebServer server (80 ) ; void setup () { Serial.begin(9600 ); server.begin(); server.on("/" , handleRoot); server.onNotFound(handleNotFound); wifiMulti.addAP("RBook" , "17191719" ); wifiMulti.addAP("bbb" , "12345678" ); wifiMulti.addAP("ccc" , "12345678" ); int i = 0 ; while (wifiMulti.run() != WL_CONNECTED){ delay(600 ); Serial.print(++i); Serial.print(" " ); } Serial.println("" ); Serial.print("IP Address: " ); Serial.println(WiFi.localIP()); } void loop () { server.handleClient(); } void handleRoot () { server.send(200 , "text/plain" , "Hello esp8266" ); } void handleNotFound () { server.send(404 , "text/plain" , "404 not found" ); }
\n2、通过网络服务器实现开发板控制 通过网页控制nodeMCU开发板小灯亮灭
\n步骤1、2、3、6不变,只是第3、4步的服务器访问配置和访问处理函数有些许改动
\n\n服务器访问配置
\n1 2 3 4 5 6 server.on("/" , HTTP_GET, handleRoot); server.on("/LED" , HTTP_POST, handleLED); server.onNotFound(handleNotFound);
\n \n访问处理函数
\n1 2 3 4 void handleRoot () { server.send(200 , "text/html" , "<form action=\\"/LED\\" method=\\"POST\\"><input type=\\"submit\\" value=\\"Toggle LED\\"></form>" ); }
\n\n注意这句html代码:
\n1 2 3 <form action ="/LED" method ="POST" > <input type ="submit" value ="Toggle LED" > </form >
\n这会生成一个按钮,点击这个按钮会把数据发到”/LED”页面
\n \n \n \n 下面是“/LED”页面的处理函数:
\n 1 2 3 4 5 6 7 void handleLED () { static bool LEDState = LOW; LEDState = !LEDState; digitalWrite(LED_BUILTIN, LEDState); server.sendHeader("Location" , "/" ); server.send(303 ); }
\n完整code
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> #include <ESP8266WebServer.h> ESP8266WiFiMulti wifiMulti; ESP8266WebServer server (80 ) ; void setup () { Serial.begin(9600 ); pinMode(LED_BUILTIN, OUTPUT); server.begin(); server.on("/" , HTTP_GET, handleRoot); server.on("/LED" , HTTP_POST, handleLED); server.onNotFound(handleNotFound); wifiMulti.addAP("RBook" , "17191719" ); wifiMulti.addAP("bbb" , "12345678" ); wifiMulti.addAP("ccc" , "12345678" ); int i = 0 ; while (wifiMulti.run() != WL_CONNECTED){ delay(600 ); Serial.print(++i); Serial.print(" " ); } Serial.println("" ); Serial.print("IP Address: " ); Serial.println(WiFi.localIP()); } void loop () { server.handleClient(); } void handleRoot () { server.send(200 , "text/html" , "<form action=\\"/LED\\" method=\\"POST\\"><input type=\\"submit\\" value=\\"Toggle LED\\"></form>" ); } void handleNotFound () { server.send(404 , "text/plain" , "404 not found" ); } void handleLED () { static bool LEDState = LOW; LEDState = !LEDState; digitalWrite(LED_BUILTIN, LEDState); server.sendHeader("Location" , "/" ); server.send(303 ); }
\n3、将开发板引脚状态发送到终端网页 将引脚D3 (已经与flash按键相连,按键按下为低电平)的电平状态显示到网页中
\n\n首先在开始时将D3引脚设置为上拉输入模式
\n1 pinMode(D3, INPUT_PULLUP);
\n \n循环读取引脚状态 digitalRead()
\n1 2 3 4 5 bool pinState;void loop () { pinState = digitalRead(D3); }
\n \n \n\n要在根节点下显示引脚状态,所以其访问处理函数为:
\n1 2 3 4 5 6 7 8 9 10 11 12 void handleRoot () { String displayPinState; if (pinState == HIGH){ displayPinState = "Button State: HIGH" ; } else { displayPinState = "Button State: LOW" ; } esp8266_server.send(200 , "text/plain" , displayPinState); }
\n \n \n完整code
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> #include <ESP8266WebServer.h> ESP8266WiFiMulti wifiMulti; ESP8266WebServer server (80 ) ; bool pinState;void setup () { Serial.begin(9600 ); pinMode(D3, INPUT_PULLUP); server.begin(); server.on("/" , HTTP_GET, handleRoot); server.onNotFound(handleNotFound); wifiMulti.addAP("RBook" , "17191719" ); wifiMulti.addAP("bbb" , "12345678" ); wifiMulti.addAP("ccc" , "12345678" ); int i = 0 ; while (wifiMulti.run() != WL_CONNECTED){ delay(800 ); Serial.print(++i); Serial.print(" " ); } Serial.println("" ); Serial.print("IP Address: " ); Serial.println(WiFi.localIP()); } void loop () { server.handleClient(); pinState = digitalRead(D3); } void handleRoot () { String displayPinState; if (pinState == HIGH){ displayPinState = "Button State: HIGH" ; } else { displayPinState = "Button State: LOW" ; } server.send(200 , "text/plain" , displayPinState); } void handleNotFound () { server.send(404 , "text/plain" , "404 not found" ); }
\n\n注意:引脚状态改变后需要手动刷新网页,网页显示才会改变
\n \n"},{"title":"esp8266(4)-NodeMCU作为客户端使用","abbrlink":"d150c3f1","date":"2021-08-03T03:21:00.000Z","description":"esp8266作为客户端如何连接服务器","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\nESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。他们是`WiFiClient`库和`ESP8266HTTPClient`库。\n\n【注意】\n\n- `WiFiClient` 被声明在 `` 中(这种方式实现Client更复杂,但灵活)\n\n- 而`ESP8266HTTPClient`被单独声明在 ``(这种方式实现Client更简单,但很多功能不能根据自己意愿来定制实现)\n\n\n\n\n\n## 1、ESP8266HTTPClient 实现\n\n1. 连接WiFi,具体实现看第一篇文章\n\n2. 连接WiFi后,写一个函数来访问服务器\n\n3. 函数设计,五个步骤:\n\n - 创建客户端对象\n - 配置访问地址url\n - 发送get(post)请求\n - 请求成功:处理返回信息\n - 请求失败:(任意)\n - 结束请求\n\n ```c\n // 发送HTTP请求并且将服务器响应通过串口输出\n void httpClientRequest(){\n \n //1 创建 HTTPClient 对象\n HTTPClient httpClient;\n \n //2 通过begin函数配置请求地址\n httpClient.begin(URL); \n \n //3 通过GET函数启动连接并发送HTTP请求\n int httpCode = httpClient.GET();\n \n //4 处理服务器返回信息\n if (httpCode == HTTP_CODE_OK) {\n // 使用getString函数获取服务器响应体内容\n String responsePayload = httpClient.getString();\n //...\n } else {\n //...\n }\n \n //5 关闭ESP8266与服务器连接\n httpClient.end();\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n\nvoid setup() {\n String url = \"http://www.example.com\";\n \n Serial.begin(115200);\n \n WiFi.mode(WIFI_STA);\n WiFi.begin(\"RBook\", \"17191719\");\n\n for(int i=0; WiFi.status() != WL_CONNECTED; i++){\n delay(1000);\n Serial.printf(\"%d \", i);\n }\n Serial.print(\"\\r\\nwifi is connected\\r\\n\");\n\n getResponse(url);\n \n}\n\nvoid loop() {\n // put your main code here, to run repeatedly:\n\n}\n\n//五个步骤进行发送请求并获取数据\nvoid getResponse(String url){\n \n HTTPClient httpClient; // 1\n\n httpClient.begin(url); // 2\n\n int httpCode = httpClient.GET(); // 3\n\n // 4\n if(httpCode == HTTP_CODE_OK){\n Serial.printf(\"httpCode: %d Success......\\n\", httpCode);\n String res = httpClient.getString();\n Serial.println(\"======================\");\n Serial.println(res);\n Serial.println(\"======================\");\n }\n else {\n Serial.printf(\"httpCode: %d Failed......\\n\", httpCode);\n }\n\n // 5\n httpClient.end();\n \n}\n\n```\n\n##### 【注意】\n\n> 转义字符 `\\r` 表示将光标移动到本行开头(继续输出会覆盖本行内容)\n>\n> `\\r\\n` 表示将光标移到开头再换行(不会覆盖本行内容),一般与 `\\n` 效果相同\n\n\n\n## 2、WiFiClient 实现\n\n1. 创建对象\n2. 连接服务器\n - 连接成功:(1)发送请求(2)获取并处理服务器响应的数据(3)断开与服务器的连接\n - 连接失败:断开连接\n\n```c\nvoid wifiClientRequest(String url){\n // 建立WiFi客户端对象\n WiFiClient client; \n\n // 建立字符串,用于HTTP请求\n String httpRequest = String(\"GET /\") + \" HTTP/1.1\\r\\n\" +\n \"url: \" + url + \"\\r\\n\" +\n \"Connection: close\\r\\n\" +\n \"\\r\\n\";\n\n // 连接网络服务器\n if (client.connect(url, httpPort)){ \t//连接成功connect会返回true\n \n client.print(httpRequest); // 向服务器发送HTTP请求\n\n // 通过串口输出网络服务器响应信息 \n while (client.connected() || client.available()){ \n if (client.available()){\n String line = client.readStringUntil('\\n'); //一行一行读取\n Serial.println(line);\n }\n }\n\n client.stop();\t\t// 断开与服务器的连接\n\n } else{ \n client.stop();\t\t// 如果连接不成功则断开连接\n }\n```\n\n\n\n## ","source":"_posts/esp8266/4-NodeMCU作为客户端使用.md","raw":"---\ntitle: esp8266(4)-NodeMCU作为客户端使用\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: d150c3f1\ndate: 2021-08-03 11:21:00\ndescription: esp8266作为客户端如何连接服务器\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\nESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。他们是`WiFiClient`库和`ESP8266HTTPClient`库。\n\n【注意】\n\n- `WiFiClient` 被声明在 `` 中(这种方式实现Client更复杂,但灵活)\n\n- 而`ESP8266HTTPClient`被单独声明在 ``(这种方式实现Client更简单,但很多功能不能根据自己意愿来定制实现)\n\n\n\n\n\n## 1、ESP8266HTTPClient 实现\n\n1. 连接WiFi,具体实现看第一篇文章\n\n2. 连接WiFi后,写一个函数来访问服务器\n\n3. 函数设计,五个步骤:\n\n - 创建客户端对象\n - 配置访问地址url\n - 发送get(post)请求\n - 请求成功:处理返回信息\n - 请求失败:(任意)\n - 结束请求\n\n ```c\n // 发送HTTP请求并且将服务器响应通过串口输出\n void httpClientRequest(){\n \n //1 创建 HTTPClient 对象\n HTTPClient httpClient;\n \n //2 通过begin函数配置请求地址\n httpClient.begin(URL); \n \n //3 通过GET函数启动连接并发送HTTP请求\n int httpCode = httpClient.GET();\n \n //4 处理服务器返回信息\n if (httpCode == HTTP_CODE_OK) {\n // 使用getString函数获取服务器响应体内容\n String responsePayload = httpClient.getString();\n //...\n } else {\n //...\n }\n \n //5 关闭ESP8266与服务器连接\n httpClient.end();\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n\nvoid setup() {\n String url = \"http://www.example.com\";\n \n Serial.begin(115200);\n \n WiFi.mode(WIFI_STA);\n WiFi.begin(\"RBook\", \"17191719\");\n\n for(int i=0; WiFi.status() != WL_CONNECTED; i++){\n delay(1000);\n Serial.printf(\"%d \", i);\n }\n Serial.print(\"\\r\\nwifi is connected\\r\\n\");\n\n getResponse(url);\n \n}\n\nvoid loop() {\n // put your main code here, to run repeatedly:\n\n}\n\n//五个步骤进行发送请求并获取数据\nvoid getResponse(String url){\n \n HTTPClient httpClient; // 1\n\n httpClient.begin(url); // 2\n\n int httpCode = httpClient.GET(); // 3\n\n // 4\n if(httpCode == HTTP_CODE_OK){\n Serial.printf(\"httpCode: %d Success......\\n\", httpCode);\n String res = httpClient.getString();\n Serial.println(\"======================\");\n Serial.println(res);\n Serial.println(\"======================\");\n }\n else {\n Serial.printf(\"httpCode: %d Failed......\\n\", httpCode);\n }\n\n // 5\n httpClient.end();\n \n}\n\n```\n\n##### 【注意】\n\n> 转义字符 `\\r` 表示将光标移动到本行开头(继续输出会覆盖本行内容)\n>\n> `\\r\\n` 表示将光标移到开头再换行(不会覆盖本行内容),一般与 `\\n` 效果相同\n\n\n\n## 2、WiFiClient 实现\n\n1. 创建对象\n2. 连接服务器\n - 连接成功:(1)发送请求(2)获取并处理服务器响应的数据(3)断开与服务器的连接\n - 连接失败:断开连接\n\n```c\nvoid wifiClientRequest(String url){\n // 建立WiFi客户端对象\n WiFiClient client; \n\n // 建立字符串,用于HTTP请求\n String httpRequest = String(\"GET /\") + \" HTTP/1.1\\r\\n\" +\n \"url: \" + url + \"\\r\\n\" +\n \"Connection: close\\r\\n\" +\n \"\\r\\n\";\n\n // 连接网络服务器\n if (client.connect(url, httpPort)){ \t//连接成功connect会返回true\n \n client.print(httpRequest); // 向服务器发送HTTP请求\n\n // 通过串口输出网络服务器响应信息 \n while (client.connected() || client.available()){ \n if (client.available()){\n String line = client.readStringUntil('\\n'); //一行一行读取\n Serial.println(line);\n }\n }\n\n client.stop();\t\t// 断开与服务器的连接\n\n } else{ \n client.stop();\t\t// 如果连接不成功则断开连接\n }\n```\n\n\n\n## ","slug":"esp8266/4-NodeMCU作为客户端使用","published":1,"updated":"2021-09-11T07:54:22.484Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pu003zakve3wlwdsem","content":"ESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。他们是WiFiClient
库和ESP8266HTTPClient
库。
\n【注意】
\n\n1、ESP8266HTTPClient 实现 \n连接WiFi,具体实现看第一篇文章
\n \n连接WiFi后,写一个函数来访问服务器
\n \n函数设计,五个步骤:
\n\n创建客户端对象 \n配置访问地址url \n发送get(post)请求\n请求成功:处理返回信息 \n请求失败:(任意) \n \n \n结束请求 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void httpClientRequest () { HTTPClient httpClient; httpClient.begin(URL); int httpCode = httpClient.GET(); if (httpCode == HTTP_CODE_OK) { String responsePayload = httpClient.getString(); } else { } httpClient.end(); }
\n \n \n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 #include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h> void setup () { String url = "http://www.example.com" ; Serial.begin(115200 ); WiFi.mode(WIFI_STA); WiFi.begin("RBook" , "17191719" ); for (int i=0 ; WiFi.status() != WL_CONNECTED; i++){ delay(1000 ); Serial.printf ("%d " , i); } Serial.print("\\r\\nwifi is connected\\r\\n" ); getResponse(url); } void loop () { } void getResponse (String url) { HTTPClient httpClient; httpClient.begin(url); int httpCode = httpClient.GET(); if (httpCode == HTTP_CODE_OK){ Serial.printf ("httpCode: %d Success......\\n" , httpCode); String res = httpClient.getString(); Serial.println("======================" ); Serial.println(res); Serial.println("======================" ); } else { Serial.printf ("httpCode: %d Failed......\\n" , httpCode); } httpClient.end(); }
\n【注意】 \n转义字符 \\r
表示将光标移动到本行开头(继续输出会覆盖本行内容)
\n\\r\\n
表示将光标移到开头再换行(不会覆盖本行内容),一般与 \\n
效果相同
\n \n2、WiFiClient 实现 \n创建对象 \n连接服务器\n连接成功:(1)发送请求(2)获取并处理服务器响应的数据(3)断开与服务器的连接 \n连接失败:断开连接 \n \n \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void wifiClientRequest (String url) { WiFiClient client; String httpRequest = String("GET /" ) + " HTTP/1.1\\r\\n" + "url: " + url + "\\r\\n" + "Connection: close\\r\\n" + "\\r\\n" ; if (client.connect(url, httpPort)){ \t client.print(httpRequest); while (client.connected() || client.available()){ if (client.available()){ String line = client.readStringUntil('\\n' ); Serial.println(line); } } client.stop();\t\t } else { client.stop();\t\t }
\n ","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"ESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。他们是WiFiClient
库和ESP8266HTTPClient
库。
\n【注意】
\n\n1、ESP8266HTTPClient 实现 \n连接WiFi,具体实现看第一篇文章
\n \n连接WiFi后,写一个函数来访问服务器
\n \n函数设计,五个步骤:
\n\n创建客户端对象 \n配置访问地址url \n发送get(post)请求\n请求成功:处理返回信息 \n请求失败:(任意) \n \n \n结束请求 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void httpClientRequest () { HTTPClient httpClient; httpClient.begin(URL); int httpCode = httpClient.GET(); if (httpCode == HTTP_CODE_OK) { String responsePayload = httpClient.getString(); } else { } httpClient.end(); }
\n \n \n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 #include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h> void setup () { String url = "http://www.example.com" ; Serial.begin(115200 ); WiFi.mode(WIFI_STA); WiFi.begin("RBook" , "17191719" ); for (int i=0 ; WiFi.status() != WL_CONNECTED; i++){ delay(1000 ); Serial.printf ("%d " , i); } Serial.print("\\r\\nwifi is connected\\r\\n" ); getResponse(url); } void loop () { } void getResponse (String url) { HTTPClient httpClient; httpClient.begin(url); int httpCode = httpClient.GET(); if (httpCode == HTTP_CODE_OK){ Serial.printf ("httpCode: %d Success......\\n" , httpCode); String res = httpClient.getString(); Serial.println("======================" ); Serial.println(res); Serial.println("======================" ); } else { Serial.printf ("httpCode: %d Failed......\\n" , httpCode); } httpClient.end(); }
\n【注意】 \n转义字符 \\r
表示将光标移动到本行开头(继续输出会覆盖本行内容)
\n\\r\\n
表示将光标移到开头再换行(不会覆盖本行内容),一般与 \\n
效果相同
\n \n2、WiFiClient 实现 \n创建对象 \n连接服务器\n连接成功:(1)发送请求(2)获取并处理服务器响应的数据(3)断开与服务器的连接 \n连接失败:断开连接 \n \n \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void wifiClientRequest (String url) { WiFiClient client; String httpRequest = String("GET /" ) + " HTTP/1.1\\r\\n" + "url: " + url + "\\r\\n" + "Connection: close\\r\\n" + "\\r\\n" ; if (client.connect(url, httpPort)){ \t client.print(httpRequest); while (client.connected() || client.available()){ if (client.available()){ String line = client.readStringUntil('\\n' ); Serial.println(line); } } client.stop();\t\t } else { client.stop();\t\t }
\n "},{"title":"esp8266(5)-Stream数据流","abbrlink":"2d7f83ef","date":"2021-08-03T05:10:00.000Z","description":"esp8266读取串口传入的数据、文件系统的数据、服务器传来的数据","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n# Stream数据流\n\n## 1、串口收发数据\n\n```c\nvoid setup() {\n // 启动串口通讯\n Serial.begin(9600); \n Serial.println();\n}\n \nvoid loop() { \n if (Serial.available()){ // 当串口接收到信息后\n String serialData = Serial.readString(); // 将接收到的信息存储于serialData变量\n Serial.print(serialData); \n }\n}\n```\n\n使用`Serial.available`来判断ESP8266开发板是否接收到串口数据\n\n实际上,ESP8266开发板通过串口收发的数据通过Stream进行的\n\n> 下面的程序将演示:\n>\n> 当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。\n>\n> ```c\n> void setup() {\n> Serial.begin(9600);\n> Serial.println(\"\");\n> Serial.println(\"Please enter input...\");\n> }\n> \n> void loop() {\n> while(Serial.available()){\n> if(Serial.find(\"ok\")){\n> Serial.println(\"Found ok in user input.\");\n> \n> int serialParseInt = Serial.parseInt();\n> Serial.print(\"serialParseInt = \");\n> Serial.println(serialParseInt);\n> \n> String serialInput = Serial.readString();\n> Serial.print(\"serialInput = \");\n> Serial.println(serialInput);\n> }\n> }\n> }\n> ```\n>\n> \n\n\n\n\n\n## 2、使用Stream方式来读取服务器响应的信息\n\n(具体看第4篇文章第2点的代码)\n\n```c\nwhile (client.connected() || client.available()){ \n if (client.available()){\n String line = client.readStringUntil('\\n'); //读取返回的数据\n Serial.println(line);\n }\n}\n```\n\n\n\n## 3、使用Stream方式来读取文件内容\n\n\n\n```c\nFile f = SPIFFS.open(file_name, \"r\"); // 以“r”模式再次打开闪存文件\nSerial.println(f.readString());\t\t\t\t// 读取文件内容\n```\n\n","source":"_posts/esp8266/5-Stream数据流.md","raw":"---\ntitle: esp8266(5)-Stream数据流\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: 2d7f83ef\ndate: 2021-08-03 13:10:00\ndescription: esp8266读取串口传入的数据、文件系统的数据、服务器传来的数据\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n# Stream数据流\n\n## 1、串口收发数据\n\n```c\nvoid setup() {\n // 启动串口通讯\n Serial.begin(9600); \n Serial.println();\n}\n \nvoid loop() { \n if (Serial.available()){ // 当串口接收到信息后\n String serialData = Serial.readString(); // 将接收到的信息存储于serialData变量\n Serial.print(serialData); \n }\n}\n```\n\n使用`Serial.available`来判断ESP8266开发板是否接收到串口数据\n\n实际上,ESP8266开发板通过串口收发的数据通过Stream进行的\n\n> 下面的程序将演示:\n>\n> 当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。\n>\n> ```c\n> void setup() {\n> Serial.begin(9600);\n> Serial.println(\"\");\n> Serial.println(\"Please enter input...\");\n> }\n> \n> void loop() {\n> while(Serial.available()){\n> if(Serial.find(\"ok\")){\n> Serial.println(\"Found ok in user input.\");\n> \n> int serialParseInt = Serial.parseInt();\n> Serial.print(\"serialParseInt = \");\n> Serial.println(serialParseInt);\n> \n> String serialInput = Serial.readString();\n> Serial.print(\"serialInput = \");\n> Serial.println(serialInput);\n> }\n> }\n> }\n> ```\n>\n> \n\n\n\n\n\n## 2、使用Stream方式来读取服务器响应的信息\n\n(具体看第4篇文章第2点的代码)\n\n```c\nwhile (client.connected() || client.available()){ \n if (client.available()){\n String line = client.readStringUntil('\\n'); //读取返回的数据\n Serial.println(line);\n }\n}\n```\n\n\n\n## 3、使用Stream方式来读取文件内容\n\n\n\n```c\nFile f = SPIFFS.open(file_name, \"r\"); // 以“r”模式再次打开闪存文件\nSerial.println(f.readString());\t\t\t\t// 读取文件内容\n```\n\n","slug":"esp8266/5-Stream数据流","published":1,"updated":"2021-09-11T07:54:38.317Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pv0042akve76zr0o90","content":"Stream数据流 1、串口收发数据 1 2 3 4 5 6 7 8 9 10 11 12 void setup () { Serial.begin(9600 ); Serial.println(); } void loop () { if (Serial.available()){ String serialData = Serial.readString(); Serial.print(serialData); } }
\n使用Serial.available
来判断ESP8266开发板是否接收到串口数据
\n实际上,ESP8266开发板通过串口收发的数据通过Stream进行的
\n\n下面的程序将演示:
\n当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void setup () { Serial.begin(9600 ); Serial.println("" ); Serial.println("Please enter input..." ); } void loop () { while (Serial.available()){ if (Serial.find("ok" )){ Serial.println("Found ok in user input." ); int serialParseInt = Serial.parseInt(); Serial.print("serialParseInt = " ); Serial.println(serialParseInt); String serialInput = Serial.readString(); Serial.print("serialInput = " ); Serial.println(serialInput); } } }
\n \n2、使用Stream方式来读取服务器响应的信息 (具体看第4篇文章第2点的代码)
\n1 2 3 4 5 6 while (client.connected() || client.available()){ if (client.available()){ String line = client.readStringUntil('\\n' ); Serial.println(line); } }
\n3、使用Stream方式来读取文件内容 1 2 File f = SPIFFS.open(file_name, "r" ); Serial.println(f.readString());\t\t\t\t
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"Stream数据流 1、串口收发数据 1 2 3 4 5 6 7 8 9 10 11 12 void setup () { Serial.begin(9600 ); Serial.println(); } void loop () { if (Serial.available()){ String serialData = Serial.readString(); Serial.print(serialData); } }
\n使用Serial.available
来判断ESP8266开发板是否接收到串口数据
\n实际上,ESP8266开发板通过串口收发的数据通过Stream进行的
\n\n下面的程序将演示:
\n当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void setup () { Serial.begin(9600 ); Serial.println("" ); Serial.println("Please enter input..." ); } void loop () { while (Serial.available()){ if (Serial.find("ok" )){ Serial.println("Found ok in user input." ); int serialParseInt = Serial.parseInt(); Serial.print("serialParseInt = " ); Serial.println(serialParseInt); String serialInput = Serial.readString(); Serial.print("serialInput = " ); Serial.println(serialInput); } } }
\n \n2、使用Stream方式来读取服务器响应的信息 (具体看第4篇文章第2点的代码)
\n1 2 3 4 5 6 while (client.connected() || client.available()){ if (client.available()){ String line = client.readStringUntil('\\n' ); Serial.println(line); } }
\n3、使用Stream方式来读取文件内容 1 2 File f = SPIFFS.open(file_name, "r" ); Serial.println(f.readString());\t\t\t\t
\n"},{"title":"esp8266(3)-闪存文件系统","abbrlink":"2080031a","date":"2021-08-03T01:00:00.000Z","description":"esp8266的文件系统使用(向闪存中写入信息、读取信息)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n## 1、文件基本操作\n\nESP8266配有一个闪存,这就像是一个小硬盘,上传的文件就可以存放在这个闪存里,这个闪存简称为 SPIFFS\n\n\n\n代码中需要用到头文件`` 包含的类`SPIFFS`\n\n下面教你如何打开一个文件并向里面写如信息\n\n1. 格式化SPIFFS\n\n ```c++\n SPIFFS.format(); \n ```\n\n2. 启动SPIFFS\n\n ```c++\n SPIFFS.begin(); //该函数会返回一个bool型结果,启动成功返回true,否则为false\n ```\n\n3. 用open函数打开一个文件,如果不存在就会创建文件(打开文件->写入数据->关闭文件)\n\n ```c++\n //以写入的方式打开一个文件,filename为文件路径\n String file_name = \"/folder/notes.txt\"\n File dataFile = SPIFFS.open(file_name, \"w\");\t\t\n dataFile.println(\"Hello World\"); // 向dataFile写入字符串信息\n dataFile.close(); // 关闭文件\n ```\n\n \n\n完整code:\n\n```c++\n#include \n\nvoid setup(){\n Serial.begin(9600);\n \n String filename = \"/folder/notes.txt\";\n SPIFFS.format(); \n if(SPIFFS.begin()) Serial.println(\"SPIFFS Started...\");\n else Serial.println(\"SPIFFS Failed to Start...\");\n\n File f = SPIFFS.open(filename, \"w\");\n f.println(\"Hello esp8266 world\");\n f.close();\n Serial.println(\"Finish writing the file...\");\n \n}\n\nvoid loop(){\n \n}\n```\n\n\n\n\n\n### SPIFFS基于文件的基本操作\n\n```c\nvoid SPIFFS.format(); //格式化闪存文件系统 【注意:格式化文件系统需要耗费一定时间】\n\nbool SPIFFS.exists(String s); //是否存在文件名为s的文件(准确说s是文件的路径)\nbool SPIFFS.remove(String s); //删除文件s,返回是否删除成功\n```\n\n```c\nFile f = SPIFFS.open(file_name, \"r\"); //以读的方式打开一个文件\n\n//打印文件中所有内容\nfor(int i=0; i 注意:不论以何种方式打开文件,最后都要记得关闭文件\n\n\n\n### 基于目录的操作\n\n\n\n获取一个目录对象 `openDir()`:\n\n```c\nString folder_name = \"/folder\"; //被读取的文件夹\nDir dir = SPIFFS.openDir(folder_name); // 建立“目录”对象\n\n// dir.next()可以看作一个指针,每循环一次就会指向下一个元素\nwhile (dir.next()) { \n Serial.println(dir.fileName()); // 输出文件名\n}\n```\n\n\n\n\n\n\n\n### 闪存文件系统的基本信息\n\n```c\nFSInfo fs_info; // 创建一个基本信息的对象\n\n// 写入闪存文件系统信息到fs_info\nSPIFFS.info(fs_info);\n\nSerial.print(fs_info.totalBytes); // 可用空间总和(Bytes)\nSerial.print(fs_info.usedBytes); // 已经用掉的空间(Bytes)\n\n// 最大文件名字符限制(含路径和'\\0')\nSerial.println(fs_info.maxPathLength);\n\n// 最多允许打开文件数量\nSerial.println(fs_info.maxOpenFiles);\n\n// 存储块大小\nSerial.println(fs_info.blockSize);\n\n// 存储页大小\nSerial.println(fs_info.pageSize);\n```\n\n\n\n\n\n## 2、通过Arduino IDE向闪存文件系统上传文件\n\n\n\n看太极创客这篇[文章](http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/upload-files/)\n\n##### 【注意】\n\n1. 上传文件前,把【串口监视器】关闭\n2. 之前上传的代码中没有使用格式化,否则刚上传的文件就被格式化删除了\n\n\n\n## 3、使用闪存系统配置功能更丰富的网络服务器\n\n[太极创客文章](http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/spiffs-web-server/)\n\n","source":"_posts/esp8266/3-闪存文件系统.md","raw":"---\ntitle: esp8266(3)-闪存文件系统\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: 2080031a\ndate: 2021-08-03 09:00:00\ndescription: esp8266的文件系统使用(向闪存中写入信息、读取信息)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n## 1、文件基本操作\n\nESP8266配有一个闪存,这就像是一个小硬盘,上传的文件就可以存放在这个闪存里,这个闪存简称为 SPIFFS\n\n\n\n代码中需要用到头文件`` 包含的类`SPIFFS`\n\n下面教你如何打开一个文件并向里面写如信息\n\n1. 格式化SPIFFS\n\n ```c++\n SPIFFS.format(); \n ```\n\n2. 启动SPIFFS\n\n ```c++\n SPIFFS.begin(); //该函数会返回一个bool型结果,启动成功返回true,否则为false\n ```\n\n3. 用open函数打开一个文件,如果不存在就会创建文件(打开文件->写入数据->关闭文件)\n\n ```c++\n //以写入的方式打开一个文件,filename为文件路径\n String file_name = \"/folder/notes.txt\"\n File dataFile = SPIFFS.open(file_name, \"w\");\t\t\n dataFile.println(\"Hello World\"); // 向dataFile写入字符串信息\n dataFile.close(); // 关闭文件\n ```\n\n \n\n完整code:\n\n```c++\n#include \n\nvoid setup(){\n Serial.begin(9600);\n \n String filename = \"/folder/notes.txt\";\n SPIFFS.format(); \n if(SPIFFS.begin()) Serial.println(\"SPIFFS Started...\");\n else Serial.println(\"SPIFFS Failed to Start...\");\n\n File f = SPIFFS.open(filename, \"w\");\n f.println(\"Hello esp8266 world\");\n f.close();\n Serial.println(\"Finish writing the file...\");\n \n}\n\nvoid loop(){\n \n}\n```\n\n\n\n\n\n### SPIFFS基于文件的基本操作\n\n```c\nvoid SPIFFS.format(); //格式化闪存文件系统 【注意:格式化文件系统需要耗费一定时间】\n\nbool SPIFFS.exists(String s); //是否存在文件名为s的文件(准确说s是文件的路径)\nbool SPIFFS.remove(String s); //删除文件s,返回是否删除成功\n```\n\n```c\nFile f = SPIFFS.open(file_name, \"r\"); //以读的方式打开一个文件\n\n//打印文件中所有内容\nfor(int i=0; i 注意:不论以何种方式打开文件,最后都要记得关闭文件\n\n\n\n### 基于目录的操作\n\n\n\n获取一个目录对象 `openDir()`:\n\n```c\nString folder_name = \"/folder\"; //被读取的文件夹\nDir dir = SPIFFS.openDir(folder_name); // 建立“目录”对象\n\n// dir.next()可以看作一个指针,每循环一次就会指向下一个元素\nwhile (dir.next()) { \n Serial.println(dir.fileName()); // 输出文件名\n}\n```\n\n\n\n\n\n\n\n### 闪存文件系统的基本信息\n\n```c\nFSInfo fs_info; // 创建一个基本信息的对象\n\n// 写入闪存文件系统信息到fs_info\nSPIFFS.info(fs_info);\n\nSerial.print(fs_info.totalBytes); // 可用空间总和(Bytes)\nSerial.print(fs_info.usedBytes); // 已经用掉的空间(Bytes)\n\n// 最大文件名字符限制(含路径和'\\0')\nSerial.println(fs_info.maxPathLength);\n\n// 最多允许打开文件数量\nSerial.println(fs_info.maxOpenFiles);\n\n// 存储块大小\nSerial.println(fs_info.blockSize);\n\n// 存储页大小\nSerial.println(fs_info.pageSize);\n```\n\n\n\n\n\n## 2、通过Arduino IDE向闪存文件系统上传文件\n\n\n\n看太极创客这篇[文章](http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/upload-files/)\n\n##### 【注意】\n\n1. 上传文件前,把【串口监视器】关闭\n2. 之前上传的代码中没有使用格式化,否则刚上传的文件就被格式化删除了\n\n\n\n## 3、使用闪存系统配置功能更丰富的网络服务器\n\n[太极创客文章](http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/spiffs-web-server/)\n\n","slug":"esp8266/3-闪存文件系统","published":1,"updated":"2021-09-11T07:55:24.239Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pw0044akveh5zqdjte","content":"1、文件基本操作 ESP8266配有一个闪存,这就像是一个小硬盘,上传的文件就可以存放在这个闪存里,这个闪存简称为 SPIFFS
\n代码中需要用到头文件<FS.h>
包含的类SPIFFS
\n下面教你如何打开一个文件并向里面写如信息
\n\n格式化SPIFFS
\n \n \n启动SPIFFS
\n \n \n用open函数打开一个文件,如果不存在就会创建文件(打开文件->写入数据->关闭文件)
\n1 2 3 4 5 String file_name = "/folder/notes.txt" File dataFile = SPIFFS.open (file_name, "w" );\t\t dataFile.println ("Hello World" ); dataFile.close ();
\n \n \n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <FS.h> void setup () { Serial.begin (9600 ); String filename = "/folder/notes.txt" ; SPIFFS.format(); if (SPIFFS.begin ()) Serial.println ("SPIFFS Started..." ); else Serial.println ("SPIFFS Failed to Start..." ); File f = SPIFFS.open (filename, "w" ); f.println ("Hello esp8266 world" ); f.close (); Serial.println ("Finish writing the file..." ); } void loop () { }
\nSPIFFS基于文件的基本操作 1 2 3 4 void SPIFFS.format(); bool SPIFFS.exists(String s); bool SPIFFS.remove(String s);
\n1 2 3 4 5 6 File f = SPIFFS.open(file_name, "r" ); for (int i=0 ; i<f.size(); i++){ Serial.print((char )f.read()); }
\n1 2 3 4 File f = SPIFFS.open(file_name, "a" ); f.println("This is Appended Info." ); f.close();
\n\n注意:不论以何种方式打开文件,最后都要记得关闭文件
\n \n基于目录的操作 获取一个目录对象 openDir()
:
\n1 2 3 4 5 6 7 String folder_name = "/folder" ; Dir dir = SPIFFS.openDir(folder_name); while (dir.next()) { Serial.println(dir.fileName()); }
\n闪存文件系统的基本信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 FSInfo fs_info; SPIFFS.info(fs_info); Serial.print(fs_info.totalBytes); Serial.print(fs_info.usedBytes); Serial.println(fs_info.maxPathLength); Serial.println(fs_info.maxOpenFiles); Serial.println(fs_info.blockSize); Serial.println(fs_info.pageSize);
\n2、通过Arduino IDE向闪存文件系统上传文件 看太极创客这篇文章
\n【注意】 \n上传文件前,把【串口监视器】关闭 \n之前上传的代码中没有使用格式化,否则刚上传的文件就被格式化删除了 \n \n3、使用闪存系统配置功能更丰富的网络服务器 太极创客文章
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"1、文件基本操作 ESP8266配有一个闪存,这就像是一个小硬盘,上传的文件就可以存放在这个闪存里,这个闪存简称为 SPIFFS
\n代码中需要用到头文件<FS.h>
包含的类SPIFFS
\n下面教你如何打开一个文件并向里面写如信息
\n\n格式化SPIFFS
\n \n \n启动SPIFFS
\n \n \n用open函数打开一个文件,如果不存在就会创建文件(打开文件->写入数据->关闭文件)
\n1 2 3 4 5 String file_name = "/folder/notes.txt" File dataFile = SPIFFS.open (file_name, "w" );\t\t dataFile.println ("Hello World" ); dataFile.close ();
\n \n \n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <FS.h> void setup () { Serial.begin (9600 ); String filename = "/folder/notes.txt" ; SPIFFS.format(); if (SPIFFS.begin ()) Serial.println ("SPIFFS Started..." ); else Serial.println ("SPIFFS Failed to Start..." ); File f = SPIFFS.open (filename, "w" ); f.println ("Hello esp8266 world" ); f.close (); Serial.println ("Finish writing the file..." ); } void loop () { }
\nSPIFFS基于文件的基本操作 1 2 3 4 void SPIFFS.format(); bool SPIFFS.exists(String s); bool SPIFFS.remove(String s);
\n1 2 3 4 5 6 File f = SPIFFS.open(file_name, "r" ); for (int i=0 ; i<f.size(); i++){ Serial.print((char )f.read()); }
\n1 2 3 4 File f = SPIFFS.open(file_name, "a" ); f.println("This is Appended Info." ); f.close();
\n\n注意:不论以何种方式打开文件,最后都要记得关闭文件
\n \n基于目录的操作 获取一个目录对象 openDir()
:
\n1 2 3 4 5 6 7 String folder_name = "/folder" ; Dir dir = SPIFFS.openDir(folder_name); while (dir.next()) { Serial.println(dir.fileName()); }
\n闪存文件系统的基本信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 FSInfo fs_info; SPIFFS.info(fs_info); Serial.print(fs_info.totalBytes); Serial.print(fs_info.usedBytes); Serial.println(fs_info.maxPathLength); Serial.println(fs_info.maxOpenFiles); Serial.println(fs_info.blockSize); Serial.println(fs_info.pageSize);
\n2、通过Arduino IDE向闪存文件系统上传文件 看太极创客这篇文章
\n【注意】 \n上传文件前,把【串口监视器】关闭 \n之前上传的代码中没有使用格式化,否则刚上传的文件就被格式化删除了 \n \n3、使用闪存系统配置功能更丰富的网络服务器 太极创客文章
\n"},{"title":"MQTT篇(一)MQTT的认识及原理和操作","abbrlink":"73ba8569","date":"2021-08-09T02:21:00.000Z","description":"MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n\n\n## 什么是MQTT\n\n\n\nMQTT是一个客户端服务端架构的发布/订阅模式的**消息传输协议**。\n\n优点:\n\n轻巧、开放、简单、规范,易于实现。\n\n这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。\n\n\n\n\n\n## 基本原理\n\n\n\n在MQTT协议通讯中,有两个最为重要的角色。它们分别是**服务端**和**客户端**。\n\n**服务端**:\n\n是消息传输的枢纽\n\n**客户端**:\n\n客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。\n\n客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。\n\n![image-20210810220807339](https://gitee.com/ajream/images/raw/master/img/2021-08-1022-08-14_image-20210810220807339.png)\n\n\n\n\n\n\n\n## 客户端连接服务端\n\n分2个步骤\n\n1. 客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息\n2. 服务端收到请求后,发送数据包“CONNACK”进行确认\n\n\n\n### CONNECT报文\n\n这是该报文的信息:\n\n![MQTT CONNECT 报文内容](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-connect.gif)\n\n\n\n**clientID**\n\nclientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端\n\n\n\n**cleanSession – 清除会话**\n\n表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;\n\n取值true表示保存,false表示不保存\n\n> 注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。\n>\n> 反之如果数据不会重要,可以取值为false\n\n\n\n**keepAlive** —— 间隔时间\n\n用于服务端每隔多久就了解一下客户端是否与其保持连接的情况\n\n\n\n### CONNACK – 确认连接请求\n\n\n\n![MQTT CONNACK 信息内容](http://www.taichi-maker.com/wp-content/uploads/2020/09/connack.gif)\n\n\n\n**sessionPresent**\n\nCONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。\n\ncleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。\n\n总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次 客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。\n\n\n\n**returnCode** —— 返回码\n\n当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况\n\n| 返回码 | 返回码描述 |\n| :----: | :----------------------------------------------------------- |\n| 0 | 成功连接 |\n| 1 | 连接被服务端拒绝,原因是不支持客户端的MQTT协议版本 |\n| 2 | 连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。 |\n| 3 | 连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。 |\n| 4 | 连接被服务端拒绝,原因是用户名或密码无效。 |\n| 5 | 连接被服务端拒绝,原因是客户端未被授权连接到此服务端。 |\n\n\n\n\n\n## ESP8266连接MQTT服务端\n\nhttp://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883\n\n接下来使用`PubSubClient`库(去[GitHub](https://github.com/knolleary/pubsubclient/)下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。\n\n1. 配置WiFi\n\n2. 创建mqtt客户端对象(间接创建)\n\n ```c\n WiFiClient wifiClient;\n PubSubClient mqttClient(wifiClient);\n ```\n\n3. 配置客户端要连接哪个服务端(绑定服务端)\n\n ```c\n String mqttServer = \"test.ranye-iot.net\";\n const int port = 1883;\n mqttClient.setServer(mqttServer, port); //连接服务端\n ```\n\n4. 连接服务端\n\n ```c\n //用设备的mac地址来生成唯一标识该设备的clientID\n String clientID = \"esp8266-\" + WiFi.macAddress(); \n \n mqttClient.connect(clientId.c_str()) //连接成功返回true\n ```\n\n \n\n5. 连接成功后保持 “心跳”,否则...(可以尝试继续连接)\n\n ```c\n void loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器 \n mqttClient.loop(); // 保持客户端心跳\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n \n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"rbook\";\nconst char* password = \"12345678\";\nconst char* mqttServer = \"test.ranye-iot.net\";\n \n// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案\n// http://www.taichi-maker.com/public-mqtt-broker/\n \nWiFiClient wifiClient;\nPubSubClient mqttClient(wifiClient);\n \nvoid setup() {\n Serial.begin(9600);\n\n //设置ESP8266工作模式为无线终端模式\n WiFi.mode(WIFI_STA);\n\n // 连接WiFi\n connectWifi();\n\n // 设置MQTT服务器和端口号\n mqttClient.setServer(mqttServer, 1883);\n\n // 连接MQTT服务器\n connectMQTTServer();\n}\n \nvoid loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器 \n mqttClient.loop(); // 保持客户端心跳\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n}\n \nvoid connectMQTTServer(){\n // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)\n String clientId = \"esp8266-\" + WiFi.macAddress();\n\n // 连接MQTT服务器\n if (mqttClient.connect(clientId.c_str())) { \n Serial.println(\"MQTT Server Connected.\");\n Serial.println(\"Server Address: \");\n Serial.println(mqttServer);\n Serial.println(\"ClientId:\");\n Serial.println(clientId);\n } else {\n Serial.print(\"MQTT Server Connect Failed. Client State:\");\n Serial.println(mqttClient.state());\n delay(3000);\n } \n}\n\n// ESP8266连接wifi\nvoid connectWifi(){\n\n WiFi.begin(ssid, password);\n\n //等待WiFi连接,成功连接后输出成功信息\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.println(\"\"); \n}\n```\n\n","source":"_posts/esp8266/MQTT篇(一)-认识MQTT与基本操作.md","raw":"---\ntitle: MQTT篇(一)MQTT的认识及原理和操作\ntags:\n - 物联网\n - esp8266\ncategories:\n - 硬件学习\n - 物联网开发\nabbrlink: 73ba8569\ndate: 2021-08-09 10:21:00\ndescription: MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n\n\n## 什么是MQTT\n\n\n\nMQTT是一个客户端服务端架构的发布/订阅模式的**消息传输协议**。\n\n优点:\n\n轻巧、开放、简单、规范,易于实现。\n\n这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。\n\n\n\n\n\n## 基本原理\n\n\n\n在MQTT协议通讯中,有两个最为重要的角色。它们分别是**服务端**和**客户端**。\n\n**服务端**:\n\n是消息传输的枢纽\n\n**客户端**:\n\n客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。\n\n客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。\n\n![image-20210810220807339](https://gitee.com/ajream/images/raw/master/img/2021-08-1022-08-14_image-20210810220807339.png)\n\n\n\n\n\n\n\n## 客户端连接服务端\n\n分2个步骤\n\n1. 客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息\n2. 服务端收到请求后,发送数据包“CONNACK”进行确认\n\n\n\n### CONNECT报文\n\n这是该报文的信息:\n\n![MQTT CONNECT 报文内容](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-connect.gif)\n\n\n\n**clientID**\n\nclientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端\n\n\n\n**cleanSession – 清除会话**\n\n表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;\n\n取值true表示保存,false表示不保存\n\n> 注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。\n>\n> 反之如果数据不会重要,可以取值为false\n\n\n\n**keepAlive** —— 间隔时间\n\n用于服务端每隔多久就了解一下客户端是否与其保持连接的情况\n\n\n\n### CONNACK – 确认连接请求\n\n\n\n![MQTT CONNACK 信息内容](http://www.taichi-maker.com/wp-content/uploads/2020/09/connack.gif)\n\n\n\n**sessionPresent**\n\nCONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。\n\ncleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。\n\n总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次 客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。\n\n\n\n**returnCode** —— 返回码\n\n当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况\n\n| 返回码 | 返回码描述 |\n| :----: | :----------------------------------------------------------- |\n| 0 | 成功连接 |\n| 1 | 连接被服务端拒绝,原因是不支持客户端的MQTT协议版本 |\n| 2 | 连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。 |\n| 3 | 连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。 |\n| 4 | 连接被服务端拒绝,原因是用户名或密码无效。 |\n| 5 | 连接被服务端拒绝,原因是客户端未被授权连接到此服务端。 |\n\n\n\n\n\n## ESP8266连接MQTT服务端\n\nhttp://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883\n\n接下来使用`PubSubClient`库(去[GitHub](https://github.com/knolleary/pubsubclient/)下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。\n\n1. 配置WiFi\n\n2. 创建mqtt客户端对象(间接创建)\n\n ```c\n WiFiClient wifiClient;\n PubSubClient mqttClient(wifiClient);\n ```\n\n3. 配置客户端要连接哪个服务端(绑定服务端)\n\n ```c\n String mqttServer = \"test.ranye-iot.net\";\n const int port = 1883;\n mqttClient.setServer(mqttServer, port); //连接服务端\n ```\n\n4. 连接服务端\n\n ```c\n //用设备的mac地址来生成唯一标识该设备的clientID\n String clientID = \"esp8266-\" + WiFi.macAddress(); \n \n mqttClient.connect(clientId.c_str()) //连接成功返回true\n ```\n\n \n\n5. 连接成功后保持 “心跳”,否则...(可以尝试继续连接)\n\n ```c\n void loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器 \n mqttClient.loop(); // 保持客户端心跳\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n \n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"rbook\";\nconst char* password = \"12345678\";\nconst char* mqttServer = \"test.ranye-iot.net\";\n \n// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案\n// http://www.taichi-maker.com/public-mqtt-broker/\n \nWiFiClient wifiClient;\nPubSubClient mqttClient(wifiClient);\n \nvoid setup() {\n Serial.begin(9600);\n\n //设置ESP8266工作模式为无线终端模式\n WiFi.mode(WIFI_STA);\n\n // 连接WiFi\n connectWifi();\n\n // 设置MQTT服务器和端口号\n mqttClient.setServer(mqttServer, 1883);\n\n // 连接MQTT服务器\n connectMQTTServer();\n}\n \nvoid loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器 \n mqttClient.loop(); // 保持客户端心跳\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n}\n \nvoid connectMQTTServer(){\n // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)\n String clientId = \"esp8266-\" + WiFi.macAddress();\n\n // 连接MQTT服务器\n if (mqttClient.connect(clientId.c_str())) { \n Serial.println(\"MQTT Server Connected.\");\n Serial.println(\"Server Address: \");\n Serial.println(mqttServer);\n Serial.println(\"ClientId:\");\n Serial.println(clientId);\n } else {\n Serial.print(\"MQTT Server Connect Failed. Client State:\");\n Serial.println(mqttClient.state());\n delay(3000);\n } \n}\n\n// ESP8266连接wifi\nvoid connectWifi(){\n\n WiFi.begin(ssid, password);\n\n //等待WiFi连接,成功连接后输出成功信息\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.println(\"\"); \n}\n```\n\n","slug":"esp8266/MQTT篇(一)-认识MQTT与基本操作","published":1,"updated":"2021-09-11T07:53:28.088Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6px0047akve8seydg2u","content":"什么是MQTT MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议 。
\n优点:
\n轻巧、开放、简单、规范,易于实现。
\n这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。
\n基本原理 在MQTT协议通讯中,有两个最为重要的角色。它们分别是服务端 和客户端 。
\n服务端 :
\n是消息传输的枢纽
\n客户端 :
\n客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。
\n客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。
\n
\n客户端连接服务端 分2个步骤
\n\n客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息 \n服务端收到请求后,发送数据包“CONNACK”进行确认 \n \nCONNECT报文 这是该报文的信息:
\n
\nclientID
\nclientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端
\ncleanSession – 清除会话
\n表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;
\n取值true表示保存,false表示不保存
\n\n注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。
\n反之如果数据不会重要,可以取值为false
\n \nkeepAlive —— 间隔时间
\n用于服务端每隔多久就了解一下客户端是否与其保持连接的情况
\nCONNACK – 确认连接请求
\nsessionPresent
\nCONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。
\ncleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。
\n总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次 客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。
\nreturnCode —— 返回码
\n当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况
\n\n
\n\n\n返回码 \n返回码描述 \n \n \n\n\n0 \n成功连接 \n \n\n1 \n连接被服务端拒绝,原因是不支持客户端的MQTT协议版本 \n \n\n2 \n连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。 \n \n\n3 \n连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。 \n \n\n4 \n连接被服务端拒绝,原因是用户名或密码无效。 \n \n\n5 \n连接被服务端拒绝,原因是客户端未被授权连接到此服务端。 \n \n \n
\n
\nESP8266连接MQTT服务端 http://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883
\n接下来使用PubSubClient
库(去GitHub 下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。
\n\n配置WiFi
\n \n创建mqtt客户端对象(间接创建)
\n1 2 WiFiClient wifiClient; PubSubClient mqttClient (wifiClient) ;
\n \n配置客户端要连接哪个服务端(绑定服务端)
\n1 2 3 String mqttServer = "test.ranye-iot.net" ; const int port = 1883 ;mqttClient.setServer(mqttServer, port);
\n \n连接服务端
\n1 2 3 4 String clientID = "esp8266-" + WiFi.macAddress(); mqttClient.connect(clientId.c_str())
\n \n \n\n连接成功后保持 “心跳”,否则…(可以尝试继续连接)
\n1 2 3 4 5 6 7 void loop () { if (mqttClient.connected()) { mqttClient.loop(); } else { connectMQTTServer(); } }
\n \n \n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <ESP8266WiFi.h> #include <PubSubClient.h> const char * ssid = "rbook" ;const char * password = "12345678" ;const char * mqttServer = "test.ranye-iot.net" ; WiFiClient wifiClient; PubSubClient mqttClient (wifiClient) ; void setup () { Serial.begin(9600 ); WiFi.mode(WIFI_STA); connectWifi(); mqttClient.setServer(mqttServer, 1883 ); connectMQTTServer(); } void loop () { if (mqttClient.connected()) { mqttClient.loop(); } else { connectMQTTServer(); } } void connectMQTTServer () { String clientId = "esp8266-" + WiFi.macAddress(); if (mqttClient.connect(clientId.c_str())) { Serial.println("MQTT Server Connected." ); Serial.println("Server Address: " ); Serial.println(mqttServer); Serial.println("ClientId:" ); Serial.println(clientId); } else { Serial.print("MQTT Server Connect Failed. Client State:" ); Serial.println(mqttClient.state()); delay(3000 ); } } void connectWifi () { WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000 ); Serial.print("." ); } Serial.println("" ); Serial.println("WiFi Connected!" ); Serial.println("" ); }
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"什么是MQTT MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议 。
\n优点:
\n轻巧、开放、简单、规范,易于实现。
\n这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。
\n基本原理 在MQTT协议通讯中,有两个最为重要的角色。它们分别是服务端 和客户端 。
\n服务端 :
\n是消息传输的枢纽
\n客户端 :
\n客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。
\n客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。
\n
\n客户端连接服务端 分2个步骤
\n\n客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息 \n服务端收到请求后,发送数据包“CONNACK”进行确认 \n \nCONNECT报文 这是该报文的信息:
\n
\nclientID
\nclientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端
\ncleanSession – 清除会话
\n表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;
\n取值true表示保存,false表示不保存
\n\n注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。
\n反之如果数据不会重要,可以取值为false
\n \nkeepAlive —— 间隔时间
\n用于服务端每隔多久就了解一下客户端是否与其保持连接的情况
\nCONNACK – 确认连接请求
\nsessionPresent
\nCONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。
\ncleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。
\n总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次 客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。
\nreturnCode —— 返回码
\n当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况
\n\n
\n\n\n返回码 \n返回码描述 \n \n \n\n\n0 \n成功连接 \n \n\n1 \n连接被服务端拒绝,原因是不支持客户端的MQTT协议版本 \n \n\n2 \n连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。 \n \n\n3 \n连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。 \n \n\n4 \n连接被服务端拒绝,原因是用户名或密码无效。 \n \n\n5 \n连接被服务端拒绝,原因是客户端未被授权连接到此服务端。 \n \n \n
\n
\nESP8266连接MQTT服务端 http://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883
\n接下来使用PubSubClient
库(去GitHub 下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。
\n\n配置WiFi
\n \n创建mqtt客户端对象(间接创建)
\n1 2 WiFiClient wifiClient; PubSubClient mqttClient (wifiClient) ;
\n \n配置客户端要连接哪个服务端(绑定服务端)
\n1 2 3 String mqttServer = "test.ranye-iot.net" ; const int port = 1883 ;mqttClient.setServer(mqttServer, port);
\n \n连接服务端
\n1 2 3 4 String clientID = "esp8266-" + WiFi.macAddress(); mqttClient.connect(clientId.c_str())
\n \n \n\n连接成功后保持 “心跳”,否则…(可以尝试继续连接)
\n1 2 3 4 5 6 7 void loop () { if (mqttClient.connected()) { mqttClient.loop(); } else { connectMQTTServer(); } }
\n \n \n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <ESP8266WiFi.h> #include <PubSubClient.h> const char * ssid = "rbook" ;const char * password = "12345678" ;const char * mqttServer = "test.ranye-iot.net" ; WiFiClient wifiClient; PubSubClient mqttClient (wifiClient) ; void setup () { Serial.begin(9600 ); WiFi.mode(WIFI_STA); connectWifi(); mqttClient.setServer(mqttServer, 1883 ); connectMQTTServer(); } void loop () { if (mqttClient.connected()) { mqttClient.loop(); } else { connectMQTTServer(); } } void connectMQTTServer () { String clientId = "esp8266-" + WiFi.macAddress(); if (mqttClient.connect(clientId.c_str())) { Serial.println("MQTT Server Connected." ); Serial.println("Server Address: " ); Serial.println(mqttServer); Serial.println("ClientId:" ); Serial.println(clientId); } else { Serial.print("MQTT Server Connect Failed. Client State:" ); Serial.println(mqttClient.state()); delay(3000 ); } } void connectWifi () { WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000 ); Serial.print("." ); } Serial.println("" ); Serial.println("WiFi Connected!" ); Serial.println("" ); }
\n"},{"title":"esp8266(8)-OTA上传代码","abbrlink":"1b6a7930","date":"2021-08-05T05:00:00.000Z","description":"esp8266如何实现无线上传代码","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n所谓OTA,就是Over-The-Air的缩写,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。\n\n\n\n在这一节课程里,我们将分步骤向您介绍如何实现ESP8266的OTA操作。\n\n## 1、通过数据线上传初始示例程序\n\n首先,请将以下示例程序通过Arduino IDE上传到ESP8266。\n\n```c\n#include \n#include \n#include \n \n// 闪烁时间间隔(秒)\nconst int blinkInterval = 2; \n \n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"taichimaker\";\nconst char* password = \"12345678\";\n \nTicker ticker;\n \nvoid setup() {\n Serial.begin(9600); \n Serial.println(\"\");\n pinMode(LED_BUILTIN, OUTPUT);\n \n ticker.attach(blinkInterval, tickerCount); // 设置Ticker对象\n \n connectWifi();\n \n // OTA设置并启动\n ArduinoOTA.setHostname(\"ESP8266\");\n ArduinoOTA.setPassword(\"12345678\");\n ArduinoOTA.begin();\n \n Serial.println(\"OTA ready\");\n}\nvoid loop() {\n ArduinoOTA.handle();\n}\n \n// 在Tinker对象控制下,此函数将会定时执行。\nvoid tickerCount(){\n digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));\n}\n \nvoid connectWifi(){\n //开始连接wifi\n WiFi.begin(ssid, password);\n \n //等待WiFi连接,连接成功打印IP\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.print(\"IP address:\\t\"); \n Serial.println(WiFi.localIP()); \n}\n```\n\n\n\n## 2、通过Arduino IDE正确选择OTA端口\n\n程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。\n\n如下图所示:\n\n[![esp8266 OTA 端口选择](http://www.taichi-maker.com/wp-content/uploads/2020/07/esp8266_select_port-1024x547.jpg)](http://www.taichi-maker.com/wp-content/uploads/2020/07/esp8266_select_port.jpg)\n\n\n\n## 3、认证并上传程序\n\n\n\n点击Arduino IDE的”上传”按钮后, IDE将会弹出对话框让用户输入OTA上传密码。请根据示例程序中的`setPassword`函数所设置的信息来输入密码。完成密码输入后,点击确定。如果密码无误,您将看到程序开始上传。\n\n程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。\n\n\n\n## 4、OTA的局限性\n\n\n\n**1. 程序占用空间变大**\n在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。\n\n**2. Arduino IDE无法通过OTA端口与开发板进行串口通讯**\n当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。\n\n**3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi**\n若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。\n\n","source":"_posts/esp8266/8-OTA上传代码.md","raw":"---\ntitle: esp8266(8)-OTA上传代码\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: 1b6a7930\ndate: 2021-08-05 13:00:00\ndescription: esp8266如何实现无线上传代码\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n所谓OTA,就是Over-The-Air的缩写,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。\n\n\n\n在这一节课程里,我们将分步骤向您介绍如何实现ESP8266的OTA操作。\n\n## 1、通过数据线上传初始示例程序\n\n首先,请将以下示例程序通过Arduino IDE上传到ESP8266。\n\n```c\n#include \n#include \n#include \n \n// 闪烁时间间隔(秒)\nconst int blinkInterval = 2; \n \n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"taichimaker\";\nconst char* password = \"12345678\";\n \nTicker ticker;\n \nvoid setup() {\n Serial.begin(9600); \n Serial.println(\"\");\n pinMode(LED_BUILTIN, OUTPUT);\n \n ticker.attach(blinkInterval, tickerCount); // 设置Ticker对象\n \n connectWifi();\n \n // OTA设置并启动\n ArduinoOTA.setHostname(\"ESP8266\");\n ArduinoOTA.setPassword(\"12345678\");\n ArduinoOTA.begin();\n \n Serial.println(\"OTA ready\");\n}\nvoid loop() {\n ArduinoOTA.handle();\n}\n \n// 在Tinker对象控制下,此函数将会定时执行。\nvoid tickerCount(){\n digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));\n}\n \nvoid connectWifi(){\n //开始连接wifi\n WiFi.begin(ssid, password);\n \n //等待WiFi连接,连接成功打印IP\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.print(\"IP address:\\t\"); \n Serial.println(WiFi.localIP()); \n}\n```\n\n\n\n## 2、通过Arduino IDE正确选择OTA端口\n\n程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。\n\n如下图所示:\n\n[![esp8266 OTA 端口选择](http://www.taichi-maker.com/wp-content/uploads/2020/07/esp8266_select_port-1024x547.jpg)](http://www.taichi-maker.com/wp-content/uploads/2020/07/esp8266_select_port.jpg)\n\n\n\n## 3、认证并上传程序\n\n\n\n点击Arduino IDE的”上传”按钮后, IDE将会弹出对话框让用户输入OTA上传密码。请根据示例程序中的`setPassword`函数所设置的信息来输入密码。完成密码输入后,点击确定。如果密码无误,您将看到程序开始上传。\n\n程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。\n\n\n\n## 4、OTA的局限性\n\n\n\n**1. 程序占用空间变大**\n在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。\n\n**2. Arduino IDE无法通过OTA端口与开发板进行串口通讯**\n当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。\n\n**3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi**\n若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。\n\n","slug":"esp8266/8-OTA上传代码","published":1,"updated":"2021-09-11T07:55:01.650Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6py0049akve15r5eo74","content":"所谓OTA,就是Over-The-Air的缩写,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。
\n在这一节课程里,我们将分步骤向您介绍如何实现ESP8266的OTA操作。
\n1、通过数据线上传初始示例程序 首先,请将以下示例程序通过Arduino IDE上传到ESP8266。
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 #include <ESP8266WiFi.h> #include <ArduinoOTA.h> #include <Ticker.h> const int blinkInterval = 2 ; const char * ssid = "taichimaker" ;const char * password = "12345678" ; Ticker ticker; void setup () { Serial.begin(9600 ); Serial.println("" ); pinMode(LED_BUILTIN, OUTPUT); ticker.attach(blinkInterval, tickerCount); connectWifi(); ArduinoOTA.setHostname("ESP8266" ); ArduinoOTA.setPassword("12345678" ); ArduinoOTA.begin(); Serial.println("OTA ready" ); } void loop () { ArduinoOTA.handle(); } void tickerCount () { digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); } void connectWifi () { WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000 ); Serial.print("." ); } Serial.println("" ); Serial.println("WiFi Connected!" ); Serial.print("IP address:\\t" ); Serial.println(WiFi.localIP()); }
\n2、通过Arduino IDE正确选择OTA端口 程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。
\n如下图所示:
\n
\n3、认证并上传程序 点击Arduino IDE的”上传”按钮后, IDE将会弹出对话框让用户输入OTA上传密码。请根据示例程序中的setPassword
函数所设置的信息来输入密码。完成密码输入后,点击确定。如果密码无误,您将看到程序开始上传。
\n程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。
\n4、OTA的局限性 1. 程序占用空间变大 在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。
\n2. Arduino IDE无法通过OTA端口与开发板进行串口通讯 当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。
\n3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi 若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"所谓OTA,就是Over-The-Air的缩写,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。
\n在这一节课程里,我们将分步骤向您介绍如何实现ESP8266的OTA操作。
\n1、通过数据线上传初始示例程序 首先,请将以下示例程序通过Arduino IDE上传到ESP8266。
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 52 #include <ESP8266WiFi.h> #include <ArduinoOTA.h> #include <Ticker.h> const int blinkInterval = 2 ; const char * ssid = "taichimaker" ;const char * password = "12345678" ; Ticker ticker; void setup () { Serial.begin(9600 ); Serial.println("" ); pinMode(LED_BUILTIN, OUTPUT); ticker.attach(blinkInterval, tickerCount); connectWifi(); ArduinoOTA.setHostname("ESP8266" ); ArduinoOTA.setPassword("12345678" ); ArduinoOTA.begin(); Serial.println("OTA ready" ); } void loop () { ArduinoOTA.handle(); } void tickerCount () { digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); } void connectWifi () { WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000 ); Serial.print("." ); } Serial.println("" ); Serial.println("WiFi Connected!" ); Serial.print("IP address:\\t" ); Serial.println(WiFi.localIP()); }
\n2、通过Arduino IDE正确选择OTA端口 程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。
\n如下图所示:
\n
\n3、认证并上传程序 点击Arduino IDE的”上传”按钮后, IDE将会弹出对话框让用户输入OTA上传密码。请根据示例程序中的setPassword
函数所设置的信息来输入密码。完成密码输入后,点击确定。如果密码无误,您将看到程序开始上传。
\n程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。
\n4、OTA的局限性 1. 程序占用空间变大 在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。
\n2. Arduino IDE无法通过OTA端口与开发板进行串口通讯 当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。
\n3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi 若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。
\n"},{"title":"esp8266(7)-多任务执行","abbrlink":"c25015c5","date":"2021-08-04T01:00:00.000Z","description":"esp8266如何同时执行多个任务","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n通过Ticker库可以让esp8266同时执行多个任务\n\n\n\n## 1、如何实现\n\n示例:在实现一个呼吸灯的同时,在串口监视器打印一些字符信息\n\n1. 创建 `Ticker`对象\n\n ```c\n Ticker ticker;\n ```\n\n2. 定时执行某个函数\n\n ```c\n ticker.attach(time, func); //time单位为s,func为函数名\n ticker.attach_ms(time, func); //time单位为ms,func为函数名\n ```\n\n \n\n```c\n#include \n\nTicker ticker;// 建立Ticker用于实现定时功能\nint count; // 计数用变量\n\nvoid setup() {\n Serial.begin(9600);\n pinMode(LED_BUILTIN, OUTPUT);\n\n // attach函数的第一个参数是控制定时间隔的变量。该参数的单位为秒。第二个参数是\n // 定时执行的函数名称。\n ticker.attach(1, sayHi);\n}\n\nvoid loop() {\n // 用LED呼吸灯效果来演示在Tinker对象控制下,ESP8266可以定时执行其它任务\n for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {\n analogWrite(LED_BUILTIN, fadeValue);\n delay(10);\n }\n\n for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {\n analogWrite(LED_BUILTIN, fadeValue);\n delay(10);\n }\n delay(3000);\n}\n\n// 在Tinker对象控制下,此函数将会定时执行。\nvoid sayHi(){\n count++;\n Serial.print(\"Hi \");\n Serial.println(count);\n}\n```\n\n\n\n## 2、其他操作\n\n\n\n- 停止执行定时任务:ticker.detach()\n\n- 向定时调用函数传递参数:ticker.attach(1, sayHi, 8)\n\n 【注意】:\n\n attach函数所能传递的参数最多只有一个\n\n 该参数仅能是以下类型中的一种:char, short, int, float, void\\*, char\\*\n\n- **利用多个Ticker对象可以让ESP8266处理多任务**\n\n\n\n","source":"_posts/esp8266/7-多任务执行.md","raw":"---\ntitle: esp8266(7)-多任务执行\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: c25015c5\ndate: 2021-08-04 09:00:00\ndescription: esp8266如何同时执行多个任务\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n通过Ticker库可以让esp8266同时执行多个任务\n\n\n\n## 1、如何实现\n\n示例:在实现一个呼吸灯的同时,在串口监视器打印一些字符信息\n\n1. 创建 `Ticker`对象\n\n ```c\n Ticker ticker;\n ```\n\n2. 定时执行某个函数\n\n ```c\n ticker.attach(time, func); //time单位为s,func为函数名\n ticker.attach_ms(time, func); //time单位为ms,func为函数名\n ```\n\n \n\n```c\n#include \n\nTicker ticker;// 建立Ticker用于实现定时功能\nint count; // 计数用变量\n\nvoid setup() {\n Serial.begin(9600);\n pinMode(LED_BUILTIN, OUTPUT);\n\n // attach函数的第一个参数是控制定时间隔的变量。该参数的单位为秒。第二个参数是\n // 定时执行的函数名称。\n ticker.attach(1, sayHi);\n}\n\nvoid loop() {\n // 用LED呼吸灯效果来演示在Tinker对象控制下,ESP8266可以定时执行其它任务\n for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {\n analogWrite(LED_BUILTIN, fadeValue);\n delay(10);\n }\n\n for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {\n analogWrite(LED_BUILTIN, fadeValue);\n delay(10);\n }\n delay(3000);\n}\n\n// 在Tinker对象控制下,此函数将会定时执行。\nvoid sayHi(){\n count++;\n Serial.print(\"Hi \");\n Serial.println(count);\n}\n```\n\n\n\n## 2、其他操作\n\n\n\n- 停止执行定时任务:ticker.detach()\n\n- 向定时调用函数传递参数:ticker.attach(1, sayHi, 8)\n\n 【注意】:\n\n attach函数所能传递的参数最多只有一个\n\n 该参数仅能是以下类型中的一种:char, short, int, float, void\\*, char\\*\n\n- **利用多个Ticker对象可以让ESP8266处理多任务**\n\n\n\n","slug":"esp8266/7-多任务执行","published":1,"updated":"2021-09-11T07:54:50.150Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6py004cakve0g6zci7t","content":"通过Ticker库可以让esp8266同时执行多个任务
\n1、如何实现 示例:在实现一个呼吸灯的同时,在串口监视器打印一些字符信息
\n\n创建 Ticker
对象
\n \n \n定时执行某个函数
\n1 2 ticker.attach(time, func); ticker.attach_ms(time, func);
\n \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <Ticker.h> Ticker ticker; int count; void setup () { Serial.begin(9600 ); pinMode(LED_BUILTIN, OUTPUT); ticker.attach(1 , sayHi); } void loop () { for (int fadeValue = 0 ; fadeValue <= 1023 ; fadeValue += 5 ) { analogWrite(LED_BUILTIN, fadeValue); delay(10 ); } for (int fadeValue = 1023 ; fadeValue >= 0 ; fadeValue -= 5 ) { analogWrite(LED_BUILTIN, fadeValue); delay(10 ); } delay(3000 ); } void sayHi () { count++; Serial.print("Hi " ); Serial.println(count); }
\n2、其他操作 \n停止执行定时任务:ticker.detach()
\n \n向定时调用函数传递参数:ticker.attach(1, sayHi, 8)
\n【注意】:
\nattach函数所能传递的参数最多只有一个
\n该参数仅能是以下类型中的一种:char, short, int, float, void*, char*
\n \n利用多个Ticker对象可以让ESP8266处理多任务
\n \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"通过Ticker库可以让esp8266同时执行多个任务
\n1、如何实现 示例:在实现一个呼吸灯的同时,在串口监视器打印一些字符信息
\n\n创建 Ticker
对象
\n \n \n定时执行某个函数
\n1 2 ticker.attach(time, func); ticker.attach_ms(time, func);
\n \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <Ticker.h> Ticker ticker; int count; void setup () { Serial.begin(9600 ); pinMode(LED_BUILTIN, OUTPUT); ticker.attach(1 , sayHi); } void loop () { for (int fadeValue = 0 ; fadeValue <= 1023 ; fadeValue += 5 ) { analogWrite(LED_BUILTIN, fadeValue); delay(10 ); } for (int fadeValue = 1023 ; fadeValue >= 0 ; fadeValue -= 5 ) { analogWrite(LED_BUILTIN, fadeValue); delay(10 ); } delay(3000 ); } void sayHi () { count++; Serial.print("Hi " ); Serial.println(count); }
\n2、其他操作 \n停止执行定时任务:ticker.detach()
\n \n向定时调用函数传递参数:ticker.attach(1, sayHi, 8)
\n【注意】:
\nattach函数所能传递的参数最多只有一个
\n该参数仅能是以下类型中的一种:char, short, int, float, void*, char*
\n \n利用多个Ticker对象可以让ESP8266处理多任务
\n \n \n"},{"title":"esp8266(6)-WiFi配网","abbrlink":"baad5185","date":"2021-08-04T00:00:00.000Z","description":"使用手机等终端设备辅助esp8266进行联网","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n之前nodeMCU一直都要用代码进行连接WiFi的操作,但这样很不方便,如何使用电脑或手机等设备来实时扫描周围WiFi进行配网?\n\n这里主要用到 `WiFiManager`库,可以去太极创客[下载](https://github.com/taichi-maker/WiFiManager/archive/refs/heads/master.zip)并加载到ArduinoIDE中。\n\n\n\n## 1、清除WiFi信息\n\n使用前要先清除掉此前连接过的WiFi信息,不然一上电nodeMCU就直接连接上之前的WiFi了\n\n只需2个步骤:\n\n1. 创建WiFiManager对象\n\n2. 清除WiFi信息\n\n ```c\n WiFiManager wifiManager;\t\t//创建对象\n wifiManager.resetSettings();\t//清除WiFi信息\n ```\n\n完整code\n\n```c\n#include \n#include \n#include \n#include \n \nvoid setup() {\n Serial.begin(9600); \n // 建立WiFiManager对象\n WiFiManager wifiManager;\n \n // 清除ESP8266所存储的WiFi连接信息以便测试WiFiManager工作效果\n wifiManager.resetSettings();\n Serial.println(\"ESP8266 WiFi Settings Cleared\");\n}\n \nvoid loop() {}\n```\n\n\n\n\n\n## 2、WiFi配网\n\n1. 创建WiFiManager对象\n\n2. 开启接入点模式(就是在开发板上开个热点,让周围设备连接)\n\n ```c\n wifiManager.autoConnect(\"热点名称\"[, \"密码\"]);\n ```\n\n3. 接下来就可以用手机、电脑配网\n\n (1)用手机(或电脑)连接开发板的热点\n\n (2)连接后会自动跳转到一个网页(网页地址就是开发板ip),然后手动选择让开发板连接周围WiFi\n\n (3)开发板连接上周围的WiFi后会自动关闭热点,连接不上就会再开启热点,重新配网\n\n (4)【注意1】:连接后不能主动更换为其他WiFi,除非该WiFi断开了,开发板才会再次打开热点,否则如要更换WiFi需要[擦除已连接的WiFi信息](#1、清除WiFi信息)\n\n (5)【注意2】:如果连接了校园网WiFi,但需要登录才能用,此时也不能主动断开WiFi去选择其他能用的WiFi,也需要擦除已连接的WiFi信息。\n\n4. 判断是否配网成功\n\n 可以在串口监视屏打印连接上的WiFi名称\n\n\n\n完整code:\n\n```c\n#include \n#include \n#include \n#include \n \nvoid setup() {\n Serial.begin(9600); \n // 建立WiFiManager对象\n WiFiManager wifiManager;\n \n // 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称\n wifiManager.autoConnect(\"ESP8266-wifi\");\n \n // 如果您希望该WiFi添加密码,可以使用以下语句:\n // wifiManager.autoConnect(\"ESP8266-wifi\", \"12345678\");\n \n \n // WiFi连接成功后将通过串口监视器输出连接成功信息 \n Serial.println(\"\"); \n Serial.print(\"ESP8266 Connected to \");\n Serial.println(WiFi.SSID()); // 连接上的WiFi名称\n Serial.print(\"IP address:\\t\");\n Serial.println(WiFi.localIP()); // IP\n}\n \nvoid loop() {}\n```\n\n![image-20210811095227391](https://gitee.com/ajream/images/raw/master/img/2021-08-1109-56-45_image-20210811095227391.png)\n\n","source":"_posts/esp8266/6-WiFi配网.md","raw":"---\ntitle: esp8266(6)-WiFi配网\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: baad5185\ndate: 2021-08-04 08:00:00\ndescription: 使用手机等终端设备辅助esp8266进行联网\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n之前nodeMCU一直都要用代码进行连接WiFi的操作,但这样很不方便,如何使用电脑或手机等设备来实时扫描周围WiFi进行配网?\n\n这里主要用到 `WiFiManager`库,可以去太极创客[下载](https://github.com/taichi-maker/WiFiManager/archive/refs/heads/master.zip)并加载到ArduinoIDE中。\n\n\n\n## 1、清除WiFi信息\n\n使用前要先清除掉此前连接过的WiFi信息,不然一上电nodeMCU就直接连接上之前的WiFi了\n\n只需2个步骤:\n\n1. 创建WiFiManager对象\n\n2. 清除WiFi信息\n\n ```c\n WiFiManager wifiManager;\t\t//创建对象\n wifiManager.resetSettings();\t//清除WiFi信息\n ```\n\n完整code\n\n```c\n#include \n#include \n#include \n#include \n \nvoid setup() {\n Serial.begin(9600); \n // 建立WiFiManager对象\n WiFiManager wifiManager;\n \n // 清除ESP8266所存储的WiFi连接信息以便测试WiFiManager工作效果\n wifiManager.resetSettings();\n Serial.println(\"ESP8266 WiFi Settings Cleared\");\n}\n \nvoid loop() {}\n```\n\n\n\n\n\n## 2、WiFi配网\n\n1. 创建WiFiManager对象\n\n2. 开启接入点模式(就是在开发板上开个热点,让周围设备连接)\n\n ```c\n wifiManager.autoConnect(\"热点名称\"[, \"密码\"]);\n ```\n\n3. 接下来就可以用手机、电脑配网\n\n (1)用手机(或电脑)连接开发板的热点\n\n (2)连接后会自动跳转到一个网页(网页地址就是开发板ip),然后手动选择让开发板连接周围WiFi\n\n (3)开发板连接上周围的WiFi后会自动关闭热点,连接不上就会再开启热点,重新配网\n\n (4)【注意1】:连接后不能主动更换为其他WiFi,除非该WiFi断开了,开发板才会再次打开热点,否则如要更换WiFi需要[擦除已连接的WiFi信息](#1、清除WiFi信息)\n\n (5)【注意2】:如果连接了校园网WiFi,但需要登录才能用,此时也不能主动断开WiFi去选择其他能用的WiFi,也需要擦除已连接的WiFi信息。\n\n4. 判断是否配网成功\n\n 可以在串口监视屏打印连接上的WiFi名称\n\n\n\n完整code:\n\n```c\n#include \n#include \n#include \n#include \n \nvoid setup() {\n Serial.begin(9600); \n // 建立WiFiManager对象\n WiFiManager wifiManager;\n \n // 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称\n wifiManager.autoConnect(\"ESP8266-wifi\");\n \n // 如果您希望该WiFi添加密码,可以使用以下语句:\n // wifiManager.autoConnect(\"ESP8266-wifi\", \"12345678\");\n \n \n // WiFi连接成功后将通过串口监视器输出连接成功信息 \n Serial.println(\"\"); \n Serial.print(\"ESP8266 Connected to \");\n Serial.println(WiFi.SSID()); // 连接上的WiFi名称\n Serial.print(\"IP address:\\t\");\n Serial.println(WiFi.localIP()); // IP\n}\n \nvoid loop() {}\n```\n\n![image-20210811095227391](https://gitee.com/ajream/images/raw/master/img/2021-08-1109-56-45_image-20210811095227391.png)\n\n","slug":"esp8266/6-WiFi配网","published":1,"updated":"2021-09-11T07:54:46.084Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q0004eakve6dzd49z7","content":"之前nodeMCU一直都要用代码进行连接WiFi的操作,但这样很不方便,如何使用电脑或手机等设备来实时扫描周围WiFi进行配网?
\n这里主要用到 WiFiManager
库,可以去太极创客下载 并加载到ArduinoIDE中。
\n1、清除WiFi信息 使用前要先清除掉此前连接过的WiFi信息,不然一上电nodeMCU就直接连接上之前的WiFi了
\n只需2个步骤:
\n\n创建WiFiManager对象
\n \n清除WiFi信息
\n1 2 WiFiManager wifiManager;\t\t wifiManager.resetSettings();\t
\n \n \n完整code
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <ESP8266WiFi.h> #include <DNSServer.h> #include <ESP8266WebServer.h> #include <WiFiManager.h> void setup () { Serial.begin(9600 ); WiFiManager wifiManager; wifiManager.resetSettings(); Serial.println("ESP8266 WiFi Settings Cleared" ); } void loop () {}
\n2、WiFi配网 \n创建WiFiManager对象
\n \n开启接入点模式(就是在开发板上开个热点,让周围设备连接)
\n1 wifiManager.autoConnect("热点名称" [, "密码" ]);
\n \n接下来就可以用手机、电脑配网
\n(1)用手机(或电脑)连接开发板的热点
\n(2)连接后会自动跳转到一个网页(网页地址就是开发板ip),然后手动选择让开发板连接周围WiFi
\n(3)开发板连接上周围的WiFi后会自动关闭热点,连接不上就会再开启热点,重新配网
\n(4)【注意1】:连接后不能主动更换为其他WiFi,除非该WiFi断开了,开发板才会再次打开热点,否则如要更换WiFi需要擦除已连接的WiFi信息
\n(5)【注意2】:如果连接了校园网WiFi,但需要登录才能用,此时也不能主动断开WiFi去选择其他能用的WiFi,也需要擦除已连接的WiFi信息。
\n \n判断是否配网成功
\n可以在串口监视屏打印连接上的WiFi名称
\n \n \n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <ESP8266WiFi.h> #include <DNSServer.h> #include <ESP8266WebServer.h> #include <WiFiManager.h> void setup () { Serial.begin(9600 ); WiFiManager wifiManager; wifiManager.autoConnect("ESP8266-wifi" ); Serial.println("" ); Serial.print("ESP8266 Connected to " ); Serial.println(WiFi.SSID()); Serial.print("IP address:\\t" ); Serial.println(WiFi.localIP()); } void loop () {}
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"之前nodeMCU一直都要用代码进行连接WiFi的操作,但这样很不方便,如何使用电脑或手机等设备来实时扫描周围WiFi进行配网?
\n这里主要用到 WiFiManager
库,可以去太极创客下载 并加载到ArduinoIDE中。
\n1、清除WiFi信息 使用前要先清除掉此前连接过的WiFi信息,不然一上电nodeMCU就直接连接上之前的WiFi了
\n只需2个步骤:
\n\n创建WiFiManager对象
\n \n清除WiFi信息
\n1 2 WiFiManager wifiManager;\t\t wifiManager.resetSettings();\t
\n \n \n完整code
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <ESP8266WiFi.h> #include <DNSServer.h> #include <ESP8266WebServer.h> #include <WiFiManager.h> void setup () { Serial.begin(9600 ); WiFiManager wifiManager; wifiManager.resetSettings(); Serial.println("ESP8266 WiFi Settings Cleared" ); } void loop () {}
\n2、WiFi配网 \n创建WiFiManager对象
\n \n开启接入点模式(就是在开发板上开个热点,让周围设备连接)
\n1 wifiManager.autoConnect("热点名称" [, "密码" ]);
\n \n接下来就可以用手机、电脑配网
\n(1)用手机(或电脑)连接开发板的热点
\n(2)连接后会自动跳转到一个网页(网页地址就是开发板ip),然后手动选择让开发板连接周围WiFi
\n(3)开发板连接上周围的WiFi后会自动关闭热点,连接不上就会再开启热点,重新配网
\n(4)【注意1】:连接后不能主动更换为其他WiFi,除非该WiFi断开了,开发板才会再次打开热点,否则如要更换WiFi需要擦除已连接的WiFi信息
\n(5)【注意2】:如果连接了校园网WiFi,但需要登录才能用,此时也不能主动断开WiFi去选择其他能用的WiFi,也需要擦除已连接的WiFi信息。
\n \n判断是否配网成功
\n可以在串口监视屏打印连接上的WiFi名称
\n \n \n完整code:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <ESP8266WiFi.h> #include <DNSServer.h> #include <ESP8266WebServer.h> #include <WiFiManager.h> void setup () { Serial.begin(9600 ); WiFiManager wifiManager; wifiManager.autoConnect("ESP8266-wifi" ); Serial.println("" ); Serial.print("ESP8266 Connected to " ); Serial.println(WiFi.SSID()); Serial.print("IP address:\\t" ); Serial.println(WiFi.localIP()); } void loop () {}
\n
\n"},{"title":"MQTT篇(三)关于MQTT的主题","abbrlink":"894e18ee","date":"2021-08-11T04:01:07.000Z","description":"客户端向服务端发布的消息类型、或者要从服务端订阅的消息类型,就是一个个主题,主题在实际应用中用一个字符串来表示","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n## 主题\n\n### 主题基本形式\n\n\t\t主题基本形式就是一个字符串,比如:currentTime,carSpeed\n\n\t\t注意:主题区分大小写、可以用空格(但尽量不要用 )、尽量使用英文字符\n\n\n\n### 主题分级\n\n\t\tMQTT主题各个级别之间可以使用`/`来分隔。如:`Tyler-1/motor/1/speed`\n\n\t\t示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。\n\n\n\n### 主题通配符\n\n\t\t当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用\n\n- 单级通配符: `+`\t\t示例:\n\n ```sh\n home/sensor/+/temperature\n \n # 可以表示:\n # home/sensor/aaa/temperature\n # home/sensor/bbb/temperature\n # ......\n ```\n\n \n\n- 多级通配符 :`#` 示例:\n\n ```sh\n home/sensor/#\n \n # 可以表示:\n # home/sensor/aaa\n # home/sensor/bbb/ccc\n # home/sensor/aaa/ccc\n ```\n\n \n\n### 注意事项\n\n- 以$开始的主题\n\n 以$开始的主题是MQTT服务端**系统保留的特殊主题**,不能随意订阅或者向其发布信息。如:\n\n ```shell\n $SYS/broker/clients/connected\n $SYS/broker/clients/disconnected\n $SYS/broker/clients/total\n $SYS/broker/messages/sent\n $SYS/broker/uptime\n ...\n ```\n\n- 避免使用`/`作为主题的开头\n\n- 主题中尽量**不要使用空格**\n\n- 主题中尽量使用**ASCII字符**\n\n- 建议在主题中嵌入客户端ID\n 【通过主题中的客户端ID内容,可以很容易的了解该主题信息是由哪一台设备所发布的】\n\n \n\n## ESP8266订阅主题\n\n","source":"_posts/esp8266/MQTT篇(三)-主题.md","raw":"---\ntitle: MQTT篇(三)关于MQTT的主题\ntags:\n - 物联网\n - esp8266\ncategories:\n - 硬件学习\n - 物联网开发\nabbrlink: 894e18ee\ndate: 2021-08-11 12:01:07\ndescription: 客户端向服务端发布的消息类型、或者要从服务端订阅的消息类型,就是一个个主题,主题在实际应用中用一个字符串来表示\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n## 主题\n\n### 主题基本形式\n\n\t\t主题基本形式就是一个字符串,比如:currentTime,carSpeed\n\n\t\t注意:主题区分大小写、可以用空格(但尽量不要用 )、尽量使用英文字符\n\n\n\n### 主题分级\n\n\t\tMQTT主题各个级别之间可以使用`/`来分隔。如:`Tyler-1/motor/1/speed`\n\n\t\t示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。\n\n\n\n### 主题通配符\n\n\t\t当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用\n\n- 单级通配符: `+`\t\t示例:\n\n ```sh\n home/sensor/+/temperature\n \n # 可以表示:\n # home/sensor/aaa/temperature\n # home/sensor/bbb/temperature\n # ......\n ```\n\n \n\n- 多级通配符 :`#` 示例:\n\n ```sh\n home/sensor/#\n \n # 可以表示:\n # home/sensor/aaa\n # home/sensor/bbb/ccc\n # home/sensor/aaa/ccc\n ```\n\n \n\n### 注意事项\n\n- 以$开始的主题\n\n 以$开始的主题是MQTT服务端**系统保留的特殊主题**,不能随意订阅或者向其发布信息。如:\n\n ```shell\n $SYS/broker/clients/connected\n $SYS/broker/clients/disconnected\n $SYS/broker/clients/total\n $SYS/broker/messages/sent\n $SYS/broker/uptime\n ...\n ```\n\n- 避免使用`/`作为主题的开头\n\n- 主题中尽量**不要使用空格**\n\n- 主题中尽量使用**ASCII字符**\n\n- 建议在主题中嵌入客户端ID\n 【通过主题中的客户端ID内容,可以很容易的了解该主题信息是由哪一台设备所发布的】\n\n \n\n## ESP8266订阅主题\n\n","slug":"esp8266/MQTT篇(三)-主题","published":1,"updated":"2021-08-29T14:54:46.346Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q1004hakve7ci71f3m","content":"主题 主题基本形式 主题基本形式就是一个字符串,比如:currentTime,carSpeed
\n 注意:主题区分大小写、可以用空格(但尽量不要用 )、尽量使用英文字符
\n主题分级 MQTT主题各个级别之间可以使用/
来分隔。如:Tyler-1/motor/1/speed
\n 示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。
\n主题通配符 当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用
\n\n单级通配符: +
示例:
\n1 2 3 4 5 6 home/sensor/+/temperature
\n \n \n\n注意事项 \nESP8266订阅主题 ","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"主题 主题基本形式 主题基本形式就是一个字符串,比如:currentTime,carSpeed
\n 注意:主题区分大小写、可以用空格(但尽量不要用 )、尽量使用英文字符
\n主题分级 MQTT主题各个级别之间可以使用/
来分隔。如:Tyler-1/motor/1/speed
\n 示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。
\n主题通配符 当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用
\n\n单级通配符: +
示例:
\n1 2 3 4 5 6 home/sensor/+/temperature
\n \n \n\n注意事项 \nESP8266订阅主题 "},{"title":"MQTT篇(二)消息的发布、订阅与取消订阅","abbrlink":"adfcabaa","date":"2021-08-10T02:20:32.000Z","description":"MQTT协议指明了客户端与服务端之间信息交流的方式","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n## 发布信息\n\n1. mqtt客户端连接到服务端后即可发布信息,每条信息必须包含一个主题\n2. 服务端根据主题决定将信息转发给哪些客户端\n\n\n\n![PUBLISH - 发布消息](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-PUBLISH.png)\n\n\n\n发布信息时,会向服务端发送一个**PUBLISH报文**:\n\n![MQTT PUBLISH 报文](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-Publish-Packet.png)\n\ntopicName:主题名\n\nqos:服务质量等级,分0、1、2共三个等级\n\npacketId:报文标识符,用于区别不同报文\n\n\t\t【注意】:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。\n\nretainFlag:保留标志。\n\n\t\t一般情况,客户端订阅某个主题的信息后,服务器不会立刻返回该主题的信息,要等服务器收到新信息时才会返回;\n\n\t\t特殊情况,我们要求客户端订阅一个主题后,服务器就要立刻返回新信息\n\n\n\npayLoad:有效载荷,可以使用mqtt协议发送文本、图片等内容,这些内容是通过payLoad来发送的\n\n\n\ndupFlag:重发标志。当接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。\n\n【注意】重发标志只在QoS级别大于0时使用。\n\n## 订阅主题\n\n当客户端连接到服务端后,除了可以发布消息,也可以接收消息,而客户端要想接收消息,首先要订阅该消息的主题。\n\n客户端是通过向服务端发送**SUBSCRIBE报文**来实现订阅主题\n\n【注意】一个SUBSCRIBE报文可以包含有单个或者多个订阅主题名。\n\nqos:客户端在订阅主题时也可以明确QoS。服务端会根据SUBSCRIBE中的QoS来提供相应的服务保证。\n\nMQTT设备可以通过**“报文标识符”**对MQTT报文进行甄别和管理。\n\n---\n\n**订阅确认:**\n\n服务端接收到客户端的订阅报文后,会向客户端发送**SUBACK报文**确认订阅。\n\n该报文包含**”订阅返回码“**和**”报文标识符“**两个信息\n\n| 返回码 | Return Code Response |\n| :----- | :------------------- |\n| 0 | 订阅成功 – QoS 0 |\n| 1 | 订阅成功- QoS 1 |\n| 2 | 订阅成功- QoS 2 |\n| 128 | 订阅失败 |\n\n---\n\n【注意】针对不同的主题订阅QoS,服务端的返回码会有所不同。\n\n**报文标识符**:MQTT设备可以通过该标识符对报文进行管理。\n\n\n\n## 取消订阅\n\n客户端要取消订阅某主题时,可通过向服务端发送**UNSUBSCRIBE** 报文来实现。\n\n![MQTT-UNSUBSCRIBE-取消订阅报文](http://www.taichi-maker.com/wp-content/uploads/2020/09/unsubscribe_packet.png)\n\nUNSUBSCRIBE报文包含两个重要信息:\n\n1. packetId:“报文标识符”\n2. topic1、topic2、…… :取消订阅的主题的名称\n\n\n\n当服务端接收到UNSUBSCRIBE报文后,会向客户端发送取消订阅确认报文 – UNSUBACK报文。该报文含有客户端所发送的“取消订阅报文标识符”。\n\n客户端接收到UNSUBACK报文后就可以确认取消主题订阅\n\n\n\n## ESP8266发布MQTT消息\n\n```c\n#include \n#include \n#include \n\n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"RBook\";\nconst char* password = \"1719171945678\";\nconst char* mqttServer = \"test.ranye-iot.net\";\n\n// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案\n// http://www.taichi-maker.com/public-mqtt-broker/\n\nTicker ticker;\nWiFiClient wifiClient;\nPubSubClient mqttClient(wifiClient);\n\nint count; // Ticker计数用变量\n\nvoid setup() {\n Serial.begin(9600);\n\n //设置ESP8266工作模式为无线终端模式\n WiFi.mode(WIFI_STA);\n\n // 连接WiFi\n connectWifi();\n\n // 设置MQTT服务器和端口号\n mqttClient.setServer(mqttServer, 1883);\n\n // 连接MQTT服务器\n connectMQTTServer();\n\n // Ticker定时对象\n ticker.attach(1, tickerCount); \n}\n\nvoid loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器\n // 每隔3秒钟发布一次信息\n if (count >= 3){\n pubMQTTmsg();\n count = 0;\n } \n // 保持心跳\n mqttClient.loop();\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n}\n\nvoid tickerCount(){\n count++;\n}\n\nvoid connectMQTTServer(){\n // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)\n String clientId = \"esp8266-\" + WiFi.macAddress();\n\n // 连接MQTT服务器\n if (mqttClient.connect(clientId.c_str())) { \n Serial.println(\"MQTT Server Connected.\");\n Serial.println(\"Server Address: \");\n Serial.println(mqttServer);\n Serial.println(\"ClientId:\");\n Serial.println(clientId);\n } else {\n Serial.print(\"MQTT Server Connect Failed. Client State:\");\n Serial.println(mqttClient.state());\n delay(3000);\n } \n}\n\n// 发布信息\nvoid pubMQTTmsg(){\n static int value; // 客户端发布信息用数字\n\n // 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。\n // 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,\n String topicString = \"Taichi-Maker-Pub-\" + WiFi.macAddress();\n char publishTopic[topicString.length() + 1]; \n strcpy(publishTopic, topicString.c_str());\n\n // 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。\n String messageString = \"Hello World \" + String(value++); \n char publishMsg[messageString.length() + 1]; \n strcpy(publishMsg, messageString.c_str());\n\n // 实现ESP8266向主题发布信息\n if(mqttClient.publish(publishTopic, publishMsg)){\n Serial.println(\"Publish Topic:\");Serial.println(publishTopic);\n Serial.println(\"Publish message:\");Serial.println(publishMsg); \n } else {\n Serial.println(\"Message Publish Failed.\"); \n }\n}\n\n// ESP8266连接wifi\nvoid connectWifi(){\n\n WiFi.begin(ssid, password);\n\n //等待WiFi连接,成功连接后输出成功信息\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.println(\"\"); \n}\n```\n\n","source":"_posts/esp8266/MQTT篇(二)-发布、订阅、取消订阅.md","raw":"---\ntitle: MQTT篇(二)消息的发布、订阅与取消订阅\ntags:\n - 物联网\n - esp8266\ncategories:\n - - 硬件学习\n - 物联网开发\nabbrlink: adfcabaa\ndate: 2021-08-10 10:20:32\ndescription: MQTT协议指明了客户端与服务端之间信息交流的方式\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n## 发布信息\n\n1. mqtt客户端连接到服务端后即可发布信息,每条信息必须包含一个主题\n2. 服务端根据主题决定将信息转发给哪些客户端\n\n\n\n![PUBLISH - 发布消息](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-PUBLISH.png)\n\n\n\n发布信息时,会向服务端发送一个**PUBLISH报文**:\n\n![MQTT PUBLISH 报文](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-Publish-Packet.png)\n\ntopicName:主题名\n\nqos:服务质量等级,分0、1、2共三个等级\n\npacketId:报文标识符,用于区别不同报文\n\n\t\t【注意】:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。\n\nretainFlag:保留标志。\n\n\t\t一般情况,客户端订阅某个主题的信息后,服务器不会立刻返回该主题的信息,要等服务器收到新信息时才会返回;\n\n\t\t特殊情况,我们要求客户端订阅一个主题后,服务器就要立刻返回新信息\n\n\n\npayLoad:有效载荷,可以使用mqtt协议发送文本、图片等内容,这些内容是通过payLoad来发送的\n\n\n\ndupFlag:重发标志。当接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。\n\n【注意】重发标志只在QoS级别大于0时使用。\n\n## 订阅主题\n\n当客户端连接到服务端后,除了可以发布消息,也可以接收消息,而客户端要想接收消息,首先要订阅该消息的主题。\n\n客户端是通过向服务端发送**SUBSCRIBE报文**来实现订阅主题\n\n【注意】一个SUBSCRIBE报文可以包含有单个或者多个订阅主题名。\n\nqos:客户端在订阅主题时也可以明确QoS。服务端会根据SUBSCRIBE中的QoS来提供相应的服务保证。\n\nMQTT设备可以通过**“报文标识符”**对MQTT报文进行甄别和管理。\n\n---\n\n**订阅确认:**\n\n服务端接收到客户端的订阅报文后,会向客户端发送**SUBACK报文**确认订阅。\n\n该报文包含**”订阅返回码“**和**”报文标识符“**两个信息\n\n| 返回码 | Return Code Response |\n| :----- | :------------------- |\n| 0 | 订阅成功 – QoS 0 |\n| 1 | 订阅成功- QoS 1 |\n| 2 | 订阅成功- QoS 2 |\n| 128 | 订阅失败 |\n\n---\n\n【注意】针对不同的主题订阅QoS,服务端的返回码会有所不同。\n\n**报文标识符**:MQTT设备可以通过该标识符对报文进行管理。\n\n\n\n## 取消订阅\n\n客户端要取消订阅某主题时,可通过向服务端发送**UNSUBSCRIBE** 报文来实现。\n\n![MQTT-UNSUBSCRIBE-取消订阅报文](http://www.taichi-maker.com/wp-content/uploads/2020/09/unsubscribe_packet.png)\n\nUNSUBSCRIBE报文包含两个重要信息:\n\n1. packetId:“报文标识符”\n2. topic1、topic2、…… :取消订阅的主题的名称\n\n\n\n当服务端接收到UNSUBSCRIBE报文后,会向客户端发送取消订阅确认报文 – UNSUBACK报文。该报文含有客户端所发送的“取消订阅报文标识符”。\n\n客户端接收到UNSUBACK报文后就可以确认取消主题订阅\n\n\n\n## ESP8266发布MQTT消息\n\n```c\n#include \n#include \n#include \n\n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"RBook\";\nconst char* password = \"1719171945678\";\nconst char* mqttServer = \"test.ranye-iot.net\";\n\n// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案\n// http://www.taichi-maker.com/public-mqtt-broker/\n\nTicker ticker;\nWiFiClient wifiClient;\nPubSubClient mqttClient(wifiClient);\n\nint count; // Ticker计数用变量\n\nvoid setup() {\n Serial.begin(9600);\n\n //设置ESP8266工作模式为无线终端模式\n WiFi.mode(WIFI_STA);\n\n // 连接WiFi\n connectWifi();\n\n // 设置MQTT服务器和端口号\n mqttClient.setServer(mqttServer, 1883);\n\n // 连接MQTT服务器\n connectMQTTServer();\n\n // Ticker定时对象\n ticker.attach(1, tickerCount); \n}\n\nvoid loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器\n // 每隔3秒钟发布一次信息\n if (count >= 3){\n pubMQTTmsg();\n count = 0;\n } \n // 保持心跳\n mqttClient.loop();\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n}\n\nvoid tickerCount(){\n count++;\n}\n\nvoid connectMQTTServer(){\n // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)\n String clientId = \"esp8266-\" + WiFi.macAddress();\n\n // 连接MQTT服务器\n if (mqttClient.connect(clientId.c_str())) { \n Serial.println(\"MQTT Server Connected.\");\n Serial.println(\"Server Address: \");\n Serial.println(mqttServer);\n Serial.println(\"ClientId:\");\n Serial.println(clientId);\n } else {\n Serial.print(\"MQTT Server Connect Failed. Client State:\");\n Serial.println(mqttClient.state());\n delay(3000);\n } \n}\n\n// 发布信息\nvoid pubMQTTmsg(){\n static int value; // 客户端发布信息用数字\n\n // 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。\n // 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,\n String topicString = \"Taichi-Maker-Pub-\" + WiFi.macAddress();\n char publishTopic[topicString.length() + 1]; \n strcpy(publishTopic, topicString.c_str());\n\n // 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。\n String messageString = \"Hello World \" + String(value++); \n char publishMsg[messageString.length() + 1]; \n strcpy(publishMsg, messageString.c_str());\n\n // 实现ESP8266向主题发布信息\n if(mqttClient.publish(publishTopic, publishMsg)){\n Serial.println(\"Publish Topic:\");Serial.println(publishTopic);\n Serial.println(\"Publish message:\");Serial.println(publishMsg); \n } else {\n Serial.println(\"Message Publish Failed.\"); \n }\n}\n\n// ESP8266连接wifi\nvoid connectWifi(){\n\n WiFi.begin(ssid, password);\n\n //等待WiFi连接,成功连接后输出成功信息\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.println(\"\"); \n}\n```\n\n","slug":"esp8266/MQTT篇(二)-发布、订阅、取消订阅","published":1,"updated":"2021-08-29T14:54:42.648Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q2004jakvedojmd1vx","content":"发布信息 \nmqtt客户端连接到服务端后即可发布信息,每条信息必须包含一个主题 \n服务端根据主题决定将信息转发给哪些客户端 \n \n
\n发布信息时,会向服务端发送一个PUBLISH报文 :
\n
\ntopicName:主题名
\nqos:服务质量等级,分0、1、2共三个等级
\npacketId:报文标识符,用于区别不同报文
\n 【注意】:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。
\nretainFlag:保留标志。
\n 一般情况,客户端订阅某个主题的信息后,服务器不会立刻返回该主题的信息,要等服务器收到新信息时才会返回;
\n 特殊情况,我们要求客户端订阅一个主题后,服务器就要立刻返回新信息
\npayLoad:有效载荷,可以使用mqtt协议发送文本、图片等内容,这些内容是通过payLoad来发送的
\ndupFlag:重发标志。当接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。
\n【注意】重发标志只在QoS级别大于0时使用。
\n订阅主题 当客户端连接到服务端后,除了可以发布消息,也可以接收消息,而客户端要想接收消息,首先要订阅该消息的主题。
\n客户端是通过向服务端发送SUBSCRIBE报文 来实现订阅主题
\n【注意】一个SUBSCRIBE报文可以包含有单个或者多个订阅主题名。
\nqos:客户端在订阅主题时也可以明确QoS。服务端会根据SUBSCRIBE中的QoS来提供相应的服务保证。
\nMQTT设备可以通过“报文标识符” 对MQTT报文进行甄别和管理。
\n \n订阅确认:
\n服务端接收到客户端的订阅报文后,会向客户端发送SUBACK报文 确认订阅。
\n该报文包含”订阅返回码“ 和”报文标识符“ 两个信息
\n\n
\n\n\n返回码 \nReturn Code Response \n \n \n\n\n0 \n订阅成功 – QoS 0 \n \n\n1 \n订阅成功- QoS 1 \n \n\n2 \n订阅成功- QoS 2 \n \n\n128 \n订阅失败 \n \n \n
\n
\n \n【注意】针对不同的主题订阅QoS,服务端的返回码会有所不同。
\n报文标识符 :MQTT设备可以通过该标识符对报文进行管理。
\n取消订阅 客户端要取消订阅某主题时,可通过向服务端发送UNSUBSCRIBE 报文来实现。
\n
\nUNSUBSCRIBE报文包含两个重要信息:
\n\npacketId:“报文标识符” \ntopic1、topic2、…… :取消订阅的主题的名称 \n \n当服务端接收到UNSUBSCRIBE报文后,会向客户端发送取消订阅确认报文 – UNSUBACK报文。该报文含有客户端所发送的“取消订阅报文标识符”。
\n客户端接收到UNSUBACK报文后就可以确认取消主题订阅
\nESP8266发布MQTT消息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 #include <ESP8266WiFi.h> #include <PubSubClient.h> #include <Ticker.h> const char * ssid = "RBook" ;const char * password = "1719171945678" ;const char * mqttServer = "test.ranye-iot.net" ;Ticker ticker; WiFiClient wifiClient; PubSubClient mqttClient (wifiClient) ;int count; void setup () { Serial.begin(9600 ); WiFi.mode(WIFI_STA); connectWifi(); mqttClient.setServer(mqttServer, 1883 ); connectMQTTServer(); ticker.attach(1 , tickerCount); } void loop () { if (mqttClient.connected()) { if (count >= 3 ){ pubMQTTmsg(); count = 0 ; } mqttClient.loop(); } else { connectMQTTServer(); } } void tickerCount () { count++; } void connectMQTTServer () { String clientId = "esp8266-" + WiFi.macAddress(); if (mqttClient.connect(clientId.c_str())) { Serial.println("MQTT Server Connected." ); Serial.println("Server Address: " ); Serial.println(mqttServer); Serial.println("ClientId:" ); Serial.println(clientId); } else { Serial.print("MQTT Server Connect Failed. Client State:" ); Serial.println(mqttClient.state()); delay(3000 ); } } void pubMQTTmsg () { static int value; String topicString = "Taichi-Maker-Pub-" + WiFi.macAddress(); char publishTopic[topicString.length() + 1 ]; strcpy (publishTopic, topicString.c_str()); String messageString = "Hello World " + String(value++); char publishMsg[messageString.length() + 1 ]; strcpy (publishMsg, messageString.c_str()); if (mqttClient.publish(publishTopic, publishMsg)){ Serial.println("Publish Topic:" );Serial.println(publishTopic); Serial.println("Publish message:" );Serial.println(publishMsg); } else { Serial.println("Message Publish Failed." ); } } void connectWifi () { WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000 ); Serial.print("." ); } Serial.println("" ); Serial.println("WiFi Connected!" ); Serial.println("" ); }
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"发布信息 \nmqtt客户端连接到服务端后即可发布信息,每条信息必须包含一个主题 \n服务端根据主题决定将信息转发给哪些客户端 \n \n
\n发布信息时,会向服务端发送一个PUBLISH报文 :
\n
\ntopicName:主题名
\nqos:服务质量等级,分0、1、2共三个等级
\npacketId:报文标识符,用于区别不同报文
\n 【注意】:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。
\nretainFlag:保留标志。
\n 一般情况,客户端订阅某个主题的信息后,服务器不会立刻返回该主题的信息,要等服务器收到新信息时才会返回;
\n 特殊情况,我们要求客户端订阅一个主题后,服务器就要立刻返回新信息
\npayLoad:有效载荷,可以使用mqtt协议发送文本、图片等内容,这些内容是通过payLoad来发送的
\ndupFlag:重发标志。当接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。
\n【注意】重发标志只在QoS级别大于0时使用。
\n订阅主题 当客户端连接到服务端后,除了可以发布消息,也可以接收消息,而客户端要想接收消息,首先要订阅该消息的主题。
\n客户端是通过向服务端发送SUBSCRIBE报文 来实现订阅主题
\n【注意】一个SUBSCRIBE报文可以包含有单个或者多个订阅主题名。
\nqos:客户端在订阅主题时也可以明确QoS。服务端会根据SUBSCRIBE中的QoS来提供相应的服务保证。
\nMQTT设备可以通过“报文标识符” 对MQTT报文进行甄别和管理。
\n \n订阅确认:
\n服务端接收到客户端的订阅报文后,会向客户端发送SUBACK报文 确认订阅。
\n该报文包含”订阅返回码“ 和”报文标识符“ 两个信息
\n\n
\n\n\n返回码 \nReturn Code Response \n \n \n\n\n0 \n订阅成功 – QoS 0 \n \n\n1 \n订阅成功- QoS 1 \n \n\n2 \n订阅成功- QoS 2 \n \n\n128 \n订阅失败 \n \n \n
\n
\n \n【注意】针对不同的主题订阅QoS,服务端的返回码会有所不同。
\n报文标识符 :MQTT设备可以通过该标识符对报文进行管理。
\n取消订阅 客户端要取消订阅某主题时,可通过向服务端发送UNSUBSCRIBE 报文来实现。
\n
\nUNSUBSCRIBE报文包含两个重要信息:
\n\npacketId:“报文标识符” \ntopic1、topic2、…… :取消订阅的主题的名称 \n \n当服务端接收到UNSUBSCRIBE报文后,会向客户端发送取消订阅确认报文 – UNSUBACK报文。该报文含有客户端所发送的“取消订阅报文标识符”。
\n客户端接收到UNSUBACK报文后就可以确认取消主题订阅
\nESP8266发布MQTT消息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 #include <ESP8266WiFi.h> #include <PubSubClient.h> #include <Ticker.h> const char * ssid = "RBook" ;const char * password = "1719171945678" ;const char * mqttServer = "test.ranye-iot.net" ;Ticker ticker; WiFiClient wifiClient; PubSubClient mqttClient (wifiClient) ;int count; void setup () { Serial.begin(9600 ); WiFi.mode(WIFI_STA); connectWifi(); mqttClient.setServer(mqttServer, 1883 ); connectMQTTServer(); ticker.attach(1 , tickerCount); } void loop () { if (mqttClient.connected()) { if (count >= 3 ){ pubMQTTmsg(); count = 0 ; } mqttClient.loop(); } else { connectMQTTServer(); } } void tickerCount () { count++; } void connectMQTTServer () { String clientId = "esp8266-" + WiFi.macAddress(); if (mqttClient.connect(clientId.c_str())) { Serial.println("MQTT Server Connected." ); Serial.println("Server Address: " ); Serial.println(mqttServer); Serial.println("ClientId:" ); Serial.println(clientId); } else { Serial.print("MQTT Server Connect Failed. Client State:" ); Serial.println(mqttClient.state()); delay(3000 ); } } void pubMQTTmsg () { static int value; String topicString = "Taichi-Maker-Pub-" + WiFi.macAddress(); char publishTopic[topicString.length() + 1 ]; strcpy (publishTopic, topicString.c_str()); String messageString = "Hello World " + String(value++); char publishMsg[messageString.length() + 1 ]; strcpy (publishMsg, messageString.c_str()); if (mqttClient.publish(publishTopic, publishMsg)){ Serial.println("Publish Topic:" );Serial.println(publishTopic); Serial.println("Publish message:" );Serial.println(publishMsg); } else { Serial.println("Message Publish Failed." ); } } void connectWifi () { WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000 ); Serial.print("." ); } Serial.println("" ); Serial.println("WiFi Connected!" ); Serial.println("" ); }
\n"},{"title":"SpringMVC(一)快速入门","description":"springMVC概述、组件","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"890c8bdb","date":"2021-09-01T11:31:26.000Z","_content":"\n\n\n\n\n\n\n## 概述\n\nSpringMVC是主流的实现MVC设计模式的企业级开发框架,是spring框架的一个子模块,无需整合,开发起来方便\n\n- 与Spring兼容性好,无缝结合\n\n- 约定优于配置\n\n\n\n## MVC设计模式\n\n\n\nMVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。\n\n使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。\n\n\n\n{% note info flat %}\nController用于接收客户端请求,调用Model生成业务数据,传递给View\n{% endnote %}\n\n\n\nspringMVC架构\n\n![springMVC架构](https://gitee.com/ajream/images/raw/master/img/20210902234928_1-1601161F914292.png)\n\n\n\n## 核心组件\n\nDispatcherServlet:\n\nMVC框架是围绕`DispatcherServlet`设计的,它处理所有的HTTP请求和响应。 **Spring Web MVC DispatcherServlet**的请求处理工作流如下图所示:\n\n![img](http://www.yiibai.com/uploads/images/201701/18/451110124_66519.png)\n\n以下是对应于到`DispatcherServlet`的传入HTTP请求的事件顺序:\n\n1. 在接收到HTTP请求后,`DispatcherServlet`会查询`HandlerMapping`以调用相应的`Controller`。\n2. `Controller`接受请求并根据使用的`GET`或`POST`方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给`DispatcherServlet`\n3. `DispatcherServlet`将从`ViewResolver`获取请求的定义视图。\n4. 当视图完成,`DispatcherServlet`将模型数据传递到最终的视图,并在浏览器上呈现。\n\n所有上述组件,即: `HandlerMapping`,`Controller`和`ViewResolver`是`WebApplicationContext`的一部分,它是普通`ApplicationContext`的扩展,带有Web应用程序所需的一些额外功能。\n\n\n\n{%folding blue, 配置%}\n\n\n\n需要通过使用web.xml文件中的URL映射来映射希望DispatcherServlet处理的请求。\n\n下面是一个示例来显示HelloWeb DispatcherServlet示例的声明和映射:\n\n\n\n```xml\n\n\n Spring MVC Application \n\n \n HelloWeb \n \n org.springframework.web.servlet.DispatcherServlet\n \n 1 \n \n\n \n HelloWeb \n *.jsp \n \n\n \n```\n\n\n\n`web.xml`文件将保存Web应用程序的`WebContent/WEB-INF`目录。\n\n在`HelloWeb DispatcherServlet`初始化时,框架将尝试从位于应用程序的`WebContent/WEB-INF`目录中的名为`[servlet-name]-servlet.xml`的文件加载应用程序上下文。在这个示例中,使用的文件将是`HelloWeb-servlet.xml`。\n\n接下来,``标记指示哪些`URL`将由`DispatcherServlet`处理。 这里所有以`.jsp`结尾的HTTP请求都将由`HelloWeb DispatcherServlet`处理。\n\n\n\n如果不想使用默认文件名为`[servlet-name]-servlet.xml`和默认位置为`WebContent/WEB-INF`,可以通过在`web.xml`文件中添加`servlet`侦听器`ContextLoaderListener`来自定义此文件名和位置 如下\n\n\n\n```xml\n\n\n\n....\n\n contextConfigLocation \n /WEB-INF/HelloWeb-servlet.xml \n \n\n\n \n org.springframework.web.context.ContextLoaderListener\n \n \n\n```\n\n\n\n{%endfolding%}\n\n## 回顾创建一个Servlet程序\n\n[项目地址springMVC-01-Servlet](https://codechina.csdn.net/m0_46079750/springmvc)\n\n\n\n{% tabs 第一个springMVC程序%}\n\n\n\n\n\n```xml\n\n \n org.springframework \n spring-webmvc \n 5.2.9.RELEASE \n \n \n junit \n junit \n 4.13 \n \n \n javax.servlet \n servlet-api \n 2.5 \n \n \n javax.servlet.jsp \n jsp-api \n 2.2 \n \n \n javax.servlet \n jstl \n 1.2 \n \n \n```\n\n\n\n{%folding blue, note:普通maven项目转为web项目%}\n\n1. 右键该项目,选择 `Add Framework support `\n\n ![image-20210901210441382](https://gitee.com/ajream/images/raw/master/img/20210901210444_image-20210901210441382.png)\n\n2. 选择 `Web Application`-> `OK`\n\n ![image-20210901210605521](https://gitee.com/ajream/images/raw/master/img/20210901210606_image-20210901210605521.png)\n\n3. 结果生成 `web`文件夹,说明成功了\n\n ![image-20210901210837382](https://gitee.com/ajream/images/raw/master/img/20210901210838_image-20210901210837382.png)\n\n\n\n\n\n{%endfolding%}\n\n\n\n\n\n\n\n1. 实现HttpServlet接口\n\n ```java\n package com.ajream.sevlet;\n \n import javax.servlet.ServletException;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n public class HelloServlet extends HttpServlet {\n \n @Override\n protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n // 1. 获取前端参数\n String method = req.getParameter(\"method\");\n if(method.equals(\"add\")){\n req.getSession().setAttribute(\"msg\", \"执行了add方法\");\n }\n if(method.equals(\"delete\")){\n req.getSession().setAttribute(\"msg\", \"执行了delete方法\");\n }\n // 2. 调用业务层\n // 3. 视图转发或者重定向\n req.getRequestDispatcher(\"WEB-INF/jsp/test.jsp\").forward(req,resp);\n \n }\n @Override\n protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n doGet(req, resp);\n }\n }\n \n ```\n\n2. 新建视图文件 `web/WEB-INF/jsp/test.jsp`\n\n ```html\n \n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n Title \n \n \n ${msg}\n \n \n \n ```\n\n3. 配置 `web/WEB-INF/web.xml`\n\n ```xml\n \n \n \n hello \n com.ajream.sevlet.HelloServlet \n \n \n \n hello \n /hello \n \n \n \n \n \n \n \n index.jsp \n \n \n ```\n\n4. 新建表单 `web/WEB-INF/jsp/form.jsp`\n\n ```html\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n Title \n \n \n \n \n \n \n \n \n ```\n\n \n\n\n\n\n\n\n\n![image-20210902000157563](https://gitee.com/ajream/images/raw/master/img/20210902000200_image-20210902000157563.png)\n\n\n\n![image-20210902000426095](https://gitee.com/ajream/images/raw/master/img/20210902000427_image-20210902000426095.png)\n\n\n\n在 `Application context` 输入: `/`\n\n![image-20210902000536524](https://gitee.com/ajream/images/raw/master/img/20210902000537_image-20210902000536524.png)\n\n\n\n\n\n\n\n\n\n在浏览器输入:`http://localhost:8083/hello?method=add`,(端口号可能不同)\n\n结果如下:\n\n![image-20210902000703371](https://gitee.com/ajream/images/raw/master/img/20210902000704_image-20210902000703371.png)\n\n\n\n\n\n{%endtabs%}\n\n\n\n","source":"_posts/SpringMVC/springMVC(一)快速入门.md","raw":"---\ntitle: SpringMVC(一)快速入门\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: springMVC概述、组件\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: 890c8bdb\ndate: 2021-09-01 19:31:26\n---\n\n\n\n\n\n\n\n## 概述\n\nSpringMVC是主流的实现MVC设计模式的企业级开发框架,是spring框架的一个子模块,无需整合,开发起来方便\n\n- 与Spring兼容性好,无缝结合\n\n- 约定优于配置\n\n\n\n## MVC设计模式\n\n\n\nMVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。\n\n使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。\n\n\n\n{% note info flat %}\nController用于接收客户端请求,调用Model生成业务数据,传递给View\n{% endnote %}\n\n\n\nspringMVC架构\n\n![springMVC架构](https://gitee.com/ajream/images/raw/master/img/20210902234928_1-1601161F914292.png)\n\n\n\n## 核心组件\n\nDispatcherServlet:\n\nMVC框架是围绕`DispatcherServlet`设计的,它处理所有的HTTP请求和响应。 **Spring Web MVC DispatcherServlet**的请求处理工作流如下图所示:\n\n![img](http://www.yiibai.com/uploads/images/201701/18/451110124_66519.png)\n\n以下是对应于到`DispatcherServlet`的传入HTTP请求的事件顺序:\n\n1. 在接收到HTTP请求后,`DispatcherServlet`会查询`HandlerMapping`以调用相应的`Controller`。\n2. `Controller`接受请求并根据使用的`GET`或`POST`方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给`DispatcherServlet`\n3. `DispatcherServlet`将从`ViewResolver`获取请求的定义视图。\n4. 当视图完成,`DispatcherServlet`将模型数据传递到最终的视图,并在浏览器上呈现。\n\n所有上述组件,即: `HandlerMapping`,`Controller`和`ViewResolver`是`WebApplicationContext`的一部分,它是普通`ApplicationContext`的扩展,带有Web应用程序所需的一些额外功能。\n\n\n\n{%folding blue, 配置%}\n\n\n\n需要通过使用web.xml文件中的URL映射来映射希望DispatcherServlet处理的请求。\n\n下面是一个示例来显示HelloWeb DispatcherServlet示例的声明和映射:\n\n\n\n```xml\n\n\n Spring MVC Application \n\n \n HelloWeb \n \n org.springframework.web.servlet.DispatcherServlet\n \n 1 \n \n\n \n HelloWeb \n *.jsp \n \n\n \n```\n\n\n\n`web.xml`文件将保存Web应用程序的`WebContent/WEB-INF`目录。\n\n在`HelloWeb DispatcherServlet`初始化时,框架将尝试从位于应用程序的`WebContent/WEB-INF`目录中的名为`[servlet-name]-servlet.xml`的文件加载应用程序上下文。在这个示例中,使用的文件将是`HelloWeb-servlet.xml`。\n\n接下来,``标记指示哪些`URL`将由`DispatcherServlet`处理。 这里所有以`.jsp`结尾的HTTP请求都将由`HelloWeb DispatcherServlet`处理。\n\n\n\n如果不想使用默认文件名为`[servlet-name]-servlet.xml`和默认位置为`WebContent/WEB-INF`,可以通过在`web.xml`文件中添加`servlet`侦听器`ContextLoaderListener`来自定义此文件名和位置 如下\n\n\n\n```xml\n\n\n\n....\n\n contextConfigLocation \n /WEB-INF/HelloWeb-servlet.xml \n \n\n\n \n org.springframework.web.context.ContextLoaderListener\n \n \n\n```\n\n\n\n{%endfolding%}\n\n## 回顾创建一个Servlet程序\n\n[项目地址springMVC-01-Servlet](https://codechina.csdn.net/m0_46079750/springmvc)\n\n\n\n{% tabs 第一个springMVC程序%}\n\n\n\n\n\n```xml\n\n \n org.springframework \n spring-webmvc \n 5.2.9.RELEASE \n \n \n junit \n junit \n 4.13 \n \n \n javax.servlet \n servlet-api \n 2.5 \n \n \n javax.servlet.jsp \n jsp-api \n 2.2 \n \n \n javax.servlet \n jstl \n 1.2 \n \n \n```\n\n\n\n{%folding blue, note:普通maven项目转为web项目%}\n\n1. 右键该项目,选择 `Add Framework support `\n\n ![image-20210901210441382](https://gitee.com/ajream/images/raw/master/img/20210901210444_image-20210901210441382.png)\n\n2. 选择 `Web Application`-> `OK`\n\n ![image-20210901210605521](https://gitee.com/ajream/images/raw/master/img/20210901210606_image-20210901210605521.png)\n\n3. 结果生成 `web`文件夹,说明成功了\n\n ![image-20210901210837382](https://gitee.com/ajream/images/raw/master/img/20210901210838_image-20210901210837382.png)\n\n\n\n\n\n{%endfolding%}\n\n\n\n\n\n\n\n1. 实现HttpServlet接口\n\n ```java\n package com.ajream.sevlet;\n \n import javax.servlet.ServletException;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n public class HelloServlet extends HttpServlet {\n \n @Override\n protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n // 1. 获取前端参数\n String method = req.getParameter(\"method\");\n if(method.equals(\"add\")){\n req.getSession().setAttribute(\"msg\", \"执行了add方法\");\n }\n if(method.equals(\"delete\")){\n req.getSession().setAttribute(\"msg\", \"执行了delete方法\");\n }\n // 2. 调用业务层\n // 3. 视图转发或者重定向\n req.getRequestDispatcher(\"WEB-INF/jsp/test.jsp\").forward(req,resp);\n \n }\n @Override\n protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n doGet(req, resp);\n }\n }\n \n ```\n\n2. 新建视图文件 `web/WEB-INF/jsp/test.jsp`\n\n ```html\n \n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n Title \n \n \n ${msg}\n \n \n \n ```\n\n3. 配置 `web/WEB-INF/web.xml`\n\n ```xml\n \n \n \n hello \n com.ajream.sevlet.HelloServlet \n \n \n \n hello \n /hello \n \n \n \n \n \n \n \n index.jsp \n \n \n ```\n\n4. 新建表单 `web/WEB-INF/jsp/form.jsp`\n\n ```html\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n Title \n \n \n \n \n \n \n \n \n ```\n\n \n\n\n\n\n\n\n\n![image-20210902000157563](https://gitee.com/ajream/images/raw/master/img/20210902000200_image-20210902000157563.png)\n\n\n\n![image-20210902000426095](https://gitee.com/ajream/images/raw/master/img/20210902000427_image-20210902000426095.png)\n\n\n\n在 `Application context` 输入: `/`\n\n![image-20210902000536524](https://gitee.com/ajream/images/raw/master/img/20210902000537_image-20210902000536524.png)\n\n\n\n\n\n\n\n\n\n在浏览器输入:`http://localhost:8083/hello?method=add`,(端口号可能不同)\n\n结果如下:\n\n![image-20210902000703371](https://gitee.com/ajream/images/raw/master/img/20210902000704_image-20210902000703371.png)\n\n\n\n\n\n{%endtabs%}\n\n\n\n","slug":"SpringMVC/springMVC(一)快速入门","published":1,"updated":"2021-09-05T10:33:47.417Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q4004makve8gio9l5r","content":"概述 SpringMVC是主流的实现MVC设计模式的企业级开发框架,是spring框架的一个子模块,无需整合,开发起来方便
\n\n与Spring兼容性好,无缝结合
\n \n约定优于配置
\n \n \nMVC设计模式 MVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。
\n使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。
\nController用于接收客户端请求,调用Model生成业务数据,传递给View
\n
\nspringMVC架构
\n
\n核心组件 DispatcherServlet:
\nMVC框架是围绕DispatcherServlet
设计的,它处理所有的HTTP请求和响应。 Spring Web MVC DispatcherServlet 的请求处理工作流如下图所示:
\n
\n以下是对应于到DispatcherServlet
的传入HTTP请求的事件顺序:
\n\n在接收到HTTP请求后,DispatcherServlet
会查询HandlerMapping
以调用相应的Controller
。 \nController
接受请求并根据使用的GET
或POST
方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给DispatcherServlet
\nDispatcherServlet
将从ViewResolver
获取请求的定义视图。 \n当视图完成,DispatcherServlet
将模型数据传递到最终的视图,并在浏览器上呈现。 \n \n所有上述组件,即: HandlerMapping
,Controller
和ViewResolver
是WebApplicationContext
的一部分,它是普通ApplicationContext
的扩展,带有Web应用程序所需的一些额外功能。
\n 配置 \n \n
需要通过使用web.xml文件中的URL映射来映射希望DispatcherServlet处理的请求。
下面是一个示例来显示HelloWeb DispatcherServlet示例的声明和映射:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <web-app id ="WebApp_ID" version ="2.4" xmlns ="http://java.sun.com/xml/ns/j2ee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" > <display-name > Spring MVC Application</display-name > <servlet > <servlet-name > HelloWeb</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet </servlet-class > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > HelloWeb</servlet-name > <url-pattern > *.jsp</url-pattern > </servlet-mapping > </web-app >
web.xml
文件将保存Web应用程序的WebContent/WEB-INF
目录。
在HelloWeb DispatcherServlet
初始化时,框架将尝试从位于应用程序的WebContent/WEB-INF
目录中的名为[servlet-name]-servlet.xml
的文件加载应用程序上下文。在这个示例中,使用的文件将是HelloWeb-servlet.xml
。
接下来,<servlet-mapping>
标记指示哪些URL
将由DispatcherServlet
处理。 这里所有以.jsp
结尾的HTTP请求都将由HelloWeb DispatcherServlet
处理。
如果不想使用默认文件名为[servlet-name]-servlet.xml
和默认位置为WebContent/WEB-INF
,可以通过在web.xml
文件中添加servlet
侦听器ContextLoaderListener
来自定义此文件名和位置 如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <web-app... > .... <context-param > <param-name > contextConfigLocation</param-name > <param-value > /WEB-INF/HelloWeb-servlet.xml</param-value > </context-param > <listener > <listener-class > org.springframework.web.context.ContextLoaderListener </listener-class > </listener > </web-app >
\n
\n \n回顾创建一个Servlet程序 项目地址springMVC-01-Servlet
\n(1)添加依赖 (2)创建Servlet应用 (3)配置Tomcat运行 (4)运行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 5.2.9.RELEASE</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > servlet-api</artifactId > <version > 2.5</version > </dependency > <dependency > <groupId > javax.servlet.jsp</groupId > <artifactId > jsp-api</artifactId > <version > 2.2</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > jstl</artifactId > <version > 1.2</version > </dependency > </dependencies >
\n
note:普通maven项目转为web项目 \n \n
右键该项目,选择 Add Framework support
选择 Web Application
-> OK
结果生成 web
文件夹,说明成功了
\n
\n \n实现HttpServlet接口
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.ajream.sevlet;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class HelloServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getParameter("method" ); if (method.equals("add" )){ req.getSession().setAttribute("msg" , "执行了add方法" ); } if (method.equals("delete" )){ req.getSession().setAttribute("msg" , "执行了delete方法" ); } req.getRequestDispatcher("WEB-INF/jsp/test.jsp" ).forward(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
\n \n新建视图文件 web/WEB-INF/jsp/test.jsp
\n1 2 3 4 5 6 7 8 9 10 11 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html > <head > <title > Title</title > </head > <body > ${msg} </body > </html >
\n \n配置 web/WEB-INF/web.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > hello</servlet-name > <servlet-class > com.ajream.sevlet.HelloServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > hello</servlet-name > <url-pattern > /hello</url-pattern > </servlet-mapping > <welcome-file-list > <welcome-file > index.jsp</welcome-file > </welcome-file-list > </web-app >
\n \n新建表单 web/WEB-INF/jsp/form.jsp
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html > <head > <title > Title</title > </head > <body > <form action ="/hello" method ="post" > <input type ="text" name ="method" > <input type ="submit" > </form > </body > </html >
\n \n
\n
\n
在 Application context
输入: /
\n
在浏览器输入:http://localhost:8083/hello?method=add
,(端口号可能不同)
\n
结果如下:
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"概述 SpringMVC是主流的实现MVC设计模式的企业级开发框架,是spring框架的一个子模块,无需整合,开发起来方便
\n\n与Spring兼容性好,无缝结合
\n \n约定优于配置
\n \n \nMVC设计模式 MVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。
\n使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。
\nController用于接收客户端请求,调用Model生成业务数据,传递给View
\n
\nspringMVC架构
\n
\n核心组件 DispatcherServlet:
\nMVC框架是围绕DispatcherServlet
设计的,它处理所有的HTTP请求和响应。 Spring Web MVC DispatcherServlet 的请求处理工作流如下图所示:
\n
\n以下是对应于到DispatcherServlet
的传入HTTP请求的事件顺序:
\n\n在接收到HTTP请求后,DispatcherServlet
会查询HandlerMapping
以调用相应的Controller
。 \nController
接受请求并根据使用的GET
或POST
方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给DispatcherServlet
\nDispatcherServlet
将从ViewResolver
获取请求的定义视图。 \n当视图完成,DispatcherServlet
将模型数据传递到最终的视图,并在浏览器上呈现。 \n \n所有上述组件,即: HandlerMapping
,Controller
和ViewResolver
是WebApplicationContext
的一部分,它是普通ApplicationContext
的扩展,带有Web应用程序所需的一些额外功能。
\n 配置 \n \n
需要通过使用web.xml文件中的URL映射来映射希望DispatcherServlet处理的请求。
下面是一个示例来显示HelloWeb DispatcherServlet示例的声明和映射:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <web-app id ="WebApp_ID" version ="2.4" xmlns ="http://java.sun.com/xml/ns/j2ee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" > <display-name > Spring MVC Application</display-name > <servlet > <servlet-name > HelloWeb</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet </servlet-class > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > HelloWeb</servlet-name > <url-pattern > *.jsp</url-pattern > </servlet-mapping > </web-app >
web.xml
文件将保存Web应用程序的WebContent/WEB-INF
目录。
在HelloWeb DispatcherServlet
初始化时,框架将尝试从位于应用程序的WebContent/WEB-INF
目录中的名为[servlet-name]-servlet.xml
的文件加载应用程序上下文。在这个示例中,使用的文件将是HelloWeb-servlet.xml
。
接下来,<servlet-mapping>
标记指示哪些URL
将由DispatcherServlet
处理。 这里所有以.jsp
结尾的HTTP请求都将由HelloWeb DispatcherServlet
处理。
如果不想使用默认文件名为[servlet-name]-servlet.xml
和默认位置为WebContent/WEB-INF
,可以通过在web.xml
文件中添加servlet
侦听器ContextLoaderListener
来自定义此文件名和位置 如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <web-app... > .... <context-param > <param-name > contextConfigLocation</param-name > <param-value > /WEB-INF/HelloWeb-servlet.xml</param-value > </context-param > <listener > <listener-class > org.springframework.web.context.ContextLoaderListener </listener-class > </listener > </web-app >
\n
\n \n回顾创建一个Servlet程序 项目地址springMVC-01-Servlet
\n(1)添加依赖 (2)创建Servlet应用 (3)配置Tomcat运行 (4)运行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 5.2.9.RELEASE</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > servlet-api</artifactId > <version > 2.5</version > </dependency > <dependency > <groupId > javax.servlet.jsp</groupId > <artifactId > jsp-api</artifactId > <version > 2.2</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > jstl</artifactId > <version > 1.2</version > </dependency > </dependencies >
\n
note:普通maven项目转为web项目 \n \n
右键该项目,选择 Add Framework support
选择 Web Application
-> OK
结果生成 web
文件夹,说明成功了
\n
\n \n实现HttpServlet接口
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.ajream.sevlet;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class HelloServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getParameter("method" ); if (method.equals("add" )){ req.getSession().setAttribute("msg" , "执行了add方法" ); } if (method.equals("delete" )){ req.getSession().setAttribute("msg" , "执行了delete方法" ); } req.getRequestDispatcher("WEB-INF/jsp/test.jsp" ).forward(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
\n \n新建视图文件 web/WEB-INF/jsp/test.jsp
\n1 2 3 4 5 6 7 8 9 10 11 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html > <head > <title > Title</title > </head > <body > ${msg} </body > </html >
\n \n配置 web/WEB-INF/web.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > hello</servlet-name > <servlet-class > com.ajream.sevlet.HelloServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > hello</servlet-name > <url-pattern > /hello</url-pattern > </servlet-mapping > <welcome-file-list > <welcome-file > index.jsp</welcome-file > </welcome-file-list > </web-app >
\n \n新建表单 web/WEB-INF/jsp/form.jsp
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html > <head > <title > Title</title > </head > <body > <form action ="/hello" method ="post" > <input type ="text" name ="method" > <input type ="submit" > </form > </body > </html >
\n \n
\n
\n
在 Application context
输入: /
\n
在浏览器输入:http://localhost:8083/hello?method=add
,(端口号可能不同)
\n
结果如下:
\n
\n"},{"title":"SpringMVC(三)HelloMVC(注解版)","description":"SpringMVC(注解版本)的HelloWorld程序","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"b8426075","date":"2021-09-03T07:01:20.000Z","_content":"\n\n\n\n\n\n\n[项目地址springMVC-03-hello-annotetion](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 环境配置\n\n1. 新建一个project:`springmvc-03-hello-annotation`, 添加依赖,添加web支持!\n\n2. 建立包结构 com.ajream.controller\n\n3. 由于Maven可能存在资源过滤的问题,我们将配置完善,在pom.xml添加:\n\n ```xml\n \n \n \n src/main/java \n \n **/*.properties \n **/*.xml \n \n false \n \n \n src/main/resources \n \n **/*.properties \n **/*.xml \n \n false \n \n \n \n ```\n\n\n\n## 进行开发\n\n### 配置web.xml\n\n注意点:\n\n- 注意web.xml版本问题,要最新版[4.0版]!\n- 注册DispatcherServlet\n- 关联SpringMVC的配置文件:`springmvc-servlet.xml`(先在resources新建springmvc-servlet.xml文件)\n- 启动级别为1\n- 映射路径为 `/` 【不要用`/*`,会404】\n\n\n\n```xml\n\n\n\n \n \n SpringMVC \n org.springframework.web.servlet.DispatcherServlet \n \n \n \n contextConfigLocation \n \n classpath:springmvc-servlet.xml \n \n \n \n 1 \n \n \n \n \n SpringMVC \n / \n \n\n \n```\n\n\n\n{%note info flat%}\n\n注意`/` 和 `/*` 的区别:\n\n` / ` 不会匹配到.jsp, 只针对我们编写的请求,即:.jsp 不会进入spring的 DispatcherServlet类\n\n` /* ` 会匹配 .jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错\n\n{%endnote%}\n\n\n\n\n\n### 添加Spring MVC配置文件\n\n- 让IOC的注解生效\n- 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..\n- MVC的注解驱动\n- 配置视图解析器\n\n在resource目录下添加`springmvc-servlet.xml`配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:\n\n\n\n```xml\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n```\n\n\n\n\n\n{%note info flat%}\n\n在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。\n\n{%endnote%}\n\n\n\n### 创建Controller\n\n\n\n编写一个Java控制类: `com.kuang.controller.HelloController` , 注意编码规范\n\n- `@Controller`是为了让Spring IOC容器初始化时自动扫描到;\n- `@RequestMapping`是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;\n- 方法中声明Model类型的参数是为了把Action中的数据带到视图中;\n- 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/**hello**.jsp。\n\n```java\npackage com.kuang.controller;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Controller\n@RequestMapping(\"/HelloController\")\npublic class HelloController {\n \n //真实访问地址 : 项目名/HelloController/hello\n @RequestMapping(\"/hello\")\n public String sayHello(Model model){\n \n //向模型中添加属性msg与值,可以在JSP页面中取出并渲染\n model.addAttribute(\"msg\",\"hello,SpringMVC\");\n \n //web-inf/jsp/hello.jsp\n return \"hello\";\n }\n}\n```\n\n\n\n\n\n### 创建视图层\n\n在WEB-INF/ jsp目录中创建`hello.jsp` , 视图可以直接取出并展示从Controller带回的信息;\n\n可以通过EL表示取出Model中存放的值,或者对象;\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n SpringMVC \n\n\n ${msg}\n\n\n```\n\n\n\n\n\n### 配置Tomcat运行\n\n配置Tomcat , 开启服务器 , 访问对应的请求路径 `http://localhost:8083/HelloController/hello`\n\n![image-20210903225624996](https://gitee.com/ajream/images/raw/master/img/20210903225626_image-20210903225624996.png)\n\n","source":"_posts/SpringMVC/springMVC(三)使用注解开发.md","raw":"---\ntitle: SpringMVC(三)HelloMVC(注解版)\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: SpringMVC(注解版本)的HelloWorld程序\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: b8426075\ndate: 2021-09-03 15:01:20\n---\n\n\n\n\n\n\n\n[项目地址springMVC-03-hello-annotetion](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 环境配置\n\n1. 新建一个project:`springmvc-03-hello-annotation`, 添加依赖,添加web支持!\n\n2. 建立包结构 com.ajream.controller\n\n3. 由于Maven可能存在资源过滤的问题,我们将配置完善,在pom.xml添加:\n\n ```xml\n \n \n \n src/main/java \n \n **/*.properties \n **/*.xml \n \n false \n \n \n src/main/resources \n \n **/*.properties \n **/*.xml \n \n false \n \n \n \n ```\n\n\n\n## 进行开发\n\n### 配置web.xml\n\n注意点:\n\n- 注意web.xml版本问题,要最新版[4.0版]!\n- 注册DispatcherServlet\n- 关联SpringMVC的配置文件:`springmvc-servlet.xml`(先在resources新建springmvc-servlet.xml文件)\n- 启动级别为1\n- 映射路径为 `/` 【不要用`/*`,会404】\n\n\n\n```xml\n\n\n\n \n \n SpringMVC \n org.springframework.web.servlet.DispatcherServlet \n \n \n \n contextConfigLocation \n \n classpath:springmvc-servlet.xml \n \n \n \n 1 \n \n \n \n \n SpringMVC \n / \n \n\n \n```\n\n\n\n{%note info flat%}\n\n注意`/` 和 `/*` 的区别:\n\n` / ` 不会匹配到.jsp, 只针对我们编写的请求,即:.jsp 不会进入spring的 DispatcherServlet类\n\n` /* ` 会匹配 .jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错\n\n{%endnote%}\n\n\n\n\n\n### 添加Spring MVC配置文件\n\n- 让IOC的注解生效\n- 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..\n- MVC的注解驱动\n- 配置视图解析器\n\n在resource目录下添加`springmvc-servlet.xml`配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:\n\n\n\n```xml\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n```\n\n\n\n\n\n{%note info flat%}\n\n在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。\n\n{%endnote%}\n\n\n\n### 创建Controller\n\n\n\n编写一个Java控制类: `com.kuang.controller.HelloController` , 注意编码规范\n\n- `@Controller`是为了让Spring IOC容器初始化时自动扫描到;\n- `@RequestMapping`是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;\n- 方法中声明Model类型的参数是为了把Action中的数据带到视图中;\n- 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/**hello**.jsp。\n\n```java\npackage com.kuang.controller;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Controller\n@RequestMapping(\"/HelloController\")\npublic class HelloController {\n \n //真实访问地址 : 项目名/HelloController/hello\n @RequestMapping(\"/hello\")\n public String sayHello(Model model){\n \n //向模型中添加属性msg与值,可以在JSP页面中取出并渲染\n model.addAttribute(\"msg\",\"hello,SpringMVC\");\n \n //web-inf/jsp/hello.jsp\n return \"hello\";\n }\n}\n```\n\n\n\n\n\n### 创建视图层\n\n在WEB-INF/ jsp目录中创建`hello.jsp` , 视图可以直接取出并展示从Controller带回的信息;\n\n可以通过EL表示取出Model中存放的值,或者对象;\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n SpringMVC \n\n\n ${msg}\n\n\n```\n\n\n\n\n\n### 配置Tomcat运行\n\n配置Tomcat , 开启服务器 , 访问对应的请求路径 `http://localhost:8083/HelloController/hello`\n\n![image-20210903225624996](https://gitee.com/ajream/images/raw/master/img/20210903225626_image-20210903225624996.png)\n\n","slug":"SpringMVC/springMVC(三)使用注解开发","published":1,"updated":"2021-09-05T10:34:03.284Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q5004oakve8g0k1g9x","content":"项目地址springMVC-03-hello-annotetion
\n环境配置 \n新建一个project:springmvc-03-hello-annotation
, 添加依赖,添加web支持!
\n \n建立包结构 com.ajream.controller
\n \n由于Maven可能存在资源过滤的问题,我们将配置完善,在pom.xml添加:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <build > <resources > <resource > <directory > src/main/java</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > false</filtering > </resource > <resource > <directory > src/main/resources</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > false</filtering > </resource > </resources > </build >
\n \n \n进行开发 配置web.xml 注意点:
\n\n注意web.xml版本问题,要最新版[4.0版]! \n注册DispatcherServlet \n关联SpringMVC的配置文件:springmvc-servlet.xml
(先在resources新建springmvc-servlet.xml文件) \n启动级别为1 \n映射路径为 /
【不要用/*
,会404】 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > SpringMVC</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:springmvc-servlet.xml</param-value > </init-param > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > SpringMVC</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > </web-app >
\n注意/
和 /*
的区别:
\n
<url-pattern> / </url-pattern>
不会匹配到.jsp, 只针对我们编写的请求,即:.jsp 不会进入spring的 DispatcherServlet类
\n
<url-pattern> /* </url-pattern>
会匹配 .jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错
\n
\n添加Spring MVC配置文件 \n让IOC的注解生效 \n静态资源过滤 :HTML . JS . CSS . 图片 , 视频 ….. \nMVC的注解驱动 \n配置视图解析器 \n \n在resource目录下添加springmvc-servlet.xml
配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xmlns:mvc ="http://www.springframework.org/schema/mvc" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd" > <context:component-scan base-package ="com.kuang.controller" /> <mvc:default-servlet-handler /> <mvc:annotation-driven /> <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean > </beans >
\n在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。
\n
\n创建Controller 编写一个Java控制类: com.kuang.controller.HelloController
, 注意编码规范
\n\n@Controller
是为了让Spring IOC容器初始化时自动扫描到; \n@RequestMapping
是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello; \n方法中声明Model类型的参数是为了把Action中的数据带到视图中; \n方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello .jsp。 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.kuang.controller;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;@Controller @RequestMapping("/HelloController") public class HelloController { @RequestMapping("/hello") public String sayHello (Model model) { model.addAttribute("msg" ,"hello,SpringMVC" ); return "hello" ; } }
\n创建视图层 在WEB-INF/ jsp目录中创建hello.jsp
, 视图可以直接取出并展示从Controller带回的信息;
\n可以通过EL表示取出Model中存放的值,或者对象;
\n1 2 3 4 5 6 7 8 9 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html > <head > <title > SpringMVC</title > </head > <body > ${msg} </body > </html >
\n配置Tomcat运行 配置Tomcat , 开启服务器 , 访问对应的请求路径 http://localhost:8083/HelloController/hello
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"项目地址springMVC-03-hello-annotetion
\n环境配置 \n新建一个project:springmvc-03-hello-annotation
, 添加依赖,添加web支持!
\n \n建立包结构 com.ajream.controller
\n \n由于Maven可能存在资源过滤的问题,我们将配置完善,在pom.xml添加:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <build > <resources > <resource > <directory > src/main/java</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > false</filtering > </resource > <resource > <directory > src/main/resources</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > false</filtering > </resource > </resources > </build >
\n \n \n进行开发 配置web.xml 注意点:
\n\n注意web.xml版本问题,要最新版[4.0版]! \n注册DispatcherServlet \n关联SpringMVC的配置文件:springmvc-servlet.xml
(先在resources新建springmvc-servlet.xml文件) \n启动级别为1 \n映射路径为 /
【不要用/*
,会404】 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > SpringMVC</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:springmvc-servlet.xml</param-value > </init-param > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > SpringMVC</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > </web-app >
\n注意/
和 /*
的区别:
\n
<url-pattern> / </url-pattern>
不会匹配到.jsp, 只针对我们编写的请求,即:.jsp 不会进入spring的 DispatcherServlet类
\n
<url-pattern> /* </url-pattern>
会匹配 .jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错
\n
\n添加Spring MVC配置文件 \n让IOC的注解生效 \n静态资源过滤 :HTML . JS . CSS . 图片 , 视频 ….. \nMVC的注解驱动 \n配置视图解析器 \n \n在resource目录下添加springmvc-servlet.xml
配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xmlns:mvc ="http://www.springframework.org/schema/mvc" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd" > <context:component-scan base-package ="com.kuang.controller" /> <mvc:default-servlet-handler /> <mvc:annotation-driven /> <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean > </beans >
\n在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。
\n
\n创建Controller 编写一个Java控制类: com.kuang.controller.HelloController
, 注意编码规范
\n\n@Controller
是为了让Spring IOC容器初始化时自动扫描到; \n@RequestMapping
是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello; \n方法中声明Model类型的参数是为了把Action中的数据带到视图中; \n方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello .jsp。 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.kuang.controller;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;@Controller @RequestMapping("/HelloController") public class HelloController { @RequestMapping("/hello") public String sayHello (Model model) { model.addAttribute("msg" ,"hello,SpringMVC" ); return "hello" ; } }
\n创建视图层 在WEB-INF/ jsp目录中创建hello.jsp
, 视图可以直接取出并展示从Controller带回的信息;
\n可以通过EL表示取出Model中存放的值,或者对象;
\n1 2 3 4 5 6 7 8 9 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html > <head > <title > SpringMVC</title > </head > <body > ${msg} </body > </html >
\n配置Tomcat运行 配置Tomcat , 开启服务器 , 访问对应的请求路径 http://localhost:8083/HelloController/hello
\n
\n"},{"title":"SpringMVC(六)数据处理和乱码问题","description":"对提交的数据进行处理以及乱码问题的解决","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"ecec7d3f","date":"2021-09-04T08:26:20.000Z","_content":"\n\n\n## 获取url提交的数据\n\n\n\n[项目地址springMVC-06-dataProcess](https://codechina.csdn.net/m0_46079750/springmvc/-/tree/master1)\n\n### 通过url提交的数据\n\n**url的参数名与java方法中的参数名一致 **(方法名没要求),如下:都是 `name`\n\nurl: http://localhost:8083/hello?name=ajream\n\n```java\n@RequestMapping(\"/hello\")\npublic String hello(String name){\n System.out.println(name);\n return \"hello\";\n}\n```\n\n\n\n**url的参数名与java方法中的参数名不一致 **, 如下,url中为 `username`, java方法中为 `name`\n\nurl: http://localhost:8083/hello?username=ajream\n\n使用注解:`@RequestParam`\n\n```java\n@RequestMapping(\"/hello\")\npublic String hello(@RequestParam(\"username\") String name){\n System.out.println(name);\n return \"hello\";\n}\n```\n\n\n\n**传入的数据封装成对象**\n\n首先创建一个 `User` 类\n\n```java\npackage com.ajream.pojo;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class User {\n private String name;\n private int id;\n private int age;\n}\n\n```\n\n在 `Controller`中,添加一个参数 `User user`,表示传入的参数将会被封装成一个 `user` 对象\n\n```java\n@RequestMapping(\"/t2\")\npublic String test2(User user, Model model){\n System.out.println(user);\n model.addAttribute(\"msg\", user);\n return \"hello\";\n}\n```\n\n\n\n测试运行,访问 `http://localhost:8083/t2?name=ajream&age=10&id=10001` :\n\n![image-20210904190448500](https://gitee.com/ajream/images/raw/master/img/20210904190452_image-20210904190448500.png)\n\n\n\n> 注意传入的参数必须要与类中的属性名一一对应,否则就获取不到数据,如下\n>\n> ![image-20210904190750826](https://gitee.com/ajream/images/raw/master/img/20210904190752_image-20210904190750826.png)\n\n\n\n\n\n\n\n## 表单提交的数据乱码\n\n### POST提交数据乱码\n\n创建表单提交页面 `web/form.jsp`\n\n![image-20210904195836766](https://gitee.com/ajream/images/raw/master/img/20210904195839_image-20210904195836766.png)\n\n添加代码:\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n Title \n\n\n\n\n\n\n\n```\n\n\n\n编写后台处理类\n\n```java\npackage com.ajream.encoding;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.PostMapping;\n\n@Controller\npublic class EncodingController {\n @PostMapping(\"/e/t1\")\n public String test1(String name, Model model){\n model.addAttribute(\"msg\", name);\n System.out.println(name);\n return \"test\";\n }\n}\n\n```\n\n运行测试,先访问表单页 `http:localhost:8083/form.jsp`, 提交后自动跳转到 `localhost:8083/e/t1`页面\n\n传入中文数据,返回乱码:\n\n![image-20210904200350867](https://gitee.com/ajream/images/raw/master/img/20210904200352_image-20210904200350867.png)\n\n后台也是:\n\n![image-20210904200522875](https://gitee.com/ajream/images/raw/master/img/20210904200524_image-20210904200522875.png)\n\n\n\n\n\n{%folding blue, 提交表单后出现bug%}\n\n提交表单出现404页面\n\n![image-20210904200902942](https://gitee.com/ajream/images/raw/master/img/20210904200904_image-20210904200902942.png)\n\n\n\n原因是我这是在上一个的项目中进行修改的,我添加了新的包,但没有在 springMVC配置文件中添加对这个包扫描的支持,导致没有注解支持,重新添加后问题解决\n\n![image-20210904201311953](https://gitee.com/ajream/images/raw/master/img/20210904201313_image-20210904201311953.png)\n\n\n\n{%endfolding%}\n\n\n\n\n\n### 乱码问题解决\n\n\n\n在 `web.xml` 添加过滤器(这里用的是springMVC提供的)\n\n\n\n```xml\n\n encoding \n org.springframework.web.filter.CharacterEncodingFilter \n \n encoding \n utf-8 \n \n \n\n encoding \n /* \n \n```\n\n{%note info flat%}\n\n注意`url-pattern`是 \"/*\", 不是 \"/\"\n\n{%endnote%}\n\n\n\n重新运行,前端、后台都显示正常:\n\n| 前端 | 后台 |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210904202053181](https://gitee.com/ajream/images/raw/master/img/20210904202054_image-20210904202053181.png) | |\n\n\n\n\n\n{%folding blue, 极端情况对get请求支持不好%}\n\n有些极端情况下.这个过滤器对get的支持不好 .\n\n处理方法 :\n\n修改tomcat配置文件`server.xml`(设置编码)\n\n```xml\n \n```\n\n自定义过滤器:\n\n```java\npackage com.kuang.filter;\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Map;\n/**\n * 解决get和post请求 全部乱码的过滤器\n */\npublic class GenericEncodingFilter implements Filter {\n @Override\n public void destroy() {\n }\n @Override\n public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {\n //处理response的字符编码\n HttpServletResponse myResponse=(HttpServletResponse) response;\n myResponse.setContentType(\"text/html;charset=UTF-8\");\n // 转型为与协议相关对象\n HttpServletRequest httpServletRequest = (HttpServletRequest) request;\n // 对request包装增强\n HttpServletRequest myrequest = new MyRequest(httpServletRequest);\n chain.doFilter(myrequest, response);\n }\n @Override\n public void init(FilterConfig filterConfig) throws ServletException {\n }\n}\n//自定义request对象,HttpServletRequest的包装类\nclass MyRequest extends HttpServletRequestWrapper {\n private HttpServletRequest request;\n //是否编码的标记\n private boolean hasEncode;\n //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰\n public MyRequest(HttpServletRequest request) {\n super(request);// super必须写\n this.request = request;\n }\n // 对需要增强方法 进行覆盖\n @Override\n public Map getParameterMap() {\n // 先获得请求方式\n String method = request.getMethod();\n if (method.equalsIgnoreCase(\"post\")) {\n // post请求\n try {\n // 处理post乱码\n request.setCharacterEncoding(\"utf-8\");\n return request.getParameterMap();\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n }\n } else if (method.equalsIgnoreCase(\"get\")) {\n // get请求\n Map parameterMap = request.getParameterMap();\n if (!hasEncode) { // 确保get手动编码逻辑只运行一次\n for (String parameterName : parameterMap.keySet()) {\n String[] values = parameterMap.get(parameterName);\n if (values != null) {\n for (int i = 0; i < values.length; i++) {\n try {\n // 处理get乱码\n values[i] = new String(values[i]\n .getBytes(\"ISO-8859-1\"), \"utf-8\");\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n }\n }\n }\n }\n hasEncode = true;\n }\n return parameterMap;\n }\n return super.getParameterMap();\n }\n //取一个值\n @Override\n public String getParameter(String name) {\n Map parameterMap = getParameterMap();\n String[] values = parameterMap.get(name);\n if (values == null) {\n return null;\n }\n return values[0]; // 取回参数的第一个值\n }\n //取所有值\n @Override\n public String[] getParameterValues(String name) {\n Map parameterMap = getParameterMap();\n String[] values = parameterMap.get(name);\n return values;\n }\n}\n```\n\n\n\n在web.xml添加自定义过滤器即可\n\n```xml\n\n encoding \n 【修改为自定义过滤器】 \n \n encoding \n utf-8 \n \n \n\n encoding \n /* \n \n```\n\n\n\n{%note flat%}\n\n乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!\n\n{%endnote%}\n\n{%endfolding%}\n\n\n\n\n\n\n\n","source":"_posts/SpringMVC/springMVC(六)数据处理.md","raw":"---\ntitle: SpringMVC(六)数据处理和乱码问题\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: 对提交的数据进行处理以及乱码问题的解决\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: ecec7d3f\ndate: 2021-09-04 16:26:20\n---\n\n\n\n## 获取url提交的数据\n\n\n\n[项目地址springMVC-06-dataProcess](https://codechina.csdn.net/m0_46079750/springmvc/-/tree/master1)\n\n### 通过url提交的数据\n\n**url的参数名与java方法中的参数名一致 **(方法名没要求),如下:都是 `name`\n\nurl: http://localhost:8083/hello?name=ajream\n\n```java\n@RequestMapping(\"/hello\")\npublic String hello(String name){\n System.out.println(name);\n return \"hello\";\n}\n```\n\n\n\n**url的参数名与java方法中的参数名不一致 **, 如下,url中为 `username`, java方法中为 `name`\n\nurl: http://localhost:8083/hello?username=ajream\n\n使用注解:`@RequestParam`\n\n```java\n@RequestMapping(\"/hello\")\npublic String hello(@RequestParam(\"username\") String name){\n System.out.println(name);\n return \"hello\";\n}\n```\n\n\n\n**传入的数据封装成对象**\n\n首先创建一个 `User` 类\n\n```java\npackage com.ajream.pojo;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class User {\n private String name;\n private int id;\n private int age;\n}\n\n```\n\n在 `Controller`中,添加一个参数 `User user`,表示传入的参数将会被封装成一个 `user` 对象\n\n```java\n@RequestMapping(\"/t2\")\npublic String test2(User user, Model model){\n System.out.println(user);\n model.addAttribute(\"msg\", user);\n return \"hello\";\n}\n```\n\n\n\n测试运行,访问 `http://localhost:8083/t2?name=ajream&age=10&id=10001` :\n\n![image-20210904190448500](https://gitee.com/ajream/images/raw/master/img/20210904190452_image-20210904190448500.png)\n\n\n\n> 注意传入的参数必须要与类中的属性名一一对应,否则就获取不到数据,如下\n>\n> ![image-20210904190750826](https://gitee.com/ajream/images/raw/master/img/20210904190752_image-20210904190750826.png)\n\n\n\n\n\n\n\n## 表单提交的数据乱码\n\n### POST提交数据乱码\n\n创建表单提交页面 `web/form.jsp`\n\n![image-20210904195836766](https://gitee.com/ajream/images/raw/master/img/20210904195839_image-20210904195836766.png)\n\n添加代码:\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n Title \n\n\n\n\n\n\n\n```\n\n\n\n编写后台处理类\n\n```java\npackage com.ajream.encoding;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.PostMapping;\n\n@Controller\npublic class EncodingController {\n @PostMapping(\"/e/t1\")\n public String test1(String name, Model model){\n model.addAttribute(\"msg\", name);\n System.out.println(name);\n return \"test\";\n }\n}\n\n```\n\n运行测试,先访问表单页 `http:localhost:8083/form.jsp`, 提交后自动跳转到 `localhost:8083/e/t1`页面\n\n传入中文数据,返回乱码:\n\n![image-20210904200350867](https://gitee.com/ajream/images/raw/master/img/20210904200352_image-20210904200350867.png)\n\n后台也是:\n\n![image-20210904200522875](https://gitee.com/ajream/images/raw/master/img/20210904200524_image-20210904200522875.png)\n\n\n\n\n\n{%folding blue, 提交表单后出现bug%}\n\n提交表单出现404页面\n\n![image-20210904200902942](https://gitee.com/ajream/images/raw/master/img/20210904200904_image-20210904200902942.png)\n\n\n\n原因是我这是在上一个的项目中进行修改的,我添加了新的包,但没有在 springMVC配置文件中添加对这个包扫描的支持,导致没有注解支持,重新添加后问题解决\n\n![image-20210904201311953](https://gitee.com/ajream/images/raw/master/img/20210904201313_image-20210904201311953.png)\n\n\n\n{%endfolding%}\n\n\n\n\n\n### 乱码问题解决\n\n\n\n在 `web.xml` 添加过滤器(这里用的是springMVC提供的)\n\n\n\n```xml\n\n encoding \n org.springframework.web.filter.CharacterEncodingFilter \n \n encoding \n utf-8 \n \n \n\n encoding \n /* \n \n```\n\n{%note info flat%}\n\n注意`url-pattern`是 \"/*\", 不是 \"/\"\n\n{%endnote%}\n\n\n\n重新运行,前端、后台都显示正常:\n\n| 前端 | 后台 |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210904202053181](https://gitee.com/ajream/images/raw/master/img/20210904202054_image-20210904202053181.png) | |\n\n\n\n\n\n{%folding blue, 极端情况对get请求支持不好%}\n\n有些极端情况下.这个过滤器对get的支持不好 .\n\n处理方法 :\n\n修改tomcat配置文件`server.xml`(设置编码)\n\n```xml\n \n```\n\n自定义过滤器:\n\n```java\npackage com.kuang.filter;\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Map;\n/**\n * 解决get和post请求 全部乱码的过滤器\n */\npublic class GenericEncodingFilter implements Filter {\n @Override\n public void destroy() {\n }\n @Override\n public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {\n //处理response的字符编码\n HttpServletResponse myResponse=(HttpServletResponse) response;\n myResponse.setContentType(\"text/html;charset=UTF-8\");\n // 转型为与协议相关对象\n HttpServletRequest httpServletRequest = (HttpServletRequest) request;\n // 对request包装增强\n HttpServletRequest myrequest = new MyRequest(httpServletRequest);\n chain.doFilter(myrequest, response);\n }\n @Override\n public void init(FilterConfig filterConfig) throws ServletException {\n }\n}\n//自定义request对象,HttpServletRequest的包装类\nclass MyRequest extends HttpServletRequestWrapper {\n private HttpServletRequest request;\n //是否编码的标记\n private boolean hasEncode;\n //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰\n public MyRequest(HttpServletRequest request) {\n super(request);// super必须写\n this.request = request;\n }\n // 对需要增强方法 进行覆盖\n @Override\n public Map getParameterMap() {\n // 先获得请求方式\n String method = request.getMethod();\n if (method.equalsIgnoreCase(\"post\")) {\n // post请求\n try {\n // 处理post乱码\n request.setCharacterEncoding(\"utf-8\");\n return request.getParameterMap();\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n }\n } else if (method.equalsIgnoreCase(\"get\")) {\n // get请求\n Map parameterMap = request.getParameterMap();\n if (!hasEncode) { // 确保get手动编码逻辑只运行一次\n for (String parameterName : parameterMap.keySet()) {\n String[] values = parameterMap.get(parameterName);\n if (values != null) {\n for (int i = 0; i < values.length; i++) {\n try {\n // 处理get乱码\n values[i] = new String(values[i]\n .getBytes(\"ISO-8859-1\"), \"utf-8\");\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n }\n }\n }\n }\n hasEncode = true;\n }\n return parameterMap;\n }\n return super.getParameterMap();\n }\n //取一个值\n @Override\n public String getParameter(String name) {\n Map parameterMap = getParameterMap();\n String[] values = parameterMap.get(name);\n if (values == null) {\n return null;\n }\n return values[0]; // 取回参数的第一个值\n }\n //取所有值\n @Override\n public String[] getParameterValues(String name) {\n Map parameterMap = getParameterMap();\n String[] values = parameterMap.get(name);\n return values;\n }\n}\n```\n\n\n\n在web.xml添加自定义过滤器即可\n\n```xml\n\n encoding \n 【修改为自定义过滤器】 \n \n encoding \n utf-8 \n \n \n\n encoding \n /* \n \n```\n\n\n\n{%note flat%}\n\n乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!\n\n{%endnote%}\n\n{%endfolding%}\n\n\n\n\n\n\n\n","slug":"SpringMVC/springMVC(六)数据处理","published":1,"updated":"2021-09-05T10:34:16.597Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q6004rakve7m1j1plt","content":"获取url提交的数据 项目地址springMVC-06-dataProcess
\n通过url提交的数据 url的参数名与java方法中的参数名一致 (方法名没要求),如下:都是 name
\nurl: http://localhost:8083/hello?name=ajream
\n1 2 3 4 5 @RequestMapping("/hello") public String hello (String name) { System.out.println(name); return "hello" ; }
\nurl的参数名与java方法中的参数名不一致 , 如下,url中为 username
, java方法中为 name
\nurl: http://localhost:8083/hello?username=ajream
\n使用注解:@RequestParam
\n1 2 3 4 5 @RequestMapping("/hello") public String hello (@RequestParam("username") String name) { System.out.println(name); return "hello" ; }
\n传入的数据封装成对象
\n首先创建一个 User
类
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.ajream.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class User { private String name; private int id; private int age; }
\n在 Controller
中,添加一个参数 User user
,表示传入的参数将会被封装成一个 user
对象
\n1 2 3 4 5 6 @RequestMapping("/t2") public String test2 (User user, Model model) { System.out.println(user); model.addAttribute("msg" , user); return "hello" ; }
\n测试运行,访问 http://localhost:8083/t2?name=ajream&age=10&id=10001
:
\n
\n\n注意传入的参数必须要与类中的属性名一一对应,否则就获取不到数据,如下
\n
\n \n表单提交的数据乱码 POST提交数据乱码 创建表单提交页面 web/form.jsp
\n
\n添加代码:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html > <head > <title > Title</title > </head > <body > <form action ="/e/t1" method ="post" > <input type ="text" name ="name" > <input type ="submit" > </form > </body > </html >
\n编写后台处理类
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.ajream.encoding;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.PostMapping;@Controller public class EncodingController { @PostMapping("/e/t1") public String test1 (String name, Model model) { model.addAttribute("msg" , name); System.out.println(name); return "test" ; } }
\n运行测试,先访问表单页 http:localhost:8083/form.jsp
, 提交后自动跳转到 localhost:8083/e/t1
页面
\n传入中文数据,返回乱码:
\n
\n后台也是:
\n
\n 提交表单后出现bug \n \n
提交表单出现404页面
原因是我这是在上一个的项目中进行修改的,我添加了新的包,但没有在 springMVC配置文件中添加对这个包扫描的支持,导致没有注解支持,重新添加后问题解决
\n
\n \n乱码问题解决 在 web.xml
添加过滤器(这里用的是springMVC提供的)
\n1 2 3 4 5 6 7 8 9 10 11 12 <filter > <filter-name > encoding</filter-name > <filter-class > org.springframework.web.filter.CharacterEncodingFilter</filter-class > <init-param > <param-name > encoding</param-name > <param-value > utf-8</param-value > </init-param > </filter > <filter-mapping > <filter-name > encoding</filter-name > <url-pattern > /*</url-pattern > </filter-mapping >
\n注意url-pattern
是 “/*”, 不是 “/“
\n
\n重新运行,前端、后台都显示正常:
\n\n
\n\n\n前端 \n后台 \n \n \n\n\n \n \n \n \n
\n
\n 极端情况对get请求支持不好 \n \n
有些极端情况下.这个过滤器对get的支持不好 .
处理方法 :
修改tomcat配置文件server.xml
(设置编码)
1 2 3 <Connector URIEncoding ="utf-8" port ="8080" protocol ="HTTP/1.1" connectionTimeout ="20000" redirectPort ="8443" />
自定义过滤器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 package com.kuang.filter;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.util.Map;public class GenericEncodingFilter implements Filter { @Override public void destroy () { } @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse myResponse=(HttpServletResponse) response; myResponse.setContentType("text/html;charset=UTF-8" ); HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletRequest myrequest = new MyRequest(httpServletRequest); chain.doFilter(myrequest, response); } @Override public void init (FilterConfig filterConfig) throws ServletException { } } class MyRequest extends HttpServletRequestWrapper { private HttpServletRequest request; private boolean hasEncode; public MyRequest (HttpServletRequest request) { super (request); this .request = request; } @Override public Map getParameterMap () { String method = request.getMethod(); if (method.equalsIgnoreCase("post" )) { try { request.setCharacterEncoding("utf-8" ); return request.getParameterMap(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if (method.equalsIgnoreCase("get" )) { Map<String, String[]> parameterMap = request.getParameterMap(); if (!hasEncode) { for (String parameterName : parameterMap.keySet()) { String[] values = parameterMap.get(parameterName); if (values != null ) { for (int i = 0 ; i < values.length; i++) { try { values[i] = new String(values[i] .getBytes("ISO-8859-1" ), "utf-8" ); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } } hasEncode = true ; } return parameterMap; } return super .getParameterMap(); } @Override public String getParameter (String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); if (values == null ) { return null ; } return values[0 ]; } @Override public String[] getParameterValues(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); return values; } }
在web.xml添加自定义过滤器即可
1 2 3 4 5 6 7 8 9 10 11 12 <filter > <filter-name > encoding</filter-name > <filter-class > 【修改为自定义过滤器】</filter-class > <init-param > <param-name > encoding</param-name > <param-value > utf-8</param-value > </init-param > </filter > <filter-mapping > <filter-name > encoding</filter-name > <url-pattern > /*</url-pattern > </filter-mapping >
乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!
\n
\n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"获取url提交的数据 项目地址springMVC-06-dataProcess
\n通过url提交的数据 url的参数名与java方法中的参数名一致 (方法名没要求),如下:都是 name
\nurl: http://localhost:8083/hello?name=ajream
\n1 2 3 4 5 @RequestMapping("/hello") public String hello (String name) { System.out.println(name); return "hello" ; }
\nurl的参数名与java方法中的参数名不一致 , 如下,url中为 username
, java方法中为 name
\nurl: http://localhost:8083/hello?username=ajream
\n使用注解:@RequestParam
\n1 2 3 4 5 @RequestMapping("/hello") public String hello (@RequestParam("username") String name) { System.out.println(name); return "hello" ; }
\n传入的数据封装成对象
\n首先创建一个 User
类
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.ajream.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class User { private String name; private int id; private int age; }
\n在 Controller
中,添加一个参数 User user
,表示传入的参数将会被封装成一个 user
对象
\n1 2 3 4 5 6 @RequestMapping("/t2") public String test2 (User user, Model model) { System.out.println(user); model.addAttribute("msg" , user); return "hello" ; }
\n测试运行,访问 http://localhost:8083/t2?name=ajream&age=10&id=10001
:
\n
\n\n注意传入的参数必须要与类中的属性名一一对应,否则就获取不到数据,如下
\n
\n \n表单提交的数据乱码 POST提交数据乱码 创建表单提交页面 web/form.jsp
\n
\n添加代码:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html > <head > <title > Title</title > </head > <body > <form action ="/e/t1" method ="post" > <input type ="text" name ="name" > <input type ="submit" > </form > </body > </html >
\n编写后台处理类
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.ajream.encoding;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.PostMapping;@Controller public class EncodingController { @PostMapping("/e/t1") public String test1 (String name, Model model) { model.addAttribute("msg" , name); System.out.println(name); return "test" ; } }
\n运行测试,先访问表单页 http:localhost:8083/form.jsp
, 提交后自动跳转到 localhost:8083/e/t1
页面
\n传入中文数据,返回乱码:
\n
\n后台也是:
\n
\n 提交表单后出现bug \n \n
提交表单出现404页面
原因是我这是在上一个的项目中进行修改的,我添加了新的包,但没有在 springMVC配置文件中添加对这个包扫描的支持,导致没有注解支持,重新添加后问题解决
\n
\n \n乱码问题解决 在 web.xml
添加过滤器(这里用的是springMVC提供的)
\n1 2 3 4 5 6 7 8 9 10 11 12 <filter > <filter-name > encoding</filter-name > <filter-class > org.springframework.web.filter.CharacterEncodingFilter</filter-class > <init-param > <param-name > encoding</param-name > <param-value > utf-8</param-value > </init-param > </filter > <filter-mapping > <filter-name > encoding</filter-name > <url-pattern > /*</url-pattern > </filter-mapping >
\n注意url-pattern
是 “/*”, 不是 “/“
\n
\n重新运行,前端、后台都显示正常:
\n\n
\n\n\n前端 \n后台 \n \n \n\n\n \n \n \n \n
\n
\n 极端情况对get请求支持不好 \n \n
有些极端情况下.这个过滤器对get的支持不好 .
处理方法 :
修改tomcat配置文件server.xml
(设置编码)
1 2 3 <Connector URIEncoding ="utf-8" port ="8080" protocol ="HTTP/1.1" connectionTimeout ="20000" redirectPort ="8443" />
自定义过滤器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 package com.kuang.filter;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.util.Map;public class GenericEncodingFilter implements Filter { @Override public void destroy () { } @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse myResponse=(HttpServletResponse) response; myResponse.setContentType("text/html;charset=UTF-8" ); HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletRequest myrequest = new MyRequest(httpServletRequest); chain.doFilter(myrequest, response); } @Override public void init (FilterConfig filterConfig) throws ServletException { } } class MyRequest extends HttpServletRequestWrapper { private HttpServletRequest request; private boolean hasEncode; public MyRequest (HttpServletRequest request) { super (request); this .request = request; } @Override public Map getParameterMap () { String method = request.getMethod(); if (method.equalsIgnoreCase("post" )) { try { request.setCharacterEncoding("utf-8" ); return request.getParameterMap(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if (method.equalsIgnoreCase("get" )) { Map<String, String[]> parameterMap = request.getParameterMap(); if (!hasEncode) { for (String parameterName : parameterMap.keySet()) { String[] values = parameterMap.get(parameterName); if (values != null ) { for (int i = 0 ; i < values.length; i++) { try { values[i] = new String(values[i] .getBytes("ISO-8859-1" ), "utf-8" ); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } } hasEncode = true ; } return parameterMap; } return super .getParameterMap(); } @Override public String getParameter (String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); if (values == null ) { return null ; } return values[0 ]; } @Override public String[] getParameterValues(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); return values; } }
在web.xml添加自定义过滤器即可
1 2 3 4 5 6 7 8 9 10 11 12 <filter > <filter-name > encoding</filter-name > <filter-class > 【修改为自定义过滤器】</filter-class > <init-param > <param-name > encoding</param-name > <param-value > utf-8</param-value > </init-param > </filter > <filter-mapping > <filter-name > encoding</filter-name > <url-pattern > /*</url-pattern > </filter-mapping >
乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!
\n
\n \n"},{"title":"SpringMVC(二)HelloMVC","description":"SpringMVC的HelloWorld程序","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"a4682555","date":"2021-09-03T02:31:26.000Z","_content":"\n\n\n\n\n[项目地址springMVC-02-hellomvc](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 环境配置\n\n1. 创建一个普通maven项目 `springmvc-02-hellomvc`\n\n2. 导入依赖\n\n ```xml\n \n \n org.springframework \n spring-webmvc \n 5.2.9.RELEASE \n \n \n junit \n junit \n 4.13 \n \n \n javax.servlet \n servlet-api \n 2.5 \n \n \n javax.servlet.jsp \n jsp-api \n 2.2 \n \n \n javax.servlet \n jstl \n 1.2 \n \n \n ```\n \n3. 启动web项目(看上一篇文章的 “普通maven项目转为web项目”部分)\n\n## 进行开发\n\n### web.xml配置\n\n在【/web/WEB-INF/web.xml】注册 `DispatcherServlet`\n\n```xml\n\n\n\n\n \n springmvc \n org.springframework.web.servlet.DispatcherServlet \n\n\n \n contextConfigLocation \n classpath:springmvc-servlet.xml \n \n\n\n 1 \n \n\n\n\n\n \n springmvc \n / \n \n \n```\n\n\n\n### 添加Spring MVC配置文件\n\n\n\n在【resources】下编写SpringMvc的配置文件,名称要按照官方的格式:`[servlet-name]-servlet.xml`,在本项目中为 `springmvc-servlet.xml`\n\n添加spring头文件\n\n```xml\n\n\n\n \n```\n\n添加处理映射器\n\n```xml\n\n```\n\n添加处理适配器\n\n```xml\n\n```\n\n添加视图解析器\n\n```xml\n\n\n \n \n \n \n \n```\n\n\n\n### 添加Controller类\n\n在包【com.ajream.controller】编写`HelloController`类,用于具体操作业务\n\n```java\npackage com.ajream.controller;\n\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n//实现Controller接口\npublic class HelloController implements Controller {\n// 重写handleRequest方法\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n// 创建模型和视图\n ModelAndView mv = new ModelAndView();\n// 添加对象到模型和视图中\n mv.addObject(\"msg\",\"Hello SpringMVC\");\n// 指定哪个模型视图\n mv.setViewName(\"hello\"); //自动拼接成路径: /WEB-INF/jsp/hello.jsp\n return mv;\n }\n}\n\n```\n\n\n\n然后将 `HelloController`类交给SpringIOC,注册到 beans 中\n\n```xml\n\n```\n\n\n\n### 创建视图层\n\n创建【/web/WEB-INF/jsp/hello.xml】视图文件,即请求后要跳转到的文件,添加以下内容\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n Title \n\n\n ${msg}\n\n\n\n```\n\n\n\n### 运行Tomcat/访问\n\n配置Tomcat,运行,访问 `http://localhost:8083/hello`\n\n![image-20210903225933025](https://gitee.com/ajream/images/raw/master/img/20210903225934_image-20210903225933025.png)\n\n\n\n\n\n\n\n## 404解决办法\n\n1. 查看控制台输出,看一下是不是缺少了什么jar包。\n\n2. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!\n\n {%folding blue, 点击查看解决办法%}\n\n \n\n 点击工程结构\n\n ![image-20210903224853189](https://gitee.com/ajream/images/raw/master/img/20210903224857_image-20210903224853189.png)\n\n \n\n 选择Artifacts,及自己的project,创建lib文件夹,并导入包\n\n ![image-20210903225354211](https://gitee.com/ajream/images/raw/master/img/20210903225355_image-20210903225354211.png)\n\n {%endfolding%}\n\n3. 重启Tomcat 即可解决!\n\n\n\n","source":"_posts/SpringMVC/springMVC(二)HelloMVC.md","raw":"---\ntitle: SpringMVC(二)HelloMVC\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: SpringMVC的HelloWorld程序\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: a4682555\ndate: 2021-09-03 10:31:26\n---\n\n\n\n\n\n[项目地址springMVC-02-hellomvc](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 环境配置\n\n1. 创建一个普通maven项目 `springmvc-02-hellomvc`\n\n2. 导入依赖\n\n ```xml\n \n \n org.springframework \n spring-webmvc \n 5.2.9.RELEASE \n \n \n junit \n junit \n 4.13 \n \n \n javax.servlet \n servlet-api \n 2.5 \n \n \n javax.servlet.jsp \n jsp-api \n 2.2 \n \n \n javax.servlet \n jstl \n 1.2 \n \n \n ```\n \n3. 启动web项目(看上一篇文章的 “普通maven项目转为web项目”部分)\n\n## 进行开发\n\n### web.xml配置\n\n在【/web/WEB-INF/web.xml】注册 `DispatcherServlet`\n\n```xml\n\n\n\n\n \n springmvc \n org.springframework.web.servlet.DispatcherServlet \n\n\n \n contextConfigLocation \n classpath:springmvc-servlet.xml \n \n\n\n 1 \n \n\n\n\n\n \n springmvc \n / \n \n \n```\n\n\n\n### 添加Spring MVC配置文件\n\n\n\n在【resources】下编写SpringMvc的配置文件,名称要按照官方的格式:`[servlet-name]-servlet.xml`,在本项目中为 `springmvc-servlet.xml`\n\n添加spring头文件\n\n```xml\n\n\n\n \n```\n\n添加处理映射器\n\n```xml\n\n```\n\n添加处理适配器\n\n```xml\n\n```\n\n添加视图解析器\n\n```xml\n\n\n \n \n \n \n \n```\n\n\n\n### 添加Controller类\n\n在包【com.ajream.controller】编写`HelloController`类,用于具体操作业务\n\n```java\npackage com.ajream.controller;\n\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n//实现Controller接口\npublic class HelloController implements Controller {\n// 重写handleRequest方法\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n// 创建模型和视图\n ModelAndView mv = new ModelAndView();\n// 添加对象到模型和视图中\n mv.addObject(\"msg\",\"Hello SpringMVC\");\n// 指定哪个模型视图\n mv.setViewName(\"hello\"); //自动拼接成路径: /WEB-INF/jsp/hello.jsp\n return mv;\n }\n}\n\n```\n\n\n\n然后将 `HelloController`类交给SpringIOC,注册到 beans 中\n\n```xml\n\n```\n\n\n\n### 创建视图层\n\n创建【/web/WEB-INF/jsp/hello.xml】视图文件,即请求后要跳转到的文件,添加以下内容\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n Title \n\n\n ${msg}\n\n\n\n```\n\n\n\n### 运行Tomcat/访问\n\n配置Tomcat,运行,访问 `http://localhost:8083/hello`\n\n![image-20210903225933025](https://gitee.com/ajream/images/raw/master/img/20210903225934_image-20210903225933025.png)\n\n\n\n\n\n\n\n## 404解决办法\n\n1. 查看控制台输出,看一下是不是缺少了什么jar包。\n\n2. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!\n\n {%folding blue, 点击查看解决办法%}\n\n \n\n 点击工程结构\n\n ![image-20210903224853189](https://gitee.com/ajream/images/raw/master/img/20210903224857_image-20210903224853189.png)\n\n \n\n 选择Artifacts,及自己的project,创建lib文件夹,并导入包\n\n ![image-20210903225354211](https://gitee.com/ajream/images/raw/master/img/20210903225355_image-20210903225354211.png)\n\n {%endfolding%}\n\n3. 重启Tomcat 即可解决!\n\n\n\n","slug":"SpringMVC/springMVC(二)HelloMVC","published":1,"updated":"2021-09-05T10:33:58.666Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q7004takvecc6ph7mb","content":"项目地址springMVC-02-hellomvc
\n环境配置 \n创建一个普通maven项目 springmvc-02-hellomvc
\n \n导入依赖
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 5.2.9.RELEASE</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > servlet-api</artifactId > <version > 2.5</version > </dependency > <dependency > <groupId > javax.servlet.jsp</groupId > <artifactId > jsp-api</artifactId > <version > 2.2</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > jstl</artifactId > <version > 1.2</version > </dependency > </dependencies >
\n \n启动web项目(看上一篇文章的 “普通maven项目转为web项目”部分)
\n \n \n进行开发 web.xml配置 在【/web/WEB-INF/web.xml】注册 DispatcherServlet
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > springmvc</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:springmvc-servlet.xml</param-value > </init-param > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > springmvc</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > </web-app >
\n添加Spring MVC配置文件 在【resources】下编写SpringMvc的配置文件,名称要按照官方的格式:[servlet-name]-servlet.xml
,在本项目中为 springmvc-servlet.xml
\n添加spring头文件
\n1 2 3 4 5 6 7 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" ></beans >
\n添加处理映射器
\n1 <bean class ="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
\n添加处理适配器
\n1 <bean class ="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
\n添加视图解析器
\n1 2 3 4 5 6 7 <bean id ="internalResourceViewResolver" class ="org.springframework.web.servlet.view.InternalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean >
\n添加Controller类 在包【com.ajream.controller】编写HelloController
类,用于具体操作业务
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.ajream.controller;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class HelloController implements Controller { @Override public ModelAndView handleRequest (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg" ,"Hello SpringMVC" ); mv.setViewName("hello" ); return mv; } }
\n然后将 HelloController
类交给SpringIOC,注册到 beans 中
\n1 <bean id ="/hello" class ="com.ajream.controller.HelloController" />
\n创建视图层 创建【/web/WEB-INF/jsp/hello.xml】视图文件,即请求后要跳转到的文件,添加以下内容
\n1 2 3 4 5 6 7 8 9 10 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html > <head > <title > Title</title > </head > <body > ${msg} </body > </html >
\n运行Tomcat/访问 配置Tomcat,运行,访问 http://localhost:8083/hello
\n
\n404解决办法 \n查看控制台输出,看一下是不是缺少了什么jar包。
\n \n如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!
\n 点击查看解决办法 \n \n
点击工程结构
选择Artifacts,及自己的project,创建lib文件夹,并导入包
\n
\n \n \n重启Tomcat 即可解决!
\n \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"项目地址springMVC-02-hellomvc
\n环境配置 \n创建一个普通maven项目 springmvc-02-hellomvc
\n \n导入依赖
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 5.2.9.RELEASE</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > servlet-api</artifactId > <version > 2.5</version > </dependency > <dependency > <groupId > javax.servlet.jsp</groupId > <artifactId > jsp-api</artifactId > <version > 2.2</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > jstl</artifactId > <version > 1.2</version > </dependency > </dependencies >
\n \n启动web项目(看上一篇文章的 “普通maven项目转为web项目”部分)
\n \n \n进行开发 web.xml配置 在【/web/WEB-INF/web.xml】注册 DispatcherServlet
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > springmvc</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:springmvc-servlet.xml</param-value > </init-param > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > springmvc</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > </web-app >
\n添加Spring MVC配置文件 在【resources】下编写SpringMvc的配置文件,名称要按照官方的格式:[servlet-name]-servlet.xml
,在本项目中为 springmvc-servlet.xml
\n添加spring头文件
\n1 2 3 4 5 6 7 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" ></beans >
\n添加处理映射器
\n1 <bean class ="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
\n添加处理适配器
\n1 <bean class ="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
\n添加视图解析器
\n1 2 3 4 5 6 7 <bean id ="internalResourceViewResolver" class ="org.springframework.web.servlet.view.InternalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean >
\n添加Controller类 在包【com.ajream.controller】编写HelloController
类,用于具体操作业务
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.ajream.controller;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class HelloController implements Controller { @Override public ModelAndView handleRequest (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg" ,"Hello SpringMVC" ); mv.setViewName("hello" ); return mv; } }
\n然后将 HelloController
类交给SpringIOC,注册到 beans 中
\n1 <bean id ="/hello" class ="com.ajream.controller.HelloController" />
\n创建视图层 创建【/web/WEB-INF/jsp/hello.xml】视图文件,即请求后要跳转到的文件,添加以下内容
\n1 2 3 4 5 6 7 8 9 10 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html > <head > <title > Title</title > </head > <body > ${msg} </body > </html >
\n运行Tomcat/访问 配置Tomcat,运行,访问 http://localhost:8083/hello
\n
\n404解决办法 \n查看控制台输出,看一下是不是缺少了什么jar包。
\n \n如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!
\n 点击查看解决办法 \n \n
点击工程结构
选择Artifacts,及自己的project,创建lib文件夹,并导入包
\n
\n \n \n重启Tomcat 即可解决!
\n \n \n"},{"title":"SpringMVC(五)请求结果的跳转方式","description":"请求结果的跳转方式:转发、重定向","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"1eb33081","date":"2021-09-04T07:26:20.000Z","_content":"\n\n\n[项目地址springMVC-05-resultgo](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 理解概念\n\n转发:将某一信息转发到当前访问路径的页面\n\n重定向:从当前页面跳转到另一页面\n\n\n\n> 转发相当于:张三向你借钱,但是你兜里没钱,所以你去找李四借到钱之后借给张三。对于张三而言并不知道你的钱是和李四借的。\n>\n> 重定向相当于:张三向你借钱,你兜里没钱,你告诉他李四有钱,然后张三去找李四借钱。\n\n## ModelAndView\n\n设置ModelAndView对象 , 根据view的名称, 和视图解析器跳到指定的页面。\n\n比如:\n\n```java\npublic ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\");\n return mv;\n}\n```\n\n\n\n视图解析器:\n\n```xml\n\n\n \n \n \n \n \n```\n\n\n\n解析过程:{视图解析器前缀} + viewName +{视图解析器后缀},即:`\"/WEB-INF/jsp/\"`+ `\"test1\"` + `\".jsp\"` --> `\"/WEB-INF/jsp/test.jsp\"`\n\n\n\n\n\n## ServletAPI\n\n通过设置ServletAPI(很少用了) , 不需要视图解析器\n\n1. 通过HttpServletResponse进行输出\n2. 通过HttpServletResponse实现重定向\n3. 通过HttpServletResponse实现转发\n\n\n\n```java\n@Controller\npublic class ResultGo {\n @RequestMapping(\"/result/t1\")\n public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {\n rsp.getWriter().println(\"Hello,Spring BY servlet API\");\n }\n @RequestMapping(\"/result/t2\")\n public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {\n// 重定向\n rsp.sendRedirect(\"/index.jsp\");\n }\n @RequestMapping(\"/result/t3\")\n public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {\n //转发\n req.setAttribute(\"msg\",\"/result/t3\");\n req.getRequestDispatcher(\"/WEB-INF/jsp/test.jsp\").forward(req,rsp);\n }\n}\n```\n\n\n\n\n\n## SpringMVC\n\n### 通过SpringMVC来实现转发和重定向-无视图解析器\n\n测试前,需要将视图解析器注释掉\n\n\n\n```java\n@Controller\npublic class ResultSpringMVC {\n @RequestMapping(\"/rsm/t1\")\n public String test1(){\n// 转发一\n return \"/index.jsp\";\n }\n\n @RequestMapping(\"/rsm/t2\")\n public String test2(){\n// 转发二\n return \"forward:/index.jsp\";\n }\n\n @RequestMapping(\"/rsm/t3\")\n public String test3(){\n// 重定向\n return \"redirect:/index.jsp\";\n\n }\n}\n\n```\n\n转发:\n\n![image-20210904160714983](https://gitee.com/ajream/images/raw/master/img/20210904160718_image-20210904160714983.png)\n\n\n\n\n\n重定向:访问 `http://localhost:8083/rsm/t3` 后重定向到 `http://localhost:8083/index.jsp`\n\n![image-20210904160851220](https://gitee.com/ajream/images/raw/master/img/20210904160852_image-20210904160851220.png)\n\n\n\n\n\n### 使用视图解析器\n\n添加视图解析器\n\n```xml\n\n \n \n \n```\n\n\n\n```java\n@Controller\npublic class ResultSpringMVC2 {\n @RequestMapping(\"/rsm2/t1\")\n public String test1(){\n //转发\n return \"test\"; //通过视图解析后得到路径 /WEB-INF/jsp/test.jsp\n }\n @RequestMapping(\"/rsm2/t2\")\n public String test2(){\n //重定向\n return \"redirect:/index.jsp\"; // 使用重定向关键字\"redirect\"后不会经过视图解析器\n //return \"redirect:hello.do\"; //hello.do为另一个请求/\n }\n}\n```\n\n\n\n> 说明:\n>\n> `return \"test\"` 通过视图解析后得到路径 /WEB-INF/jsp/test.jsp\n>\n> `return \"redirect:/index.jsp\"` 使用重定向关键字\"redirect\"后不会经过视图解析器\n\n","source":"_posts/SpringMVC/springMVC(五)结果跳转方式.md","raw":"---\ntitle: SpringMVC(五)请求结果的跳转方式\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: 请求结果的跳转方式:转发、重定向\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: 1eb33081\ndate: 2021-09-04 15:26:20\n---\n\n\n\n[项目地址springMVC-05-resultgo](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 理解概念\n\n转发:将某一信息转发到当前访问路径的页面\n\n重定向:从当前页面跳转到另一页面\n\n\n\n> 转发相当于:张三向你借钱,但是你兜里没钱,所以你去找李四借到钱之后借给张三。对于张三而言并不知道你的钱是和李四借的。\n>\n> 重定向相当于:张三向你借钱,你兜里没钱,你告诉他李四有钱,然后张三去找李四借钱。\n\n## ModelAndView\n\n设置ModelAndView对象 , 根据view的名称, 和视图解析器跳到指定的页面。\n\n比如:\n\n```java\npublic ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\");\n return mv;\n}\n```\n\n\n\n视图解析器:\n\n```xml\n\n\n \n \n \n \n \n```\n\n\n\n解析过程:{视图解析器前缀} + viewName +{视图解析器后缀},即:`\"/WEB-INF/jsp/\"`+ `\"test1\"` + `\".jsp\"` --> `\"/WEB-INF/jsp/test.jsp\"`\n\n\n\n\n\n## ServletAPI\n\n通过设置ServletAPI(很少用了) , 不需要视图解析器\n\n1. 通过HttpServletResponse进行输出\n2. 通过HttpServletResponse实现重定向\n3. 通过HttpServletResponse实现转发\n\n\n\n```java\n@Controller\npublic class ResultGo {\n @RequestMapping(\"/result/t1\")\n public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {\n rsp.getWriter().println(\"Hello,Spring BY servlet API\");\n }\n @RequestMapping(\"/result/t2\")\n public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {\n// 重定向\n rsp.sendRedirect(\"/index.jsp\");\n }\n @RequestMapping(\"/result/t3\")\n public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {\n //转发\n req.setAttribute(\"msg\",\"/result/t3\");\n req.getRequestDispatcher(\"/WEB-INF/jsp/test.jsp\").forward(req,rsp);\n }\n}\n```\n\n\n\n\n\n## SpringMVC\n\n### 通过SpringMVC来实现转发和重定向-无视图解析器\n\n测试前,需要将视图解析器注释掉\n\n\n\n```java\n@Controller\npublic class ResultSpringMVC {\n @RequestMapping(\"/rsm/t1\")\n public String test1(){\n// 转发一\n return \"/index.jsp\";\n }\n\n @RequestMapping(\"/rsm/t2\")\n public String test2(){\n// 转发二\n return \"forward:/index.jsp\";\n }\n\n @RequestMapping(\"/rsm/t3\")\n public String test3(){\n// 重定向\n return \"redirect:/index.jsp\";\n\n }\n}\n\n```\n\n转发:\n\n![image-20210904160714983](https://gitee.com/ajream/images/raw/master/img/20210904160718_image-20210904160714983.png)\n\n\n\n\n\n重定向:访问 `http://localhost:8083/rsm/t3` 后重定向到 `http://localhost:8083/index.jsp`\n\n![image-20210904160851220](https://gitee.com/ajream/images/raw/master/img/20210904160852_image-20210904160851220.png)\n\n\n\n\n\n### 使用视图解析器\n\n添加视图解析器\n\n```xml\n\n \n \n \n```\n\n\n\n```java\n@Controller\npublic class ResultSpringMVC2 {\n @RequestMapping(\"/rsm2/t1\")\n public String test1(){\n //转发\n return \"test\"; //通过视图解析后得到路径 /WEB-INF/jsp/test.jsp\n }\n @RequestMapping(\"/rsm2/t2\")\n public String test2(){\n //重定向\n return \"redirect:/index.jsp\"; // 使用重定向关键字\"redirect\"后不会经过视图解析器\n //return \"redirect:hello.do\"; //hello.do为另一个请求/\n }\n}\n```\n\n\n\n> 说明:\n>\n> `return \"test\"` 通过视图解析后得到路径 /WEB-INF/jsp/test.jsp\n>\n> `return \"redirect:/index.jsp\"` 使用重定向关键字\"redirect\"后不会经过视图解析器\n\n","slug":"SpringMVC/springMVC(五)结果跳转方式","published":1,"updated":"2021-09-05T10:34:11.953Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q9004wakve8yyz5ix5","content":"项目地址springMVC-05-resultgo
\n理解概念 转发:将某一信息转发到当前访问路径的页面
\n重定向:从当前页面跳转到另一页面
\n\n转发相当于:张三向你借钱,但是你兜里没钱,所以你去找李四借到钱之后借给张三。对于张三而言并不知道你的钱是和李四借的。
\n重定向相当于:张三向你借钱,你兜里没钱,你告诉他李四有钱,然后张三去找李四借钱。
\n \nModelAndView 设置ModelAndView对象 , 根据view的名称, 和视图解析器跳到指定的页面。
\n比如:
\n1 2 3 4 5 6 public ModelAndView handleRequest (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg" , "这是ControllerTest1" ); mv.setViewName("test1" ); return mv; }
\n视图解析器:
\n1 2 3 4 5 6 7 8 <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean >
\n解析过程:{视图解析器前缀} + viewName +{视图解析器后缀},即:"/WEB-INF/jsp/"
+ "test1"
+ ".jsp"
—> "/WEB-INF/jsp/test.jsp"
\nServletAPI 通过设置ServletAPI(很少用了) , 不需要视图解析器
\n\n通过HttpServletResponse进行输出 \n通过HttpServletResponse实现重定向 \n通过HttpServletResponse实现转发 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Controller public class ResultGo { @RequestMapping("/result/t1") public void test1 (HttpServletRequest req, HttpServletResponse rsp) throws IOException { rsp.getWriter().println("Hello,Spring BY servlet API" ); } @RequestMapping("/result/t2") public void test2 (HttpServletRequest req, HttpServletResponse rsp) throws IOException { rsp.sendRedirect("/index.jsp" ); } @RequestMapping("/result/t3") public void test3 (HttpServletRequest req, HttpServletResponse rsp) throws Exception { req.setAttribute("msg" ,"/result/t3" ); req.getRequestDispatcher("/WEB-INF/jsp/test.jsp" ).forward(req,rsp); } }
\nSpringMVC 通过SpringMVC来实现转发和重定向-无视图解析器 测试前,需要将视图解析器注释掉
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Controller public class ResultSpringMVC { @RequestMapping("/rsm/t1") public String test1 () { return "/index.jsp" ; } @RequestMapping("/rsm/t2") public String test2 () { return "forward:/index.jsp" ; } @RequestMapping("/rsm/t3") public String test3 () { return "redirect:/index.jsp" ; } }
\n转发:
\n
\n重定向:访问 http://localhost:8083/rsm/t3
后重定向到 http://localhost:8083/index.jsp
\n
\n使用视图解析器 添加视图解析器
\n1 2 3 4 <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean >
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Controller public class ResultSpringMVC2 { @RequestMapping("/rsm2/t1") public String test1 () { return "test" ; } @RequestMapping("/rsm2/t2") public String test2 () { return "redirect:/index.jsp" ; } }
\n\n说明:
\nreturn "test"
通过视图解析后得到路径 /WEB-INF/jsp/test.jsp
\nreturn "redirect:/index.jsp"
使用重定向关键字”redirect”后不会经过视图解析器
\n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"项目地址springMVC-05-resultgo
\n理解概念 转发:将某一信息转发到当前访问路径的页面
\n重定向:从当前页面跳转到另一页面
\n\n转发相当于:张三向你借钱,但是你兜里没钱,所以你去找李四借到钱之后借给张三。对于张三而言并不知道你的钱是和李四借的。
\n重定向相当于:张三向你借钱,你兜里没钱,你告诉他李四有钱,然后张三去找李四借钱。
\n \nModelAndView 设置ModelAndView对象 , 根据view的名称, 和视图解析器跳到指定的页面。
\n比如:
\n1 2 3 4 5 6 public ModelAndView handleRequest (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg" , "这是ControllerTest1" ); mv.setViewName("test1" ); return mv; }
\n视图解析器:
\n1 2 3 4 5 6 7 8 <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean >
\n解析过程:{视图解析器前缀} + viewName +{视图解析器后缀},即:"/WEB-INF/jsp/"
+ "test1"
+ ".jsp"
—> "/WEB-INF/jsp/test.jsp"
\nServletAPI 通过设置ServletAPI(很少用了) , 不需要视图解析器
\n\n通过HttpServletResponse进行输出 \n通过HttpServletResponse实现重定向 \n通过HttpServletResponse实现转发 \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Controller public class ResultGo { @RequestMapping("/result/t1") public void test1 (HttpServletRequest req, HttpServletResponse rsp) throws IOException { rsp.getWriter().println("Hello,Spring BY servlet API" ); } @RequestMapping("/result/t2") public void test2 (HttpServletRequest req, HttpServletResponse rsp) throws IOException { rsp.sendRedirect("/index.jsp" ); } @RequestMapping("/result/t3") public void test3 (HttpServletRequest req, HttpServletResponse rsp) throws Exception { req.setAttribute("msg" ,"/result/t3" ); req.getRequestDispatcher("/WEB-INF/jsp/test.jsp" ).forward(req,rsp); } }
\nSpringMVC 通过SpringMVC来实现转发和重定向-无视图解析器 测试前,需要将视图解析器注释掉
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Controller public class ResultSpringMVC { @RequestMapping("/rsm/t1") public String test1 () { return "/index.jsp" ; } @RequestMapping("/rsm/t2") public String test2 () { return "forward:/index.jsp" ; } @RequestMapping("/rsm/t3") public String test3 () { return "redirect:/index.jsp" ; } }
\n转发:
\n
\n重定向:访问 http://localhost:8083/rsm/t3
后重定向到 http://localhost:8083/index.jsp
\n
\n使用视图解析器 添加视图解析器
\n1 2 3 4 <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean >
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Controller public class ResultSpringMVC2 { @RequestMapping("/rsm2/t1") public String test1 () { return "test" ; } @RequestMapping("/rsm2/t2") public String test2 () { return "redirect:/index.jsp" ; } }
\n\n说明:
\nreturn "test"
通过视图解析后得到路径 /WEB-INF/jsp/test.jsp
\nreturn "redirect:/index.jsp"
使用重定向关键字”redirect”后不会经过视图解析器
\n \n"},{"title":"SpringMVC(四)Controller与RestFul","description":"Controller详解","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"57ac54fe","date":"2021-09-04T02:31:11.000Z","_content":"\n\n\n\n\n[项目地址springMVC-04-controller](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 创建工程\n\n首先创建一个项目,运行观察效果\n\n\n\n导入依赖\n\n**配置web.xml**\n\n```xml\n\n\n\n \n \n SpringMVC \n org.springframework.web.servlet.DispatcherServlet \n\n \n \n contextConfigLocation \n \n classpath:springmvc-servlet.xml \n \n\n \n 1 \n \n\n \n \n SpringMVC \n / \n \n\n \n```\n\n\n\n**配置SpringMVC配置文件**\n\n创建 `resources/springmvc-servlet.xml`\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n\n \n```\n\n\n\n\n\n创建 `ControllerTest1`\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class ControllerTest1 implements Controller {\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\");\n return mv;\n }\n}\n\n```\n\n\n\n注册 bean\n\n```xml\n\n\n```\n\n\n\n\n\n配置tomcat,添加lib,运行,访问 `http://localhost:8083/t1`\n\n\n\n![image-20210904104223512](https://gitee.com/ajream/images/raw/master/img/20210904104231_image-20210904104223512.png)\n\n\n\n\n\n## 解释\n\n\n\n### 控制器Controller\n\n- 控制器复杂提供访问应用程序的行为,通常通过接口定义 或注解 定义两种方法实现\n- 控制器负责解析用户的请求并将其转换为一个模型\n- 在Spring MVC中一个控制器类可以包含多个方法\n- 在Spring MVC中,对于Controller的配置方式有很多种\n\n\n\n\n\n### 实现Controller\n\n通常通过接口定义 或注解 定义两种方法实现\n\n#### 通过接口实现\n\nController是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;\n\n```java\n//实现该接口的类获得控制器功能\npublic interface Controller {\n //处理请求且返回一个模型与视图对象\n ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;\n}\n```\n\n\n\n例如上面的例子:\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n//实现Controller接口\npublic class ControllerTest1 implements Controller {\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n //返回一个模型视图对象\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\"); //WEB-INF/jsp/test1.jsp\n return mv;\n }\n}\n\n```\n\n\n\n然后去spring MVC的配置文件注册bean\n\n```xml\n\n```\n\n\n\n> 说明:\n>\n> 实现接口Controller定义控制器是较老的办法\n>\n> 缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦\n\n\n\n\n\n#### 通过注解实现\n\n\n\n- `@Controller`注解类型用于声明Spring类的实例是一个控制器\n\n- Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描\n\n ```xml\n \n \n ```\n\n\n\n例如,创建一个 `ControllerTest2`,使用注解来实现Controller\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n//@Controller注解的类会自动添加到Spring上下文中\n@Controller\npublic class ControllerTest2{\n //映射访问路径\n @RequestMapping(\"/t2\")\n public String index(Model model){\n //Spring MVC会自动实例化一个Model对象用于向视图中传值\n model.addAttribute(\"msg\", \"这是ControllerTest2\");\n //返回视图位置\n return \"test1\";\n }\n}\n```\n\n添加注解支持\n\n```xml\n\n```\n\n运行,访问:`http://localhost:8083/t2`\n\n\n\n![image-20210904110819742](https://gitee.com/ajream/images/raw/master/img/20210904110823_image-20210904110819742.png)\n\n\n\n\n\n> 可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。\n\n\n\n\n\n\n\n### 注解RequestMapping\n\n`@RequestMapping`注解用于映射url到控制器类或一个特定的处理程序方法。\n\n可用于类或方法 上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。\n\n\n\n例如\n\n只用于方法上,访问路径是 `http://localhost:8080/h1`\n\n```java\n@Controller\npublic class TestController {\n @RequestMapping(\"/h1\")\n public String test(){\n return \"test\";\n }\n}\n```\n\n\n\n同时用于类和方法上,访问路径是 `http://localhost:8080/admin/h1`\n\n```java\n@Controller\n@RequestMapping(\"/admin\")\npublic class TestController {\n @RequestMapping(\"/h1\")\n public String test(){\n return \"test\";\n }\n}\n```\n\n\n\n\n\n## RestFul 风格\n\n\n\nRestful就是一个资源定位 及资源操作 的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。\n\n\n\nRestFul风格即对访问路径进行的优化\n\n比如:如果需要传参,传统访问路径可能是:`http://localhost:8080/admin?name=ajream&password=123456`\n\n而RestFul风格的访问路径是:`http://localhost:8080/admin/ajream/123456`\n\n\n\n接下来用个例子来说明:\n\n新建一个类 `RestFulController`\n\n```java\n@Controller\npublic class RestFulController {\n\n}\n```\n\n在Spring MVC中可以使用 `@PathVariable` 注解,让方法参数的值对应绑定到一个URI模板变量上。\n\n```java\n@Controller\npublic class RestFulController {\n //映射访问路径\n @RequestMapping(\"/commit/{p1}/{p2}\")\n public String index(@PathVariable int p1, @PathVariable int p2, Model model){\n int result = p1+p2;\n //Spring MVC会自动实例化一个Model对象用于向视图中传值\n model.addAttribute(\"msg\", \"Result:\"+result);\n //返回视图位置\n return \"test1\";\n }\n}\n```\n\n{%note info flat%}\n\n注意 p1, p2 均为int类型变量,如果传入 `a`, `b` ...这些字符就会出错\n\n{%endnote%}\n\n\n\n运行测试:\n\n \n\n\n\n \n\n\n\n## 使用method属性指定请求方式\n\n\n\n请求方式常用的有:get、post、...\n\n在注解 `@RequestMapping` 使用`mothod` 属性可以指定请求方式\n\n如:\n\n```java\n@Controller\npublic class RestFulController {\n\t//映射访问路径,必须是POST请求\n @RequestMapping(value = \"/t3/{a}/{b}\", method = RequestMethod.POST)\n public String test(@PathVariable int a, @PathVariable int b, Model model){\n\n int r = a + b;\n model.addAttribute(\"msg\",\"Result: \" + r);\n\n return \"test1\";\n\n }\n}\n```\n\n使用浏览器地址栏进行访问默认是Get请求,会报错405:\n\n![image-20210904150415014](https://gitee.com/ajream/images/raw/master/img/20210904150419_image-20210904150415014.png)\n\n\n\n可以同时使用多种请求方式:\n\n```java\n@RequestMapping(value = \"/t3/{a}/{b}\", method = {RequestMethod.POST, RequestMethod.GET})\n```\n\n\n\n\n\n也可以使用对应请求方式的注解(可以把它们叫做组合注解 ):\n\n```java\n@GetMapping(\"/t1\") \t\t//等价于 @RequestMapping(value=\"t1\", method=RequestMethod.GET)\n@PostMapping(\"/t1\")\n@PutMapping(\"/t1\")\n@DeleteMapping(\"/t1\")\n@PatchMapping(\"/t1\")\n```\n\n例如\n\n```java\n@GetMapping(\"/t3/{a}/{b}\")\npublic String test(@PathVariable int a, @PathVariable int b, Model model){\n int r = a + b;\n model.addAttribute(\"msg\",\"Result: \" + r);\n return \"test1\";\n\n}\n```\n\n","source":"_posts/SpringMVC/springMVC(四)Controller与restful.md","raw":"---\ntitle: SpringMVC(四)Controller与RestFul\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: Controller详解\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: 57ac54fe\ndate: 2021-09-04 10:31:11\n---\n\n\n\n\n\n[项目地址springMVC-04-controller](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 创建工程\n\n首先创建一个项目,运行观察效果\n\n\n\n导入依赖\n\n**配置web.xml**\n\n```xml\n\n\n\n \n \n SpringMVC \n org.springframework.web.servlet.DispatcherServlet \n\n \n \n contextConfigLocation \n \n classpath:springmvc-servlet.xml \n \n\n \n 1 \n \n\n \n \n SpringMVC \n / \n \n\n \n```\n\n\n\n**配置SpringMVC配置文件**\n\n创建 `resources/springmvc-servlet.xml`\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n\n \n```\n\n\n\n\n\n创建 `ControllerTest1`\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class ControllerTest1 implements Controller {\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\");\n return mv;\n }\n}\n\n```\n\n\n\n注册 bean\n\n```xml\n\n\n```\n\n\n\n\n\n配置tomcat,添加lib,运行,访问 `http://localhost:8083/t1`\n\n\n\n![image-20210904104223512](https://gitee.com/ajream/images/raw/master/img/20210904104231_image-20210904104223512.png)\n\n\n\n\n\n## 解释\n\n\n\n### 控制器Controller\n\n- 控制器复杂提供访问应用程序的行为,通常通过接口定义 或注解 定义两种方法实现\n- 控制器负责解析用户的请求并将其转换为一个模型\n- 在Spring MVC中一个控制器类可以包含多个方法\n- 在Spring MVC中,对于Controller的配置方式有很多种\n\n\n\n\n\n### 实现Controller\n\n通常通过接口定义 或注解 定义两种方法实现\n\n#### 通过接口实现\n\nController是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;\n\n```java\n//实现该接口的类获得控制器功能\npublic interface Controller {\n //处理请求且返回一个模型与视图对象\n ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;\n}\n```\n\n\n\n例如上面的例子:\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n//实现Controller接口\npublic class ControllerTest1 implements Controller {\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n //返回一个模型视图对象\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\"); //WEB-INF/jsp/test1.jsp\n return mv;\n }\n}\n\n```\n\n\n\n然后去spring MVC的配置文件注册bean\n\n```xml\n\n```\n\n\n\n> 说明:\n>\n> 实现接口Controller定义控制器是较老的办法\n>\n> 缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦\n\n\n\n\n\n#### 通过注解实现\n\n\n\n- `@Controller`注解类型用于声明Spring类的实例是一个控制器\n\n- Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描\n\n ```xml\n \n \n ```\n\n\n\n例如,创建一个 `ControllerTest2`,使用注解来实现Controller\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n//@Controller注解的类会自动添加到Spring上下文中\n@Controller\npublic class ControllerTest2{\n //映射访问路径\n @RequestMapping(\"/t2\")\n public String index(Model model){\n //Spring MVC会自动实例化一个Model对象用于向视图中传值\n model.addAttribute(\"msg\", \"这是ControllerTest2\");\n //返回视图位置\n return \"test1\";\n }\n}\n```\n\n添加注解支持\n\n```xml\n\n```\n\n运行,访问:`http://localhost:8083/t2`\n\n\n\n![image-20210904110819742](https://gitee.com/ajream/images/raw/master/img/20210904110823_image-20210904110819742.png)\n\n\n\n\n\n> 可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。\n\n\n\n\n\n\n\n### 注解RequestMapping\n\n`@RequestMapping`注解用于映射url到控制器类或一个特定的处理程序方法。\n\n可用于类或方法 上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。\n\n\n\n例如\n\n只用于方法上,访问路径是 `http://localhost:8080/h1`\n\n```java\n@Controller\npublic class TestController {\n @RequestMapping(\"/h1\")\n public String test(){\n return \"test\";\n }\n}\n```\n\n\n\n同时用于类和方法上,访问路径是 `http://localhost:8080/admin/h1`\n\n```java\n@Controller\n@RequestMapping(\"/admin\")\npublic class TestController {\n @RequestMapping(\"/h1\")\n public String test(){\n return \"test\";\n }\n}\n```\n\n\n\n\n\n## RestFul 风格\n\n\n\nRestful就是一个资源定位 及资源操作 的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。\n\n\n\nRestFul风格即对访问路径进行的优化\n\n比如:如果需要传参,传统访问路径可能是:`http://localhost:8080/admin?name=ajream&password=123456`\n\n而RestFul风格的访问路径是:`http://localhost:8080/admin/ajream/123456`\n\n\n\n接下来用个例子来说明:\n\n新建一个类 `RestFulController`\n\n```java\n@Controller\npublic class RestFulController {\n\n}\n```\n\n在Spring MVC中可以使用 `@PathVariable` 注解,让方法参数的值对应绑定到一个URI模板变量上。\n\n```java\n@Controller\npublic class RestFulController {\n //映射访问路径\n @RequestMapping(\"/commit/{p1}/{p2}\")\n public String index(@PathVariable int p1, @PathVariable int p2, Model model){\n int result = p1+p2;\n //Spring MVC会自动实例化一个Model对象用于向视图中传值\n model.addAttribute(\"msg\", \"Result:\"+result);\n //返回视图位置\n return \"test1\";\n }\n}\n```\n\n{%note info flat%}\n\n注意 p1, p2 均为int类型变量,如果传入 `a`, `b` ...这些字符就会出错\n\n{%endnote%}\n\n\n\n运行测试:\n\n \n\n\n\n \n\n\n\n## 使用method属性指定请求方式\n\n\n\n请求方式常用的有:get、post、...\n\n在注解 `@RequestMapping` 使用`mothod` 属性可以指定请求方式\n\n如:\n\n```java\n@Controller\npublic class RestFulController {\n\t//映射访问路径,必须是POST请求\n @RequestMapping(value = \"/t3/{a}/{b}\", method = RequestMethod.POST)\n public String test(@PathVariable int a, @PathVariable int b, Model model){\n\n int r = a + b;\n model.addAttribute(\"msg\",\"Result: \" + r);\n\n return \"test1\";\n\n }\n}\n```\n\n使用浏览器地址栏进行访问默认是Get请求,会报错405:\n\n![image-20210904150415014](https://gitee.com/ajream/images/raw/master/img/20210904150419_image-20210904150415014.png)\n\n\n\n可以同时使用多种请求方式:\n\n```java\n@RequestMapping(value = \"/t3/{a}/{b}\", method = {RequestMethod.POST, RequestMethod.GET})\n```\n\n\n\n\n\n也可以使用对应请求方式的注解(可以把它们叫做组合注解 ):\n\n```java\n@GetMapping(\"/t1\") \t\t//等价于 @RequestMapping(value=\"t1\", method=RequestMethod.GET)\n@PostMapping(\"/t1\")\n@PutMapping(\"/t1\")\n@DeleteMapping(\"/t1\")\n@PatchMapping(\"/t1\")\n```\n\n例如\n\n```java\n@GetMapping(\"/t3/{a}/{b}\")\npublic String test(@PathVariable int a, @PathVariable int b, Model model){\n int r = a + b;\n model.addAttribute(\"msg\",\"Result: \" + r);\n return \"test1\";\n\n}\n```\n\n","slug":"SpringMVC/springMVC(四)Controller与restful","published":1,"updated":"2021-09-05T10:34:07.415Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qb004yakve5qk6a50r","content":"项目地址springMVC-04-controller
\n创建工程 首先创建一个项目,运行观察效果
\n导入依赖
\n配置web.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > SpringMVC</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:springmvc-servlet.xml</param-value > </init-param > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > SpringMVC</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > </web-app >
\n配置SpringMVC配置文件
\n创建 resources/springmvc-servlet.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xmlns:mvc ="http://www.springframework.org/schema/mvc" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd" > <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean > </beans >
\n创建 ControllerTest1
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.ajream.controller;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class ControllerTest1 implements Controller { @Override public ModelAndView handleRequest (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg" , "这是ControllerTest1" ); mv.setViewName("test1" ); return mv; } }
\n注册 bean
\n1 2 <bean id ="/t1" class ="com.ajream.controller.ControllerTest1" />
\n配置tomcat,添加lib,运行,访问 http://localhost:8083/t1
\n
\n解释 控制器Controller \n控制器复杂提供访问应用程序的行为,通常通过接口定义 或注解 定义两种方法实现 \n控制器负责解析用户的请求并将其转换为一个模型 \n在Spring MVC中一个控制器类可以包含多个方法 \n在Spring MVC中,对于Controller的配置方式有很多种 \n \n实现Controller 通常通过接口定义 或注解 定义两种方法实现
\n通过接口实现 Controller是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;
\n1 2 3 4 5 public interface Controller { ModelAndView handleRequest (HttpServletRequest var1, HttpServletResponse var2) throws Exception ; }
\n例如上面的例子:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.ajream.controller;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class ControllerTest1 implements Controller { @Override public ModelAndView handleRequest (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg" , "这是ControllerTest1" ); mv.setViewName("test1" ); return mv; } }
\n然后去spring MVC的配置文件注册bean
\n1 <bean name ="/t1" class ="com.kuang.controller.ControllerTest1" />
\n\n说明:
\n实现接口Controller定义控制器是较老的办法
\n缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦
\n \n通过注解实现 \n例如,创建一个 ControllerTest2
,使用注解来实现Controller
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.ajream.controller;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;@Controller public class ControllerTest2 { @RequestMapping("/t2") public String index (Model model) { model.addAttribute("msg" , "这是ControllerTest2" ); return "test1" ; } }
\n添加注解支持
\n1 <context:component-scan base-package ="com.ajream.controller" />
\n运行,访问:http://localhost:8083/t2
\n
\n\n可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。
\n \n注解RequestMapping @RequestMapping
注解用于映射url到控制器类或一个特定的处理程序方法。
\n可用于类或方法 上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
\n例如
\n只用于方法上,访问路径是 http://localhost:8080/h1
\n1 2 3 4 5 6 7 @Controller public class TestController { @RequestMapping("/h1") public String test () { return "test" ; } }
\n同时用于类和方法上,访问路径是 http://localhost:8080/admin/h1
\n1 2 3 4 5 6 7 8 @Controller @RequestMapping("/admin") public class TestController { @RequestMapping("/h1") public String test () { return "test" ; } }
\nRestFul 风格 Restful就是一个资源定位 及资源操作 的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
\nRestFul风格即对访问路径进行的优化
\n比如:如果需要传参,传统访问路径可能是:http://localhost:8080/admin?name=ajream&password=123456
\n而RestFul风格的访问路径是:http://localhost:8080/admin/ajream/123456
\n接下来用个例子来说明:
\n新建一个类 RestFulController
\n1 2 3 4 @Controller public class RestFulController {}
\n在Spring MVC中可以使用 @PathVariable
注解,让方法参数的值对应绑定到一个URI模板变量上。
\n1 2 3 4 5 6 7 8 9 10 11 12 @Controller public class RestFulController { @RequestMapping("/commit/{p1}/{p2}") public String index (@PathVariable int p1, @PathVariable int p2, Model model) { int result = p1+p2; model.addAttribute("msg" , "Result:" +result); return "test1" ; } }
\n注意 p1, p2 均为int类型变量,如果传入 a
, b
…这些字符就会出错
\n
\n运行测试:
\n
\n
\n使用method属性指定请求方式 请求方式常用的有:get、post、…
\n在注解 @RequestMapping
使用mothod
属性可以指定请求方式
\n如:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 @Controller public class RestFulController {\t @RequestMapping(value = "/t3/{a}/{b}", method = RequestMethod.POST) public String test (@PathVariable int a, @PathVariable int b, Model model) { int r = a + b; model.addAttribute("msg" ,"Result: " + r); return "test1" ; } }
\n使用浏览器地址栏进行访问默认是Get请求,会报错405:
\n
\n可以同时使用多种请求方式:
\n1 @RequestMapping(value = "/t3/{a}/{b}", method = {RequestMethod.POST, RequestMethod.GET})
\n也可以使用对应请求方式的注解(可以把它们叫做组合注解 ):
\n1 2 3 4 5 @GetMapping("/t1") \t\t@PostMapping("/t1") @PutMapping("/t1") @DeleteMapping("/t1") @PatchMapping("/t1")
\n例如
\n1 2 3 4 5 6 7 @GetMapping("/t3/{a}/{b}") public String test (@PathVariable int a, @PathVariable int b, Model model) { int r = a + b; model.addAttribute("msg" ,"Result: " + r); return "test1" ; }
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"项目地址springMVC-04-controller
\n创建工程 首先创建一个项目,运行观察效果
\n导入依赖
\n配置web.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > SpringMVC</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:springmvc-servlet.xml</param-value > </init-param > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > SpringMVC</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > </web-app >
\n配置SpringMVC配置文件
\n创建 resources/springmvc-servlet.xml
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xmlns:mvc ="http://www.springframework.org/schema/mvc" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd" > <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean > </beans >
\n创建 ControllerTest1
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.ajream.controller;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class ControllerTest1 implements Controller { @Override public ModelAndView handleRequest (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg" , "这是ControllerTest1" ); mv.setViewName("test1" ); return mv; } }
\n注册 bean
\n1 2 <bean id ="/t1" class ="com.ajream.controller.ControllerTest1" />
\n配置tomcat,添加lib,运行,访问 http://localhost:8083/t1
\n
\n解释 控制器Controller \n控制器复杂提供访问应用程序的行为,通常通过接口定义 或注解 定义两种方法实现 \n控制器负责解析用户的请求并将其转换为一个模型 \n在Spring MVC中一个控制器类可以包含多个方法 \n在Spring MVC中,对于Controller的配置方式有很多种 \n \n实现Controller 通常通过接口定义 或注解 定义两种方法实现
\n通过接口实现 Controller是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;
\n1 2 3 4 5 public interface Controller { ModelAndView handleRequest (HttpServletRequest var1, HttpServletResponse var2) throws Exception ; }
\n例如上面的例子:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.ajream.controller;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class ControllerTest1 implements Controller { @Override public ModelAndView handleRequest (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg" , "这是ControllerTest1" ); mv.setViewName("test1" ); return mv; } }
\n然后去spring MVC的配置文件注册bean
\n1 <bean name ="/t1" class ="com.kuang.controller.ControllerTest1" />
\n\n说明:
\n实现接口Controller定义控制器是较老的办法
\n缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦
\n \n通过注解实现 \n例如,创建一个 ControllerTest2
,使用注解来实现Controller
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.ajream.controller;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;@Controller public class ControllerTest2 { @RequestMapping("/t2") public String index (Model model) { model.addAttribute("msg" , "这是ControllerTest2" ); return "test1" ; } }
\n添加注解支持
\n1 <context:component-scan base-package ="com.ajream.controller" />
\n运行,访问:http://localhost:8083/t2
\n
\n\n可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。
\n \n注解RequestMapping @RequestMapping
注解用于映射url到控制器类或一个特定的处理程序方法。
\n可用于类或方法 上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
\n例如
\n只用于方法上,访问路径是 http://localhost:8080/h1
\n1 2 3 4 5 6 7 @Controller public class TestController { @RequestMapping("/h1") public String test () { return "test" ; } }
\n同时用于类和方法上,访问路径是 http://localhost:8080/admin/h1
\n1 2 3 4 5 6 7 8 @Controller @RequestMapping("/admin") public class TestController { @RequestMapping("/h1") public String test () { return "test" ; } }
\nRestFul 风格 Restful就是一个资源定位 及资源操作 的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
\nRestFul风格即对访问路径进行的优化
\n比如:如果需要传参,传统访问路径可能是:http://localhost:8080/admin?name=ajream&password=123456
\n而RestFul风格的访问路径是:http://localhost:8080/admin/ajream/123456
\n接下来用个例子来说明:
\n新建一个类 RestFulController
\n1 2 3 4 @Controller public class RestFulController {}
\n在Spring MVC中可以使用 @PathVariable
注解,让方法参数的值对应绑定到一个URI模板变量上。
\n1 2 3 4 5 6 7 8 9 10 11 12 @Controller public class RestFulController { @RequestMapping("/commit/{p1}/{p2}") public String index (@PathVariable int p1, @PathVariable int p2, Model model) { int result = p1+p2; model.addAttribute("msg" , "Result:" +result); return "test1" ; } }
\n注意 p1, p2 均为int类型变量,如果传入 a
, b
…这些字符就会出错
\n
\n运行测试:
\n
\n
\n使用method属性指定请求方式 请求方式常用的有:get、post、…
\n在注解 @RequestMapping
使用mothod
属性可以指定请求方式
\n如:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 @Controller public class RestFulController {\t @RequestMapping(value = "/t3/{a}/{b}", method = RequestMethod.POST) public String test (@PathVariable int a, @PathVariable int b, Model model) { int r = a + b; model.addAttribute("msg" ,"Result: " + r); return "test1" ; } }
\n使用浏览器地址栏进行访问默认是Get请求,会报错405:
\n
\n可以同时使用多种请求方式:
\n1 @RequestMapping(value = "/t3/{a}/{b}", method = {RequestMethod.POST, RequestMethod.GET})
\n也可以使用对应请求方式的注解(可以把它们叫做组合注解 ):
\n1 2 3 4 5 @GetMapping("/t1") \t\t@PostMapping("/t1") @PutMapping("/t1") @DeleteMapping("/t1") @PatchMapping("/t1")
\n例如
\n1 2 3 4 5 6 7 @GetMapping("/t3/{a}/{b}") public String test (@PathVariable int a, @PathVariable int b, Model model) { int r = a + b; model.addAttribute("msg" ,"Result: " + r); return "test1" ; }
\n"},{"title":"1-Servlet","abbrlink":"345d8e17","date":"2021-04-12T02:44:20.000Z","description":"Servlet举例介绍","_content":"\n\n# Servlet\n\n## servlet技术\n\n1. Servlet是Java EE规范之一,规范即接口\n2. 是JavaWeb三大组件之一,其他2个是:Filter过滤器、Listener监听器\n3. 是运行在服务器上一个Java小程序,可以接收客户端发来的请求,并相应给客户端\n4. Servlet没有 `main()`方法,由系统自动调用\n\n\n\n## IDEA创建servlet\n\n \n\n\n\n \n\n\n\n \n\n\n\n\n\n## 手动实现Servlet程序\n\n1. 编写类实现Servlet规范,有3种方式:\n\n 1. 继承 `HttpServlet`类\n\n ```java\n public class HelloServlet extends HttpServlet {\n\n }\n ```\n\n 2. 实现 `Servlet`接口\n\n ```java\n package com.example.servlet01;\n \n import javax.servlet.*;\n import java.io.IOException;\n \n public class Servlet01 implements Servlet {\n @Override\n public void init(ServletConfig servletConfig) throws ServletException {\n \n }\n \n @Override\n public ServletConfig getServletConfig() {\n return null;\n }\n \n @Override\n public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {\n \t\tSystem.out.println(\"实现Servlet接口\");\n }\n \n @Override\n public String getServletInfo() {\n return null;\n }\n \n @Override\n public void destroy() {\n \n }\n }\n \n ```\n\n \n\n2. 实现 `service()`方法,处理请求,响应数据\n\n 【快捷键 ctrl + o 可以重写方法或实现接口】\n\n ```java\n public class HelloServlet extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n //控制台打印\n System.out.println(\"Hello Servlet\");\n \n //通过流输出数据到浏览器\n resp.getWriter().write(\"hello servlet!\");\n }\n }\n ```\n\n3. 设置注解`@WebServlet`,指定访问路径\n\n ```java\n @WebServlet(name=\"helloServlet\", value = \"/ser01\") //name可以不设置\n ```\n\n\n\n\n> 在第二步中,也可以不重写 `Service()`方法,而是更具体些,重写 `doGet()`、`doPost`方法;\n>\n> 根据浏览器请求方式来进行不同处理\n>\n> 但实际我们不知道浏览器以何种方式请求,故两个方法都需要去写,因此这种方式用的较少\n\n\n\n## Servlet工作流程\n\nurl`http://localhost:8083/servlet01/ser01`含义:\n\n> - 服务器:`http://localhost:8083`\n> - 项目:`/servlet01`\n> - 资源:`/ser01`\n\n","source":"_posts/javaEE/1-Servlet.md","raw":"---\ntitle: 1-Servlet\ntags:\n - Servlet\ncategories:\n - - java\n - javaEE\nabbrlink: 345d8e17\ndate: 2021-04-12 10:44:20\ndescription: Servlet举例介绍\n---\n\n\n# Servlet\n\n## servlet技术\n\n1. Servlet是Java EE规范之一,规范即接口\n2. 是JavaWeb三大组件之一,其他2个是:Filter过滤器、Listener监听器\n3. 是运行在服务器上一个Java小程序,可以接收客户端发来的请求,并相应给客户端\n4. Servlet没有 `main()`方法,由系统自动调用\n\n\n\n## IDEA创建servlet\n\n \n\n\n\n \n\n\n\n \n\n\n\n\n\n## 手动实现Servlet程序\n\n1. 编写类实现Servlet规范,有3种方式:\n\n 1. 继承 `HttpServlet`类\n\n ```java\n public class HelloServlet extends HttpServlet {\n\n }\n ```\n\n 2. 实现 `Servlet`接口\n\n ```java\n package com.example.servlet01;\n \n import javax.servlet.*;\n import java.io.IOException;\n \n public class Servlet01 implements Servlet {\n @Override\n public void init(ServletConfig servletConfig) throws ServletException {\n \n }\n \n @Override\n public ServletConfig getServletConfig() {\n return null;\n }\n \n @Override\n public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {\n \t\tSystem.out.println(\"实现Servlet接口\");\n }\n \n @Override\n public String getServletInfo() {\n return null;\n }\n \n @Override\n public void destroy() {\n \n }\n }\n \n ```\n\n \n\n2. 实现 `service()`方法,处理请求,响应数据\n\n 【快捷键 ctrl + o 可以重写方法或实现接口】\n\n ```java\n public class HelloServlet extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n //控制台打印\n System.out.println(\"Hello Servlet\");\n \n //通过流输出数据到浏览器\n resp.getWriter().write(\"hello servlet!\");\n }\n }\n ```\n\n3. 设置注解`@WebServlet`,指定访问路径\n\n ```java\n @WebServlet(name=\"helloServlet\", value = \"/ser01\") //name可以不设置\n ```\n\n\n\n\n> 在第二步中,也可以不重写 `Service()`方法,而是更具体些,重写 `doGet()`、`doPost`方法;\n>\n> 根据浏览器请求方式来进行不同处理\n>\n> 但实际我们不知道浏览器以何种方式请求,故两个方法都需要去写,因此这种方式用的较少\n\n\n\n## Servlet工作流程\n\nurl`http://localhost:8083/servlet01/ser01`含义:\n\n> - 服务器:`http://localhost:8083`\n> - 项目:`/servlet01`\n> - 资源:`/ser01`\n\n","slug":"javaEE/1-Servlet","published":1,"updated":"2021-09-10T03:08:58.332Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qc0051akve1nql6qt7","content":"Servlet servlet技术 \nServlet是Java EE规范之一,规范即接口 \n是JavaWeb三大组件之一,其他2个是:Filter过滤器、Listener监听器 \n是运行在服务器上一个Java小程序,可以接收客户端发来的请求,并相应给客户端 \nServlet没有 main()
方法,由系统自动调用 \n \nIDEA创建servlet
\n
\n
\n手动实现Servlet程序 \n编写类实现Servlet规范,有3种方式:
\n\n继承 HttpServlet
类
\n 1 2 3 public class HelloServlet extends HttpServlet {}
\n \n实现 Servlet
接口
\n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.example.servlet01;import javax.servlet.*;import java.io.IOException;public class Servlet01 implements Servlet { @Override public void init (ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig () { return null ; } @Override public void service (ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { \t\tSystem.out.println("实现Servlet接口" ); } @Override public String getServletInfo () { return null ; } @Override public void destroy () { } }
\n \n \n \n \n\n实现 service()
方法,处理请求,响应数据
\n【快捷键 ctrl + o 可以重写方法或实现接口】
\n1 2 3 4 5 6 7 8 9 10 public class HelloServlet extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("Hello Servlet" ); resp.getWriter().write("hello servlet!" ); } }
\n \n设置注解@WebServlet
,指定访问路径
\n1 @WebServlet(name="helloServlet", value = "/ser01")
\n \n \n\n在第二步中,也可以不重写 Service()
方法,而是更具体些,重写 doGet()
、doPost
方法;
\n根据浏览器请求方式来进行不同处理
\n但实际我们不知道浏览器以何种方式请求,故两个方法都需要去写,因此这种方式用的较少
\n \nServlet工作流程 urlhttp://localhost:8083/servlet01/ser01
含义:
\n\n\n服务器:http://localhost:8083
\n项目:/servlet01
\n资源:/ser01
\n \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"Servlet servlet技术 \nServlet是Java EE规范之一,规范即接口 \n是JavaWeb三大组件之一,其他2个是:Filter过滤器、Listener监听器 \n是运行在服务器上一个Java小程序,可以接收客户端发来的请求,并相应给客户端 \nServlet没有 main()
方法,由系统自动调用 \n \nIDEA创建servlet
\n
\n
\n手动实现Servlet程序 \n编写类实现Servlet规范,有3种方式:
\n\n继承 HttpServlet
类
\n 1 2 3 public class HelloServlet extends HttpServlet {}
\n \n实现 Servlet
接口
\n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.example.servlet01;import javax.servlet.*;import java.io.IOException;public class Servlet01 implements Servlet { @Override public void init (ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig () { return null ; } @Override public void service (ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { \t\tSystem.out.println("实现Servlet接口" ); } @Override public String getServletInfo () { return null ; } @Override public void destroy () { } }
\n \n \n \n \n\n实现 service()
方法,处理请求,响应数据
\n【快捷键 ctrl + o 可以重写方法或实现接口】
\n1 2 3 4 5 6 7 8 9 10 public class HelloServlet extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("Hello Servlet" ); resp.getWriter().write("hello servlet!" ); } }
\n \n设置注解@WebServlet
,指定访问路径
\n1 @WebServlet(name="helloServlet", value = "/ser01")
\n \n \n\n在第二步中,也可以不重写 Service()
方法,而是更具体些,重写 doGet()
、doPost
方法;
\n根据浏览器请求方式来进行不同处理
\n但实际我们不知道浏览器以何种方式请求,故两个方法都需要去写,因此这种方式用的较少
\n \nServlet工作流程 urlhttp://localhost:8083/servlet01/ser01
含义:
\n\n\n服务器:http://localhost:8083
\n项目:/servlet01
\n资源:/ser01
\n \n \n"},{"title":"2-Servlet生命周期","abbrlink":"e7ba7594","date":"2021-04-12T08:27:24.000Z","description":"Servlet的几个生命周期","_content":"\n\n# Servlet生命周期\n\n\n\n1. 通过调用 `init()`方法创建、初始化服务(系统自动调用,只执行一次)\n2. 调用 `Service()` 方法执行服务\n3. 调用 `destroy()`销毁服务(关闭服务器时系统自动调用,只执行一次)\n\n\n\n#### Servlet工作流程\n\n![image-20210502165959933](https://gitee.com/ajream/images/raw/master/img/2021-05-0217-00-01_image-20210502165959933.png)","source":"_posts/javaEE/2-Servlet生命周期.md","raw":"---\ntitle: 2-Servlet生命周期\ntags:\n - Servlet\ncategories:\n - - java\n - javaEE\nabbrlink: e7ba7594\ndate: 2021-04-12 16:27:24\ndescription: Servlet的几个生命周期\n---\n\n\n# Servlet生命周期\n\n\n\n1. 通过调用 `init()`方法创建、初始化服务(系统自动调用,只执行一次)\n2. 调用 `Service()` 方法执行服务\n3. 调用 `destroy()`销毁服务(关闭服务器时系统自动调用,只执行一次)\n\n\n\n#### Servlet工作流程\n\n![image-20210502165959933](https://gitee.com/ajream/images/raw/master/img/2021-05-0217-00-01_image-20210502165959933.png)","slug":"javaEE/2-Servlet生命周期","published":1,"updated":"2021-09-10T03:09:15.138Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qd0053akve87tvdadq","content":"Servlet生命周期 \n通过调用 init()
方法创建、初始化服务(系统自动调用,只执行一次) \n调用 Service()
方法执行服务 \n调用 destroy()
销毁服务(关闭服务器时系统自动调用,只执行一次) \n \nServlet工作流程
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":"Servlet生命周期 \n通过调用 init()
方法创建、初始化服务(系统自动调用,只执行一次) \n调用 Service()
方法执行服务 \n调用 destroy()
销毁服务(关闭服务器时系统自动调用,只执行一次) \n \nServlet工作流程
\n"},{"title":"4-请求转发","abbrlink":"1a9b2152","date":"2021-04-14T01:34:29.000Z","description":"Servlet如何处理客户端发来的请求","_content":"\n# 请求转发\n\n1. 可以让请求从服务端跳转到另一页面(或者跳转到另一个Servlet类处理)\n2. 是服务端行为\n3. 特点:\n 1. 浏览器地址栏不发生改变\n 2. 实现不同的Servlet数据贡献\n\n\n\n步骤:\n\n1. 分别创建类:Servlet02和Servlet03\n\n 填入以下内容:\n\n ```java\n //类Servlet02\n \n package com.xxxx.servlet02;\n \n import javax.servlet.ServletException;\n import javax.servlet.annotation.WebServlet;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n @WebServlet(\"/ser02\")\n public class Servlet02 extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n String name = req.getParameter(\"name\");\n System.out.println(\"ser02获取到用户名:\" + name);\n \n //跳转到servlet03\n req.getRequestDispatcher(\"ser03\").forward(req, resp);\n }\n }\n \n ```\n\n \n\n ```java\n //类Servlet03\n \n package com.xxxx.servlet02;\n \n import javax.servlet.ServletException;\n import javax.servlet.annotation.WebServlet;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n @WebServlet(\"/ser03\")\n public class servlet03 extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n String name = req.getParameter(\"name\");\n System.out.println(\"ser03获取到用户名:\" + name);\n \n }\n }\n \n ```\n\n 在类Servlet02中,跳转语句为:\n\n ```java\n req.getRequestDispatcher(\"ser03\").forward(req, resp);\n ```\n\n `ser03`(没有斜线“/”)表示servlet03的注解 “**@WebServlet(\"/ser03\")**”\n\n \n\n > 也可以跳转到 login.jsp页面\n >\n > ```java\n > //跳转到login.jsp\n > req.getRequestDispatcher(\"login.jsp\").forward(req, resp);\n > ```\n\n \n\n2. 启动服务,浏览器访问servlet02,然后自动会访问servlet03,但地址栏没变\n\n ![image-20210502211542653](https://gitee.com/aJream/images/raw/master/img/2021-05-0221-15-48_image-20210502211542653.png)\n\n \n\n ![image-20210502211627393](https://gitee.com/ajream/images/raw/master/img/2021-05-0221-16-32_image-20210502211627393.png)\n\n\n\n","source":"_posts/javaEE/4-请求转发.md","raw":"---\ntitle: 4-请求转发\ntags:\n - Servlet\ncategories:\n - - java\n - javaEE\nabbrlink: 1a9b2152\ndate: 2021-04-14 09:34:29\ndescription: Servlet如何处理客户端发来的请求\n---\n\n# 请求转发\n\n1. 可以让请求从服务端跳转到另一页面(或者跳转到另一个Servlet类处理)\n2. 是服务端行为\n3. 特点:\n 1. 浏览器地址栏不发生改变\n 2. 实现不同的Servlet数据贡献\n\n\n\n步骤:\n\n1. 分别创建类:Servlet02和Servlet03\n\n 填入以下内容:\n\n ```java\n //类Servlet02\n \n package com.xxxx.servlet02;\n \n import javax.servlet.ServletException;\n import javax.servlet.annotation.WebServlet;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n @WebServlet(\"/ser02\")\n public class Servlet02 extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n String name = req.getParameter(\"name\");\n System.out.println(\"ser02获取到用户名:\" + name);\n \n //跳转到servlet03\n req.getRequestDispatcher(\"ser03\").forward(req, resp);\n }\n }\n \n ```\n\n \n\n ```java\n //类Servlet03\n \n package com.xxxx.servlet02;\n \n import javax.servlet.ServletException;\n import javax.servlet.annotation.WebServlet;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n @WebServlet(\"/ser03\")\n public class servlet03 extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n String name = req.getParameter(\"name\");\n System.out.println(\"ser03获取到用户名:\" + name);\n \n }\n }\n \n ```\n\n 在类Servlet02中,跳转语句为:\n\n ```java\n req.getRequestDispatcher(\"ser03\").forward(req, resp);\n ```\n\n `ser03`(没有斜线“/”)表示servlet03的注解 “**@WebServlet(\"/ser03\")**”\n\n \n\n > 也可以跳转到 login.jsp页面\n >\n > ```java\n > //跳转到login.jsp\n > req.getRequestDispatcher(\"login.jsp\").forward(req, resp);\n > ```\n\n \n\n2. 启动服务,浏览器访问servlet02,然后自动会访问servlet03,但地址栏没变\n\n ![image-20210502211542653](https://gitee.com/aJream/images/raw/master/img/2021-05-0221-15-48_image-20210502211542653.png)\n\n \n\n ![image-20210502211627393](https://gitee.com/ajream/images/raw/master/img/2021-05-0221-16-32_image-20210502211627393.png)\n\n\n\n","slug":"javaEE/4-请求转发","published":1,"updated":"2021-09-10T03:10:21.610Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qe0056akvea9ks9ha1","content":"请求转发 \n可以让请求从服务端跳转到另一页面(或者跳转到另一个Servlet类处理) \n是服务端行为 \n特点:\n浏览器地址栏不发生改变 \n实现不同的Servlet数据贡献 \n \n \n \n步骤:
\n\n分别创建类:Servlet02和Servlet03
\n填入以下内容:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.xxxx.servlet02;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/ser02") public class Servlet02 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name" ); System.out.println("ser02获取到用户名:" + name); req.getRequestDispatcher("ser03" ).forward(req, resp); } }
\n \n \n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.xxxx.servlet02;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/ser03") public class servlet03 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name" ); System.out.println("ser03获取到用户名:" + name); } }
\n 在类Servlet02中,跳转语句为:
\n 1 req.getRequestDispatcher("ser03" ).forward(req, resp);
\n ser03
(没有斜线“/”)表示servlet03的注解 “@WebServlet(“/ser03”) ”
\n\n也可以跳转到 login.jsp页面
\n1 2 req.getRequestDispatcher("login.jsp" ).forward(req, resp);
\n \n\n启动服务,浏览器访问servlet02,然后自动会访问servlet03,但地址栏没变
\n
\n \n \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"请求转发 \n可以让请求从服务端跳转到另一页面(或者跳转到另一个Servlet类处理) \n是服务端行为 \n特点:\n浏览器地址栏不发生改变 \n实现不同的Servlet数据贡献 \n \n \n \n步骤:
\n\n分别创建类:Servlet02和Servlet03
\n填入以下内容:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.xxxx.servlet02;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/ser02") public class Servlet02 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name" ); System.out.println("ser02获取到用户名:" + name); req.getRequestDispatcher("ser03" ).forward(req, resp); } }
\n \n \n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.xxxx.servlet02;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/ser03") public class servlet03 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name" ); System.out.println("ser03获取到用户名:" + name); } }
\n 在类Servlet02中,跳转语句为:
\n 1 req.getRequestDispatcher("ser03" ).forward(req, resp);
\n ser03
(没有斜线“/”)表示servlet03的注解 “@WebServlet(“/ser03”) ”
\n\n也可以跳转到 login.jsp页面
\n1 2 req.getRequestDispatcher("login.jsp" ).forward(req, resp);
\n \n\n启动服务,浏览器访问servlet02,然后自动会访问servlet03,但地址栏没变
\n
\n \n \n
\n"},{"title":"IDEA配置Tomcat","abbrlink":"1a57c6b6","date":"2021-06-10T06:51:17.000Z","description":"关于如何在idea中配置Tomcat服务器的具体操作","_content":"\n\n\n三个步骤\n\n1. \n\n![image-20210502152308870](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-23-11_image-20210502152308870.png)\n\n2. \n\n![image-20210502152803463](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-28-04_image-20210502152803463.png)\n\n3. \n\n![image-20210502152955436](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-29-56_image-20210502152955436.png)\n\n\n\n","source":"_posts/javaEE/IDEA配置Tomcat.md","raw":"---\ntitle: IDEA配置Tomcat\ntags:\n - Tomcat\ncategories:\n - - java\n - javaEE\nabbrlink: 1a57c6b6\ndate: 2021-06-10 14:51:17\ndescription: 关于如何在idea中配置Tomcat服务器的具体操作\n---\n\n\n\n三个步骤\n\n1. \n\n![image-20210502152308870](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-23-11_image-20210502152308870.png)\n\n2. \n\n![image-20210502152803463](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-28-04_image-20210502152803463.png)\n\n3. \n\n![image-20210502152955436](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-29-56_image-20210502152955436.png)\n\n\n\n","slug":"javaEE/IDEA配置Tomcat","published":1,"updated":"2021-09-10T03:11:01.852Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qf0058akve50qfda62","content":"三个步骤
\n\n
\n \n \n \n
\n\n
\n \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"三个步骤
\n\n
\n \n \n \n
\n\n
\n \n \n"},{"title":"3-ServletRequest对象","abbrlink":"4609d38b","date":"2021-04-13T10:27:34.000Z","description":"Servlet的Request对象使用","_content":"\n\n\n\n\n\n# HttpServletRequest对象\n\n\n\n该对象主要用于接收客户端发送过来的信息,如请求参数、请求头等\n\n\n\nHttpServletRequest是ServletRequest的唯一的子**接口**\n\n\n\n## 接收请求\n\n### 常用方法\n\n```java\n//完整url路径\nString url = req.getRequestURL().toString();\n\n//部分路径\nString uri = req.getRequestURI();\n\n//获取请求的参数(如果有的话,即路径中`?`后面的字符串)\nString query = req.getQueryString();\n\n//请求方式get、post\nString method = req.getMethod();\n\n//请求协议(http/1/1)\nString protocol = req.getProtocol();\n\n//获取项目站点名\nString webapp = req.getContextPath();\n\n```\n\n请求为【`localhost:8083/sr02/servlet02`】时输出:\n\n![image-20210502192535571](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-25-40_image-20210502192535571.png)\n\n请求为【`localhost:8083/sr02/servlet02?name=xiaod&passwd=123456`】时:\n\n![image-20210502192917681](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-29-20_image-20210502192917681.png)\n\n\n\n**详细参数获取(重点)**\n\n```java\n//获取指定参数,返回字符串\nString name = req.getParameter(\"name\"); //用户名\nString passwd = req.getParameter(\"passwd\"); //密码\n```\n\n![image-20210502193840780](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-38-47_image-20210502193840780.png)\n\n\n\n获取多个同名参数(适用于复选框)\n\n```java\n//多个参数名称相同,用数组收集\n//如:参数为 name=xiaod&passwd=123456&hobby=sing&hobby=dance\nString[] hobbies = req.getParameterValues(\"hobby\"); \n```\n\n\n\n\n\n## 中文乱码问题\n\ntomcat8以上版本只有get请求会乱码,使用字符串乱码处理方式来解决\n\n\n\n使用**post请求**提交表单时,若含有中文,获取到的是乱码\n\n**验证步骤**\n\n1. 在webapp下创建login.jsp文件\n\n \n\n2. 在login.jsp下粘贴以下代码\n\n ```jsp\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n 登录 \n \n \n \n \n \n \n \n ```\n\n > 需要注意,form标签的属性 `action`为**注解@WebServlet(\"/servlet02\")**中的 `servlet02`(不要有斜线),这样就与这个处理类关联起来了\n\n3. 运行启动\n\n4. 浏览器访问\n\n ![image-20210502202959582](https://gitee.com/ajream/images/raw/master/img/2021-05-0220-30-03_image-20210502202959582.png)\n\n5. 点击登录提交给 `Servlet01`类(含有注解 `@WebServlet(\"/servlet02\")` )处理\n\n 查看获取到的数据:\n\n ![image-20210502203233059](https://gitee.com/ajream/images/raw/master/img/2021-05-0220-32-36_image-20210502203233059.png)\n\n","source":"_posts/javaEE/3-ServletRequest对象.md","raw":"---\ntitle: 3-ServletRequest对象\ntags:\n - Servlet\ncategories:\n - - java\n - javaEE\nabbrlink: 4609d38b\ndate: 2021-04-13 18:27:34\ndescription: Servlet的Request对象使用\n---\n\n\n\n\n\n\n# HttpServletRequest对象\n\n\n\n该对象主要用于接收客户端发送过来的信息,如请求参数、请求头等\n\n\n\nHttpServletRequest是ServletRequest的唯一的子**接口**\n\n\n\n## 接收请求\n\n### 常用方法\n\n```java\n//完整url路径\nString url = req.getRequestURL().toString();\n\n//部分路径\nString uri = req.getRequestURI();\n\n//获取请求的参数(如果有的话,即路径中`?`后面的字符串)\nString query = req.getQueryString();\n\n//请求方式get、post\nString method = req.getMethod();\n\n//请求协议(http/1/1)\nString protocol = req.getProtocol();\n\n//获取项目站点名\nString webapp = req.getContextPath();\n\n```\n\n请求为【`localhost:8083/sr02/servlet02`】时输出:\n\n![image-20210502192535571](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-25-40_image-20210502192535571.png)\n\n请求为【`localhost:8083/sr02/servlet02?name=xiaod&passwd=123456`】时:\n\n![image-20210502192917681](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-29-20_image-20210502192917681.png)\n\n\n\n**详细参数获取(重点)**\n\n```java\n//获取指定参数,返回字符串\nString name = req.getParameter(\"name\"); //用户名\nString passwd = req.getParameter(\"passwd\"); //密码\n```\n\n![image-20210502193840780](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-38-47_image-20210502193840780.png)\n\n\n\n获取多个同名参数(适用于复选框)\n\n```java\n//多个参数名称相同,用数组收集\n//如:参数为 name=xiaod&passwd=123456&hobby=sing&hobby=dance\nString[] hobbies = req.getParameterValues(\"hobby\"); \n```\n\n\n\n\n\n## 中文乱码问题\n\ntomcat8以上版本只有get请求会乱码,使用字符串乱码处理方式来解决\n\n\n\n使用**post请求**提交表单时,若含有中文,获取到的是乱码\n\n**验证步骤**\n\n1. 在webapp下创建login.jsp文件\n\n \n\n2. 在login.jsp下粘贴以下代码\n\n ```jsp\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n 登录 \n \n \n \n \n \n \n \n ```\n\n > 需要注意,form标签的属性 `action`为**注解@WebServlet(\"/servlet02\")**中的 `servlet02`(不要有斜线),这样就与这个处理类关联起来了\n\n3. 运行启动\n\n4. 浏览器访问\n\n ![image-20210502202959582](https://gitee.com/ajream/images/raw/master/img/2021-05-0220-30-03_image-20210502202959582.png)\n\n5. 点击登录提交给 `Servlet01`类(含有注解 `@WebServlet(\"/servlet02\")` )处理\n\n 查看获取到的数据:\n\n ![image-20210502203233059](https://gitee.com/ajream/images/raw/master/img/2021-05-0220-32-36_image-20210502203233059.png)\n\n","slug":"javaEE/3-ServletRequest对象","published":1,"updated":"2021-09-10T03:10:07.722Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qg005aakveh3vd1y50","content":"HttpServletRequest对象 该对象主要用于接收客户端发送过来的信息,如请求参数、请求头等
\nHttpServletRequest是ServletRequest的唯一的子接口
\n接收请求 常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 String url = req.getRequestURL().toString(); String uri = req.getRequestURI(); String query = req.getQueryString(); String method = req.getMethod(); String protocol = req.getProtocol(); String webapp = req.getContextPath();
\n请求为【localhost:8083/sr02/servlet02
】时输出:
\n
\n请求为【localhost:8083/sr02/servlet02?name=xiaod&passwd=123456
】时:
\n
\n详细参数获取(重点)
\n1 2 3 String name = req.getParameter("name" ); String passwd = req.getParameter("passwd" );
\n
\n获取多个同名参数(适用于复选框)
\n1 2 3 String[] hobbies = req.getParameterValues("hobby" );
\n中文乱码问题 tomcat8以上版本只有get请求会乱码,使用字符串乱码处理方式来解决
\n使用post请求 提交表单时,若含有中文,获取到的是乱码
\n验证步骤
\n\n在webapp下创建login.jsp文件
\n
\n \n在login.jsp下粘贴以下代码
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登录</title> <form method="post" action="servlet02" > 账户名:<input type="text" name="name" > <br> 密码:<input type="password" name="passwd" > <br> <button>登录</button> <!--button默认为提交按钮--> </form> </head> <body> </body> </html>
\n\n需要注意,form标签的属性 action
为注解@WebServlet(“/servlet02”) 中的 servlet02
(不要有斜线),这样就与这个处理类关联起来了
\n \n \n运行启动
\n \n浏览器访问
\n
\n \n点击登录提交给 Servlet01
类(含有注解 @WebServlet("/servlet02")
)处理
\n查看获取到的数据:
\n
\n \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"HttpServletRequest对象 该对象主要用于接收客户端发送过来的信息,如请求参数、请求头等
\nHttpServletRequest是ServletRequest的唯一的子接口
\n接收请求 常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 String url = req.getRequestURL().toString(); String uri = req.getRequestURI(); String query = req.getQueryString(); String method = req.getMethod(); String protocol = req.getProtocol(); String webapp = req.getContextPath();
\n请求为【localhost:8083/sr02/servlet02
】时输出:
\n
\n请求为【localhost:8083/sr02/servlet02?name=xiaod&passwd=123456
】时:
\n
\n详细参数获取(重点)
\n1 2 3 String name = req.getParameter("name" ); String passwd = req.getParameter("passwd" );
\n
\n获取多个同名参数(适用于复选框)
\n1 2 3 String[] hobbies = req.getParameterValues("hobby" );
\n中文乱码问题 tomcat8以上版本只有get请求会乱码,使用字符串乱码处理方式来解决
\n使用post请求 提交表单时,若含有中文,获取到的是乱码
\n验证步骤
\n\n在webapp下创建login.jsp文件
\n
\n \n在login.jsp下粘贴以下代码
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登录</title> <form method="post" action="servlet02" > 账户名:<input type="text" name="name" > <br> 密码:<input type="password" name="passwd" > <br> <button>登录</button> <!--button默认为提交按钮--> </form> </head> <body> </body> </html>
\n\n需要注意,form标签的属性 action
为注解@WebServlet(“/servlet02”) 中的 servlet02
(不要有斜线),这样就与这个处理类关联起来了
\n \n \n运行启动
\n \n浏览器访问
\n
\n \n点击登录提交给 Servlet01
类(含有注解 @WebServlet("/servlet02")
)处理
\n查看获取到的数据:
\n
\n \n \n"},{"title":"1-类与对象","abbrlink":"52b44ec7","date":"2021-02-05T05:12:19.000Z","description":"关于Java的类的基本知识,例如类的属性、方法(传参及调用)、继承、this关键字等","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n\n# 类与对象\n\n类可以看成是具有很多相似性质、功能的事物的集合,如鸟可以看作一个类,鸟都具有各自的名称,各色的羽毛、飞等相似特点;\n\n用代码创建一个鸟类:\n\n```java\npublic class Bird{\n String name;\n String feathers_color;\n public void fly(){\n System.out.println(name + \"可以飞\")\n }\n}\n```\n\n对象可以看作是类的一个具体事例,如上面所说的鸟类,鸽子是鸟类的一种,老鹰也是鸟类一种,因此鸽子、老鹰都可以分别看作是鸟类的一个对象;\n\n用代码来创建对象:\n\n```java\nBird bird1 = new Bird();\nBird bird2 = new Bird();\n```\n\n## 引用与指向\n\n在下面创建的这一对象中\n\n```java\nBird bird1 = new Bird();\n```\n\n`bird1`存储的是对象的地址,因此`bird1`是对象的**引用**;这一地址**指向**了对象本身;\n\n \n\n- 多个引用指向同一对象\n\n```java\nBird b1 = new Bird();\nBird b2 = b1;\nBird b3 = b1;\n```\n\n \n\n- 多个引用指向多个(不同)对象\n\n```java\nBird b1 = new Bird();\nBird b2 = new Bird();\nBird b3 = new Bird();\n```\n\n \n\n## 继承\n\n可以创建两个类,其中一个类可以继承另一个类的属性和方法,继承语法:\n\n```java\npublic class ClassName2 extends ClassName1{\n\n}\n```\n\n例如:\n\n首先创建父类Item\n\n```java\npublic class Item {\n String name;\n int price;\n}\n```\n\n创建子类Weapon继承Item\n\n```java\npublic class Weapon extends Item{\n int damage; //攻击力\n \n public static void main(String[] args) {\n Weapon infinityEdge = new Weapon();\n infinityEdge.damage = 65; //damage属性在类Weapon中新设计的\n \n infinityEdge.name = \"无尽之刃\";//name属性,是从Item中继承来的,就不需要重复设计了\n infinityEdge.price = 3600;\n \n }\n \n}\n```\n\n## 方法重载\n\n方法的重载指的是**方法名一样**,但是参数类型或数量不一样\n\n```java\npublic class ADHero extends Hero {\n //无参数\n public void attack() {\n System.out.println(name + \" 进行了一次攻击 ,但是不确定打中谁了\");\n }\n \n \t//1个参数\n public void attack(Hero h1) {\n System.out.println(name + \"对\" + h1.name + \"进行了一次攻击 \");\n }\n \t\n //2个参数\n public void attack(Hero h1, Hero h2) {\n System.out.println(name + \"同时对\" + h1.name + \"和\" + h2.name + \"进行了攻击 \");\n }\n \n public static void main(String[] args) {\n ADHero bh = new ADHero();\n bh.name = \"赏金猎人\";\n \n Hero h1 = new Hero();\n h1.name = \"盖伦\";\n Hero h2 = new Hero();\n h2.name = \"提莫\";\n \n bh.attack(h1);\n bh.attack(h1, h2);\n }\n \n}\n```\n\n可变数量的参数\n\n采用可变数量的参数 **只需要设计一个方法**:\n`public void attack(Hero... heros)`\n即可代表上述所有的方法了,此时 `heros`被当作一个**数组**,在方法里,使用操作数组的方式处理参数heros即可\n\n```java\npublic class ADHero extends Hero {\n \n public void attack() {\n System.out.println(name + \" 进行了一次攻击 ,但是不确定打中谁了\");\n }\n \n // 可变数量的参数\n public void attack(Hero... heros) {\n for (int i = 0; i < heros.length; i++) {\n System.out.println(name + \" 攻击了 \" + heros[i].name);\n \n }\n }\n \n public static void main(String[] args) {\n ADHero bh = new ADHero();\n bh.name = \"赏金猎人\";\n \n Hero h1 = new Hero();\n h1.name = \"盖伦\";\n Hero h2 = new Hero();\n h2.name = \"提莫\";\n \n bh.attack(h1);\n bh.attack(h1, h2);\n \n }\n \n}\n```\n\n## 构造方法\n\n构造方法是一个特殊的方法,其方法名与类名一样,没有返回类型,在实例化一个对象时,必然调用构造方法\n\n```java\npublic class Hero {\n \n String name;\n \n float hp;\n \n float armor;\n \n int moveSpeed;\n \n // 方法名和类名一样(包括大小写)\n // 没有返回类型\n public Hero() {\n System.out.println(\"实例化一个对象的时候,必然调用构造方法\");\n }\n \n public static void main(String[] args) {\n //实例化一个对象的时候,必然调用构造方法\n Hero h = new Hero();\n }\n \n}\n\n//输出:实例化一个对象的时候,必然调用构造方法\n```\n\n构造方法也可以重载,即可以有几个构造方法,但它们的参数数量或类型不同\n\n## this\n\n`this` 代表**当前对象**,因此把this看成一个对象就行,对象的属性、方法、构造方法它都有\n\n```java\npublic class Hero {\n \n String name; //姓名\n \n float hp; //血量\n \n float armor; //护甲\n \n int moveSpeed; //移动速度\n \n //打印内存中的虚拟地址\n public void showAddressInMemory(){\n System.out.println(\"打印this看到的虚拟地址:\"+this);\n }\n \n public static void main(String[] args) {\n //---------------------------------------------\n Hero garen = new Hero();\n garen.name = \"aaa\";\n System.out.println(\"打印对象看到的虚拟地址:\"+garen);\n garen.showAddressInMemory();\n \n //-------------------------------------------\n Hero teemo = new Hero();\n teemo.name = \"bbb\";\n System.out.println(\"打印对象看到的虚拟地址:\"+teemo);\n teemo.showAddressInMemory();\n } \n \n}\n\n//输出:\n/*\n打印对象看到的虚拟地址:Hero@2a139a55\n打印this看到的虚拟地址:Hero@2a139a55\n打印对象看到的虚拟地址:Hero@15db9742\n打印this看到的虚拟地址:Hero@15db9742\n*/\n```\n\n通过`this` 可以访问对象的属性、方法\n\n通过`this()`可以调用其他的构造方法\n\n```java\npublic class Hero {\n\n String name; // 姓名\n\n float hp; // 血量\n\n float armor; // 护甲\n\n int moveSpeed; // 移动速度\n\n // 带一个参数的构造方法\n public Hero(String name) {\n System.out.println(\"一个参数的构造方法\");\n this.name = name;\n }\n\n // 带两个参数的构造方法\n public Hero(String name, float hp) {\n this(name); \n System.out.println(\"两个参数的构造方法\");\n this.hp = hp;\n }\n\n public static void main(String[] args) {\n Hero teemo = new Hero(\"aaaa\", 383); //两个参数\n\n System.out.println(teemo.name);\n\n }\n\n}\n\n/*输出:\n\n一个参数的构造方法\n两个参数的构造方法\naaaa\n*/\n```\n\n## 传参\n\n- 基本类型传参与引用类型传参\n\n 基本类型传参,不多说\n\n 引用类型传参,即传入参数为引用类型\n\n- `=`的含义\n\n - 如果变量是基本类型,则 `=`表示赋值\n\n - 如果变量是引用类型,`=` 表示指向的意思,比如\n\n ```java\n Hero h = new Hero(); //引用h,指向一个Hero对象\n ```\n\n- 引用类型传参\n\n 形参与实参都指向同一对象\n\n ```java\n public class Hero {\n \n String name; \n \n float hp;\n \n public Hero(String name, float hp) {\n this.name = name;\n this.hp = hp;\n }\n \n public void attack(Hero hero) {\n System.out.println(\"hero:\"+hero);\n }\n \n public static void main(String[] args) {\n Hero teemo = new Hero(\"teemo\", 383);\n Hero garen = new Hero(\"garen\", 616);\n garen.attack(teemo);\n System.out.println(\"teemo:\"+teemo);\n \n }\n \n }\n \n /**输出:\n hero:Hero@2a139a55\n teemo:Hero@2a139a55\n */\n ```\n\n 用图表示如下![image-20210123184909979](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210123184909979.png)\n\n 但如果形参在内部指向了新的对象,实参是不会跟着改变的\n\n ```java\n public class Hero {\n \n String name; // 姓名\n \n float hp; // 血量\n \n public Hero(String name, float hp) {\n this.name = name;\n this.hp = hp;\n }\n \n public void attack(Hero hero) {\n hero = new Hero(\"hero\", 100); //形参指向了新的对象\n System.out.println(\"hero:\"+hero);\n }\n \n public static void main(String[] args) {\n Hero teemo = new Hero(\"teemo\", 383);\n Hero garen = new Hero(\"garen\", 616);\n garen.attack(teemo);\n System.out.println(\"teemo:\"+teemo);\n \n }\n \n }\n \n /**输出:\n hero:Hero@2a139a55\n teemo:Hero@15db9742\n */\n ```\n\n 用图表示如下![image-20210123185224792](https://gitee.com/ajream/images/raw/master/img/image-20210123185224792.png)\n\n\n\n## 类属性(又叫静态属性)\n\n当一个属性被**`static`**修饰的时候,就叫做**类属性**,又叫做**静态属性**\n当一个属性被声明成类属性,那么**所有的对象,都共享一个值**\n\n**对象属性:** 又叫实例属性,非静态属性\n\n> 与对象属性对比:\n> 不同对象的 对象属性 的值都可能不一样。\n\n- 访问类属性:\n\n ```java\n 类.类属性\n ```\n\n ```java\n //访问对象属性:\n 对象.对象属性\n ```\n\n- 类属性与对象属性的选择\n\n - 如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是**跟着对象走的**,每个对象的name都是不同的\n - 如果一个属性,**所有的英雄都共享**,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性\n\n\n\n## 类方法(静态方法)\n\n**类方法:** 又叫做静态方法,即被`static`修饰的方法\n\n**对象方法:** 又叫实例方法,非静态方法\n\n> 区别:\n>\n> 调用一个对象方法,必须**建立在有一个对象**的前提的基础上\n> 调用类方法,**不需要对象**的存在,直接就访问\n\n- 调用类方法\n\n ```java\n 类.类方法\n ```\n\n ```java\n //访问对象方法\n 对象.对象方法\n ```\n\n- 什么时候用类方法\n\n 如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如:\n\n ```java\n public static void printGameDuration(){\n \tSystem.out.println(\"已经玩了50秒\");\n }\n ```\n\n 反之,如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法;\n\n 注意:类方法中不能直接调用对象方法\n\n---\n\n[回到开头](#1-类与对象)\n\n","source":"_posts/javaSE/1-类与对象.md","raw":"---\ntitle: 1-类与对象\ntags:\n - javaSE\ncategories:\n - java\n - javaSE\nabbrlink: 52b44ec7\ndate: 2021-02-05 13:12:19\ndescription: 关于Java的类的基本知识,例如类的属性、方法(传参及调用)、继承、this关键字等\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n\n# 类与对象\n\n类可以看成是具有很多相似性质、功能的事物的集合,如鸟可以看作一个类,鸟都具有各自的名称,各色的羽毛、飞等相似特点;\n\n用代码创建一个鸟类:\n\n```java\npublic class Bird{\n String name;\n String feathers_color;\n public void fly(){\n System.out.println(name + \"可以飞\")\n }\n}\n```\n\n对象可以看作是类的一个具体事例,如上面所说的鸟类,鸽子是鸟类的一种,老鹰也是鸟类一种,因此鸽子、老鹰都可以分别看作是鸟类的一个对象;\n\n用代码来创建对象:\n\n```java\nBird bird1 = new Bird();\nBird bird2 = new Bird();\n```\n\n## 引用与指向\n\n在下面创建的这一对象中\n\n```java\nBird bird1 = new Bird();\n```\n\n`bird1`存储的是对象的地址,因此`bird1`是对象的**引用**;这一地址**指向**了对象本身;\n\n \n\n- 多个引用指向同一对象\n\n```java\nBird b1 = new Bird();\nBird b2 = b1;\nBird b3 = b1;\n```\n\n \n\n- 多个引用指向多个(不同)对象\n\n```java\nBird b1 = new Bird();\nBird b2 = new Bird();\nBird b3 = new Bird();\n```\n\n \n\n## 继承\n\n可以创建两个类,其中一个类可以继承另一个类的属性和方法,继承语法:\n\n```java\npublic class ClassName2 extends ClassName1{\n\n}\n```\n\n例如:\n\n首先创建父类Item\n\n```java\npublic class Item {\n String name;\n int price;\n}\n```\n\n创建子类Weapon继承Item\n\n```java\npublic class Weapon extends Item{\n int damage; //攻击力\n \n public static void main(String[] args) {\n Weapon infinityEdge = new Weapon();\n infinityEdge.damage = 65; //damage属性在类Weapon中新设计的\n \n infinityEdge.name = \"无尽之刃\";//name属性,是从Item中继承来的,就不需要重复设计了\n infinityEdge.price = 3600;\n \n }\n \n}\n```\n\n## 方法重载\n\n方法的重载指的是**方法名一样**,但是参数类型或数量不一样\n\n```java\npublic class ADHero extends Hero {\n //无参数\n public void attack() {\n System.out.println(name + \" 进行了一次攻击 ,但是不确定打中谁了\");\n }\n \n \t//1个参数\n public void attack(Hero h1) {\n System.out.println(name + \"对\" + h1.name + \"进行了一次攻击 \");\n }\n \t\n //2个参数\n public void attack(Hero h1, Hero h2) {\n System.out.println(name + \"同时对\" + h1.name + \"和\" + h2.name + \"进行了攻击 \");\n }\n \n public static void main(String[] args) {\n ADHero bh = new ADHero();\n bh.name = \"赏金猎人\";\n \n Hero h1 = new Hero();\n h1.name = \"盖伦\";\n Hero h2 = new Hero();\n h2.name = \"提莫\";\n \n bh.attack(h1);\n bh.attack(h1, h2);\n }\n \n}\n```\n\n可变数量的参数\n\n采用可变数量的参数 **只需要设计一个方法**:\n`public void attack(Hero... heros)`\n即可代表上述所有的方法了,此时 `heros`被当作一个**数组**,在方法里,使用操作数组的方式处理参数heros即可\n\n```java\npublic class ADHero extends Hero {\n \n public void attack() {\n System.out.println(name + \" 进行了一次攻击 ,但是不确定打中谁了\");\n }\n \n // 可变数量的参数\n public void attack(Hero... heros) {\n for (int i = 0; i < heros.length; i++) {\n System.out.println(name + \" 攻击了 \" + heros[i].name);\n \n }\n }\n \n public static void main(String[] args) {\n ADHero bh = new ADHero();\n bh.name = \"赏金猎人\";\n \n Hero h1 = new Hero();\n h1.name = \"盖伦\";\n Hero h2 = new Hero();\n h2.name = \"提莫\";\n \n bh.attack(h1);\n bh.attack(h1, h2);\n \n }\n \n}\n```\n\n## 构造方法\n\n构造方法是一个特殊的方法,其方法名与类名一样,没有返回类型,在实例化一个对象时,必然调用构造方法\n\n```java\npublic class Hero {\n \n String name;\n \n float hp;\n \n float armor;\n \n int moveSpeed;\n \n // 方法名和类名一样(包括大小写)\n // 没有返回类型\n public Hero() {\n System.out.println(\"实例化一个对象的时候,必然调用构造方法\");\n }\n \n public static void main(String[] args) {\n //实例化一个对象的时候,必然调用构造方法\n Hero h = new Hero();\n }\n \n}\n\n//输出:实例化一个对象的时候,必然调用构造方法\n```\n\n构造方法也可以重载,即可以有几个构造方法,但它们的参数数量或类型不同\n\n## this\n\n`this` 代表**当前对象**,因此把this看成一个对象就行,对象的属性、方法、构造方法它都有\n\n```java\npublic class Hero {\n \n String name; //姓名\n \n float hp; //血量\n \n float armor; //护甲\n \n int moveSpeed; //移动速度\n \n //打印内存中的虚拟地址\n public void showAddressInMemory(){\n System.out.println(\"打印this看到的虚拟地址:\"+this);\n }\n \n public static void main(String[] args) {\n //---------------------------------------------\n Hero garen = new Hero();\n garen.name = \"aaa\";\n System.out.println(\"打印对象看到的虚拟地址:\"+garen);\n garen.showAddressInMemory();\n \n //-------------------------------------------\n Hero teemo = new Hero();\n teemo.name = \"bbb\";\n System.out.println(\"打印对象看到的虚拟地址:\"+teemo);\n teemo.showAddressInMemory();\n } \n \n}\n\n//输出:\n/*\n打印对象看到的虚拟地址:Hero@2a139a55\n打印this看到的虚拟地址:Hero@2a139a55\n打印对象看到的虚拟地址:Hero@15db9742\n打印this看到的虚拟地址:Hero@15db9742\n*/\n```\n\n通过`this` 可以访问对象的属性、方法\n\n通过`this()`可以调用其他的构造方法\n\n```java\npublic class Hero {\n\n String name; // 姓名\n\n float hp; // 血量\n\n float armor; // 护甲\n\n int moveSpeed; // 移动速度\n\n // 带一个参数的构造方法\n public Hero(String name) {\n System.out.println(\"一个参数的构造方法\");\n this.name = name;\n }\n\n // 带两个参数的构造方法\n public Hero(String name, float hp) {\n this(name); \n System.out.println(\"两个参数的构造方法\");\n this.hp = hp;\n }\n\n public static void main(String[] args) {\n Hero teemo = new Hero(\"aaaa\", 383); //两个参数\n\n System.out.println(teemo.name);\n\n }\n\n}\n\n/*输出:\n\n一个参数的构造方法\n两个参数的构造方法\naaaa\n*/\n```\n\n## 传参\n\n- 基本类型传参与引用类型传参\n\n 基本类型传参,不多说\n\n 引用类型传参,即传入参数为引用类型\n\n- `=`的含义\n\n - 如果变量是基本类型,则 `=`表示赋值\n\n - 如果变量是引用类型,`=` 表示指向的意思,比如\n\n ```java\n Hero h = new Hero(); //引用h,指向一个Hero对象\n ```\n\n- 引用类型传参\n\n 形参与实参都指向同一对象\n\n ```java\n public class Hero {\n \n String name; \n \n float hp;\n \n public Hero(String name, float hp) {\n this.name = name;\n this.hp = hp;\n }\n \n public void attack(Hero hero) {\n System.out.println(\"hero:\"+hero);\n }\n \n public static void main(String[] args) {\n Hero teemo = new Hero(\"teemo\", 383);\n Hero garen = new Hero(\"garen\", 616);\n garen.attack(teemo);\n System.out.println(\"teemo:\"+teemo);\n \n }\n \n }\n \n /**输出:\n hero:Hero@2a139a55\n teemo:Hero@2a139a55\n */\n ```\n\n 用图表示如下![image-20210123184909979](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210123184909979.png)\n\n 但如果形参在内部指向了新的对象,实参是不会跟着改变的\n\n ```java\n public class Hero {\n \n String name; // 姓名\n \n float hp; // 血量\n \n public Hero(String name, float hp) {\n this.name = name;\n this.hp = hp;\n }\n \n public void attack(Hero hero) {\n hero = new Hero(\"hero\", 100); //形参指向了新的对象\n System.out.println(\"hero:\"+hero);\n }\n \n public static void main(String[] args) {\n Hero teemo = new Hero(\"teemo\", 383);\n Hero garen = new Hero(\"garen\", 616);\n garen.attack(teemo);\n System.out.println(\"teemo:\"+teemo);\n \n }\n \n }\n \n /**输出:\n hero:Hero@2a139a55\n teemo:Hero@15db9742\n */\n ```\n\n 用图表示如下![image-20210123185224792](https://gitee.com/ajream/images/raw/master/img/image-20210123185224792.png)\n\n\n\n## 类属性(又叫静态属性)\n\n当一个属性被**`static`**修饰的时候,就叫做**类属性**,又叫做**静态属性**\n当一个属性被声明成类属性,那么**所有的对象,都共享一个值**\n\n**对象属性:** 又叫实例属性,非静态属性\n\n> 与对象属性对比:\n> 不同对象的 对象属性 的值都可能不一样。\n\n- 访问类属性:\n\n ```java\n 类.类属性\n ```\n\n ```java\n //访问对象属性:\n 对象.对象属性\n ```\n\n- 类属性与对象属性的选择\n\n - 如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是**跟着对象走的**,每个对象的name都是不同的\n - 如果一个属性,**所有的英雄都共享**,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性\n\n\n\n## 类方法(静态方法)\n\n**类方法:** 又叫做静态方法,即被`static`修饰的方法\n\n**对象方法:** 又叫实例方法,非静态方法\n\n> 区别:\n>\n> 调用一个对象方法,必须**建立在有一个对象**的前提的基础上\n> 调用类方法,**不需要对象**的存在,直接就访问\n\n- 调用类方法\n\n ```java\n 类.类方法\n ```\n\n ```java\n //访问对象方法\n 对象.对象方法\n ```\n\n- 什么时候用类方法\n\n 如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如:\n\n ```java\n public static void printGameDuration(){\n \tSystem.out.println(\"已经玩了50秒\");\n }\n ```\n\n 反之,如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法;\n\n 注意:类方法中不能直接调用对象方法\n\n---\n\n[回到开头](#1-类与对象)\n\n","slug":"javaSE/1-类与对象","published":1,"updated":"2021-09-10T03:05:35.295Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qj005dakve37dy5lne","content":"类与对象 类可以看成是具有很多相似性质、功能的事物的集合,如鸟可以看作一个类,鸟都具有各自的名称,各色的羽毛、飞等相似特点;
\n用代码创建一个鸟类:
\n1 2 3 4 5 6 7 public class Bird { String name; String feathers_color; public void fly () { System.out.println(name + "可以飞" ) } }
\n对象可以看作是类的一个具体事例,如上面所说的鸟类,鸽子是鸟类的一种,老鹰也是鸟类一种,因此鸽子、老鹰都可以分别看作是鸟类的一个对象;
\n用代码来创建对象:
\n1 2 Bird bird1 = new Bird(); Bird bird2 = new Bird();
\n引用与指向 在下面创建的这一对象中
\n1 Bird bird1 = new Bird();
\nbird1
存储的是对象的地址,因此bird1
是对象的引用 ;这一地址指向 了对象本身;
\n
\n\n1 2 3 Bird b1 = new Bird(); Bird b2 = b1; Bird b3 = b1;
\n
\n\n1 2 3 Bird b1 = new Bird(); Bird b2 = new Bird(); Bird b3 = new Bird();
\n
\n继承 可以创建两个类,其中一个类可以继承另一个类的属性和方法,继承语法:
\n1 2 3 public class ClassName2 extends ClassName1 {}
\n例如:
\n首先创建父类Item
\n1 2 3 4 public class Item { String name; int price; }
\n创建子类Weapon继承Item
\n1 2 3 4 5 6 7 8 9 10 11 12 13 public class Weapon extends Item { int damage; public static void main (String[] args) { Weapon infinityEdge = new Weapon(); infinityEdge.damage = 65 ; infinityEdge.name = "无尽之刃" ; infinityEdge.price = 3600 ; } }
\n方法重载 方法的重载指的是方法名一样 ,但是参数类型或数量不一样
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class ADHero extends Hero { public void attack () { System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了" ); } \t public void attack (Hero h1) { System.out.println(name + "对" + h1.name + "进行了一次攻击 " ); } \t public void attack (Hero h1, Hero h2) { System.out.println(name + "同时对" + h1.name + "和" + h2.name + "进行了攻击 " ); } public static void main (String[] args) { ADHero bh = new ADHero(); bh.name = "赏金猎人" ; Hero h1 = new Hero(); h1.name = "盖伦" ; Hero h2 = new Hero(); h2.name = "提莫" ; bh.attack(h1); bh.attack(h1, h2); } }
\n可变数量的参数
\n采用可变数量的参数 只需要设计一个方法 :public void attack(Hero... heros)
即可代表上述所有的方法了,此时 heros
被当作一个数组 ,在方法里,使用操作数组的方式处理参数heros即可
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class ADHero extends Hero { public void attack () { System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了" ); } public void attack (Hero... heros) { for (int i = 0 ; i < heros.length; i++) { System.out.println(name + " 攻击了 " + heros[i].name); } } public static void main (String[] args) { ADHero bh = new ADHero(); bh.name = "赏金猎人" ; Hero h1 = new Hero(); h1.name = "盖伦" ; Hero h2 = new Hero(); h2.name = "提莫" ; bh.attack(h1); bh.attack(h1, h2); } }
\n构造方法 构造方法是一个特殊的方法,其方法名与类名一样,没有返回类型,在实例化一个对象时,必然调用构造方法
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Hero { String name; float hp; float armor; int moveSpeed; public Hero () { System.out.println("实例化一个对象的时候,必然调用构造方法" ); } public static void main (String[] args) { Hero h = new Hero(); } }
\n构造方法也可以重载,即可以有几个构造方法,但它们的参数数量或类型不同
\nthis this
代表当前对象 ,因此把this看成一个对象就行,对象的属性、方法、构造方法它都有
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Hero { String name; float hp; float armor; int moveSpeed; public void showAddressInMemory () { System.out.println("打印this看到的虚拟地址:" +this ); } public static void main (String[] args) { Hero garen = new Hero(); garen.name = "aaa" ; System.out.println("打印对象看到的虚拟地址:" +garen); garen.showAddressInMemory(); Hero teemo = new Hero(); teemo.name = "bbb" ; System.out.println("打印对象看到的虚拟地址:" +teemo); teemo.showAddressInMemory(); } }
\n通过this
可以访问对象的属性、方法
\n通过this()
可以调用其他的构造方法
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Hero { String name; float hp; float armor; int moveSpeed; public Hero (String name) { System.out.println("一个参数的构造方法" ); this .name = name; } public Hero (String name, float hp) { this (name); System.out.println("两个参数的构造方法" ); this .hp = hp; } public static void main (String[] args) { Hero teemo = new Hero("aaaa" , 383 ); System.out.println(teemo.name); } }
\n传参 \n类属性(又叫静态属性) 当一个属性被static
修饰的时候,就叫做类属性 ,又叫做静态属性 当一个属性被声明成类属性,那么所有的对象,都共享一个值
\n对象属性: 又叫实例属性,非静态属性
\n\n与对象属性对比: 不同对象的 对象属性 的值都可能不一样。
\n \n\n访问类属性:
\n \n \n \n类属性与对象属性的选择
\n\n如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是跟着对象走的 ,每个对象的name都是不同的 \n如果一个属性,所有的英雄都共享 ,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性 \n \n \n \n类方法(静态方法) 类方法: 又叫做静态方法,即被static
修饰的方法
\n对象方法: 又叫实例方法,非静态方法
\n\n区别:
\n调用一个对象方法,必须建立在有一个对象 的前提的基础上 调用类方法,不需要对象 的存在,直接就访问
\n \n\n \n回到开头
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"类与对象 类可以看成是具有很多相似性质、功能的事物的集合,如鸟可以看作一个类,鸟都具有各自的名称,各色的羽毛、飞等相似特点;
\n用代码创建一个鸟类:
\n1 2 3 4 5 6 7 public class Bird { String name; String feathers_color; public void fly () { System.out.println(name + "可以飞" ) } }
\n对象可以看作是类的一个具体事例,如上面所说的鸟类,鸽子是鸟类的一种,老鹰也是鸟类一种,因此鸽子、老鹰都可以分别看作是鸟类的一个对象;
\n用代码来创建对象:
\n1 2 Bird bird1 = new Bird(); Bird bird2 = new Bird();
\n引用与指向 在下面创建的这一对象中
\n1 Bird bird1 = new Bird();
\nbird1
存储的是对象的地址,因此bird1
是对象的引用 ;这一地址指向 了对象本身;
\n
\n\n1 2 3 Bird b1 = new Bird(); Bird b2 = b1; Bird b3 = b1;
\n
\n\n1 2 3 Bird b1 = new Bird(); Bird b2 = new Bird(); Bird b3 = new Bird();
\n
\n继承 可以创建两个类,其中一个类可以继承另一个类的属性和方法,继承语法:
\n1 2 3 public class ClassName2 extends ClassName1 {}
\n例如:
\n首先创建父类Item
\n1 2 3 4 public class Item { String name; int price; }
\n创建子类Weapon继承Item
\n1 2 3 4 5 6 7 8 9 10 11 12 13 public class Weapon extends Item { int damage; public static void main (String[] args) { Weapon infinityEdge = new Weapon(); infinityEdge.damage = 65 ; infinityEdge.name = "无尽之刃" ; infinityEdge.price = 3600 ; } }
\n方法重载 方法的重载指的是方法名一样 ,但是参数类型或数量不一样
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class ADHero extends Hero { public void attack () { System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了" ); } \t public void attack (Hero h1) { System.out.println(name + "对" + h1.name + "进行了一次攻击 " ); } \t public void attack (Hero h1, Hero h2) { System.out.println(name + "同时对" + h1.name + "和" + h2.name + "进行了攻击 " ); } public static void main (String[] args) { ADHero bh = new ADHero(); bh.name = "赏金猎人" ; Hero h1 = new Hero(); h1.name = "盖伦" ; Hero h2 = new Hero(); h2.name = "提莫" ; bh.attack(h1); bh.attack(h1, h2); } }
\n可变数量的参数
\n采用可变数量的参数 只需要设计一个方法 :public void attack(Hero... heros)
即可代表上述所有的方法了,此时 heros
被当作一个数组 ,在方法里,使用操作数组的方式处理参数heros即可
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class ADHero extends Hero { public void attack () { System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了" ); } public void attack (Hero... heros) { for (int i = 0 ; i < heros.length; i++) { System.out.println(name + " 攻击了 " + heros[i].name); } } public static void main (String[] args) { ADHero bh = new ADHero(); bh.name = "赏金猎人" ; Hero h1 = new Hero(); h1.name = "盖伦" ; Hero h2 = new Hero(); h2.name = "提莫" ; bh.attack(h1); bh.attack(h1, h2); } }
\n构造方法 构造方法是一个特殊的方法,其方法名与类名一样,没有返回类型,在实例化一个对象时,必然调用构造方法
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Hero { String name; float hp; float armor; int moveSpeed; public Hero () { System.out.println("实例化一个对象的时候,必然调用构造方法" ); } public static void main (String[] args) { Hero h = new Hero(); } }
\n构造方法也可以重载,即可以有几个构造方法,但它们的参数数量或类型不同
\nthis this
代表当前对象 ,因此把this看成一个对象就行,对象的属性、方法、构造方法它都有
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Hero { String name; float hp; float armor; int moveSpeed; public void showAddressInMemory () { System.out.println("打印this看到的虚拟地址:" +this ); } public static void main (String[] args) { Hero garen = new Hero(); garen.name = "aaa" ; System.out.println("打印对象看到的虚拟地址:" +garen); garen.showAddressInMemory(); Hero teemo = new Hero(); teemo.name = "bbb" ; System.out.println("打印对象看到的虚拟地址:" +teemo); teemo.showAddressInMemory(); } }
\n通过this
可以访问对象的属性、方法
\n通过this()
可以调用其他的构造方法
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Hero { String name; float hp; float armor; int moveSpeed; public Hero (String name) { System.out.println("一个参数的构造方法" ); this .name = name; } public Hero (String name, float hp) { this (name); System.out.println("两个参数的构造方法" ); this .hp = hp; } public static void main (String[] args) { Hero teemo = new Hero("aaaa" , 383 ); System.out.println(teemo.name); } }
\n传参 \n类属性(又叫静态属性) 当一个属性被static
修饰的时候,就叫做类属性 ,又叫做静态属性 当一个属性被声明成类属性,那么所有的对象,都共享一个值
\n对象属性: 又叫实例属性,非静态属性
\n\n与对象属性对比: 不同对象的 对象属性 的值都可能不一样。
\n \n\n访问类属性:
\n \n \n \n类属性与对象属性的选择
\n\n如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是跟着对象走的 ,每个对象的name都是不同的 \n如果一个属性,所有的英雄都共享 ,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性 \n \n \n \n类方法(静态方法) 类方法: 又叫做静态方法,即被static
修饰的方法
\n对象方法: 又叫实例方法,非静态方法
\n\n区别:
\n调用一个对象方法,必须建立在有一个对象 的前提的基础上 调用类方法,不需要对象 的存在,直接就访问
\n \n\n \n回到开头
\n"},{"title":"Http协议","description":"http协议介绍与工作原理","abbrlink":"374aa28c","date":"2021-04-16T06:31:33.000Z","_content":"\n\n\n\n\n\n\n## 介绍\n\n\n\n超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在[TCP](https://baike.baidu.com/item/TCP/33012)之上,如图\n\n![image-20210903214830491](https://gitee.com/ajream/images/raw/master/img/20210903214835_image-20210903214830491.png)\n\n\n\n文本:html, 字符串......\n\n超文本:图片、音乐……\n\nhttp端口:80\n\nhttps端口:443\n\n\n\n## Http请求与响应\n\nHTTP协议永远都是客户端发起请求,服务器回送响应。见下图:\n\n![image-20210903214905388](https://gitee.com/ajream/images/raw/master/img/20210903214907_image-20210903214905388.png)\n\n这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端\n\n\n\n请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体\n\n响应(应答)报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体\n\n\n\n**HTTP请求方式**\n\nGET:最常用的请求方式,使用GET方法应该只用在获取数据 \n\nPOST:向指定资源提交数据 ,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有\n\n除此以外还有`HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT` 这8种不常用的请求方式\n\n\n\n**URL**\n\nURL被称为:统一资源定位符\n\n其中通常包含信息:\n\n- 传送协议\n- 层级URL标记符号(为[//],固定不变)\n- 访问资源需要的凭证信息(可省略)\n- 服务器地址(通常为域名,有时为IP地址)\n- 端口号(可省略,默认使用http协议,端口为80)\n- 资源路径(以“/”字符区别路径中的每一个目录名称)\n- 查询(可省略,GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)\n- 片段。以“#”字符为起点(可省略)\n\n例如:\n\n```\nhttp://www.luffycity.com:80/news/index.html?id=250&page=1\n```\n\n\n\n**HTTP状态码**\n\n状态码元由3位数字组成,表示请求是否被理解或被满足。\n\n\n\n1XX: 信息——请求已被服务器接收,继续处理\n\n2XX: 连接成功\n\n- 200:请求成功(其后是对GET和POST请求的应答文档)\n\n3XX: 重定向\n\n- 300:多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址\n\n- 301:所请求的页面已经转移至新的url\n\n- 302:所请求的页面已经临时转移至新的url\n\n- 303:所请求的页面可在别的url下被找到\n\n \n\n4XX: 请求错误\n\n- 400:服务器不理解请求\n- 401:被请求的页面需要用户名和密码\n- 403:对被请求页面的访问被禁止\n- 404:服务器无法找到被请求的页面\n\n5XX: 服务器错误\n\n- 500:请求未完成,服务器遇到不可预知的情况。\n- 501:请求未完成。服务器不支持所请求的功能。\n- 503:请求未完成。服务器临时过载或宕机。\n- 504:网关超时\n\n\n\n\n\n**HTTP请求报文**\n\n \n\n例如:\n\n \n\n\n\n**HTTP响应报文**\n\n \n\n\n\n例如:\n\n \n\n\n\n## Http工作原理\n\nHTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。\n\nHTTP协议采用了请求/响应模型:\n\n- 客户端向服务器发送一个【请求报文】,请求报文包含请求的方法(get/post/...)、URL、协议版本、请求头部和请求数据。\n\n- 服务器以一个【状态行】作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。\n\n典型的HTTP事务处理有如下的过程: \n\n(1)客户与服务器建立连接:\n\n一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接\n\n(2)客户向服务器提出请求:\n\n通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。\n\n(3)服务器接受请求,并根据请求返回相应的文件作为应答:\n\nWeb服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。\n\n(4)客户与服务器关闭连接:\n\n- 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;\n\n- 若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;\n\n(5)客户端浏览器解析HTML内容:\n\n客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。\n\n然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。\n\n客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。\n\n","source":"_posts/javaEE/http.md","raw":"---\ntitle: Http协议\ntags:\n - Http\ncategories:\n - - java\n - javaEE\ndescription: http协议介绍与工作原理\nabbrlink: 374aa28c\ndate: 2021-04-16 14:31:33\n---\n\n\n\n\n\n\n\n## 介绍\n\n\n\n超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在[TCP](https://baike.baidu.com/item/TCP/33012)之上,如图\n\n![image-20210903214830491](https://gitee.com/ajream/images/raw/master/img/20210903214835_image-20210903214830491.png)\n\n\n\n文本:html, 字符串......\n\n超文本:图片、音乐……\n\nhttp端口:80\n\nhttps端口:443\n\n\n\n## Http请求与响应\n\nHTTP协议永远都是客户端发起请求,服务器回送响应。见下图:\n\n![image-20210903214905388](https://gitee.com/ajream/images/raw/master/img/20210903214907_image-20210903214905388.png)\n\n这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端\n\n\n\n请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体\n\n响应(应答)报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体\n\n\n\n**HTTP请求方式**\n\nGET:最常用的请求方式,使用GET方法应该只用在获取数据 \n\nPOST:向指定资源提交数据 ,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有\n\n除此以外还有`HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT` 这8种不常用的请求方式\n\n\n\n**URL**\n\nURL被称为:统一资源定位符\n\n其中通常包含信息:\n\n- 传送协议\n- 层级URL标记符号(为[//],固定不变)\n- 访问资源需要的凭证信息(可省略)\n- 服务器地址(通常为域名,有时为IP地址)\n- 端口号(可省略,默认使用http协议,端口为80)\n- 资源路径(以“/”字符区别路径中的每一个目录名称)\n- 查询(可省略,GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)\n- 片段。以“#”字符为起点(可省略)\n\n例如:\n\n```\nhttp://www.luffycity.com:80/news/index.html?id=250&page=1\n```\n\n\n\n**HTTP状态码**\n\n状态码元由3位数字组成,表示请求是否被理解或被满足。\n\n\n\n1XX: 信息——请求已被服务器接收,继续处理\n\n2XX: 连接成功\n\n- 200:请求成功(其后是对GET和POST请求的应答文档)\n\n3XX: 重定向\n\n- 300:多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址\n\n- 301:所请求的页面已经转移至新的url\n\n- 302:所请求的页面已经临时转移至新的url\n\n- 303:所请求的页面可在别的url下被找到\n\n \n\n4XX: 请求错误\n\n- 400:服务器不理解请求\n- 401:被请求的页面需要用户名和密码\n- 403:对被请求页面的访问被禁止\n- 404:服务器无法找到被请求的页面\n\n5XX: 服务器错误\n\n- 500:请求未完成,服务器遇到不可预知的情况。\n- 501:请求未完成。服务器不支持所请求的功能。\n- 503:请求未完成。服务器临时过载或宕机。\n- 504:网关超时\n\n\n\n\n\n**HTTP请求报文**\n\n \n\n例如:\n\n \n\n\n\n**HTTP响应报文**\n\n \n\n\n\n例如:\n\n \n\n\n\n## Http工作原理\n\nHTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。\n\nHTTP协议采用了请求/响应模型:\n\n- 客户端向服务器发送一个【请求报文】,请求报文包含请求的方法(get/post/...)、URL、协议版本、请求头部和请求数据。\n\n- 服务器以一个【状态行】作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。\n\n典型的HTTP事务处理有如下的过程: \n\n(1)客户与服务器建立连接:\n\n一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接\n\n(2)客户向服务器提出请求:\n\n通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。\n\n(3)服务器接受请求,并根据请求返回相应的文件作为应答:\n\nWeb服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。\n\n(4)客户与服务器关闭连接:\n\n- 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;\n\n- 若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;\n\n(5)客户端浏览器解析HTML内容:\n\n客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。\n\n然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。\n\n客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。\n\n","slug":"javaEE/http","published":1,"updated":"2021-09-03T13:53:35.404Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qj005fakvegpvj86dz","content":"介绍 超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP 之上,如图
\n
\n文本:html, 字符串……
\n超文本:图片、音乐……
\nhttp端口:80
\nhttps端口:443
\nHttp请求与响应 HTTP协议永远都是客户端发起请求,服务器回送响应。见下图:
\n
\n这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端
\n请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体
\n响应(应答)报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体
\nHTTP请求方式
\nGET:最常用的请求方式,使用GET方法应该只用在获取数据
\nPOST:向指定资源提交数据 ,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有
\n除此以外还有HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT
这8种不常用的请求方式
\nURL
\nURL被称为:统一资源定位符
\n其中通常包含信息:
\n\n传送协议 \n层级URL标记符号(为[//],固定不变) \n访问资源需要的凭证信息(可省略) \n服务器地址(通常为域名,有时为IP地址) \n端口号(可省略,默认使用http协议,端口为80) \n资源路径(以“/”字符区别路径中的每一个目录名称) \n查询(可省略,GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题) \n片段。以“#”字符为起点(可省略) \n \n例如:
\n1 http://www.luffycity.com:80/news/index.html?id=250&page=1
\nHTTP状态码
\n状态码元由3位数字组成,表示请求是否被理解或被满足。
\n1XX: 信息——请求已被服务器接收,继续处理
\n2XX: 连接成功
\n\n200:请求成功(其后是对GET和POST请求的应答文档) \n \n3XX: 重定向
\n\n4XX: 请求错误
\n\n400:服务器不理解请求 \n401:被请求的页面需要用户名和密码 \n403:对被请求页面的访问被禁止 \n404:服务器无法找到被请求的页面 \n \n5XX: 服务器错误
\n\n500:请求未完成,服务器遇到不可预知的情况。 \n501:请求未完成。服务器不支持所请求的功能。 \n503:请求未完成。服务器临时过载或宕机。 \n504:网关超时 \n \nHTTP请求报文
\n
\n例如:
\n
\nHTTP响应报文
\n
\n例如:
\n
\nHttp工作原理 HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。
\nHTTP协议采用了请求/响应模型:
\n\n典型的HTTP事务处理有如下的过程:
\n(1)客户与服务器建立连接:
\n一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接
\n(2)客户向服务器提出请求:
\n通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。
\n(3)服务器接受请求,并根据请求返回相应的文件作为应答:
\nWeb服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。
\n(4)客户与服务器关闭连接:
\n\n(5)客户端浏览器解析HTML内容:
\n客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。
\n然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。
\n客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":"介绍 超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP 之上,如图
\n
\n文本:html, 字符串……
\n超文本:图片、音乐……
\nhttp端口:80
\nhttps端口:443
\nHttp请求与响应 HTTP协议永远都是客户端发起请求,服务器回送响应。见下图:
\n
\n这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端
\n请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体
\n响应(应答)报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体
\nHTTP请求方式
\nGET:最常用的请求方式,使用GET方法应该只用在获取数据
\nPOST:向指定资源提交数据 ,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有
\n除此以外还有HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT
这8种不常用的请求方式
\nURL
\nURL被称为:统一资源定位符
\n其中通常包含信息:
\n\n传送协议 \n层级URL标记符号(为[//],固定不变) \n访问资源需要的凭证信息(可省略) \n服务器地址(通常为域名,有时为IP地址) \n端口号(可省略,默认使用http协议,端口为80) \n资源路径(以“/”字符区别路径中的每一个目录名称) \n查询(可省略,GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题) \n片段。以“#”字符为起点(可省略) \n \n例如:
\n1 http://www.luffycity.com:80/news/index.html?id=250&page=1
\nHTTP状态码
\n状态码元由3位数字组成,表示请求是否被理解或被满足。
\n1XX: 信息——请求已被服务器接收,继续处理
\n2XX: 连接成功
\n\n200:请求成功(其后是对GET和POST请求的应答文档) \n \n3XX: 重定向
\n\n4XX: 请求错误
\n\n400:服务器不理解请求 \n401:被请求的页面需要用户名和密码 \n403:对被请求页面的访问被禁止 \n404:服务器无法找到被请求的页面 \n \n5XX: 服务器错误
\n\n500:请求未完成,服务器遇到不可预知的情况。 \n501:请求未完成。服务器不支持所请求的功能。 \n503:请求未完成。服务器临时过载或宕机。 \n504:网关超时 \n \nHTTP请求报文
\n
\n例如:
\n
\nHTTP响应报文
\n
\n例如:
\n
\nHttp工作原理 HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。
\nHTTP协议采用了请求/响应模型:
\n\n典型的HTTP事务处理有如下的过程:
\n(1)客户与服务器建立连接:
\n一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接
\n(2)客户向服务器提出请求:
\n通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。
\n(3)服务器接受请求,并根据请求返回相应的文件作为应答:
\nWeb服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。
\n(4)客户与服务器关闭连接:
\n\n(5)客户端浏览器解析HTML内容:
\n客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。
\n然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。
\n客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。
\n"},{"title":"11-数字与字符串相互转换","abbrlink":"38b36403","date":"2021-02-13T10:58:18.000Z","description":"java的数字类型与String类型的转换实际依靠封装类里面的方法","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 数字⇿字符串\n\n## 数字转字符串(2种方法)\n\n- 使用String类的静态方法`valueOf`\n- 先把基本类型装箱为对象,然后调用对象的`toString`\n\n```java\nint i = 5;\n \n//方法1\nString str = String.valueOf(i);\n \n//方法2\nInteger it = i;\nString str2 = it.toString();\n```\n\n\n\n## 字符串转数字\n\n- 调用Integer的静态方法`parseInt`\n\n```java\nString str = \"999\";\n \nint i= Integer.parseInt(str);\n```\n\n\n\n","source":"_posts/javaSE/11-数字与字符串相互转换.md","raw":"---\ntitle: 11-数字与字符串相互转换\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 38b36403\ndate: 2021-02-13 18:58:18\ndescription: java的数字类型与String类型的转换实际依靠封装类里面的方法\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 数字⇿字符串\n\n## 数字转字符串(2种方法)\n\n- 使用String类的静态方法`valueOf`\n- 先把基本类型装箱为对象,然后调用对象的`toString`\n\n```java\nint i = 5;\n \n//方法1\nString str = String.valueOf(i);\n \n//方法2\nInteger it = i;\nString str2 = it.toString();\n```\n\n\n\n## 字符串转数字\n\n- 调用Integer的静态方法`parseInt`\n\n```java\nString str = \"999\";\n \nint i= Integer.parseInt(str);\n```\n\n\n\n","slug":"javaSE/11-数字与字符串相互转换","published":1,"updated":"2021-08-29T15:31:24.118Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ql005jakve8xo8cwcv","content":"数字⇿字符串 数字转字符串(2种方法) \n使用String类的静态方法valueOf
\n先把基本类型装箱为对象,然后调用对象的toString
\n \n1 2 3 4 5 6 7 8 int i = 5 ; String str = String.valueOf(i); Integer it = i; String str2 = it.toString();
\n字符串转数字 \n调用Integer的静态方法parseInt
\n \n1 2 3 String str = "999" ; int i= Integer.parseInt(str);
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"数字⇿字符串 数字转字符串(2种方法) \n使用String类的静态方法valueOf
\n先把基本类型装箱为对象,然后调用对象的toString
\n \n1 2 3 4 5 6 7 8 int i = 5 ; String str = String.valueOf(i); Integer it = i; String str2 = it.toString();
\n字符串转数字 \n调用Integer的静态方法parseInt
\n \n1 2 3 String str = "999" ; int i= Integer.parseInt(str);
\n"},{"title":"Tomcat基础","abbrlink":"530cfffd","date":"2021-06-11T05:31:26.000Z","description":"Web基础知识、服务器、Tomcat介绍、安装与使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210901182628_tomcat.png","_content":"\n# Tomcat了解\n\n\n\n## web概念\n\n1. 软件架构\n - C/S:客户端/服务器端(QQ、360。。。)\n - B/S:浏览器/服务器端(京东、淘宝、网易。。。)\n2. 资源分类\n - 静态资源(所有用户访问得到的结果一样)\n - 动态资源(不同用户访问的结果不同)\n3. 网络通信三要素\n - 协议\n - tcp:安全协议、三次握手、速度较慢\n - udp:不安全、速度快\n - ip:电子设备在网络中唯一标识\n - 端口号\n\n\n\n## 常见web服务器软件\n\n1. webLogic:大型JavaEE服务器,属于Oracle公司,收费\n2. webSphere:IBM公司\n3. JBOSS:JBOSS公司\n4. Tomcat:Apache组织\n\n\n\n## Tomcat介绍\n\n[Tomcat下载](https://tomcat.apache.org/download-80.cgi)\n\n\n\nTomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用[服务器](https://baike.baidu.com/item/服务器),在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应[HTML](https://baike.baidu.com/item/HTML)页面的访问请求。\n\n实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。\n\n\n\n### 目录结构\n\n![image-20210903200423036](https://gitee.com/ajream/images/raw/master/img/20210903200427_image-20210903200423036.png)\n\n\n\n重点,服务器配置文件 `/conf/server.xml`\n\n配置访问端口号,默认是 `8080`,如果被占用了可以改为其它端口\n\n```xml\n \n```\n\n配置服务器地址\n\nlocalhost: 实际是指向域名 `127.0.0.1`\n\n```xml\n\n```\n\n{%note primary flat%}\n\n面试题:访问一个网站的流程?\n\n大概是下面这样:(实际会更加麻烦,下面只是简化后的样子)\n\n1. 浏览器输入域名,回车\n2. 浏览器查找本地 `C:\\Windows\\System32\\drivers\\etc\\host`文件,进行域名解析\n3. 如果没有找到,则去DNS服务器查找\n\n{%endnote%}\n\n\n\n## Tomcat使用\n\n\n\n### 启动tomcat\n\n运行bin目录下的批处理文件 `startup.bat`(确认当前是jdk1.8或以上,用`java -version`查看)\n\n\n\n### 部署网页\n\n例如部署一个test.html文件\n\n1. 把test.html文件复制到**D:\\tomcat\\webapps\\ROOT** 目录下\n2. 启动tomcat\n3. 访问 http://127.0.0.1:8080/test.html\n\n\n\n### 改端口号\n\n8080是tomcat默认端口号,而平时上网默认使用80端口\n\ntomcat的端口配置相关信息在 server.xml中,server.xml 记录了非常多的tomcat配置信息,其中就包括端口。\n\n修改端口号步骤:\n\n1. 打开server.xml文件\n2. `ctrl+f`查询8080,将下面这句代码的`8080`修改为其他端口号,如80,8081,8082\n\n```xml\n \n```\n\n接着就可以直接通过 http://127.0.0.1/test.html访问网页了。\n\n80端口就是web服务默认的端口号,所以就不需要显式写这个端口号了。\n\n\n\n### 端口被占用\n\n端口被占用就无法启动tomcat,所以要找到端口被占用的程序是哪一个,然后再关闭对应的程序即可\n\n- 查看80端口被哪些程序占用\n\n ```shell\n netstat -ano|findstr \"80\"\n ```\n\n ![image-20210408072223224](https://gitee.com/ajream/images/raw/master/img/image-20210408072223224.png)\n\n 第一行就是占用端口“80”的进程(其他只是含有80这个字符串而已),最后一列对应的进程的pid\n\n- 根据pid查询对应应用程序\n\n ```sh\n tasklist|findstr \"4\"\n ```\n\n ![image-20210408072728546](https://gitee.com/ajream/images/raw/master/img/image-20210408072728546.png)\n\n 第一个system就是占用的程序\n\n- 根据名称结束该进程\n\n ```shell\n taskkill /f /t /im java.exe\n ```\n\n 结束java.exe\n\n 结束成功会提示:\n 成功: 已终止 。。。\n\n 或者通过pid终止:\n\n ```shell\n taskkill /f /pid 1828\n ```\n\n \n\n ![根据名称 结束该程序](https://stepimagewm.how2j.cn/1565.png)\n\n\n\n\n\n> 也可以把配置文件 `server.xml` 的默认端口号更改\n>\n\n","source":"_posts/javaEE/Tomcat基础.md","raw":"---\ntitle: Tomcat基础\ntags:\n - Tomcat\ncategories:\n - - java\n - javaEE\nabbrlink: 530cfffd\ndate: 2021-06-11 13:31:26\ndescription: Web基础知识、服务器、Tomcat介绍、安装与使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210901182628_tomcat.png\n---\n\n# Tomcat了解\n\n\n\n## web概念\n\n1. 软件架构\n - C/S:客户端/服务器端(QQ、360。。。)\n - B/S:浏览器/服务器端(京东、淘宝、网易。。。)\n2. 资源分类\n - 静态资源(所有用户访问得到的结果一样)\n - 动态资源(不同用户访问的结果不同)\n3. 网络通信三要素\n - 协议\n - tcp:安全协议、三次握手、速度较慢\n - udp:不安全、速度快\n - ip:电子设备在网络中唯一标识\n - 端口号\n\n\n\n## 常见web服务器软件\n\n1. webLogic:大型JavaEE服务器,属于Oracle公司,收费\n2. webSphere:IBM公司\n3. JBOSS:JBOSS公司\n4. Tomcat:Apache组织\n\n\n\n## Tomcat介绍\n\n[Tomcat下载](https://tomcat.apache.org/download-80.cgi)\n\n\n\nTomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用[服务器](https://baike.baidu.com/item/服务器),在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应[HTML](https://baike.baidu.com/item/HTML)页面的访问请求。\n\n实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。\n\n\n\n### 目录结构\n\n![image-20210903200423036](https://gitee.com/ajream/images/raw/master/img/20210903200427_image-20210903200423036.png)\n\n\n\n重点,服务器配置文件 `/conf/server.xml`\n\n配置访问端口号,默认是 `8080`,如果被占用了可以改为其它端口\n\n```xml\n \n```\n\n配置服务器地址\n\nlocalhost: 实际是指向域名 `127.0.0.1`\n\n```xml\n\n```\n\n{%note primary flat%}\n\n面试题:访问一个网站的流程?\n\n大概是下面这样:(实际会更加麻烦,下面只是简化后的样子)\n\n1. 浏览器输入域名,回车\n2. 浏览器查找本地 `C:\\Windows\\System32\\drivers\\etc\\host`文件,进行域名解析\n3. 如果没有找到,则去DNS服务器查找\n\n{%endnote%}\n\n\n\n## Tomcat使用\n\n\n\n### 启动tomcat\n\n运行bin目录下的批处理文件 `startup.bat`(确认当前是jdk1.8或以上,用`java -version`查看)\n\n\n\n### 部署网页\n\n例如部署一个test.html文件\n\n1. 把test.html文件复制到**D:\\tomcat\\webapps\\ROOT** 目录下\n2. 启动tomcat\n3. 访问 http://127.0.0.1:8080/test.html\n\n\n\n### 改端口号\n\n8080是tomcat默认端口号,而平时上网默认使用80端口\n\ntomcat的端口配置相关信息在 server.xml中,server.xml 记录了非常多的tomcat配置信息,其中就包括端口。\n\n修改端口号步骤:\n\n1. 打开server.xml文件\n2. `ctrl+f`查询8080,将下面这句代码的`8080`修改为其他端口号,如80,8081,8082\n\n```xml\n \n```\n\n接着就可以直接通过 http://127.0.0.1/test.html访问网页了。\n\n80端口就是web服务默认的端口号,所以就不需要显式写这个端口号了。\n\n\n\n### 端口被占用\n\n端口被占用就无法启动tomcat,所以要找到端口被占用的程序是哪一个,然后再关闭对应的程序即可\n\n- 查看80端口被哪些程序占用\n\n ```shell\n netstat -ano|findstr \"80\"\n ```\n\n ![image-20210408072223224](https://gitee.com/ajream/images/raw/master/img/image-20210408072223224.png)\n\n 第一行就是占用端口“80”的进程(其他只是含有80这个字符串而已),最后一列对应的进程的pid\n\n- 根据pid查询对应应用程序\n\n ```sh\n tasklist|findstr \"4\"\n ```\n\n ![image-20210408072728546](https://gitee.com/ajream/images/raw/master/img/image-20210408072728546.png)\n\n 第一个system就是占用的程序\n\n- 根据名称结束该进程\n\n ```shell\n taskkill /f /t /im java.exe\n ```\n\n 结束java.exe\n\n 结束成功会提示:\n 成功: 已终止 。。。\n\n 或者通过pid终止:\n\n ```shell\n taskkill /f /pid 1828\n ```\n\n \n\n ![根据名称 结束该程序](https://stepimagewm.how2j.cn/1565.png)\n\n\n\n\n\n> 也可以把配置文件 `server.xml` 的默认端口号更改\n>\n\n","slug":"javaEE/Tomcat基础","published":1,"updated":"2021-09-03T12:21:26.646Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ql005makve7a1dc654","content":"Tomcat了解 web概念 \n软件架构\nC/S:客户端/服务器端(QQ、360。。。) \nB/S:浏览器/服务器端(京东、淘宝、网易。。。) \n \n \n资源分类\n静态资源(所有用户访问得到的结果一样) \n动态资源(不同用户访问的结果不同) \n \n \n网络通信三要素\n协议\ntcp:安全协议、三次握手、速度较慢 \nudp:不安全、速度快 \n \n \nip:电子设备在网络中唯一标识 \n端口号 \n \n \n \n常见web服务器软件 \nwebLogic:大型JavaEE服务器,属于Oracle公司,收费 \nwebSphere:IBM公司 \nJBOSS:JBOSS公司 \nTomcat:Apache组织 \n \nTomcat介绍 Tomcat下载
\nTomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器 ,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML 页面的访问请求。
\n实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。
\n目录结构
\n重点,服务器配置文件 /conf/server.xml
\n配置访问端口号,默认是 8080
,如果被占用了可以改为其它端口
\n1 2 3 <Connector port ="8080" protocol ="HTTP/1.1" connectionTimeout ="20000" redirectPort ="8443" />
\n配置服务器地址
\nlocalhost: 实际是指向域名 127.0.0.1
\n1 2 <Host name ="localhost" appBase ="webapps" unpackWARs ="true" autoDeploy ="true" >
\n面试题:访问一个网站的流程?
\n
大概是下面这样:(实际会更加麻烦,下面只是简化后的样子)
\n
\n浏览器输入域名,回车 \n浏览器查找本地 C:\\Windows\\System32\\drivers\\etc\\host
文件,进行域名解析 \n如果没有找到,则去DNS服务器查找 \n \n
\nTomcat使用 启动tomcat 运行bin目录下的批处理文件 startup.bat
(确认当前是jdk1.8或以上,用java -version
查看)
\n部署网页 例如部署一个test.html文件
\n\n把test.html文件复制到D:\\tomcat\\webapps\\ROOT 目录下 \n启动tomcat \n访问 http://127.0.0.1:8080/test.html \n \n改端口号 8080是tomcat默认端口号,而平时上网默认使用80端口
\ntomcat的端口配置相关信息在 server.xml中,server.xml 记录了非常多的tomcat配置信息,其中就包括端口。
\n修改端口号步骤:
\n\n打开server.xml文件 \nctrl+f
查询8080,将下面这句代码的8080
修改为其他端口号,如80,8081,8082 \n \n1 <Connector port ="8080" protocol ="HTTP/1.1" connectionTimeout ="20000" redirectPort ="8543" />
\n接着就可以直接通过 http://127.0.0.1/test.html访问网页了。
\n80端口就是web服务默认的端口号,所以就不需要显式写这个端口号了。
\n端口被占用 端口被占用就无法启动tomcat,所以要找到端口被占用的程序是哪一个,然后再关闭对应的程序即可
\n\n
\n\n也可以把配置文件 server.xml
的默认端口号更改
\n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"Tomcat了解 web概念 \n软件架构\nC/S:客户端/服务器端(QQ、360。。。) \nB/S:浏览器/服务器端(京东、淘宝、网易。。。) \n \n \n资源分类\n静态资源(所有用户访问得到的结果一样) \n动态资源(不同用户访问的结果不同) \n \n \n网络通信三要素\n协议\ntcp:安全协议、三次握手、速度较慢 \nudp:不安全、速度快 \n \n \nip:电子设备在网络中唯一标识 \n端口号 \n \n \n \n常见web服务器软件 \nwebLogic:大型JavaEE服务器,属于Oracle公司,收费 \nwebSphere:IBM公司 \nJBOSS:JBOSS公司 \nTomcat:Apache组织 \n \nTomcat介绍 Tomcat下载
\nTomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器 ,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML 页面的访问请求。
\n实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。
\n目录结构
\n重点,服务器配置文件 /conf/server.xml
\n配置访问端口号,默认是 8080
,如果被占用了可以改为其它端口
\n1 2 3 <Connector port ="8080" protocol ="HTTP/1.1" connectionTimeout ="20000" redirectPort ="8443" />
\n配置服务器地址
\nlocalhost: 实际是指向域名 127.0.0.1
\n1 2 <Host name ="localhost" appBase ="webapps" unpackWARs ="true" autoDeploy ="true" >
\n面试题:访问一个网站的流程?
\n
大概是下面这样:(实际会更加麻烦,下面只是简化后的样子)
\n
\n浏览器输入域名,回车 \n浏览器查找本地 C:\\Windows\\System32\\drivers\\etc\\host
文件,进行域名解析 \n如果没有找到,则去DNS服务器查找 \n \n
\nTomcat使用 启动tomcat 运行bin目录下的批处理文件 startup.bat
(确认当前是jdk1.8或以上,用java -version
查看)
\n部署网页 例如部署一个test.html文件
\n\n把test.html文件复制到D:\\tomcat\\webapps\\ROOT 目录下 \n启动tomcat \n访问 http://127.0.0.1:8080/test.html \n \n改端口号 8080是tomcat默认端口号,而平时上网默认使用80端口
\ntomcat的端口配置相关信息在 server.xml中,server.xml 记录了非常多的tomcat配置信息,其中就包括端口。
\n修改端口号步骤:
\n\n打开server.xml文件 \nctrl+f
查询8080,将下面这句代码的8080
修改为其他端口号,如80,8081,8082 \n \n1 <Connector port ="8080" protocol ="HTTP/1.1" connectionTimeout ="20000" redirectPort ="8543" />
\n接着就可以直接通过 http://127.0.0.1/test.html访问网页了。
\n80端口就是web服务默认的端口号,所以就不需要显式写这个端口号了。
\n端口被占用 端口被占用就无法启动tomcat,所以要找到端口被占用的程序是哪一个,然后再关闭对应的程序即可
\n\n
\n\n也可以把配置文件 server.xml
的默认端口号更改
\n \n"},{"title":"10-装箱与拆箱","abbrlink":"3aac01ca","date":"2021-02-13T11:33:27.000Z","description":"java的基本数据类型实际上都有一个封装类,这样就可以更方便的操作这些基本数据类型,封装类与基本数据类型的转换即为装箱、拆箱","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 装箱与拆箱\n\n## 封装类\n\n> 所有的**基本类型**,都有对应的**类类型**\n> 比如int对应的类是Integer\n> 这种类就叫做封装类\n\n```java\nint i = 5;\n \n//把一个基本类型的变量,转换为Integer对象\nInteger it = new Integer(i);\n\n//把一个Integer对象,转换为一个基本类型的int\nint i2 = it.intValue();\n```\n\n## 10.2 Number类\n\n> 数字封装类有\n>\n> - Byte Short Integer Long\n> - Float Double\n>\n> 这些类都是抽象类`Number`的子类\n\n## 基本类型<->封装类型\n\n- 基本类型转换成封装类型\n\n```java\nint i = 5;\n \nInteger it = new Integer(i); //i转换成封装类型it\n \n```\n\n- 封装类型转换成基本类型\n\n```java\nint i = 5;\n\nInteger it = new Integer(i); //i转换成封装类型it\n\nint i2 = it.intValue(); //封装类型转it换成基本类型i2,i2与i是一样的\n```\n\n\n\n## 自动装箱与拆箱\n\n- 自动装箱:通过`=`符号自动把 基本类型 —> 类类型\n\n ```java\n int i = 5;\n Integer it = i; //自动装箱\n ```\n\n- 自动拆箱:与自动装箱相反,通过`=`符号自动把 类类型 —> 基本类型\n\n ```java\n int i = 5;\n Integer it = i; //自动装箱\n \n int i1 = it;\t\t//自动拆箱\n ```\n\n\n\n## int的最大值/最小值\n\nint的最大值可以通过其对应的封装类`Integer.MAX_VALUE`获取;\n\n同理,最小值通过 `Integer.MIN_VALUE` 获取;\n\n\n\n","source":"_posts/javaSE/10-装箱与拆箱.md","raw":"---\ntitle: 10-装箱与拆箱\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 3aac01ca\ndate: 2021-02-13 19:33:27\ndescription: java的基本数据类型实际上都有一个封装类,这样就可以更方便的操作这些基本数据类型,封装类与基本数据类型的转换即为装箱、拆箱\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 装箱与拆箱\n\n## 封装类\n\n> 所有的**基本类型**,都有对应的**类类型**\n> 比如int对应的类是Integer\n> 这种类就叫做封装类\n\n```java\nint i = 5;\n \n//把一个基本类型的变量,转换为Integer对象\nInteger it = new Integer(i);\n\n//把一个Integer对象,转换为一个基本类型的int\nint i2 = it.intValue();\n```\n\n## 10.2 Number类\n\n> 数字封装类有\n>\n> - Byte Short Integer Long\n> - Float Double\n>\n> 这些类都是抽象类`Number`的子类\n\n## 基本类型<->封装类型\n\n- 基本类型转换成封装类型\n\n```java\nint i = 5;\n \nInteger it = new Integer(i); //i转换成封装类型it\n \n```\n\n- 封装类型转换成基本类型\n\n```java\nint i = 5;\n\nInteger it = new Integer(i); //i转换成封装类型it\n\nint i2 = it.intValue(); //封装类型转it换成基本类型i2,i2与i是一样的\n```\n\n\n\n## 自动装箱与拆箱\n\n- 自动装箱:通过`=`符号自动把 基本类型 —> 类类型\n\n ```java\n int i = 5;\n Integer it = i; //自动装箱\n ```\n\n- 自动拆箱:与自动装箱相反,通过`=`符号自动把 类类型 —> 基本类型\n\n ```java\n int i = 5;\n Integer it = i; //自动装箱\n \n int i1 = it;\t\t//自动拆箱\n ```\n\n\n\n## int的最大值/最小值\n\nint的最大值可以通过其对应的封装类`Integer.MAX_VALUE`获取;\n\n同理,最小值通过 `Integer.MIN_VALUE` 获取;\n\n\n\n","slug":"javaSE/10-装箱与拆箱","published":1,"updated":"2021-08-29T15:31:20.436Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qn005qakve26rcd19s","content":"装箱与拆箱 封装类 \n所有的基本类型 ,都有对应的类类型 比如int对应的类是Integer 这种类就叫做封装类
\n \n1 2 3 4 5 6 7 int i = 5 ; Integer it = new Integer(i); int i2 = it.intValue();
\n10.2 Number类 \n数字封装类有
\n\nByte Short Integer Long \nFloat Double \n \n这些类都是抽象类Number
的子类
\n \n基本类型<->封装类型 \n1 2 3 4 int i = 5 ; Integer it = new Integer(i);
\n\n1 2 3 4 5 int i = 5 ;Integer it = new Integer(i); int i2 = it.intValue();
\n自动装箱与拆箱 \nint的最大值/最小值 int的最大值可以通过其对应的封装类Integer.MAX_VALUE
获取;
\n同理,最小值通过 Integer.MIN_VALUE
获取;
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"装箱与拆箱 封装类 \n所有的基本类型 ,都有对应的类类型 比如int对应的类是Integer 这种类就叫做封装类
\n \n1 2 3 4 5 6 7 int i = 5 ; Integer it = new Integer(i); int i2 = it.intValue();
\n10.2 Number类 \n数字封装类有
\n\nByte Short Integer Long \nFloat Double \n \n这些类都是抽象类Number
的子类
\n \n基本类型<->封装类型 \n1 2 3 4 int i = 5 ; Integer it = new Integer(i);
\n\n1 2 3 4 5 int i = 5 ;Integer it = new Integer(i); int i2 = it.intValue();
\n自动装箱与拆箱 \nint的最大值/最小值 int的最大值可以通过其对应的封装类Integer.MAX_VALUE
获取;
\n同理,最小值通过 Integer.MIN_VALUE
获取;
\n"},{"title":"15-异常处理","abbrlink":"f5c95c","date":"2021-02-15T02:46:30.000Z","description":"java中的异常处理","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 异常处理\n\n## 异常定义\n\n导致程序的正常流程被中断的事件,叫做异常\n\n异常处理常见手段:try catch finally throws\n\n\n\n例如:文件不存在异常\n\n```java\npackage ExceptionProcess;\n\nimport java.io.File;\nimport java.io.FileInputStream;\n\npublic class Test {\n\n public static void main(String[] args){\n File f = new File(\"D:/LOL.exe\");\n new FileInputStream(f);\n }\n \n}\n\n\n/*输出:\nException in thread \"main\" java.lang.Error: Unresolved compilation problem:\n Unhandled exception type FileNotFoundException\n\n at ExceptionProcess.Test.main(Test.java:10)\n*/\n```\n\n\n\n## 异常处理\n\n异常处理常见手段: try catch finally throws\n\n### try catch\n\n1. 将可能抛出FileNotFoundException **文件不存在异常**的代码放在try里\n\n2. 如果文件存在,就会顺序往下执行,并且不执行catch块中的代码\n\n3. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中\n\n4. e.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n\n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n }\n catch(FileNotFoundException e){\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 使用异常的父类进行catch\n\n`FileNotFoundException`是`Exception`的子类,使用Exception也可以catch住 FileNotFoundException\n\n```java\nFile f= new File(\"d:/LOL.exe\");\n\ntry{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n}\n\ncatch(Exception e){ \n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n}\n```\n\n\n\n### 多异常捕捉办法一\n\n有的时候一段代码会抛出多种异常,比如\n\n```java\nnew FileInputStream(f);\nDate d = sdf.parse(\"2016-06-03\");\n```\n\n这段代码,会抛出 文件不存在异常 FileNotFoundException 和解析异常ParseException\n解决办法之一是**分别进行catch**\n\n```java\ncatch (FileNotFoundException e) {\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n \n} catch (ParseException e) {\n System.out.println(\"日期格式解析错误\");\n e.printStackTrace();\n}\n```\n\n\n\n### 多异常捕捉办法2\n\n另一个种办法是把多个异常,放在一个catch里统一捕捉\n\n```java\ncatch (FileNotFoundException | ParseException e) {\n \n}\n```\n\n这种方式从 JDK7开始支持,好处是捕捉的代码**更紧凑**,不足之处是,一旦发生异常,**不能确定到底是哪种异常**,需要通过`instanceof` 进行判断具体的异常类型\n\n```java\nif (e instanceof FileNotFoundException)\n\tSystem.out.println(\"d:/LOL.exe不存在\");\nif (e instanceof ParseException)\n\tSystem.out.println(\"日期格式解析错误\");\n```\n\n\n\n例如:\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try {\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n Date d = sdf.parse(\"2016-06-03\");\n \n } catch (FileNotFoundException | ParseException e) {\n if (e instanceof FileNotFoundException)\n System.out.println(\"d:/LOL.exe不存在\");\n if (e instanceof ParseException)\n System.out.println(\"日期格式解析错误\");\n \n e.printStackTrace();\n }\n \n }\n}\n```\n\n### finally\n\n无论是否出现异常,finally中的代码都会被执行\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f= new File(\"d:/LOL.exe\");\n \n try{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n }\n catch(FileNotFoundException e){\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n }\n finally{\n System.out.println(\"无论文件是否存在, 都会执行的代码\");\n }\n }\n}\n```\n\n\n\n### throws\n\n> 考虑如下情况:\n>\n> 1. 主方法调用method1\n> 2. method1调用method2\n> 3. method2中打开文件\n> 4. method2中需要进行异常处理\n> 但是method2**不打算处理**,而是把这个异常通过**throws**抛出去\n> 5. 那么method1就会**接到该异常**。 处理办法也是两种,要么是try catch处理掉,要么也是**抛出去**。\n> 6. method1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n \n public static void main(String[] args) {\n method1();\n \n }\n \n private static void method1() {\n try {\n method2();\n } catch (FileNotFoundException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n \n private static void method2() throws FileNotFoundException {\n \n File f = new File(\"d:/LOL.exe\");\n \n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n \n }\n}\n```\n\n\n\n### throws与throw区别\n\nthrows与throw这两个关键字接近,不过意义不一样,有如下区别:\n\n1. throws 出现在方法**声明**上,而throw通常都出现在**方法体内**。\n\n2. - throws 表示出现异常的一种可能性,并不一定会发生这些异常;\n\n - throw则是抛出了异常,执行throw则一定抛出了某个异常对象\n\n## 异常分类\n\n- 可查异常(CheckedException)\n- 非可查异常\n - 运行时异常(RunTimeException)\n - 错误(Error)![三种分类](https://stepimagewm.how2j.cn/2412.png)\n\n\n\n### 可查异常\n\n可查异常即**必须进行处理的异常**,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException\n如果不处理,编译器,就不让你通过\n\n\n\n### 运行时异常\n\n运行时异常RuntimeException指: **不是必须进行try catch的异常**\n\n**常见运行时异常:**\n\n- 除数不能为0异常:ArithmeticException\n- 下标越界异常:ArrayIndexOutOfBoundsException\n- 空指针异常:NullPointerException\n\n在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,**即便不进行try catch,也不会有编译错误**。\n\nJava之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常**太过于普遍**,如果都需要进行捕捉,代码的可读性就会变得很糟糕。\n\n```java\npackage exception;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n //任何除数不能为0:ArithmeticException\n int k = 5/0;\n \n //下标越界异常:ArrayIndexOutOfBoundsException\n int j[] = new int[5];\n j[10] = 10;\n \n //空指针异常:NullPointerException\n String str = null;\n str.length();\n }\n}\n```\n\n### 错误Error\n\n指的是**系统级别的异常**,通常是内存用光了\n\n在**默认设置下**,一般java程序启动的时候,最大可以使用16m的内存\n\n如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出`OutOfMemoryError`,与运行时异常一样,错误也是不要求强制捕捉的\n\n```java\npackage exception;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n StringBuffer sb =new StringBuffer();\n \n for (int i = 0; i < Integer.MAX_VALUE; i++) {\n sb.append('a');\n }\n \n }\n \n}\n```\n\n\n\n## Throwable\n\n### Throwable\n\nThrowable是类,Exception和Error都继承了该类\n所以在捕捉的时候,也可以使用Throwable进行捕捉\n如图: 异常分**Error**和**Exception**\nException里又分**运行时异常**和**可查异常**\n\n![Throwable](https://stepimagewm.how2j.cn/742.png)\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try {\n new FileInputStream(f);\n //使用Throwable进行异常捕捉\n } catch (Throwable t) {\n // TODO Auto-generated catch block\n t.printStackTrace();\n }\n \n }\n}\n```\n\n## 自定义异常\n\n### 创建自定义异常\n\n一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException\n创建一个类EnemyHeroIsDeadException,并继承Exception\n提供两个构造方法\n\n1. 无参的构造方法\n2. 带参的构造方法,并调用父类的对应的构造方法\n\n```java\nclass EnemyHeroIsDeadException extends Exception{\n \n public EnemyHeroIsDeadException(){\n \n }\n public EnemyHeroIsDeadException(String msg){\n super(msg);\n }\n}\n```\n\n\n\n### 抛出自定义异常\n\n在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常\n\n1. 创建一个EnemyHeroIsDeadException实例\n\n2. 通过**throw** 抛出该异常\n\n3. 当前方法通过 **throws** 抛出该异常\n\n在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void attackHero(Hero h) throws EnemyHeroIsDeadException{\n if(h.hp == 0){\n throw new EnemyHeroIsDeadException(h.name + \" 已经挂了,不需要施放技能\" );\n }\n }\n \n public String toString(){\n return name;\n }\n \n class EnemyHeroIsDeadException extends Exception{\n \n public EnemyHeroIsDeadException(){\n \n }\n public EnemyHeroIsDeadException(String msg){\n super(msg);\n }\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n garen.hp = 616;\n \n Hero teemo = new Hero();\n teemo.name = \"提莫\";\n teemo.hp = 0;\n \n try {\n garen.attackHero(teemo);\n \n } catch (EnemyHeroIsDeadException e) {\n // TODO Auto-generated catch block\n System.out.println(\"异常的具体原因:\"+e.getMessage());\n e.printStackTrace();\n }\n \n }\n}\n```\n\n输出:\n\n![抛出自定义异常](https://stepimagewm.how2j.cn/744.png)","source":"_posts/javaSE/15-异常处理.md","raw":"---\ntitle: 15-异常处理\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: f5c95c\ndate: 2021-02-15 10:46:30\ndescription: java中的异常处理\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 异常处理\n\n## 异常定义\n\n导致程序的正常流程被中断的事件,叫做异常\n\n异常处理常见手段:try catch finally throws\n\n\n\n例如:文件不存在异常\n\n```java\npackage ExceptionProcess;\n\nimport java.io.File;\nimport java.io.FileInputStream;\n\npublic class Test {\n\n public static void main(String[] args){\n File f = new File(\"D:/LOL.exe\");\n new FileInputStream(f);\n }\n \n}\n\n\n/*输出:\nException in thread \"main\" java.lang.Error: Unresolved compilation problem:\n Unhandled exception type FileNotFoundException\n\n at ExceptionProcess.Test.main(Test.java:10)\n*/\n```\n\n\n\n## 异常处理\n\n异常处理常见手段: try catch finally throws\n\n### try catch\n\n1. 将可能抛出FileNotFoundException **文件不存在异常**的代码放在try里\n\n2. 如果文件存在,就会顺序往下执行,并且不执行catch块中的代码\n\n3. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中\n\n4. e.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n\n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n }\n catch(FileNotFoundException e){\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 使用异常的父类进行catch\n\n`FileNotFoundException`是`Exception`的子类,使用Exception也可以catch住 FileNotFoundException\n\n```java\nFile f= new File(\"d:/LOL.exe\");\n\ntry{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n}\n\ncatch(Exception e){ \n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n}\n```\n\n\n\n### 多异常捕捉办法一\n\n有的时候一段代码会抛出多种异常,比如\n\n```java\nnew FileInputStream(f);\nDate d = sdf.parse(\"2016-06-03\");\n```\n\n这段代码,会抛出 文件不存在异常 FileNotFoundException 和解析异常ParseException\n解决办法之一是**分别进行catch**\n\n```java\ncatch (FileNotFoundException e) {\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n \n} catch (ParseException e) {\n System.out.println(\"日期格式解析错误\");\n e.printStackTrace();\n}\n```\n\n\n\n### 多异常捕捉办法2\n\n另一个种办法是把多个异常,放在一个catch里统一捕捉\n\n```java\ncatch (FileNotFoundException | ParseException e) {\n \n}\n```\n\n这种方式从 JDK7开始支持,好处是捕捉的代码**更紧凑**,不足之处是,一旦发生异常,**不能确定到底是哪种异常**,需要通过`instanceof` 进行判断具体的异常类型\n\n```java\nif (e instanceof FileNotFoundException)\n\tSystem.out.println(\"d:/LOL.exe不存在\");\nif (e instanceof ParseException)\n\tSystem.out.println(\"日期格式解析错误\");\n```\n\n\n\n例如:\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try {\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n Date d = sdf.parse(\"2016-06-03\");\n \n } catch (FileNotFoundException | ParseException e) {\n if (e instanceof FileNotFoundException)\n System.out.println(\"d:/LOL.exe不存在\");\n if (e instanceof ParseException)\n System.out.println(\"日期格式解析错误\");\n \n e.printStackTrace();\n }\n \n }\n}\n```\n\n### finally\n\n无论是否出现异常,finally中的代码都会被执行\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f= new File(\"d:/LOL.exe\");\n \n try{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n }\n catch(FileNotFoundException e){\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n }\n finally{\n System.out.println(\"无论文件是否存在, 都会执行的代码\");\n }\n }\n}\n```\n\n\n\n### throws\n\n> 考虑如下情况:\n>\n> 1. 主方法调用method1\n> 2. method1调用method2\n> 3. method2中打开文件\n> 4. method2中需要进行异常处理\n> 但是method2**不打算处理**,而是把这个异常通过**throws**抛出去\n> 5. 那么method1就会**接到该异常**。 处理办法也是两种,要么是try catch处理掉,要么也是**抛出去**。\n> 6. method1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n \n public static void main(String[] args) {\n method1();\n \n }\n \n private static void method1() {\n try {\n method2();\n } catch (FileNotFoundException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n \n private static void method2() throws FileNotFoundException {\n \n File f = new File(\"d:/LOL.exe\");\n \n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n \n }\n}\n```\n\n\n\n### throws与throw区别\n\nthrows与throw这两个关键字接近,不过意义不一样,有如下区别:\n\n1. throws 出现在方法**声明**上,而throw通常都出现在**方法体内**。\n\n2. - throws 表示出现异常的一种可能性,并不一定会发生这些异常;\n\n - throw则是抛出了异常,执行throw则一定抛出了某个异常对象\n\n## 异常分类\n\n- 可查异常(CheckedException)\n- 非可查异常\n - 运行时异常(RunTimeException)\n - 错误(Error)![三种分类](https://stepimagewm.how2j.cn/2412.png)\n\n\n\n### 可查异常\n\n可查异常即**必须进行处理的异常**,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException\n如果不处理,编译器,就不让你通过\n\n\n\n### 运行时异常\n\n运行时异常RuntimeException指: **不是必须进行try catch的异常**\n\n**常见运行时异常:**\n\n- 除数不能为0异常:ArithmeticException\n- 下标越界异常:ArrayIndexOutOfBoundsException\n- 空指针异常:NullPointerException\n\n在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,**即便不进行try catch,也不会有编译错误**。\n\nJava之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常**太过于普遍**,如果都需要进行捕捉,代码的可读性就会变得很糟糕。\n\n```java\npackage exception;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n //任何除数不能为0:ArithmeticException\n int k = 5/0;\n \n //下标越界异常:ArrayIndexOutOfBoundsException\n int j[] = new int[5];\n j[10] = 10;\n \n //空指针异常:NullPointerException\n String str = null;\n str.length();\n }\n}\n```\n\n### 错误Error\n\n指的是**系统级别的异常**,通常是内存用光了\n\n在**默认设置下**,一般java程序启动的时候,最大可以使用16m的内存\n\n如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出`OutOfMemoryError`,与运行时异常一样,错误也是不要求强制捕捉的\n\n```java\npackage exception;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n StringBuffer sb =new StringBuffer();\n \n for (int i = 0; i < Integer.MAX_VALUE; i++) {\n sb.append('a');\n }\n \n }\n \n}\n```\n\n\n\n## Throwable\n\n### Throwable\n\nThrowable是类,Exception和Error都继承了该类\n所以在捕捉的时候,也可以使用Throwable进行捕捉\n如图: 异常分**Error**和**Exception**\nException里又分**运行时异常**和**可查异常**\n\n![Throwable](https://stepimagewm.how2j.cn/742.png)\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try {\n new FileInputStream(f);\n //使用Throwable进行异常捕捉\n } catch (Throwable t) {\n // TODO Auto-generated catch block\n t.printStackTrace();\n }\n \n }\n}\n```\n\n## 自定义异常\n\n### 创建自定义异常\n\n一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException\n创建一个类EnemyHeroIsDeadException,并继承Exception\n提供两个构造方法\n\n1. 无参的构造方法\n2. 带参的构造方法,并调用父类的对应的构造方法\n\n```java\nclass EnemyHeroIsDeadException extends Exception{\n \n public EnemyHeroIsDeadException(){\n \n }\n public EnemyHeroIsDeadException(String msg){\n super(msg);\n }\n}\n```\n\n\n\n### 抛出自定义异常\n\n在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常\n\n1. 创建一个EnemyHeroIsDeadException实例\n\n2. 通过**throw** 抛出该异常\n\n3. 当前方法通过 **throws** 抛出该异常\n\n在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void attackHero(Hero h) throws EnemyHeroIsDeadException{\n if(h.hp == 0){\n throw new EnemyHeroIsDeadException(h.name + \" 已经挂了,不需要施放技能\" );\n }\n }\n \n public String toString(){\n return name;\n }\n \n class EnemyHeroIsDeadException extends Exception{\n \n public EnemyHeroIsDeadException(){\n \n }\n public EnemyHeroIsDeadException(String msg){\n super(msg);\n }\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n garen.hp = 616;\n \n Hero teemo = new Hero();\n teemo.name = \"提莫\";\n teemo.hp = 0;\n \n try {\n garen.attackHero(teemo);\n \n } catch (EnemyHeroIsDeadException e) {\n // TODO Auto-generated catch block\n System.out.println(\"异常的具体原因:\"+e.getMessage());\n e.printStackTrace();\n }\n \n }\n}\n```\n\n输出:\n\n![抛出自定义异常](https://stepimagewm.how2j.cn/744.png)","slug":"javaSE/15-异常处理","published":1,"updated":"2021-08-29T15:31:38.885Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qn005takveawfj22pb","content":"异常处理 异常定义 导致程序的正常流程被中断的事件,叫做异常
\n异常处理常见手段:try catch finally throws
\n例如:文件不存在异常
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package ExceptionProcess;import java.io.File;import java.io.FileInputStream;public class Test { public static void main (String[] args) { File f = new File("D:/LOL.exe" ); new FileInputStream(f); } }
\n异常处理 异常处理常见手段: try catch finally throws
\ntry catch \n将可能抛出FileNotFoundException 文件不存在异常 的代码放在try里
\n \n如果文件存在,就会顺序往下执行,并且不执行catch块中的代码
\n \n如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中
\n \ne.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常
\n \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package exception; import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException; public class TestException { public static void main (String[] args) { File f = new File("d:/LOL.exe" ); try { System.out.println("试图打开 d:/LOL.exe" ); new FileInputStream(f); System.out.println("成功打开" ); } catch (FileNotFoundException e){ System.out.println("d:/LOL.exe不存在" ); e.printStackTrace(); } } }
\n使用异常的父类进行catch FileNotFoundException
是Exception
的子类,使用Exception也可以catch住 FileNotFoundException
\n1 2 3 4 5 6 7 8 9 10 11 12 File f= new File("d:/LOL.exe" ); try { System.out.println("试图打开 d:/LOL.exe" ); new FileInputStream(f); System.out.println("成功打开" ); } catch (Exception e){ System.out.println("d:/LOL.exe不存在" ); e.printStackTrace(); }
\n多异常捕捉办法一 有的时候一段代码会抛出多种异常,比如
\n1 2 new FileInputStream(f);Date d = sdf.parse("2016-06-03" );
\n这段代码,会抛出 文件不存在异常 FileNotFoundException 和解析异常ParseException 解决办法之一是分别进行catch
\n1 2 3 4 5 6 7 8 catch (FileNotFoundException e) { System.out.println("d:/LOL.exe不存在" ); e.printStackTrace(); } catch (ParseException e) { System.out.println("日期格式解析错误" ); e.printStackTrace(); }
\n多异常捕捉办法2 另一个种办法是把多个异常,放在一个catch里统一捕捉
\n1 2 3 catch (FileNotFoundException | ParseException e) { }
\n这种方式从 JDK7开始支持,好处是捕捉的代码更紧凑 ,不足之处是,一旦发生异常,不能确定到底是哪种异常 ,需要通过instanceof
进行判断具体的异常类型
\n1 2 3 4 if (e instanceof FileNotFoundException)\tSystem.out.println("d:/LOL.exe不存在" ); if (e instanceof ParseException)\tSystem.out.println("日期格式解析错误" );
\n例如:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package exception; import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date; public class TestException { public static void main (String[] args) { File f = new File("d:/LOL.exe" ); try { System.out.println("试图打开 d:/LOL.exe" ); new FileInputStream(f); System.out.println("成功打开" ); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd" ); Date d = sdf.parse("2016-06-03" ); } catch (FileNotFoundException | ParseException e) { if (e instanceof FileNotFoundException) System.out.println("d:/LOL.exe不存在" ); if (e instanceof ParseException) System.out.println("日期格式解析错误" ); e.printStackTrace(); } } }
\nfinally 无论是否出现异常,finally中的代码都会被执行
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package exception; import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException; public class TestException { public static void main (String[] args) { File f= new File("d:/LOL.exe" ); try { System.out.println("试图打开 d:/LOL.exe" ); new FileInputStream(f); System.out.println("成功打开" ); } catch (FileNotFoundException e){ System.out.println("d:/LOL.exe不存在" ); e.printStackTrace(); } finally { System.out.println("无论文件是否存在, 都会执行的代码" ); } } }
\nthrows \n考虑如下情况:
\n\n主方法调用method1 \nmethod1调用method2 \nmethod2中打开文件 \nmethod2中需要进行异常处理 但是method2不打算处理 ,而是把这个异常通过throws 抛出去 \n那么method1就会接到该异常 。 处理办法也是两种,要么是try catch处理掉,要么也是抛出去 。 \nmethod1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了 \n \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package exception; import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException; public class TestException { public static void main (String[] args) { method1(); } private static void method1 () { try { method2(); } catch (FileNotFoundException e) { e.printStackTrace(); } } private static void method2 () throws FileNotFoundException { File f = new File("d:/LOL.exe" ); System.out.println("试图打开 d:/LOL.exe" ); new FileInputStream(f); System.out.println("成功打开" ); } }
\nthrows与throw区别 throws与throw这两个关键字接近,不过意义不一样,有如下区别:
\n\nthrows 出现在方法声明 上,而throw通常都出现在方法体内 。
\n \n\n \n \n异常分类 \n可查异常(CheckedException) \n非可查异常\n运行时异常(RunTimeException) \n错误(Error) \n \n \n \n可查异常 可查异常即必须进行处理的异常 ,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException 如果不处理,编译器,就不让你通过
\n运行时异常 运行时异常RuntimeException指: 不是必须进行try catch的异常
\n常见运行时异常:
\n\n除数不能为0异常:ArithmeticException \n下标越界异常:ArrayIndexOutOfBoundsException \n空指针异常:NullPointerException \n \n在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,即便不进行try catch,也不会有编译错误 。
\nJava之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常太过于普遍 ,如果都需要进行捕捉,代码的可读性就会变得很糟糕。
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package exception; public class TestException { public static void main (String[] args) { int k = 5 /0 ; int j[] = new int [5 ]; j[10 ] = 10 ; String str = null ; str.length(); } }
\n错误Error 指的是系统级别的异常 ,通常是内存用光了
\n在默认设置下 ,一般java程序启动的时候,最大可以使用16m的内存
\n如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出OutOfMemoryError
,与运行时异常一样,错误也是不要求强制捕捉的
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package exception; public class TestException { public static void main (String[] args) { StringBuffer sb =new StringBuffer(); for (int i = 0 ; i < Integer.MAX_VALUE; i++) { sb.append('a' ); } } }
\nThrowable Throwable Throwable是类,Exception和Error都继承了该类 所以在捕捉的时候,也可以使用Throwable进行捕捉 如图: 异常分Error 和Exception Exception里又分运行时异常 和可查异常
\n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package exception; import java.io.File;import java.io.FileInputStream; public class TestException { public static void main (String[] args) { File f = new File("d:/LOL.exe" ); try { new FileInputStream(f); } catch (Throwable t) { t.printStackTrace(); } } }
\n自定义异常 创建自定义异常 一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException 创建一个类EnemyHeroIsDeadException,并继承Exception 提供两个构造方法
\n\n无参的构造方法 \n带参的构造方法,并调用父类的对应的构造方法 \n \n1 2 3 4 5 6 7 8 9 class EnemyHeroIsDeadException extends Exception { public EnemyHeroIsDeadException () { } public EnemyHeroIsDeadException (String msg) { super (msg); } }
\n抛出自定义异常 在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常
\n\n创建一个EnemyHeroIsDeadException实例
\n \n通过throw 抛出该异常
\n \n当前方法通过 throws 抛出该异常
\n \n \n在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 package charactor; public class Hero { public String name; protected float hp; public void attackHero (Hero h) throws EnemyHeroIsDeadException { if (h.hp == 0 ){ throw new EnemyHeroIsDeadException(h.name + " 已经挂了,不需要施放技能" ); } } public String toString () { return name; } class EnemyHeroIsDeadException extends Exception { public EnemyHeroIsDeadException () { } public EnemyHeroIsDeadException (String msg) { super (msg); } } public static void main (String[] args) { Hero garen = new Hero(); garen.name = "盖伦" ; garen.hp = 616 ; Hero teemo = new Hero(); teemo.name = "提莫" ; teemo.hp = 0 ; try { garen.attackHero(teemo); } catch (EnemyHeroIsDeadException e) { System.out.println("异常的具体原因:" +e.getMessage()); e.printStackTrace(); } } }
\n输出:
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"异常处理 异常定义 导致程序的正常流程被中断的事件,叫做异常
\n异常处理常见手段:try catch finally throws
\n例如:文件不存在异常
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package ExceptionProcess;import java.io.File;import java.io.FileInputStream;public class Test { public static void main (String[] args) { File f = new File("D:/LOL.exe" ); new FileInputStream(f); } }
\n异常处理 异常处理常见手段: try catch finally throws
\ntry catch \n将可能抛出FileNotFoundException 文件不存在异常 的代码放在try里
\n \n如果文件存在,就会顺序往下执行,并且不执行catch块中的代码
\n \n如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中
\n \ne.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常
\n \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package exception; import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException; public class TestException { public static void main (String[] args) { File f = new File("d:/LOL.exe" ); try { System.out.println("试图打开 d:/LOL.exe" ); new FileInputStream(f); System.out.println("成功打开" ); } catch (FileNotFoundException e){ System.out.println("d:/LOL.exe不存在" ); e.printStackTrace(); } } }
\n使用异常的父类进行catch FileNotFoundException
是Exception
的子类,使用Exception也可以catch住 FileNotFoundException
\n1 2 3 4 5 6 7 8 9 10 11 12 File f= new File("d:/LOL.exe" ); try { System.out.println("试图打开 d:/LOL.exe" ); new FileInputStream(f); System.out.println("成功打开" ); } catch (Exception e){ System.out.println("d:/LOL.exe不存在" ); e.printStackTrace(); }
\n多异常捕捉办法一 有的时候一段代码会抛出多种异常,比如
\n1 2 new FileInputStream(f);Date d = sdf.parse("2016-06-03" );
\n这段代码,会抛出 文件不存在异常 FileNotFoundException 和解析异常ParseException 解决办法之一是分别进行catch
\n1 2 3 4 5 6 7 8 catch (FileNotFoundException e) { System.out.println("d:/LOL.exe不存在" ); e.printStackTrace(); } catch (ParseException e) { System.out.println("日期格式解析错误" ); e.printStackTrace(); }
\n多异常捕捉办法2 另一个种办法是把多个异常,放在一个catch里统一捕捉
\n1 2 3 catch (FileNotFoundException | ParseException e) { }
\n这种方式从 JDK7开始支持,好处是捕捉的代码更紧凑 ,不足之处是,一旦发生异常,不能确定到底是哪种异常 ,需要通过instanceof
进行判断具体的异常类型
\n1 2 3 4 if (e instanceof FileNotFoundException)\tSystem.out.println("d:/LOL.exe不存在" ); if (e instanceof ParseException)\tSystem.out.println("日期格式解析错误" );
\n例如:
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package exception; import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date; public class TestException { public static void main (String[] args) { File f = new File("d:/LOL.exe" ); try { System.out.println("试图打开 d:/LOL.exe" ); new FileInputStream(f); System.out.println("成功打开" ); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd" ); Date d = sdf.parse("2016-06-03" ); } catch (FileNotFoundException | ParseException e) { if (e instanceof FileNotFoundException) System.out.println("d:/LOL.exe不存在" ); if (e instanceof ParseException) System.out.println("日期格式解析错误" ); e.printStackTrace(); } } }
\nfinally 无论是否出现异常,finally中的代码都会被执行
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package exception; import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException; public class TestException { public static void main (String[] args) { File f= new File("d:/LOL.exe" ); try { System.out.println("试图打开 d:/LOL.exe" ); new FileInputStream(f); System.out.println("成功打开" ); } catch (FileNotFoundException e){ System.out.println("d:/LOL.exe不存在" ); e.printStackTrace(); } finally { System.out.println("无论文件是否存在, 都会执行的代码" ); } } }
\nthrows \n考虑如下情况:
\n\n主方法调用method1 \nmethod1调用method2 \nmethod2中打开文件 \nmethod2中需要进行异常处理 但是method2不打算处理 ,而是把这个异常通过throws 抛出去 \n那么method1就会接到该异常 。 处理办法也是两种,要么是try catch处理掉,要么也是抛出去 。 \nmethod1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了 \n \n \n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package exception; import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException; public class TestException { public static void main (String[] args) { method1(); } private static void method1 () { try { method2(); } catch (FileNotFoundException e) { e.printStackTrace(); } } private static void method2 () throws FileNotFoundException { File f = new File("d:/LOL.exe" ); System.out.println("试图打开 d:/LOL.exe" ); new FileInputStream(f); System.out.println("成功打开" ); } }
\nthrows与throw区别 throws与throw这两个关键字接近,不过意义不一样,有如下区别:
\n\nthrows 出现在方法声明 上,而throw通常都出现在方法体内 。
\n \n\n \n \n异常分类 \n可查异常(CheckedException) \n非可查异常\n运行时异常(RunTimeException) \n错误(Error) \n \n \n \n可查异常 可查异常即必须进行处理的异常 ,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException 如果不处理,编译器,就不让你通过
\n运行时异常 运行时异常RuntimeException指: 不是必须进行try catch的异常
\n常见运行时异常:
\n\n除数不能为0异常:ArithmeticException \n下标越界异常:ArrayIndexOutOfBoundsException \n空指针异常:NullPointerException \n \n在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,即便不进行try catch,也不会有编译错误 。
\nJava之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常太过于普遍 ,如果都需要进行捕捉,代码的可读性就会变得很糟糕。
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package exception; public class TestException { public static void main (String[] args) { int k = 5 /0 ; int j[] = new int [5 ]; j[10 ] = 10 ; String str = null ; str.length(); } }
\n错误Error 指的是系统级别的异常 ,通常是内存用光了
\n在默认设置下 ,一般java程序启动的时候,最大可以使用16m的内存
\n如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出OutOfMemoryError
,与运行时异常一样,错误也是不要求强制捕捉的
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package exception; public class TestException { public static void main (String[] args) { StringBuffer sb =new StringBuffer(); for (int i = 0 ; i < Integer.MAX_VALUE; i++) { sb.append('a' ); } } }
\nThrowable Throwable Throwable是类,Exception和Error都继承了该类 所以在捕捉的时候,也可以使用Throwable进行捕捉 如图: 异常分Error 和Exception Exception里又分运行时异常 和可查异常
\n
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package exception; import java.io.File;import java.io.FileInputStream; public class TestException { public static void main (String[] args) { File f = new File("d:/LOL.exe" ); try { new FileInputStream(f); } catch (Throwable t) { t.printStackTrace(); } } }
\n自定义异常 创建自定义异常 一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException 创建一个类EnemyHeroIsDeadException,并继承Exception 提供两个构造方法
\n\n无参的构造方法 \n带参的构造方法,并调用父类的对应的构造方法 \n \n1 2 3 4 5 6 7 8 9 class EnemyHeroIsDeadException extends Exception { public EnemyHeroIsDeadException () { } public EnemyHeroIsDeadException (String msg) { super (msg); } }
\n抛出自定义异常 在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常
\n\n创建一个EnemyHeroIsDeadException实例
\n \n通过throw 抛出该异常
\n \n当前方法通过 throws 抛出该异常
\n \n \n在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 package charactor; public class Hero { public String name; protected float hp; public void attackHero (Hero h) throws EnemyHeroIsDeadException { if (h.hp == 0 ){ throw new EnemyHeroIsDeadException(h.name + " 已经挂了,不需要施放技能" ); } } public String toString () { return name; } class EnemyHeroIsDeadException extends Exception { public EnemyHeroIsDeadException () { } public EnemyHeroIsDeadException (String msg) { super (msg); } } public static void main (String[] args) { Hero garen = new Hero(); garen.name = "盖伦" ; garen.hp = 616 ; Hero teemo = new Hero(); teemo.name = "提莫" ; teemo.hp = 0 ; try { garen.attackHero(teemo); } catch (EnemyHeroIsDeadException e) { System.out.println("异常的具体原因:" +e.getMessage()); e.printStackTrace(); } } }
\n输出:
\n
\n"},{"title":"12-字符串操作","abbrlink":"5e64b19a","date":"2021-02-13T11:20:44.000Z","description":"java的字符串初始化、基本操作函数的使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 12 java字符串\n\nJava用`String` 和 `StringBuffer`类来处理字符串,在Java中,每个字符串都是一个对象。\n\n- String:主要用于内容不可改变的字符串对象,即字符串一旦创建就不能再改变了(只读)\n- StringBuffer:用于串内容可以改变的字符串对象(可读、可写)\n\n## 12.1字符串初始化\n\n以下用3种方法创建并初始化字符串对象\n\n```java\nString s1 = new String( \"hello\");\n\nString s2 = \"hello\";\n\nchar[] ch = {'h', 'e', 'l', 'l', 'o'};\nString s3 = new String(ch);\n\n```\n\n## 12.2 字符串连接\n\n字符串连接用 `+` 号\n\n```java\nString str1 = \"abc\";\nString str2 = \"123\";\n\nString str3 = str1 + str2; //字符串连接\n```\n\n\n\n## 12.3 字符串比较\n\n1. `==`只检查两个串**引用**是否相同,不比较串值;\n\n > 注意:\n >\n > 一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象\n > 所以在如下代码中:\n >\n > 第6行会创建了一个新的字符串\"the light\",\n >\n > 但是在第7行,编译器发现已经存在现成的\"the light\",那么就直接拿来使用,而没有进行重复创建\n\n ```java\n package character;\n \n public class TestString {\n \n public static void main(String[] args) {\n String str1 = \"the light\";\n String str3 = \"the light\";\n System.out.println( str1 == str3); //true\n }\n \n }\n ```\n\n \n\n2. `equals()`或`equalsIgnoreCase()`方法:比较串值是否相同\n\n `equalsIgnoreCase()`表示忽略大小写的影响\n\n \n\n3. `compareTo()`方法:串大小比较(字典序)\n\n\n\n```java\nstring str1 = \"abc\"; \nstring str2 = \"a\";\n\nstr2 += \"bc\"; \nif(str1 == str2){ }; //false\nif(str1.equals(str2)){ }; \t\t//true\n\n```\n\n\n\n## 12.4 字符串拆分\n\n`split(regex)`方法根据匹配确定的分隔符 `regex` 将串分解成子串,所有子串存储在字符串数组(每个成员是一个子串)中;\n\n```java\npublic class HelloWorld {\n public static void main(String[] args){\n\n String s = \"The-cat-sat-on-the-mat.\";\n \n String[] words = s.split(\"-\"); //从-处开始分割\n \n for(int i=0; i 注意:\n >\n > 一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象\n > 所以在如下代码中:\n >\n > 第6行会创建了一个新的字符串\"the light\",\n >\n > 但是在第7行,编译器发现已经存在现成的\"the light\",那么就直接拿来使用,而没有进行重复创建\n\n ```java\n package character;\n \n public class TestString {\n \n public static void main(String[] args) {\n String str1 = \"the light\";\n String str3 = \"the light\";\n System.out.println( str1 == str3); //true\n }\n \n }\n ```\n\n \n\n2. `equals()`或`equalsIgnoreCase()`方法:比较串值是否相同\n\n `equalsIgnoreCase()`表示忽略大小写的影响\n\n \n\n3. `compareTo()`方法:串大小比较(字典序)\n\n\n\n```java\nstring str1 = \"abc\"; \nstring str2 = \"a\";\n\nstr2 += \"bc\"; \nif(str1 == str2){ }; //false\nif(str1.equals(str2)){ }; \t\t//true\n\n```\n\n\n\n## 12.4 字符串拆分\n\n`split(regex)`方法根据匹配确定的分隔符 `regex` 将串分解成子串,所有子串存储在字符串数组(每个成员是一个子串)中;\n\n```java\npublic class HelloWorld {\n public static void main(String[] args){\n\n String s = \"The-cat-sat-on-the-mat.\";\n \n String[] words = s.split(\"-\"); //从-处开始分割\n \n for(int i=0; i12 java字符串Java用String
和 StringBuffer
类来处理字符串,在Java中,每个字符串都是一个对象。
\n\nString:主要用于内容不可改变的字符串对象,即字符串一旦创建就不能再改变了(只读) \nStringBuffer:用于串内容可以改变的字符串对象(可读、可写) \n \n12.1字符串初始化 以下用3种方法创建并初始化字符串对象
\n1 2 3 4 5 6 7 String s1 = new String( "hello" ); String s2 = "hello" ; char [] ch = {'h' , 'e' , 'l' , 'l' , 'o' };String s3 = new String(ch);
\n12.2 字符串连接 字符串连接用 +
号
\n1 2 3 4 String str1 = "abc" ; String str2 = "123" ; String str3 = str1 + str2;
\n12.3 字符串比较 \n==
只检查两个串引用 是否相同,不比较串值;
\n\n注意:
\n一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象 所以在如下代码中:
\n第6行会创建了一个新的字符串”the light”,
\n但是在第7行,编译器发现已经存在现成的”the light”,那么就直接拿来使用,而没有进行重复创建
\n \n1 2 3 4 5 6 7 8 9 10 11 package character; public class TestString { public static void main (String[] args) { String str1 = "the light" ; String str3 = "the light" ; System.out.println( str1 == str3); } }
\n \n \n\nequals()
或equalsIgnoreCase()
方法:比较串值是否相同
\nequalsIgnoreCase()
表示忽略大小写的影响
\n \n \n\ncompareTo()
方法:串大小比较(字典序) \n \n1 2 3 4 5 6 7 string str1 = "abc" ; string str2 = "a" ; str2 += "bc" ; if (str1 == str2){ }; if (str1.equals(str2)){ }; \t\t
\n12.4 字符串拆分 split(regex)
方法根据匹配确定的分隔符 regex
将串分解成子串,所有子串存储在字符串数组(每个成员是一个子串)中;
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class HelloWorld { public static void main (String[] args) { String s = "The-cat-sat-on-the-mat." ; String[] words = s.split("-" ); for (int i=0 ; i<words.length; i++) { System.out.println(words[i]); } } }
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class HelloWorld { public static void main (String[] args) { String s = "12+34+567" ; String[] ss = s.split("[+]" ); int sum = 0 ; for (int i=0 ;i<ss.length;i++) { sum += Integer.parseInt(ss[i]); } System.out.println(sum); } }
\n12.5操作字符串常用函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int length () ;\t\t\tchar charAt (int index) ; boolean equals (String s) ; int indexOf (String str) ; String concat (String str) ; String substring (int begin ,int end) ; String toLowerCase () ; String toUpperCase () ; String trim () ; char [] toCharArray(); String replace (char old, char new) ; String replaceAll (String old, String new) String replaceFirst (String old, String new)
\n12.6 StringBuffer类 StringBuffer类创建的串其内容是可以修改的,其占用的空间能自动增长:
\n1 String s = "a" + 4 + "b123" + false ; \t
\n 为提高效率 被编译成下列等价 代码:
\n1 2 StringBuffer s = new StringBuffer(); s.append("a" ).append(4 ).append("b123" ).append(false ).toString();
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"12 java字符串 Java用String
和 StringBuffer
类来处理字符串,在Java中,每个字符串都是一个对象。
\n\nString:主要用于内容不可改变的字符串对象,即字符串一旦创建就不能再改变了(只读) \nStringBuffer:用于串内容可以改变的字符串对象(可读、可写) \n \n12.1字符串初始化 以下用3种方法创建并初始化字符串对象
\n1 2 3 4 5 6 7 String s1 = new String( "hello" ); String s2 = "hello" ; char [] ch = {'h' , 'e' , 'l' , 'l' , 'o' };String s3 = new String(ch);
\n12.2 字符串连接 字符串连接用 +
号
\n1 2 3 4 String str1 = "abc" ; String str2 = "123" ; String str3 = str1 + str2;
\n12.3 字符串比较 \n==
只检查两个串引用 是否相同,不比较串值;
\n\n注意:
\n一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象 所以在如下代码中:
\n第6行会创建了一个新的字符串”the light”,
\n但是在第7行,编译器发现已经存在现成的”the light”,那么就直接拿来使用,而没有进行重复创建
\n \n1 2 3 4 5 6 7 8 9 10 11 package character; public class TestString { public static void main (String[] args) { String str1 = "the light" ; String str3 = "the light" ; System.out.println( str1 == str3); } }
\n \n \n\nequals()
或equalsIgnoreCase()
方法:比较串值是否相同
\nequalsIgnoreCase()
表示忽略大小写的影响
\n \n \n\ncompareTo()
方法:串大小比较(字典序) \n \n1 2 3 4 5 6 7 string str1 = "abc" ; string str2 = "a" ; str2 += "bc" ; if (str1 == str2){ }; if (str1.equals(str2)){ }; \t\t
\n12.4 字符串拆分 split(regex)
方法根据匹配确定的分隔符 regex
将串分解成子串,所有子串存储在字符串数组(每个成员是一个子串)中;
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class HelloWorld { public static void main (String[] args) { String s = "The-cat-sat-on-the-mat." ; String[] words = s.split("-" ); for (int i=0 ; i<words.length; i++) { System.out.println(words[i]); } } }
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class HelloWorld { public static void main (String[] args) { String s = "12+34+567" ; String[] ss = s.split("[+]" ); int sum = 0 ; for (int i=0 ;i<ss.length;i++) { sum += Integer.parseInt(ss[i]); } System.out.println(sum); } }
\n12.5操作字符串常用函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int length () ;\t\t\tchar charAt (int index) ; boolean equals (String s) ; int indexOf (String str) ; String concat (String str) ; String substring (int begin ,int end) ; String toLowerCase () ; String toUpperCase () ; String trim () ; char [] toCharArray(); String replace (char old, char new) ; String replaceAll (String old, String new) String replaceFirst (String old, String new)
\n12.6 StringBuffer类 StringBuffer类创建的串其内容是可以修改的,其占用的空间能自动增长:
\n1 String s = "a" + 4 + "b123" + false ; \t
\n 为提高效率 被编译成下列等价 代码:
\n1 2 StringBuffer s = new StringBuffer(); s.append("a" ).append(4 ).append("b123" ).append(false ).toString();
\n"},{"title":"13-日期与日历","abbrlink":"b3cec184","date":"2021-02-14T02:15:21.000Z","description":"java的日期与日历数据类型,如何使用及与字符串的转换","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 日期\n\n在Java中使用日期,需导入 `java.util.Date` 类\n\n```java\nimport java.util.Date;\n```\n\n\n\n## 创建一个日期对象\n\n- `Date date = new Date(); ` \n\n```java\nDate now = new Date(); \n\n//不加参数表示当前时间\n//加了参数n表示从1970-01-01 8:00:00开始经历了n毫秒\n```\n\n- 日期对象转字符串对象:\n\n```java\nString s = now.toString()\n```\n\n\n\n## getTime()方法\n\n`getTime()`是Date()对象的一个方法,返回类型 `long`,用于获取从1970-01-01 8:00:00开始到现在经历的**毫秒**数\n\n另外,`Date().getTime()` 和 `System.currentTimeMillis()` 是一样的\n\n```java\npackage date;\n \n//\nimport java.util.Date;\n \npublic class TestDate {\n \n public static void main(String[] args) {\n Date now= new Date();\n \n //当前日期的毫秒数\n System.out.println(\"Date.getTime() \\t\\t\\t返回值: \"+now.getTime());\n \n //通过System.currentTimeMillis()获取当前日期的毫秒数\n System.out.println(\"System.currentTimeMillis() \\t返回值: \"+System.currentTimeMillis());\n \n }\n}\n\n/*输出:\n\nDate.getTime() 返回值: 1611591854927\nSystem.currentTimeMillis() 返回值: 1611591854927\n\n*/\n\n```\n\n\n\n## 格式化日期并转为字符串\n\n格式化日期:\n\n需要用到 `SimpleDateFormat` 类\n\n```java\nimport java.text.SimpleDateFormat;\n```\n\n1. 指定格式\n\n ```java\n SimpleDateFormat sdf =new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss SSS\" );\n \n //y 代表年\n //M 代表月\n //d 代表日\n //H 代表24进制的小时\n //h 代表12进制的小时\n //m 代表分钟\n //s 代表秒\n //S 代表毫秒\n \n // 以上7种可以只选其中一种或多种,根据自己需要来,如:\n SimpleDateFormat sdf1 = new SimpleDateFormat(\"yyyy-MM-dd\" );\n SimpleDateFormat sdf2 = new SimpleDateFormat(\"MM/dd\" );\n ```\n\n2. 利用sdf对象的 `format`方法对日期 `Date()`进行格式化,并返回格式化后的`字符串`\n\n ```java\n Date d = new Date();\n String str = sdf.format(d); //这样日期就按格式转为了字符串\n ```\n\n\n\n## 字符串转日期\n\n三个步骤 :\n\n1. 指定字符串格式:\n\n ```java\n SimpleDateFormat sdf =new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\" );\n \n //SimpleDateFormat sdf1 =new SimpleDateFormat(\"yyyy/MM/dd\" );\n ```\n\n2. 创建字符串,注意格式要与 sdf 一致\n\n ```java\n String str = \"2019-1-15 16:02:27\";\n \n //String str1 = \"2019-1-15\";\n ```\n\n3. 创建日期对象,用 sdf的 `parse(str)`方法来把字符串转为日期对象\n\n ```java\n Date d = sdf.parse(str);\n ```\n\n\n\n## 日历 Calendar\n\n使用前需要导入:\n\n```java\nimport java.util.Calendar;\n```\n\n\n\n1. 获取日历对象 `Calendar.getInstance();`\n\n ```java\n Calendar cl = Calendar.getInstance();\n ```\n\n2. 通过日历对象得到日期对象: `getTime()`\n\n ```java\n Date d = cl.getTime();\n ```\n\n3. 日期设置:把 `cl` 这个日历,调成日期(时间原点) —— 1970.1.1 08:00:00,用 `setTime()`\n\n ```java\n Date d2 = new Date(0);\n cl.setTime(d2);\n ```\n\n## 翻日历\n\n> add方法,在原日期上**增加(add)** 年/月/日\n> set方法,**直接设置(set)** 年/月/日\n\n1. 创建日历对象\n\n ```java\n Calendar c = Calendar.getInstance();\n ```\n\n2. 获取当前日期\n\n ```java\n Date now = c.getTime();\n ```\n\n3. 把日历改成下个月的今天\n\n ```java\n c.setTime(now); //表示把日历改为当前时间\n \n c.add(Calendar.MONTH, 1); \n //在当前时间now的基础上修改日历,这里修改了月份,下个月 = 本月 + 1\n ```\n\n4. 把日历改成去年的今天\n\n ```java\n c.setTime(now);\n c.add(Calendar.YEAR, -1); //负数表示以前的,去年 = 今年 - 1\n ```\n\n5. 把日历直接**设置**成上个月的第三天\n\n ```java\n c.setTime(now);\n c.add(Calendar.MONTH, -1); //先改月份\n c.set(Calendar.DATE, 3);\t\t//再改日数,注意用set\n \n //如果是今天后的第三天,则用:\n //c.add(Calendar.DATE, 3)\n ```\n\n ","source":"_posts/javaSE/13-日期.md","raw":"---\ntitle: 13-日期与日历\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: b3cec184\ndate: 2021-02-14 10:15:21\ndescription: java的日期与日历数据类型,如何使用及与字符串的转换\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 日期\n\n在Java中使用日期,需导入 `java.util.Date` 类\n\n```java\nimport java.util.Date;\n```\n\n\n\n## 创建一个日期对象\n\n- `Date date = new Date(); ` \n\n```java\nDate now = new Date(); \n\n//不加参数表示当前时间\n//加了参数n表示从1970-01-01 8:00:00开始经历了n毫秒\n```\n\n- 日期对象转字符串对象:\n\n```java\nString s = now.toString()\n```\n\n\n\n## getTime()方法\n\n`getTime()`是Date()对象的一个方法,返回类型 `long`,用于获取从1970-01-01 8:00:00开始到现在经历的**毫秒**数\n\n另外,`Date().getTime()` 和 `System.currentTimeMillis()` 是一样的\n\n```java\npackage date;\n \n//\nimport java.util.Date;\n \npublic class TestDate {\n \n public static void main(String[] args) {\n Date now= new Date();\n \n //当前日期的毫秒数\n System.out.println(\"Date.getTime() \\t\\t\\t返回值: \"+now.getTime());\n \n //通过System.currentTimeMillis()获取当前日期的毫秒数\n System.out.println(\"System.currentTimeMillis() \\t返回值: \"+System.currentTimeMillis());\n \n }\n}\n\n/*输出:\n\nDate.getTime() 返回值: 1611591854927\nSystem.currentTimeMillis() 返回值: 1611591854927\n\n*/\n\n```\n\n\n\n## 格式化日期并转为字符串\n\n格式化日期:\n\n需要用到 `SimpleDateFormat` 类\n\n```java\nimport java.text.SimpleDateFormat;\n```\n\n1. 指定格式\n\n ```java\n SimpleDateFormat sdf =new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss SSS\" );\n \n //y 代表年\n //M 代表月\n //d 代表日\n //H 代表24进制的小时\n //h 代表12进制的小时\n //m 代表分钟\n //s 代表秒\n //S 代表毫秒\n \n // 以上7种可以只选其中一种或多种,根据自己需要来,如:\n SimpleDateFormat sdf1 = new SimpleDateFormat(\"yyyy-MM-dd\" );\n SimpleDateFormat sdf2 = new SimpleDateFormat(\"MM/dd\" );\n ```\n\n2. 利用sdf对象的 `format`方法对日期 `Date()`进行格式化,并返回格式化后的`字符串`\n\n ```java\n Date d = new Date();\n String str = sdf.format(d); //这样日期就按格式转为了字符串\n ```\n\n\n\n## 字符串转日期\n\n三个步骤 :\n\n1. 指定字符串格式:\n\n ```java\n SimpleDateFormat sdf =new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\" );\n \n //SimpleDateFormat sdf1 =new SimpleDateFormat(\"yyyy/MM/dd\" );\n ```\n\n2. 创建字符串,注意格式要与 sdf 一致\n\n ```java\n String str = \"2019-1-15 16:02:27\";\n \n //String str1 = \"2019-1-15\";\n ```\n\n3. 创建日期对象,用 sdf的 `parse(str)`方法来把字符串转为日期对象\n\n ```java\n Date d = sdf.parse(str);\n ```\n\n\n\n## 日历 Calendar\n\n使用前需要导入:\n\n```java\nimport java.util.Calendar;\n```\n\n\n\n1. 获取日历对象 `Calendar.getInstance();`\n\n ```java\n Calendar cl = Calendar.getInstance();\n ```\n\n2. 通过日历对象得到日期对象: `getTime()`\n\n ```java\n Date d = cl.getTime();\n ```\n\n3. 日期设置:把 `cl` 这个日历,调成日期(时间原点) —— 1970.1.1 08:00:00,用 `setTime()`\n\n ```java\n Date d2 = new Date(0);\n cl.setTime(d2);\n ```\n\n## 翻日历\n\n> add方法,在原日期上**增加(add)** 年/月/日\n> set方法,**直接设置(set)** 年/月/日\n\n1. 创建日历对象\n\n ```java\n Calendar c = Calendar.getInstance();\n ```\n\n2. 获取当前日期\n\n ```java\n Date now = c.getTime();\n ```\n\n3. 把日历改成下个月的今天\n\n ```java\n c.setTime(now); //表示把日历改为当前时间\n \n c.add(Calendar.MONTH, 1); \n //在当前时间now的基础上修改日历,这里修改了月份,下个月 = 本月 + 1\n ```\n\n4. 把日历改成去年的今天\n\n ```java\n c.setTime(now);\n c.add(Calendar.YEAR, -1); //负数表示以前的,去年 = 今年 - 1\n ```\n\n5. 把日历直接**设置**成上个月的第三天\n\n ```java\n c.setTime(now);\n c.add(Calendar.MONTH, -1); //先改月份\n c.set(Calendar.DATE, 3);\t\t//再改日数,注意用set\n \n //如果是今天后的第三天,则用:\n //c.add(Calendar.DATE, 3)\n ```\n\n ","slug":"javaSE/13-日期","published":1,"updated":"2021-08-29T15:31:31.919Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qq005zakve2q775ly3","content":"日期 在Java中使用日期,需导入 java.util.Date
类
\n \n创建一个日期对象 \nDate date = new Date();
\n \n1 2 3 4 Date now = new Date();
\n\n1 String s = now.toString()
\ngetTime()方法 getTime()
是Date()对象的一个方法,返回类型 long
,用于获取从1970-01-01 8:00:00开始到现在经历的毫秒 数
\n另外,Date().getTime()
和 System.currentTimeMillis()
是一样的
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package date; import java.util.Date; public class TestDate { public static void main (String[] args) { Date now= new Date(); System.out.println("Date.getTime() \\t\\t\\t返回值: " +now.getTime()); System.out.println("System.currentTimeMillis() \\t返回值: " +System.currentTimeMillis()); } }
\n格式化日期并转为字符串 格式化日期:
\n需要用到 SimpleDateFormat
类
\n1 import java.text.SimpleDateFormat;
\n\n指定格式
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" ); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd" ); SimpleDateFormat sdf2 = new SimpleDateFormat("MM/dd" );
\n \n利用sdf对象的 format
方法对日期 Date()
进行格式化,并返回格式化后的字符串
\n1 2 Date d = new Date(); String str = sdf.format(d);
\n \n \n字符串转日期 三个步骤 :
\n\n指定字符串格式:
\n1 2 3 SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" );
\n \n创建字符串,注意格式要与 sdf 一致
\n1 2 3 String str = "2019-1-15 16:02:27" ;
\n \n创建日期对象,用 sdf的 parse(str)
方法来把字符串转为日期对象
\n1 Date d = sdf.parse(str);
\n \n \n日历 Calendar 使用前需要导入:
\n1 import java.util.Calendar;
\n\n获取日历对象 Calendar.getInstance();
\n1 Calendar cl = Calendar.getInstance();
\n \n通过日历对象得到日期对象: getTime()
\n \n \n日期设置:把 cl
这个日历,调成日期(时间原点) —— 1970.1.1 08:00:00,用 setTime()
\n1 2 Date d2 = new Date(0 ); cl.setTime(d2);
\n \n \n翻日历 \nadd方法,在原日期上增加(add) 年/月/日 set方法,直接设置(set) 年/月/日
\n \n\n创建日历对象
\n1 Calendar c = Calendar.getInstance();
\n \n获取当前日期
\n \n \n把日历改成下个月的今天
\n1 2 3 4 c.setTime(now); c.add(Calendar.MONTH, 1 );
\n \n把日历改成去年的今天
\n1 2 c.setTime(now); c.add(Calendar.YEAR, -1 );
\n \n把日历直接设置 成上个月的第三天
\n1 2 3 4 5 6 c.setTime(now); c.add(Calendar.MONTH, -1 ); c.set(Calendar.DATE, 3 );\t\t
\n \n \n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"日期 在Java中使用日期,需导入 java.util.Date
类
\n \n创建一个日期对象 \nDate date = new Date();
\n \n1 2 3 4 Date now = new Date();
\n\n1 String s = now.toString()
\ngetTime()方法 getTime()
是Date()对象的一个方法,返回类型 long
,用于获取从1970-01-01 8:00:00开始到现在经历的毫秒 数
\n另外,Date().getTime()
和 System.currentTimeMillis()
是一样的
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package date; import java.util.Date; public class TestDate { public static void main (String[] args) { Date now= new Date(); System.out.println("Date.getTime() \\t\\t\\t返回值: " +now.getTime()); System.out.println("System.currentTimeMillis() \\t返回值: " +System.currentTimeMillis()); } }
\n格式化日期并转为字符串 格式化日期:
\n需要用到 SimpleDateFormat
类
\n1 import java.text.SimpleDateFormat;
\n\n指定格式
\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" ); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd" ); SimpleDateFormat sdf2 = new SimpleDateFormat("MM/dd" );
\n \n利用sdf对象的 format
方法对日期 Date()
进行格式化,并返回格式化后的字符串
\n1 2 Date d = new Date(); String str = sdf.format(d);
\n \n \n字符串转日期 三个步骤 :
\n\n指定字符串格式:
\n1 2 3 SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" );
\n \n创建字符串,注意格式要与 sdf 一致
\n1 2 3 String str = "2019-1-15 16:02:27" ;
\n \n创建日期对象,用 sdf的 parse(str)
方法来把字符串转为日期对象
\n1 Date d = sdf.parse(str);
\n \n \n日历 Calendar 使用前需要导入:
\n1 import java.util.Calendar;
\n\n获取日历对象 Calendar.getInstance();
\n1 Calendar cl = Calendar.getInstance();
\n \n通过日历对象得到日期对象: getTime()
\n \n \n日期设置:把 cl
这个日历,调成日期(时间原点) —— 1970.1.1 08:00:00,用 setTime()
\n1 2 Date d2 = new Date(0 ); cl.setTime(d2);
\n \n \n翻日历 \nadd方法,在原日期上增加(add) 年/月/日 set方法,直接设置(set) 年/月/日
\n \n\n创建日历对象
\n1 Calendar c = Calendar.getInstance();
\n \n获取当前日期
\n \n \n把日历改成下个月的今天
\n1 2 3 4 c.setTime(now); c.add(Calendar.MONTH, 1 );
\n \n把日历改成去年的今天
\n1 2 c.setTime(now); c.add(Calendar.YEAR, -1 );
\n \n把日历直接设置 成上个月的第三天
\n1 2 3 4 5 6 c.setTime(now); c.add(Calendar.MONTH, -1 ); c.set(Calendar.DATE, 3 );\t\t
\n \n \n"},{"title":"17-容器","abbrlink":"c0626013","date":"2021-02-17T04:31:15.000Z","description":"java的集合(容器)介绍,关于这一内容写了2篇笔记,看完不是很理解可以看另一篇","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n# 容 器\n\n## Collectiion接口\n\nCollection接口声明了以下方法:\n\n```java\nboolean add(Object element);\nboolean remove(Object element);\nboolean contains(Object element);\n\nint size(); //容器中元素数量 \n\nboolean isEmpty();\nvoid clear();\n\nIterator iterator();\nboolean containsAll(Collection c);\nboolean addAll(Collection c);\n\nboolean removeAll(Collection c);\nboolean retainAll(Collection c); //移除非交集元素,保留交集元素\n\nObject[] toArray(); //转换成Object数组\n```\n\n\n\n> 注:List和Set是Collection的子接口\n\n\n\n### List接口\n\nList特点:有序、元素可重复\n\n- 有序:每个元素都有索引标记\n- List通常允许满足 `e1.equals(e2)` 的元素重复加入容器\n\n相比Collection接口,List中多了一些与索引有关的方法\n\n```java\nvoid add(int index, Object element);\nObject set(int index, Object element);\nObject get(int index);\nObject remove(int index);\n\nint indexOf(Object o); //返回第一个匹配元素的索引,若没有返回-1\nint lastIndexOf(Object o); //返回最后一个匹配的元素的索引,若没有返回-1\n\n```\n\n\n\n\n\n### ArrayList类\n\nArrayList底层是用【数组】实现的存储。==特点:查询效率高,增删效率低,线程不安全==。\n\n我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?\n\n本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。 \n\nArrayList的Object数组【初始化长度为10】,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中\n\n\n\n### LinkList类\n\nLinkList底层用【双向链表】实现\n\n==特点:查询效率低、增删效率高,线程不安全==\n\n> 双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。 \n>\n> 所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。\n\n\n\n### Vector类\n\nVector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”。 比如,indexOf方法就增加了synchronized同步标记。\n\n> 如何选用ArrayList、LinkedList、Vector\n\n1. 需要线程安全时,用Vector。\n\n2. 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)。\n\n3. 不存在线程安全问题时,增加或删除元素较多用LinkedList。\n\n\n\n## Map接口\n\n Map就是用来存储“**键(key)-值(value) 对**”的。 Map类中存储的“键值对”通过键来标识,所以“键”不能重复。\n\n\n\nMap接口常用方法:\n\n```java\nObject put(Object key, Object value);\nObject get(Object key);\nObject remove(Object key);\n\nboolean containsKey(Object Key);\nboolean containsValue(Object value);\n\nint size();\nboolean isEmpty();\n\nvoid putAll(Map t);\nvoid clear();\n```\n\n Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等。\n\n\n\n### HashMap类\n\nHashMap采用**哈希算法**实现,是Map接口最常用的实现类。\n\n由于底层采用了哈希表存储数据,我们要求**键不能重复**,==如果发生重复,新的键值对会替换旧的键值对==\n\nHashMap在查找、删除、修改方面都有非常高的效率\n\n\n\n> HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。\n>\n> **HashMap与HashTable的区别**\n>\n> 1. HashMap: 线程不安全,效率高。**允许key或value为null**\n>\n> 2. HashTable: 线程安全,效率低。**不允许key或value为null**\n\n\n\n## Set接口与HashSet类\n\nSet接口继承自Collection,Set接口中**没有新增方法**,方法和Collection保持完全一致。\n\nSet容器特点:无序、不可重复\n\n> 新元素如果和Set中某个元素通过`equals()`方法对比为true,则不能加入;\n>\n> 甚至,Set中也只能放入一个null元素,不能多个。\n\nSet常用的**实现类**有:HashSet、TreeSet等,我们一般使用HashSet\n\n\n\nHashSet基本用法\n\n```java\npublic class Test {\n public static void main(String[] args) {\n Set s = new HashSet();\n s.add(\"hello\");\n s.add(\"world\");\n System.out.println(s);\n s.add(\"hello\"); //相同的元素不会被加入\n System.out.println(s);\n s.add(null);\n System.out.println(s);\n s.add(null);\n System.out.println(s);\n }\n}\n```\n\n\n\n\n\n## 迭代器\n\n\n\n将容器转为迭代器:\n\n```java\nIterator iter = aList.iterator()\n```\n\n使用Iterator迭代器遍历容器元素(List/Set/Map)\n\n- 迭代器遍历ArrayList\n\n```java\nList aList = new ArrayList();\n\nfor (int i = 0; i < 5; i++) {\n aList.add(\"a\" + i);\n}\n\nfor (Iterator iter = aList.iterator(); iter.hasNext();){\n String temp = iter.next();\n System.out.print(temp + \"\\t\");\n if (temp.endsWith(\"3\")) {// 删除3结尾的字符串\n iter.remove();\n }\n}\n```\n\n\n\n- 迭代器遍历Map 【1】\n\n```java\npublic class Test{\n public static void main(String[] args){\n Map map = new HashMap();\n map.put(\"A\", \"GaoQi\");\n map.put(\"B\", \"LiuBa\");\n Set> ss = map.entrySet();\n for(Iterator> iterator = ss.iterator(); iterator.hasNext();){\n Entry e = iterator.next();\n System.out.println(e.getKey() + \"--\" + e.getValue());\n }\n }\n}\n```\n\n\n\n- 迭代器遍历Map 【2】\n\n```java\npublic class Test {\n public static void main(String[] args) {\n Map map = new HashMap();\n map.put(\"A\", \"GaoQi\");\n map.put(\"B\", \"LiuBa\");\n Set ss = map.keySet();\n for (Iterator iterator = ss.iterator(); iterator.hasNext();) {\n String key = iterator.next();\n System.out.println(key + \"--\" + map.get(key));\n }\n }\n}\n```\n\n\n\n\n\n## Collections工具类\n\n类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。\n\n1. void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。\n2. void shuffle(List) //对List容器内的元素进行随机排列。\n3. void reverse(List) //对List容器内的元素进行逆续排列 。\n4. void fill(List, Object) //用一个特定的对象重写整个List容器。\n5. int binarySearch(List, Object) //对于顺序的List容器,采用折半查找的方法查找特定对象。\n\n","source":"_posts/javaSE/17-容器.md","raw":"---\ntitle: 17-容器\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: c0626013\ndate: 2021-02-17 12:31:15\ndescription: java的集合(容器)介绍,关于这一内容写了2篇笔记,看完不是很理解可以看另一篇\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n# 容 器\n\n## Collectiion接口\n\nCollection接口声明了以下方法:\n\n```java\nboolean add(Object element);\nboolean remove(Object element);\nboolean contains(Object element);\n\nint size(); //容器中元素数量 \n\nboolean isEmpty();\nvoid clear();\n\nIterator iterator();\nboolean containsAll(Collection c);\nboolean addAll(Collection c);\n\nboolean removeAll(Collection c);\nboolean retainAll(Collection c); //移除非交集元素,保留交集元素\n\nObject[] toArray(); //转换成Object数组\n```\n\n\n\n> 注:List和Set是Collection的子接口\n\n\n\n### List接口\n\nList特点:有序、元素可重复\n\n- 有序:每个元素都有索引标记\n- List通常允许满足 `e1.equals(e2)` 的元素重复加入容器\n\n相比Collection接口,List中多了一些与索引有关的方法\n\n```java\nvoid add(int index, Object element);\nObject set(int index, Object element);\nObject get(int index);\nObject remove(int index);\n\nint indexOf(Object o); //返回第一个匹配元素的索引,若没有返回-1\nint lastIndexOf(Object o); //返回最后一个匹配的元素的索引,若没有返回-1\n\n```\n\n\n\n\n\n### ArrayList类\n\nArrayList底层是用【数组】实现的存储。==特点:查询效率高,增删效率低,线程不安全==。\n\n我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?\n\n本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。 \n\nArrayList的Object数组【初始化长度为10】,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中\n\n\n\n### LinkList类\n\nLinkList底层用【双向链表】实现\n\n==特点:查询效率低、增删效率高,线程不安全==\n\n> 双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。 \n>\n> 所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。\n\n\n\n### Vector类\n\nVector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”。 比如,indexOf方法就增加了synchronized同步标记。\n\n> 如何选用ArrayList、LinkedList、Vector\n\n1. 需要线程安全时,用Vector。\n\n2. 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)。\n\n3. 不存在线程安全问题时,增加或删除元素较多用LinkedList。\n\n\n\n## Map接口\n\n Map就是用来存储“**键(key)-值(value) 对**”的。 Map类中存储的“键值对”通过键来标识,所以“键”不能重复。\n\n\n\nMap接口常用方法:\n\n```java\nObject put(Object key, Object value);\nObject get(Object key);\nObject remove(Object key);\n\nboolean containsKey(Object Key);\nboolean containsValue(Object value);\n\nint size();\nboolean isEmpty();\n\nvoid putAll(Map t);\nvoid clear();\n```\n\n Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等。\n\n\n\n### HashMap类\n\nHashMap采用**哈希算法**实现,是Map接口最常用的实现类。\n\n由于底层采用了哈希表存储数据,我们要求**键不能重复**,==如果发生重复,新的键值对会替换旧的键值对==\n\nHashMap在查找、删除、修改方面都有非常高的效率\n\n\n\n> HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。\n>\n> **HashMap与HashTable的区别**\n>\n> 1. HashMap: 线程不安全,效率高。**允许key或value为null**\n>\n> 2. HashTable: 线程安全,效率低。**不允许key或value为null**\n\n\n\n## Set接口与HashSet类\n\nSet接口继承自Collection,Set接口中**没有新增方法**,方法和Collection保持完全一致。\n\nSet容器特点:无序、不可重复\n\n> 新元素如果和Set中某个元素通过`equals()`方法对比为true,则不能加入;\n>\n> 甚至,Set中也只能放入一个null元素,不能多个。\n\nSet常用的**实现类**有:HashSet、TreeSet等,我们一般使用HashSet\n\n\n\nHashSet基本用法\n\n```java\npublic class Test {\n public static void main(String[] args) {\n Set s = new HashSet();\n s.add(\"hello\");\n s.add(\"world\");\n System.out.println(s);\n s.add(\"hello\"); //相同的元素不会被加入\n System.out.println(s);\n s.add(null);\n System.out.println(s);\n s.add(null);\n System.out.println(s);\n }\n}\n```\n\n\n\n\n\n## 迭代器\n\n\n\n将容器转为迭代器:\n\n```java\nIterator iter = aList.iterator()\n```\n\n使用Iterator迭代器遍历容器元素(List/Set/Map)\n\n- 迭代器遍历ArrayList\n\n```java\nList aList = new ArrayList