[SpringBoot3]博客管理系统(源码放评论区了)

news2025/3/10 5:25:04

八、博客管理系统

  • 创建新的SpringBoot项目,综合运用以上知识点,做一个文章管理的后台应用。
  • 依赖:
    • Spring Web
    • Lombok
    • Thymeleaf
    • MyBatis Framework
    • MySQL Driver
    • Bean Validation
    • hutool
  • 需求:文章管理工作,发布新文章,编辑文章,查看文章内容等

8.1配置文件

1.组织配置文件

app-base.yml

article:
  #最低阅读数
  low-read: 10
  #首页显示记录的数量
  top-read: 20

db.properties(一定要用properties)

#配置数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=030522
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=10
#获取连接时,检测语句
spring.datasource.hikari.connection-test-query=select 1
spring.datasource.hikari.connection-timeout=20000
#其他属性
spring.datasource.hikari.data-source-properties.cachePrepStmts=true
spring.datasource.hikari.data-source-properties.dataSource.cachePrepStmtst=true
spring.datasource.hikari.data-source-properties.dataSource.prepStmtCacheSize=250
spring.datasource.hikari.data-source-properties.dataSource.prepStmtCacheSqlLimit=2048
spring.datasource.hikari.data-source-properties.dataSource.useServerPrepStmts=true

<img src=“C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230902103338099.png” alt="image-20230902103338099" style="zoom:80%;" />

8.2视图文件

2.logo文件

  • favicon.ico 放在 static/ 根目录下

3.创建模板页面

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

articleList.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="icon" th:href="@{/favicon.ico}" type="image/x-icon"/>
    <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<div style="margin-left: 200px">
    <h3>阅读最多的20篇文章</h3>
    <table border="1px" cellpadding="0px" cellspacing="0px">
        <thead>
        <th>选择</th>
        <th>序号</th>
        <th>标题</th>
        <th>副标题</th>
        <th>已读数</th>
        <th>发布时间</th>
        <th>最后修改时间</th>
        <th>操作</th>
        </thead>
        <tbody>
        <tr th:each="article , loopStatus : ${articleList}">
            <td><input type="checkbox" th:value="${article.id}"></td>
            <td th:text="${loopStatus.index + 1}"></td>
            <td th:text="${article.title}"></td>
            <td th:text="${article.summary}"></td>
            <td th:text="${article.readCount}"></td>
            <td th:text="${article.createTime}"></td>
            <td th:text="${article.updateTime}"></td>
            <td><a th:href="@{/article/get(id=${article.id})}">编辑文章</a> </td>
        </tr>
        <tr>
            <td colspan="8">
                <table width="100%">
                    <tr>
                        <td><button id="add" onclick="addArticle()">发布新文章</button></td>
                        <td><button id="del" onclick="deleteArticle()">删除文章</button></td>
                        <td><button id="view" onclick="overView()">文章概览</button></td>
                    </tr>
                </table>
            </td>
        </tr>
        </tbody>
    </table>
    <form id="frm" th:action="@{/view/addArticle}" method="get"></form>

    <form id="delFrm" th:action="@{/article/remove}" method="post">
        <input type="hidden" name="ids" id="ids" value="">
    </form>
</div>
<script>
    function addArticle(){
        $("#frm").submit();
    }
    function deleteArticle(){
        var ids=[];
        $("input[type='checkbox']:checked").each((index,item)=>{
            ids.push(item.value);
        })
        if (ids.length==0){
            alert("请选择文章");
            return;
        }
        $("#ids").val(ids);
        $("#delFrm").submit();
    }
    function overView(){
        var ids = [];
        $("input[type='checkbox']:checked").each( (index,item) =>{
            ids.push(item.value);
        })
        if( ids.length != 1){
            alert("请选择一个文章查看");
            return;
        }
        $.get("../article/detail/overview",{id: ids[0]}, (data,status)=>{
            alert(data);
        })
    }
</script>
</body>
</html>

addArticle.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="icon" th:href="@{/favicon.ico}" type="image/x-icon"/>
</head>
<body>
<div style="margin-left: 200px">
    <h3>发布新的文章</h3>
    <form th:action="@{/article/add}" method="post">
        <table>
            <tr>
                <td>标题</td>
                <td><input type="text" name="title"></td>
            </tr>
            <tr>
                <td>副标题</td>
                <td><input type="text" name="summary" size="50"></td>
            </tr>
            <tr>
                <td>内容</td>
                <td><textarea name="content" cols="60" rows="20"></textarea></td>
            </tr>
        </table>
        <br/>
        <input type="submit" value="发布新文章" style="margin-left: 200px">
    </form>
