通过SSM框架实现CMS管理系统。

项目搭建

Maven聚合工程搭建

如果你还不了解maven可以看如下文章:
Maven 聚合工程
工程的整体结构:
edu_home: 做为父工程,其余子工程继承该父工程。
子工程之间的依赖关系:按层拆分
edu_pojo 依赖 common(通用的工具类)
edu_dao 依赖 edu_pojo
edu_service 依赖 edu_dao
edu_api(war工程) 依赖 edu_service

依赖关系建立原则:当前项目中要用到哪个项目的资源,那么当前项目就依赖要用到资源的项目(依赖分为:直接依赖和间接依赖)

:::tips 思考:如何实现按模块划分的聚合工程呢? :::

创建父工程和子工程(module),父工程的pom配置如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <!-- 父工程 -->
  7. <groupId>com.edu</groupId>
  8. <artifactId>edu_home</artifactId>
  9. <packaging>pom</packaging>
  10. <version>1.0-SNAPSHOT</version>
  11. <!-- 聚合工程 -->
  12. <modules>
  13. <module>common</module>
  14. <module>edu_pojo</module>
  15. <module>edu_dao</module>
  16. <module>edu_service</module>
  17. <module>edu_api</module>
  18. </modules>
  19. <properties>
  20. <maven.compiler.source>8</maven.compiler.source>
  21. <maven.compiler.target>8</maven.compiler.target>
  22. </properties>
  23. </project>

注意edu_api 是一个web项目,整体结构如下:
image.png

  • 配置子工程的依赖关系
  1. edu_pojo 依赖 common(通用的工具类)

edu_pojo子工程如何依赖common子工程呢?其实就和我们依赖其他jar包一样,配置dependencies

    <dependencies>
        <!-- 引入common子工程 -->
        <dependency>
            <!-- 当前工程的group id -->
            <groupId>com.edu</groupId>
            <!-- 子工程名 -->
            <artifactId>common</artifactId>
            <!-- 版本 -->
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
  1. edu_dao 依赖 edu_pojo

     <dependencies>
         <dependency>
             <groupId>com.edu</groupId>
             <artifactId>edu_pojo</artifactId>
             <version>1.0-SNAPSHOT</version>
         </dependency>
     </dependencies>
    
  2. edu_service 依赖 edu_dao

     <dependencies>
         <dependency>
             <groupId>com.edu</groupId>
             <artifactId>edu_dao</artifactId>
             <version>1.0-SNAPSHOT</version>
         </dependency>
     </dependencies>
    
  3. edu_api(war工程) 依赖 edu_service

     <dependencies>
         <dependency>
             <groupId>com.edu</groupId>
             <artifactId>edu_service</artifactId>
             <version>1.0-SNAPSHOT</version>
         </dependency>
     </dependencies>
    

    至此,我们项目工程搭建已经完毕了:可以直接看edu_api 它的依赖关系
    image.png
    也可以直接运行:mvn install 如下日志 则我们的聚合工程搭建完毕
    image.png

  • 父工程的jar包依赖,其他所有的子工程够可以使用依赖的jar包

      <properties>
          <maven.compiler.source>8</maven.compiler.source>
          <maven.compiler.target>8</maven.compiler.target>
          <!-- 配置jar包的版本号 -->
          <spring.version>5.1.5.RELEASE</spring.version>
          <mybatis.version>3.5.4</mybatis.version>
      </properties>
    
      <!-- 锁定jar包的版本 -->
      <dependencyManagement>
          <dependencies>
              <!-- mybatis -->
              <dependency>
                  <groupId>org.mybatis</groupId>
                  <artifactId>mybatis</artifactId>
                  <version>${mybatis.version}</version>
              </dependency>
              <!-- spring mvc -->
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-webmvc</artifactId>
                  <version>${spring.version}</version>
              </dependency>
              <!-- sprin -->
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-context</artifactId>
                  <version>${spring.version}</version>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-core</artifactId>
                  <version>${spring.version}</version>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-web</artifactId>
                  <version>${spring.version}</version>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-aop</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-expression</artifactId>
                  <version>${spring.version}</version>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-aspects</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-test</artifactId>
                  <version>${spring.version}</version>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-jdbc</artifactId>
                  <version>${spring.version}</version>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-tx</artifactId>
                  <version>${spring.version}</version>
              </dependency>
          </dependencies>
      </dependencyManagement>
    
      <dependencies>
          <!-- 持久层依赖 -->
          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>5.1.37</version>
          </dependency>
          <dependency>
              <groupId>org.mybatis</groupId>
              <artifactId>mybatis</artifactId>
          </dependency>
          <dependency>
              <groupId>com.alibaba</groupId>
              <artifactId>druid</artifactId>
              <version>1.1.15</version>
          </dependency>
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.13</version>
              <scope>test</scope>
          </dependency>
          <dependency>
              <groupId>com.github.pagehelper</groupId>
              <artifactId>pagehelper</artifactId>
              <version>4.1.6</version>
          </dependency>
          <!-- spring依赖坐标 -->
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-context</artifactId>
          </dependency>
          <dependency>
              <groupId>org.aspectj</groupId>
              <artifactId>aspectjweaver</artifactId>
              <version>1.8.13</version>
          </dependency>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-jdbc</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-tx</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-test</artifactId>
          </dependency>
          <!-- mybatis整合spring的依赖 -->
          <dependency>
              <groupId>org.mybatis</groupId>
              <artifactId>mybatis-spring</artifactId>
              <version>1.3.1</version>
          </dependency>
          <!-- springmvc 的依赖 -->
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-webmvc</artifactId>
          </dependency>
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>javax.servlet-api</artifactId>
              <version>3.1.0</version>
              <scope>provided</scope>
          </dependency>
          <dependency>
              <groupId>javax.servlet.jsp</groupId>
              <artifactId>jsp-api</artifactId>
              <version>2.2</version>
              <scope>provided</scope>
          </dependency>
          <dependency>
              <groupId>jstl</groupId>
              <artifactId>jstl</artifactId>
              <version>1.2</version>
          </dependency>
          <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>
          <!--    Beanutils -->
          <dependency>
              <groupId>commons-beanutils</groupId>
              <artifactId>commons-beanutils</artifactId>
              <version>1.8.3</version>
          </dependency>
          <dependency>
              <groupId>commons-fileupload</groupId>
              <artifactId>commons-fileupload</artifactId>
              <version>1.3.1</version>
          </dependency>
          <!-- 解决跨域问题所需依赖 -->
          <dependency>
              <groupId>com.thetransactioncompany</groupId>
              <artifactId>cors-filter</artifactId>
              <version>2.5</version>
          </dependency>
      </dependencies>
    
      <build>
          <plugins>
              <plugin>
                  <groupId>org.apache.maven.plugins</groupId>
                  <artifactId>maven-compiler-plugin</artifactId>
                  <version>3.1</version>
                  <configuration>
                      <source>1.8</source>
                      <target>1.8</target>
                      <encoding>UTF-8</encoding>
                  </configuration>
              </plugin>
          </plugins>
      </build>
    

    OK,至此我们的项目已经初步搭建完毕了,下面开始进入编码阶段。

    SSM整合

    了解在实际项目中的整合ssm框架,以及聚合工程如何整合和拆分代码。

    POJO层

    创建实体类:com.edu.pojo.Test ```java package com.edu.pojo;

public class Test { private Integer id; private String name; settter/getter…. }

:::tips
注意:所有子工程的包名统一:com.edu.xxx
:::
<a name="dGmQJ"></a>
### DAO层
由于dao层依赖POJO层,所有DAO层可以直接获取到Test的实例。

1. 创建dao接口:`TestMapper`
```java
package com.edu.dao;

