[记录学习]分析学习第一个SSM项目:ForestBlog _01

首先感谢原作者的倾囊相授!

资源链接:https://github.com/saysky/ForestBlog

本内容仅为个人对整个项目的分析展示,用作学习,如有错误之处还望指点,本项目前端采用LayUI+前端三件套+jQuery实现,本人没有学过LayUI因此不做前端分析,仅展示部分运行部分的页面效果,打算在后续学习使用Vue框架进行实现,再根据分析经验重新对后端进行新的构建,尽力实现自己学习的第一个SSM项目,加油。

分析学习第一个SSM项目:ForestBlog

目录基础结构

在这里插入图片描述

一、分析pom.xml文件和web.xml文件

1. pox.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.liuyanzhao</groupId>
    <artifactId>ForestBlog</artifactId>
    <packaging>war</packaging>
    <version>1.0.0-SNAPSHOT</version>
    <name>ForestBlog Maven Webapp</name>
    <url>http://maven.apache.org</url>


    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>4.3.19.RELEASE</spring.version>
        <jackson-databind-version>2.10.1</jackson-databind-version>
    </properties>


    <dependencies>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.22</version>
        </dependency>

        <!-- 添加sevlet支持 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- 添加jsp支持 -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        <!-- 添加jstl支持 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>
        <!-- 添加spring支持 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--spring test支持-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--spring mvc支持-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!--spring 事务管理支持-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--spring jdbc操作支持-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--spring aop编程支持-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!-- 添加mybatis支持 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>
        <!--  jdbc驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <!-- 添加阿里巴巴连接池Druid支持 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.16</version>
        </dependency>
        <!-- 添加logback日志 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.logback-extensions</groupId>
            <artifactId>logback-ext-spring</artifactId>
            <version>0.1.1</version>
        </dependency>

        <!-- 添加junit支持 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>


        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>

        <!-- jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.8</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.0</version>
        </dependency>

        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20170516</version>
        </dependency>
        <dependency>
            <groupId>com.googlecode.rapid-framework</groupId>
            <artifactId>rapid-core</artifactId>
            <version>4.0.5</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>26.0-jre</version>
        </dependency>

        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>4.2.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>

        <!-- hutool工具包 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.1.13</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson-databind-version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>${jackson-databind-version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>${jackson-databind-version}</version>
        </dependency>

    </dependencies>

    <build>
        <resources>
            <!-- 编译之后包含xml -->
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <!-- 编译之后包含xml和properties -->
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
</project>

pom.xml文件使用的依赖:

  • lombok

  • 添加sevlet支持

    • javax.servlet-api
  • 添加jsp支持

    • javax.servlet.jsp-api
  • 添加jstl支持

    • jstl
    • standard
  • 添加spring支持

    • spring-core
    • spring-beans
    • spring-context
    • spring-context-support
    • spring-web
  • spring test支持

    • spring-test
  • spring mvc支持

    • spring-webmvc
  • spring 事务管理支持

    • spring-tx
  • spring jdbc操作支持

    • spring-jdbc
  • spring aop编程支持

    • spring-aop
    • spring-aspects
  • 添加mybatis支持

    • mybatis
    • mybatis-spring
  • jdbc驱动包

    • mysql-connector-java
  • 添加阿里巴巴连接池Druid支持

    • druid
  • 添加logback日志

    • logback-classic
    • logback-core
    • logback-ext-spring
  • 添加junit支持

    • junit
  • 文件上传

    • commons-fileupload
    • commons-io
  • jackson

    • jackson-databind
    • jackson-core
    • jackson-annotations
  • rapid-framework是谷歌的一个项目,可以实现jsp网页的继承,也就是书写模板页。

    • json
    • rapid-core
  • Guava是Google的一个开源Java框架,包含许多Google 内部Java项目依赖的核心类,包括:从集合类型、不可变集合、图结构处理、并发工具、I/O、哈希、缓存等等各个方面的工具。Guava是对Java API的补充,对Java开发中常用功能进行更优雅的实现,Guava库经过高度的优化,使得编码更加轻松,代码容易理解。

    • guava
  • 添加分页插件pagehelper

    • pagehelper
    • fastjson
  • hutool工具包

    • hutool-all
  • <build>方式

    <build>
        <resources>
            <!-- 编译之后包含xml -->
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <!-- 编译之后包含xml和properties -->
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
    

2. src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee" xmlns:mvc="http://www.springframework.org/schema/mvc"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
    <display-name>ForestBlog</display-name>

    <!--post乱码过滤器-->
    <!-- 配置springMVC编码过滤器 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <!-- 设置过滤器中的属性值 -->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <!-- 启动过滤器 -->
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <!-- 过滤所有请求 -->
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--加载spring容器-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:spring/spring-*.xml,
        </param-value>
    </context-param>

    <!--配置监听器,来加载spring容器-->
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>


    <!-- logback日志打印 -->
    <context-param>
        <param-name>logbackConfigLocation</param-name>
        <param-value>classpath:logback.xml</param-value>
    </context-param>
    <listener>
        <listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class>
    </listener>

    <!--springmvc前端控制器 -->
    <servlet>
        <servlet-name>ForestBlog</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/spring-mvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>ForestBlog</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--随时获得request-->
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>

    <!-- 4、使用Rest风格的URI,将页面普通的post请求转为指定的delete或者put请求 -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter>
        <filter-name>HttpPutFormContentFilter</filter-name>
        <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HttpPutFormContentFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>DruidWebStatFilter</filter-name>
        <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
        <init-param>
            <param-name>exclusions</param-name>
            <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>DruidWebStatFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!--去掉jsp页面的空白-->
    <jsp-config>
        <jsp-property-group>
            <url-pattern>*.jsp</url-pattern>
            <trim-directive-whitespaces>true</trim-directive-whitespaces>
        </jsp-property-group>
    </jsp-config>

</web-app>

web.xml文件的作用

  • 配置欢迎页面

  • 配置springMVC编码过滤器

  • 加载spring容器

  • 配置监听器,来加载spring容器

  • 配置logback日志打印

  • springmvc前端控制器

  • 配置监听器,随时获得request

  • 使用Rest风格的URI,将页面普通的post请求转为指定的delete或者put请求

  • 配置WebStatFilter用于采集web-jdbc关联监控的数据。

  • 去掉jsp页面的空白

二、分析main文件夹下文件

1. 分析resources文件夹

1.1 文件夹结构

│ db.properties
│ logback.xml

├─mapper
│ ArticleCategoryRefMapper.xml
│ ArticleMapper.xml
│ ArticleTagRefMapper.xml
│ CategoryMapper.xml
│ CommentMapper.xml
│ LinkMapper.xml
│ MenuMapper.xml
│ NoticeMapper.xml
│ OptionsMapper.xml
│ PageMapper.xml
│ TagMapper.xml
│ UserMapper.xml

├─mybatis
│ mybatis-config.xml

└─spring
spring-mvc.xml
spring-mybatis.xml

mapper 文件夹暂不分析,等分析具体映射时再一起分析

1.2 db.properties
  • 此文件为数据库连接配置文件

    #MySQL
    mysql.driver=com.mysql.cj.jdbc.Driver
    mysql.url=jdbc:mysql://localhost:3306/forest_blog?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
    mysql.username=root
    mysql.password=123
    
1.3 logback.xml
  • 此文件为logback日志配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <!-- 尽量别用绝对路径,如果带参数不同容器路径解释可能不同,以下配置参数在pom.xml里 -->
        <property name="log.root.level" value="INFO"/> <!-- 日志级别 -->
        <property name="log.other.level" value="ERROR"/> <!-- 其他日志级别 -->
        <property name="log.base" value="logs"/> <!-- 日志路径,这里是相对路径,默认会输出到tomcat/bin目录 下 -->
        <property name="log.moduleName" value="ForestBlog"/>  <!-- 模块名称, 影响日志配置名,日志文件名 -->
        <property name="log.max.size" value="20MB"/> <!-- 日志文件大小 -->
    
        <!--控制台输出 -->
        <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <Pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread]%logger{56}.%method:%L -%msg%n</Pattern>
            </encoder>
        </appender>
    
        <!-- 日志文件输出 -->
        <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <File>${log.base}/${log.moduleName}.log
            </File><!-- 设置日志不超过${log.max.size}时的保存路径,注意如果 是web项目会保存到Tomcat的bin目录 下 -->
            <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。-->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <FileNamePattern>${log.base}/archive/${log.moduleName}_all_%d{yyyy-MM-dd}.%i.log.zip
                </FileNamePattern>
                <!-- 当天的日志大小 超过${log.max.size}时,压缩日志并保存 -->
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>${log.max.size}</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
            </rollingPolicy>
            <!-- 日志输出的文件的格式  -->
            <layout class="ch.qos.logback.classic.PatternLayout">
                <pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread]%logger{56}.%method:%L -%msg%n</pattern>
            </layout>
        </appender>
    </configuration>
    