</div>
</body>
</html>

editArticle.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="icon" th:href="@{/favicon.ico}" type="image/x-icon"/>
</head>
<body>
<div style="margin-left: 200px">
    <h3>修改文章</h3>
    <form th:action="@{/article/edit}" method="post">
        <table>
            <tr>
                <td>标题</td>
                <td><input type="text" name="title" th:value="${article.title}"></td>
            </tr>
            <tr>
                <td>副标题</td>
                <td><input type="text" name="summary" size="50" th:value="${article.summary}"></td>
            </tr>
            <tr>
                <td>内容</td>
                <td><textarea name="content" cols="60" rows="20" th:text="${article.content}"></textarea></td>
            </tr>
        </table>
        <br/>
        <input type="hidden" th:value="${article.id}" name="id">
        <input type="submit" value="确认修改" style="margin-left: 200px">
    </form>
</div>
</body>
</html>

bind.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="icon" th:href="@{/favicon.ico}" type="image/x-icon"/>
</head>
<body>
<h3>输入异常</h3>
<table border="1px">
    <thead>
    <th>字段</th>
    <th>描述</th>
    </thead>
    <tbody>
    <tr th:each="err:${errors}">
        <td th:text="${err.field}"></td>
        <td th:text="${err.defaultMessage}"></td>
    </tr>
    </tbody>
</table>
</body>
</html>

error.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="icon" th:href="@{/favicon.ico}" type="image/x-icon"/>
</head>
<body>
<h3>请求错误</h3>
<h3 th:text="${msg}"></h3>
</body>
</html>

8.3Java代码

4.java代码

model包

ArticleDTO

package com.hhb.blog.model.dto;

import lombok.Data;

@Data
public class ArticleDTO {
    private Integer id;
    private String title;
    private String summary;
    private String content;
}

ArticleAndDetailMap

package com.hhb.blog.model.map;

import lombok.Data;

@Data
public class ArticleAndDetailMap {
    private Integer id;
    private String title;
    private String summary;
    private String content;
}

ArticleParam

package com.hhb.blog.model.param;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;

@Data
public class ArticleParam {
    //使用JSR303注解
    public static interface AddArticle{};
    public static interface EditArticle{};

    @NotNull(message = "修改时必须有id",groups = {EditArticle.class})
    @Min(value = 1,message = "文章id大于{value}",groups = {EditArticle.class})
    private Integer id;

    @NotBlank(message = "请输入文章标题",groups = {AddArticle.class,EditArticle.class})
    @Size(min = 2,max = 20,message = "文章标题在{min}-{max}",groups = {AddArticle.class,EditArticle.class})
    private String title;

    @NotBlank(message = "请输入文章副标题",groups = {AddArticle.class,EditArticle.class})
    @Size(min = 10,max=30,message = "文章副标题在{min}-{max}",groups = {AddArticle.class,EditArticle.class})
    private String summary;

    @NotBlank(message = "请输入文章内容",groups = {AddArticle.class,EditArticle.class})
    @Size(min = 20,max = 8000,message = "文章内容至少{min}字,最多{max}字",groups = {AddArticle.class,EditArticle.class})
    private String content;

}

ArticleDeatilPO

package com.hhb.blog.model.po;

import lombok.Data;

@Data
public class ArticleDetailPO {
  private Integer id;
  private Integer articleId;
  private String content;
}

ArticlePO

package com.hhb.blog.model.po;

import lombok.Data;

import java.time.LocalDateTime;

@Data
public class ArticlePO {
  private Integer id;
  private Integer userId;
  private String title;
  private String summary;
  private Integer readCount;
  private LocalDateTime createTime;
  private LocalDateTime updateTime;
}

ArticleVO

package com.hhb.blog.model.po;

import lombok.Data;

import java.time.LocalDateTime;

@Data
public class ArticlePO {
  private Integer id;
  private Integer userId;
  private String title;
  private String summary;
  private Integer readCount;
  private LocalDateTime createTime;
  private LocalDateTime updateTime;
}

