摘要:时间年月日星期四说明本文部分内容均来自慕课网。那么里面的数据就可以分为各种各样的索引,比如汽车索引图书索引家具索引等等。图书索引又可以细分为各种类型,比如科普类小说类技术类等等。具体到每一本书籍,就是文档,就是整个图书里面最小的存储单位。
时间:2017年09月14日星期四
说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com
教学源码:无
学习源码:https://github.com/zccodere/s...
什么是ElasticSearch
基于Apache Lucene构建的开源搜索引擎 采用Java编写,提供简单易用的RESTful API 轻松的横向扩展,可支持PB级的结构化和非结构化数据处理
可用应用场景
海量数据分析引擎 站内搜索引擎 数据仓库
一线公司实际应用场景
英国卫报-实时分析公众对文章的回应 维基百科、GitHub-站内实时搜索 百度-实时日志监控平台 阿里巴巴、谷歌、京东、腾讯、小米等等
前置知识
熟悉用Maven构建项目 了解Spring Boot的基本使用
环境要求
IDE工具:IntelliJ IDEA、Eclipse等常用IDE即可 Java版本:JDK1.8 其他依赖:Maven、NodeJs(6.0以上)
课程安排
如何安装单节点的ElasticSearch 如何安装插件及插件的主要作用 如何安装分布式的ElasticSearch 了解ElasticSearch的基础概念 了解ElasticSearch的基本用法 了解ElasticSearch的高级查询 使用Spring Boot集合ElasticSearch实战开发第二章:软件安装 2-1 版本选择
ES版本问题
版本历史:1.x -> 2.x -> 5.x 版本选择:拥抱新的版本2-2 单机安装
学习笔记
单机安装ElasticSearch 安装前,请确保已经安装JDK1.8 安装前,请确保已经安装nodejs6.0以上 官网:https://www.elastic.co/products/elasticsearch 下载安装包:https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.0.tar.gz 解压安装包:tar -vxf elasticsearch-5.6.0.tar.gz cd elasticsearch-5.6.0.tar.gz 启动前,检查JDK环境 java -v 请确保已经安装JDK1.8 启动elasticsearch sh ./bin/elasticsearch 当日志输出started时,表示启动成功 验证服务 127.0.0.1:9200 elasticsearch服务默认监听9200端口 访问:http://127.0.0.1:9200 如果出现版本信息,则安装成功2-3 插件安装
学习笔记
实用插件Head安装 打开github:https://github.com/mobz/elasticsearch-head 下载插件包:https://codeload.github.com/mobz/elasticsearch-head/zip/master unzip elasticsearch-head-master.zip cd elasticsearch-head-master 检查Node环境 node -v 请确保已经安装nodejs6.0以上 安装插件 npm install 启动插件 npm run start 输出日志表示启动成功 Started connect web server on http://localhost:9100 访问 http://localhost:9100 ElasticSearch整合elasticsearch-head插件 cd elasticsearch-5.6.0 vim config/elasticsearch.yml 在配置文件的最后面加上 允许head插件跨域访问rest接口 http.cors.allowed: true http.cors.allow-origin: "*" :wq 后台启动 ./bin/elasticsearch -d 再次重新启动elasticsearch-head插件 cd elasticsearch-head-master 启动插件 npm run start 访问 http://localhost:91002-4 集群安装
学习笔记
集群安装 1个master、2个slave master节点配置 配置当前节点为主节点 cd elasticsearch-5.6.0 修改配置 vim config/elasticsearch.yml 在配置文件的最后面加上 # 指定集群的名字 cluster.name: myes # 指定当前节点的名字 node.name: master # 指定当前节点为master node.master: true # 指定绑定的IP network.host: 127.0.0.1 # 使用默认端口:9200 :wq ps -ef | grep "pwd" kill pid 重新启动 ./bin/elasticsearch -d 检查服务是否正常启动 http://localhost:9200 slave节点配置 mkdir es_slave cp elasticsearch-5.6.0.tar.gz es_slave/ cd es_slave tar -vxf elasticsearch-5.6.0.tar.gz cp -r elasticsearch-5.6.0 es_slave1 cp -r elasticsearch-5.6.0 es_slave2 修改es_slave1配置 cd es_slave1 vim config/elasticsearch.yml 在配置文件的最后面加上 # 指定集群的名字:需要和master节点一致 cluster.name: myes # 指定当前节点的名字 node.name: slave1 # 指定绑定的IP network.host: 127.0.0.1 # 指定当前节点绑定端口号8200 http.port: 8200 # 该配置主要是为了找到master节点 discovery.zen.ping.unicast.hosts: ["127.0.0.1"] :wq 启动服务 ./bin/elasticsearch -d 检查服务是否正常启动 http://localhost:9100 安装之前的步骤配置slave2 cd es_slave2 vim config/elasticsearch.yml 在配置文件的最后面加上 # 指定集群的名字:需要和master节点一致 cluster.name: myes # 指定当前节点的名字 node.name: slave2 # 指定绑定的IP network.host: 127.0.0.1 # 指定当前节点绑定端口号8000 http.port: 8000 # 该配置主要是为了找到master节点 discovery.zen.ping.unicast.hosts: ["127.0.0.1"] :wq 启动服务 ./bin/elasticsearch -d 检查服务是否正常启动 http://localhost:9100第三章:基础概念 3-1 基础概念
集群和节点
一个集群是由一个或多个ES组成的集合 每一个集群都有一个唯一的名字 每一个节点都是通过集群的名字来加入集群的 每一个节点都有自己的名字 节点能够存储数据,参与集群索引数据以及搜索数据的独立服务
基础概念
索引:含有相同属性的文档集合 类型:索引可以定义一个或多个类型,文档必须属于一个类型 (通常会定义有相同字段的文档作为一个类型) 文档:文档是可以被索引的基本数据单位
三者之间的关系
索引相当于SQL里的DataBase,也就是数据库 类型相当于SQL里的Table,也就是表 文档相当于SQL里的一行记录,也就是一行数据
举个例子
假设有一个信息查询系统,使用ES做存储。那么里面的数据就可以分为各种各样的索引,比如:汽车索引、图书索引、家具索引等等。图书索引又可以细分为各种类型,比如:科普类、小说类、技术类等等。具体到每一本书籍,就是文档,就是整个图书里面最小的存储单位。
和索引相关的两个高级概念
分片:每个索引都有多个分片,每个分片是一个Lucene索引 备份:拷贝一份分片就完成了分片的备份 ES默认在创建索引时,会创建5个分片、1个备份 分片的数量只能在创建索引时设置,而不能在后期进行修改 备份是可以动态修改的第四章:基本用法 4-1 创建索引
ES的API组成结构:使用RESTful API风格来命名API
API基本格式:http://: /<索引>/<类型>/<文档id> 常用HTTP动词:GET/PUT/POST/DELETE
使用Head插件创建非结构化索引
访问:localhost:9100 路径:索引->新建索引->索引名称:book->点击OK 索引名称:必须小写,不能有中划线
如何区分索引是结构化的还是非结构化的
结合Head插件查看 点击索引信息->索引信息->mappings节点 当mappings节点后的内容为空时:非结构化索引
使用Head插件创建结构化索引
路径:复合查询->查询->book/novel/_mappings 指定映射:使用JSON结构体 { "novel":{ "propertise":{ "title":{"type":"test"} } } } 然后勾选易读->点击验证JSON->提交请求 再次查看mappings节点时,已经不是空的了
使用PostMan创建索引
PUT:127.0.0.1:9200/people Body->raw->JSON(application/json) 编写JSON体 点击Send,然后到Head插件中查看people索引信息
编写JSON体如下
{ "settings":{ "number_of_shards":3, "number_of_replicas":1 }, "mappings":{ "man":{ "properties":{ "name":{ "type": "text" }, "country":{ "type": "keyword" }, "age":{ "type": "integer" }, "date":{ "type": "date", "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" } } }, "woman":{ } } }4-2 新增文档
新增文档
指定文档id新增 自动产生文档id新增 文档id:唯一索引值指向文档数据
使用PostMan工具新增数据-指定文档id新增
PUT:127.0.0.1::9200/people/man/1 Body->raw->JSON(application/json) { "name":"zc", "country":"china", "age":22, "date":"1995-01-01" } 点击Send,可以看到ES响应的信息 使用Head插件查看索引下的数据,docs字段代表索引下所有文档的数量值 点击数据浏览,可以看见刚刚新增的数据
使用PostMan工具新增数据-自动产生文档id新增
POST:127.0.0.1::9200/people/man/ Body->raw->JSON(application/json) { "name":"myzc", "country":"china", "age":22, "date":"1995-02-01" } 点击Send,可以看到ES响应的信息 使用Head插件,点击数据浏览,可以看见刚刚新增的数据4-3 修改文档
修改文档
直接修改文档 脚本修改文档
使用PostMan工具修改文档-指定文档ID修改
POST:127.0.0.1:9200/people/man/1/_update Body->raw->JSON(application/json) { "doc":{ "name":"who is zc" } } 点击Send,可以看到ES响应的信息 使用Head插件,点击数据浏览,可以看见刚刚修改的数据
使用PostMan工具修改文档-指定文档ID使用脚本修改
POST:127.0.0.1:9200/people/man/1/_update Body->raw->JSON(application/json) { "script":{ "lang":"painless", "inline":"ctx._sources.age+=10" } } 或使用以下格式 { "script":{ "lang":"painless", "inline":"ctx._sources.age = params.age", "params":{ "age":100 } } } 点击Send,可以看到ES响应的信息 使用Head插件,点击数据浏览,可以看见刚刚修改的数据4-4 删除文档
删除
删除文档 删除索引
使用PostMan删除文档-指定文档ID
DELETE:127.0.0.1:9200/people/man/1 点击Send,可以看到ES响应的信息 使用Head插件,点击数据浏览,可以看见数据已经删除
使用Head插件删除索引
路径:概览->book->动作->删除->输入删除->确定 注意:删除操作本身很危险,删除索引时会删除它所有的文档数据
使用PostMan删除索引
DELETE:127.0.0.1:9200/people 点击Send,可以看到ES响应的信息 使用Head插件,点击数据浏览,可以看见索引已经删除4-5 查询语法
ES查询分类
简单查询 条件查询 聚合查询
前置条件,创建book索引,并预先新增一些数据
使用PostMan简单查询-指定文档ID
GET:127.0.0.1:9200/book/novel/1 点击Send,可以看到ES响应的信息
使用PostMan条件查询
POST:127.0.0.1:9200/book/_search Body->raw->JSON(application/json) 编写查询JSON体 点击Send,可以看到ES响应的信息
编写查询JSON体如下
查询所有数据
{ "query":{ "match_all":{} } }
用from指定从哪里返回,用size指定返回的数据大小
{ "query":{ "match_all":{} }, "from":1, "size":1 }
使用关键字查询,查询标题含有ElasticSearch的数据
{ "query":{ "match":{ "title":"ElasticSearch" } } }
使用sort指定结果集排序-按照出版日期倒序
{ "query":{ "match":{ "title":"ElasticSearch" } }, "sort":[ { "publish_date":{ "order":"desc" } } ] }
按照书籍的字数进行单个聚合查询
{ "aggs":{ "group_by_word_count":{ "terms":{ "field":"word_count" } } } }
按照书籍的字数及出版日期进行多个聚合查询
{ "aggs":{ "group_by_word_count":{ "terms":{ "field":"word_count" } }, "group_by_publish_date":{ "terms":{ "field":"publish_date" } } } }
对书籍字数进行统计计算
{ "aggs":{ "grades_word_count":{ "stats":{ "field":"word_count" } } } }第五章:高级查询 5-1 query语法
高级查询
子条件查询:特定字段查询所指特定值 query context filter context 复合条件查询:以一定的逻辑组合子条件查询 固定分数查询 布尔查询
query context介绍
在查询过程中,除了判断是否满足查询条件外 ES还会计算一个_score来标识匹配的程度 旨在判断目标文档和查询条件匹配的有多好
query context查询
全文本查询:针对文本类型数据 字段级别查询:针对结构化数据,如数字、日期等
使用PostMan进行query context文本查询
POST:127.0.0.1:9200/book/_search Body->raw->JSON(application/json) 编写查询JSON体 点击Send,可以看到ES响应的信息
编写查询JSON体如下
使用match关键字模糊匹配
{ "query":{ "match":{ "author":"wali" } } }
使用match_phrase关键字习语匹配
{ "query":{ "match_phrase":{ "author":"ElasticSearch入门" } } }
使用multi_match查询作者和标题包含wali的数据
{ "query":{ "multi_match":{ "query":"wali", "fields":["author","title"] } } }
使用query_string进行语法查询
{ "query":{ "query_string":{ "query":"(ElasticSearch AND 大法) OR Python" } } }
使用query_string查询多个字段
{ "query":{ "query_string":{ "query":"wali OR ElasticSearch", "field":["title","author"] } } }
使用PostMan进行query context字段查询
POST:127.0.0.1:9200/book/_search Body->raw->JSON(application/json) 编写查询JSON体 点击Send,可以看到ES响应的信息
编写查询JSON体如下
查询字数在某个特定集(1000)的书籍
{ "query":{ "term":{ "word_count":1000 } } }
查询字符在某个范围(大于等于1000-小于等于2000)的书籍
{ "query":{ "range":{ "word_count":{ "gte":1000, "lte":2000 } } } }
查询出版日期在某个范围(2017-01-01至2017-12-31)的书籍
{ "query":{ "range":{ "publish_date":{ "gte":"2017-01-01", "lte":"2017-12-31"//或 "lte":"now" } } } }
使用关键now,代指当前日志(即现在)
5-2 filter语法filter context介绍
在查询过程中,只判断该文档是否满足条件 只有Yes或No
使用PostMan进行filter context查询
POST:127.0.0.1:9200/book/_search Body->raw->JSON(application/json) 点击Send,可以看到ES响应的信息
查询字数1000的书籍
{ "query":{ "bool":{ "filter":{ "term":{ "word_count":1000 } } } } }5-3 复合查询
常用复合条件查询
固定分数查询 布尔查询
使用PostMan进行复合查询
127.0.0.1:9200 /_search Body->raw->JSON(application/json) 点击Send,可以看到ES响应的信息
全文搜索-标题含有ElasticSearch的书籍
{ "query":{ "constant_score":{ "filter":{ "match":{ "title": "ElasticSearch" } }, "boost":2 } } }
布尔查询- should满足任意条件
{ "query":{ "bool":{ "should":[ { "match":{ "author":"wali" } }, { "match":{ "title":"ElasticSearch" } } ] } } }
布尔查询- must满足全部条件
{ "query":{ "bool":{ "must":[ { "match":{ "author":"wali" } }, { "match":{ "title":"ElasticSearch" } } ] } } }
使用must和filter复合查询
{ "query":{ "bool":{ "must":[ { "match":{ "author":"wali" } }, { "match":{ "title":"ElasticSearch" } } ], "filter":[ { "term":{ "word_count":1000 } } ] } } }
布尔查询- must_not一定不能满足的条件
{ "query":{ "bool":{ "must_not":{ "term":{ "author":"wali" } } } } }第六章:实战开发 6-1 环境搭建
实战演练
SpringBoot集成ES 图书信息管理接口开发
创建名为springbootes的gradle项目build.gradle如下
buildscript { ext { springBootVersion = "1.5.6.RELEASE" } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: "java" apply plugin: "eclipse" apply plugin: "org.springframework.boot" group = "com.myimooc" version = "0.0.1-SNAPSHOT" sourceCompatibility = 1.8 repositories { maven{url:"http://maven.aliyun.com/nexus/content/groups/public/"} mavenCentral() } dependencies { compile("org.springframework.boot:spring-boot-starter-web") //compile("org.springframework.boot:spring-boot-starter-data-elasticsearch") compile("org.elasticsearch.client:transport:5.5.2") compile("org.apache.logging.log4j:log4j-core:2.7.0") testCompile("org.springframework.boot:spring-boot-starter-test") }6-2 接口开发
接口列表
新增图书信息 修改图书信息 删除图书信息 综合查询功能
代码编写
1.编写EsConfig类
package com.myimooc.springbootes.config; import java.net.InetAddress; import java.net.UnknownHostException; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.transport.client.PreBuiltTransportClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @title ElasticSearch配置类 * @describe ElasticSearch配置 * @author zc * @version 1.0 2017-09-15 */ @Configuration public class EsConfig { @Bean public TransportClient client() throws UnknownHostException{ InetSocketTransportAddress node = new InetSocketTransportAddress(InetAddress.getByName("localhost"),9300); Settings settings = Settings.builder() // es集群名称 .put("cluster.name", "myes") .build(); TransportClient client = new PreBuiltTransportClient(settings); client.addTransportAddress(node); return client; } }
2.编写BookRest类
package com.myimooc.springbootes.rest; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.RangeQueryBuilder; import org.elasticsearch.search.SearchHit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @title 图书REST接口类 * @describe 调ES接口 * @author zc * @version 1.0 2017-09-15 */ @RestController public class BookRest { @Autowired private TransportClient client; @GetMapping("/") public String index(){ return "index"; } /** * @describe 查询接口 * @author zc * @version 1.0 2017-09-15 */ @GetMapping("/get/book/novel") public ResponseEntity> get(@RequestParam(name="id",defaultValue="")String id){ if(id.isEmpty()){ return new ResponseEntity<>(HttpStatus.NOT_FOUND); } GetResponse result = this.client.prepareGet("book","novel",id).get(); if(!result.isExists()){ return new ResponseEntity<>(HttpStatus.NOT_FOUND); } return new ResponseEntity<>(result.getSource(), HttpStatus.OK); } /** * @describe 增加接口 * @author zc * @version 1.0 2017-09-15 */ @PostMapping("/add/book/novel") public ResponseEntity> add( @RequestParam(name="title")String title, @RequestParam(name="author")String author, @RequestParam(name="word_count")int wordCount, @RequestParam(name="publish_date") @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") Date publishDate){ try { XContentBuilder content = XContentFactory.jsonBuilder() .startObject() .field("title",title) .field("author", author) .field("word_count", wordCount) .field("publish_date", publishDate.getTime()) .endObject(); IndexResponse result = this.client.prepareIndex("book","novel").setSource(content).get(); return new ResponseEntity<>(result.getId(),HttpStatus.OK); } catch (IOException e) { e.printStackTrace(); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } /** * @describe 删除接口 * @author zc * @version 1.0 2017-09-15 */ @DeleteMapping("/delete/book/novel") public ResponseEntity> delete(@RequestParam(name="id",defaultValue="")String id){ DeleteResponse result = this.client.prepareDelete("book", "novel", id).get(); return new ResponseEntity<>(result.toString(),HttpStatus.OK); } /** * @describe 修改接口 * @author zc * @version 1.0 2017-09-15 */ @DeleteMapping("/update/book/novel") public ResponseEntity> update( @RequestParam(name="id",defaultValue="")String id, @RequestParam(name="title",required=false)String title, @RequestParam(name="author",required=false)String author){ UpdateRequest update = new UpdateRequest("book","novel",id); try { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject(); if(!StringUtils.isEmpty(title)){ builder.field("title",title); } if(!StringUtils.isEmpty(author)){ builder.field("author", author); } builder.endObject(); update.doc(builder); UpdateResponse result = this.client.update(update).get(); return new ResponseEntity<>(result.toString(),HttpStatus.OK); } catch (Exception e) { e.printStackTrace(); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } /** * @describe 复合查询 * @author zc * @version 1.0 2017-09-15 */ @DeleteMapping("/query/book/novel") public ResponseEntity> query( @RequestParam(name="author",required=false)String author, @RequestParam(name="title",required=false)String title, @RequestParam(name="gt_word_count",defaultValue="0") int gtWordCount, @RequestParam(name="lt_word_count",required=false) Integer ltWordCount){ // 构建布尔查询 BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); if(!StringUtils.isEmpty(author)){ boolQuery.must(QueryBuilders.matchQuery("author", author)); } if(!StringUtils.isEmpty(title)){ boolQuery.must(QueryBuilders.matchQuery("title", title)); } // 构建范围查询 RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("word_count") .from(gtWordCount); if(ltWordCount != null && ltWordCount > 0){ rangeQuery.to(ltWordCount); } // 使用filter构建 boolQuery.filter(rangeQuery); SearchRequestBuilder builder = this.client.prepareSearch("book") .setTypes("novel") .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .setQuery(boolQuery) .setFrom(0) .setSize(10); System.out.println("[ES查询请求参数]:"+builder); SearchResponse response = builder.get(); List
3.编写SpringbootesApplication类
package com.myimooc.springbootes; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @title SpringBoot集成ElasticSearch * @describe 启动类 * @author zc * @version 1.0 2017-09-15 */ @SpringBootApplication public class SpringbootesApplication { public static void main(String[] args) { SpringApplication.run(SpringbootesApplication.class, args); } }第七章:课程总结 7-1 课程总结
课程总结
ES简介:使用场景例子、ES的重要性 ES安装:单机安装、集群安装、Head插件安装 ES基础:核心基础概念:索引、类型、文档 ES用法:基本用法:增删改查 ES高级:高级查询语法 ES实战:SpringBoot集成ES开发增删改查接口
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70474.html
时间:2017年10月16日星期一说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学源码:无学习源码:https://github.com/zccodere/s... 第一章:课程简介 1-1 课程介绍 本门课程的主要内容 RxJava是什么 RxAndroid是什么 RxJava常用操作符(重点、难点) 怎样在项目中使用RxJava和RxAndroid 如何学...
摘要:数据绑定入门学习总结时间年月日星期日说明本文部分内容均来自慕课网。慕课网教学示例源码个人学习源码第一章课程介绍数据绑定入门概述数据绑定概念来自百度百科简单绑定是将一个用户界面元素控件的属性绑定到一个类型对象实例上的某个属性的方法。 《SpringMVC数据绑定入门》学习总结 时间:2017年2月19日星期日说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.co...
摘要:入门篇学习总结时间年月日星期三说明本文部分内容均来自慕课网。主要的功能是日志记录,性能统计,安全控制,事务处理,异常处理等等。 《Spring入门篇》学习总结 时间:2017年1月18日星期三说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:https://github.com/zccodere/s...个人学习源码:https://git...
阅读 3685·2021-09-07 10:19
阅读 3626·2021-09-03 10:42
阅读 3583·2021-09-03 10:28
阅读 2547·2019-08-29 14:11
阅读 808·2019-08-29 13:54
阅读 1593·2019-08-29 12:14
阅读 416·2019-08-26 12:12
阅读 3613·2019-08-26 10:45