- 1 需求与功能
- 1.1 用户故事
- 1.2 功能规划
- 2 页面设计与开发
- 2.1 页面设计
- 2.2 页面开发
- 2.3 插件集成
- 3 框架搭建
- 3.1 构建与配置
- 3.2 异常处理
- 3.3 日志处理
- 3.4 页面处理
- 4 设计与规范
- 4.1 实体设计
- 4.2 应用分层
- 4.3 命名约定
- 5 后台管理
- 5.1 登录
- 5.2 分类管理
- 5.3 标签管理
- 5.4 博客管理
- 6 前端展示
- 6.1 首页展示
- 6.2 分类页
- 6.3 标签页
- 6.4 归档页
- 6.5 关于我
- 部署到服务器
学习链接: SpringBoot开发一个小而美的个人博客。
- 技术组合:
- 后端:SpringBoot + JPA + thymeleaf模板
- 数据库:MySQL
- 前端UI:Semantic UI框架
- 工作与环境:
- IDEA
- Maven3
- JDK8
- Axure RP 8
- 课程内容模块
- 需求分析和功能规划
- 页面设计与开发
- 技术框架搭建
- 后端管理功能实现
- 前端管理功能实现
- 你能学到什么
- 基于SpringBoot完整全栈式的开发套路
- Semantic UI框架的使用
- 一套博客系统的源代码与设计
用户故事是敏捷开发的一种方法,从用户的角度描述需求,用户故事模板:
- 作为一个(某个角色)使用者,我可以做(某个功能)事情,如此可以有某个(商业价值)的好处。
- 角色、功能、商业价值
举例:
- 作为一个招聘网站注册用户,我想查看最近三天发布的招聘信息,以便于了解最新的招聘信息。
确定角色:
- 普通访客、管理员(我)
访客实现的功能:
管理员实现的功能:
使用Axure RP 8进行原型设计,软件下载安装可自行百度。根据上面的功能规划图可以总结出需要设计的页面。
- 前端展示页面:首页、详情页、分类、标签、归档、关于我
- 后端管理页面:模板页
Semantic是快速构建前端页面的框架。我们使用WebStorm开发。把下面的css放在head标签中,第二行js放在body标签最底部。
在https://www.jsdelivr.com/里找到JQuery的CDN导入。
2.3 插件集成
背景图片资源
编辑器Markdown,
内容排版typo.css,
动画animate.css,
代码高亮prism或者官网,
滚动侦测waypoints,
平滑滚动jquery.scrollTo,
目录生成Tocbot,
二维码生成qrcode.js。
1、引入SpringBoot模块:
- web
- Thymeleaf
- IPA
- MySQL
- Aspects(在SpringBoot2.x自动导入了)
- Devtools
2、application-dev.yml配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/bolg?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: root
jpa:
hibernate:
ddl-auto: update
show-sql: true
logging:
level:
root: info
com.zkp: debug
配合logback.xml
${FILE_LOG_PATTERN} ${LOG_FILE} ${LOG_FILE}.%d{yyyy-MM-dd}.%i 3 10MB
application/yml
spring:
profiles:
active: dev
3.2 异常处理
1、定义错误页面
- 404
- 500
- error
从后台获取信息在static下完成不了,因此在template下定义错误页面,新建error文件夹,下面创建404.html和500.html,如果出现异常,Spring约定会在error文件夹下使用对应的html页面,比如404对应404.html。
2、全局处理异常
新建包handler,创建类ControllerExceptionHandler,
@ControllerAdvice
public class ControllerExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@ExceptionHandler(Exception.class)
public ModelAndView exceptionHandler(HttpServletRequest request, Exception e) throws Exception {
logger.error("Request URL: {}, Exception: {}", request.getRequestURL(), e.toString());
e.printStackTrace();
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
throw e;
}
ModelAndView mv = new ModelAndView();
mv.addObject("url", request.getRequestURL());
mv.addObject("exception", e);
mv.setViewName("error/error");
return mv;
}
}
在error.html中加上下列代码,作用是在源代码中打印错误信息,这样如果出错,在前端就能看到哪里出错,不用跑到服务器去看。
>'" th:remove="tag">
新建exception,创建NotFoundException
@ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException{
public NotFoundException() {
}
public NotFoundException(String message) {
super(message);
}
public NotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
3.3 日志处理
采用SpringBoot的AOP进行日志处理,记录日志内容有:
- 请求url
- 访问者ip
- 调用方法classMethod
- 参数args
- 返回内容
编写BlogAspect如下:
@Aspect
@Component
public class LogAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Pointcut("execution(* com.zkp.web.*.*(..))")
public void log(){
}
@Before("log()")
public void doBrfore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String url = request.getRequestURL().toString();
String ip = request.getRemoteAddr();
String classMethod = joinPoint.getSignature() + " ";
Object[] args = joinPoint.getArgs();
RequestLog requestLog = new RequestLog(url, ip, classMethod, args);
//记录
logger.info("Request:" + requestLog);
}
@After("log()")
public void doAfter() {
// logger.info("------after------");
}
@AfterReturning(returning = "result",pointcut = "log()")
public void doAfterReturn(Object result) {
//记录
logger.info("-----Result------:" + result);
}
private class RequestLog {
private String url;
private String ip;
private String classMethod;
private Object[] args;
public RequestLog(String url, String ip, String classMethod, Object[] args) {
this.url = url;
this.ip = ip;
this.classMethod = classMethod;
this.args = args;
}
@Override
public String toString() {
return "{" +
"url='" + url + ''' +
", ip='" + ip + ''' +
", classMethod='" + classMethod + ''' +
", args=" + Arrays.toString(args) +
'}';
}
}
}
效果如下:
1、静态页面导入project
这里如果目录结构没有对上,需要移动html文件位置,记得勾选search for references,会自动修改引用位置。
导入之后再设置路径进行访问,结果报错500,解决方法是clean之后重新编译。F12控制台可以看到有些访问不到
需要使用th:href=“@{}”,注意这里默认到stati里面找。如果页面很多,每一个都需要这样处理比较麻烦,在thymeleaf中的fragment可以解决这个问题。
2、thymeleaf布局
- 定义fragment
新建_fregments.html,编写head标签
首页
- 使用fragment布局
在其他html页面的head中这样修改
首页
这样两端都可以用
3、优化错误页面
加入head和footer,并加入错误信息提示:
4 设计与规范
4.1 实体设计
可以先建表,也可以面向对象的思维,先设计实体,再用Spring Boot的JPA设计数据库表结构。
实体类:
- 博客Blog
- 博客分类Type
- 博客标签Tag
- 博客评论Comment
- 用户User
关系:例如,类型和博客之间是一对多的关系。
评论和回复的关系:
Blog类:包含一组标签,一组评论,一个用户。
评论类:
用户类:
以创建Blog为例,需要加上这些注解:JPA使用@Entity表示有数据库生成的能力,@Table可以指定数据库表的名字,@Id是主键,@GeneratedValue默认生成策略。
@Entity
@Table(name = "t_blog")
public class Blog {
@Id
@GeneratedValue
private Long id;
}
如果是创建日期类型,需要加上注解:
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
创建完实体类后,处理实体类之间的关系,例如一个博客有多个标签。在Blog类中加入注解@ManyToOne,在Type中假如注解@OneToMany。多对多同理用@ManyToMany。关系被维护方指定mappedBy。
@ManyToOne
private Type type;
@OneToMany(mappedBy = "type")
private List blogs = new ArrayList<>();
@ManyToMany
private List tags = new ArrayList<>();
级联新增@ManyToMany(cascade = {CascadeType.PERSIST})。
启动之后,可以看到自动生成数据库表:
终端显示层即template。
Serverce和Da层方法命名约定:
- 获取单个用get
- 获取多个用list
- 获取统计值用count
- 插入用insert
- 删除用delete
- 修改用update
登录需要的步骤如下:
1、构建登录页面和后台管理首页
登录后台:
2、UserService和UserRepository
继承JpaRepository,User表示操作的对象,Long是主键类型。只要方法名符合规范,就能直接封装好sql语句。
public interface UserRepository extends JpaRepository{ User findByUsernameAndPassword(String username, String password); } @Service public class UserServiceImpl implements UserService { @Autowired private UserRepository userRepository; @Override public User checkUser(String username, String password) { User user = userRepository.findByUsernameAndPassword(username,password); return user; } }
3、LoginController实现登录
@Controller
@RequestMapping("/admin")
public class LoginController {
@Autowired
private UserService userService;
@GetMapping
public String loginPage(){
return "admin/login";
}
@PostMapping("/login")
public String login(@RequestParam String username,
@RequestParam String password,
HttpSession session,
RedirectAttributes attributes){
User user = userService.checkUser(username,password);
if(user!=null){
user.setPassword(null);
session.setAttribute("user",user);
return "admin/index";
}
else{
attributes.addFlashAttribute("message","用户名或密码错误");
return "redirect:/admin";
}
}
@GetMapping("/logout")
public String logout(HttpSession session){
session.removeAttribute("user");
return "redirect:/admin";
}
}
4、MD5加密
创建util.MD5Utils类
public class MD5Utils {
public static String code(String str){
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
byte[] byteDigest = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < byteDigest.length; offset++){
i = byteDigest[offset];
if (i<0){
i+=256;
}
if (i<16){
buf.append("0");
}
buf.append(Integer.toHexString(i));
}
return buf.toString();
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
}
5、登录拦截器
创建interceptor.LoginInterceptor类
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if(request.getSession().getAttribute("user")==null){
response.sendRedirect("/admin");
return false;
}
return true;
}
}
编写config配置类
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/admin
验证失败
提交信息不符合规则
"";
var qrcode = new QRCode("qrcode", {
text: serurl + url,
width: 110,
height: 110,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
评论功能:
1、评论信息提交与回复
2、评论信息列表展示功能
3、管理员回复评论
展示效果如下:
操作类似分类页。
6.4 归档页所谓归档就是按照年份把发布的博客进行展示。
6.5 关于我最后在resources下新建messages.properties文件。
可以全球化,新建两个文件messages_en_US.properties,和messages_zh_CN.properties。
如果放在i18n文件夹下,需要在yml文件中配置路径。
spring: message: basename: i18n/messages部署到服务器
参考另外一篇博客.