mapper包

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ArticleMapper

package com.hhb.blog.mapper;

import com.hhb.blog.model.dto.ArticleDTO;
import com.hhb.blog.model.map.ArticleAndDetailMap;
import com.hhb.blog.model.po.ArticleDetailPO;
import com.hhb.blog.model.po.ArticlePO;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface ArticleMapper {
    //查询首页需要的文章列表
    @Select("""
            select id,user_id ,title,summary, read_count , create_time, update_time
            from article
            where read_count >= #{lowRead}
            order by read_count desc 
            limit #{topRead}
            """)
    List<ArticlePO> topSortByReadCount(Integer lowRead, Integer topRead);

    //添加文章
    @Insert("""
            insert into article(user_id, title, summary, read_count, create_time, update_time)
            values(#{userId},#{title},#{summary},#{readCount},#{createTime},#{updateTime})
            """)
    //主键自增
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    int insertArticle(ArticlePO articlePO);

    //添加文章内容
    @Insert("""
            insert into article_detail(article_id, content) 
            values(#{articleId},#{content})
            """)
    int insertArticleDetail(ArticleDetailPO articleDetailPO);

    //两表连接,根据主键查询文章
    @Select("""
            select article.id,title,summary,content
            from article,article_detail
            where article.id=article_detail.article_id and article_id=#{id}
            """)
    ArticleAndDetailMap selectArticleAndDetail(Integer id);

    //修改文章属性
    @Update("""
            update article 
            set title=#{title},summary=#{summary},update_time=#{updateTime}
            where id=#{id}
            """)
    int updateArticle(ArticlePO articlePO);

    //更新文章内容
    @Update("""
            update article_detail set content=#{content} where article_id=#{articleId}
            """)
    int updateArticleDetail(ArticleDetailPO articleDetailPO);

    //删除文章
    @Delete("""
            <script>
              delete from article where id in 
              <foreach item="id" collection="idList" open="(" separator="," close=")">
                  #{id}
              </foreach>
            </script>
            """)
    int deleteArticle(List<Integer> idList);

    //删除文章内容
    @Delete("""
            <script>
              delete from article_detail where article_id in 
              <foreach item="id" collection="idList" open="(" separator="," close=")">
                  #{id}
              </foreach>
            </script>
            """)
    int deleteDetail(List<Integer> idList);

    //根据id查询内容
    @Select("""
      select id,article_id,content from article_detail
      where article_id = #{articleId}
      """)
    ArticleDetailPO selectArticleDetailByArticleId(Integer articleId);
}

service包

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ArticleService

package com.hhb.blog.service;

import com.hhb.blog.model.dto.ArticleDTO;
import com.hhb.blog.model.po.ArticlePO;

import java.util.List;

public interface ArticleService {

    //获取首页文章列表
    List<ArticlePO> queryTopArticle();

    //发布文章(article,article_detail)
    boolean addArticle(ArticleDTO articleDTO);

    //根据主键查询文章
    ArticleDTO queryByArticleId(Integer id);

    //修改文章属性和内容
    boolean modifyArticle(ArticleDTO articleDTO);

    //删除文章
    boolean removeArticle(List<Integer> idList);

    //查询文章内容前20个字符
    String queryTop20Content(Integer id);
}

ArticleServiceImpl

package com.hhb.blog.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.hhb.blog.mapper.ArticleMapper;
import com.hhb.blog.model.dto.ArticleDTO;
import com.hhb.blog.model.map.ArticleAndDetailMap;
import com.hhb.blog.model.po.ArticleDetailPO;
import com.hhb.blog.model.po.ArticlePO;
import com.hhb.blog.service.ArticleService;
import com.hhb.blog.settings.ArticleSettings;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Random;

@Service
@RequiredArgsConstructor
public class ArticleServiceImpl implements ArticleService {

    private final ArticleMapper articleMapper;
    private final ArticleSettings articleSettings;

    //构造注入
 /* public ArticleServiceImpl(ArticleMapper articleMapper) {
    this.articleMapper = articleMapper;
  }*/

    @Override
    public List<ArticlePO> queryTopArticle() {
        Integer lowRead = articleSettings.getLowRead();
        Integer topRead = articleSettings.getTopRead();
        return articleMapper.topSortByReadCount(lowRead, topRead);
    }

