目录
- 前言
- 一、一点建网站的背景知识
- 二、个人博客系统介绍
-
- 1、核心功能和数据库
- 2、前端页面
- 3、后端
-
- servlet
- service层
- dao层
- 配置文件
- 参考资料
前言
这篇博主会介绍下我用javaweb搭建的个人博客系统,源码也会打包放到gitee上,需要的朋友可以自取,大家互相学习,请不要直接CV。
tip:本篇承上篇,许多基本内容在上篇谈到,建议看之前先浏览下上篇博客。
上篇:自己动手搭网站(五):javaweb基础:登录功能
系列总目录:自己动手搭建网站系列总目录
gitee仓库链接
一、一点建网站的背景知识
在正式介绍代码前,博主先谈谈个人了解的一点web背景知识,也是博主接触web后“东拼西凑”来的,若有不当之处,希望大家指正。
最早的网站是非常简单的,基本就是用html和css写的一些静态页面,几乎没有什么交互能力,只是用来展示某些信息的。这个时期的web技术也常被称为web1.0
后来,随着技术的不断发展,C#、php、java等语言逐渐扩展到web领域,或者说,它们本来便是“应运而生”的。这些高级语言的加入使得网站渐渐有了交互能力,不再是只能看的页面了。也被称为web2.0时代,这个时期出现了许多经典的web建站技术栈,比如下面几个:
javaweb系列 (j2EE /javaweb,前端jsp,后端java执行各种复杂操作;最先由sun公司提出,后来sun公司被oracle收购,现在oracle的网站可能还是用j2ee这套,j2ee比较适合大型网站,但是原生的j2ee真的很复杂,现在发展简化到了“spring(boot)+前端”的模式,方便多了,也是目前比较主流的网站开发技术栈)、
asp.net系列(感觉是微软仿照jsp那套做得,前端asp,后端C#执行各种复杂操作,顺便一说,C#和Java据说有90%的相似度)、
php(这个博主没怎么用过,不过听说php可以嵌入html中,导致前后端的界限模糊消失,增加了中小型网站开发效率的同时也使得其很难用于大型网站的开发并且维护也变得更加困难,但目前似乎仍有不少网站用着这套技术栈)
当然,上述还称不上web技术栈,一般还要加上数据库(mysql)、操作系统(centos)、服务器(tomcat、Nginx等)…形成从开发到部署的一整套技术,便可称之为web技术栈了。
web技术博大精深,近年来web3.0的概念也逐渐产生,涉及更复杂的用户交互体验,甚至渗透到生活的方方面面,已经渐渐超脱网站的限制而走向更多元的发展方向。入坑的朋友们,有没有觉得“任重而道远”呢?
二、个人博客系统介绍
闲话少说,咱们步入正题,这次博主采用的技术栈为javaweb(jsp、servlet、jdbc、mysql、tomcat),jsp主要是写前端页面、servlet和jdbc后端操作和连接数据库、tomcat是服务器、mysql就不必多说了,吾等开发者最青睐的开源服务器。关于jsp、servlet、jdbc大家有疑问的话可以看参考资料中狂神的相关视频,博主主要也是跟着这视频学的。这次用的集成开发环境是idea2022.1
关于用idea新建一个简单的web项目在我放到另外一篇博客
idea基本web开发环境配置及新建javaweb项目
1、核心功能和数据库
系统非常简单,核心功能就两个:登录和博客管理(发布、编辑、修改、展示)
数据库表我也就建了两个,一个user表,一个blog表,分别服务于上面两个功能。登录方面,就简单的登录,甚至没有注册和找回密码等功能,因为是个人博客系统,注册什么的没太大必要(绝对不是因为偷懒,确信!)
-- 创建数据库
create database if not exists blog_system;
use blog_system;
-- 创建用户表
create table user(
user_id varchar(20) primary key,
password varchar(20)
);
-- 创建博客表,主键为blog_id,无符号int,自增
create table blog(
blog_id int unsigned auto_increment primary key,
author varchar(20) not null,
title varchar(40) not null,
content text not null,
create_time datetime not null,
update_time datetime not null,
is_deleted tinyint unsigned not null
);
-- 向用户表中插入一条数据
insert into user(user_id,password) values('admin','test@123');
-- 向博客表中插入一条数据
insert into blog(author,title,content,create_time,update_time,is_deleted) values("微光落尘","###测试文章","嘻嘻哈哈","2022-08-31 20:36:08","2022-08-31 20:36:08",0);
2、前端页面
这次博主用的前端框架为bootstrap5,其实和bootstrap3也差不了多少,当初只是想了解下最新的bootstrap。涉及到博客功能,还引入了一个开源项目editor.md ,引入后webapp下的目录结构大概是这样子的
博主在jsp页面插入了一些js脚本,用来和后端交互数据,其中最重要的函数是$.ajax(),关于此方法的详细介绍,参见ajax()方法详解,简单来说就是个用来向后端传递数据并接收后端传回参数的函数。
因为最上面部分和左侧导航栏重复使用,我把它们抽出来做为母版页header.jsp和left.jsp,之后在jsp里面用<%@ include file=“header.jsp”%>引入进来就行。页面相比于第四篇中介绍的几个页面有了一些改变。下面是部分前端页面的代码:
header.jsp
<%--网站头部母版页--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<div style="margin-top:0px">
<div class="topImg">
<h1>欢迎来小尘空间</h1>
<p>小站虽小,或有其妙</p>
</div>
<nav class="navbar navbar-expand-sm bg-white navbar-white border-bottom">
<div class="container-fluid align-items-center">
<div>
<ul id="navBar" class="nav nav-pills">
<li id="mianPage" class="nav-item"><a href="index.jsp" class="nav-link active" aria-current="page">主页</a></li>
<li id="scenery" class="nav-item"><a href="scenery.jsp" class="nav-link">美景分享</a></li>
<li id="blog" class="nav-item"><a href="paging" class="nav-link">博客文章</a></li>
<li id="about" class="nav-item"><a href="about.jsp" class="nav-link">关于本站</a></li>
</ul>
</div>
<div class="dropdown col-1 text-end">
<a href="#" class="d-block link-dark text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
<img src="img/north-star-2869817_1920.jpg" alt="mdo" width="40" height="40" class="rounded-circle">
</a>
<ul class="dropdown-menu text-small" aria-labelledby="dropdownUser1">
<li><a class="dropdown-item" href="personalCenter/personalCenter.jsp">个人中心</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="publish.jsp">发布文章</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="login.jsp">登录</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="login.jsp">退出</a></li>
</ul>
</div>
</div>
</nav>
</div>
<script type="text/javascript">
$(document).ready(function () {
//console.log("changing active...")
// each 是 为每一个匹配的元素 执行定义的方法
$("#navBar").find("li").each(function (){
var a = $(this).find("a:first")[0];
// location.pathname 获取当前浏览器上的url路径部分
if ( location.pathname.search($(a).attr("href")) !== -1) {
$(a).addClass("active");
} else {
$(a).removeClass("active");
}
});
});
</script>
left.jsp
<%--网站个人中心侧边栏--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<div style="width: 280px;height: 100vh;background-color: rgba(1,1,19,0.4);position: fixed">
<a href="personalCenter.jsp" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
<svg class="bi me-2" width="40" height="32"><use xlink:href="#bootstrap"/></svg>
<span class="fs-4">个人中心</span>
</a>
<hr>
<ul class="nav nav-pills flex-column mb-auto" id="leftNav">
<li class="nav-item">
<a href="personalCenter.jsp" class="nav-link text-white active" aria-current="page">
<svg class="bi me-2" width="16" height="16"><use xlink:href="#home"/></svg>
个人资料
</a>
</li>
<li>
<a href="blogManagement.jsp" class="nav-link text-white">
<svg class="bi me-2" width="16" height="16"><use xlink:href="#speedometer2"/></svg>
博客管理
</a>
</li>
<li>
<a href="../index.jsp" class="nav-link text-white">
<svg class="bi me-2" width="16" height="16"><use xlink:href="#speedometer2"/></svg>
返回主页
</a>
</li>
</ul>
</div>
<script type="text/javascript">
$(document).ready(function () {
//console.log("changing active...")
// each 是 为每一个匹配的元素 执行定义的方法
$("#leftNav").find("li").each(function () {
var a = $(this).find("a:first")[0];
// location.pathname 获取当前浏览器上的url路径部分
if ( location.pathname.search($(a).attr("href")) !== -1) {
$(a).addClass("active");
} else {
$(a).removeClass("active");
}
});
});
</script>
主页(index.jsp)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="bootstrap5/css/bootstrap.min.css">
<script src="bootstrap5/js/bootstrap.bundle.min.js"></script>
<title>微光落尘的个人空间</title>
<script src="js/jquery-3.5.1.min.js"></script>
<link href="css/myStyle.css" rel="stylesheet">
</head>
<body>
<%@ include file="header.jsp"%>
<div class="container">
<div class="row" style="height: 80vh">
<!-- 轮播 -->
<div id="demo" class="carousel slide" data-bs-ride="carousel" style="margin-top:5vh">
<!-- 指示符 -->
<div class="carousel-indicators">
<button type="button" data-bs-target="#demo" data-bs-slide-to="0" class="active"></button>
<button type="button" data-bs-target="#demo" data-bs-slide-to="1"></button>
<button type="button" data-bs-target="#demo" data-bs-slide-to="2"></button>
</div>
<!-- 轮播图片 -->
<div class="carousel-inner">
<div class="carousel-item active">
<img src="http://static.runoob.com/images/mix/img_fjords_wide.jpg" class="d-block" style="width:100%">
</div>
<div class="carousel-item">
<img src="https://www.yuucn.com/wp-content/uploads/2023/05/1684969988-33baa0415c3910e.jpg" class="d-block" style="width:100%">
</div>
<div class="carousel-item">
<img src="https://www.yuucn.com/wp-content/uploads/2023/05/1684969995-33baa0415c3910e.jpg" class="d-block" style="width:100%">
</div>
</div>
<!-- 左右切换按钮 -->
<button class="carousel-control-prev" type="button" data-bs-target="#demo" data-bs-slide="prev">
<span class="carousel-control-prev-icon"></span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#demo" data-bs-slide="next">
<span class="carousel-control-next-icon"></span>
</button>
</div>
</div>
</div>
<div class="text-center" style="margin-bottom:0;background-color: rgba(150,145,145,0.4)">
<p style="color:#0000FF;font-size:30px"><a href="message.jsp">给我留言
<span class="glyphicon glyphicon-pencil"></span>
</a>
</p>
<br>
<p>备案信息:暂无</p>
</div>
</body>
</html>
效果图如下
美景分享页(scenery.jsp)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="bootstrap5/css/bootstrap.min.css">
<script src="bootstrap5/js/bootstrap.bundle.min.js"></script>
<title>微光落尘的个人空间</title>
<script src="js/jquery-3.5.1.min.js"></script>
<link href="css/myStyle.css" rel="stylesheet">
</head>
<body>
<div>
<div style="width:100vw;height:40px;position:fixed;left:0px;top:0px;z-index: 999;background-color: rgba(30,144,255,0.4)">
<ul id="navBar" class="nav nav-pills">
<li id="mianPage" class="nav-item"><a href="index.jsp" class="nav-link active" aria-current="page">主页</a></li>
<li id="scenery" class="nav-item"><a href="scenery.jsp" class="nav-link">美景分享</a></li>
<li id="blog" class="nav-item"><a href="paging" class="nav-link">博客文章</a></li>
<li id="about" class="nav-item"><a href="about.jsp" class="nav-link">关于本站</a></li>
</ul>
</div>
<!-- 轮播 -->
<div id="demo" class="carousel slide" data-bs-ride="carousel">
<!-- 指示符 -->
<div class="carousel-indicators">
<button type="button" data-bs-target="#demo" data-bs-slide-to="0" class="active"></button>
<button type="button" data-bs-target="#demo" data-bs-slide-to="1"></button>
<button type="button" data-bs-target="#demo" data-bs-slide-to="2"></button>
</div>
<!-- 轮播图片 -->
<div class="carousel-inner">
<div class="carousel-item active">
<img src="img/pic1.jpg" class="d-block" style="width:100%;height: 100vh">
</div>
<div class="carousel-item">
<img src="img/pic2.jpg" class="d-block" style="width:100%;height: 100vh">
</div>
<div class="carousel-item">
<img src="img/pic3.jpg" class="d-block" style="width:100%;height: 100vh">
</div>
</div>
<!-- 左右切换按钮 -->
<button class="carousel-control-prev" type="button" data-bs-target="#demo" data-bs-slide="prev">
<span class="carousel-control-prev-icon"></span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#demo" data-bs-slide="next">
<span class="carousel-control-next-icon"></span>
</button>
</div>
</div>
</body>
<script type="text/javascript">
//这个函数用来操作导航栏的蓝色激活胶囊,但是有个小bug...
$(document).ready(function () {
console.log("changing active...")
// each 是 为每一个匹配的元素 执行定义的方法
$("#navBar").find("li").each(function () {
var a = $(this).find("a:first")[0];
// location.pathname 获取当前浏览器上的url路径部分
if ( location.pathname.search($(a).attr("href")) !== -1) {
$(a).addClass("active");
} else {
$(a).removeClass("active");
}
});
});
</script>
</html>
效果图如下:
博客列表展示页(blogListPage.jsp)
这里使用了jstl的一些标签来生成动态博客列表,并做了分页功能,分页功能的实现参见参考资料给的链接。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="bootstrap5/css/bootstrap.min.css">
<script src="bootstrap5/js/bootstrap.bundle.min.js"></script>
<title>微光落尘的个人空间</title>
<script src="js/jquery-3.5.1.min.js"></script>
<link href="css/myStyle.css" rel="stylesheet">
</head>
<body>
<%@ include file="header.jsp"%>
<div class="row">
<div class="col-md-12" style="text-align: center">
<table class="table table-hover">
<caption>共${page.totalQuantity}篇博客</caption>
<thead>
</thead>
<tbody>
<c:forEach items="${blogList}" var="blog" varStatus="status">
<tr>
<td style="width: 60%;height: 100px">
<a href="showBlog.jsp?blogId=${blog.blogId}" style="text-decoration: none;font-size:25px;font-weight:bold;">${blog.title}</a>
</td>
<td style="width: 20%;height: 100px;padding-top:80px">${blog.author}</td>
<td style="width: 20%;height: 100px;padding-top:80px">${blog.createTime}</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<div style="width:100%;padding-left: 48vw">
<ul class="pagination">
<li <c:if test="${!page.hasPreviouse}">class="disabled"</c:if>>
<a href="?page.startIndex=0" style="text-decoration: none;font-size:20px;font-weight:bold;">
<span>«</span>
</a>
</li>
<li <c:if test="${!page.hasPreviouse}">class="disabled"</c:if>>
<a href="?page.startIndex=${page.startIndex-page.perPageQuantity}" style="text-decoration: none;font-size:20px;font-weight:bold;margin-left: 10px">
<span>‹</span>
</a>
</li>
<c:forEach begin="0" end="${page.totalPage-1}" varStatus="status">
<c:if test="${status.count*page.perPageQuantity-page.startIndex<=30 && status.count*page.perPageQuantity-page.startIndex>=-10}">
<li <c:if test="${status.index*page.perPageQuantity==page.startIndex}">class="disabled"</c:if>>
<a href="?page.startIndex=${status.index*page.perPageQuantity}" style="text-decoration: none;font-size:20px;font-weight:bold;margin-left: 10px"
<c:if test="${status.index*page.perPageQuantity==page.startIndex}">class="current"</c:if>
>${status.count}</a>
</li>
</c:if>
</c:forEach>
<li>
<a href="?page.startIndex=${page.startIndex+page.perPageQuantity}" style="text-decoration: none;font-size:20px;font-weight:bold;margin-left: 10px">
<span>›</span>
</a>
</li>
<li>
<a href="?page.startIndex=${page.last}" style="text-decoration: none;font-size:20px;font-weight:bold;margin-left: 10px">
<span>»</span>
</a>
</li>
</ul>
</div>
</div>
</body>
<script>
$(function () {
//禁用分页按钮的函数
$("ul.pagination li.disabled a").click(function () {
return false;
});
});
</script>
</html>
效果图:
点击标题进入到博客内容展示页(showBlog.jsp),可以看到详细的博客内容
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="bootstrap5/css/bootstrap.min.css">
<script src="bootstrap5/js/bootstrap.bundle.min.js"></script>
<title>微光落尘的个人空间</title>
<script src="js/jquery-3.5.1.min.js"></script>
<link href="css/myStyle.css" rel="stylesheet">
<link rel="stylesheet" href="editorMd/css/editormd.min.css"/>
<%-- <link rel="stylesheet" href="editorMd/css/editormd.css"/>--%>
</head>
<body>
<%@ include file="header.jsp"%>
<div style="text-align: center">
<h2 id="title">hhh</h2>
<p style="text-align: right">
<span id="author" style="margin-right: 20px">asdfuhiod</span>
<span id="time" style="margin-right: 100px">fffff</span>
</p>
</div>
<div id="test-editormd" style="width: 90%;padding-left: 5%" >
<textarea style="display: none" id="test-editormd-markdown-doc" name="test-editormd-markdown-doc">这篇博客
</textarea>
</div>
</body>
<script src="editorMd/editormd.min.js"></script>
<script src="editorMd/lib/marked.min.js"></script>
<script src="editorMd/lib/prettify.min.js"></script>
<script src="editorMd/lib/raphael.min.js"></script>
<script src="editorMd/lib/underscore.min.js"></script>
<script src="editorMd/lib/sequence-diagram.min.js"></script>
<script src="editorMd/lib/flowchart.min.js"></script>
<script src="editorMd/lib/jquery.flowchart.min.js"></script>
<script type="text/javascript">
var testEditor;
//得到后端传来的md格式的文章
function getBlogContent(){
// 用=将路由参数分割成数组
let idArray = window.location.search.split('=');
//console.log(idArray);
// 获取路由的参数
var blogId = idArray[1];
$.ajax({
url:"${pageContext.request.contextPath}/blog",//这个url是处理请求的servlet的url
type:"GET",
data: { "method":"blogDisplay","blogId":blogId},
dataType:"json",
success:function (data,status){
document.getElementById("title").innerText = data.title;
document.getElementById("author").innerText = data.author;
document.getElementById("time").innerText = data.createTime;
//console.log("data"+ data.blogContent);
var text = document.getElementById("test-editormd-markdown-doc");
text.innerHTML = data.blogContent;//不可用innerText
showBlog();
},error:function (status){
console.log(status);
}
});
}
//DOM加载完成后执行上面函数,将文章内容放到textarea内,并用editormd进行处理、展示
$(document).ready(getBlogContent());
function showBlog(){
testEditor = editormd.markdownToHTML("test-editormd", {
htmlDecode: "style, script, iframe",
//width:"90%",
//preview:true,
//watch:true,
emoji: true,
taskList: true,
tex: true, // 默认不解析
flowChart: true, // 默认不解析
sequenceDiagram: true, // 默认不解析
});
}
</script>
</html>
效果图如下:
关于本站页面就是个简单的静态页面,内容基本就第四篇博客中的那些,这里就不赘述了。
点击右上角圆形头像展开提示菜单,可以选择“个人中心”、“发布博客”、“登录”、“退出”,如下图效果:
点击“个人中心”,触发登录验证,若没有登录则转向登录页面(login.jsp),登录成功后转到个人中心(personalCenter.jsp)
登录页面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset=UTF-8">
<title>微光落尘的个人空间</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<script src="js/jquery-3.5.1.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<style>
.in_style{
background-color:rgba(30,144,255,0.1);
color:#1E90FF;
border:1px solid #1E90FF
}
body{
background:url("img/north-star-2869817_1920.jpg") no-repeat center center; /*加载背景图*/ /* 背景图不平铺 */
background-size:cover; /* 让背景图基于容器大小伸缩 */
background-attachment:fixed; /* 当内容高度大于图片高度时,背景图像的位置相对于viewport固定 */
background-color:#CCCCCC; /* 设置背景颜色,背景图加载过程中会显示背景色 */
}
.myBox{
background-color: rgba(30,144,255,0.1);
border:1px solid #1E90FF;
border-radius: 10px;
}
</style>
</head>
<body>
<div class="text-center">
<div class="container">
<div class="row" style="margin-top:30vh;">
<div class="col-sm-4 col-md-offset-8 myBox">
<div class="row" style="margin-bottom: 5vh">
<h1 style="color: rgba(253,252,252,0.98)">小尘空间传送门</h1>
</div>
<form id="form1" class="bs-example bs-example-form" role="form" action="${pageContext.request.contextPath}/login" method="post">
<%-- 登录失败提示信息--%>
<div class="info" style="color:red">${error}</div>
<div class="input-group" >
<span class="input-group-addon control-label in_style">时空节点</span>
<input type="text" id="username" name="username" class="form-control in_style" placeholder="">
</div>
<br>
<div class="input-group" style="margin-top:10px">
<span class="input-group-addon control-label in_style">开启秘钥</span>
<input type="password" name="password" class="form-control in_style" placeholder="">
</div>
<br>
<div class="input-group-btn" style="padding-top:3vh;padding-bottom: 1vh;text-align: end">
<input type="submit" id="login" class="btn btn-default" style="border:none;background-color: rgba(30,144,255,0.4);color: #1E90FF">点击传送</input>
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
效果图如下:
个人中心(personalCenter.jsp),这页其实也只是拿来充门面的:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../bootstrap5/css/bootstrap.min.css">
<script src="../bootstrap5/js/bootstrap.bundle.min.js"></script>
<title>微光落尘的个人空间</title>
<script src="../js/jquery-3.5.1.min.js"></script>
<link href="../css/myStyle.css" rel="stylesheet">
</head>
<body>
<div class="row">
<%@ include file="left.jsp"%>
<div class="col-md-9" style="text-align: center;margin-left:300px">
<img src="../img/north-star-2869817_1920.jpg" alt="mdo" width="100" height="100" class="rounded-circle" style="margin-top:10vh">
<from>
<div class="row">
<div class="col-md-1 col-offset-1" style="margin-top:22px;margin-left: 10vw;">昵称:
</div>
<div class="col-md-8">
<input type="text" class="form-control mt-3" value="微光落尘" readonly>
</div>
</div>
<div class="row">
<div class="col-md-1" style="margin-top:22px;margin-left: 10vw;">个性签名:
</div>
<div class="col-md-8">
<input type="text" class="form-control mt-3" value="可惜一溪风月,莫教踏碎琼瑶" readonly>
</div>
</div>
<div class="row">
<div class="col-md-1" style="margin-top:22px;margin-left: 10vw;">邮箱:
</div>
<div class="col-md-8">
<input type="text" class="form-control mt-3" value="1054553042@qq.com" readonly>
</div>
</div>
</from>
</div>
</div>
</body>
<script type="text/javascript"></script>
</html>
点击左侧导航栏“博客管理”,进入博客管理页面(blogManagement.jsp),可以选择修改和删除博客,也是本系统的核心,这里的动态列表使用js动态生成的(别问我这里为什么不用jstl,jstl真的简单些…)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../bootstrap5/css/bootstrap.min.css">
<script src="../bootstrap5/js/bootstrap.bundle.min.js"></script>
<title>微光落尘的个人空间</title>
<script src="../js/jquery-3.5.1.min.js"></script>
<link href="../css/myStyle.css" rel="stylesheet">
</head>
<body>
<div class="row" style="height: 100%">
<%@ include file="left.jsp"%>
<div class="col-md-9" id="blogList" name="blogList" style="margin-left: 300px">
</div>
</div>
</body>
<script type="text/javascript">
$(document).ready(function (){
$.ajax({
url:"${pageContext.request.contextPath}/blog",//这个url是处理请求的servlet的url
type:"POST",
data: {"method":"getAllBlog"},
dataType:"json",
success:function (data,status){
//console.log("data"+ JSON.stringify(data));
//console.log(status);
generateBlogList(data);
},error:function (status){
console.log(status);
}
});
})
//下面是生成动态列表的函数,主要是循环创建具有层次的节点,把数据填充进去
function generateBlogList(blogList){
var list = document.getElementById("blogList");
for(let i = 0;i<blogList.length;i++){
let blogItem = document.createElement("div");
blogItem.setAttribute("class","row");
blogItem.style.cssText = "border-bottom: 1px solid black;height: 100px;";
let adiv = document.createElement("div");
adiv.setAttribute("class","col-md-6");
let blogTitle = document.createElement("a");
blogTitle.setAttribute("href","${pageContext.request.contextPath}/showBlog.jsp?blogId="+blogList[i].blogId);
blogTitle.style.cssText="text-decoration: none;font-size:25px;font-weight:bold;"
blogTitle.innerText = blogList[i].title;
adiv.appendChild(blogTitle)
blogItem.appendChild(adiv)
let divTwo = document.createElement("div");
divTwo.setAttribute("class","col-md-2");
let blogAuthor = document.createElement("span");
blogAuthor.style.cssText="margin-top:80px"
blogAuthor.innerText = blogList[i].author;
divTwo.appendChild(blogAuthor)
blogItem.appendChild(divTwo);
let divThree = document.createElement("div")
divThree.setAttribute("class","col-md-2");
let blogCreateTime = document.createElement("span");
blogCreateTime.style.cssText="margin-top:80px"
blogCreateTime.innerText = blogList[i].createTime;
divThree.appendChild(blogCreateTime)
blogItem.appendChild(divThree);
let divFour = document.createElement("div");
divFour.setAttribute("class","col-md-2");
let buttonOne = document.createElement("button");
buttonOne.setAttribute("class","btn btn-success");
buttonOne.setAttribute("id","edit");
buttonOne.setAttribute("onclick","toEditPage("+blogList[i].blogId+")");
buttonOne.innerText="编辑";
divFour.appendChild(buttonOne);
let buttonTwo = document.createElement("button");
buttonTwo.setAttribute("class","btn btn-danger");
buttonTwo.setAttribute("id","delete");
buttonTwo.setAttribute("onclick","deleteBlog("+blogList[i].blogId+")");
buttonTwo.innerText="删除";
divFour.appendChild(buttonTwo);
blogItem.appendChild(divFour);
list.appendChild(blogItem);
}
}
//跳转到文章对应的编辑页面
function toEditPage(blogId){
window.location.href="../editBlog.jsp?blogId="+blogId;
}
//点击删除按钮的事件处理函数
function deleteBlog(blogId){
if(confirm("确定删除这篇博客?") == false){
return;
}
$.ajax({
url:"${pageContext.request.contextPath}/blog",//这个url是处理请求的servlet的url
type:"POST",
data: {"method":"delete",
"blogId":blogId},
dataType:"json",
success:function (data,status){
// console.log("data"+ JSON.stringify(data));
// console.log(status)
if(data.result === "success"){
location.reload();
alert("已删除");
}else{
alert("出错了");
}
},error:function (status){
console.log(status);
alert("出错了");
}
});
}
</script>
</html>
点击编辑,可以修改博客内容,顺便一说,因为是个人博客系统,所以作者也被我写成默认的了(不是偷懒啊…)
博客编辑页面(editBlog.jsp)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="bootstrap5/css/bootstrap.min.css">
<script src="bootstrap5/js/bootstrap.bundle.min.js"></script>
<title>微光落尘的个人空间</title>
<script src="js/jquery-3.5.1.min.js"></script>
<link href="css/myStyle.css" rel="stylesheet">
<link rel="stylesheet" href="editorMd/css/editormd.min.css">
<script src="editorMd/editormd.min.js"></script>
</head>
<body>
<%@ include file="header.jsp"%>
<div class="row">
<div id="topbar" name="topbar" class="col-sm-1 p-3" style="margin-top: 6px;text-align: center;font-weight: bold;font-size: 20px" >
<a style="text-decoration: none" href="${pageContext.request.contextPath}/personalCenter/blogManagement.jsp">
<img src="img/icons8-back-64.png" width="32" height="30">返回
</a>
</div>
<div class="col-sm-8 p-3">
<input type="text" id="title" name="title" class="form-control" style="font-weight: bold" placeholder="">
</div>
<div class="col-sm-1" style="padding-top: 12px;text-align: center">
<button type="submit" id="publish" class="btn btn-info" onclick="modify()">发布</button>
</div>
</div>
</div>
<div id="test-editor">
<textarea id="test-editor-blogContent" name="test-editor-blogContent"></textarea>
<br>
</div>
</body>
<script type="text/javascript">
//得到后端传来的md格式的文章
function prepareEditBlog(){
// 用=将路由参数分割成数组
let idArray = window.location.search.split('=');
//console.log(idArray);
// 获取路由的参数
var blogId = idArray[1];
$.ajax({
url:"${pageContext.request.contextPath}/blog",//这个url是处理请求的servlet的url
type:"GET",
data: { "method":"blogDisplay","blogId":blogId},
dataType:"json",
success:function (data,status){
//console.log("data"+ data.blogContent);
var title = document.getElementById("title");
title.value = data.title;
var text = document.getElementById("test-editor-blogContent");
text.innerHTML = data.blogContent;//不可用innerText
//editBlog;//editormd处理
},error:function (status){
console.log(status);
}
});
}
//DOM加载完成后执行上面函数,将文章内容放到textarea内,并用editormd进行处理、展示
$(document).ready(prepareEditBlog());
function modify(){
if(document.getElementById("title").value === "" ||document.getElementById("title").value == null){
alert("请输入文章标题");
return;
}
//编码后传输到后端
var content = encodeURIComponent(editor.getMarkdown());
//console.log(content);
// 用=将路由参数分割成数组
let idArray = window.location.search.split('=');
//console.log(idArray);
// 获取路由的参数
var blogId = idArray[1];
$.ajax({
url:"${pageContext.request.contextPath}/blog",//这个url是处理请求的servlet的url
type:"POST",
data: {"method":"modify",
"blogId":blogId,
"title":document.getElementById("title").value,
"blogContent":content},//document.getElementById("test-editor-blogContent").innerText
dataType:"json",
success:function (data,status){
// console.log("data"+ JSON.stringify(data));
// console.log(status)
if(status === "success"){
alert("文章发布成功");
}
},error:function (status){
console.log(status);
alert("出错了");
}
});
}
var editor;
$(function editBlog() {
editor = editormd("test-editor", {
// 在页面显示的宽度
width : "100%",
// 高度
height : 400,
path : "editorMd/lib/"
});
});
</script>
</html>
发布博客页面(publish.jsp)和编辑页面差不多,就不多说了。
3、后端
后端整体结构和上篇说的一样,下面是后端的文件结构
后端具体的代码就不再罗列了,要完整代码的到我的gitee仓库下载即可,下面简单介绍下后端的一些重点内容。
servlet
两类servlet,分别用来处理登录和博客管理相关的请求,登录上篇说得差不多了,博客管理相关的servlet主要有下面几个函数
String method = req.getParameter("method");
if(method.equals("publish")){//用户写完博客点击发布后触发的函数,也就是添加博客到数据库中
this.publish(req,resp);
} else if (method.equals("getAllBlog")){//从数据库中取出所有博客
this.getAllBlog(req,resp);
}else if(method.equals("delete")){//删除博客
this.deleteBlog(req,resp);
}else if(method.equals("modify")){//修改博客
this.modify(req,resp);
}else if(method.equals("blogDisplay")){//博客展示,用户点击标题后触发的请求
this.queryBlogById(req,resp);
}
}
还有一个专门用于辅助分页展示的servlet,关于分页功能,这篇文章讲的比较详细,大家可以参考下
Java Web -【分页功能】详解
登录功能已经在上篇中说过,这里不再赘述。
service层
这层主要是处理一些业务逻辑,并调用数据访问层进行进一步处理,以添加博客功能为例,代码如下
@Override
public boolean addBlog(String author, String title, String content) {
//返回给servlet的状态标识,用以表示操作是否成功
boolean flag = false;
Connection connection = null;//数据库连接
//获取时间并格式化
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String createTime = format.format(date);
String updateTime = createTime;
//连接数据库,并执行添加博客的操作
try {
connection = BaseDao.getConnection();
flag = blogdao.addBlog(connection,author,title,content,createTime,updateTime);
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
//关闭连接
BaseDao.closeResourse(connection,null,null);
}
return flag;
}
其它函数也差不多是这个逻辑,这里页不再赘述
dao层
数据访问层,一些基本的内容在上篇中也已经谈到,一般我们会把基本的数据库操作封装到一起,命名为BaseDao,其它Dao调用BaseDao的函数。仍然以添加博客为例,简单介绍下
@Override
public boolean addBlog(Connection connection,String author,String title, String content, String createTime, String updateTime) {
boolean flag = false;//返回给上层的表示,表示操作是否成功
PreparedStatement pstm = null;//预编译的sql语句
ResultSet result = null;//执行操作得到的结果集
int res;//执行操作得到的数据库返回的状态码,一般是受影响的行数,不过在mysql中可能是匹配的行数
if(connection != null) {
String sql = "insert into blog(author,title,content,create_time,update_time,is_deleted) values(?,?,?,?,?,?)";
//is_deleted默认为0,表示博客未被删除
Object[] params = {author,title,content,createTime,updateTime,0};//上面sql语句需要的参数
//执行数据更新操作
try {
res = BaseDao.update(connection, sql, result, pstm, params);
if (res == 1) {
flag = true;
}
//关闭资源
BaseDao.closeResourse(null, result, pstm);
}catch (Exception e){
e.printStackTrace();
}
}
return flag;
}
配置文件
主要是网站配置文件web.xml、数据库配置文件按db.properties和maven配置文件pom.xml
web.xml文件主要是声明过滤器和servlet,设置网站首页
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
metadata-complete="true">
<!-- 设置访问首页-->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- servlet声明 -->
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.shimmer.servlet.user.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>BlogServlet</servlet-name>
<servlet-class>com.shimmer.servlet.blog.BlogServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BlogServlet</servlet-name>
<url-pattern>/blog</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>PagingServlet</servlet-name>
<servlet-class>com.shimmer.servlet.blog.PagingServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>PagingServlet</servlet-name>
<url-pattern>/paging</url-pattern>
</servlet-mapping>
<!--字符编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.shimmer.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.shimmer.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/personalCenter/*</url-pattern>
</filter-mapping>
</web-app>
db.properties配置数据库连接必要信息
//driver是驱动
driver=com.mysql.cj.jdbc.Driver
//mysql后是MySQL服务器IP和端口,再后面是数据库名字,再是一些配置
url=jdbc:mysql://127.0.0.1:3306/blog_system?useUnicode=true&characterEncoding=utf-8&userSSL=false&serverTimezone=GMT%2B8
username=自己设的
password=自己设置的
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>MyBlogSystem</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
到这里,这个系统基本就介绍得差不多了,可能讲解的不是很详细,不过篇幅已经够长了(主要是博主快编不动了…)若有不清晰的地方,大家可以参考后面参考资料中的文章、视频等再研究研究。整个《自己动手搭网站》系列到此暂且也算是结束第一期了。虽然,部署维护什么的只是稍微提及,等后面有空再补上吧(等我学会再来编…)。
另外,功能方面做得比较简陋是因为考虑到后面可能升级到spring boot+vue的技术栈,spring+前端的模式也是目前比较主流的web技术栈之一,这套技术怎么说呢,其实也是从J2EE简化扩展来的,有了javaweb的基础后,springboot上手还是比较快的,但想深入了解,可能就非一时之功了。另外,前端框架的学习可能要稍微多花些功夫,虽然有现成的样式可以参考(ElementUI),但是要灵活使用还是比较困难的,这也是博主没有一次性跨到这套技术栈的原因,步子太大了,容易那啥来着…
最后,如果觉得本篇文章对您有帮助的话,别忘了点赞!!!
参考资料
1、菜鸟-Bootstrap5 教程
2、editor.md官网
3、富文本编辑器Editor.md入门
4、javaweb从入门到实战
5、Java Web -【分页功能】详解