Spring_5_Interceptor拦截器_SSM整合配置_ExceptionHandler异常处理器_项目异常处理方案

SpringMvc拦截器

概念与作用

在这里插入图片描述

**概念:**拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行。使用了AOP思想,拦截器性质上也是切面类。

作用:

  1. 在指定的方法调用前后执行预先设定的代码
  2. 阻止原始方法的执行
  3. 总结:增强

Interceptor与Filter区别:

  • Fliter属于Servlet技术,对所有的访问进行增强或拦截。
  • Interceptor属于SpringMvc,只对SpringMvc的访问进行处理。

定义拦截器

使用步骤:

  1. 搭建环境(导入servlet与SpringMvc依赖)

  2. 创建SpringMvc配置类/文件

    @Configuration
    //同时扫描控制器和配置类所在的包
    @ComponentScan({"com.lxl.controller","com.lxl.config"})
    @EnableWebMvc 
    //使用拦截器需要添加这个注解。不建议使用实现接口的配置方式,两种方式只能配置一种,否则有冲突
    public class SpringMvcConfig {
    
    }
    
  3. 定义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[]{"/"};
        }
    }
    
  4. 创建控制器、定义拦截器

    @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("最终通知");
        }
    }
    
  5. 配置加载拦截器

    @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整合流程

  1. 创建工程
  2. SSM整合
    • Spring
      • SpringConfig
    • MyBatis
      • MybatisConfig
      • JdbcConfig
      • jdbc.properties
    • SpringMVC
      • SpringMvcConfig
      • SpringMvcSupport(WebMvcConfigurationSupport)
    • ServletConfigInitializer 启动容器的配置类
  3. 功能模块
    • 表与实体类
    • 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,"系统繁忙,请稍后再试!");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值