    //发布文章
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean addArticle(ArticleDTO articleDTO) {
        //文章
        ArticlePO articlePO = new ArticlePO();
        articlePO.setTitle(articleDTO.getTitle());
        articlePO.setSummary(articleDTO.getSummary());
        articlePO.setCreateTime(LocalDateTime.now());
        articlePO.setUpdateTime(LocalDateTime.now());
        articlePO.setReadCount(new Random().nextInt(1000));
        articlePO.setUserId(new Random().nextInt(5000));
        int addArticle = articleMapper.insertArticle(articlePO);

        //文章内容
        ArticleDetailPO articleDetailPO = new ArticleDetailPO();
        articleDetailPO.setArticleId(articlePO.getId());
        articleDetailPO.setContent(articleDTO.getContent());
        int addDetail = articleMapper.insertArticleDetail(articleDetailPO);

        return (addDetail + addArticle) == 2 ? true : false;
    }

    @Override
    public ArticleDTO queryByArticleId(Integer id) {
        //文章属性,内容
        ArticleAndDetailMap mapper = articleMapper.selectArticleAndDetail(id);
        //转为DTO,两种方式
        /*ArticleDTO articleDTO = new ArticleDTO();
        articleDTO.setTitle(mapper.getTitle());
        articleDTO.setContent(mapper.getContent());
        articleDTO.setSummary(mapper.getSummary());
        articleDTO.setId(mapper.getId());*/
        ArticleDTO articleDTO = BeanUtil.copyProperties(mapper, ArticleDTO.class);
        return articleDTO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean modifyArticle(ArticleDTO articleDTO) {
        //修改文章属性
        ArticlePO articlePO = new ArticlePO();
        articlePO.setTitle(articleDTO.getTitle());
        articlePO.setSummary(articleDTO.getSummary());
        articlePO.setUpdateTime(LocalDateTime.now());
        articlePO.setId(articleDTO.getId());
        int article = articleMapper.updateArticle(articlePO);

        //修改文章内容
        ArticleDetailPO articleDetailPO = new ArticleDetailPO();
        articleDetailPO.setArticleId(articleDTO.getId());
        articleDetailPO.setContent(articleDTO.getContent());
        int detail = articleMapper.updateArticleDetail(articleDetailPO);

        return (article + detail) == 2 ? true : false;
    }

    //删除文章属性、内容
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeArticle(List<Integer> idList) {
        int article = articleMapper.deleteArticle(idList);
        int detail = articleMapper.deleteDetail(idList);
        return article == detail ? true : false;
    }

    //查询文章内容前20个字符
    @Override
    public String queryTop20Content(Integer id) {
        ArticleDetailPO articleDetailPO = articleMapper.selectArticleDetailByArticleId(id);
        String content = "无内容";
        if( articleDetailPO != null ){
            content = articleDetailPO.getContent();
            if(StringUtils.hasText(content)){
                //content = content.substring(0, content.length() >= 20 ? 20 : content.length() );
                content = content.substring(0, 20 );
            }
        }
        return content;
    }
}

controller包

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ArticleController

package com.hhb.blog.controller;

import cn.hutool.core.bean.BeanUtil;
import com.hhb.blog.format.IdType;
import com.hhb.blog.handler.exp.IdTypeException;
import com.hhb.blog.model.dto.ArticleDTO;
import com.hhb.blog.model.param.ArticleParam;
import com.hhb.blog.model.po.ArticlePO;
import com.hhb.blog.model.vo.ArticleVO;
import com.hhb.blog.service.ArticleService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RequiredArgsConstructor
@Controller
public class ArticleController {

    private final ArticleService articleService;

    @GetMapping(value = {"/", "/article/hot"})
    public String showHotArticle(Model model) {
        List<ArticlePO> articlePOList = articleService.queryTopArticle();
        //转为VO  .hutool工具类
        List<ArticleVO> articleVOList = BeanUtil.copyToList(articlePOList, ArticleVO.class);

        //添加数据
        model.addAttribute("articleList", articleVOList);

        //视图
        return "/blog/articleList";
    }