import com.edu.pojo.Test;

import java.util.List;

public interface TestMapper {
    List<Test> findAll();
}
  1. 创建映射配置文件:com/edu/dao/TestMapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.edu.dao.TestMapper">
     <select id="findAll" resultType="test">
         select *
         from account;
     </select>
    </mapper>
    
  2. 创建spring配置文件applicationContext-dao.xml 完成spring整合mybatis的配置

    <?xml version="1.0" encoding="UTF-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        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
    ">
     <!-- 1. 配置数据源 jdbc.properties -->
     <context:property-placeholder location="classpath:jdbc.properties"/>
    
     <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
         <property name="driverClassName" value="${jdbc.driver}"/>
         <property name="url" value="${jdbc.url}"/>
         <property name="username" value="${jdbc.username}"/>
         <property name="password" value="${jdbc.password}"/>
     </bean>
    
     <!-- 2. sqlSessionFactory -->
     <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
         <property name="dataSource" ref="dataSource"/>
         <!-- pojo实体类别名的配置 -->
         <property name="typeAliasesPackage" value="com.edu.pojo"/>
     </bean>
    
     <!-- 3. mapper映射扫描 -->
     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
         <property name="basePackage" value="com.edu.dao"/>
     </bean>
    </beans>
    
  3. MyBatis的其他配置(驼峰命名、分页插件等等),需要我们创建一个mybatis的核心配置文件mybatis-config.xml

    <?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>
         <!-- 开启驼峰命名 -->
         <setting name="mapUnderscoreToCamelCase" value="true"/>
     </settings>
    </configuration>
    

    Service层

    service层调用dao层的mapper相关代码,所以要引入dao层的依赖,在构建项目工程的时候,我们已经依赖好了。

  4. 创建service接口和实现类

    @Service
    public class TestServiceImpl implements TestService {
     @Autowired
     private TestMapper testMapper;
    
     @Override
     public List<Test> findAll() {
         return testMapper.findAll();
     }
    }
    
  5. service层的配置applicationContext-service.xml

扫描Service层的service注解,引入DAO层子工程的配置子模块

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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
">
    <!-- IOC 注解扫描 service -->
    <context:component-scan base-package="com.edu.service"/>

    <!-- 引入dao子工程的子模块配置 -->
    <import resource="classpath:applicationContext-dao.xml"/>
</beans>

Web/Api层

  1. 创建controller :com.edu.controller

    @RestController //组合了:@Controller和@ResponseBody
    @RequestMapping("/test")
    public class TestController {
    
     @Autowired //注入service
     private TestService testService;
    
     @RequestMapping("/findAll")
     public List<Test> findAll() {
         List<Test> list = testService.findAll();
         return list;
     }
    }
    
  2. 配置springmvc核心配置文件spring-mvc.xml

这里我们不在需要去配置视图解析器了,因为我们的项目是一个前后端分离的项目,只提供接口。

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 扫描controller层的IOC注解 -->
    <context:component-scan base-package="com.edu.controller"/>

    <!-- mvc注解增强:支持json读写 -->
    <mvc:annotation-driven/>

    <!-- 视图解析器 暂时不用配置,因为该项目是前后端分离的项目 -->

    <!-- 静态资源放行 -->
    <mvc:default-servlet-handler/>
</beans>
  1. spring核心配置文件applicationContext.xml

    在API层是真正加载spring的核心配置文件,所以我们需要把service的配置和dao层的配置都要引入到Api层的配置文件中,由于在service层引入了dao层的配置,所以在api层只需要将service层的配置引入即可,当服务器启动一起进行加载.

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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">

    <!-- 只需要 引入service层的配置 -->
    <import resource="classpath:applicationContext-service.xml"/>

