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

八、博客管理系统

  • 创建新的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界面展示

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

windows11 利用vmware17 安装rocky9操作系统

下载相关软件和镜像 vmware17 下载 下载页面 Download VMware Workstation Pro ​ rocky8镜像下载 官网链接&#xff1a;Rocky Linux 下载页面 Download Rocky | Rocky Linux 点击Minimal下载 安装rocky9 选择镜像文件&#xff0c;点击下一步 点击下一步 启动虚拟机 选…

CSS中border-radius的来美化table的实战方案

border-radius是一种CSS属性&#xff0c;用于设置元素的边框的圆角程度。其具体的用法如下&#xff1a; 设置一个值&#xff1a;可以为元素设置一个单一的圆角半径&#xff0c;这个半径将应用于元素的四个角。例如&#xff1a; div {border-radius: 10px; }设置四个值&#x…

【文心一言】学习笔记

学习资料 《听说文心一言App霸榜了&#xff0c;那必须来一波全方位实测了》 情感陪伴&#xff1a;文心一言 App 可以充当用户的情感树洞&#xff0c;提供知心姐姐、【暖男】等角色扮演&#xff0c;为用户提供情绪疏导、情感分析、约会建议等服务。 1. 模型属性 【提示词工具…

NTT功能与实现

NTT的基础功用与拓展功能: 1.evaluate和interpolate evaluate的本质是选择n个点(假设f(x)的度为n)&#xff0c;计算得到其值&#xff0c;因此根据定义可以直接进行代入计算。为了加快计算的过程选取 w n w_n wn​的幂次(DFT问题即离散傅里叶变换)&#xff0c;使用FFT算法来加…

OpenGL-入门-BMP像素图glReadPixels(1)实现读取屏幕中间的颜色和获取屏幕上鼠标点击位置的颜色

glReadPixels函数用于从帧缓冲区中读取像素数据。它可以用来获取屏幕上特定位置的像素颜色值或者获取一块区域内的像素数据。下面是该函数的基本语法&#xff1a; void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *da…

JVM内存模型

文章目录 一、前言二、JVM内存模型1、Java堆2、方法区3、Java栈3.1、局部变量表3.2、操作数栈3.3、动态链接3.4、返回地址 4、本地方法栈5、程序计数器 一、前言 本文将详细介绍JVM内存模型&#xff0c;JVM定义了若干个程序执行期间使用的数据区域。这个区域里的一些数据在JVM…

手撕二叉平衡树

今天给大家带来的是平衡树的代码实现&#xff0c;如下&#xff1a; #pragma once #include <iostream> #include <map> #include <set> #include <assert.h> #include <math.h> using namespace std; namespace cc {template<class K, clas…

TypeScript学习 + 贪吃蛇项目

TypeSCript简介 TypeScript是JavaScript的超集。它对JS进行了扩展&#xff0c;向JS中引入了类型的概念&#xff0c;并添加了许多新的特性。TS代码需要通过编译器编译为JS&#xff0c;然后再交由JS解析器执行。TS完全兼容JS&#xff0c;换言之&#xff0c;任何的JS代码都可以直…

IP地址、网关、网络/主机号、子网掩码关系

一、IP地址 IP地址组成 IP地址分为两个部分&#xff1a;网络号和主机号 &#xff08;1&#xff09;网络号:标识网段&#xff0c;保证相互连接的两个网段具有不同的标识。 &#xff08;2&#xff09;主机号:标识主机&#xff0c;同一网段内&#xff0c;主机之间具有相同的网…

构建稳定的爬虫系统:如何选择合适的HTTP代理服务商

在构建一个稳定、高效的爬虫系统中&#xff0c;选择合适的HTTP代理服务商是至关重要的一步。本文将介绍如何选取可靠且性能优秀的HTTP代理服务供应商&#xff0c;来完成搭建一个强大而稳定的爬虫系统。 1.了解不同类型和特点 -免费公开代理服务器:提供免费但可能存在限制或不…

opencv鼠标事件函数setMouseCallback()详解

文章目录 opencv鼠标事件函数setMouseCallback()详解1、鼠标事件函数&#xff1a;&#xff08;1&#xff09;鼠标事件函数原型&#xff1a;setMouseCallback()&#xff0c;此函数会在调用之后不断查询回调函数onMouse()&#xff0c;直到窗口销毁&#xff08;2&#xff09;回调函…

优秀的ui设计作品(合集)

UI设计师需要了解的九个Tips 1.图片类APP排版突破 规则是死的&#xff0c;人是活的。很多时候&#xff0c;如果需求是比较宽要尝试突破原则&#xff0c;用一些另类的排版方式&#xff0c;其实也是做好设计的本质。在图片类app中&#xff0c;错落一些的排版会使你的作品更有魅力…

怎么将pdf合并成一个?将pdf合并成一个的方法

在日常工作和学习中&#xff0c;我们经常会遇到需要将多个PDF文件合并成一个的情况。这不仅能够提高文件管理的便捷性&#xff0c;还能节省存储空间并使阅读更加流畅。那么&#xff0c;怎么将pdf合并成一个呢&#xff1f;在本文中&#xff0c;我将为您介绍几种简单实用的方法&a…

xml

1.xml 1.1概述【理解】 万维网联盟(W3C) 万维网联盟(W3C)创建于1994年&#xff0c;又称W3C理事会。1994年10月在麻省理工学院计算机科学实验室成立。 建立者&#xff1a; Tim Berners-Lee (蒂姆伯纳斯李)。 是Web技术领域最具权威和影响力的国际中立性技术标准机构。 到目前为…

C++/C:pass-by-value(值传递)与pass-by-reference(引用传递)

一、C的引用&#xff08;reference&#xff09; 1.1、引用的概念 c中新增了引用&#xff08;reference&#xff09;的概念&#xff0c;引用可以作为一个已定义变量的别名。 Declares a named variable as a reference, that is, an alias to an already-existing object or f…

docker 笔记1

目录 1.为什么有docker ? 2.Docker 的核心概念 3.容器与虚拟机比较 3.1传统的虚拟化技术 3.2容器技术 3.3Docker容器的有什么作用&#xff1f; 3.4应用案例 4. docker 安装下载 4.1CentOS Docker 安装 4.2 Docker的基本组成 &#xff1f;&#xff08;面试&#xff09…

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书海口经济学院图书馆

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书海口经济学院图书馆

基于 Debian 12 的 Devuan GNU+Linux 5 为软件自由爱好者而生

导读Devuan 开发人员宣布发布 Devuan GNULinux 5.0 “代达罗斯 “发行版&#xff0c;它是 Debian GNU/Linux 操作系统的 100% 衍生版本&#xff0c;不包含 systemd 和相关组件。 Devuan GNULinux 5 基于最新的 Debian GNU/Linux 12 “书虫 “操作系统系列&#xff0c;采用长期支…

路由器的简单概述(详细理解+实例精讲)

系列文章目录 华为数通学习&#xff08;4&#xff09; 目录 系列文章目录 华为数通学习&#xff08;4&#xff09; 前言 一&#xff0c;网段间通信 二&#xff0c;路由器的基本特点 三&#xff0c;路由信息介绍 四&#xff0c;路由表 五&#xff0c;路由表的来源有哪些…

【笔记】常用 js 函数

数组去重 Array.from(new Set()) 对象合并 Object.assign . 这里有个细节&#xff1a;当两个对象中含有key相同value不同时&#xff0c;会以 后面对象的key&#xff1a;value为准 保留小数点后几位 toFixed 注意&#xff1a; Number型&#xff0c;用该方法处理完&#xff0c;会…