    //发布新文章
    @PostMapping("/article/add")
    //接收对象类型参数
    public String addArticle(@Validated(ArticleParam.AddArticle.class) ArticleParam param) {
        ArticleDTO articleDTO = new ArticleDTO();
        articleDTO.setContent(param.getContent());
        articleDTO.setSummary(param.getSummary());
        articleDTO.setTitle(param.getTitle());
        boolean add = articleService.addArticle(articleDTO);
        return "redirect:/article/hot";
    }

    //查询文章内容
    @GetMapping("/article/get")
    public String queryById(Integer id, Model model) {
        if (id != null && id > 0) {
            ArticleDTO articleDTO = articleService.queryByArticleId(id);
            //DTO-VO
            ArticleVO articleVO = BeanUtil.copyProperties(articleDTO, ArticleVO.class);
            //添加数据
            model.addAttribute("article", articleVO);
            //视图
            return "/blog/editArticle";
        } else {
            return "/blog/error/error";
        }
    }

    //更新文章
    @PostMapping("/article/edit")
    public String modifyArticle(@Validated(ArticleParam.EditArticle.class) ArticleParam param) {
        /*ArticleDTO articleDTO = new ArticleDTO();
        articleDTO.setId(param.getId());
        articleDTO.setTitle(param.getTitle());
        articleDTO.setSummary(param.getSummary());
        articleDTO.setContent(param.getContent());*/
        ArticleDTO articleDTO = BeanUtil.copyProperties(param, ArticleDTO.class);
        boolean edit = articleService.modifyArticle(articleDTO);
        return "redirect:/article/hot";
    }

    //删除文章
    @PostMapping("/article/remove")
    //public String removeArticle(Integer ids[])
    public String removeArticle(@RequestParam("ids") IdType idType) {
        if (idType == null) {
            throw new IdTypeException("ID为空");
        }
        boolean delete = articleService.removeArticle(idType.getIdList());
        return "redirect:/article/hot";
    }

    //预览文章
    @GetMapping("/article/detail/overview")
    @ResponseBody
    public String queryDetail(Integer id) {
        String top20Content = "无ID";
        if (id != null) {
            top20Content = articleService.queryTop20Content(id);
        }
        return top20Content;
    }
}

异常处理包

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

GlobalExceptionHandler

package com.hhb.blog.handler;

import java.util.List;

import com.hhb.blog.handler.exp.IdTypeException;
import org.springframework.ui.Model;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {
    //处理JSR303
    @ExceptionHandler({BindException.class})
    public String handlerBindException(BindException bindException, Model model) {
        BindingResult bindingResult = bindException.getBindingResult();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        model.addAttribute("errors", fieldErrors);
        return "/blog/error/bind";
    }

    @ExceptionHandler({IdTypeException.class})
    public String handleIdTypeException(IdTypeException idTypeException, Model model) {
        model.addAttribute("msg", idTypeException.getMessage());
        return "/blog/error/error";
    }

    @ExceptionHandler({Exception.class})
    public String handleDefaultException(Exception e, Model model) {
        model.addAttribute("msg", "请稍后重试!");
        return "/blog/error/error";
    }
}

BlogRootException

package com.hhb.blog.handler.exp;

public class BlogRootException extends RuntimeException{
    public BlogRootException() {
    }

    public BlogRootException(String message) {
        super(message);
    }
}

IdTypeException

package com.hhb.blog.handler.exp;

public class IdTypeException extends BlogRootException {
    public IdTypeException() {
    }

    public IdTypeException(String message) {
        super(message);
    }
}

数据格式化包

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

IdType

package com.hhb.blog.format;

import lombok.Data;

import java.util.List;

@Data
public class IdType {
    private List<Integer> idList;
}

IdTypeFormatter

package com.hhb.blog.format;

import org.springframework.format.Formatter;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class IdTypeFormatter implements Formatter<IdType> {
    @Override
    public IdType parse(String text, Locale locale) throws ParseException {
        IdType idType = null;
        if (StringUtils.hasText(text)) {
            List<Integer> ids = new ArrayList<>();
            for (String id : text.split(",")) {
                ids.add(Integer.parseInt(id));
            }
            idType = new IdType();
            idType.setIdList(ids);
        }
        return idType;
    }

    @Override
    public String print(IdType object, Locale locale) {
        return null;
    }
}

设置包

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ArticleSettings

package com.hhb.blog.settings;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "article")
public class ArticleSettings {
    private Integer lowRead;
    private Integer topRead;
}

