javaweb——>个人博客项目

javaweb——>个人博客项目

具体代码参考:java_blog

一.简单介绍

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 {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      doPost(req, resp);
    }

    @Override
    protected 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){
                //将异常信息取出赋给s
                code=((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();
  //静态代码块初始化DS
   static {
        ((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 {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    /**
     *每次http请求匹配到过滤器路径时,会执行该过滤器的doFilter方法
     *如果我们要往下执行,是调用filterChain.doFilter(request,response)
     *否则需要自行处理响应内容
     */
    @Override
    public 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();//获取http
        String host=req.getServerName();//主机ip或域名
        int port=req.getServerPort();//服务器端口号
        String contextPath=req.getContextPath();//获取应用上下文路径
        return schema+"://"+host+":"+port+contextPath;
    }
    @Override
    public 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

在这里插入图片描述
在这里插入图片描述

(5)Servlet统一处理异常

在这里插入图片描述

  • 10
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Serendipity sn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值