1.4 mybatis/mybatis-config.xml
  • 此文件为mybatis配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    
        <!-- 全局配置 -->
        <settings>
            <!--允许 JDBC 支持自动生成主键-->
            <setting name="useGeneratedKeys" value="false"/>
            <!--是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典Java 属性名 aColumn 的类似映射。 -->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
    
            <!-- 打印查询语句 -->
    <!--        <setting name="logImpl" value="STDOUT_LOGGING" />-->
        </settings>
    
        <!-- 注册分页插件PageHelper -->
        <plugins>
            <plugin interceptor="com.github.pagehelper.PageHelper">
                <!--<property name="dialect" value="mysql"/>-->
                <property name="offsetAsPageNum" value="false"/>
                <property name="rowBoundsWithCount" value="false"/>
                <property name="pageSizeZero" value="true"/>
                <property name="reasonable" value="true"/>
                <property name="supportMethodsArguments" value="false"/>
                <property name="returnPageInfo" value="none"/>
            </plugin>
        </plugins>
    
    </configuration>
    
  • 主要任务

    • 允许 JDBC 支持自动生成主键
    • 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典Java 属性名 aColumn 的类似映射。
    • 注册分页插件PageHelper
1.5 spring文件夹
1.5.1 spring-mvc.xml
  • springmvc配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    
        <context:component-scan base-package="com.liuyanzhao.ssm.blog"/>
    
        <!-- 一个配置节解决映射器和适配器的配置注解配置 -->
        <mvc:annotation-driven></mvc:annotation-driven>
    
        <!-- 配置视图解析器
            进行jsp解析,默认使用jstl标签,classpath下得有jstl的包
        -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!--配置前缀和后缀,也可以不指定-->
            <property name="prefix" value="/WEB-INF/view/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
    
        <!--&lt;!&ndash;文件上传&ndash;&gt;-->
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <!--设置上传最大尺寸为50MB-->
            <property name="maxUploadSizePerFile" value="52428800"/>
            <property name="defaultEncoding" value="UTF-8"/>
            <property name="resolveLazily" value="true"/>
        </bean>
    
    
        <!-- 静态资源映射 -->
        <mvc:resources mapping="/css/**" location="/resource/assets/css/"></mvc:resources>
        <mvc:resources mapping="/js/**" location="/resource/assets/js/"></mvc:resources>
        <mvc:resources mapping="/img/**" location="/resource/assets/img/"></mvc:resources>
        <mvc:resources mapping="/plugin/**" location="/resource/assets/plugin/"></mvc:resources>
    
    
        <mvc:interceptors>
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.HomeResourceInterceptor"/>
            </mvc:interceptor>
            <mvc:interceptor>
                <mvc:mapping path="/admin"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.LoginInterceptor"/>
            </mvc:interceptor>
            <mvc:interceptor>
                <mvc:mapping path="/admin/**"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.LoginInterceptor"/>
            </mvc:interceptor>
    
            <mvc:interceptor>
                <mvc:mapping path="/admin/page"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
            <mvc:interceptor>
                <mvc:mapping path="/admin/page/**"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
    
            <mvc:interceptor>
                <mvc:mapping path="/admin/tag"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
            <mvc:interceptor>
                <mvc:mapping path="/admin/tag/**"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
    
            <mvc:interceptor>
                <mvc:mapping path="/admin/category"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
            <mvc:interceptor>
                <mvc:mapping path="/admin/category/**"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
    
    
            <mvc:interceptor>
                <mvc:mapping path="/admin/user"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
            <mvc:interceptor>
                <mvc:mapping path="/admin/user/**"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
    
            <mvc:interceptor>
                <mvc:mapping path="/admin/menu"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
            <mvc:interceptor>
                <mvc:mapping path="/admin/menu/**"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
    
            <mvc:interceptor>
                <mvc:mapping path="/admin/options"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
            <mvc:interceptor>
                <mvc:mapping path="/admin/options/**"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
    
            <mvc:interceptor>
                <mvc:mapping path="/admin/link"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
            <mvc:interceptor>
                <mvc:mapping path="/admin/link/**"/>
                <bean class="com.liuyanzhao.ssm.blog.util.interceptor.AdminInterceptor"/>
            </mvc:interceptor>
    
        </mvc:interceptors>
    </beans>
    
    
  • 主要任务

    • 配置包扫描器对动态java文件进行扫描实例化
    • 配置视图解析器进行jsp解析,默认使用jstl标签,classpath下得有jstl的包
    • 配置文件上传限制
    • 对静态资源进行映射
    • 注册拦截器
1.5.2 spring-mybatis.xml
  • spring与mybatis的配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
    	http://www.springframework.org/schema/beans/spring-beans.xsd
    	http://www.springframework.org/schema/context
    	http://www.springframework.org/schema/context/spring-context.xsd
    	http://www.springframework.org/schema/aop
    	http://www.springframework.org/schema/aop/spring-aop.xsd
    	http://www.springframework.org/schema/tx
    	http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    
        <!--加载db.properties-->
        <context:property-placeholder location="classpath:db.properties"/>
    
        <!--配置druid连接池-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <!-- 基本属性 url、user、password -->
            <property name="driverClassName" value="${mysql.driver}" />
            <property name="url" value="${mysql.url}" />
            <property name="username" value="${mysql.username}" />
            <property name="password" value="${mysql.password}" />
    
            <!-- 配置初始化大小、最小、最大 -->
            <property name="initialSize" value="1" />
            <property name="minIdle" value="1" />
            <property name="maxActive" value="20" />
    
            <!-- 配置获取连接等待超时的时间,单位是毫秒 -->
            <property name="maxWait" value="60000" />
    
            <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
            <property name="timeBetweenEvictionRunsMillis" value="60000" />
    
            <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
            <property name="minEvictableIdleTimeMillis" value="300000" />
    
            <property name="validationQuery" value="SELECT 'x'" />
            <property name="testWhileIdle" value="true" />
            <property name="testOnBorrow" value="false" />
            <property name="testOnReturn" value="false" />
    
            <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
            <property name="poolPreparedStatements" value="true" />
            <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
    
            <!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->
            <property name="filters" value="stat" />
        </bean>
    
    
        <!--  mybatis核心配置  -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!--数据库连接池-->
            <property name="dataSource" ref="dataSource"/>
            <!--加载mybatis全局配置文件-->
            <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
            <!--mapper.xml所在位置-->
            <property name="mapperLocations" value="classpath*:mapper/*Mapper.xml" />
            <!--指定需要使用别名的PO类所在的包-->
            <property name="typeAliasesPackage" value="com.liuyanzhao.ssm.blog.entity" />
        </bean>
    
        <!--mapper扫描器-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!--如果需要扫描多个包,中间使用半角逗号隔开-->
            <property name="basePackage" value="com.liuyanzhao.ssm.blog.mapper"></property>
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        </bean>
    
    
    
        <!-- 对mybatis操作数据事务控制,spring使用jdbc的事务控制类 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- 数据源dataSource在spring-mybatis.xml中配置了 -->
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!-- 通知 -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="save*" propagation="REQUIRED"/>
                <tx:method name="delete*" propagation="REQUIRED"/>
                <tx:method name="update*" propagation="REQUIRED"/>
                <tx:method name="insert*" propagation="REQUIRED"/>
                <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
                <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
                <tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
            </tx:attributes>
        </tx:advice>
    
        <aop:config>
            <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.liuyanzhao.ssm.blog.service.impl.*.*(..))"/>
        </aop:config>
    </beans>
    
  • 主要任务

    • 加载db.properties数据库信息配置文件
    • 配置druid连接池信息
      • 基本属性 url、user、password
      • 配置初始化大小、最小、最大
      • 配置获取连接等待超时的时间
      • 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      • 配置一个连接在池中最小生存的时间,单位是毫秒
      • 打开PSCache,并且指定每个连接上PSCache的大小
      • 配置监控统计拦截的filters,去掉后监控界面sql无法统计
    • mybatis核心配置
      • 数据库连接池
      • 加载mybatis全局配置文件
      • mapper.xml所在位置
      • 指定需要使用别名的PO类所在的包
    • mapper扫描器
      • 如果需要扫描多个包,中间使用半角逗号隔开
    • 对mybatis操作数据事务控制,spring使用jdbc的事务控制类
      • 数据源dataSource在spring-mybatis.xml中配置了
    • 定义通知advice
    • 配置切面

