Bootstrap

SSM整合

SSM整合(配置类形式)

环境准备

  • Step1: 创建web项目并在pom.xml文件中导入所有相关坐标及相关插件(Tomcat、maven插件)

    • 导入Spring基础坐标:spring-context

    • 导入Spring提供的监听器ContextLoaderListener的相关坐标:spring-web

    • 导入Spring集成Web环境相关坐标:servlet、jsp

    • 导入Spring注解相关坐标:Annotation

    • 导入与AOP相关的坐标:aop、aspectj

      • AOP坐标会在导入spring-context坐标后系统自动导入,如图所示

        在这里插入图片描述

    • 导入事务相关坐标:spring-tx

    • 导入数据库相关坐标:mysql、数据源坐标(druid、cp30)

    • 导入Spring集成JUnit相关坐标:junit、spring-test

    • 导入Spring集成MyBatis相关坐标:mybatis、spring-jdbc、mybatis-spring

    • SpringMVC的基础坐标:spring-webmvc (注意:必须为必须为5.2.x.RELEASE版本)

    • 导入获取指定格式(比如JSON)字符串的坐标:jackson-core、jackson-databind、jackson-annotations

    • 导入文件上传相关坐标:fileupload、io

    <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>
        <parent>
            <groupId>org.example</groupId>
            <artifactId>SpringMvcDemo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <artifactId>MvcReqXmlDemo</artifactId>
        <packaging>war</packaging>
        <name>SpringMvcThree Maven Webapp</name>
        <url>http://maven.apache.org</url>
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>3.8.1</version>
                <scope>test</scope>
            </dependency>
            <!--===================Spring基础坐标=======================-->
            <!--spring坐标-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>6.1.6</version>
            </dependency>
    
            <!--===================Spring提供的监听器ContextLoaderListener相关的坐标=======================-->
            <!--spring-web -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>5.2.25.RELEASE</version>
            </dependency>
    
            <!--===================Spring集成web环境相关坐标=======================-->
            <!-- servlet-->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>4.0.1</version>
                <scope>provided</scope>
            </dependency>
            <!--jsp-->
            <dependency>
                <groupId>javax.servlet.jsp</groupId>
                <artifactId>javax.servlet.jsp-api</artifactId>
                <version>2.3.3</version>
                <scope>provided</scope>
            </dependency>
    
            <!--===================Spring注解相关坐标=======================-->
            <!--Annotation坐标-->
            <dependency>
                <groupId>javax.annotation</groupId>
                <artifactId>javax.annotation-api</artifactId>
                <version>1.3.2</version>
            </dependency>
    
            <!--=====================Spring集成AOP相关坐标=========================-->
            <!--aspectj坐标-->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.22.1</version>
            </dependency>
            
            <!--=====================Spring集成事务相关坐标=========================-->
            <!--spring-tx坐标-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>6.1.15</version>
            </dependency>
            
            <!--=====================数据库相关坐标=========================-->
            <!--mysql坐标-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.33</version>
            </dependency>
    
            <!--druid坐标-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.2.18</version>
            </dependency>
    
            <!--c3p0坐标-->
            <dependency>
                <groupId>com.mchange</groupId>
                <artifactId>c3p0</artifactId>
                <version>0.9.5.5</version>
            </dependency>
    
            <!--===================Spring集成JUnit相关的坐标=======================-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.2</version>
                <scope>test</scope>
            </dependency>
            <!--spring-test坐标-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>6.1.6</version>
                <scope>test</scope>
            </dependency>
            
            <!--=====================Spring集成MyBatis相关坐标=========================-->
            <!--MyBatis坐标-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.16</version>
            </dependency>
            <!--mybatis-spring-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>3.0.3</version>
            </dependency>
            <!--spring-jdbc-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>6.1.10</version>
            </dependency>
    
            <!--===================SpringMVC基础坐标=======================-->
            <!--spring-webmvc-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.2.25.RELEASE</version>
            </dependency>
            
            <!--===================获取指定格式(比如JSON)数据相关坐标=======================-->
            <!--jackson-core-->
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-core</artifactId>
                <version>2.17.1</version>
            </dependency>
            <!--jackson-databind-->
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.17.1</version>
            </dependency>
            <!--jackson-annotations-->
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-annotations</artifactId>
                <version>2.17.1</version>
            </dependency>
            
            <!--===================文件上传相关坐标=======================-->
            <!--fileupload坐标-->
            <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>1.5</version>
            </dependency>
    
            <!--io坐标-->
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.17.0</version>
            </dependency>
    
        </dependencies>
        <build>
            <finalName>SpringMvcThree</finalName>
            <plugins>
                <!-- Tomcat插件 -->
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.2</version>
                </plugin>
                <!--<plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    &lt;!&ndash; maven插件版本 &ndash;&gt;
                    <version>3.13.0</version>
                    <configuration>
                        &lt;!&ndash; Java版本 &ndash;&gt;
                        <source>21</source>
                        <compilerArgs>
                            <arg>-parameters</arg>
                        </compilerArgs>
                    </configuration>
                </plugin>-->
            </plugins>
        </build>
    </project>
    
  • Step2: 创建数据库ssm_db,并在该库下创建表tbl_book并使IDEA与数据库建立连接 ,SQL代码如下

    -- 创建ssm_db数据库
    CREATE DATABASE IF NOT EXISTS ssm_db CHARACTER SET utf8;
    
    -- 使用ssm_db数据库
    USE ssm_db;
    
    -- 创建tbl_book表
    CREATE TABLE tbl_book(
        id INT PRIMARY KEY AUTO_INCREMENT, -- 图书编号
        TYPE VARCHAR(100), -- 图书类型
        NAME VARCHAR(100), -- 图书名称
        description VARCHAR(100) -- 图书描述
    );
    -- 添加初始化数据
    INSERT INTO tbl_book VALUES(NULL,'计算机理论','Spring实战 第5版','Spring入门经典教材,深入理解Spring原理技术内幕');
    INSERT INTO tbl_book VALUES(NULL,'计算机理论','Spring 5核心原理与30个类手写实战','十年沉淀之作,手写Spring精华思想');
    INSERT INTO tbl_book VALUES(NULL,'计算机理论','Spring 5设计模式','深入Spring源码剖析,Spring源码蕴含的10大设计模式');
    INSERT INTO tbl_book VALUES(NULL,'市场营销','直播就该这么做:主播高效沟通实战指南','李子柒、李佳琦、薇娅成长为网红的秘密都在书中');
    INSERT INTO tbl_book VALUES(NULL,'市场营销','直播销讲实战一本通','和秋叶一起学系列网络营销书籍');
    INSERT INTO tbl_book VALUES(NULL,'市场营销','直播带货:淘宝、天猫直播从新手到高手','一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');
    

    在这里插入图片描述

    在这里插入图片描述

  • Step3: 右键源代码配置文件目录(即资源文件resources)→NewFile,创建properties配置文件,博主文件名为jdbc.properties,该配置文件代码如下

    • 注意: properties配置文件中配置的各个属性前必须添加个id.(即id.属性,比如:属性url就设置为id.url,博主设置的为jdbc.url),以供Spring配置文件可以使用属性占位符${} 语法引用这些属性

      #driverClassName代表数据库驱动,后跟驱动全类名(在MySQL驱动jar包下的META-INF下的services文件夹下的java.sql.Driver文件内)
      jdbc.driverClassName=com.mysql.cj.jdbc.Driver
      # 数据库连接URL
      jdbc.url=jdbc:mysql://localhost:3306/ssm_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
      # 数据库用户名
      jdbc.username=root
      # 数据库密码
      jdbc.password=123456
      # 初始化连接数量---即容器中初始的数据库连接数量
      jdbc.initialSize=5
      # 最大活跃连接数量---容器中初始为5个,但若5个用完了,此时可以在申请5个数据库连接数量
      #也就是说容器中最多存放10个数据库连接
      jdbc.maxActive=10
      # 获取连接时的最大等待时间,单位:毫秒。---与数据库进行连接时若超过3s仍未连接成功,则会报错
      jdbc.maxWait=3000
      #最小空闲连接数量---minIdle=5
      # 配置检测连接是否有效的SQL,可以是一个查询语句,如果不指定则默认为"SELECT 1"---validationQuery=SELECT 1
      # 是否开启自动提交事务---defaultAutoCommit=true
      
  • Step4:at.guigu包下创建三层架构包dao、service、controller以及pojo、config包,并在service包下创建impl包。同时在源代码配置文件目录(即资源文件resources)下创建三层文件夹at/guigu/dao

    在这里插入图片描述

  • Step5: 在config包下创建Spring的分配置类:JdbcConfiguration类(即DataSourceConfiguration)、MyBatisConfiguration类以及Spring的核心配置类SpringConfiguration类,代码如下

    • JdbcConfiguration

      • 配置数据源对应的bean
      • 配置事务管理器
      package at.guigu.config;
      
      import com.alibaba.druid.pool.DruidDataSource;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.PropertySource;
      import org.springframework.jdbc.datasource.DataSourceTransactionManager;
      import org.springframework.transaction.PlatformTransactionManager;
      
      import javax.sql.DataSource;
      
      // 分配置文件对应的类不用配置@Configuration以及@ComponentScan注解
      // 加载properties配置文件<context:property-placeholder location="classpath:jdbc.properties"/>
      @PropertySource("classpath:jdbc.properties")
      public class JdbcConfiguration {
          @Value("${jdbc.driverClassName}")
          private String driverClassName;
          @Value("${jdbc.url}")
          private String url;
          @Value("${jdbc.username}")
          private String username;
          @Value("${jdbc.password}")
          private String password;
      
          /**
           * Druid对应的bean
           * Spring会将当前方法的返回值以指定的id存储到Spring的IOC容器中
           * @return
           * @throws Exception
           */
          @Bean("dataSourceDruid")
          public DataSource getDruidDataSource() throws Exception{
              // 创建数据源对象
              DruidDataSource dataSource = new DruidDataSource();
              // 设置数据源基本连接数据
              dataSource.setDriverClassName(driverClassName);
              dataSource.setUrl(url);
              dataSource.setUsername(username);
              dataSource.setPassword(password);
              return dataSource;
          }
      
          // 事务管理器配置
          @Bean
          public PlatformTransactionManager transactionManager(@Qualifier("dataSourceDruid") DataSource dataSource) {
              DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
              transactionManager.setDataSource(dataSource);
              return transactionManager;
          }
      }
      

      注意:

      • 在以上代码示例中,transactionManager方法通过参数进行bean的依赖注入时,加上了@Qualifier("dataSourceDruid")注解是为了让其精确匹配,此处可以不加,因为此时IOC容器中只有一个DataSource对应的bean,系统会自动去IOC容器中判断是否存在DataSource对应的bean,若有则直接将其作为参数注入。当IOC容器中不只有一个DataSource对应的bean时,此时就需要@Qualifier注解来指明参数注入的是哪个数据源bean
      • @bean注解未显式指定数据源名称时,其在IOC容器中的唯一标识为方法名,即getDruidDataSource
      • 事务管理器配置中注入的数据源bean必须与MyBatisConfiguration配置类中注入的数据源bean是同一一个
    • MyBatisConfiguration

      package at.guigu.config;
      
      import org.mybatis.spring.SqlSessionFactoryBean;
      import org.mybatis.spring.mapper.MapperScannerConfigurer;
      import org.springframework.context.annotation.Bean;
      
      import javax.sql.DataSource;
      
      public class MyBatisConfiguration {
      
          @Bean
          public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
              SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
              // 相当于设置别名<package name="at.guigu.pojo"/>
              sqlSessionFactoryBean.setTypeAliasesPackage("at.guigu.pojo");
              // 相当于配置数据库连接信息
              sqlSessionFactoryBean.setDataSource(dataSource);
              return sqlSessionFactoryBean;
          }
          // 映射扫描配置类,相当于引入dao包下所有接口对应的SQL映射文件<package name="at.guigu.dao"/>
          @Bean
          public MapperScannerConfigurer mapperScannerConfigurer() {
              MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
              mapperScannerConfigurer.setBasePackage("at.guigu.dao");
              return mapperScannerConfigurer;
          }
      }
      

      注意:MyBatis核心配置类形式设置别名失效,暂未知原因,所以可使用注解形式来给pojo类中的类设置别名

    • SpringConfiguration

      • 配置@Configuration注解代表该类是Spring的核心配置类
      • 利用@ComponentScan注解配置组件扫描
      • 利用@Import注解引入拆分配置文件
      • 利用@EnableAspectJAutoProxy注解启用Spring基于AspectJ 注解驱动的AOP功能
      • 利用@EnableTransactionManagement注解开启注解式事务驱动

      注意:

      利用@EnableTransactionManagement注解开启注解式事务驱动后不需要添加@EnableAspectJAutoProxy注解,只有当使用更多AspectJ特性(例如一些自定义的切面或更多AOP功能)时,才需要显式加上@EnableAspectJAutoProxy 此处博主示例加上了该注解,可自行选择是否添加

      package at.guigu.config;
      
      import org.mybatis.spring.annotation.MapperScan;
      import org.springframework.context.annotation.ComponentScan;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.FilterType;
      import org.springframework.context.annotation.Import;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RestController;
      
      // 该注解代表该类是Spring的核心配置类
      @Configuration
      // 配置注解的组件扫描<context:component-scan base-package="at.guigu.dao, at.guigu.service"></context:component-scan>
      /*@ComponentScan(
              basePackages = "at.guigu",
              excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = RestController.class)
      )
      该方法运行时会报错,所以只能使用精确扫描
      */
      @ComponentScan(basePackages = {"at.guigu.dao", "at.guigu.service"})
      //@MapperScan("at.guigu.dao")
      // 引入拆分配置文件<import resource="applicationContext-xxx.xml"/>
      @Import({JdbcConfiguration.class, MyBatisConfiguration.class})
      @EnableAspectJAutoProxy
      // 开启注解式事务驱动
      @EnableTransactionManagement
      public class SpringConfiguration {
      }
      

      注意:由于后期controller包下使用的是@RestController注解,所以必须通过@ComponentScan(basePackages = {"at.guigu.dao", "at.guigu.service"})来设置Spring的精确扫描范围,否则会报错

  • Step6: 在config包下创建配置静态资源的路径映射类,即继承WebMvcConfigurationSupport类的子类SpringMvcSupport,代码如下:

    package at.guigu.config;
    
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    
    // 配置静态资源的路径映射
    @Configuration
    public class SpringMvcSupport extends WebMvcConfigurationSupport {
        @Override
        protected void addResourceHandlers (ResourceHandlerRegistry registry) {
            // 当访问/pages/????时候,从/pages目录下查找内容
            // 配置静态资源的路径映射,开放某些资源的访问
            // 等同于<mvc:resources mapping="/xxx/**" location="/xx/"/>
            registry.addResourceHandler("/xxx/**").addResourceLocations("/xxx/");
            // 等同于<mvc:resources mapping="/xxx/**" location="/xxx/"/>
            registry.addResourceHandler("/xxx/**").addResourceLocations("/xxx/");
        }
    }
    

    注意:后期自行进行路径映射更改

  • Step7: 在config包下创建SpringMVC的核心配置类,并在该类中配置视图解析器以及文件上传解析器,代码如下

    package at.guigu.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.web.multipart.commons.CommonsMultipartResolver;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.view.InternalResourceViewResolver;
    
    // 该注解代表该类是SpringMVC的核心配置类
    @Configuration
    // 配置注解的组件扫描<context:component-scan base-package="at.guigu.controller"></context:component-scan>
    // 加载controller对应的bean
    @ComponentScan({"at.guigu.controller", "at.guigu.config"})
    // 自动配置 Spring MVC 的各种特性,比如:类型转换器、mvc的注解驱动<mvc:annotation-driven/>
    @EnableWebMvc
    // 引入配置静态资源类
    @Import(SpringMvcSupport.class)
    public class SpringMvcConfiguration {
        // 配置视图解析器
        @Bean
        public InternalResourceViewResolver viewResolver() {
            InternalResourceViewResolver resolver = new InternalResourceViewResolver();
            resolver.setPrefix("/user/");  // 设置视图文件路径前缀
            resolver.setSuffix(".jsp");   // 设置视图文件后缀
            return resolver;
        }
    
        // 配置文件上传解析器
        @Bean
        public CommonsMultipartResolver multipartResolver() {
            CommonsMultipartResolver resolver = new CommonsMultipartResolver();
            resolver.setDefaultEncoding("UTF-8"); // 所上传文件的编码类型
            resolver.setMaxUploadSizePerFile(500000);// 所上传的单个文件的大小
            resolver.setMaxUploadSize(5000000);// 所上传的总文件的大小
            return resolver;
        }
    }
    

    注意:后期可自行更改配置视图解析器和文件上传解析器中的参数设置

  • Step8: 在config包下创建代替web.xml配置文件对应的配置类,即继承AbstractAnnotationConfigDispatcherServletInitializer类的子类ServletConfiguration,并重写其中的三个方法,同时进行POST请求的乱码处理,完整代码如下:

    package at.guigu.config;
    
    import org.springframework.web.filter.CharacterEncodingFilter;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    import javax.servlet.Filter;
    
    public class ServletConfiguration extends AbstractAnnotationConfigDispatcherServletInitializer {
        // 加载SpringMVC的配置
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class[]{SpringMvcConfiguration.class};
        }
        // 设置哪些请求归SpringMVC处理
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
        // 加载非SringMVC的配置,比如:加载Spring的配置
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class[]{SpringConfiguration.class};
        }
    
        // POST请求乱码处理:指定字符过滤器
        @Override
        protected Filter[] getServletFilters() {
            CharacterEncodingFilter filter = new CharacterEncodingFilter();
            filter.setEncoding("UTF-8");
            return new Filter[]{filter};
        }
    }
    
    /* 等同于
    // 定义一个Servlet容器启动的配置类,来加载Spring的配置
    public class ServletConfiguration extends AbstractDispatcherServletInitializer {
        // 加载SpringMVC的配置
        @Override
        protected WebApplicationContext createServletApplicationContext() {
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.register(SpringMvcConfiguration.class);
            return context;
        }
    
        // 设置哪些请求归SpringMVC处理
        @Override
        protected String[] getServletMappings() {
            // 代表将所有请求都交给前端控制器处理
            return new String[]{"/"};
        }
    
        // 加载非SringMVC的配置,比如:加载Spring的配置
        @Override
        protected WebApplicationContext createRootApplicationContext() {
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.register(SpringConfiguration.class);
            return context;
        }
        // POST请求乱码处理:指定字符过滤器
        @Override
        protected Filter[] getServletFilters() {
            CharacterEncodingFilter filter = new CharacterEncodingFilter();
            filter.setEncoding("UTF-8");
            return new Filter[]{filter};
        }
    }*/
    
  • **Step9: ** 在pojo包下创建Book类,代码如下:

    注意:要加上@Alias注解来设置别名,因为MyBatis核心配置类中设置的别名无效

    package at.guigu.pojo;
    
    import org.apache.ibatis.type.Alias;
    
    @Alias("book")
    public class Book {
        private Integer id;
        private String type;
        private String name;
        private String description;
        public Book() {}
        public Book(Integer id, String type, String name, String description) {
            this.id = id;
            this.type = type;
            this.name = name;
            this.description = description;
        }
    
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getType() {
            return type;
        }
        public void setType(String type) {
            this.type = type;
        }
    
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDescription() {
            return description;
        }
        public void setDescription(String description) {
            this.description = description;
        }
    
        @Override
        public String toString() {
            return "Book{" +
                    "id=" + id +
                    ", type='" + type + '\'' +
                    ", name='" + name + '\'' +
                    ", description='" + description + '\'' +
                    '}';
        }
    }
    
  • Step10: 在dao包下创建BookDao接口,在service包下创建BookService接口并在service包下的impl包下创建BookServiceImpl实现类,初始代码如下

    • BookDao接口

      package at.guigu.dao;
      
      import org.apache.ibatis.annotations.Mapper;
      
      //@Mapper
      public interface BookDao {
      }
      
    • BookService接口

      package at.guigu.service;
      
      public interface BookService {
      }
      
    • BookService接口的实现类BookServiceImpl

      package at.guigu.service.impl;
      
      import at.guigu.dao.BookDao;
      import at.guigu.service.BookService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      
      @Service
      public class BookServiceImpl implements BookService {
          @Autowired
          private BookDao bookDao;
      }
      
  • Step11: 在源代码配置文件目录(即资源文件resources)下的三层文件夹at/guigu/dao下创建BookDao接口的映射文件BookDao.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">
    
    <!--namespace:名称空间-->
    <mapper namespace="at.guigu.dao.BookDao">
        <!--结果映射-->
        <resultMap id="brandResultMap" type="book">
            <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可-->
            <result column="type" property="type"/>
            <result column="name" property="name"/>
            <result column="description" property="description"/>
        </resultMap>
        
    </mapper>
    
  • Step12: 在controller包下创建BookController类,代码如下

    package at.guigu.controller;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    // @RestController = @Controller+ @ResponseBody
    @RestController
    // Restful风格
    @RequestMapping(value = "/books")
    public class BookController {
    }
    