</beans>
  1. web.xml 配置 ```xml <?xml version=”1.0” encoding=”UTF-8”?>

     <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>
    

     <filter-name>characterEncodingFilter</filter-name>
     <url-pattern>/*</url-pattern>
    

     <servlet-name>dispatcherServlet</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>classpath:spring-mvc.xml</param-value>
     </init-param>
     <load-on-startup>2</load-on-startup>
    

     <servlet-name>dispatcherServlet</servlet-name>
     <url-pattern>/</url-pattern>
    

<!-- spring的监听器-加载applicationContext.xml -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

<!-- 配置跨域过滤器 -->
<filter>
    <filter-name>corsFilter</filter-name>
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>corsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

ok,至此Api层的代码编写完成了,那么SSM也整合完毕,配置Tomcat服务器测试一下,我们写的接口。请求:`http://localhost:8080/edu_api/test/findAll`<br />返回json数据,那么就说明我们的项目搭建测试完毕。呼呼呼。。。。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/375694/1611152578095-d2f37b68-11aa-4fab-9a09-4ae9348abda7.png#align=left&display=inline&height=195&margin=%5Bobject%20Object%5D&name=image.png&originHeight=390&originWidth=526&size=18688&status=done&style=none&width=263)
<a name="Vlpd0"></a>
## 功能开发
<a name="sFWbG"></a>
#### 多条件查询需求
> 根据多个条件进行查询,例如
> 1. 只根据名称查询、只根据状态查询
> 1. 根据名称和状态查询
> 1. 无条件查询-查询所有操作

前端传递参数不确定,后台需要进行逻辑判断。

1. 创建VO类,主要用于接收前台向后台发送过来的参数
```java
/**
 * 前台传递参数的实体类
 */
public class CourseVo {
    private String courseName;
    private Integer status;
    //getter/setter...
}
  1. 调用service方法,传递VO

    @Service
    public class CourseServiceImpl implements CourseService {
    
     @Autowired
     private CourseMapper courseMapper;
    
     @Override
     public List<Course> findCourseByCondition(CourserVo courserVo) {
         List<Course> courses = courseMapper.findCourseByCondition(courserVo);
         return courses;
     }
    }
    
  2. 创建mapper映射,调用dao方法,执行SQL

     <select id="findCourseByCondition" resultMap="courseMap" parameterType="courseVo">
         select * from course
         <where>
             <if test="courseName != null and courseName != ''">
                 and course_name like concat('%',#{courseName},'%')
             </if>
             <if test="status != null and status != ''">
                 and status = #{status}
             </if>
             <if test="true">
                 and id_del != 1
             </if>
         </where>
     </select>
    
  3. 返回响应的实体类:ResponseResult

    /**
    * 响应的实体类
    */
    public class ResponseResult {
     private Boolean success;
     private String state;
     private String message;
     private Object content;
     //getter/setter...
    }
    
  4. 编写controller代码

    注意参数VO需要添加@RequestBody注解,它会将json参数封装成实体Bean

@RestController
@RequestMapping("/course")
public class CourseController {

    @Autowired
    private CourseService courseService;

    @PostMapping("/findAllCourse ")
    public ResponseResult findAllCourse(@RequestBody CourserVo courserVo) {
        List<Course> courses = courseService.findCourseByCondition(courserVo);
        ResponseResult result = new ResponseResult(true, StateCode.SUCCESS.getCode(), StateCode.SUCCESS.getMsg(), courses);
        return result;
    }
}

测试我们编写的代码。

图片上传需求

  1. springmvc 配置文件解析器

会将传过来的图片封装成Multipart

    <!-- 文件解析器 注意 id的值必须是:multipartResolver -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="1048567"/>
    </bean>
  1. controller接口去接收到图片信息,进行数据响应,fileName,filePath,创建map然后存储到响应实体类的content中

    参数名称要与请求参数的key一致

    @PostMapping("/courseUpload")
    public ResponseResult fileUpload(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws IOException {
        //1. 判断文件是否为空
        if (file.isEmpty()) {
            throw new RuntimeException();
        }
        //2. 获取项目部署路径
        //tomcat/webapp/edu_home/
        String realPath = request.getServletContext().getRealPath("/");
        //截取文件路径:tomcat/webapp/
        String path = realPath.substring(0, realPath.indexOf("edu_api"));

        //3. 获取文件的原名 jake.jpg
        String originalFilename = file.getOriginalFilename();

        //4. 生成新的文件名
        long millis = System.currentTimeMillis();
        //1231321213_.jpg
        String fileName = millis + "_" + originalFilename.substring(originalFilename.lastIndexOf("."));

        //5. 文件上传
        //文件上传的目录
        String uploadPath = path + "upload";
        File filePath = new File(uploadPath, fileName);
        //如果目录不存在 则进行创建
        if (!filePath.getParentFile().exists()) {
            filePath.getParentFile().mkdirs();
        }
        //图片就进行了真正的上传了
        file.transferTo(filePath);

        //6. 将文件名和文件路径返回进行响应
        HashMap<String, String> map = new HashMap<>();
        map.put("fileName", fileName);
        //如何将地址动态写活呢?
        map.put("filePath", "http://localhost:8080/upload/" + fileName);
        ResponseResult result = new ResponseResult(true, StateCode.SUCCESS.getCode(), StateCode.SUCCESS.getMsg(), map);
        return result;
    }

:::tips 思考:如何将文件存储路径写成动态的呢? ::: 注意 在启动Tomcat服务器的时候,要把upload文件夹也要添加部署进去,这样才能访问upload目录下的文件
image.png

  1. 接收到图片信息保存到服务器

    表与表之间关联的需求

    例如:在课程表和老师表两张表,我们在添加课程的时候,也需要添加老师的信息,但是老师的信息需要课程的id,所以就需要先添加课程然后得到课程的id,再去将老师的记录添加到表中。

  • 添加好Course课程信息,获取成功插入记录的ID值
  • 封装Teacher主要封装CourseId,在将记录真正添加到表中

image.png

  1. CourseController 编写代码

     @PostMapping("/saveOrUpdateCourse")
     public ResponseResult saveOrUpdateCourse(@RequestBody CourseVo courseVo) throws InvocationTargetException, IllegalAccessException {
         courseService.saveCourseOrTeacher(courseVo);
         ResponseResult result = new ResponseResult(true,
                 StateCode.SUCCESS.getCode(), StateCode.SUCCESS.getMsg(), null);
         return result;
     }
    
  2. CourseService 编写代码

    @Service
    public class CourseServiceImpl implements CourseService {
    
     @Autowired
     private CourseMapper courseMapper;
    
     @Override
     public List<Course> findCourseByCondition(CourseVo courseVo) {
         List<Course> courses = courseMapper.findCourseByCondition(courseVo);
         return courses;
     }
    
     @Override
     public void saveCourseOrTeacher(CourseVo courseVo) throws InvocationTargetException, IllegalAccessException {
         //1. 封装Course实体类
         Course course = new Course();
         //将courseVo中的属性值 赋给Course实体中
         BeanUtils.copyProperties(course, courseVo);
         //补全信息:创建时间和更新时间
         Date date = new Date();
         course.setCreateTime(date);
         course.setUpdateTime(date);
         //2. 调用dao 保存课程
         courseMapper.saveCourse(course);
         //3. 获取课程id 封装teacher实体
         int courseId = course.getId();
         Teacher teacher = new Teacher();
         BeanUtils.copyProperties(teacher, courseVo);
         teacher.setCourseId(courseId);
         teacher.setCreateTime(date);
         teacher.setUpdateTime(date);
         teacher.setIsDel(0);
         //4. 调用dao 保存讲师信息
         courseMapper.saveTeacher(teacher);
     }
    }
    
  3. CourseMapper 编写代码 ```xml