2. 分析java文件夹

2.1 文件夹结构

└─com
└─liuyanzhao
└─ssm
└─blog
├─controller
│ ├─admin
│ │ AdminController.java
│ │ BackArticleController.java
│ │ BackCategoryController.java
│ │ BackCommentController.java
│ │ BackLinkController.java
│ │ BackMenuController.java
│ │ BackNoticeController.java
│ │ BackOptionsController.java
│ │ BackPageController.java
│ │ BackTagController.java
│ │ BackUserController.java
│ │ UploadFileController.java
│ │
│ └─home
│ ArticleController.java
│ CategoryController.java
│ CommentController.java
│ IndexController.java
│ LinkController.java
│ NoticeController.java
│ PageController.java
│ TagController.java

├─dto
│ ArticleParam.java
│ JsonResult.java
│ ResultVO.java
│ UploadFileVO.java

├─entity
│ Article.java
│ ArticleCategoryRef.java
│ ArticleTagRef.java
│ Category.java
│ Comment.java
│ Link.java
│ Menu.java
│ Notice.java
│ Options.java
│ Page.java
│ Tag.java
│ User.java

├─enums
│ ArticleCommentStatus.java
│ ArticleStatus.java
│ CategoryStatus.java
│ LinkStatus.java
│ MenuLevel.java
│ NoticeStatus.java
│ PageStatus.java
│ Role.java
│ UserRole.java

├─mapper
│ ArticleCategoryRefMapper.java
│ ArticleMapper.java
│ ArticleTagRefMapper.java
│ CategoryMapper.java
│ CommentMapper.java
│ LinkMapper.java
│ MenuMapper.java
│ NoticeMapper.java
│ OptionsMapper.java
│ PageMapper.java
│ TagMapper.java
│ UserMapper.java

├─service
│ │ ArticleService.java
│ │ CategoryService.java
│ │ CommentService.java
│ │ LinkService.java
│ │ MenuService.java
│ │ NoticeService.java
│ │ OptionsService.java
│ │ PageService.java
│ │ TagService.java
│ │ UserService.java
│ │
│ └─impl
│ ArticleServiceImpl.java
│ CategoryServiceImpl.java
│ CommentServiceImpl.java
│ LinkServiceImpl.java
│ MenuServiceImpl.java
│ NoticeServiceImpl.java
│ OptionsServiceImpl.java
│ PageServiceImpl.java
│ TagServiceImpl.java
│ UserServiceImpl.java

└─util
│ MyUtils.java

└─interceptor
AdminInterceptor.java
HomeResourceInterceptor.java
LoginInterceptor.java

由于包名为com.liuyanzhao.ssm.blog,其余文件都在该目录下进行分类,因此以下分析默认在此目录下进行。

2.2 entity实体类
2.2.0 实例关系分析
  • 实体基础需求

    • 文章(文章ID,用户ID,文章标题,文章访问量,文章评论数,文章点赞数,文章创建时间,文章更新时间,文章能否评论,文章状态,文章排序值,文章内容,文章摘要,文章缩略图)
    • 分类(分类ID,父亲ID,分类名称,分类描述,分类排序值,分类图标)
    • 标签(标签ID,标签名称,标签描述)
    • 用户(用户ID,用户名,密码,用户昵称,用户邮箱,用户主页url,用户头像,用户上次登录IP,用户注册时间,用户上次登录时间,用户状态,用户角色)
    • 评论(评论ID,上级评论人ID,上级评论人名称,评论文章ID,评论人昵称,评论人email,评论人主页url,评论人头像,评论内容,评论浏览器信息,评论IP,评论时间,评论角色,评论用户ID)

    ------------------------下方为管理员管理内容-------------------------------

    • 链接(链接ID,链接url,链接名称,链接头像,链接描述,链接所属人昵称,链接所属人联系方式,链接创建时间,链接更新时间,链接排序值,链接状态)
    • 菜单(菜单ID,菜单名称,菜单url,菜单等级,菜单图标,菜单排序值)
    • 公告(公告ID,公告标题,公告内容,公告创建时间,公告更新时间,公告状态,公告排序值)
    • 基本信息(基本信息ID,站点标题,站点描述,媒体描述,媒体关键字,相关站点头像,相关站点标题,相关站点内容,相关站点微信,相关站点QQ,相关站点GitHub,相关站点微博,基本信息统计,基本信息状态)
    • 页面(页面ID,页面别称key,页面标题,页面内容,页面创建时间,页面更新时间,页面访问量,页面评论数,页面状态)
  • 关系模式

    • 文章:分类 = m:n
    • 文章:标签 = m:n
    • 文章:用户 = n:1
    • 文章:评论 = 1:n
    • 父分类:子分类 = 1:1
    • 用户:评论 = 1:n
    • 父评论:子评论 = 1:1
  • 加入关系模式后的实体类设计

    • 文章(文章ID,文章标题,文章访问量,文章评论数,文章点赞数,文章创建时间,文章更新时间,文章能否评论,文章状态,文章排序值,文章内容,文章摘要,文章缩略图,用户ID)
      • PK:文章ID,用户ID
      • FK:用户ID
    • 分类(分类ID,父亲分类ID,分类名称,分类描述,分类排序值,分类图标)
      • PK:分类ID,父亲分类ID
      • FK:父亲分类ID
    • 标签(标签ID,标签名称,标签描述)
      • PK:标签ID
      • FK:
    • 用户(用户ID,用户名,密码,用户昵称,用户邮箱,用户主页url,用户头像,用户上次登录IP,用户注册时间,用户上次登录时间,用户状态,用户角色)
      • PK:用户ID
      • FK:
    • 评论(评论ID,评论人昵称,评论人email,评论人主页url,评论人头像,评论内容,评论浏览器信息,评论IP,评论时间,评论角色,上级评论人ID,上级评论人名称,评论文章ID,评论用户ID)
      • PK:评论ID,评论文章ID,上级评论人ID,评论用户ID
      • FK:评论文章ID,上级评论人ID,评论用户ID
    • 文章与分类关系(文章ID,分类ID)
      • PK:文章ID,分类ID
      • FK:文章ID,分类ID
    • 文章与标签关系(文章ID,标签ID)
      • PK:文章ID,标签ID
      • FK:文章ID,标签ID

    ------------------------下方为管理员管理内容-------------------------------

    • 链接(链接ID,链接url,链接名称,链接头像,链接描述,链接所属人昵称,链接所属人联系方式,链接创建时间,链接更新时间,链接排序值,链接状态)
      • PK:链接ID
      • FK:
    • 菜单(菜单ID,菜单名称,菜单url,菜单等级,菜单图标,菜单排序值)
      • PK:菜单ID
      • FK:
    • 公告(公告ID,公告标题,公告内容,公告创建时间,公告更新时间,公告状态,公告排序值)
      • PK:公告ID
      • FK:
    • 基本信息(基本信息ID,站点标题,站点描述,媒体描述,媒体关键字,相关站点头像,相关站点标题,相关站点内容,相关站点微信,相关站点QQ,相关站点GitHub,相关站点微博,基本信息统计,基本信息状态)
      • PK:基本信息ID
      • FK:
    • 页面(页面ID,页面别称key,页面标题,页面内容,页面创建时间,页面更新时间,页面访问量,页面评论数,页面状态)
      • PK:页面ID
      • FK:
  • 为增加查询效率,增加冗余,最终实体类设计

    • 文章(文章ID,文章标题,文章访问量,文章评论数,文章点赞数,文章创建时间,文章更新时间,文章能否评论,文章状态,文章排序值,文章内容,文章摘要,文章缩略图,用户ID,用户,标签列表,分类列表)
      • PK:文章ID,用户ID
      • FK:用户ID
      • 冗余属性:用户,标签列表,分类列表
    • 分类(分类ID,父亲分类ID,分类名称,分类描述,分类排序值,分类图标,该分类下的文章数量)
      • PK:分类ID,父亲分类ID
      • FK:父亲分类ID
      • 冗余属性:该分类下的文章总数
    • 标签(标签ID,标签名称,标签描述,该标签下的文章数量)
      • PK:标签ID
      • FK:
      • 冗余属性:该标签下的文章总数
    • 用户(用户ID,用户名,密码,用户昵称,用户邮箱,用户主页url,用户头像,用户上次登录IP,用户注册时间,用户上次登录时间,用户状态,用户角色,该用户写的文章数量)
      • PK:用户ID
      • FK:
      • 冗余属性:该用户写的文章数量
    • 评论(评论ID,评论人昵称,评论人email,评论人主页url,评论人头像,评论内容,评论浏览器信息,评论IP,评论时间,评论角色,上级评论人ID,上级评论人名称,评论文章ID,评论用户ID,文章)
      • PK:评论ID,评论文章ID,上级评论人ID,评论用户ID
      • FK:评论文章ID,上级评论人ID,评论用户ID
      • 冗余属性:文章
    • 文章与分类关系(文章ID,分类ID)
      • PK:文章ID,分类ID
      • FK:文章ID,分类ID
    • 文章与标签关系(文章ID,标签ID)
      • PK:文章ID,标签ID
      • FK:文章ID,标签ID

    ------------------------下方为管理员管理内容-------------------------------

    • 链接(链接ID,链接url,链接名称,链接头像,链接描述,链接所属人昵称,链接所属人联系方式,链接创建时间,链接更新时间,链接排序值,链接状态)
      • PK:链接ID
      • FK:
    • 菜单(菜单ID,菜单名称,菜单url,菜单等级,菜单图标,菜单排序值)
      • PK:菜单ID
      • FK:
    • 公告(公告ID,公告标题,公告内容,公告创建时间,公告更新时间,公告状态,公告排序值)
      • PK:公告ID
      • FK:
    • 基本信息(基本信息ID,站点标题,站点描述,媒体描述,媒体关键字,相关站点头像,相关站点标题,相关站点内容,相关站点微信,相关站点QQ,相关站点GitHub,相关站点微博,基本信息统计,基本信息状态)
      • PK:基本信息ID
      • FK:
    • 页面(页面ID,页面别称key,页面标题,页面内容,页面创建时间,页面更新时间,页面访问量,页面评论数,页面状态)
      • PK:页面ID
      • FK:

