springboot 小而美博客开发经验总结

本文介绍了使用SpringBoot和Semantic UI开发一个博客系统的经验,涵盖了从需求分析到功能实现的全过程,包括Markdown编辑器问题、后台登录MD5加密、登录拦截和跨域问题的解决方案。技术栈包括Spring Boot、JPA、MySQL和Thymeleaf。

哔哩哔哩自学博客系统

Semantic UI开发前端,SpringBoot开发后端
技术组合:
后端:Spring Boot + JPA + thymeleaf模板
数据库:MySQL
前端UI:Semantic UI框架
工具与环境:

IDEA
Maven 3
JDK 8
Axure RP 8
课程内容模块:

需求分析与功能规划
页面设计与开发
技术框架搭建
后端管理功能实现
前端管理功能实现
1、创建SpringBoot项目勾选依赖
devtools
web
mysql
jpa
thymeleaf
2、编写配置(YML方式)
application.yml

spring:
  thymeleaf:
    mode: HTML
  profiles:
    active: dev #设置开发环境

application-dev.yml(开发环境)



spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://ip地址/blog?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
    username: root
    password: 123456
#    jpa配置
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
#    日志配置
logging:
  level:
    root: info
    com.hzy: debug
  file: log/blog-dev.log
server:
  port: 7999

application-pro.yml(生产环境)

spring:
  thymeleaf:
    mode: HTML
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://ip地址:3306/blog?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
    username: root
    password: 123456
#    jpa配置
  jpa:
    hibernate:
      ddl-auto: none
    show-sql: true
#    日志配置
logging:
  level:
    root: warn
    com.hzy: info
  file: log/blog-pro.log
server:
  port: 7999

日志的配置文件(logback-spring)

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!--包含Spring boot对logback日志的默认配置-->
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />

    <!--重写了Spring Boot框架 org/springframework/boot/logging/logback/file-appender.xml 配置-->
    <appender name="TIME_FILE"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <file>${LOG_FILE}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i</fileNamePattern>
            <!--保留历史日志一个月的时间-->
            <maxHistory>30</maxHistory>
            <!--
            Spring Boot默认情况下,日志文件10M时,会切分日志文件,这样设置日志文件会在100M时切分日志
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>

        </rollingPolicy>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="TIME_FILE" />
    </root>

</configuration>

3、异常处理

@ControllerAdvice//会拦截所有@Controller注解
public class ControllerExceptionHandler {
    private final Logger logger= LoggerFactory.getLogger(this.getClass());
    @ExceptionHandler(Exception.class)//拦截的异常
    public ModelAndView exceptionHandler(HttpServletRequest request,Exception e) throws Exception {
        logger.error("Request URL:{},Exception:{}",request.getRequestURI(),e);
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class)!=null){
            throw e;
        }
        ModelAndView model=new ModelAndView();
        model.addObject("url",request.getRequestURI());
        model.addObject("exception",e);
        model.setViewName("error/error");
        return model;
    }
}

404处理

@ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {
    public NotFoundException() {
    }

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

    public NotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

controller 处理

@Controller
public class IndexController {
    @GetMapping({"/index","/"})
    public String index(){
//        int i=9/0;
        String blog=null;
        if(blog==null){
            throw new NotFoundException("博客不存在");
        }
        return "index";
    }
}

5、页面处理
将路径改为@{},必须以/开头(/表示static)
页面组件化
将th:fragment="“属性添加到标签中(普通方式‘单词’,传参方式‘单词(参数)’)
引用组件th:replace=”_fragments(文件名) :: 单词(参数)" 参数可以直接写title,默认会找titlle标签中的值
6、设计与规范

实体设计

实体类

博客 Blog
博客分类 Type
博客标签 Tag
博客评论 Comment
用户 User
实体关系

涉及重点 Markdown编辑器问题

public class MarkDownToHtmlUtils {

    /**
     * markdown格式转换成HTML格式
     * @param markdown markdown文本
     * @return 渲染
     */
    public static String markdownToHtml(String markdown) {
        Parser parser = Parser.builder().build();
        Node document = parser.parse(markdown);
        HtmlRenderer renderer = HtmlRenderer.builder().build();
        return renderer.render(document);
    }

    /**
     * 增加扩展[标题锚点,表格生成]
     * Markdown转换成HTML
     * @param markdown markdown文本
     * @return 渲染
     */
    public static String markdownToHtmlExtensions(String markdown) {
        //h标题生成id
        Set<Extension> headingAnchorExtensions = Collections.singleton(HeadingAnchorExtension.create());
        //转换table的HTML
        List<Extension> tableExtension = Arrays.asList(TablesExtension.create());
        Parser parser = Parser.builder()
                .extensions(tableExtension)
                .build();
        Node document = parser.parse(markdown);
        HtmlRenderer renderer = HtmlRenderer.builder()
                .extensions(headingAnchorExtensions)
                .extensions(tableExtension)
                .attributeProviderFactory(new AttributeProviderFactory() {
                    public AttributeProvider create(AttributeProviderContext context) {
                        return new CustomAttributeProvider();
                    }
                })
                .build();
        return renderer.render(document);
    }

    /**
     * 处理标签的属性
     */
    static class CustomAttributeProvider implements AttributeProvider {
        @Override
        public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
            //改变a标签的target属性为_blank
            if (node instanceof Link) {
                attributes.put("target", "_blank");
            }
            if (node instanceof TableBlock) {
                attributes.put("class", "ui celled table");
            }
        }
    }
}

后台登录md5 加密问题

public class MD5Utils {

    @Value("${project.salt}")
    private String salt;

    private static String thisSalt;

    @PostConstruct
    private void setThisSalt(){
        thisSalt = salt;
    }

    public static String digest(String str){
        return DigestUtils.md5DigestAsHex((str + thisSalt).getBytes(StandardCharsets.UTF_8));
    }


}

后台登录拦截

@Configuration
public class WebConfig implements WebMvcConfigurer {

    /**
     * 登录拦截
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/admin/**")
                .excludePathPatterns("/admin")
                .excludePathPatterns("/admin/login");
    }

}

跨域问题解决办法


public class CorsFilter implements Filter{  
   
@Override  
public void init(FilterConfig filterConfig) throws ServletException {  
// TODO Auto-generated method stub  
   
}  
   
@Override  
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,  
ServletException {  
// TODO Auto-generated method stub  
HttpServletResponse res = (HttpServletResponse) response;  
res.setContentType("text/html;charset=UTF-8");  
   res.setHeader("Access-Control-Allow-Origin", "*");  
   res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");  
   res.setHeader("Access-Control-Max-Age", "0");  
   res.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");  
   res.setHeader("Access-Control-Allow-Credentials", "true");  
   res.setHeader("XDomainRequestAllowed","1");  
   chain.doFilter(request, response);  
}  
   
@Override  
public void destroy() {  
// TODO Auto-generated method stub  
   
}  
}  

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值