MvcSettings

package com.hhb.blog.settings;

import com.hhb.blog.format.IdTypeFormatter;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MvcSettings implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/view/addArticle").setViewName("/blog/addArticle");
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new IdTypeFormatter());
    }
}

启动类

package com.hhb.blog;

import com.hhb.blog.settings.ArticleSettings;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableTransactionManagement
@MapperScan(basePackages = "com.hhb.blog.mapper")
@EnableConfigurationProperties({ArticleSettings.class})
@SpringBootApplication
public class Springboot20BlogAdminApplication {

    public static void main(String[] args) {
        SpringApplication.run(Springboot20BlogAdminApplication.class, args);
    }

}

8.4界面展示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/965157.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Web安全——穷举爆破上篇(仅供学习)

Web安全 一、概述二、常见的服务1、burpsuite 穷举后台密码2、burpsuite 对 webshell 穷举破解密码3、有 token 防御的网站后台穷举破解密码3.1 burpsuite 设置宏获取 token 对网站后台密码破解3.2 编写脚本获取token 对网站后台密码破解 4、针对有验证码后台的穷举方法4.1 coo…

华为云云服务器评测 [Vue3 博物馆管理系统] 使用Vue3、Element-plus菜单组件构建轮播图

系列文章目录 第一章 定制上中下&#xff08;顶部菜单、底部区域、中间主区域显示&#xff09;三层结构首页 第二章 使用Vue3、Element-plus菜单组件构建菜单 第三章 使用Vue3、Element-plus菜单组件构建轮播图 [第四章 使用Vue3、Element-plus菜单组件构建组图文章] 华为云云…

WebSocket(一)

一.什么是WebSocket 【1】WebSocket是一种协议&#xff0c;设计用于提供低延迟&#xff0c;全双工和长期运行的连接。 全双工&#xff1a;通信的两个参与方可以同时发送和接收数据&#xff0c;不需要等待对方的响应或传输完成。 【2】比较 传统通信&#xff08;http协议&am…

QML与C++的交互操作

QML旨在通过C 代码轻松扩展。Qt QML模块中的类使QML对象能够从C 加载和操作&#xff0c;QML引擎与Qt元对象系统集成的本质使得C 功能可以直接从QML调用。这允许开发混合应用程序&#xff0c;这些应用程序是通过混合使用QML&#xff0c;JavaScript和C 代码实现的。除了从QML访问…

15年检测生涯转瞬即逝,复旦MBA助力邢国芒实现质量强国梦

日月光华&#xff0c;旦复旦兮&#xff01;复旦MBA如同一个巨大的磁场&#xff0c;吸引了诸多来自五湖四海、各行各业的职场精英。从初入职场的青涩懵懂到如今的独当一面专业干练&#xff0c;他们逐渐成长为职场的中坚力量&#xff0c;在各自领域内发光发热。作为新时代的青年&…

多线程应用——阻塞队列

阻塞队列 文章目录 阻塞队列1.队列的概念2.阻塞队列3.现实中的例子4.消息队列5.使用队列的优势1.解耦2.削峰填谷3.异步操作 6.实现 1.队列的概念 一种先进先出的数据结构 2.阻塞队列 队列写元素是从队尾插入&#xff0c;从对头取出 当插入元素时&#xff0c;先判断一下队列…

数据治理-数据管理框架

DMBOK2提出的想法和概念在不同的组织中都可以应用&#xff0c;组织所采用的数据管理方法取决于某些关键要素&#xff0c;如其所处行业、所应用的数据范围、企业文化、成熟度、战略、愿景以及待解决的问题和挑战。 战略一致性模型和阿姆斯特丹模型&#xff0c;展示了组织管理数…

算法通关村第十二关——字符串反转问题解析

前言 字符串反转是关于字符串算法里的重要问题&#xff0c;虽然不是太难&#xff0c;但需要考虑到一些边界问题。本篇文章就对几道字符串反转题目进行分析。 1.反转字符串 力扣344题&#xff0c;编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数…

opencv 提取选中区域内指定hsv颜色的水印

