SpringBoot Solr
文章目录
Solr全文检索
一、Solr
官网下载:https://lucene.apache.org/solr/downloads.html
1、下载 .zip 为windows版
我下载的最新版本solr-8.2.0:内嵌jeety服务器,solr5之前的版本都需要放到自己的tomcat里.
下载:solr-8.2.0.zip
安装:
下载完成之后放在的D:\solr-8.2.0目录下,然后解压到当前目录
解压结构目录说明:
还有一个:server文件夹,图示比较老的,没有显示server。
2、启动、关闭solr
在解压之后的solr目录中找到bin文件夹,进去之后右键进入Powershell输入以下命令:
# 启动
.\solr start
其它命令:
# 启动
.\solr start
# 关闭
.\solr stop -all
# 步骤:
# 启动 Solr 将默认监听 8983 端口,其中 -m 1g 指定分配给 JVM 的内存为 1 G
.\solr start -m 1g
# 接下来我们创建一个solr应用:在solr的目录下生成应用对应的文件夹wenda,这个文件夹非常重要,后面我们会在该文件夹下,配置文件来实现中文检索
.\solr create -c wenda
测试启动是否成功:
其中8983是默认端口号。此时在浏览器输入网址http://localhost:8983/solr/#/
成功的话即可看到如下图界面。
3、创建core文件夹
两种方式效果一样,方式一方便而已。
创建core文件夹方式一(推荐):
cmd在solr-8.2.0/solr/bin目录***意,需要在solr start启动solr后才能创建)输入:
.\solr create -c corename # (core名称,自定义)命令创建一个core
重启:
.\solr stop -all
启动
.\solr start
运行完成后在浏览器中访问http://localhost:8983/solr/#/(刷新页面)可以看到如下图所示,新创建的core被添加进去
此时打开文件夹D:\solr-8.2.0\server\solr可以看到下面多了一个corename文件夹,就是我们刚才创建的core。创建core可以直接在文件夹操作,百度上可以收到。这里为了方便使用命令创建。
创建core文件夹方式二:
在solr目录中的\server\solr文件夹中创建一个文件夹,我这边文件夹名称叫做core(名称随便起)。然后把\server\solr\configsets\sample_techproducts_configs下面的conf文件夹复制到刚才创建的core文件夹下面。
完成上面操作之后启动solr,启动成功之后打开浏览器进入地址:http://127.0.0.1:8983/solr/#/
可以看见solr的管理界面,点击左侧的Core Admin,在新页面的name和instanceDir输入框中分别修改为刚才创建的文件夹名称(我这里是core),然后点击下面Add Core按钮即可成功创建core。
创建成功之后,会在你刚才创建的core文件夹下面生成一个文件夹和一个文件*.properties,这样即表示创建成功。
4、创建springboot项目和测试代码
(solr在项目中使用:此处连接的是core,方式二创建的;也可以连接方式一创建的corename)
pom.xml依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-solr</artifactId>
</dependency>
具体pom文件内容:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!--集成swagger-API-->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
<!--<scope>compile</scope>-->
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
<!--<scope>compile</scope>-->
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
.yml文件
server:
context-path: /
port: 8080
spring:
data:
solr:
#后面这个core就是文件夹的名称,这里也可以不用写这个,如果这里不指定core,那么在代码中使用的时候,就需要指定core。
#代码中可以指定core的地方有注释可以看
host: http://127.0.0.1:8983/solr/core
添加swagger2配置
创建文件夹:config\Swagger2Config.java
package com.dist.config;
import com.google.common.base.Predicate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author yangmin
* @date 2018/8/15
* @desc
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket adminApi(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("Admin API")
.forCodeGeneration(true)
.pathMapping("/")
.select()
.paths(paths())
.build()
.apiInfo(apiInfo())
.useDefaultResponseMessages(false);
}
private Predicate<String> paths(){
return PathSelectors.regex("^/(?!error).*$");
}
private ApiInfo apiInfo(){
Contact contact = new Contact("BaiDu", "controller://baidu.com", " aaa@qq.com");
return new ApiInfoBuilder()
.title("个人SpringBoot测试系统")
.description("开发API文档")
.contact(contact)
.version("1.0")
.build();
}
}
在resources/static下创建index.html
把以下内容复制,需要更改端口和项目地址
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>进入首页</h1>
<a href="http://localhost:8080/swagger-ui.html">进入swagger页面测试2</a>
</body>
</html>
controller下SolrController.java类
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
/**
* @author aaa@qq.com
* @data 2019/8/15 15:43
*/
@RequestMapping(value = "rest/solr")
@RestController
@Api(tags = {"SolrController"},description = "Solr 全文检索")
public class SolrController {
@Autowired
private SolrClient client;
@ApiOperation(value = "添加文档内容",notes = "向文档中添加域,必须有id域,域的名称必须在scheme.xml中定义",httpMethod = "GET")
@RequestMapping(value = "insert",method = RequestMethod.GET)
public Object validator(@ApiParam(value = "name") @RequestParam String name,
@ApiParam(value = "age") @RequestParam String age){
try {
String idStr = String.valueOf(System.currentTimeMillis());
SolrInputDocument document = new SolrInputDocument();
document.setField("id", idStr);
document.setField("name", name);
document.setField("age",age);
// 把文档对象写入索引库
client.add("core",document);//如果配置文件中没有指定core,这个方法的第一个参数就需要指定core名称,比如client.add("core", doc);
client.commit("core");//如果配置文件中没有指定core,这个方法的第一个参数就需要指定core名称client.commit("core");
return idStr;
} catch (Exception e) {
e.printStackTrace();
}
return "error";
}
@ApiOperation(value = "更新文档内容",notes = "更新文档内容,跟添加的区别是:id不能变,其他的可以变",httpMethod = "GET")
@RequestMapping(value = "updateDocument",method = RequestMethod.GET)
public Object updateDocument(@ApiParam(value = "idStr") @RequestParam String idStr,
@ApiParam(value = "name") @RequestParam String name,
@ApiParam(value = "age") @RequestParam String age) throws Exception{
// 创建一个文档对象, 向文档中添加域,必须有id域,域的名称必须在scheme.xml中定义
SolrInputDocument document = new SolrInputDocument();
document.setField("id", idStr);
document.setField("name", name);
document.setField("age",age);
// 把文档对象写入索引库
client.add("core",document);
// 提交
client.commit("core");
return document;
}
@ApiOperation(value = "全量或增量更新-数据库-没成功",notes = "java操作Solr的全量或增量更新,可以结合定时任务做定时全量或增量更新",httpMethod = "PUT")
@RequestMapping(value = "updateSolrData",method = RequestMethod.PUT)
public void updateSolrData() throws SolrServerException, IOException {
//创建一个查询对象
SolrQuery solrQuery = new SolrQuery();
//增量更新全部完成;注意这里entity的值为solr-data-config.xml里entity标签里的name值
final String SOLR_DELTA_PARAM = "/dataimport?command=delta-import&entity=order_info&clean=false&commit=true";
//全量更新全部完成
final String SOLR_FULL_PARAM = "/dataimport?command=full-import&entity=order_info&clean=true&commit=true";
//设置更新方式
solrQuery.setRequestHandler(SOLR_DELTA_PARAM);
// 执行查询
QueryResponse query = client.query("core",solrQuery);
//提交
client.commit("core");
}
@ApiOperation(value = "查询文档内容",notes = "查询文档内容",httpMethod = "GET")
@RequestMapping(value = "queryDocument",method = RequestMethod.GET)
public Object queryDocument(@ApiParam(value = "条件",defaultValue = "*:*") @RequestParam String condition,
@ApiParam(value = "连接文件夹 默 core",defaultValue = "core") @RequestParam String collection,
@ApiParam(value = "分页起始 默 1",defaultValue = "1") @RequestParam Integer pageStart,
@ApiParam(value = "分页结束 默 10",defaultValue = "10") @RequestParam Integer pageEnd) throws Exception {
// 创建一个查询条件
SolrQuery solrQuery = new SolrQuery();
// 设置查询条件
solrQuery.setQuery(condition);
// 设置分页
solrQuery.setStart(pageStart);
solrQuery.setRows(pageEnd);
// 执行查询
QueryResponse query = client.query(collection,solrQuery);
// 取查询结果
SolrDocumentList solrDocumentList = query.getResults();
System.out.println("总记录数:" + solrDocumentList.getNumFound());
for (SolrDocument sd : solrDocumentList) {
System.out.println(sd.get("id"));
System.out.println(sd.get("name"));
System.out.println(sd.get("age"));
}
return solrDocumentList;
}
@ApiOperation(value = "删除文档",notes = "删除文档",httpMethod = "DELETE")
@RequestMapping(value = "deteleDocument",method = RequestMethod.DELETE)
public Object deteleDocument(@ApiParam(value = "连接文件夹 默 core" ,defaultValue = "core") @RequestParam String collection,
@ApiParam(value = "idStr") @RequestParam String idStr) throws Exception {
// 根据id删除
UpdateResponse response = client.deleteById(collection, idStr);
// 根据条件删除
// httpSolrServer.deleteByQuery("");
// 提交
client.commit(collection);
return response;
}
}
然后启动该项目之后,进入http://localhost:8080/swagger-ui.html地址即可测试插入索引操作。插入成功之后,在solr管理界面的左下方有一个下拉框,选择你创建的core之后,再选择Query,往下拖到最后,点击Execute Query即可看见你刚才插入的索引。
其他问题:
刚才我们代码中插入了两个列(id、name),如果你想新增一个列,比如名称叫做age,然后启动项目,在插入索引,在solr管理平台中就看不见这个列名和列对应的值,原因是id、name这两列在solr中已经默认配置了。这种情况解决方式如下:
在你创建的core文件夹下面,打开conf文件夹,修改里面的managed-schema文件,修改地点如下:
132行后面增加(后面这几行随意位置增加就可以)
<field name="age" type="text_general" indexed="true" stored="true"/>
245行后面增加(后面这几行随意位置增加就可以)
<copyField source="age" dest="text"/>
修改完成之后重启solr,然后重新插入索引就可以成功操作了。
再测试:
SolrController.java
SolrInputDocument doc = new SolrInputDocument();
doc.setField("id", idStr); //id是默认配置,在你创建的core文件夹下面,打开conf文件夹,修改里面的managed-schema文件
doc.setField("name", content); // name是默认配置
doc.setField("age","23"); //自定义配置:在你创建的core文件夹下面,打开conf文件夹,修改里面的managed-schema文件,添加列 age
效果图:
5.solr连接数据库
此处测试配置的是oracle数据库,用的是方式一创建的corename文件包
1、添加驱动依赖包
添加驱动依赖包到如下目录:D:\solr-8.2.0\server\solr-webapp\webapp\WEB-INF\lib,我使用的oracle数据库,添加的是oracle驱动包,其他数据库添加相应驱动包ojdbc6.jar即可.
ojdbc6.jar下载地址:百度网盘
链接:https://pan.baidu.com/s/1OlNdA32xKGXUTbFzDTLSZQ 提取码:bgul
我添加的是ojdbc6.jar ,也可以添加其它版本如:ojdbc8.jar 或者是mysql的驱动jar等
2、配置dataimporthandler相关包
找到解压目录D:\solr-8.2.0\dist下的solr-dataimporthandler-8.2.0.jar包和solr-dataimporthandler-extras-8.2.0.jar两个包,也复制到D:\solr-8.2.0\server\solr-webapp\webapp\WEB-INF\lib目录下
3、添加数据库配置
找到目录D:\solr-8.2.0\server\solr\corename\conf下的solrconfig.xml并添加配置
<!-- 数据库配置 -->
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">data-config2.xml</str>
</lst>
</requestHandler>
4、创建data-config2.xml数据库连接配置
在目录D:\solr-8.2.0\server\solr\corename\conf下新建一个文件data-config2.xml并添加如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<dataConfig>
<dataSource
type="JdbcDataSource"
driver="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@localhost:1521:ORCL"
user="duke"
password="duke" />
<document>
<entity name="userEntity" query="select
ID,
NAME,
AGE
from t_user">
<field column="ID" name="user_id"/>
<field column="NAME" name="user_name"/>
<field column="AGE" name="user_age"/>
</entity>
</document>
</dataConfig>
其中数据库驱动,地址,表,用户名,密码和查询sql及字段都需要替换成你自己数据库的相应配置。field的culumn是数据库字段名称(查询出来后没有改别名下),name可以自定义,之后就是solr字段名称。
5、配置managed-schema
打开文件D:\solr-8.2.0\server\solr\corename\conf下的managed-schema,并在其中的标签里面添加如下配置:
<!--字段配置 对应userEntity -->
<field name="u_id" type="string" indexed="true" stored="true"/>
<field name="u_age" type="string" indexed="true" stored="true"/>
<field name="u_name" type="string" indexed="true" stored="true"/>
注意上面的name需要和前面userEntity配置的字段一样如:
6、验证是否成功
关掉之前打开的solr,重新启动。
cmd到D:\solr-8.2.0\bin目录下输入命令:
.\solr stop -all
.\solr start
待启动成功后,浏览器打开solr地址:
http://localhost:8983/solr/#/
- 导入库中数据
并按如下图选择好,其中的userEntity是你的表名。点击excute执行
导入完成后/或刷新页面。
- 查询导入的所有数据:
数据库连接配置完成!
6.solr 安装中文分词器
第一步:下载Ikanalyzer7.x分词器的jar 文件,下载地址:https://search.maven.org/search?q=com.github.magese
第二步:将下载的Ikanalyzer7.x.jar 文件拷贝至D:\solr-8.2.0\server\solr-webapp\webapp\WEB-INF\lib
第三步:在solrHome(solr数据源)文件夹,新建一个mycore文件夹,D:\solr-8.2.0\server\solr
第四步:把solr-8.1.0安装包中的example→example-DIH→solr→solr下的所有文件拷贝到solrHome(solr数据源)文件夹下的mycore文件夹中。
data是之后运行才会出来的,不用在意;
第五步:打开solrHome(solr数据源)文件夹中的mycore→conf→managed-schema文件,添加如下代码:
<!-- ik分词器 -->
<fieldType name="text_ik" class="solr.TextField">
<analyzer type="index">
<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false" conf="ik.conf"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="true" conf="ik.conf"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
第六步:重启solr ,输入:http://localhost:8983/solr/#/
7.Solr 界面功能
8.Solr全量更新/增量更新 -要配置数据库
1、全量更新
参考 5.solr连接数据库 配置,完成后:
记住:clean=true、commit=true
2、增量更新
1.首先要弄懂几个必要的属性,以及数据库建表事项,和dataimporter.properties 、data-config.xml里面的数据
<!-- transformer 格式转化:HTMLStripTransformer 索引中忽略HTML标签 --->
<!-- query:查询数据库表符合记录数据 --->
<!-- deltaQuery:增量索引查询主键ID ---> 注意这个只能返回ID字段
<!-- deltaImportQuery:增量索引查询导入数据 --->
<!-- deletedPkQuery:增量索引删除主键ID查询 ---> 注意这个只能返回ID字段
2.数据库配置注意事项
1.如果只涉及添加,与修改业务,那么数据库里只需额外有一个timpstamp字段
就可以了,默认值为当前系统时间,CURRENT_TIMESTAMP
2.如果还涉及删除业务,那么数据里就需额外再多添加一个字段isdelete,int类型的
用0,1来标识,此条记录是否被删除
3.dataimporter.properties
这个配置文件很重要,它是用来记录当前时间与上一次修改时间的,通过它能够找出,那些,新添加的,修改的,或删除的记录标识,此条记录是否被删除的记录
4.增量更新就是在全量更新的基础上加上一些配置,data-config.xml配置如下:
原配置:
<?xml version="1.0" encoding="UTF-8"?>
<dataConfig>
<dataSource
type="JdbcDataSource"
driver="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@localhost:1521:ORCL"
user="duke"
password="duke" />
<document>
<entity name="userEntity" query="select ID, NAME,AGE from t_user">
<!--查询的数据和数据库索引意义对应column 是查询的字段name 是solr索引对应的字段-->
<field column="ID" name="user_id"/>
<field column="NAME" name="user_name"/>
<field column="AGE" name="user_age"/>
</entity>
</document>
</dataConfig>
增量更新配置:
<?xml version="1.0" encoding="UTF-8"?>
<dataConfig>
<dataSource
type="JdbcDataSource"
driver="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@localhost:1521:ORCL"
user="duke"
password="duke" />
<document>
<entity name="userEntity" query="select ID,NAME,AGE from t_user"
deltaImportQuery ="select ID,NAME,AGE from t_user t where t.ID='${dataimport.delta.id}'"
deltaQuery = "select ID from t_user t where t.LASTUPDATETIME='${dataimport.last_index_time}'"
deletedPkQuery = "select ID from t_user">
<field column="ID" name="user_id"/>
<field column="NAME" name="user_name"/>
<field column="AGE" name="user_age"/>
<field column="LASTUPDATETIME" name="user_lastUpdateTime"/>
</entity>
</document>
</dataConfig>
5.通过后台管理手动增量更新和通过浏览器手动更新
在浏览器直接输入网站 :
http://127.0.0.1:8983/solr/#/corename/dataimport//dataimport
记住:clean=false、commit=true
9、solr 常见的自动更新方式
方式一:solr 自带的增量更新更新
**1.下载jar包 **
apache-solr-dataimportscheduler.jar、solr-dataimporthandler-7.0.0.jar、solr-dataimporthandler-extras-7.0.0.jar到 solr 项目的\WEB-INF\lib 目录下
apache-solr-dataimportscheduler 的下载地址:
链接:https://pan.baidu.com/s/110-F7DrO_4QEILWb6FFvzQ
提取码:yxm8
2. 修改web.xml文件配置监听,在servlet节点前增加:
<listener>
<listener-class>
org.apache.solr.handler.dataimport.scheduler.ApplicationListener
</listener-class>
</listener>
3.创建 dataimport.properties
在solrHome(存储solr数据的目录) 的目录下创建conf文件夹,创建 dataimport.properties 文件,内容根据实际情况修改,内容如下:
#################################################
# #
# dataimport scheduler properties #
# #
#################################################
# tosync or not to sync
# 1- active; anything else - inactive
# 这里的配置不用修改
syncEnabled=1
# which cores to schedule
# ina multi-core environment you can decide which cores you want syncronized
# leave empty or comment it out if using single-core deployment
# 修改成你所使用的core,我这里是我自定义的core:simple
syncCores=active
# solr server name or IP address
# [defaults to localhost if empty]
这个一般都是localhost不会变
server=localhost
# solr server port
# [defaults to 80 if empty]
# 安装solr的tomcat端口,如果你使用的是默认的端口,就不用改了,否则改成自己的端口就好了
port=8089
# application name/context
# [defaults to current ServletContextListener's context (app) name]
# 这里默认不改
webapp=solr
# URL params [mandatory]
# remainder of URL
# 这里改成下面的形式,solr同步数据时请求的链接
params=/dataimport?command=delta-import&clean=false&commit=true
# schedule interval
# number of minutes between two runs
# [defaults to 30 if empty]
#这里是设置定时任务的,单位是分钟,也就是多长时间你检测一次数据同步,根据项目需求修改
# 开始测试的时候为了方便看到效果,时间可以设置短一点
interval=1
# 重做索引的时间间隔,单位分钟,默认7200,即5天;
# 为空,为0,或者注释掉:表示永不重做索引
reBuildIndexInterval=7200
# 重做索引的参数
reBuildIndexParams=/select?qt=/dataimport&command=full-import&clean=true&commit=true
# 重做索引时间间隔的计时开始时间,第一次真正执行的时间=reBuildIndexBeginTime+reBuildIndexInterval*60*1000;
# 两种格式:2012-04-11 03:10:00 或者 03:10:00,后一种会自动补全日期部分为服务启动时的日期
reBuildIndexBeginTime=03:10:00
最后重启solr,在数据库中添加一条数据,静等一分钟,然后query。因为我们设置的是一分钟监听一次
方式二:使用windows 的计划任务进行增量更新
此方法,用于tomcat下部署的solr。
1.在windows开始那里搜索“计划任务”
2.创建一个新的计划任务
3.对计划任务的常规、触发器、操作 进行设置
4.启动计划任务
5.solr.bat 中的内容为(使用curl进行模拟请求,curl 需要下载):
curl http://localhost:8089/solr/active/dataimport?command=delta-import^&clean=false^&commit=true
6.windows 的curl 需要下载配置环境变量(和jdk的差不多),curl下载地址为:
链接:https://pan.baidu.com/s/1CArPu0vdPGGWRfo8KuZxWg 提取码:09ks (64位系统的,32位的可以去官网下载)
方式三:linux下使用 crontab实现增量更新
此方法,用于tomcat下部署的solr。
1.修改crontab的配置文件:vim /etc/crontab
crontab -e
*/5 * * * * curl http://localhost:8089/solr/active/dataimport?command=delta-import^&clean=false^&commit=true
注:5分钟执行一次增量索引(可根据实际业务调整时间)
************************本片文章阅读结束************************
作者:宇宙小神特别萌
个人博客:www.zhengjiaao.cn
Gitee 仓库:https://gitee.com/zhengjiaao
Github 仓库:https://github.com/zhengjiaao?tab=repositories
描述:喜欢文章的点赞收藏一下,关注不迷路,避免以后找不到哦,大家遇到问题下方可评论
************************本片文章阅读结束************************