功能模块开发

本功能模块主要有三个功能:查询、添加/保存、更新/修改、删除

  • Step1: BookDao接口中代码如下:

    注意:对于简单SQL语句采用注解方式书写,对于复杂SQL语句采用映射文件书写

    package at.guigu.dao;
    
    import at.guigu.pojo.Book;
    import org.apache.ibatis.annotations.*;
    import java.util.List;
    import java.util.Map;
    
    //@Mapper
    public interface BookDao {
        /*-----------------------查询数据-------------------------*/
        // 查询所有数据
        @Select("select * from tbl_book")
        public List<Book> getAll();
        // 查询单条数据:通过id查询
        @Select("select * from tbl_book where id = #{id}")
        public Book getById(Integer id);
    
        // 静态单条件查询
        List<Book> selectBySingleConOne(Integer id);
        // 动态单条件查询——对象参数接收
        List<Book> selectBySingleConTwo(Book book);
        // 动态单条件查询——Map集合参数接收
        List<Book> selectBySingleConTwoo(Map map);
        // 动态多条件查询——对象参数接收
        List<Book> selectByMaxConOne(Book book);
        // 动态多条件查询——Map集合参数接收
        List<Book> selectByMaxConTwo(Map map);
    
        /*-----------------------添加/保存数据-------------------------*/
        //@Insert("insert into tbl_book values(null,#{type},#{name},#{description})")
        @Insert("insert into tbl_book (type,name,description) values(#{type},#{name},#{description})")
        public int save(Book book);  //返回值表示影响的行数
    
        /*-----------------------更新/修改数据-------------------------*/
        @Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}")
        public int update(Book book);
    
        /*-----------------------删除数据-------------------------*/
        // 单条删除
        @Delete("delete from tbl_book where id = #{id}")
        public int deleteById(Integer id);
        // 批量删除
        int deleteByIds(Integer[] ids);
    }
    
  • Step2: SQL映射文件BookDao.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">
    
    <!--namespace:名称空间-->
    <mapper namespace="at.guigu.dao.BookDao">
        <!--结果映射-->
        <resultMap id="bookResultMap" type="book">
            <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可-->
            <result column="type" property="type"/>
            <result column="name" property="name"/>
            <result column="description" property="description"/>
        </resultMap>
    
        <!--======================================查询数据===================================-->
    
        <!--静态单条件查询  parameterType属性值为传入的参数类型-->
        <select id="selectBySingleConOne" parameterType="int" resultMap="bookResultMap">
            # 此处根据情况选择使用转义字符还是CDATA区
            select * from tbl_book where id <![CDATA[
            >
            ]]>
            #{id};
        </select>
        <!--动态单条件查询——对象参数接收-->
        <select id="selectBySingleConTwo" resultMap="bookResultMap">
            select * from tbl_book
            <where>
                <choose> <!--类似于switch-->
                    <when test="type != null">
                        type = #{type}
                    </when>
                    <when test="name != null and name != ''">
                        name like #{name}
                    </when>
                    <when test="description != null and description != ''">
                        description like #{description}
                    </when>
                </choose>
            </where>
        </select>
        <!--动态单条件查询——Map集合参数接收-->
        <select id="selectBySingleConTwoo" resultMap="bookResultMap">
            select * from tbl_book
            <where>
                <choose> <!--类似于switch-->
                    <when test="type != null">
                        type = #{type}
                    </when>
                    <when test="name != null and name != ''">
                        name like #{name}
                    </when>
                    <when test="description != null and description != ''">
                        description like #{description}
                    </when>
                </choose>
            </where>
        </select>
    
        <!--动态多条件查询——对象参数接收-->
        <select id="selectByMaxConOne" resultMap="bookResultMap">
            select * from tbl_book
            <where>
                <if test="type != null">
                    and type = #{type}
                </if>
                <if test="name != null and name != ''">
                    and name like #{name}
                </if>
                <if test="description != null and description != ''">
                    and description like #{description}
                </if>
            </where>
        </select>
        <!--动态多条件查询——Map集合参数接收-->
        <select id="selectByMaxConTwo" resultMap="bookResultMap">
            select * from tbl_book
            <where>
                <if test="type != null">
                    and type = #{type}
                </if>
                <if test="name != null and name != ''">
                    and name like #{name}
                </if>
                <if test="description != null and description != ''">
                    and description like #{description}
                </if>
            </where>
        </select>
        <!--======================================删除数据===================================-->
        <!--批量删除-->
        <delete id="deleteByIds">
            delete from tbl_book where id in
            <foreach collection="array" item="id" separator="," open="(" close=")">
                #{id}
            </foreach>
        </delete>
    </mapper>
    
  • Step3: BookService接口中代码如下

    • 加上@Transactional注解(该注解可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务;通常添加在业务层接口中而不会添加到业务层实现类中,以此来降低耦合)
      • 此处为方便加在接口上

    注意:增删改操作要返回Boolean值告知前端是否执行成功

    package at.guigu.service;
    
    import at.guigu.pojo.Book;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.List;
    import java.util.Map;
    
    @Transactional
    public interface BookService {
        /*-----------------------查询数据-------------------------*/
        // 查询所有数据
        public List<Book> getAll();
        // 查询单条数据:通过id查询
        public Book getById(Integer id);
        // 静态单条件查询
        public List<Book> selectBySingleConOne(Integer id);
        // 动态单条件查询——对象参数接收
        public List<Book> selectBySingleConTwo(Book book);
        // 动态单条件查询——Map集合参数接收
        public List<Book> selectBySingleConTwoo(Map map);
        // 动态多条件查询——对象参数接收
        public List<Book> selectByMaxConOne(Book book);
        // 动态多条件查询——Map集合参数接收
        public List<Book> selectByMaxConTwo(Map map);
    
        /*-----------------------添加/保存数据---------------------*/
        public boolean save(Book book);
    
        /*-----------------------更新/修改数据---------------------*/
        public boolean update(Book book);
    
        /*-----------------------删除数据-------------------------*/
        // 单条删除
        public boolean deleteById(Integer id);
        // 批量删除
        public boolean deleteByIds(Integer[] ids);
    }
    
  • Step4: BookService接口的实现类BookServiceImpl代码如下

    package at.guigu.service.impl;
    
    import at.guigu.dao.BookDao;
    import at.guigu.pojo.Book;
    import at.guigu.service.BookService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    import java.util.Map;
    
    @Service
    public class BookServiceImpl implements BookService {
        @Autowired
        private BookDao bookDao;
    
        /*-----------------------查询数据-------------------------*/
        // 查询所有数据
        @Override
        public List<Book> getAll() {
            return bookDao.getAll();
        }
        // 查询单条数据:通过id查询
        @Override
        public Book getById(Integer id) {
            return bookDao.getById(id);
        }
        // 静态单条件查询
        @Override
        public List<Book> selectBySingleConOne(Integer id) {
            return bookDao.selectBySingleConOne(id);
        }
        // 动态单条件查询——对象参数接收
        @Override
        public List<Book> selectBySingleConTwo(Book book) {
            return bookDao.selectBySingleConTwo(book);
        }
        // 动态单条件查询——Map集合参数接收
        @Override
        public List<Book> selectBySingleConTwoo(Map map) {
            return bookDao.selectBySingleConTwoo(map);
        }
        // 动态多条件查询——对象参数接收
        @Override
        public List<Book> selectByMaxConOne(Book book) {
            return bookDao.selectByMaxConOne(book);
        }
        // 动态多条件查询——Map集合参数接收
        @Override
        public List<Book> selectByMaxConTwo(Map map) {
            return bookDao.selectByMaxConTwo(map);
        }
    
        /*-----------------------添加/保存数据---------------------*/
       @Override
        public boolean save(Book book) {
            return bookDao.save(book) > 0;
        }
    
        /*-----------------------更新/修改数据---------------------*/
        @Override
        public boolean update(Book book) {
            return bookDao.update(book) > 0;
        }
    
        /*-----------------------删除数据-------------------------*/
        // 单条删除
        @Override
        public boolean deleteById(Integer id) {
            return bookDao.deleteById(id) > 0;
        }
        // 批量删除
        @Override
        public boolean deleteByIds(Integer[] ids) {
            return bookDao.deleteByIds(ids) > 0;
        }
    }
    
  • Step5: BookController类的代码如下

    package at.guigu.controller;
    
    import at.guigu.pojo.Book;
    import at.guigu.service.BookService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    
    // @RestController = @Controller+ @ResponseBody
    @RestController
    // Restful风格
    @RequestMapping(value = "/books")
    public class BookController {
        @Autowired
        private BookService bookService;
    
        /*-----------------------查询数据-------------------------*/
        // 查询所有数据
        @GetMapping
        public List<Book> getAll() {
            return bookService.getAll();
        }
        // 查询单条数据:通过id查询
        @GetMapping("/{id}")
        public Book getById(@PathVariable(value = "id") Integer id) {
            return bookService.getById(id);
        }
        // 静态单条件查询
        @GetMapping("/singleConOne/{id}")
        public List<Book> selectBySingleConOne(@PathVariable(value = "id") Integer id) {
            return bookService.selectBySingleConOne(id);
        }
        // 动态单条件查询——对象参数接收
        @GetMapping("/singleConTwo")
        public List<Book> selectBySingleConTwo(@RequestParam("type") String type, @RequestParam("name") String name, @RequestParam("description") String description) {
            Book book = new Book();
            book.setType(type);
            book.setName(name);
            book.setDescription(description);
            return bookService.selectBySingleConTwo(book);
        }
        // 动态单条件查询——Map集合参数接收
        @GetMapping("/singleConTwoo")
        public List<Book> selectBySingleConTwoo(@RequestParam("map") Map map) {
            return bookService.selectBySingleConTwoo(map);
        }
        // 动态多条件查询——对象参数接收
        @GetMapping("/maxConOne")
        public List<Book> selectByMaxConOne(@RequestParam("type") String type, @RequestParam("name") String name, @RequestParam("description") String description) {
            Book book = new Book();
            book.setType(type);
            book.setName(name);
            book.setDescription(description);
            return bookService.selectByMaxConOne(book);
        }
        // 动态多条件查询——Map集合参数接收
        @GetMapping("/maxConTwo")
        public List<Book> selectByMaxConTwo(@RequestParam("map") Map map) {
            return bookService.selectByMaxConTwo(map);
        }
        /*-----------------------添加/保存数据---------------------*/
        @PostMapping
        public boolean save(@RequestBody Book book) {
            bookService.save(book);
            return true;
        }
    
        /*-----------------------更新/修改数据---------------------*/
        @PutMapping
        public boolean update(@RequestBody Book book) {
            bookService.update(book);
            return true;
        }
    
        /*-----------------------删除数据-------------------------*/
        // 单条删除
        @DeleteMapping("/{id}")
        public boolean deleteById(@PathVariable(value = "id") Integer id) {
            bookService.deleteById(id);
            return true;
        }
        // 批量删除
        @DeleteMapping
        public boolean deleteByIds(@RequestParam Integer[] ids) {
            bookService.deleteByIds(ids);
            return true;
        }
    }
    