├─entity
│ Article.java
│ ArticleCategoryRef.java
│ ArticleTagRef.java
│ Category.java
│ Comment.java
│ Link.java
│ Menu.java
│ Notice.java
│ Options.java
│ Page.java
│ Tag.java
│ User.java

  • 该目录下的实体类均采用@Data注解,简化代码。

  • @Data 的用途是:注在类上,提供类的get、set、equals、hashCode、toString等方法。

  • 使用该注解需要引入lombok依赖,idea中需要引入lombok相关插件。

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.22</version>
    </dependency>
    

    在这里插入图片描述

2.2.1 Article
  1. com.liuyanzhao.ssm.blog.entity.Article

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.util.Date;
    import java.util.List;
    
    /**
     * @author liuyanzhao
     */
    @Data
    public class Article implements Serializable{
    
        private static final long serialVersionUID = 5207865247400761539L;
    	// 文章ID
        private Integer articleId;
    	// 文章用户ID
        private Integer articleUserId;
    	// 文章标题
        private String articleTitle;
    	// 文章访问量
        private Integer articleViewCount;
    	// 文章评论数
        private Integer articleCommentCount;
    	// 文章点赞数
        private Integer articleLikeCount;
    	// 文章创建时间
        private Date articleCreateTime;
    	// 文章更新时间
        private Date articleUpdateTime;
    	// 文章是否允许评论
        private Integer articleIsComment;
    	// 文章状态
        private Integer articleStatus;
    	// 文章排序值
        private Integer articleOrder;
    	// 文章内容
        private String articleContent;
    	// 文章摘要
        private String articleSummary;
    	// 文章缩略图
        private String articleThumbnail;
    
        private User user;
    
        private List<Tag> tagList;
    
        private List<Category> categoryList;
    }
    
    • 文章实体类

      • 增加了三个非数据库属性
        • User:用户
        • tagList:标签列表
        • categoryList:分类列表
    • 数据库设计

      在这里插入图片描述

2.2.2 ArticleCategoryRef
  1. com.liuyanzhao.ssm.blog.entity.ArticleCategoryRef

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * 文章分类关联表
     *
     * @author liuyanzhao
     */
    @Data
    public class ArticleCategoryRef implements Serializable{
    
        private static final long serialVersionUID = -6809206515467725995L;
    	// 文章ID
        private Integer articleId;
    	// 分类ID
        private Integer categoryId;
    
        public ArticleCategoryRef() {
        }
    
        public ArticleCategoryRef(Integer articleId, Integer categoryId) {
            this.articleId = articleId;
            this.categoryId = categoryId;
        }
    }
    
    • 文章分类关联表实体类

    • 数据库设计

      在这里插入图片描述

2.2.3 ArticleTagRef
  1. com.liuyanzhao.ssm.blog.entity.ArticleTagRef

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * 文章和标签关联
     *
     * @author 言曌
     * @date 2018/11/17 下午5:20
     */
    @Data
    public class ArticleTagRef implements Serializable {
        private static final long serialVersionUID = -5816783232020910492L;
    	// 文章ID
        private Integer articleId;
    	// 标签ID
        private Integer tagId;
    
        public ArticleTagRef() {
        }
    
        public ArticleTagRef(Integer articleId, Integer tagId) {
            this.articleId = articleId;
            this.tagId = tagId;
        }
    }
    
    • 文章和标签关联实体类

    • 数据库设计

      在这里插入图片描述

2.2.4 Category
  1. com.liuyanzhao.ssm.blog.entity.Category

    package com.liuyanzhao.ssm.blog.entity;
    
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * @author liuyanzhao
     */
    @Data
    public class Category implements Serializable {
    
        private static final long serialVersionUID = 6687286913317513141L;
    	// 分类ID
        private Integer categoryId;
    	// 分类父ID
        private Integer categoryPid;
    	// 分类名称
        private String categoryName;
    	// 分类描述
        private String categoryDescription;
    	// 分类排序值
        private Integer categoryOrder;
    	// 分类图标
        private String categoryIcon;
    
        /**
         * 文章数量(非数据库字段)
         */
        private Integer articleCount;
    
        public Category(Integer categoryId, Integer categoryPid, String categoryName, String categoryDescription, Integer categoryOrder, String categoryIcon,Integer articleCount) {
            this.categoryId = categoryId;
            this.categoryPid = categoryPid;
            this.categoryName = categoryName;
            this.categoryDescription = categoryDescription;
            this.categoryOrder = categoryOrder;
            this.categoryIcon = categoryIcon;
            this.articleCount = articleCount;
        }
    
        public Category(Integer categoryId, String categoryName) {
            this.categoryId = categoryId;
            this.categoryName = categoryName;
        }
    
        public Category(Integer categoryId) {
            this.categoryId = categoryId;
        }
        
        public Category() {}
    
        /**
         * 未分类
         *
         * @return 分类
         */
        public static Category Default() {
            return new Category(100000000, "未分类");
        }
    }
    
    • 分类实体类

      • 增加了一个非数据库属性
        • articleCount:该分类下的文章数量
      • 设计了构造方法,包括:
        • 仅有分类ID
        • 有分类ID、分类名称
        • 全参
      • 设置了一个静态方法:Default(),用于返回一个固定内容的未分类对象
    • 数据库设计

      在这里插入图片描述

