【Java项目】社区论坛项目
项目现已经发布在 Gitee 平台,欢迎 Star 收藏!
项目地址:https://gitee.com/realBeBetter/community
HTTP | MDN (mozilla.org)
使用 MVC 获取 HTTP 请求参数
在使用 MVC 框架的时候,我们通常使用 HttpServletRequest
对象和 HttpServletResponse
对象,可以通过这两个类来封装请求和应答对象。
/**
* 获取Http请求的参数,并打印
* @param request
* @param response
*/
@RequestMapping("/http")
public void http(HttpServletRequest request, HttpServletResponse response) {
System.out.println(request.getMethod());
System.out.println(request.getServletPath());
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = request.getHeader(name);
System.out.println(name + " : " + value);
}
// 获取请求的参数
System.out.println(request.getParameter("Code"));
// 设置返回的数据类型
response.setContentType("text/html;charset=utf-8");
// 这样的写法,最终会自动关闭 () 里面的读写流,可以简化开发,避免手动close流
try (PrintWriter writer = response.getWriter()) {
writer.write("<h1>牛客网</h1>"); // 写标题
} catch (IOException e) {
e.printStackTrace();
}
}
三层架构
三层架构包括表现层、业务层、数据访问层。
MVC主要负责的是表现层的工作,MVC一共包括Model模型层、View视图层、Controller控制层。在三层架构中负责表现层的工作,MVC的核心组件是前端控制器 DispatcherServlet 。前端控制器在整个前端的业务中主要是为了解决以下步骤中的一些问题:
模板引擎
在开发中,如果想要给浏览器返回一个动态的网页,要使用模板引擎来完成这件事。模板引擎的作用主要是生成动态的HTML文件。最常见常用的就是ThymeLeaf模板引擎,优点在于倡导自然模板,即使用HTML文件为模板。它常用的语法包括:标准表达式、判断与循环(主要是循环判断集合元素)、模板的布局(主要包括如何复用网页中相同的部分的页面)。
处理GET请求
在处理用户的GET请求的时候,一般传参有两种方式。一种是使用参数名=参数值,另外一种是直接拼接在请求的URL中。两种方式分别适合于不同的场景。
第一种请求的方式,使用 Key = Value
的形式进行传参,多个参数之间使用 &
符号进行分隔。
// ①GET 请求的处理 /students?current=1&limit=20 当前是第一页,一共显示20条数据
@RequestMapping(path = "/students", method = RequestMethod.GET)
@ResponseBody
public String getStudents(
@RequestParam(name = "current", required = false, defaultValue = "1") int current,
@RequestParam(name = "limit", required = false, defaultValue = "1") int limit) {
System.out.println(current);
System.out.println(limit);
return "some students";
}
第二种方式,直接将请求参数写在URL中,并不明文记录参数的key值,直接使用参数的Value值。参数和参数之间使用 /
进行分隔。
// ②查询单个学生,id为123,不使用参数的时候,直接编排到url中时:/student/123
@RequestMapping(path = "/student/{id}", method = RequestMethod.GET)
@ResponseBody
public String getStudent(@PathVariable("id") int id) {
System.out.println(id);
return " a student";
}
处理POST请求
- 因为 GET 请求是直接将参数或者 Value 暴露在URL中,这样会导致数据安全性,另外 GET 请求的URL长度也是有限制的,所以GET请求并不可能满足开发中的所有需求。POST请求相比于GET请求则没有上面的问题,POST请求在进行的时候,使用的是请求体,避免了URL直接暴露请求参数的问题。
在POST请求中,一般用来处理表单数据发送。准备的表单如下:
注意其中的action值,这个路径就是表单提交之后返回的路径。比如现在action之后的路径是/community/Hello/student,那么表单提交之后返回的路径就是localhost:8080/community/Hello/student,我们在controller中间需要针对表单数据做出的处理,就要创建一个方法,映射到 /community/Hello/student 路径下。
<form method="post" action="/community/Hello/student">
<p>
姓名:<input type="text" name="name">
</p>
<p>
年龄:<input type="text" name="age">
</p>
<p>
<input type="submit" value="保存">
</p>
</form>
编写的controller部分,我们使用的是POST的请求方式。表单的数据,我们只需要在方法的参数传入和表单 name 属性值一致的参数,就可以获取表单输入的值。
/**
* 请求的参数填写的应该和html表单中的名字一致
* @param name
* @param age
* @return
*/
// POST 请求参数的处理
@RequestMapping(path = "/student", method = RequestMethod.POST)
@ResponseBody
public String saveStudent(String name, int age) {
System.out.println("name : " + name);
System.out.println("age : " + age);
return "success";
}
运行结果:
以上的过程,就代表了MVC对于POST请求处理的一个过程。
响应HTML数据
请求响应之后,我们需要给浏览器返回一个页面,这个时候我们需要创建一个html页面用于返回请求的响应结果。通常在controller中处理请求,我们使用的是ModelAndView,返回的ModelAndView对象。
// 响应HTML请求,直接使用ModelAndView完成
@RequestMapping(path = "/teacher", method = RequestMethod.GET)
public ModelAndView getTeacher() {
ModelAndView modelAndView = new ModelAndView();
// 添加的数据是一个键值对的形式,返回就是直接返回一个对象
modelAndView.addObject("name", "张三");
modelAndView.addObject("age", 30);
// 设置一个视图,规定返回的是哪一个html页面。html页面通常放在templates目录下
// 下面的路径名,则表示返回的页面是 templates 目录下的 demo 目录下的 view.html
modelAndView.setViewName("/demo/view");
return modelAndView;
}
在开发页面的时候,我们要想把model添加的Object对象值渲染到页面中,需要使用模板引擎,现在用的比较多的是ThymeLeaf模板引擎。在声明中加上ThymeLeaf的声明,使用ThymeLeaf提供的语法,之后就可以将文本渲染到页面中。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Teacher</title>
</head>
<body>
<p th:text="${name}"></p>
<p th:text="${age}"></p>
</body>
</html>
根据两者相结合,请求 http://localhost:8080/community/Hello/teacher 应该返回的是 name 和 age 的数据。
此外,还有另外一种响应的方式。这个方式直接返回一个页面,不使用 ModelAndView,而是在参数列表中使用 Model 对象,将 Model 对象传入,向 Model 中添加数据,最后返回的 String 就是一个网页的页面文件。
这种方式只是使用的 Model 对象,相比于上一种方式,只是使用的对象不同,添加参数的方法不同。其余的没有区别。
// 响应HTML请求,使用简化的方式,直接返回String
@RequestMapping(path = "/school", method = RequestMethod.GET)
public String getSchool(Model model) {
// 这里直接使用 Model 对象添加数据,完成数据的添加
model.addAttribute(" name : ", "HUT");
model.addAttribute("age: ", "60");
return "/demo/view";
}
编译之后,请求路径 http://localhost:8080/community/Hello/school ,运行的结果:
响应 JSON 请求数据
利用 JSON 字符串,可以将Java对象很方便地转换为其他语言的对象,一般方便转换为JS对象。
// 响应 JSON 数据,处理异步请求
// Java 对象 -> JSON 字符串 -> JS 对象,JSON 只是起到一个中间值的作用,方便将 Java 对象转换为其他语言对象
@RequestMapping(path = "/emp", method = RequestMethod.GET)
@ResponseBody // 不加这个注解,会认为返回一个html页面
public Map<String, Object> getEmp() {
Map<String, Object> map = new HashMap<>();
map.put("name", "张三");
map.put("age", 20);
map.put("salary", 8000.00);
return map;
}
编译之后,请求 http://localhost:8080/community/Hello/emp 之后,可以得到 JSON 字符串的结果:
这样就成功返回了一个 JSON 格式的字符串。但是这个字符串中,仅有一个对象。要返回多个对象,返回值应该设置为一个 List 集合,这样就可以返回多个对象。
// 响应 JSON 数据,处理异步请求
// Java 对象 -> JSON 字符串 -> JS 对象,JSON 只是起到一个中间值的作用,方便将 Java 对象转换为其他语言对象
@RequestMapping(path = "/emps", method = RequestMethod.GET)
@ResponseBody // 不加这个注解,会认为返回一个html页面
public List<Map<String, Object>> getEmps() {
List<Map<String, Object>> list = new ArrayList<>();
Map<String, Object> map = new HashMap<>();
map.put("name", "张三");
map.put("age", 20);
map.put("salary", 8000.00);
list.add(map);
map = new HashMap<>();
map.put("name", "李四");
map.put("age", 30);
map.put("salary", 9000.00);
list.add(map);
return list;
}
运行结果:
浏览器向服务器传参有两种方式,一是在通过get请求,在路径后加问号携带参数,如/xxx?id=1。另一种是通过post请求,在request请求体中携带表单中的参数,这种参数在路径上是看不到的。这两种方式所传的参数,在服务端都可以通过request.getParameter(参数名)这样的方式来获取。而@RequestParam注解,就相当于是request.getParameter(),是从request对象中获取参数的。有时,我们也愿意利用请求路径本身来传参,即将参数拼到路径里,如/xxx/1,这里的1就是参数,那么在解析路径的时候,也是能获取到这个参数的。而@PathVarible就是解析路径,从中获得对应级次的参数。
ModelAndView对象需要主动进行实例化,而Model对象只需要写在参数列表中,MVC框架会自动实例化Model对象。Model对象你可以放任何数据,但是它的作用正如它的名字,主要是用于放模型数据的。ModelAndView对象既可以存放模型数据,也可以存储模板路径。
Spring MVC主要是用来处理请求:
- 创建Controller类,在 Controller 类上标注 @Controller 注解,将类注入到 IoC 容器。
- 在类名上标注 @RequestMapping 注解,表示请求访问的上级 URL 路径
- 在方法上标注 @RequestMapping 注解,表示请求访问的下级 URL 路径。如果标注了 @ResponseBody 注解,表示该方法返回的对象会被写入到 Response 的 Body 数据区;如果不标注,则会认为返回的是一个 html 页面。
- Spring MVC 可以支持的返回方式:ModelAndView, Model, ModelMap, Map,View, String, void
- Spring MVC 主要用来响应 GET 请求或者 POST 请求,HTML 请求 以及 JSON 请求。
salt值_百度百科 (baidu.com)
使用Mybatis查询流程
①引入Mybatis依赖以及Spring整合Mybatis、MySQL连接的依赖:
<!--Mybatis整合Spring的依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!-- 添加MySQL依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<!-- 添加JDBC依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
②构建实体对应类:Entity.User类,并添加对应的所有Get和Set方法、toString()
方法
public class User {
private int id;
private String username;
private String password;
private String salt;
private String email;
private int type;
private int status;
private int activationCode;
private String headerUrl;
private Date createTime;
}
③创建相应的dao层的接口,编写需要使用的查询方法,等待使用xml文件将接口中的方法变为可用状态。接口使用@Mapper注解标注,可以让Mybatis识别该接口是一个dao接口。除了使用@Mapper注解,还可以使用@Repository注解来标注该接口,实现的效果是一致的。
@Mapper
public interface UserMapper {
User selectById(int id);
User selectByName(String username);
User selectByEmail(String email);
int insertUser(User user);
int updateStatus(int id, int status);
int updateHeader(int id, String headerUrl);
int updatePassword(int id, String password);
}
④在resources文件夹下,新建mapper文件夹,添加对应的Mapper文件:UserMapper.xml
在文件头部添加映射文件头,根据相应的方法,编写相应的标签,包含不同的sql语句。
<?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.nowcoder.community.dao.UserMapper">
<sql id="selectFields">
id, username, password, salt, email, type, status, activation_code, header_url, create_time
</sql>
<sql id="insertFields">
username, password, salt, email, type, status, activation_code, header_url, create_time
</sql>
<select id="selectById" resultType="User">
select <include refid="selectFields"></include>
from user
where id = #{id}
</select>
<select id="selectByName" resultType="User">
select <include refid="selectFields"></include>
from user
where username = #{username}
</select>
<select id="selectByEmail" resultType="User">
select <include refid="selectFields"></include>
from user
where email = #{email}
</select>
<insert id="insertUser" parameterType="User" keyProperty="id">
insert into user(<include refid="insertFields"></include>)
values(#{username}, #{password}, #{salt}, #{email}, #{type}, #{status}, #{activationCode}, #{headerUrl}, #{createTime})
</insert>
<update id="updateStatus">
update user set status = #{status} where id = #{id}
</update>
<update id="updateHeader">
update user set header_url = #{headerUrl} where id = #{id}
</update>
<update id="updatePassword">
update user set password = #{password} where id = #{id}
</update>
</mapper>
其中的<sql id="xxx">
标签,可以在很多语句内部进行填充使用,方便后期的维护以及语句维护。使用的时候,使用<include refid="xxx">
可以替换响应位置为<sql>
标签内的内容。
⑤编写完Mapper.xml文件中的sql语句之后,我们就可以通过接口的实例化对象来调用相关的方法。方法执行的时候,会自动将sql语句映射到对应的方法上,完成对应的数据操作。
@Autowired
private UserMapper userMapper;
@Test
public void testSelectUser() {
User user = userMapper.selectById(101);
System.out.println(user);
System.out.println(userMapper.selectByName("liubei"));
System.out.println(userMapper.selectByEmail("nowcoder101@sina.com"));
}
编写完毕之后,调用相关的方法,会返回相关的语句执行结果。最后查询数据库也可以直接获得语句运行之后的状态。
总结Mybatis编写的顺序:
- 先编写好数据库中数据表对应的实体类放到 Entity 包中,编写好对应的 Get 和 Set 以及 toString 方法
- 在 dao 层编写 Mapper 接口,标注
@Mapper
或者@Repository
注解,接口内部编写需要的一些方法声明 - 在 resources 目录下的 mapper 目录中新建一个 mapper.xml 文件,在文件中添加 mapper 映射,设置 namespace 为 dao 下对应的 Mapper 接口,然后编写相关方法对应的 sql 语句
- 编写语句取值的时候,使用 #{parameterName} 获取方法声明中的参数,以 preparedStatement 的形式添加进 sql 语句中,完成对数据库的相关操作。
HTTP 响应代码 - HTTP | MDN (mozilla.org)
重定向是一个能够以非常低的耦合状态实现页面的跳转。
服务端断点调试技巧:在程序中打断点,Debug 启动服务之后,查看值的状态,调查错误信息。
客户端断点调试技巧:在前端代码中打断点,之后使用浏览器的开发工具进行调试。
设置日志级别,并将日志输出到不同的终端:
Spring Boot内置的日志:Chapter 2: Architecture (qos.ch)
package org.slf4j; public interface Logger { // Printing methods: 级别从低到高排名 public void trace(String message); public void debug(String message); public void info(String message); public void warn(String message); public void error(String message); }
设置不同的日志级别,使用相应的配置文件就可以指定将日志文件输出到指定文件夹。
public class LoggerTests {
private static final Logger logger = LoggerFactory.getLogger(LoggerTests.class);
@Test
public void testLogger() {
System.out.println(logger.getName());
// 日志输出打印
logger.debug("debug log");
logger.info("info log");
logger.warn("warn log");
logger.error("error log");
}
}
在配置文件中进行相关的配置即可:
logging:
# 日志级别设置
level:
com.nowcoder.community:
debug
# 文件路径。要设置文件指定名称只使用name即可
file:
path: D:/Java/IdeaProjects/community/log
name: D:/Java/IdeaProjects/community/log/community.log
logback:
rollingpolicy:
max-file-size: 5MB
file-name-pattern: D:/Java/IdeaProjects/community/log/error/log-error-%d{yyyy-MM-dd}.%i.log
max-history: 30
kaptcha谷歌验证码工具 - 勤俭的搬运工 - 博客园 (cnblogs.com)
Ajax - Web 开发者指南 | MDN (mozilla.org)
- 使用 jQuery 发送 AJAX 请求
此处使用 Ajax 发送 POST 请求,编写 post 方法,传入三个参数:访问 URL 路径、JSON 字符串值、回调函数。
function send() {
$.post(
"/community/Hello/ajax",
{"age":20, "name":"张三"},
function (data) {
/*回调函数*/
console.log(typeof (data));
console.log(data);
data = $.parseJSON(data);
console.log(typeof (data));
console.log(data.code);
console.log(data.message);
}
)
}
最终调用的时候,点击相关按钮,调用该函数,会实现异步发送 post 请求。然后在 Controller 层进行一些处理:
/**
* 发送 ajax 请求实例
*/
@RequestMapping(path = "/ajax", method = RequestMethod.POST)
@ResponseBody
public String testAjax(String name, int age) {
System.out.println(name);
System.out.println(age);
return CommunityUtil.getJSONString(0, "操作成功!");
}
- 实践
- 采用 AJAX 请求,实现发布帖子的功能
要实现发布帖子,首先需要在数据库的层面上实现发布的功能。在 UserMapper 中添加抽象方法:
/**
* 插入新的讨论帖子
* @param discussPost 讨论帖对象
* @return 插入的提示符
*/
int insertDiscussPost(DiscussPost discussPost);
在 xml 文件中添加对应的方法映射:
<insert id="insertDiscussPost" parameterType="discussPost">
insert into discuss_post(<include refid="insertFields"></include>)
values(#{userId}, #{title}, #{content}, #{type}, #{status}, #{createTime}, #{commentCount}, #{score})
</insert>
之后在 web 页面中实现 ajax 异步发布功能:
$(function(){
$("#publishBtn").click(publish);
});
function publish() {
$("#publishModal").modal("hide");
// 获取标题和正文
let title = $("#recipient-name").val();
let content = $("#message-text").val();
// 发送异步请求, POST 请求
$.post(
CONTEXT_PATH + "/discuss/add",
{"title": title, "content": content},
function (data) {
data = $.parseJSON(data);
// 在提示框中显示提示消息
$("#hintBody").text(data.message);
// 显示提示框
$("#hintModal").modal("show");
// 显示提示框之后 2s 隐藏
setTimeout(function(){
$("#hintModal").modal("hide");
// 刷新页面,判断是否成功
if (data.code === 0) {
window.location.reload();
}
}, 2000);
}
);
}
点击发布按钮默认触发 publish()
方法,然后在其中编写 ajax 中的 post 方法,回调方法中设置弹出框的显示逻辑。
在 Service 层处理 post 发布之后的存储逻辑,要针对帖子进行过滤操作:
/**
* 添加讨论帖
*
* @param discussPost 讨论帖
* @return 插入结果
*/
public int addDiscussPost(DiscussPost discussPost) {
if (discussPost == null) {
throw new IllegalArgumentException("参数不能为空!");
}
// 转义 HTML 标签
discussPost.setTitle(HtmlUtils.htmlEscape(discussPost.getTitle()));
discussPost.setContent(HtmlUtils.htmlEscape(discussPost.getContent()));
// title 以及 content 需要进行敏感词过滤
discussPost.setTitle(sensitiveFilter.filter(discussPost.getTitle()));
discussPost.setContent(sensitiveFilter.filter(discussPost.getContent()));
return discussPostMapper.insertDiscussPost(discussPost);
}
在 Controller 处理用户登录检查以及讨论帖的数据库插入操作:
@RequestMapping(path = "/add", method = RequestMethod.POST)
@ResponseBody
public String addDiscussPost(String title, String content) {
User user = hostHolder.getUser();
if (user == null) {
return CommunityUtil.getJSONString(403, "您还没有登录哦~");
}
// 构造讨论贴
DiscussPost discussPost = new DiscussPost();
discussPost.setUserId(user.getId());
discussPost.setTitle(title);
discussPost.setContent(content);
discussPost.setCreateTime(new Date());
discussPostService.addDiscussPost(discussPost);
// 异常情况统一处理
return CommunityUtil.getJSONString(0, "发布成功!");
}
Apache Downloads
Kafka 主要被用来充当消息队列,可以处理 TB 级别的数据量。原因是对比于传统的阻塞队列, Kafka 可以将消息进行持久化操作,存储到硬盘中。虽然内存的速度普遍要快于硬盘,但是因为 Kafka 对于持久化消息的特殊处理,持久化的消息在硬盘上是有序的。所以 Kafka 对于硬盘上的持久化的消息可以顺序读写,相比于对内存的随机读写,硬盘的顺序读写速度并不比内存相差很大。
下载完成之后,对压缩文件进行解压缩处理。
打开 config 目录下的 zookeeper.properties 文件以及 server.properties 文件,分别修改其中的属性:
dataDir=D:/Java/IdeaProjects/community/log/zookeeper
log.dirs=D:/Java/IdeaProjects/community/log/kafka-logs
修改的目的是修改其日志以及数据文件的存放目录。
配置完成之后,进入到 kafka_2.13-3.0.0 目录下,启动 cmd 窗口,指定以 zookeeper.properties 配置文件启动 zookeeper 服务:
bin\windows\zookeeper-server-start.bat config\zookeeper.properties
启动完成会有相应的提示。
启动 kafka 的方式也类似,进入到安装目录之后,输入 bin\windows\kafka-server-start.bat config\server.properties 启动 kafka ,之后报错:提示 ERROR Disk error while writing log start offsets checkpoint in directory 。查询解决方法,两种可能:① jdk 版本过低(安装的 14 ,原因除外);② 更换 Kafka 版本,按照提示,更换成 kafka_2.12-2.8.1 版本。下载链接:https://archive.apache.org/dist/kafka/2.8.1/kafka_2.12-2.8.1.tgz ,下载之后,修改两个配置文件,重新启动 zookeeper 以及 Kafka ,启动成功。
成功启动在末尾可以看到 started 的字样,启动命令:
bin\windows\kafka-server-start.bat config\server.properties
之后再启动一个 cmd 窗口,进入 Kafka 安装目录下的 \bin\windows 目录,输入
kafka-topics.bat --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test
参数意义:–replication-factor 指的是主题的副本数量,副本位于集群中不同的 Broker 上,也就是该参数的大小不能超过 Broker 的数量,否则会创建失败。 partitions 指的是主题分区数量, Kafka 通过分区策略,将不同的分区分配在一个集群的 Broker 上,通常会是不同的 Broker 。分区数量越多,可以提高 Kafka 的并发执行能力,提高一定的吞吐量。
创建一个名为 test 的主题,之后可以使用 --list
命令指定查看哪一台机器上的主题。
创建主题,就相当于创建了一个消息队列。此时我们就可以向这个 test 的主题中发送生产者消息了。发送消息使用指令:
kafka-console-producer.bat --broker-list localhost:9092 --topic test
之后会出现 >
符号,我们直接输入需要发送的消息即可:
消息队列中的消息要想消费,也需要相应的操作,我们再启动一个窗口,输入指令消费消息队列中的消息:
kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic test --from-beginning
在此之后,我们在生产者中输入新的消息,消费者窗口就能同步查看到同样的消息。测试发现,目前只能直接发送英文消息,输入中文会造成编码错误,源于 GBK 字符集与 Unicode 字符集之间的转换问题。
其中出现问题, zk 启动报错: Unexpected exception exiting abnormally java.io.eofexception 。解决方法:找到 zoo.conf 中配置的 dataDir 和 dataLogDir 路径,然后删除两个文件夹下的 version -2 文件夹,重新启动即可。
Elasticsearch:官方分布式搜索和分析引擎 | Elastic
推荐下载 6.4.3 版本:Elasticsearch 6.4.3 | Elastic
一、下载和安装
根据提供的网站下载好对应的系统版本,解压缩即可。
二、配置
进入到安装目录下的 config 目录中,打开 elasticsearch.yml 文件,配置其中的集群名字、data 数据文件夹、 logs 文件夹:
cluster.name: community
path.data: D:\Java\IdeaProjects\community\log\Elaticsearch-6.4.3\data
path.logs: D:\Java\IdeaProjects\community\log\Elaticsearch-6.4.3\logs
ES 在启动的时候,默认申请 1G 的内存空间,我们可以指定分配的内存大小,打开 config 目录下的 jvm.options 文件,修改其中的参数:
第一个调整的是初始内存,第二个调整的是最大内存。分别调小一点就可以。
三、分词插件
由于中文词义比较复杂,搜索的时候分词并不像英文一样以空格为分隔。所以需要将中文检索词进行分词,需要下载一个分词插件。分词插件为 IK 分词插件。
下载之后,将文件解压缩到 ES 安装目录的 plugins 目录中,在此新建一个 ik 文件夹,解压缩至此即可。注意需要新建一个文件夹,之后再将插件解压缩,否则会出现错误。
IK 分词插件是根据 config 目录下的 dic 词典文件进行分词操作的,但是因为新词语的出现,原有的词典并不一定能满足所有需要。用户可以在 config 目录下的 IKAnalyzer.cfg.xml 文件中自己配置一些新的词语。
之后,我们需要安装 postman 软件,使用 postman 软件对其中的一些功能进行测试。
启动 ElasticSearch 的时候,出现了一些错误。
An exception was caught and reported. Message: access denied ("java.lang.RuntimePermission" "accessClassInPackage.jdk.internal.vm.annotation") at _unknown_
查找原因得知,错误发生的原因是 jdk 版本过高,导致 ElasticSearch 不兼容。之后重新装上 JDK 11,配置好新的 Java 环境变量,重新启动 ES ,最后启动成功。
启动成功的运行状态:
可以从中看到,ES 默认占用端口 9200 。
由于已经调整 ES 占用内存为默认的 1G 内存,打开任务管理器可以看到非常高的内存占用。
现在就可以直接使用命令行查看 ES 的一些状态了。
D:\Java\elasticsearch-6.4.3\bin>
进入 bin 目录下,输入命令行查看 ES 的健康状态。
curl -X GET "localhost:9200/_cat/health?v"
输入命令行查看 ES 的节点状态。
curl -X GET "localhost:9200/_cat/nodes?v"
输入命令行查看 ES 的索引。
curl -X GET "localhost:9200/_cat/indices?v"
这种状态表示没有索引存在。
创建索引命令:
curl -X PUT "localhost:9200/test"
表示在 localhost:9200 这台机器上创建一个名为 test 的索引。
再次输入命令查看索引,可以看到:
输入指令删除索引。
curl -X DELETE "localhost:9200/test"
再次查询就不存在 test 索引了。
同样,我们可以利用 postman 发送不同的 web 请求,包括 GET 、PUT 、 DELETE 等。测试的方法以及结果与命令行的结果一样,只是测试流程会更加简单。
发送创建索引的请求:
发送删除索引的请求:
我们现在需要往索引中插入数据,指定提交的路径之后,我们添加 JSON 格式的字符串即可。
PUT 请求的路径格式:【localhost:9200/test/_doc/1】端口号 / 索引名 / 类型 / ID ,之后我们在 Body 中添加 JSON 格式的字符串,添加完毕之后发送请求,可以得到:
{
"_index": "test",
"_type": "_doc",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
查询该数据,只需要将请求类型修改为 GET 即可,一样的请求路径,得到:
删除数据与查询一致,只需要将请求方式修改为 DELETE 即可,返回的结果为删除操作成功与否的提示。
至此,对于 ES 的操作,我们可以大致了解到步骤:创建索引,之后向索引中添加索引,并且针对这些索引中的内容进行增删改查。
我们可以创建更多的 _doc 类型的数据,添加更多的文字信息。使用命令可以进行搜索指定关键字、指定位置(标题/内容)。
使用查询命令,可以查到标题中有关键字的数据:
localhost:9200/test/_search?q=title:互联网
我们可以从查询结果中的 hits 列中看到命中的数据:
如果我们既要针对标题也要针对内容进行匹配,就需要更复杂的操作。在 Body 中提交 JSON 字符串数据,发送一些更加复杂的请求命令。
{
"query":{
"multi_match":{
"query":"互联网",
"fields":["title","content"]
}
}
}
这样的请求方式,我们就既可以查到标题中有关键字的数据,也可以查到内容中有关键字的数据。
Maven Repository: org.springframework.boot » spring-boot-starter-data-elasticsearch » 2.1.5.RELEASE (mvnrepository.com) 找到。
重新配置版本依赖之后,依然会出现 org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘discussPostRepository’ 错误的提示。更换 Spring Boot 的版本为 2.1.5.RELEASE 版本。之后出现插件加载错误:Cannot resolve plugin org.apache.maven.plugins:maven-site-plugin:3.7.1 提示。解决办法:向 pom.xml 文件中添加依赖:
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</dependency>
重新下载一下指定的依赖。
之后还是发生了依赖错误的问题,最终降级 Spring Boot 版本,将 Spring Boot 版本降级到 2.1.5.RELEASE 得以解决。降级的时候不只修改 <version>
,还需要删除可能产生冲突的依赖。最后直接删除 repository 文件夹得以解决。
之后测试的时候又出现错误:mapper [createTime] of different type, current_type [long], merged_type [date] 导致整个服务不能正常启动。查看提示发现是类型不匹配导致的,但是检查发现实体类代码不存在错误。clean install 清除缓存都尝试了之后还是不行,最后重新启动 ES 服务器,通过 postman 将已经存在的 ES 索引删除,之后重新进行测试,成功!
原因:ES 中的索引和映射发生了改变
解决:删除 ES 中已经存在的索引和映射,重新导入
至此,ES 导致的错误全部解决了。
Spring Cloud 从入门到精通 | 程序猿DD (didispace.com)
社区 Spring Security 从入门到进阶系列教程 | Spring For All (spring4all.com)
可以从 Spring4all 社区进行相关资料的查看。
Spring Security 一旦引入,即刻生效,会对整个系统的安全进行管理。
重定向和转发
重定向:
服务器原先的组件推荐浏览器重新去请求新的组件,浏览器开始新的请求,避免组件之间产生直接耦合。相当于是两次请求,且两者之间没有直接的关联,所以在传递参数的时候略微复杂。
弊端在于 A 组件与 B 组件之间不方便传数据,要想实现数据之间的传输,需要使用 Cookie 和 Session 的方式来完成组件之间的传参。
转发:
浏览器向服务器发出请求,最终请求完成需要 A 和 B 组件一起协作完成,那么 A 组件会向 B 组件发起调用,会造成组件之间的耦合。整个过程是在一个请求之间完成的,在组件之间传参很容易实现。