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

被折叠的 条评论
为什么被折叠?