基于《QT 插件化图像算法研究平台》做的功能插件。提取选中区域内指定hsv颜色的水印。 《QT 插件化图像算法研究平台》有个HSV COLOR PICK功能&#xff0c;可以很直观、方便地分析出水印 的hsv颜色&#xff0c;比如, 蓝色&#xff1a;100,180,0,255,100,255。 然后利用 opencv …

JavaScript中关于数组的小挑战

史蒂芬仍在建立他的小费计算器&#xff0c;使用的规则与以前一样&#xff1a; 如果账单价值在50到300之间&#xff0c;小费为账单的15%&#xff0c;如果价值不同&#xff0c;小费为20%。 编写一个函数’calcTip’&#xff0c;将任何账单值作为输入&#xff0c;并返回相应的小费…

【业务功能篇94】微服务-springcloud-springboot-认证服务-注册功能-第三方短信验证API

商城认证服务 一、搭建认证服务环境 结合我们前面介绍的商城的架构我们需要单独的搭建一个认证服务。 1.创建项目 首先创建一个SpringBoot项目&#xff0c;然后添加对应的依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"htt…

MySQL主从复制与读写分离 及其实例

目录 主从复制与读写分离 1、MySQL主从复制原理 1.1、MySQL的复制类型 1.2、MySQL主从复制的工作过程 1.3、mysq支持的复制类型 1.4、 数据流向 1.5、主从复制的工作过程 2、读写分离 2.1、什么是读写分离&#xff1f; 2.2、为什么要读写分离呢&#xff1f; 2.3、什么…

HFSS 3维曲线导入

HFSS 3维曲线导入 简介环境参考代码使用结果 简介 如图一所示&#xff0c;CST中可以通过导入和到出由任意点组成的曲线&#xff0c;但是HFSS中貌似不能导入&#xff08;如图二所示&#xff09;&#xff0c;如果我们要将matlab的产生的曲线的点的数据导入特变麻烦&#xff0c;特…

21.4 CSS 盒子模型

1. 边框样式 border-style属性: 指定元素的边框样式.常用属性值: - none: 无边框(默认值). - solid: 实线边框. - dotted: 点状边框. - dashed: 虚线边框. - double: 双线边框. - groove: 凹槽状边框. - ridge: 脊状边框. - inset: 内阴影边框. - outset: 外阴影边框.这些值可…

Loki日志系统

1、Loki是什么&#xff1f; Loki是一个开源的日志聚合系统&#xff0c;由Grafana Labs开发和维护。它旨在帮助用户收集、存储和查询大规模的日志数据&#xff0c;帮助用户更好地理解和监控他们的应用程序和系统。 Loki的设计灵感来自于Prometheus&#xff0c;它采用了类似的标…

【ROS 03】ROS通信机制进阶

上一章内容&#xff0c;主要介绍了ROS通信的实现&#xff0c;内容偏向于粗粒度的通信框架的讲解&#xff0c;没有详细介绍涉及的API&#xff0c;也没有封装代码&#xff0c;鉴于此&#xff0c;本章主要内容如下: ROS常用API介绍&#xff1b;ROS中自定义头文件与源文件的使用。…

springboot 与异步任务,定时任务,邮件任务

异步任务 在Java应用中&#xff0c;绝大多数情况下都是通过同步的方式来实现交互处理的&#xff1b;但是在处理与第三方系统交互的时候&#xff0c;容易造成响应迟缓的情况&#xff0c;之前大部分都是使用多线程来完成此类任务&#xff0c;其实&#xff0c;在Spring 3.x之后&a…

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书浙江师范大学图书馆

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书浙江师范大学图书馆

【GUI开发】用python爬YouTube博主信息,并开发成exe软件

文章目录 一、背景介绍二、代码讲解2.1 爬虫2.2 tkinter界面2.3 存日志 三、软件演示视频四、说明 一、背景介绍 你好&#xff0c;我是马哥python说&#xff0c;一名10年程序猿。 最近我用python开发了一个GUI桌面软件&#xff0c;目的是爬取相关YouTube博主的各种信息&#…

算法:图解位运算以及鸽巢原理应用

文章目录 实现原理基础位运算位图思想找最右侧数按位异或 算法思路典型例题基础位运算只出现一次的数字只出现一次的数字III 经典题型判断字符是否唯一两整数之和只出现一次的数字II消失的两个数字 鸽巢原理总结 本篇总结位运算中常见的算法题和思路&#xff0c;首先总结位运算…