Spring-boot Personal Blog System
GitHub: https://github.com/Werdio66/myblog
1. Environmental Construction
gradle dependencies:
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'
2. Database Design
3. Effect Display
Front End Interface
Home page: blog page breaks, categories, label displays, latest recommendations, search
Categories: Show all categories and the number of blogs in the current category, and show blogs in each category by page
Tags: Show all the tags and the number of blogs under the current tag, and show each tag's blog page by page
Archives: By Time
About me: Read personal information from profile
Background interface
Blog management
- Paging shows blog information, which can be queried by title or category
- Blog publishing, with Markdown editor integrated, allows you to choose from original or reprinted, categorized, tagged, etc.
Classification management: Paging display of classifications, adding classifications, deleting classifications and batch deleting classifications
Label management: Label paging, label addition, deletion and bulk deletion are not implemented, similar to classification management
4. Functional Realization
1. Logon Interception
Login interception for requests under /admin/** path, background administration page requires privileges
/** * Logon Request Interception */ @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]Current Request uri = {}", uri); // Intercept only resources under/admin/** if (!uri.startsWith(noAuthUrl)){ chain.doFilter(request, response); return; } // URLs you add yourself can be configured into yml files if (exclusionUrlSet.contains(uri)){ chain.doFilter(request, response); return; } String username = (String) req.getSession().getAttribute("loginUser"); log.info("session Users in:{}", username); if (StringUtils.isEmpty(username)){ log.error("You do not have access, please log in first"); resp.sendRedirect("/admin"); return; } // Login Successful chain.doFilter(request, response); } }
2. Logging
Record the ip, uri, method name of the request before each request in the controller layer
@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("Page returned:{}", 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. Comment Management
@Override public List<Comment> queryAllByBlogId(Long id) { // Query all comments for the specified blog List<Comment> allComments = commentMapper.queryAllByBlogId(id); // Store parent comments List<Comment> parentComments = new ArrayList<>(); Map<Long, Comment> map = new HashMap<>(); Map<Long, Comment> allComms = new HashMap<>(); // Find Parent Comments for (Comment comment : allComments){ if (comment.getParentId() == -1){ parentComments.add(comment); // Save parent comments in map to set up child comments map.put(comment.getId(), comment); } allComms.put(comment.getId(), comment); } // Set Subcomments for (Comment comment : allComments){ if (comment.getParentId() != -1){ // Take out the parent comment of the current comment Comment parent = map.get(comment.getParentId()); // Parent comments may not be the root node, so take them from all comments if (parent == null){ parent = allComms.get(comment.getParentId()); } // Add the current comment to the child comment of the parent comment parent.getChildComments().add(comment); // Set parent comment for current 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()); } }
5. Optimization
1. You can store the front-end display blog in redis to increase speed
2. Use ab for pressure testing
Pressure the blog home page
Stress test the blog details page