接口测试

注意:在功能模块写完之后,要做两个测试,分别是

​ 1.使用JUint来测试业务层接口

​ 2.使用POstMan测试表现层

使用JUint来测试业务层接口

  • Step1:在test包下创建三层架构包的业务层service包,并在该包中创建BookServiceTest测试类,完整代码如下

    • Step1-1: 该类要使用@Runwith注解替换原来的运行器,并设置新的类运行器
      • 其属性为SpringRunner.classSpringJUnit4ClassRunner.class:用于集成 Spring 测试框架
    • Step1-2: 该类使用@ContextConfiguration指定Spring配置文件或Spring配置类
      • 指定Spring配置文件:@ContextConfiguration("classpath:applicationContext.xml")
      • 指定单个Spring配置类:@ContextConfiguration(classes = SpringConfiguration.class)
      • 指定多个Spring配置类:@ContextConfiguration(classes = {SpringConfiguration.class,...})
    • Step1-3: 在该类中使用@Autowired注入需要测试的对象
    • Step1-4: 在该类中创建对应测试类测试对应方法
    package at.guigu.service;
    
    import at.guigu.config.SpringConfiguration;
    import at.guigu.pojo.Book;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import java.util.List;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {SpringConfiguration.class})
    public class BookServiceTest {
        @Autowired
        private BookService bookService;
        
        @Test
        public void testGetAll( ) {
            List<Book> brands = bookService.getAll();
            for (Book book : brands) {
                System.out.println(book);
            }
        }
        
    }
    

    运行结果如下

    在这里插入图片描述

