文章目录
SpringMvc拦截器
概念与作用
**概念:**拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行。使用了AOP思想,拦截器性质上也是切面类。
作用:
- 在指定的方法调用前后执行预先设定的代码
- 阻止原始方法的执行
- 总结:增强
Interceptor与Filter区别:
- Fliter属于Servlet技术,对所有的访问进行增强或拦截。
- Interceptor属于SpringMvc,只对SpringMvc的访问进行处理。
定义拦截器
使用步骤:
搭建环境(导入servlet与SpringMvc依赖)
创建SpringMvc配置类/文件
@Configuration //同时扫描控制器和配置类所在的包 @ComponentScan({"com.lxl.controller","com.lxl.config"}) @EnableWebMvc //使用拦截器需要添加这个注解。不建议使用实现接口的配置方式,两种方式只能配置一种,否则有冲突 public class SpringMvcConfig { }
定义web容器初始化类
// Tomcat在启动时会创建这个类的对象,运行在Servlet之前 public class ServletConfigInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override // 加载Spring的配置类 protected Class<?>[] getRootConfigClasses() { return new Class[0]; } @Override // 加载SpringMVC的配置类 protected Class<?>[] getServletConfigInitializerClasses() { return new Class[]{SpringMvcConfig.class}; } @Override //配置接受SpringMVC管理的访问路径 protected String[] getServletMappings() { return new String[]{"/"}; } }
创建控制器、定义拦截器
@RestController @RequestMapping("/book") public class BookController { @RequestMapping("/find") public String find() { System.out.println("查找书籍"); return "find success"; } } public class BookInterceptor implements HandlerInterceptor { //定义拦截器类,实现HandlerInterceptor接口 @Override //原始方法调用前执行的内容 //返回值类型可以拦截控制的执行,true放行,false终止 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("前置通知"); return true; } @Override //原始方法调用后执行的内容 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("后置通知"); } @Override //原始方法调用完成后执行的内容 //无论处理器方法内部是否出现异常,该方法都会执行。 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("最终通知"); } }
配置加载拦截器
@Configuration //SpringMvc副配置类 public class SpringMvcSupport extends WebMvcConfigurationSupport { @Override protected void addInterceptors(InterceptorRegistry registry) { //注解拦截器和拦截地址 registry.addInterceptor(new BookInterceptor()).addPathPatterns("/book/*"); //*代表只拦截一层,**代表拦截任意层 } }
拦截执行流程
拦截器方法参数
- HttpServletRequest request 请求参数
- HttpServletResponse response 响应参数
- Object handler 被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装,可以转为HandlerMethod类型
- modelAndView 如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行跳转
- Exception ex 如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
拦截器链规则
//配置第二个拦截器
public class BookInterceptor2 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器2:前置通知");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器2:后置通知");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器2:最终通知");
}
}
//配置第二个拦截器
@Configuration
//同时扫描控制器和配置类所在的包
@ComponentScan({"com.itheima.controller","com.itheima.config"})
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(
new BookInterceptor()
).addPathPatterns("/book/*");
registry.addInterceptor(
new BookInterceptor2()
).addPathPatterns("/book/*").excludePathPatterns("/book/to/*");
//可以使用excludePathPatterns()方法排除某些地址不被拦截
}
}
/*
* 执行结果:
* 拦截器1:前置通知
* 拦截器2:前置通知
* 查找书籍
* 拦截器2:后置通知
* 拦截器1:后置通知
* 拦截器2:最终通知
* 拦截器1:最终通知
*/
拦截流程分析:
- 当配置多个拦截器时,形成拦截器链
- 拦截器链的运行顺序参照拦截器添加顺序为准
- 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
- 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作
SSM整合配置
SSM整合流程
- 创建工程
- SSM整合
- Spring
- SpringConfig
- MyBatis
- MybatisConfig
- JdbcConfig
- jdbc.properties
- SpringMVC
- SpringMvcConfig
- SpringMvcSupport(WebMvcConfigurationSupport)
- ServletConfigInitializer 启动容器的配置类
- 功能模块
- 表与实体类
- dao(接口+自动代理)
- service(接口+实现类)
- 业务层接口测试(整合JUnit)
- controller
- 表现层接口测试(PostMan)
SSM-POM配置
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<spring.version>5.2.10.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</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.22</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.13</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>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<!--使用内置Tomcat部署-->
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 端口号 -->
<port>80</port>
<!-- 模块的访问地址 -->
<path>/</path>
<!-- GET方法不会乱码问题 -->
<uriEncoding>utf-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
SSM-MyBatis配置
- jdbc.properties连接配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_db?useSSL=false
jdbc.username=root
jdbc.password=root
- JdbcConfig配置类
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
//配置连接池
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
//Spring事务管理需要的平台事务管理器对象
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ds = new DataSourceTransactionManager();
ds.setDataSource(dataSource);
return ds;
}
}
- MybatisConfig配置类
public class MybatisConfig {
@Bean //设置sqlsession会话工厂配置类
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTypeAliasesPackage("com.lxl.domain");
//创建配置对象
Configuration configuration = new Configuration();
//开启日志记录,显示SQL语句
configuration.setLogImpl(StdOutImpl.class);
//设置列名下划线与驼峰命名对应
configuration.setMapUnderscoreToCamelCase(true);
//指定工厂的配置对象
factoryBean.setConfiguration(configuration);
return factoryBean;
}
@Bean //设置映射mapper扫描配置器,生成代理对象存放IoC容器中
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
}
- SpringConfig配置类
@Configuration
@ComponentScan("com.lxl.service")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableTransactionManagement //开启Spring事务管理
public class SpringConfig {
}
/*
注:如果Spring的配置文件中,使用排除Controller的这种方式
@ComponentScan(basePackages = "com.lxl",
excludeFilters = @ComponentScan.Filter(Controller.class))
会与@EnableWebMvc冲突,导致业务层测试失败,报BeanInstantiationException
*/
SSM-SpringMvc配置
- SpringMvcConfig配置类
@Configuration
@ComponentScan("com.lxl.controller")
@EnableWebMvc //导入SpringMVC的配置
public class SpringMvcConfig {
}
- ServletConfigInitializer配置类,加载SpringMvcConfig和SpringConfig配置类
public class ServletConfigInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
protected Class<?>[] getServletConfigInitializerClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
protected Filter[] getServletFilters() {
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{encodingFilter};
}
}
SSM-体系图
Spring-SpringMvc整合本质
Spring和SpringMVC框架的整合本质上是两个Spring容器的整合,因为我们知道SpringMVC框架本质也是一个Spring容器,所以这里所谓的整合就是在web容器中配置两个Spring容器,并让他们各司其职。
Spring管理service,dao,事务等相关的组件的注入。
SpringMVC管理控制器相关的组件controller。
父子容器
- IoC容器体系中可以有多个子容器,但是父容器只有一个。不同的子容器之间不能共享bean,但是子容器都可以获得父容器中的bean信息。
- Spring容器对应的是父容器,SpringMVC容器对应的是子容器。从容器里面getBean的时候,先从本容器取,如果取不到再从父容器取。
- 父容器和子容器被初始化后会以属性的形式被存储在ServletContext上下文域中以供调用。因此控制器对象(子容器中)可以获取Service对象(父容器)并且注入,反过来业务对象不能获取控制器对象。
Controller加载控制与业务bean加载控制
SpringMVC相关bean(表现层bean)
Spring控制的bean:
- 业务bean(Service)
- 功能bean(DataSource等)
SpringMVC相关bean加载控制:
- SpringMVC加载的bean对应的包均在com.lxl.controller/advice/config包内
Spring相关bean加载控制:
- 方式一:Spring加载的bean设定扫描范围为com.lxl,排除掉controller包内的bean
- 方式二:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等
- 方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中(不推荐)
@Configuration
@ComponentScan(
basePackages = "com.lxl",
excludeFilters = @ComponentScan.Filter(Controller.class)
)
public class SpringConfig {}
//excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes),默认类别就是ANNOTATION
//includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)
SSM-controller结果封装
// 封装结果
@Data
public class Result {
//描述统一格式中的数据
private Object data;
//描述统一格式中的编码,用于区分操作,可以简化配置0或1表示成功失败
private Integer code;
//描述统一格式中的消息,可选属性
private String msg;
}
//Code类封装响应码
public interface Code {
Integer SAVE_OK = 20011;
Integer DELETE_OK = 20021;
Integer UPDATE_OK = 20031;
Integer GET_OK = 20041;
Integer SAVE_ERR = 20010;
Integer DELETE_ERR = 20020;
Integer UPDATE_ERR = 20030;
Integer GET_ERR = 20040;
}
异常处理器
异常情况分析
出现异常现象的常见位置与常见诱因如下:
- 框架内部抛出的异常:因使用不合规导致
- 数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
- 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
- 表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
- 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)
定义异常处理器
概述:@RestControllerAdvice是对Controller进行增强的,可以全局捕获spring mvc抛的异常。并匹配相应的==@ExceptionHandler==中指定的异常类型,重新封装异常信息,将统一格式返回给前端。
@RestControllerAdvice
//这个类所在的包要能够被Spring MVC扫描到
//用于标识当前类为REST风格对应的异常处理器
public class ProjectExceptionAdvice {
//统一处理所有的Exception异常
@ExceptionHandler(Exception.class)
public Result doException(Exception ex) {
//状态码,数据,信息
return new Result(666, null, ex.getMessage());
}
}
异常处理器解析
@RestControllerAdvice注解介绍
名称:@RestControllerAdvice
类型:类注解
位置:Rest风格开发的控制器增强类上定义
作用:为Rest风格开发的控制器类做增强
说明:此注解自带**@ResponseBody注解与@Component**注解,具备对应的功能
2.2.3 @ExceptionHandler注解介绍
- 名称:@ExceptionHandler
- 类型:方法注解
- 位置:专用于异常处理的控制器方法上方
- 作用:设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行
项目异常处理方案
- 业务异常(BusinessException)
- 发送对应消息传递给用户,提醒规范操作
- 系统异常(SystemException)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给运维人员,提醒维护
- 记录日志
- 其他异常(Exception)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给编程人员,提醒维护(纳入预期范围内)
- 记录日志
//自定义异常处理器,用于封装异常信息,对异常进行分类
public class SystemException extends RuntimeException{
//异常的编码
@Getter @Setter
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 class BusinessException extends RuntimeException{
@Getter @Setter
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;
}
}
/************************************************************/
@RestControllerAdvice //用于标识当前类为REST风格对应的异常处理器
public class ProjectExceptionAdvice {
//@ExceptionHandler用于设置当前处理器类对应的异常类型
@ExceptionHandler(SystemException.class)
public Result doSystemException(SystemException ex){
//记录日志
System.out.println("出现系统级异常");
//发送消息给运维
//发送邮件给开发人员,ex对象发送给开发人员
return new Result(ex.getCode(),null,ex.getMessage());
}
@ExceptionHandler(BusinessException.class)
public Result doBusinessException(BusinessException ex){
System.out.println("出现业务级异常");
return new Result(ex.getCode(),null,ex.getMessage());
}
//除了自定义的异常处理器,保留对Exception类型的异常处理,用于处理非预期的异常
@ExceptionHandler(Exception.class)
public Result doOtherException(Exception ex){
//记录日志
System.out.println("出现其它未知异常");
//发送消息给运维
//发送邮件给开发人员,ex对象发送给开发人员
return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系统繁忙,请稍后再试!");
}
}