2.2.5 Comment
  1. com.liuyanzhao.ssm.blog.entity.Comment

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * 文章评论
     *
     * @author liuyanzhao
     */
    @Data
    public class Comment implements Serializable {
    
        private static final long serialVersionUID = -1038897351672911219L;
    	// 评论ID
        private Integer commentId;
    	// 上级评论ID
        private Integer commentPid;
    	// 上级评论名称
        private String commentPname;
    	// 文章ID
        private Integer commentArticleId;
    	// 评论人名称
        private String commentAuthorName;
    	// 评论人邮箱
        private String commentAuthorEmail;
    	// 评论人个人主页
        private String commentAuthorUrl;
    	// 评论人头像
        private String commentAuthorAvatar;
    	// 内容
        private String commentContent;
    	// 浏览器信息
        private String commentAgent;
    	// IP
        private String commentIp;
    	// 评论时间
        private Date commentCreateTime;
    
        /**
         * 角色(管理员1,访客0)
         */
        private Integer commentRole;
    
        /**
         * 评论用户ID
         */
        private Integer commentUserId;
    
        /**
         * 非数据库字段
         */
        private Article article;
    }
    
    • 评论实体类

      • 增加了一个非数据库属性:article:文章
    • 数据库设计

      在这里插入图片描述

2.2.6 Link
  1. com.liuyanzhao.ssm.blog.entity.Link

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * @author liuyanzhao
     */
    @Data
    public class Link  implements Serializable{
    
        private static final long serialVersionUID = -259829372268790508L;
    	// 链接ID
        private Integer linkId;
    	// URL
        private String linkUrl;
    	// 姓名
        private String linkName;
    	// 头像
        private String linkImage;
    	// 描述
        private String linkDescription;
    	// 所属人名称
        private String linkOwnerNickname;
    	// 联系方式
        private String linkOwnerContact;
    	// 更新时间
        private Date linkUpdateTime;
    	// 创建时间
        private Date linkCreateTime;
    	// 排序号
        private Integer linkOrder;
    	// 状态
        private Integer linkStatus;
    
    }
    
    • 连接实体类

    • 数据库设计

      在这里插入图片描述

2.2.7 Menu
  1. com.liuyanzhao.ssm.blog.entity.Menu

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * @author liuyanzhao
     */
    @Data
    public class Menu implements Serializable {
        
        private static final long serialVersionUID = 489914127235951698L;
    	// 菜单ID
        private Integer menuId;
    	// 名称
        private String menuName;
    	// URL
        private String menuUrl;
    	// 等级
        private Integer menuLevel;
    	// 图标
        private String menuIcon;
    	// 排序值
        private Integer menuOrder;
    
    }
    
    • 菜单实体类

    • 数据库设计

      在这里插入图片描述

2.2.8 Notice
  1. com.liuyanzhao.ssm.blog.entity.Notice

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * @author liuyanzhao
     */
    @Data
    public class Notice implements Serializable{
    
        private static final long serialVersionUID = -4901560494243593100L;
    	// 公告ID
        private Integer noticeId;
    	// 公告标题
        private String noticeTitle;
    	// 内容
        private String noticeContent;
    	// 创建时间
        private Date noticeCreateTime;
    	// 更新时间
        private Date noticeUpdateTime;
    	// 状态
        private Integer noticeStatus;
    	// 排序值
        private Integer noticeOrder;
    }
    
    • 公告实体类

    • 数据库设计

      在这里插入图片描述

2.2.9 Options
  1. com.liuyanzhao.ssm.blog.entity.Options

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * @author liuyanzhao
     */
    @Data
    public class Options implements Serializable{
        
        private static final long serialVersionUID = -776792869602511933L;
    	// 
        private Integer optionId;
    	// 
        private String optionSiteTitle;
    	// 
        private String optionSiteDescrption;
    	// 
        private String optionMetaDescrption;
    	// 
        private String optionMetaKeyword;
    	// 
        private String optionAboutsiteAvatar;
    	// 
        private String optionAboutsiteTitle;
    	// 
        private String optionAboutsiteContent;
    	// 
        private String optionAboutsiteWechat;
    	// 
        private String optionAboutsiteQq;
    	// 
        private String optionAboutsiteGithub;
    	// 
        private String optionAboutsiteWeibo;
    	// 
        private String optionTongji;
    	// 
        private Integer optionStatus;
    
    }
    
    • 系统设置实体类

    • 数据库设计

      在这里插入图片描述

2.2.10 Page
  1. com.liuyanzhao.ssm.blog.entity.Page

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * @author liuyanzhao
     */
    @Data
    public class Page implements Serializable{
    
        private static final long serialVersionUID = 3927496662110298634L;
    	// 自定义页面ID
        private Integer pageId;
    	// key
        private String pageKey;
    	// 标题
        private String pageTitle;
    	// 内容
        private String pageContent;
    	// 创建时间
        private Date pageCreateTime;
    	// 更新时间
        private Date pageUpdateTime;
    	// 访问量
        private Integer pageViewCount;
    	// 评论数
        private Integer pageCommentCount;
    	// 状态
        private Integer pageStatus;
    
    }
    
    • 页面实体类

    • 数据库设计

      在这里插入图片描述

2.2.11 Tag
  1. com.liuyanzhao.ssm.blog.entity.Tag

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * @author liuyanzhao
     */
    @Data
    public class Tag implements Serializable{
        
        private static final long serialVersionUID = 605449151900057035L;
    	// 标签ID
        private Integer tagId;
    	// 标签名称
        private String tagName;
    	// 描述
        private String tagDescription;
    
        /**
         * 文章数量(不是数据库字段)
         */
        private Integer articleCount;
    
        public Tag() {
        }
    
        public Tag(Integer tagId) {
            this.tagId = tagId;
        }
    
        public Tag(Integer tagId, String tagName, String tagDescription, Integer articleCount) {
            this.tagId = tagId;
            this.tagName = tagName;
            this.tagDescription = tagDescription;
            this.articleCount = articleCount;
        }
    }
    
    • 标签实体类

      • 增加了一个非数据库属性:articleCount:文章数量总计
      • 增加了三个构造方法
        • 无参
        • 单一标签ID
        • 全参
    • 数据库设计

      在这里插入图片描述

2.2.12 User
  1. com.liuyanzhao.ssm.blog.entity.User

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * @author liuyanzhao
     */
    @Data
    public class User implements Serializable{
        private static final long serialVersionUID = -4415517704211731385L;
    	// 用户ID
        private Integer userId;
    	// 用户名
        private String userName;
    	// 密码
        private String userPass;
    	// 昵称
        private String userNickname;
    	// 邮箱
        private String userEmail;
    	// 个人主页
        private String userUrl;
    	// 头像
        private String userAvatar;
    	// 上次登录IP
        private String userLastLoginIp;
    	// 注册时间
        private Date userRegisterTime;
    	// 上次登录时间
        private Date userLastLoginTime;
    	// 状态
        private Integer userStatus;
    
        /**
         * 用户角色:admin/user
         */
        private String userRole;
    
        /**
         * 文章数量(不是数据库字段)
         */
        private Integer articleCount;
    
    }
    
    • 用户实体类

      • 增加一个非数据库属性:articleCount:发布文章总数
    • 数据库设计

      在这里插入图片描述