使用POstMan测试表现层

Tomcat运行该项目后,运行截图如下

  • 查询数据

    • 查询单条数据

      在这里插入图片描述

  • 添加/保存数据

    在这里插入图片描述

  • 修改/更新数据

    在这里插入图片描述

  • 删除数据

    • 删除单条数据

      在这里插入图片描述

表现层数据封装

  • 表现层数据响应问题

    • 表现层增删改方法返回true或者false表示是否成功,getById()方法返回一个json对象,getAll()方法返回一个json对象数组,这里就出现了三种格式的响应结果,极其不利于前端解析

      在这里插入图片描述

  • 所以后端需要统一响应结果的格式, 即创建一个结果模型类,封装数据到data属性中,将操作结果封装到code属性中,将特殊消息封装到message(msg)属性中

    • 此时若查询成功则前端将data属性的数据显示即可;若查询失败则前端将message属性中的消息展示给用户即可

    在这里插入图片描述

  • Step1: 在controller包下创建Result类来封装响应结果,代码如下

    • 注意
      • Result类中的字段并不是固定的,可以根据需要自行增减
      • 此处提供了若干构造方法,可根据需求使用,方便操作
    package at.guigu.controller;
    
    public class Result {
        private Integer code;
        private Object data;
        private String message;
        public Result(){}
        public Result(Integer code, Object data){
            this.code = code;
            this.data = data;
        }
        public Result(Integer code, Object data, String message) {
            this.code = code;
            this.data = data;
            this.message = message;
        }
    
        public Integer getCode() {
            return code;
        }
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public Object getData() {
            return data;
        }
        public void setData(Object data) {
            this.data = data;
        }
    
        public String getMessage() {
            return message;
        }
        public void setMessage(String message) {
            this.message = message;
        }
    
        @Override
        public String toString() {
            return "Result{" +
                    "code=" + code +
                    ", data=" + data +
                    ", message='" + message + '\'' +
                    '}';
        }
    }
    
  • Step2: 在controller包下创建Code类,来封装自定义的响应码,代码如下

    package at.guigu.controller;
    
    public class Code {
        // 1结尾代表成功
        public static final Integer SAVE_OK = 20011;
        public static final Integer DELETE_OK = 20021;
        public static final Integer UPDATE_OK = 20031;
        public static final Integer SELETE_OK = 20041;
        // 0结尾代表失败
        public static final Integer SAVE_ERR = 20010;
        public static final Integer DELETE_ERR = 20020;
        public static final Integer UPDATE_ERR = 20030;
        public static final Integer SELETE_ERR = 20040;
    }
    
  • Step3: 将controller包下的BookController类中的所有方法的返回结果均变为Result对象,并根据执行情况返回对应的响应码值,代码如下

    package at.guigu.controller;
    
    import at.guigu.pojo.Book;
    import at.guigu.service.BookService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    import java.util.List;
    import java.util.Map;
    
    // @RestController = @Controller+ @ResponseBody
    @RestController
    // Restful风格
    @RequestMapping(value = "/books")
    public class BookController {
        @Autowired
        private BookService bookService;
    
        /*-----------------------查询数据-------------------------*/
        // 查询所有数据
        @GetMapping
        public Result getAll() {
            List<Book> books = bookService.getAll();
            Integer code = books != null ? Code.SELETE_OK : Code.SELETE_ERR;
            String message = books != null ? "" : "数据查询失败,请重试";
            return new Result(code, books, message);
        }
        // 查询单条数据:通过id查询
        @GetMapping("/{id}")
        public Result getById(@PathVariable(value = "id") Integer id) {
            Book book = bookService.getById(id);
            Integer code = book != null ? Code.SELETE_OK : Code.SELETE_ERR;
            String message = book != null ? "" : "数据查询失败,请重试";
            return new Result(code, book, message);
        }
        // 静态单条件查询
        @GetMapping("/singleConOne/{id}")
        public Result selectBySingleConOne(@PathVariable(value = "id") Integer id) {
            List<Book> books = bookService.selectBySingleConOne(id);
            Integer code = books != null ? Code.SELETE_OK : Code.SELETE_ERR;
            String message = books != null ? "" : "数据查询失败,请重试";
            return new Result(code, books, message);
        }
        // 动态单条件查询——对象参数接收
        @GetMapping("/singleConTwo")
        public Result selectBySingleConTwo(@RequestParam("type") String type, @RequestParam("name") String name, @RequestParam("description") String description) {
            Book book = new Book();
            book.setType(type);
            book.setName(name);
            book.setDescription(description);
            List<Book> books = bookService.selectBySingleConTwo(book);
            Integer code = books != null ? Code.SELETE_OK : Code.SELETE_ERR;
            String message = books != null ? "" : "数据查询失败,请重试";
            return new Result(code, books, message);
        }
        // 动态单条件查询——Map集合参数接收
        @GetMapping("/singleConTwoo")
        public Result selectBySingleConTwoo(@RequestParam("map") Map map) {
            List<Book> books = bookService.selectBySingleConTwoo(map);
            Integer code = books != null ? Code.SELETE_OK : Code.SELETE_ERR;
            String message = books != null ? "" : "数据查询失败,请重试";
            return new Result(code, books, message);
        }
        // 动态多条件查询——对象参数接收
        @GetMapping("/maxConOne")
        public Result selectByMaxConOne(@RequestParam(value = "type", required = false) String type, @RequestParam(value = "name", required = false) String name, @RequestParam(value = "description", required = false) String description) {
            Book book = new Book();
            book.setType(type);
            book.setName(name);
            book.setDescription(description);
            List<Book> books = bookService.selectByMaxConOne(book);
            Integer code = books != null ? Code.SELETE_OK : Code.SELETE_ERR;
            String message = books != null ? "" : "数据查询失败,请重试";
            return new Result(code, books, message);
        }
        // 动态多条件查询——Map集合参数接收
        @GetMapping("/maxConTwo")
        public Result selectByMaxConTwo(@RequestParam("map") Map map) {
            List<Book> books = bookService.selectByMaxConTwo(map);
            Integer code = books != null ? Code.SELETE_OK : Code.SELETE_ERR;
            String message = books != null ? "" : "数据查询失败,请重试";
            return new Result(code, books, message);
        }
        /*-----------------------添加/保存数据-------------------------*/
        @PostMapping
        public Result save(@RequestBody Book book) {
            boolean flag = bookService.save(book);
            return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERR, flag);
        }
    
        /*-----------------------更新/修改数据-------------------------*/
        @PutMapping
        public Result update(@RequestBody Book book) {
            boolean flag = bookService.update(book);
            return new Result(flag ? Code.UPDATE_OK:Code.UPDATE_ERR, flag);
        }
    
        /*-----------------------删除数据-------------------------*/
        // 单条删除
        @DeleteMapping("/{id}")
        public Result deleteById(@PathVariable(value = "id") Integer id) {
            boolean flag = bookService.deleteById(id);
            return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERR, flag);
        }
        // 批量删除
        @DeleteMapping
        public Result deleteByIds(@RequestParam("ids") Integer[] ids) {
            boolean flag = bookService.deleteByIds(ids);
            return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERR, flag);
        }
    }
    
  • 运行截图如下

    • 查询存在的数据

      在这里插入图片描述

    • 查询不存在的数据

      在这里插入图片描述

