spring-boot 博客系统

Spring-boot 个人博客系统

GitHub:https://github.com/Werdio66/myblog
spring-boot 博客系统_第1张图片

一、环境搭建

gradle 依赖:

	implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-aop'

    compile group: 'com.github.pagehelper', name: 'pagehelper-spring-boot-starter', version: '1.2.13'

    compile group: 'com.atlassian.commonmark', name: 'commonmark', version: '0.14.0'
    compile group: 'com.atlassian.commonmark', name: 'commonmark-ext-gfm-tables', version: '0.14.0'
    compile group: 'com.atlassian.commonmark', name: 'commonmark-ext-heading-anchor', version: '0.14.0'


    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.1'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'mysql:mysql-connector-java'
    annotationProcessor 'org.projectlombok:lombok'

二、数据库设计

三、效果展示

前端界面

首页:博客分页展示,分类,标签展示,最新推荐,搜索
spring-boot 博客系统_第2张图片

分类:展示所有的分类和当前分类下的博客数,并分页显示每一个分类的博客

spring-boot 博客系统_第3张图片

标签:展示所有的标签和当前标签下的博客数,并分页显示每一个标签的博客

spring-boot 博客系统_第4张图片

归档:按时间分类

spring-boot 博客系统_第5张图片

关于我:从配置文件中读取个人信息

spring-boot 博客系统_第6张图片

后台界面

博客管理

  • 分页显示博客信息,可以按标题或者分类查询

spring-boot 博客系统_第7张图片

  • 博客的发布,集成了 Markdown 编辑器,可以选择原创或者转载,分类,标签等

spring-boot 博客系统_第8张图片

分类管理:分类的分页显示,增加分类,删除和批量删除分类

spring-boot 博客系统_第9张图片

标签管理:标签的分页显示,增加标签,删除和批量删除未实现,和分类管理类似

spring-boot 博客系统_第10张图片

四、功能实现

1、登录拦截

​ 对 /admin/** 路径下的请求做登录拦截,后台管理页面需要权限

/**
 *  登录请求拦截
 */
@Slf4j
public class LoginFilter implements Filter {

    private String noAuthUrl = "/admin";

    private Set<String> exclusionUrlSet = new ConcurrentSkipListSet<>();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String url = filterConfig.getInitParameter("loginUrl");

        exclusionUrlSet.add(noAuthUrl);
        exclusionUrlSet.add("/login");
        exclusionUrlSet.add("redirect:/main.html");
        exclusionUrlSet.add("redirect:/admin");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        String uri = req.getRequestURI();
        log.info("【LongFilter】当前请求 uri = {}", uri);

        // 只对 /admin/** 下的资源拦截
        if (!uri.startsWith(noAuthUrl)){
            chain.doFilter(request, response);
            return;
        }
		// 自己添加的 url,可以配置到 yml 文件中
        if (exclusionUrlSet.contains(uri)){
            chain.doFilter(request, response);
            return;
        }

        String username = (String) req.getSession().getAttribute("loginUser");
        log.info("session 中的用户:{}", username);

        if (StringUtils.isEmpty(username)){
            log.error("您没有访问权限,请先登录");
            resp.sendRedirect("/admin");
            return;
        }

        // 登录成功
        chain.doFilter(request, response);
    }
}
2、日志记录

​ 在 controller 层每一个请求之前记录请求的 ip,uri,方法名

@Slf4j
@Aspect
@Component
public class LogAspect {

    @Pointcut("execution(* com._520.myblog.controller.*.*(..))")
    public void log(){}

    @Before(value = "log()")
    public void before(JoinPoint joinPoint){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        String uri = request.getRequestURL().toString();
        String ip = request.getRemoteAddr();
        String method = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        RequestLog requestLog = new RequestLog(uri, ip, method, args);

        log.info("{}", requestLog);
    }

    @AfterReturning(returning = "result", pointcut = "log()")
    public void afterReturn(Object result){
        log.info("返回的页面:{}", result);
    }


    @AllArgsConstructor
    private static class RequestLog{
        private final String url;
        private final String ip;
        private final String reqMethod;
        private final Object[] args;

        @Override
        public String toString() {
            return "RequestLog{" +
                    "url='" + url + '\'' +
                    ", ip='" + ip + '\'' +
                    ", reqMethod='" + reqMethod + '\'' +
                    ", args=" + Arrays.toString(args) +
                    '}';
        }
    }
}

3、评论管理
   @Override
    public List<Comment> queryAllByBlogId(Long id) {

        // 查询指定博客对应的所有评论
        List<Comment> allComments = commentMapper.queryAllByBlogId(id);

        // 存放父评论
        List<Comment> parentComments = new ArrayList<>();

        Map<Long, Comment> map = new HashMap<>();

        Map<Long, Comment> allComms = new HashMap<>();

        // 找出父评论
        for (Comment comment : allComments){
            if (comment.getParentId() == -1){
                parentComments.add(comment);
                // 将父评论存入 map 中,便于设置子评论
                map.put(comment.getId(), comment);
            }

            allComms.put(comment.getId(), comment);
        }

        // 设置子评论
        for (Comment comment : allComments){

            if (comment.getParentId() != -1){
                // 取出当前评论的父评论
                Comment parent = map.get(comment.getParentId());
                // 父评论有可能不是根结点,所以从所有的评论中取获取
                if (parent == null){
                    parent = allComms.get(comment.getParentId());
                }
                // 将当前评论加到父评论的子评论中
                 parent.getChildComments().add(comment);
                // 为当前评论设置父评论
                comment.setParentName(parent.getNickName());
            }
        }

        for (int i = 0; i < parentComments.size(); i++) {
            merge(parentComments.get(i));
        }

//        parentComments.forEach(System.out::println);
        return parentComments;
    }

    private void merge(Comment parentComment) {
        if (parentComment.getChildComments().isEmpty()){
            return;
        }

        List<Comment> childComments = parentComment.getChildComments();
        for (int i = 0; i < childComments.size(); i++) {
            merge(childComments.get(i));
            parentComment.getChildComments().addAll(childComments.get(i).getChildComments());
        }
    }

五、优化

1、可以将前端展示的博客存放到 redis 中,增加速度
2、使用 ab 进行压力测试

​ 对博客主页进行压测

spring-boot 博客系统_第11张图片

对博客详情页进行压力测试

spring-boot 博客系统_第12张图片

你可能感兴趣的:(项目总结)