项目介绍:使用技术springboot+thymeleaf+redis+kafka+elasticsearch+git
前期总结:
目录
工具类介绍
1.Page
package com.nowcoder.community.entity;
/**
* 封装分页相关的信息.
*/
public class Page {
// 当前页码
private int current = 1;
// 显示上限
private int limit = 10;
// 数据总数(用于计算总页数)
private int rows;
// 查询路径(用于复用分页链接)
private String path;
public int getCurrent() {
return current;
}
public void setCurrent(int current) {
if (current >= 1) {
this.current = current;
}
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
if (limit >= 1 && limit <= 100) {
this.limit = limit;
}
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
if (rows >= 0) {
this.rows = rows;
}
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
/**
* 获取当前页的起始行
*
* @return
*/
public int getOffset() {
// current * limit - limit
return (current - 1) * limit;
}
/**
* 获取总页数
*
* @return
*/
public int getTotal() {
// rows / limit [+1]
if (rows % limit == 0) {
return rows / limit;
} else {
return rows / limit + 1;
}
}
/**
* 获取起始页码
*
* @return
*/
public int getFrom() {
int from = current - 2;
return from < 1 ? 1 : from;
}
/**
* 获取结束页码
*
* @return
*/
public int getTo() {
int to = current + 2;
int total = getTotal();
return to > total ? total : to;
}
}
总结:current代表当前页号,limit中文译为限制,也就是要限制显示几条数据,固定10条,path中定义的就是当前controller方法的映射地址,然后其他属性看注解,这个项目的分页要求显示五页也就是要求只显示五个页号
实现是通过getFrom和getTo两个方法实现,其余方法作用可以看注解。
2.发送邮件MailClient
package com.nowcoder.community.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
@Component
public class MailClient {
private static final Logger logger= LoggerFactory.getLogger(MailClient.class);
@Autowired
private JavaMailSender mailSender;
@Value("${spring.mail.username}")
private String from;
/**
*
* @param to 接受者
* @param subject 主题
* @param content 内容
*/
public void sendEmail(String to,String subject,String content) {
try {
MimeMessage message=mailSender.createMimeMessage();
MimeMessageHelper helper=new MimeMessageHelper(message);
helper.setFrom(from);
helper.setSubject(subject);
helper.setTo(to);
helper.setText(content,true);//第二个参数是设置邮件内容支持html
mailSender.send(helper.getMimeMessage());
} catch (MessagingException e) {
logger.error("发送邮件失败"+e.getMessage());
}
}
}
通过springboot发邮件主要就是对MimeMessage进行构造
配置参数
#视频写的是qq密码,这写的是授权码
spring.mail.password=xdxnosdevhtsecgf
spring.mail.protocol=smtp
mail.smtp.auth=true
spring.mail.properties.mail.smtp.ssl.enable=true
总结:依次是授权码,协议,身份验证(true代表身份验证,false代表不身份验证),是否用启用加密传送的协议验证项
3.HostHolder
package com.nowcoder.community.util;
import com.nowcoder.community.entity.User;
import org.springframework.stereotype.Component;
/**
* 这个类用来代替session,每一个浏览器访问服务器都会创建一个线程,不能用公共类,使用threadlocal来实现线程隔离
*/
@Component
public class HostHolder {
private ThreadLocal<User> users=new ThreadLocal<>();
public void setUser(User user) {
users.set(user);
}
public User getUser(){
return users.get();
}
public void clear(){
users.remove();
}
}
起到一个与session类似的作用,主要是使用了ThreadLocal本地线程
一、首页模块
代码:HomeController、DiscussPostService、UserService、LikeService、CommunityConstant、Page
模块介绍:首次编写首页模块功能为,将帖子信息从数据库查出来并返回给客户端,随后有增加点赞功能,还有一个错误跳转页面,用到了分页。
package com.nowcoder.community.controller;
import com.nowcoder.community.entity.DiscussPost;
import com.nowcoder.community.entity.Page;
import com.nowcoder.community.entity.User;
import com.nowcoder.community.service.DiscussPostService;
import com.nowcoder.community.service.LikeService;
import com.nowcoder.community.service.UserService;
import com.nowcoder.community.util.CommunityConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
public class HomeController implements CommunityConstant {
@Autowired
private DiscussPostService discussPostService;
@Autowired
private UserService userService;
@Autowired
private LikeService likeService;
@RequestMapping(path = "/index", method = RequestMethod.GET)
public String getIndexPage(Model model, Page page) {
// 方法调用钱,SpringMVC会自动实例化Model和Page,并将Page注入Model.
// 所以,在thymeleaf中可以直接访问Page对象中的数据.
page.setRows(discussPostService.findDiscussPostRows(0));
page.setPath("/index");
List<DiscussPost> list = discussPostService.findDiscussPosts(0, page.getOffset(), page.getLimit());
List<Map<String, Object>> discussPosts = new ArrayList<>();
if (list != null) {
for (DiscussPost post : list) {
Map<String, Object> map = new HashMap<>();
map.put("post", post);
User user = userService.findUserById(post.getUserId());
map.put("user", user);
long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, post.getId());
map.put("likeCount", likeCount);
discussPosts.add(map);
}
}
model.addAttribute("discussPosts", discussPosts);
return "/index";
}
@RequestMapping(path = "/error", method = RequestMethod.GET)
public String getErrorPage() {
return "/error/500";
}
}
总结:Page是自己定义的一个分页工具类,当把Page对象或者其他任何对象和model作为controller方法参数时,对象自动注入到model对象当中,客户端可以直接调用并使用,其中返回的list也是一个值得注意的点,之前做的项目list中封装的只是一个实体类对象,而这个项目返回的list大多封装是多个实体类,使用List<Map<String,Object>>实现,例如当前getIndexPage方法,根据这个页面可以判断当前方法要求返回帖子和当前帖子的user信息,所以当把List<DiscussPost>查出来之后还要遍历这个集合,声明一个List<Map<String,Object>>集合,遍历过程中,会根据每一个DiscussPost中的userId将对应的user信息查询出来,然后将此时的DiscussPost对象和对应的User对象存放进map集合当中然后将map放入到List<Map<String,Object>>中去,直到将List<DiscussPost>遍历完,最后将List<Map<String,Object>>注入到model中返回客户端
前端代码(帖子列表)
<!-- 帖子列表 -->
<ul class="list-unstyled">
<li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${discussPosts}">
<a th:href="@{|/user/profile/${map.user.id}|}">
<img th:src="${map.user.headerUrl}" class="mr-4 rounded-circle" alt="用户头像" style="width:50px;height:50px;">
</a>
<div class="media-body">
<h6 class="mt-0 mb-3">
<a th:href="@{|/discuss/detail/${map.post.id}|}" th:utext="${map.post.title}">备战春招,面试刷题跟他复习,一个月全搞定!</a>
<span class="badge badge-secondary bg-primary" th:if="${map.post.type==1}">置顶</span>
<span class="badge badge-secondary bg-danger" th:if="${map.post.status==1}">精华</span>
</h6>
<div class="text-muted font-size-12">
<u class="mr-3" th:utext="${map.user.username}">寒江雪</u> 发布于 <b th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</b>
<ul class="d-inline float-right">
<li class="d-inline ml-2">赞 <span th:text="${map.likeCount}">11</span></li>
<li class="d-inline ml-2">|</li>
<li class="d-inline ml-2">回帖 <span th:text="${map.post.commentCount}">7</span></li>
</ul>
</div&g