异常处理

  • Step1: 创建一个与三层架构包同级的exception包,并在该包下创建继承RuntimeException的子类SystemException自定义一个系统异常 ,代码如下:

    • 设置一个code私有属性来接收异常对应的编号
    • 添加构造器(注意:RuntimeException有5个构造器,可根据需要选用一个或多个)
    • 添加getset方法
    package at.guigu.exception;
    
    public class SystemException extends RuntimeException {
        // 给异常添加编号
        private Integer code;
    
        // 添加构造方法
        public SystemException(Integer code, String message) {
            super(message);
            this.code = code;
        }
        public SystemException(Integer code, String message, Throwable cause) {
            super(message, cause);
            this.code = code;
        }
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    }
    
  • Step2: 按照第一步在创建一个BusinessException类来自定义一个业务异常 ,代码如下:

    package at.guigu.exception;
    
    public class BusinessException extends RuntimeException {
        // 给异常添加编号
        private Integer code;
    
        // 添加构造方法
        public BusinessException(Integer code, String message) {
            super(message);
            this.code = code;
        }
        public BusinessException(Integer code, String message, Throwable cause) {
            super(message, cause);
            this.code = code;
        }
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    }
    
  • Step3:Code类中封装自定义异常的响应码,代码如下

    package at.guigu.controller;
    
    /**
     * 封装自定义的响应码
     */
    public class Code {
        /*增删改查*/
        // 1结尾代表成功
        public static final Integer SAVE_OK = 20011;
        public static final Integer DELETE_OK = 20021;
        public static final Integer UPDATE_OK = 20031;
        public static final Integer SELETE_OK = 20041;
        // 0结尾代表失败
        public static final Integer SAVE_ERR = 20010;
        public static final Integer DELETE_ERR = 20020;
        public static final Integer UPDATE_ERR = 20030;
        public static final Integer SELETE_ERR = 20040;
    
        /*异常*/
        // 系统异常
        public static final Integer SYST_EXC = 20040;
        // 业务异常
        public static final Integer BUSI_EXC = 20040;
        // 其它未知异常
        public static final Integer UNKNOW_EXC = 20040;
    }
    
  • Step4: 将业务层中可能出现的异常进行拦截包装,然年将其转换成自定义的异常,代码如下:

    • 若是系统异常,则需要将产生的异常也传到自定义异常中,最终由开发人员处理
    • 若是业务异常,则不需要将产生的异常传到自定义异常中,只需要告知用户规范输入即可
    • 若是其它异常则告知用户系统繁忙请稍后在试(也就是找个借口修复完在让用户使用)
    • 此代码中进行了简单的异常模拟,在实际项目开发中可自行在可能出现异常的地方进行异常处理
    package at.guigu.service.impl;
    
    import at.guigu.controller.Code;
    import at.guigu.dao.BookDao;
    import at.guigu.exception.BusinessException;
    import at.guigu.exception.SystemException;
    import at.guigu.pojo.Book;
    import at.guigu.service.BookService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    import java.util.Map;
    
    
    @Service
    public class BookServiceImpl implements BookService {
        @Autowired
        private BookDao bookDao;
    
        /*-----------------------查询数据-------------------------*/
        // 查询所有数据
        @Override
        public List<Book> getAll() {
            // 异常拦截包装并将其转换成自定义的异常
            // 此处假设为系统异常,所以需要将异常e也传出去
            try {
                int i = 1/0;
            } catch (Exception e) {
                throw new SystemException(Code.SYST_EXC, "模拟系统异常", e);
            }
            return bookDao.getAll();
        }
        // 查询单条数据:通过id查询
        @Override
        public Book getById(Integer id) {
            // 异常拦截包装并将其转换成自定义的异常
            // 此处假设为业务异常,所以不需要将异常传出
            if (id == 0) {
                throw new BusinessException(Code.BUSI_EXC, "模拟业务异常");
            }
            return bookDao.getById(id);
        }
        // 静态单条件查询
        @Override
        public List<Book> selectBySingleConOne(Integer id) {
            return bookDao.selectBySingleConOne(id);
        }
        // 动态单条件查询——对象参数接收
        @Override
        public List<Book> selectBySingleConTwo(Book book) {
            return bookDao.selectBySingleConTwo(book);
        }
        // 动态单条件查询——Map集合参数接收
        @Override
        public List<Book> selectBySingleConTwoo(Map map) {
            return bookDao.selectBySingleConTwoo(map);
        }
        // 动态多条件查询——对象参数接收
        @Override
        public List<Book> selectByMaxConOne(Book book) {
            return bookDao.selectByMaxConOne(book);
        }
        // 动态多条件查询——Map集合参数接收
        @Override
        public List<Book> selectByMaxConTwo(Map map) {
            return bookDao.selectByMaxConTwo(map);
        }
    
        /*-----------------------添加/保存数据-------------------------*/
       @Override
        public boolean save(Book book) {
            return bookDao.save(book) > 0;
        }
    
        /*-----------------------更新/修改数据-------------------------*/
        @Override
        public boolean update(Book book) {
            return bookDao.update(book) > 0;
        }
    
        /*-----------------------删除数据-------------------------*/
        // 单条删除
        @Override
        public boolean deleteById(Integer id) {
            return bookDao.deleteById(id) > 0;
        }
        // 批量删除
        @Override
        public boolean deleteByIds(Integer[] ids) {
            return bookDao.deleteByIds(ids) > 0;
        }
    }
    
  • Step5: 创建一个与三层架构包同级的resolver包,并在该包下创建ProjectEcepAdvice类,代码如下

    • Step5-1: 添加@ControllerAdvice注解或@RestControllerAdvice注解
      • 若当前controller包下使用的是@Controller注解则为ProjectEcepAdvice类添加@ControllerAdvice注解
      • 若当前controller包下使用的是@RestController注解则为ProjectEcepAdvice类添加@RestControllerAdvice注解
    • Step5-2: 写一个处理异常的方法doException,并将拦截的异常作为参数传入
    • Step5-3: 给该方法添加一个@ExceptionHandler(value)注解,并设置value属性,以此来指定捕获的异常类型
      • 捕获所有类型异常:@ExceptionHandler(Exception.class)
      • 捕获指定类型异常:@ExceptionHandler({FileNotFoundException.class, IOException.class})
    package at.guigu.resolver;
    
    import at.guigu.controller.Code;
    import at.guigu.controller.Result;
    import at.guigu.exception.BusinessException;
    import at.guigu.exception.SystemException;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    
    @RestControllerAdvice
    public class ProjectEcepAdvice {
        // 捕获指定的自定义异常BusinessException进行处理
        @ExceptionHandler(BusinessException.class)
        public Result doBusinessException(BusinessException e){
            // 发送对应消息传递给用户,提醒规范操作
            return new Result(e.getCode(), null, e.getMessage());
        }
    
        // 捕获指定的自定义异常SystemException进行处理
        @ExceptionHandler(SystemException.class)
        public Result doSystemException(SystemException e){
            // 记录日志
            // 发送消息给运维
            // 发送对应消息传递给用户
            // 发送邮件以及异常发送给开发人员
            return new Result(e.getCode(), null, e.getMessage());
        }
    
        // 捕获指定的自定义异常BusinessException进行处理
        @ExceptionHandler(Exception.class)
        public Result doException(Exception e){
            // 记录日志
            // 发送消息给运维
            // 发送对应消息传递给用户
            // 发送邮件以及异常发送给开发人员
            return new Result(Code.UNKNOW_EXC, null, "系统繁忙请稍后在试");
        }
    }
    
  • Step6: 在SpringMVC的核心配置类中利用@ComponentScan注解扫描resolver包,代码如下

    注意:若ProjectEcepAdvice类在controller包下,则不需要该步

    package at.guigu.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.web.multipart.commons.CommonsMultipartResolver;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.view.InternalResourceViewResolver;
    
    // 该注解代表该类是SpringMVC的核心配置类
    @Configuration
    // 配置注解的组件扫描<context:component-scan base-package="at.guigu.controller"></context:component-scan>
    // 加载controller对应的bean
    @ComponentScan({"at.guigu.controller", "at.guigu.config", "at.guigu.resolver"})
    // 自动配置 Spring MVC 的各种特性,比如:类型转换器、mvc的注解驱动<mvc:annotation-driven/>
    @EnableWebMvc
    // 引入配置静态资源类
    @Import(SpringMvcSupport.class)
    public class SpringMvcConfiguration {
        // 配置视图解析器
        @Bean
        public InternalResourceViewResolver viewResolver() {
            InternalResourceViewResolver resolver = new InternalResourceViewResolver();
            resolver.setPrefix("/user/");  // 设置视图文件路径前缀
            resolver.setSuffix(".jsp");   // 设置视图文件后缀
            return resolver;
        }
    
        // 配置文件上传解析器
        @Bean
        public CommonsMultipartResolver multipartResolver() {
            CommonsMultipartResolver resolver = new CommonsMultipartResolver();
            resolver.setDefaultEncoding("UTF-8"); // 所上传文件的编码类型
            resolver.setMaxUploadSizePerFile(500000);// 所上传的单个文件的大小
            resolver.setMaxUploadSize(5000000);// 所上传的总文件的大小
            return resolver;
        }
    }
    
  • 运行截图如下

    • 自定义的系统异常SystemException

      在这里插入图片描述

    • 自定义的业务异常BusinessException

      在这里插入图片描述

    • 其它异常Exception

      在这里插入图片描述

前端资源联入

后续补充ing…

;