SpringMVC学习笔记三:SSM整合
前后端协议整合
Spring+SpringMVC+MyBatis架构搭建
引入相关的jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
引入Tomcat插件
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
创建各层文件夹
config
文件夹用于放置配置类
dao
和daomain
用于放置数据层
各种接口和类controller
文件夹用于放置表现层
各种类service
用于放置业务层
各种类test
用于放置测试类
resource
用于放置一些配置文件
webapp
用于放置网页资源- 其他自定义类文件夹
配置Spring配置类
@Configuration //配置类注解
@ComponentScan({"service"}) //扫描注解服务层文件夹
@PropertySource({"classpath:jdbc.properties"}) //调用jdbc的配置文件
@Import({JdbcConfig.class,MyBatisConfig.class}) //导入jdbc配置类、MyBatispei
@EnableTransactionManagement //开启事务管理器
public class SpringConfig {
}
- jdbc配置文件:用于连接数据库
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_db
jdbc.username=root
jdbc.password=1234
- jdbc配置类
public class JdbcConfig {
//通过@Value将外部配置文件的值动态注入
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
//创建一个 dataSource的Bean
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
//创建一个事务平台管理的Bean
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ds = new DataSourceTransactionManager();
ds.setDataSource(dataSource);
return ds;
}
}
- 创建MyBatis配置类
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTypeAliasesPackage("domain");
return factoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("dao");
return msc;
}
}
配置SpringMVC配置类
@Configuration
@ComponentScan({"controller","config"})//扫描业务层,以及配置层(如果内部设有多个扫描要注意排除项是否被覆盖)
@EnableWebMvc //开启web资源转换
public class SpringMvcConfig {
}
配置servelt容器
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"}; //拦截一切发送的资源
}
}
- 如果想要一些静态资源不被拦截,创建配置类设置
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//如果拦截的请求为->localhost/pages/**,则直接调用web/pages/文件夹下的资源
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
表现层的数据层模型封装
- 增删改都返回统一的数据类型data
- 不同的功能和查询成功与否使用code
- 操作失败没有结果,返回信息使用message
//创建一个结果类,用于前端数据分析
public class Result{
private Object data;
private Integer code;
private String message;
//根据实际需求可以进行增删
}
//状态码枚举
```java
public class Code {
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 GET_OK= 20041;
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 GET_ERR = 20040;
public static final Integer RESULT_Exception = 20000;
}
//前端控制器类
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@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 delete(@PathVariable Integer id) {
boolean flag = bookService.delete(id);
return new Result(flag?Code.DELETE_OK:Code.DELETE_ERR,flag);
}
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
int i =1/0;
Book book = bookService.getById(id);
Integer code = book!=null? Code.GET_OK:Code.GET_ERR;
String message = book!=null? "":"查询失败";
return new Result(code,book,message);
}
@GetMapping
public Result getAll() {
List<Book> list = bookService.getAll();
Integer code = list!=null? Code.GET_OK:Code.GET_ERR;
String message = list!=null? "":"查询失败";
return new Result(code,list,message);
}
}
异常处理的封装
封装抛出异常的格式:
当程序出现问题的时候,抛出到前端显示的是一连串的错误输出,不利于格式整合
异常的处理应放在表现层:
因为异常会应为各种原因,在表现层,数据层,业务层产生各种异常,为了方面处理,统一将异常上抛,汇聚到表现层进行统一捕捉处理
使用AOP方式对异常进行处理:
在不改变源代码的情况,以及不能在不同方法中都在最初写入异常处理机制,而且会出现大量的重复代码,使用AOP方式可以较好的处理
异常处理器
//在表现层定义一个异常处理器
//配置异常处理器
@RestControllerAdvice
public class ProjectExceptionAdvice {
//想要捕获的异常类型
@ExceptionHandler(Exception.class)
public Result doException(Exception e){
System.out.println("捕获成功");
return new Result(Code.RESULT_Exception,null,"程序出现异常");
}
}
异常处理方案
项目异常分类
- 业务异常
- 规范的用户行为产生的异常
- 规范的用户行为产生的异常
- 系统异常
- 项目运行过程中可预计且无法避免的异常
- 其他异常
- 编程人员未预期到的异常
项目异常处理方案
- 业务异常
- 发送对应消息传递给用户,提醒规范操作
- 系统异常
- 发送规定消息传递给用户,安抚用户
- 发送特定消息给运维人员,提醒维护
- 记录日志
- 其他异常
- 发送规定消息传递给用户,安抚用户
- 发送特消息给编程人员,提醒维护
- 记录日志
异常处理机制
- 自定义项目系统级异常
- 自定义项目业务级异常
- 自定义异常编码
- 触发异常
- 拦截并处理异常
//继承运行时异常
public class BusinessException extends RuntimeException{
private Integer code;
public Integer getCode() {
return 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;
}
}
@RestControllerAdvice
public class ProjectExceptionAdvice {
//想要捕获的异常类型
@ExceptionHandler(BusinessException.class)
public Result doException(BusinessException e){
System.out.println("捕获成功");
return new Result(e.getCode(),null,e.getMessage());
}
@ExceptionHandler(SystemException.class)
public Result doException(SystemException e){
System.out.println("捕获成功");
return new Result(e.getCode(),null,e.getMessage());
}
}