javaweb——>个人博客项目
具体代码参考:java_blog
目录
- javaweb——>个人博客项目
- 具体代码参考:[java_blog](https://github.com/zhuyunfeisn/java-project/tree/master/java_Blog)
- 一.简单介绍
- 1.功能简介
- 2.使用的技术
- 二.项目准备
- 1.需要的资源
- 2.创建Web项目
- 3.数据库设计
- <1>用户表
- <2>文章表
- 三.项目开发
- 1.前置知识
- <1>Filter过滤器
- <2>简单Ajax和json介绍
- (1). Ajax
- B.json的序列化和反序列化
- 2.具体开发(后端服务)
- <1>设计数据库实体类和返回的JSONResponse对象
- (1)用户类
- (2)文章类
- (3)JSONResponse对象
- <2>设计自定义异常以及Servlet实现的父类
- (1)自定义异常
- (2)Servlet实现的父类以及异常的处理
- <3>设计数据库连接工具类
- (1).获取数据库连接
- (2)资源释放
- <4>设计JSON与java对象的序列化和反序列化方法
- <5>设计Filter过滤器工具
- <6>业务功能的具体实现
- 四.项目总结
- 1.使用的技术和功能
- <1>使用的技术
- <2>功能
- <3>项目执行的流程
- (1)请求网页
- (2)js发送ajax请求
- (3)Tomcat
- (4)Servlet
- (5)Servlet统一处理异常
一.简单介绍
1.功能简介
实现一个简易的博客功能,包括用户登录、,发表新文章,显示文章详情,显示文章列表,显示文章列表接口访问量(使用到多线程,暂时未做)。
2.使用的技术
二.项目准备
1.需要的资源
2.创建Web项目
Web项目的前期配置
3.数据库设计
<1>用户表
create table user (id int primary key auto_increment,username varchar (20) not null unique,password varchar (20) not null,nickname varchar(20),sex bit,birthday date,head varchar (50) comment '头像'
);
insert into user(username,password) values
('张三','1001'),
('李四','1002'),
('王五','1003');
<2>文章表
create table article(
id int primary key auto_increment,
title varchar (20) not null comment'标题',
content mediumtext not null comment '正文' ,
create_time timestamp default now(),
view_count int default 0,
user_id int ,
foreign key (user_id) references user(id)
);
insert into article( title, content, user_id) values
('快速排序','public...',1),
('冒泡排序','public...',1),
('选择排序','public...',1),
('归并排序','public...',2),
('插入排序','public...',2);
三.项目开发
1.前置知识
<1>Filter过滤器
Filter:拦截请求,过滤响应
<2>简单Ajax和json介绍
(1). Ajax
B.json的序列化和反序列化
参考:序列化和反序列化
2.具体开发(后端服务)
<1>设计数据库实体类和返回的JSONResponse对象
(1)用户类
@Getter
@Setter
@ToString
public class User {private Integer id;private String username;private String password;private String nickname;//昵称private Boolean sex;private Date birthday;private String head;//头像
}
(2)文章类
@Setter
@Getter
@ToString
public class Article {private Integer id;private String title;private String content;private Date createTime;private Integer viewCount;private Integer userId;
}
(3)JSONResponse对象
/*** http响应json数据,前后端统一约定的格式* 前端响应的状态码都是200,进入ajax的success来使用* 操作成功:(success:true, data :xxx)* 操作失败:(success:false code:xxx, message:xxx)*/
@Setter
@Getter
@ToString
public class JSONResponse {//业务操作是否成功private boolean success;//业务操作的消息码,一般来说,出现错误的错误码才有意义private String code;//业务操作的错误信息,给用户看的信息private String message;//业务数据:业务操作成功时,给前端ajax,success方法使用,解析响应json数据,渲染网页信息private Object data;}
<2>设计自定义异常以及Servlet实现的父类
(1)自定义异常
package org.example.exception;/*** 自定义异常类:业务代码抛自定义异常或者其他异常* 自定义异常返回给定的错误码,其他异常返回其他错误码*/public class APPexception extends RuntimeException{//给前端返回json字符串中,保存错误码private String code;//message父类自带,返回josn字符串中,保存错误信息给用户用public APPexception(String code,String message) {super(message);this.code=code;}public APPexception(String code,String message, Throwable cause) {super(message, cause);this.code=code;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}
}
(2)Servlet实现的父类以及异常的处理
package org.example.servlet;import org.example.exception.APPexception;
import org.example.modle.JSONResponse;
import org.example.util.JSONUtil;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;public abstract class AbstractBaseServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//设置请求体编码req.setCharacterEncoding("UTF-8");//设置请求体的编码resp.setCharacterEncoding("UTF-8");//为响应体设置数据类型(浏览器要采取什么方式执行)resp.setContentType("application/json");//获取当前URL,校验是否是登录或者注册,如果不是,进行session校验//session会话管理,除登录和注册接口,其他都需要登录后访问//通过req.getServletPath();获取请求服务路径JSONResponse json=new JSONResponse();//调用子类重写的方法try{Object data=process(req, resp);//子类的process执行完全没有抛异常,表示业务执行成功json.setSuccess(true);json.setData(data);}catch (Exception e){e.printStackTrace();//异常如何处理//自定义异常返回错误信息//json.setSuccess(false)不用设置了,因为new的时候初始化就是String code="UNKNOWN";String s="未知错误";if(e instanceof APPexception){//将异常信息取出赋给scode=((APPexception)e).getCode();s=e.getMessage();json.setCode(code);json.setMessage(s);}}PrintWriter pr=resp.getWriter();//将json信息打印pr.println(JSONUtil.serialize(json));pr.flush();pr.close();}protected abstract Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception;}
<3>设计数据库连接工具类
private static final String URL="jdbc:mysql://localhost:3306/servlet_blog?" +"user=root&password=sn20000904&useUnicode=true&characterEncoding=UTF-8";//数据库连接池private static final DataSource DS=new MysqlDataSource();//静态代码块初始化DSstatic {((MysqlDataSource)DS).setUrl(URL);}
(1).获取数据库连接
//获取数据库连接public static Connection getConnection(){try {return DS.getConnection();} catch (SQLException e) {e.printStackTrace();//抛自定义异常throw new APPexception("SQL001","获取数据库连接异常",e);}}
(2)资源释放
//资源释放public static void close(Connection c, Statement s, ResultSet r){try {if(r!=null)r.close();if(s!=null)s.close();if(c!=null)c.close();} catch (SQLException e) {throw new APPexception("SQL002","数据库释放资源出错",e);}}public static void close(Connection c, Statement s){close(c,s ,null );}
<4>设计JSON与java对象的序列化和反序列化方法
package org.example.util;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;
import java.io.InputStream;public class JSONUtil {//定义一个ObjectMapper类,主要实现java类和json对象之间的转换private static final ObjectMapper MAPPER = new ObjectMapper();/*** JSON序列化,将java对象序列化为json字符串** @param o java对象* @return json字符串*///序列化,返回json字符串public static String serialize(Object o) {try {return MAPPER.writeValueAsString(o);} catch (JsonProcessingException e) {e.printStackTrace();throw new RuntimeException("json序列化失败" + o);}}/*** 反序列化操作** @param is 输入流* @param clazz 指定要反序列化的类型* @param <T>* @return 反序列化对象*///反序列化(使用输入流InputStream获取输入字符串)public static <T> T deserialize(InputStream is, Class<T> clazz) {try {return MAPPER.readValue(is, clazz);} catch (IOException e) {e.printStackTrace();throw new RuntimeException("json反序列化失败" + clazz.getName());}}
}
<5>设计Filter过滤器工具
package org.example.filter;import org.example.modle.JSONResponse;
import org.example.util.JSONUtil;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;//import javax.servlet.annotation.WebFilter;/*** 配置用户统一会话管理的过滤器,匹配所有请求路径* 服务端资源:/login不用校验session,其他都要校验,如果不通过,返回401,响应资源随便加* 前端资源:/jsp/校验session,不通过重定向到登录页面* /js/,/static/,/view/,全部不校验*/
@WebFilter("/*")
public class LoginFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}/***每次http请求匹配到过滤器路径时,会执行该过滤器的doFilter方法*如果我们要往下执行,是调用filterChain.doFilter(request,response)*否则需要自行处理响应内容*/@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {//类强制转换HttpServletRequest req=(HttpServletRequest) request;HttpServletResponse resp=(HttpServletResponse) response;String servletPath=req.getServletPath(); //获取当前请求的服务路径//先处理不需要登录允许访问的:往下执行,继续调用if(servletPath.startsWith("/js/")||servletPath.startsWith("/static/")||servletPath.startsWith("/view/")||servletPath.equals("/login")){chain.doFilter(request, response);}else {//先获取Session对象,没有就返回空HttpSession session=req.getSession(false);//验证用户是否登录,如果没有登录,还需要根据前端和后端做不同的处理if(session==null||session.getAttribute("user")==null){//前端重定向到登陆页面if(servletPath.startsWith("/jsp/")){//使用绝对路径,重定向resp.sendRedirect(basePath(req)+"/view/login.html");}else {//后端返回401状态码resp.setStatus(401);//设置请求体的编码resp.setCharacterEncoding("UTF-8");//为响应体设置数据类型(浏览器要采取什么方式执行)resp.setContentType("application/json");JSONResponse json=new JSONResponse();json.setCode("LOG000");json.setMessage("用户没有登录,不允许访问");PrintWriter pw=resp.getWriter();pw.println(JSONUtil.serialize(json));pw.flush();pw.close();}}else {//敏感资源,但已登录,允许继续执行chain.doFilter(request, response);}}}/*** 根据http请求,动态的获取访问路径(服务路径之前的部分)*/public static String basePath(HttpServletRequest req){String schema=req.getScheme();//获取httpString host=req.getServerName();//主机ip或域名int port=req.getServerPort();//服务器端口号String contextPath=req.getContextPath();//获取应用上下文路径return schema+"://"+host+":"+port+contextPath;}@Overridepublic void destroy() {}
}
<6>业务功能的具体实现
用户登录、,发表新文章,显示文章详情,显示文章列表,删除文章,修改文章,显示文章列表接口访问量(使用到多线程,暂时未做)。
源代码参考:java_blog
Servlet相关操作位于servlet包下,数据库查询操作位于DAO包下
四.项目总结
1.使用的技术和功能
<1>使用的技术
Servlet,MySQL,jackson,ajax,UEditor
<2>功能
1.已完成:用户登录、,发表新文章,显示文章详情,显示文章列表,删除文章,修改文章
Servlet模板方法:结合jackson序列化响应的统一格式,结合自定义异常完成统一的异常处理
敏感资源的查看,使用到Session以及Filter过滤器
扩展:
Filter过滤器完成用户会话的统一管理
富文本编辑器实现博客文章的展示,以及图片上传
2.未完成:使用到多线程
数据库连接池,通过双重校验锁的单例模式来完成
文章列表接口的统计访问量:(内存实现)在文章列表接口中,定义一个变量来保存访问量,在定义一个获取访问量的接口,请求时返回访问量的内容
<3>项目执行的流程
(1)请求网页
(2)js发送ajax请求
(3)Tomcat
(4)Servlet