<insert id="saveCourse" parameterType="course">
    <!-- 获取新增成功之后的主键id -->
    <selectKey resultType="int" order="AFTER" keyProperty="id">
        select last_insert_id()
    </selectKey>
    insert into course(course_name,
    brief,
    preview_first_field,
    preview_second_field,
    course_img_url,
    course_list_img,
    sort_num,
    price,
    discounts,
    sales,
    discounts_tag,
    course_description_mark_down,
    create_time,
    update_time)
    values (#{courseName}, #{brief}, #{previewFirstField}, #{previewSecondField}, #{courseImgUrl},
    #{courseListImg}, #{sortNum}, #{price}, #{discounts}, #{sales}, #{discountsTag},
    #{courseDescriptionMarkDown},
    #{createTime}, #{updateTime})
</insert>

<!-- 新增讲师信息 -->
<insert id="saveTeacher" parameterType="teacher">
    INSERT INTO teacher(course_id,
                        teacher_name,
                        POSITION,
                        description,
                        create_time,
                        update_time)
    VALUES (#{courseId}, #{teacherName}, #{position}, #{description}, #{createTime}, #{updateTime});
</insert>
<a name="6usKI"></a>
#### 多表关联查询需求
> 例如我们要查询课程信息及关联的老师信息

主要是DAO层的实现:<br />外链查询:
```xml
    <!-- 查询课程信息 关联查询讲师信息 -->
    <select id="findByCourseId" resultType="courseBo" parameterType="int">
        select c.*, t.teacher_name, t.position, t.description
        from course c
                 left join teacher t on c.id = t.course_id
        where c.id = #{id}
    </select>

嵌套查询:
课程->章节->课时
课程->章节: 1 -> N
章节->课时: 1 -> N

  • 根据课程id查询具体的课程信息
  • 获取课程的章节信息列表,循环拿到章节id,然后查询关联的课时信息列表

在一方的实体类中,添加多方的list

    // 课时集合
    private List<CourseLesson> lessonList;

    public List<CourseLesson> getLessonList() {
        return lessonList;
    }

mapper映射文件

    <resultMap id="courseSectionMap" type="courseSection">
        <id property="id" column="id"/>
        <result property="courseId" column="course_id"/>
        <result property="sectionName" column="section_name"/>
        <result property="description" column="description"/>
        <result property="createTime" column="create_time"/>
        <result property="updateTime" column="update_time"/>
        <result property="isDe" column="is_de"/>
        <result property="orderNum" column="order_num"/>
        <result property="status" column="status"/>
        <collection property="lessonList" ofType="courseLesson"
                    select="com.edu.dao.CourseLessonMapper.findBySectionId"
                    column="id">
        </collection>
    </resultMap>

    <!-- 根据课程id查询课时 -->
    <select id="findSectionAndLessonByCourseId" resultType="courseSectionMap" parameterType="int">
        select *
        from course_section
        where course_id = #{id}
        order by order_num
    </select>

<!-- com.edu.dao.CourseLessonMapper -->

    <select id="findBySectionId" resultType="courseLesson" parameterType="int">
        select *
        from course_lesson
        where section_id = #{id}
    </select>

trim标签的使用

 <update id="updateCourse" parameterType="course">
        update course
        <!-- suffixOverrides会移除最后一个,逗号 -->
        <trim prefix="set" suffixOverrides=",">
            <if test="courseName != null and courseName != ''">
                course_name = #{courseName},
            </if>

            <if test="brief != null and brief != ''">
                brief=#{brief},
            </if>

            <if test="previewFirstField != null and previewFirstField != ''">
                preview_first_field=#{previewFirstField},
            </if>

            <if test="previewSecondField != null and previewSecondField != ''">
                preview_second_field=#{previewSecondField},
            </if>

            <if test="courseImgUrl != null and courseImgUrl != ''">
                course_img_url=#{courseImgUrl},
            </if>

            <if test="courseListImg != null and courseListImg != ''">
                course_list_img=#{courseListImg},
            </if>

            <if test="sortNum != null and sortNum != ''">
                sort_num=#{sortNum},
            </if>

            <if test="price != null and price != ''">
                price=#{price},
            </if>

            <if test="discounts != null and discounts != ''">
                discounts=#{discounts},
            </if>

            <if test="sales != null and sales != '' or sales==0">
                sales=#{sales},
            </if>

            <if test="discountsTag != null and discountsTag != ''">
                discounts_tag=#{discountsTag},
            </if>

            <if test="courseDescriptionMarkDown != null and courseDescriptionMarkDown != ''">
                course_description_mark_down=#{courseDescriptionMarkDown},
            </if>

            <if test="updateTime != null">
                update_time=#{updateTime},
            </if>
        </trim>
        <where>
            <if test="id!=null and id != ''">
                id=#{id}
            </if>
        </where>
    </update>

分页需求

需要使用到PageHelper的mybatis插件
需要准备的分页数据:

  1. 总条数
  2. 每页显示多少条
  3. 总页数
  4. 当前页
  5. 分页数据

image.png

  • PageHelper配置

          <dependency>
              <groupId>com.github.pagehelper</groupId>
              <artifactId>pagehelper</artifactId>
              <version>4.1.6</version>
          </dependency>
    

    mybatis配置文件中完成配置

      <!-- mybatis插件配置 -->
      <plugins>
          <plugin interceptor="com.github.pagehelper.PageHelper">
              <!-- 指定方言 -->
              <property name="dialect" value="mysql"/>
          </plugin>
      </plugins>
    

    也可以在applicationContext.xml整合mybatis中进行配置

      <!-- 2. sqlSessionFactory -->
      <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
          <property name="dataSource" ref="dataSource"/>
          <!-- applicationContext配置mybatis插件 -->
          <property name="plugins">
              <array>
                  <bean class="com.github.pagehelper.PageHelper">
                      <property name="properties">
                          <value>helperDialect=mysql</value>
                      </property>
                  </bean>
              </array>
          </property>
    
          <!-- 引入mybatis的其他配置 -->
          <property name="configLocation" value="classpath:mybatis-config.xml"/>
      </bean>
    
      <!-- 3. mapper映射扫描 -->
      <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
          <property name="basePackage" value="com.edu.dao"/>
      </bean>
    

    在Service层使用PageHelper进行分页 :::tips 注意:PageHelper.startPage() 一定要在mapper调用之前,否则无法分页的 :::

    @Service
    public class PromotionAdServiceImpl implements PromotionAdService {
    
      @Autowired
      private PromotionAdMapper promotionAdMapper;
    
      @Override
      public PageInfo<PromotionAd> findAllPromotionAdByPage(PromotionAdVo promotionAdVo) {
          PageHelper.startPage(promotionAdVo.getCurrentPage(), promotionAdVo.getPageSize());
          List<PromotionAd> promotionAdList = promotionAdMapper.findAllPromotionAdByPage();
          //分页信息以及分页数据
          PageInfo<PromotionAd> pageInfo = new PageInfo<>(promotionAdList);
          return pageInfo;
      }
    }
    

    注解的方式 解决日期格式的问题

    可以使用如下的注解方式解决日期格式,而不用在自定义类型转换器了。

      import org.springframework.format.annotation.DateTimeFormat;
      @DateTimeFormat(pattern = "yyyy-MM-dd")
      private Date startCreateTime;
    
      @DateTimeFormat(pattern = "yyyy-MM-dd")
      private Date endCreateTime;
    

    权限系统

    权限:权利和限制,在权限范围内做好自己的事情.

  • 认证:验证用户名密码是否正确的过程

  • 授权:对用户所能访问的资源进行控制

不用登录的用户有不同的权利,例如:财务经理针对系统中财务相关模块进行操作,人事经理针对系统中人事模块进行操作。

权限控制基本原理:

  1. ACL(Assecc Control Lists )

    ACL 是最早也是最基本的一种访问控制机制,它的原理非常简单:每一项资源,都配有一个列表,这个列表记录的就是哪些用户可以对这项资源执行CURD的操作,当系统视图访问这项资源时,会首先检查这个列表中是否有关于当前用户的访问权限,从而确定当前用户可否执行相应的操作。ACL是一种面向资源的访问控制模型,它的机制是围绕“资源”展开的

  2. 基于角色的访问控制RBAC (Role-Based Access Control)

    RBAC 是把用户按角色进行归类,通过用户的角色确定用户能否针对某项资源进行某项操作,RBAC相当于ACL最大的优势就是它简化了用户与权限的管理,通过对用户进行分类,使得角色与权限关联起来,而用户与权限变成了间接关联。RBAC模型使得访问控制,特别是对用户的授权管理编程简单和易于维护,被广泛应用。

image.png

  • 每个登录的用户,可以有多个角色
  • 每个角色又可以拥有多个权限(菜单、资源访问)

例子:
image.png :::tips 权限系统经典五张表:用户表、角色表、权限表、用户角色中间表、角色权限中间表。用户表和权限表没有直接关系,是通过角色表进行了间接关联 :::

下面已角色和菜单为例:
角色分配菜单管理:三个主要接口

  • 显示了针对当前角色可以添加的菜单信息
  • 主要是角色和菜单的关联:将选中的菜单信息和当前角色进行关联
  • 回显当前角色已经关联的菜单
  1. 查询所有的菜单节点信息。

菜单的自连接查询,菜单分为顶级菜单和子级菜单。
首先查询顶级的菜单,所有的顶级菜单parent_id都为-1

select * from menu where parent_id = -1

查询顶级菜单所关联的子级菜单:

select * from menu where parent_id = 1

基于mybatis嵌套查询完成:

    <resultMap id="menuMap" type="Menu">
        <id column="id" property="id"></id>
        <result column="href" property="href"></result>
        <result column="icon" property="icon"></result>
        <result column="name" property="name"></result>
        <result column="parent_id" property="parentId"></result>
        <result column="description" property="description"></result>
        <result column="order_num" property="orderNum"></result>
        <result column="shown" property="shown"></result>
        <result column="created_time" property="createdTime"></result>
        <result column="updated_time" property="updatedTime"></result>
        <result column="created_by" property="createdBy"></result>
        <result column="updated_by" property="updatedBy"></result>

        <collection property="subMenuList" ofType="Menu"
                    select="findAllMenu" column="id">

        </collection>
    </resultMap>

    <!-- 查询所有父子关系的菜单 -->
    <select id="findAllMenu" resultMap="menuMap" parameterType="int">
        select *
        from menu
        where parent_id = #{id}
    </select>
  1. 根据角色id查询已经关联的菜单信息

角色表和菜单表有一个中间表,通过中间表关联的roleId 找到menuId

    <select id="findMenuIdByRoleId" resultType="java.lang.Integer" parameterType="int">
        select menu_id
        from role_menu_relation
        where role_id = #{roleId}
    </select>
  1. 为角色分配菜单列表

    向角色菜单中间表添加记录,接收前端传递过来的菜单id列表,遍历菜单id.插入即可。 但是需要注意,如果该角色之前关联了某些菜单,而在前端操作了不关联了之前某个菜单,又关联了新的菜单,那么点击保存并没有在中间表中去掉之前的关联信息。也就是说我们在向角色菜单中间表添加关联关系时,通常会先把之前的关联关系清空,在添加关联关系。

例如:

  • 角色A的原有菜单列表:【1,4,5,6,7,8,12】
  • 在前端操作修改菜单列表:【1,5,7,8,11,12】
  • 这时候如果后端直接插入到中间表,那么4和6的菜单关联关系并没有去掉 最终变成了:[1,4,5,6,7,8,11,12]

image.png
在插入之前,先删除之前的关联关系:

delete from 中间表 where role_id = ?

最终实现代码如下:

  • dao层的代码实现:

      <!-- 根据roleId 删除中间表的关系 -->
      <delete id="deleteRoleContextMenu" parameterType="int">
          delete
          from role_menu_relation
          where role_id = #{roleId}
      </delete>
    
      <!-- 关联角色的菜单,为角色分配菜单 -->
      <insert id="roleContextMenu" parameterType="Role_menu_relation">
          insert into role_menu_relation
          values (null, #{menuId}, #{roleId}, #{createdTime}, #{updatedTime}, #{createdBy}, #{updatedBy})
      </insert>
    
  • Service层的代码实现:

    //事务管理 防止删除后  添加出现异常导致没有添加成功,而且关联关系也删除了
      @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
      @Override
      public void roleContextMenu(RoleMenuVo roleMenuVo) {
          //1. 先删除关联关系
          roleMapper.deleteRoleContextMenu(roleMenuVo.getRoleId());
          //2. 添加关联关系
          List<Integer> menuIdList = roleMenuVo.getMenuIdList();
          Date date = new Date();
          for (Integer menuId : menuIdList) {
              Role_menu_relation relation = new Role_menu_relation();
              relation.setRoleId(roleMenuVo.getRoleId());
              relation.setMenuId(menuId);
              relation.setCreatedTime(date);
              relation.setUpdatedTime(date);
              relation.setCreatedBy("system");
              relation.setUpdatedby("system");
              roleMapper.roleContextMenu(relation);
          }
      }
    

    测试:
    image.png
    查询角色管理的菜单信息
    image.png

  1. 删除角色操作那么角色和菜单的关联表也要删除相同的角色信息
  • 先清空和中间表的关联关系:用户和角色的中间表,角色和菜单的中间表
  • 在进行角色表的删除
      @Transactional(propagation = Propagation.REQUIRED)
      @Override
      public void deleteRole(Integer roleId) {
          //删除角色和菜单的关联关系
          roleMapper.deleteRoleContextMenu(roleId);
          //删除角色和用户的关联关系
          roleMapper.deleteRoleContextUser(roleId);
          //从角色表删除角色
          roleMapper.deleteRole(roleId);
      }
    
  1. 用户登录之后查询角色授权的权限
  • 根据用户id,从用户角色中间表查询角色id
  • 根据角色id,查询角色的信息列表进行显示(一个用户有多个角色)

      <!-- 查询用户拥有的角色 -->
      <select id="findUserRoleById" resultType="com.edu.pojo.Role" parameterType="int">
          select *
          from roles r
                   inner join user_role_relation urr on r.id = urr.role_id
          where user_id = #{id}
      </select>
    
  • 给用户分配角色,需要移除之前的关联信息,添加新的角色信息

      <!--
          删除用户角色中间表用户关联的数据
       -->
      <delete id="deleteUserContextRole" parameterType="int">
          delete
          from user_role_relation
          where user_id = #{userId}
      </delete>
    
      <!-- 建立用户和角色的关联关系 -->
      <insert id="insertUserContextRole" parameterType="User_Role_relation">
          insert into user_role_relation
          values (null, #{userId}, #{roleId}, #{createdTime}, #{updatedTime}, #{createdBy}, #{updatedby})
      </insert>
    

    service层业务逻辑实现:注意要进行事务管理,因为涉及到了删除、和多个新增操作,如果出现异常没有统一事务管理会导致数据出现错误

      /**
       * 给用户分配角色
       *
       * @param userRoleVo
       */
      @Transactional(propagation = Propagation.REQUIRED)
      @Override
      public void userContextRole(UserRoleVo userRoleVo) {
          //1. 移除之前关联的角色
          userMapper.deleteUserContextRole(userRoleVo.getUserId());
          //2. 关联新的角色
          // 获取角色id 列表
          List<Integer> roleIdList = userRoleVo.getRoleIdList();
          if (roleIdList != null && roleIdList.size() > 0) {
              for (Integer roleId : roleIdList) {
                  User_Role_relation relation = new User_Role_relation();
                  relation.setUserId(userRoleVo.getUserId());
                  relation.setRoleId(roleId);
                  Date date = new Date();
                  relation.setCreatedTime(date);
                  relation.setUpdatedTime(date);
                  relation.setCreatedBy("system");
                  relation.setUpdatedby("system");
                  userMapper.insertUserContextRole(relation);
              }
          }
      }
    
  • 获取用户拥有的权限:动态显示菜单列表

    1. 通过用户id查询用户角色中间表,查询到具体关联的角色。
    2. 根据角色查询角色菜单中间表和查询角色资源中间表。

image.png
DAO层的编写:

<!-- 查询用户拥有的角色 -->
    <select id="findUserRoleById" resultType="com.edu.pojo.Role" parameterType="int">
        select *
        from roles r
                 inner join user_role_relation urr on r.id = urr.role_id
        where user_id = #{id}
    </select>

    <!--  根据角色id 查询顶级菜单 -->
    <select id="findParentMenuByRoleId" resultType="Menu" parameterType="list">
        select DISTINCT m.* from roles r inner join role_menu_relation rmr on r.id = rmr.role_id
        inner join menu m on rmr.menu_id = m.id
        where m.parent_id=-1 and r.id in
        <foreach collection="list" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>

    <!-- 根据顶级菜单去查询所分配的子级菜单 且只包括了有权限的所有子菜单id -->
    <select id="findSubMenuByPid" resultType="Menu">
        select * from menu where parent_id = #{pid} and id in
        <foreach collection="contextSubIds" item="subId" open="(" separator="," close=")">
            #{subId}
        </foreach>
    </select>

    <!-- 查询有权限的所有子菜单id -->
    <select id="findContextSubByRoleId" resultType="java.lang.Integer">
        select distinct m.id from menu m join role_menu_relation rmr on m.id = rmr.menu_id
        join roles r on rmr.role_id = r.id
        where r.id in
        <foreach collection="role_ids" item="roleId" open="(" separator="," close=")">
            #{roleId}
        </foreach>
        and m.id not in
        <foreach collection="pids" item="pid" open="(" separator="," close=")">
            #{pid}
        </foreach>
    </select>

    <!-- 获取用户关联的资源权限信息 -->
    <select id="findUserContextResourceByRoleId" resultType="com.edu.pojo.Resource" parameterType="list">
        select * from resource r inner join role_resource_relation rrr on r.id = rrr.resource_id
        inner join roles r2 on rrr.role_id = r2.id
        where r2.id in
        <foreach collection="list" item="roleId" open="(" separator="," close=")">
            #{roleId}
        </foreach>
    </select>

Service 层的编写:

@Override
    public ResponseResult getUserPermission(Integer userId) {
        //1. 根据用户id 查询用户所拥有的角色
        List<Role> roles = userMapper.findUserRoleById(userId);
        //2. 获取角色id 保存list集合中
        ArrayList<Integer> roleIds = new ArrayList<>();
        for (Role role : roles) {
            roleIds.add(role.getId());
        }
        //3. 获取角色分配的顶级菜单信息
        List<Menu> parentMenus = userMapper.findParentMenuByRoleId(roleIds);
        List<Integer> pids = new ArrayList<>();
        for (Menu parentMenu : parentMenus) {
            pids.add(parentMenu.getId());
        }
        //5. 查询有权限的所有子菜单id
        List<Integer> subIds = userMapper.findContextSubByRoleId(pids, roleIds);
        //6. 查询所在父菜单 分配的子级菜单
        for (Menu parentMenu : parentMenus) {
            List<Menu> subMenus = userMapper.findSubMenuByPid(parentMenu.getId(), subIds);
            parentMenu.setSubMenuList(subMenus);
        }
        //7. 查询所有的资源信息
        List<Resource> resources = userMapper.findUserContextResourceByRoleId(roleIds);

        HashMap<String, Object> map = new HashMap<>();
        map.put("menuList", parentMenus);
        map.put("resourceList", resources);

        ResponseResult responseResult = new ResponseResult(true, StateCode.SUCCESS.getCode(), StateCode.SUCCESS.getMsg(), map);
        return responseResult;
    }

Controller层的编写:

    @GetMapping("getUserPermission")
    public ResponseResult getUserPermission(HttpServletRequest request) {
        //获取请求头 设置的access_token信息,对比token获取session中存储的userId
        String req_access_token = request.getHeader("Authorization");
        HttpSession session = request.getSession();
        String access_token = (String) session.getAttribute("access_token");
        if (access_token.equals(req_access_token)) {//校验正确
            Integer user_id = (Integer) session.getAttribute("user_id");
            ResponseResult result = userService.getUserPermission(user_id);
            return result;
        } else {//校验错误
            //响应错误
            ResponseResult responseResult = new ResponseResult(false, "400", "token过期", null);
            return responseResult;
        }
    }

MD5 加密密码

MD5加密,它是对信息进行摘要采集,再通过一定的位运算,最终获取加密后的MD5字符串

  1. 针对不同长度待加密的数据,字符串等等,其都可以返回一个固定长度的MD5加密字符串,通常32位的16进制字符串。
  2. 其加密过程几乎不可逆,除非维护一个庞大的key-value数据库进行碰撞破解
  3. 运算简便,且可实现方式多样,通过一定的处理方式也可以避免碰撞算法的破解(加盐:随机字符串)
  4. 对于一个固定的字符串、数字等等,MD5加密后的字符串是固定的,不管MD5加密多少次都是同样的效果

添加依赖:

        <!-- MD5 加密相关 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.11</version>
        </dependency>

MD5工具类:

    /**
     * MD5方法
     *
     * @param text 明文 123456
     * @param key  密钥  lagou
     * @return 密文
     * @throws Exception
     */
    public static String md5(String text, String key) throws Exception {
        //加密后的字符串
        String encodeStr = DigestUtils.md5Hex(text + key);
        System.out.println("MD5加密后的字符串为:encodeStr=" + encodeStr);
        return encodeStr;
    }

    /**
     * MD5验证方法
     *
     * @param text 明文
     * @param key  密钥
     * @param md5  密文
     * @return true/false
     * @throws Exception
     */
    public static boolean verify(String text, String key, String md5) throws Exception {
        //根据传入的密钥进行验证
        String md5Text = md5(text, key);
        if (md5Text.equalsIgnoreCase(md5)) {
            System.out.println("MD5验证通过");
            return true;
        }
        return false;
    }

登录需求

image.png

    @RequestMapping("/login")
    public ResponseResult login(User user, HttpServletRequest request) {
        try {
            User findUser = userService.findByPhone(user);
            if (findUser != null) {
                //登录成功
                //存储session中 access_token
                HttpSession session = request.getSession();
                String access_token = UUID.randomUUID().toString();
                session.setAttribute("access_token", access_token);
                session.setAttribute("user_id", findUser.getId());
                HashMap<String, Object> map = new HashMap<>();
                map.put("access_token", access_token);
                map.put("user_id", findUser.getId());
                ResponseResult responseResult = new ResponseResult(true, StateCode.SUCCESS.getCode(), StateCode.SUCCESS.getMsg(), map);
                return responseResult;
            } else {
                //登录失败
                ResponseResult responseResult = new ResponseResult(true, StateCode.LOGIN_ERROR.getCode(), StateCode.LOGIN_ERROR.getMsg(), null);
                return responseResult;
            }
        } catch (Exception e) {
            e.printStackTrace();
            ResponseResult responseResult = new ResponseResult(true, StateCode.LOGIN_ERROR.getCode(), StateCode.LOGIN_ERROR.getMsg(), "");
            return responseResult;
        }
    }

后台项目部署

  1. 将sql导入到服务器中,通过数据库图形工具连接服务器

image.png

  1. 项目打包发布

在平时开发过程中,不同的环境中项目的相关配置也会有相关的不同,我们在不同的环境中部署就要手动修改为对应环境的配置,通过maven的相关配置来在打包时指定各个环境对应配置文件。
创建两个配置文件:development.properties 开发环境配置。product.properties生产环境配置
image.png
在之前配置的jdbc.properties 中进行修改,动态引用配置

jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = ${jdbc.url}
jdbc.username = ${jdbc.username}
jdbc.password = ${jdbc.password}

maven配置:在DAO层的pom.xml 中进行配置

 <profiles>
        <profile>
            <id>dev</id>
            <properties>
                <!-- 测试环境 -->
                <env>development</env>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
        </profile>
        <profile>
            <id>prod</id>
            <properties>
                <!-- 正式环境 -->
                <env>product</env>
            </properties>
        </profile>
    </profiles>

    <build>
        <finalName>web</finalName>
        <filters>
            <filter>src/main/resources/filter/${env}.properties</filter>
        </filters>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <excludes>
                    <exclude>filter/*.properties</exclude>

                </excludes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

这时候在maven的工具中就会多了一个 Profiles 来指定环境
image.png
项目打包
• 命令打包


打本地包 mvn -Pdev install 或者mvn install(因为本例activeByDefault配的为true)
打产品包 mvn -Pprod install

结果:src/main/resources/config/jdbc.properties根据 mvn -P 参数决定值

最终会在edu_api Web层的target中,可以看到其他的其子工程都打成了jar
image.png
• 使用idea打包
image.png
打包之后将其放在Tomcat的webapps/目录下即可

前端项目部署

3.2.1 修改配置文件

  • 生产环境配置文件,配置后台URL

SSM 单体架构实战-常见需求实现 - 图19

VUE_APP_NAME = Edu Boss
VUE_APP_TITLE = Lagou Edu Boss (Dev)

VUE_APP_STORAGE_PREFIX = lagou_edu_boss_dev

#VUE_APP_API_FAKE = /front
VUE_APP_API_FAKE = http://192.168.52.100:8080/ssm-web

#VUE_APP_API_BASE = /boss
VUE_APP_API_BASE = http://192.168.52.100:8080/ssm-web
  • 自定义配置文件,配置打包相关信息

SSM 单体架构实战-常见需求实现 - 图20

将下面内容拷贝到 vue.config.js

module.exports = {
  publicPath: process.env.NODE_ENV === "production" ? "/edu-boss/" : "/",
  indexPath: "index.html",
  assetsDir: "static",
  lintOnSave: process.env.NODE_ENV !== "production",
  productionSourceMap: false,
  devServer: {
    open: true,
    port: 8081
  }
};

3.2.2 打包测试操作

  • 打包命令
npm run build
  • 在项目下会生成一个 dist 目录

SSM 单体架构实战-常见需求实现 - 图21

  • 在本地tomcat的webapps目录下,创建一个edu-boss文件夹,将dist目录中的文件拷贝到里面

SSM 单体架构实战-常见需求实现 - 图22

  1. 测试: 启动本地tomcat ,访问前端项目 路径为:
http://localhost:8081/edu-boss/

3.3.3 发布前端项目

  1. 解压一个新的tomcat, 修改端口号
#解压
tar xvf apache-tomcat-8.5.50.tar 
#改名
mv apache-tomcat-8.5.50 ui-tomcat
#修改端口号
cd ui-tomcat/conf/
vim server.xml

SSM 单体架构实战-常见需求实现 - 图23

SSM 单体架构实战-常见需求实现 - 图24

  1. 上传前端项目到 webapps
//上传 edu-boss.zip ,并解压
unzip edu-boss.zip 

//删除edu-boss.zip
rm -rf edu-boss.zip
  1. 运行前端项目,并访问
./bin/startup.sh 

//动态查看日志
tail -f logs/catalina.out 

//访问
http://192.168.52.100:8081/edu-boss/

3.3 修改tomcat默认访问项目

  • 使用notpad打开前端tomcat的配置文件 server.xml, 找到 Host 标签

SSM 单体架构实战-常见需求实现 - 图25

  1. 在Host标签内加入
<Context path="" docBase="edu-boss" reloadable="true" debug="0" privileged="true">
</Context>
  1. 重新启动 并访问前端项目,这个时候只需要直接访问 8081即可
http://192.168.52.100:8081/

3.4 配置反向代理

  1. 使用notpad打开nginx的配置文件 nginx.conf

SSM 单体架构实战-常见需求实现 - 图26

  1. 配置反向代理
#配置ssm项目 反向代理
    upstream lagouedu{
        server 192.168.52.100:8081;
    }

    server {
        listen       80;
        server_name  www.edu-boss.com;

        location / {

            proxy_pass http://lagouedu;  #转发的地址
            index  index.html index.htm;
        }
    }
  1. 修改本地hosts, 配置域名映射

SSM 单体架构实战-常见需求实现 - 图27

  1. 访问 www.edu-boss.com

SSM 单体架构实战-常见需求实现 - 图28