2.3 enums枚举类

├─enums
│ ArticleCommentStatus.java
│ ArticleStatus.java
│ CategoryStatus.java
│ LinkStatus.java
│ MenuLevel.java
│ NoticeStatus.java
│ PageStatus.java
│ Role.java
│ UserRole.java

2.3.1 ArticleCommentStatus
  1. com.liuyanzhao.ssm.blog.enums.ArticleCommentStatus

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum ArticleCommentStatus {
    
        ALLOW(1, "允许"),
        NOT_ALLOW(0, "不允许");
    
        private Integer value;
    
        private String message;
    
        ArticleCommentStatus(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 文章是否允许评论
      • 1:允许
      • 0:不允许
2.3.2 ArticleStatus
  1. com.liuyanzhao.ssm.blog.enums.ArticleStatus

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum ArticleStatus {
    
        PUBLISH(1, "已发布"),
        DRAFT(0, "草稿");
    
        private Integer value;
    
        private String message;
    
        ArticleStatus(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 文章状态
      • 1:已发布
      • 0:草稿
2.3.3 CategoryStatus
  1. com.liuyanzhao.ssm.blog.enums.CategoryStatus

    package com.liuyanzhao.ssm.blog.enums;
    
    import lombok.Data;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum CategoryStatus {
    
        NORMAL(1, "正常"),
        HIDDEN(0, "隐藏");
    
        private Integer value;
    
        private String message;
    
        CategoryStatus(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 分类状态
      • 1:正常
      • 0:隐藏
2.3.4 LinkStatus
  1. com.liuyanzhao.ssm.blog.enums.LinkStatus

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum LinkStatus {
    
        NORMAL(1, "显示"),
        HIDDEN(0, "隐藏");
    
        private Integer value;
    
        private String message;
    
        LinkStatus(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 链接状态
      • 1:显示
      • 0:隐藏
2.3.5 MenuLevel
  1. com.liuyanzhao.ssm.blog.enums.MenuLevel

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum MenuLevel {
    
        TOP_MENU(1, "顶部菜单"),
        MAIN_MENU(2, "主体菜单");
    
        private Integer value;
    
        private String message;
    
        MenuLevel(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 菜单等级
      • 1:顶部菜单
      • 2:主体菜单
2.3.6 NoticeStatus
  1. com.liuyanzhao.ssm.blog.enums.NoticeStatus

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum NoticeStatus {
    
        NORMAL(1, "显示"),
        HIDDEN(0, "隐藏");
    
        private Integer value;
    
        private String message;
    
        NoticeStatus(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 通知状态
      • 1:显示
      • 0:隐藏
2.3.7 PageStatus
  1. com.liuyanzhao.ssm.blog.enums.PageStatus

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum PageStatus {
    
        NORMAL(1, "显示"),
        HIDDEN(0, "隐藏");
    
        private Integer value;
    
        private String message;
    
        PageStatus(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 页面状态
      • 1:显示
      • 0:隐藏
2.3.8 Role
  1. com.liuyanzhao.ssm.blog.enums.Role

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum Role {
    
        OWNER(1, "博主"),
        VISITOR(0, "其他用户");
    
        private Integer value;
    
        private String message;
    
        Role(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 浏览角色
      • 1:博主
      • 0:其他用户
2.3.9 UserRole
  1. com.liuyanzhao.ssm.blog.enums.UserRole

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * 角色枚举
     *
     * @author 言曌
     * @date 2021/2/25 2:31 下午
     */
    
    public enum UserRole {
    
        ADMIN("admin", "管理员"),
    
        USER("user", "用户");
    
        private String value;
    
        private String message;
    
        UserRole(String value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public String getValue() {
            return value;
        }
    
        public void setValue(String value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 用户角色
      • admin:管理员
      • user:用户
2.4 dto数据传输对象

├─dto
│ ArticleParam.java
│ JsonResult.java
│ ResultVO.java
│ UploadFileVO.java

2.4.1 ArticleParam
  1. com.liuyanzhao.ssm.blog.dto.ArticleParam

    package com.liuyanzhao.ssm.blog.dto;
    
    import lombok.Data;
    
    import java.util.List;
    
    /**
     * @author 言曌
     * @date 2018/11/25 下午3:56
     */
    @Data
    public class ArticleParam {
    
        private Integer articleId;
    
        private String articleTitle;
    
        private String articleContent;
    
        private Integer articleParentCategoryId;
    
        private Integer articleChildCategoryId;
    
        private Integer articleOrder;
    
        private Integer articleStatus;
    
        private String articleThumbnail;
    
        private List<Integer> articleTagIds;
    
    }
    
    • 文章参数
2.4.2 JsonResult
  1. com.liuyanzhao.ssm.blog.dto.JsonResult

    package com.liuyanzhao.ssm.blog.dto;
    
    import lombok.Data;
    
    /**
     * @author 言曌
     * @date 2017/11/30 下午7:04
     */
    
    @Data
    public class JsonResult<T> {
    
    
        /**
         * 错误码
         */
        private Integer code;
    
        /**
         * 提示信息
         */
        private String msg;
    
        /**
         * 返回的具体内容
         */
        private T data;
    
    
        public JsonResult(Integer code, String msg, T data) {
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    
        public JsonResult() {
        }
    
    
        public JsonResult fail() {
            return new JsonResult(1, "操作失败", null);
        }
    
        public JsonResult fail(String msg) {
            return new JsonResult(1, msg, null);
        }
    
        public JsonResult ok() {
            return new JsonResult(0, "操作成功", null);
        }
    
    
        public JsonResult ok(T data) {
            return new JsonResult(0, "操作成功", data);
        }
    }
    
    • json传输结果
2.4.3 ResultVO
  1. com.liuyanzhao.ssm.blog.dto.ResultVO

    package com.liuyanzhao.ssm.blog.dto;
    
    import lombok.Data;
    
    /**
     * @author 言曌
     * @date 2017/11/30 下午7:04
     */
    
    @Data
    public class ResultVO<T> {
    
        /**
         * 错误码
         */
        private Integer code;
    
        /**
         * 提示信息
         */
        private String msg;
    
        /**
         * 返回的具体内容
         */
        private T data;
    
    
        public ResultVO(Integer code, String msg, T data) {
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    
        public ResultVO() {
        }
    }
    
    • 结果显示层对象
2.4.4 UploadFileVO
  1. com.liuyanzhao.ssm.blog.dto.UploadFileVO

    package com.liuyanzhao.ssm.blog.dto;
    
    import lombok.Data;
    
    /**
     * @author 言曌
     * @date 2017/11/30 下午7:41
     */
    
    @Data
    public class UploadFileVO {
    
        private String src;
    
        private String title;
    
    }
    
    • 上传文件显示层对象
2.5 util工具类

└─util
│ MyUtils.java

└─interceptor
AdminInterceptor.java
HomeResourceInterceptor.java
LoginInterceptor.java

2.5.1 MyUtils 我的工具类
  • com.liuyanzhao.ssm.blog.util.MyUtils

    package com.liuyanzhao.ssm.blog.util;
    
    import javax.servlet.http.HttpServletRequest;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    /**
     * 常用的方法
     * Created by 言曌 on 2017/8/24.
     */
    
    public class MyUtils {
    
        /**
         * 获得IP地址
         *
         * @param request
         * @return
         */
        public static String getIpAddr(HttpServletRequest request) {
            String ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
            }
            //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) {
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
            return ipAddress;
        }
    
    
        /**
         * 获得Md5加密
         *
         * @param str 原字符串
         * @return 加密后的字符串
         */
        public static String strToMd5(String str) {
            String md5Str = null;
            if (str != null && str.length() != 0) {
                try {
                    MessageDigest md = MessageDigest.getInstance("MD5");
                    md.update(str.getBytes());
                    byte b[] = md.digest();
    
                    int i;
                    StringBuffer buf = new StringBuffer("");
                    for (int offset = 0; offset < b.length; offset++) {
                        i = b[offset];
                        if (i < 0) {
                            i += 256;
                        }
                        if (i < 16) {
                            buf.append("0");
                        }
                        buf.append(Integer.toHexString(i));
                    }
                    //32位
                    md5Str = buf.toString();
                    //16位
                    //md5Str = buf.toString().substring(8, 24);
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                }
            }
            return md5Str;
        }
    
        /**
         * 根据email获取gravatar头像
         *
         * @param email Email
         * @return 头像URL
         */
        public static String getGravatar(String email) {
            String emailMd5 = strToMd5(email);
            //设置图片大小32px
            String avatar = "http://cn.gravatar.com/avatar/" + emailMd5 + "?s=128&d=identicon&r=PG";
            return avatar;
        }
    
    }
    
    • getIpAddr(HttpServletRequest request)

      • 获得IP地址

        • @param request HttpServletRequest请求
        • @return IP地址
      • 源代码

        public static String getIpAddr(HttpServletRequest request) {
            String ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
            }
            //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) {
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
            return ipAddress;
        }
        
      • 自上而下分别从request.getHeader("x-forwarded-for");、request.getHeader("Proxy-Client-IP");request.getHeader("WL-Proxy-Client-IP");request.getRemoteAddr();获取IP地址,若未从上方获取则进入下层获取。

      • 获取完毕后,若是多个代理的情况,则取第一个IP为客户端真实IP

    • String strToMd5(String str)

      • 获得Md5加密

        • @param str 原字符串
        • @return 加密后的字符串
      • 源代码

        public static String strToMd5(String str) {
            String md5Str = null;
            if (str != null && str.length() != 0) {
                try {
                    MessageDigest md = MessageDigest.getInstance("MD5");
                    md.update(str.getBytes());
                    byte b[] = md.digest();
        
                    int i;
                    StringBuffer buf = new StringBuffer("");
                    for (int offset = 0; offset < b.length; offset++) {
                        i = b[offset];
                        if (i < 0) {
                            i += 256;
                        }
                        if (i < 16) {
                            buf.append("0");
                        }
                        buf.append(Integer.toHexString(i));
                    }
                    //32位
                    md5Str = buf.toString();
                    //16位
                    //md5Str = buf.toString().substring(8, 24);
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                }
            }
        
    • String getGravatar(String email)

      • 根据email获取gravatar头像

        • @param email Email
        • @return 头像URL
      • 源代码

        public static String getGravatar(String email) {
            String emailMd5 = strToMd5(email);
            //设置图片大小32px
            String avatar = "http://cn.gravatar.com/avatar/" + emailMd5 + "?s=128&d=identicon&r=PG";
            return avatar;
        }
        
2.2.11 Tag
  1. com.liuyanzhao.ssm.blog.entity.Tag

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * @author liuyanzhao
     */
    @Data
    public class Tag implements Serializable{
        
        private static final long serialVersionUID = 605449151900057035L;
    	// 标签ID
        private Integer tagId;
    	// 标签名称
        private String tagName;
    	// 描述
        private String tagDescription;
    
        /**
         * 文章数量(不是数据库字段)
         */
        private Integer articleCount;
    
        public Tag() {
        }
    
        public Tag(Integer tagId) {
            this.tagId = tagId;
        }
    
        public Tag(Integer tagId, String tagName, String tagDescription, Integer articleCount) {
            this.tagId = tagId;
            this.tagName = tagName;
            this.tagDescription = tagDescription;
            this.articleCount = articleCount;
        }
    }
    
    • 标签实体类

      • 增加了一个非数据库属性:articleCount:文章数量总计
      • 增加了三个构造方法
        • 无参
        • 单一标签ID
        • 全参
    • 数据库设计

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pbeg5QkS-1675858523534)(C:\Users\ShiningSong\AppData\Roaming\Typora\typora-user-images\image-20230205151553132.png)]

2.2.12 User
  1. com.liuyanzhao.ssm.blog.entity.User

    package com.liuyanzhao.ssm.blog.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * @author liuyanzhao
     */
    @Data
    public class User implements Serializable{
        private static final long serialVersionUID = -4415517704211731385L;
    	// 用户ID
        private Integer userId;
    	// 用户名
        private String userName;
    	// 密码
        private String userPass;
    	// 昵称
        private String userNickname;
    	// 邮箱
        private String userEmail;
    	// 个人主页
        private String userUrl;
    	// 头像
        private String userAvatar;
    	// 上次登录IP
        private String userLastLoginIp;
    	// 注册时间
        private Date userRegisterTime;
    	// 上次登录时间
        private Date userLastLoginTime;
    	// 状态
        private Integer userStatus;
    
        /**
         * 用户角色:admin/user
         */
        private String userRole;
    
        /**
         * 文章数量(不是数据库字段)
         */
        private Integer articleCount;
    
    }
    
    • 用户实体类

      • 增加一个非数据库属性:articleCount:发布文章总数
    • 数据库设计

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AljMObZN-1675858523535)(C:\Users\ShiningSong\AppData\Roaming\Typora\typora-user-images\image-20230205151644431.png)]

2.3 enums枚举类

├─enums
│ ArticleCommentStatus.java
│ ArticleStatus.java
│ CategoryStatus.java
│ LinkStatus.java
│ MenuLevel.java
│ NoticeStatus.java
│ PageStatus.java
│ Role.java
│ UserRole.java

2.3.1 ArticleCommentStatus
  1. com.liuyanzhao.ssm.blog.enums.ArticleCommentStatus

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum ArticleCommentStatus {
    
        ALLOW(1, "允许"),
        NOT_ALLOW(0, "不允许");
    
        private Integer value;
    
        private String message;
    
        ArticleCommentStatus(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 文章是否允许评论
      • 1:允许
      • 0:不允许
2.3.2 ArticleStatus
  1. com.liuyanzhao.ssm.blog.enums.ArticleStatus

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum ArticleStatus {
    
        PUBLISH(1, "已发布"),
        DRAFT(0, "草稿");
    
        private Integer value;
    
        private String message;
    
        ArticleStatus(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 文章状态
      • 1:已发布
      • 0:草稿
2.3.3 CategoryStatus
  1. com.liuyanzhao.ssm.blog.enums.CategoryStatus

    package com.liuyanzhao.ssm.blog.enums;
    
    import lombok.Data;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum CategoryStatus {
    
        NORMAL(1, "正常"),
        HIDDEN(0, "隐藏");
    
        private Integer value;
    
        private String message;
    
        CategoryStatus(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 分类状态
      • 1:正常
      • 0:隐藏
2.3.4 LinkStatus
  1. com.liuyanzhao.ssm.blog.enums.LinkStatus

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum LinkStatus {
    
        NORMAL(1, "显示"),
        HIDDEN(0, "隐藏");
    
        private Integer value;
    
        private String message;
    
        LinkStatus(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 链接状态
      • 1:显示
      • 0:隐藏
2.3.5 MenuLevel
  1. com.liuyanzhao.ssm.blog.enums.MenuLevel

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum MenuLevel {
    
        TOP_MENU(1, "顶部菜单"),
        MAIN_MENU(2, "主体菜单");
    
        private Integer value;
    
        private String message;
    
        MenuLevel(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 菜单等级
      • 1:顶部菜单
      • 2:主体菜单
2.3.6 NoticeStatus
  1. com.liuyanzhao.ssm.blog.enums.NoticeStatus

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum NoticeStatus {
    
        NORMAL(1, "显示"),
        HIDDEN(0, "隐藏");
    
        private Integer value;
    
        private String message;
    
        NoticeStatus(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 通知状态
      • 1:显示
      • 0:隐藏
2.3.7 PageStatus
  1. com.liuyanzhao.ssm.blog.enums.PageStatus

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum PageStatus {
    
        NORMAL(1, "显示"),
        HIDDEN(0, "隐藏");
    
        private Integer value;
    
        private String message;
    
        PageStatus(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 页面状态
      • 1:显示
      • 0:隐藏
2.3.8 Role
  1. com.liuyanzhao.ssm.blog.enums.Role

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * @author 言曌
     * @date 2018/11/17 下午4:47
     */
    
    public enum Role {
    
        OWNER(1, "博主"),
        VISITOR(0, "其他用户");
    
        private Integer value;
    
        private String message;
    
        Role(Integer value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public void setValue(Integer value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 浏览角色
      • 1:博主
      • 0:其他用户
2.3.9 UserRole
  1. com.liuyanzhao.ssm.blog.enums.UserRole

    package com.liuyanzhao.ssm.blog.enums;
    
    /**
     * 角色枚举
     *
     * @author 言曌
     * @date 2021/2/25 2:31 下午
     */
    
    public enum UserRole {
    
        ADMIN("admin", "管理员"),
    
        USER("user", "用户");
    
        private String value;
    
        private String message;
    
        UserRole(String value, String message) {
            this.value = value;
            this.message = message;
        }
    
        public String getValue() {
            return value;
        }
    
        public void setValue(String value) {
            this.value = value;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    • 用户角色
      • admin:管理员
      • user:用户
2.4 dto数据传输对象

├─dto
│ ArticleParam.java
│ JsonResult.java
│ ResultVO.java
│ UploadFileVO.java

2.4.1 ArticleParam
  1. com.liuyanzhao.ssm.blog.dto.ArticleParam

    package com.liuyanzhao.ssm.blog.dto;
    
    import lombok.Data;
    
    import java.util.List;
    
    /**
     * @author 言曌
     * @date 2018/11/25 下午3:56
     */
    @Data
    public class ArticleParam {
    
        private Integer articleId;
    
        private String articleTitle;
    
        private String articleContent;
    
        private Integer articleParentCategoryId;
    
        private Integer articleChildCategoryId;
    
        private Integer articleOrder;
    
        private Integer articleStatus;
    
        private String articleThumbnail;
    
        private List<Integer> articleTagIds;
    
    }
    
    • 文章参数
2.4.2 JsonResult
  1. com.liuyanzhao.ssm.blog.dto.JsonResult

    package com.liuyanzhao.ssm.blog.dto;
    
    import lombok.Data;
    
    /**
     * @author 言曌
     * @date 2017/11/30 下午7:04
     */
    
    @Data
    public class JsonResult<T> {
    
    
        /**
         * 错误码
         */
        private Integer code;
    
        /**
         * 提示信息
         */
        private String msg;
    
        /**
         * 返回的具体内容
         */
        private T data;
    
    
        public JsonResult(Integer code, String msg, T data) {
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    
        public JsonResult() {
        }
    
    
        public JsonResult fail() {
            return new JsonResult(1, "操作失败", null);
        }
    
        public JsonResult fail(String msg) {
            return new JsonResult(1, msg, null);
        }
    
        public JsonResult ok() {
            return new JsonResult(0, "操作成功", null);
        }
    
    
        public JsonResult ok(T data) {
            return new JsonResult(0, "操作成功", data);
        }
    }
    
    • json传输结果
2.4.3 ResultVO
  1. com.liuyanzhao.ssm.blog.dto.ResultVO

    package com.liuyanzhao.ssm.blog.dto;
    
    import lombok.Data;
    
    /**
     * @author 言曌
     * @date 2017/11/30 下午7:04
     */
    
    @Data
    public class ResultVO<T> {
    
        /**
         * 错误码
         */
        private Integer code;
    
        /**
         * 提示信息
         */
        private String msg;
    
        /**
         * 返回的具体内容
         */
        private T data;
    
    
        public ResultVO(Integer code, String msg, T data) {
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    
        public ResultVO() {
        }
    }
    
    • 结果显示层对象
2.4.4 UploadFileVO
  1. com.liuyanzhao.ssm.blog.dto.UploadFileVO

    package com.liuyanzhao.ssm.blog.dto;
    
    import lombok.Data;
    
    /**
     * @author 言曌
     * @date 2017/11/30 下午7:41
     */
    
    @Data
    public class UploadFileVO {
    
        private String src;
    
        private String title;
    
    }
    
    • 上传文件显示层对象
2.5 util工具类

└─util
│ MyUtils.java

└─interceptor
AdminInterceptor.java
HomeResourceInterceptor.java
LoginInterceptor.java

2.5.1 MyUtils 我的工具类
  • com.liuyanzhao.ssm.blog.util.MyUtils

    package com.liuyanzhao.ssm.blog.util;
    
    import javax.servlet.http.HttpServletRequest;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    /**
     * 常用的方法
     * Created by 言曌 on 2017/8/24.
     */
    
    public class MyUtils {
    
        /**
         * 获得IP地址
         *
         * @param request
         * @return
         */
        public static String getIpAddr(HttpServletRequest request) {
            String ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
            }
            //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) {
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
            return ipAddress;
        }
    
    
        /**
         * 获得Md5加密
         *
         * @param str 原字符串
         * @return 加密后的字符串
         */
        public static String strToMd5(String str) {
            String md5Str = null;
            if (str != null && str.length() != 0) {
                try {
                    MessageDigest md = MessageDigest.getInstance("MD5");
                    md.update(str.getBytes());
                    byte b[] = md.digest();
    
                    int i;
                    StringBuffer buf = new StringBuffer("");
                    for (int offset = 0; offset < b.length; offset++) {
                        i = b[offset];
                        if (i < 0) {
                            i += 256;
                        }
                        if (i < 16) {
                            buf.append("0");
                        }
                        buf.append(Integer.toHexString(i));
                    }
                    //32位
                    md5Str = buf.toString();
                    //16位
                    //md5Str = buf.toString().substring(8, 24);
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                }
            }
            return md5Str;
        }
    
        /**
         * 根据email获取gravatar头像
         *
         * @param email Email
         * @return 头像URL
         */
        public static String getGravatar(String email) {
            String emailMd5 = strToMd5(email);
            //设置图片大小32px
            String avatar = "http://cn.gravatar.com/avatar/" + emailMd5 + "?s=128&d=identicon&r=PG";
            return avatar;
        }
    
    }
    
    • getIpAddr(HttpServletRequest request)

      • 获得IP地址

        • @param request HttpServletRequest请求
        • @return IP地址
      • 源代码

        public static String getIpAddr(HttpServletRequest request) {
            String ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
            }
            //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) {
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
            return ipAddress;
        }
        
      • 自上而下分别从request.getHeader("x-forwarded-for");、request.getHeader("Proxy-Client-IP");request.getHeader("WL-Proxy-Client-IP");request.getRemoteAddr();获取IP地址,若未从上方获取则进入下层获取。

      • 获取完毕后,若是多个代理的情况,则取第一个IP为客户端真实IP

    • String strToMd5(String str)

      • 获得Md5加密

        • @param str 原字符串
        • @return 加密后的字符串
      • 源代码

        public static String strToMd5(String str) {
            String md5Str = null;
            if (str != null && str.length() != 0) {
                try {
                    MessageDigest md = MessageDigest.getInstance("MD5");
                    md.update(str.getBytes());
                    byte b[] = md.digest();
        
                    int i;
                    StringBuffer buf = new StringBuffer("");
                    for (int offset = 0; offset < b.length; offset++) {
                        i = b[offset];
                        if (i < 0) {
                            i += 256;
                        }
                        if (i < 16) {
                            buf.append("0");
                        }
                        buf.append(Integer.toHexString(i));
                    }
                    //32位
                    md5Str = buf.toString();
                    //16位
                    //md5Str = buf.toString().substring(8, 24);
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                }
            }
        
    • String getGravatar(String email)

      • 根据email获取gravatar头像

        • @param email Email
        • @return 头像URL
      • 源代码

        public static String getGravatar(String email) {
            String emailMd5 = strToMd5(email);
            //设置图片大小32px
            String avatar = "http://cn.gravatar.com/avatar/" + emailMd5 + "?s=128&d=identicon&r=PG";
            return avatar;
        }
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值