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> <!– maven插件版本 –> <version>3.13.0</version> <configuration> <!– Java版本 –> <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
)→New
→File
,创建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.class
或SpringJUnit4ClassRunner.class
:用于集成 Spring 测试框架
- 其属性为
- Step1-2: 该类使用
@ContextConfiguration
指定Spring配置文件或Spring配置类- 指定Spring配置文件:
@ContextConfiguration("classpath:applicationContext.xml")
- 指定单个Spring配置类:
@ContextConfiguration(classes = SpringConfiguration.class)
- 指定多个Spring配置类:
@ContextConfiguration(classes = {SpringConfiguration.class,...})
- 指定Spring配置文件:
- 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); } } }
运行结果如下
- Step1-1: 该类要使用
使用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个构造器,可根据需要选用一个或多个) - 添加
get
、set
方法
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
注解
- 若当前controller包下使用的是
- 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, "系统繁忙请稍后在试"); } }
- Step5-1: 添加
-
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…