Spring MVC-SSM整合

一、回顾SpringMVC访问接口流程

1.容器加载分析

  • 容器分析

  • 手动注册WebApplicationContext  
public class ServletConfig extends AbstractDispatcherServletInitializer {

    @Override
    protected WebApplicationContext createServletApplicationContext() {
        //获取SpringMVC容器
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(SpringMvcConfig.class);
        return context;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }
}

2.容器加载过程分析

  • tomcat 服务器启动的时候,加载ServletConfig类之后,做初始化web容器操作,相当于 web.xml

  • 执行注册容器的方法,获取 SpringMVC容器 WebApplicationContext  
@Nullable
    protected WebApplicationContext createRootApplicationContext() {
        Class<?>[] configClasses = this.getRootConfigClasses();
        if (!ObjectUtils.isEmpty(configClasses)) {
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.register(configClasses);
            return context;
        } else {
            return null;
        }
    }

    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        Class<?>[] configClasses = this.getServletConfigClasses();
        if (!ObjectUtils.isEmpty(configClasses)) {
            context.register(configClasses);
        }

        return context;
    }
  • 通过@ComponentScan("cn.sycoder.controller") 加载 Controller 下面的bean 进 WebApplicationContext
@RestController
public class TestController {

    @GetMapping("/test/{id}")
    public String test(@PathVariable Long id) {
        return "ok:" + id;
    }
}
  • 把使用了 RequestMapping 注解的方法的 value --- 对应一个方法,建立起了一对一映射关系(可以想象为hashmap)

    • /test/{id} ---- test 方法

3.请求接口过程

  • 访问 http://localhost:8080/test/1

  • 匹配 springmvc 的 / servletMapping 规则,交给 springmvc 处理

  • 解析 /test/1路径找到对应的 test 方法

  • 执行方法

  • 因为使用 RestController ,所以返回方法的返回值作为响应体返回给浏览器

4.SSM整合会出现bean界定不清楚问题

  • SpringMVC 需要加载哪些bean?

    • controller 层(表现层即可)

  • Spring 加载哪些bean?

    • service

    • dao

4.1如何处理

  • 将spring配置注入到 web 容器中
@Configuration
@ComponentScan(value={"cn.sycoder.service","cn.sycoder.dao"})
public class SpringConfig {
}
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[]{"/"};
    }
}

4.2验证两个容器的bean相互不干扰

@Test
public void test(){
    AnnotationConfigApplicationContext applicationContext =
            new AnnotationConfigApplicationContext(SpringConfig.class);
    ITestService bean = applicationContext.getBean(ITestService.class);
    bean.get(1L);

    TestController bean1 = applicationContext.getBean(TestController.class);
    System.out.println(bean1.test(1L));
}

二、SSM整合

1.SSM整合流程分析

1.1创建项目

  • 导入依赖
  • 配置 web 项目入口配置替换 web.x(AbstractAnnotationConfigDispatcherServletInitializer)

配置 Spring 配置类交给 web 容器管理

@Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

 配置 SpringMVC 配置类交给 web 容器管理

@Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }

配置请求拦截规则,交给 springmvc 处理

@Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

1.2配置 Spring

  • SpringConfig

    • @Configuration 标记Spring配置类,替换Spring-config-xml

    • @ComponetScan 扫描需要被Spring 管理的bean

    • @EnableTransactionManagment 启动管理事务支持

    • @PropertySource 引入db.properties 配置文件

  • 配置 JdbcConfig 配置类

    • 使用德鲁伊 DataSource 数据源

    • 构建平台事务管理器 DataSourceTransactionManager

  • 配置 MyBatis 配置类

    • 构建 SqlSessionFactoryBean

    • 指定 MapperScanner 设置 mapper 包扫描寻找 mapper.xml 文件

1.3配置 SpringMVC

  • 配置SpringMvcConfig

    • @Configuration

    • @ComponentScan 只扫描 Controller

    • 开启SpringMVC 注解支持 @EnableWebMvc

1.4开发业务

  • 使用注解

    • 注入bean 注解

      • @Autowired

    • @RestController

      • @GetMapping

        • @RequestParam

      • @PostMapping

        • @RequestBody

      • @DeleteMapping

        • @PathVariable

      • @PutMapping

    • @Service

      • @Transactional

    • junit

      • @RunWith

      • @ContextConfiguration

      • @Test

2.SSM整合

2.1导入依赖

<dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.17.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.17.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.17.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>8.0.29</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>
  </dependencies>

2.2创建各目录结构

  • 目录如下

2.3创建SpringConfig

  • SpringConfig

@Configuration
@ComponentScan(value = {"cn.sycoder.service","cn.sycoder.dao"})
@EnableTransactionManagement
@PropertySource("classpath:db.properties")
@Import({DbConfig.class,MybatisConfig.class})
public class SpringConfig {
}

2.4创建DbConfig配置类

  • 创建数据库配置文件

jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=123456
  •  创建DbConfig
public class DbConfig {
    
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    /**
     * 配置德鲁伊连接池
     * @return
     */
    @Bean
    public DataSource dataSource(){
        DruidDataSource source = new DruidDataSource();
        source.setUrl(url);
        source.setDriverClassName(driver);
        source.setPassword(password);
        source.setUsername(username);
        return source;
    }
    
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager manager = new DataSourceTransactionManager();
        manager.setDataSource(dataSource);
        return manager;
    }
    
}

2.5创建MybatisConfig配置类

  • MyBatisConfig

public class MybatisConfig {
    
    @Bean
    public SqlSessionFactoryBean sessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setTypeAliasesPackage("cn.sycoder.domain");
        return bean;
    }
    
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage("cn.sycoder.dao");
        return configurer;
    }
}

2.6创建SpringMVC配置类

  • SpringMvcConfig

@Configuration
@ComponentScan("cn.sycoder.controller")
@EnableWebMvc
public class SpringMvcConfig {
}

2.7创建Web项目入口配置类

  • ServletConfig

public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};//配置Spring交给Web 管理
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

3.功能开发

3.1创建数据库及表

  • 创建 ssm 数据库

  • 创建 item 表

create table item
(
	id bigint auto_increment,
	type varchar(64) null,
	name varchar(64) null,
	remark text null,
	constraint item_pk
		primary key (id)
);

3.2编写模型类

  • 添加 lombok 依赖

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.22</version>
</dependency>
  • 模型类  
@Data
public class Item {
    private Long id;
    private String name;
    private String type;
    private String remark;
}

3.3编写Mapper接口

  • Mapper 接口

@Repository
public interface ItemMapper {
    @Insert("insert into item(name,type,remark) value(#{name},#{type},#{remark})")
    public int save(Item item);
    @Delete("delete from item where id = #{id}")
    public int delete(Long id);
    @Update("update item set name = #{name},type = #{type},remark=#{remark} where id=#{id}")
    public int update(Item item);
    @Select("select * from item where id = #{id}")
    public Item getById(Long id);
    @Select("select * from item")
    public List<Item> list();

}

3.4编写Service接口和实现类

  • Service 接口

public interface IItemService {
    /**
     * 添加闲置物品方法
     * @param item
     * @return
     */
    public boolean save(Item item);

    /**
     * 删除闲置物品
     * @param id
     * @return
     */
    public boolean delete(Long id);

    /**
     * 更新闲置物品
     * @param item
     * @return
     */
    public boolean update(Item item);

    /**
     * 查询闲置物品通过id
     * @param id
     * @return
     */
    public Item getById(Long id);

    /**
     * 查询所有闲置商品
     * @return
     */
    public List<Item> lists();
}
  •  定义接口实现类
@Service
public class ItemServiceImpl implements IItemService {
    
    @Autowired
    ItemMapper mapper;
    
    @Override
    @Transactional
    public boolean save(Item item) {
        return mapper.save(item) > 0;
    }

    @Override
    @Transactional
    public boolean delete(Long id) {
        return mapper.delete(id) >0;
    }

    @Override
    @Transactional
    public boolean update(Item item) {
        return mapper.update(item) >0;
    }

    @Override
    public Item getById(Long id) {
        return mapper.getById(id);
    }

    @Override
    public List<Item> lists() {
        return mapper.list();
    }
}

3.5编写Contorller类

  • Controller

@RestController
@RequestMapping("/item")
public class ItemController {

    @Autowired
    IItemService service;

    @PostMapping
    public boolean save(@RequestBody Item item){
        return service.save(item);
    }

    @PutMapping
    public boolean update(@RequestBody Item item){
        return service.update(item);
    }

    @DeleteMapping("/{id}")
    public boolean delete(@PathVariable Long id){
        return service.delete(id);
    }

    @GetMapping("/{id}")
    public Item getById(@PathVariable Long id){
        return service.getById(id);
    }

    @GetMapping
    public List<Item> list(){
        return service.lists();
    }
}

4.验证 ssm 整合结果

  • 启动项目并且解决问题

  • 修改Spring配置类  
@Configuration
@ComponentScan(value = {"cn.sycoder.service","cn.sycoder.dao"})
@EnableTransactionManagement
@PropertySource("classpath:db.properties")
@Import({DbConfig.class,MybatisConfig.class})
public class SpringConfig {
}

4.1添加item 数据

  • 准备 item 数据

{"name":"键盘","type":"电脑外设","remark":"9成新,半价卖"}

{"name":"笔记本","type":"电脑","remark":"9成新,8折出售"}

{"name":"鞋子","type":"收藏鞋","remark":"科比签名的,独一无二"} 

  • 添加数据

4.2修改数据

  • 准备数据

{"id":4,"name":"二手鞋子","type":"废鞋","remark":"破鞋子"}

4.3查询单个数据

  • 查询id=4的物品

4.4删除单个数据

  • 删除id=4的物品

4.5查询全部数据操作

  • 查询全部

5.整合单元测试

  • 目录结构

  • 新建测试类  
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class ItemTest {

    @Autowired
    IItemService service;


    @Test
    public void save(){
        Item item = new Item();
        item.setName("单元测试");
        item.setRemark("单元测试");
        item.setType("单元测试");
        boolean save = service.save(item);
        System.out.println(save);
    }

    @Test
    public void update(){
        Item item = new Item();
        item.setId(5L);
        item.setName("单元测试");
        item.setRemark("单元测试");
        item.setType("单元测试");
        boolean save = service.update(item);
        System.out.println(save);
    }

    @Test
    public void getById(){
        Item byId = service.getById(5L);
        System.out.println(byId);
    }

    @Test
    public void list(){
        List<Item> lists = service.lists();
        System.out.println(lists);
    }
}

三、项目实战中细节问题

1.导入前端资源

1.1静态资源拦截处理

  • 设置访问 index 访问主页

@Controller
public class IndexController {
    @RequestMapping("/index")
    public String index(){
        System.out.println("----------------");
        return "/pages/items.html";
    }

}
  • 出现静态资源被拦截问题  
@Configuration
public class StaticSupport extends WebMvcConfigurationSupport {

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");

    }
}
  •  将 staticSupport 交给 SpringMvc 管理
@Configuration
@ComponentScan(value = {"cn.sycoder.controller","cn.sycoder.config"})
@EnableWebMvc
public class SpringMvcConfig {
}

1.2项目实现

  • 保存方法

handleAdd () {
    console.log("========")
    axios.post("/item",this.formData).then((res)=>{
        //todo
    })
},
  •  列表查询
getAll() {
    axios.get("/item",).then((res)=>{
        this.dataList = res.data;
    })
},
  •  删除操作
handleDelete(row) {
    axios.delete("/item/"+row.id).then((res)=>{
        //todo
    })
}
  •  编辑操作
handleEdit() {
      axios.put("/item", this.formData).then((res) => {
                    
      
},

2.全局统一结果集处理

2.1分析目前写代码存在的问题

  • 查询单个物品时返回单条数据

{"id":2,"name":"笔记本","type":"电脑","remark":"9成新,8折出售"}

  • 查询列表返回列表数据  

[{"id":1,"name":"键盘","type":"电脑外设","remark":"9成新,半价卖"},{"id":2,"name":"笔记本","type":"电脑","remark":"9成新,8折出售"},{"id":3,"name":"鞋子","type":"收藏鞋","remark":"科比签名的,独一无二"},{"id":5,"name":"单元测试","type":"单元测试","remark":"单元测试"}]

  • 删除数据返回单个结果

    true
  • 目前对于系统而言参数了三种数据类型

    • 不能做到前后端同时开发

    • 前后端的交互很乱

  • 最后处理

{"data":true}
{"data":{"id":2,"name":"笔记本","type":"电脑","remark":"9成新,8折出售"}}


{
    "code":2000,
    "data":true,
    "msg":"详情"  
}

2.2处理方式

  • 通过定义一个统一结果集处理

{
    "code":2000,
    "data":true,
    "msg":"详情"  
}
  •  后端实体
@Data
public class ResultResp {
    private Integer code;
    private Object data;
    private String msg;
}
  •  后端返回实体最终结果
@Data
public class ResultResp {
    private Integer code;
    private Object data;
    private String msg;

    public ResultResp(){
    }

    public ResultResp(Integer code,Object data){
        this.code = code;
        this.data=data;
    }

    public ResultResp(Integer code,Object data,String msg){
        this.code=code;
        this.data=data;
        this.msg=msg;
    }

    public static ResultResp success(Integer code,Object data){
        return new ResultResp(code,data);
    }
}
  • 状态码类
public class Code {
    /**
     * 定义好协议之后,前端和后端统一按照协议执行
     */
    public static final Integer SAVE_OK = 20000;
    public static final Integer SAVE_FAIL = 20001;


    public static final Integer UPDATE_OK = 20010;
    public static final Integer UPDATE_FAIL = 20011;

    public static final Integer DELETE_OK = 20020;
    public static final Integer DELETE_FAIL = 20021;

    public static final Integer GET_OK = 20030;
    public static final Integer GET_FAIL = 20031;

    public static final Integer PAGE_OK = 20040;
    public static final Integer PAGE_FAIL = 20041;
}

修改控制层返回代码

  • save 方法

@PostMapping
public ResultResp save(@RequestBody Item item){
    boolean ret = service.save(item);
    return new ResultResp(ret? Code.SAVE_OK:Code.SAVE_FAIL,ret);
}
  • update 方法
@PutMapping
public ResultResp update(@RequestBody Item item){
    boolean ret = service.update(item);
    return new ResultResp(ret? Code.UPDATE_OK:Code.UPDATE_FAIL,ret);
}
  • delete 方法
@DeleteMapping("/{id}")
public ResultResp delete(@PathVariable Long id){
    boolean ret = service.delete(id);
    return new ResultResp(ret? Code.DELETE_OK:Code.DELETE_FAIL,ret);
}
  • 单个结果集处理
@GetMapping("/{id}")
public ResultResp getById(@PathVariable Long id){
    Item ret = service.getById(id);
    return ResultResp.success(ret==null?Code.GET_FAIL:Code.GET_OK,ret);
}
  • 集合结果集
@GetMapping
public ResultResp list(){
    List<Item> ret = service.lists();
    return ResultResp.success(ret==null?Code.PAGE_FAIL:Code.PAGE_OK,ret);
}

2.3前后端联调

  • 保存操作

handleAdd() {
    console.log("========")
    axios.post("/item", this.formData).then((res) => {
        if (res.data.code == 20000) {
            //关闭新增弹框
            this.dialogFormVisible = false;
            this.$message.success("添加物品成功")
        } else {
            this.$message.error("添加失败")
        }
    }).finally(() => {
        this.getAll();
    })
},
  •  列表查询
getAll() {
    axios.get("/item",{
            params:{
                name:this.pagination.queryString
            }
    }).then((res)=>{
        // this.dataList = res.data;
        if(res.data.code == 20040){
            this.dataList = res.data.data;
        }else{
            this.$message.error("加载失败")
        }
    })
},
  • 删除操作
 handleDelete(row) {
    axios.delete("/item/" + row.id).then((res) => {
        if (res.data.code == 20020) {
            this.$message.success("删除物品成功")
        } else {
            this.$message.error("删除失败")
        }
    }).finally(() => {
        this.getAll();
    })
}
  • 编辑操作
handleEdit() {
    axios.put("/item", this.formData).then((res) => {
        if (res.data.code == 20010) {
            this.dialogFormVisible4Edit = false;
            this.$message.success("编辑物品成功")
        } else {
            this.$message.error("编辑失败")
        }
    }).finally(() => {
        this.getAll();
    })
},

高级查询修改

  • controller

@GetMapping
    public ResultResp list(@RequestParam(required = false) String name){
        List<Item> ret = service.lists(name);
        return ResultResp.success(ret==null?Code.PAGE_FAIL:Code.PAGE_OK,ret);
    }
  •  业务层
@Override
    public List<Item> lists(String name) {
        if (name != null && !"".equals(name.trim())) {

            return mapper.list(name);
        }

        return mapper.listNull();
    }
  • mapper 接口
@Select("select * from item where name like concat('%',#{name},'%')")
public List<Item> list(String name);

@Select("select * from item ")
public List<Item> listNull();

3.全局统一异常处理

3.1目前存在问题

  • 模拟后台出现服务器异常

@GetMapping
public ResultResp list(@RequestParam(required = false) String name){
    System.out.println(1/0);
    List<Item> ret = service.lists(name);
    return ResultResp.success(ret==null?Code.PAGE_FAIL:Code.PAGE_OK,ret);
}

出现如下错误  

3.2后端服务可能会出现的异常

  • 框架可能报错

  • 持久层代码报错

  • 业务层业务代码报错

  • 控制层业务代码报错

  • 注意:这些异常不能避免的,此时应该把所有的异常在表现层进行统一的处理(aop)

  • 解决方案

@RestControllerAdvice
public class ExceptionHandlerController {

    @ExceptionHandler(ArithmeticException.class)
    public ResultResp handlerException(){
        System.out.println("出现异常了");
        return new ResultResp(500,null,"服务繁忙,请稍后再试");
    }
}

3.3@RestControllerAdvice 

  • 使用来做控制器增强操作  
名称@RestControllerAdvice
位置Rest 风格增强类上
作用给控制器做增强操作
使用类上面
  • 包含了如下注解

    • @ResponseBody

    • @ControllerAdvice

    • @Component

3.4@ExceptionHandler

  • 异常处理器

名称@ExceptionHandler
位置方法上
属性具体的异常类型
作用处理具体异常的,设定具体异常类型,出现异常后,终止controller中方法的执行,转入当前方法执行

3.5项目中具体处理

  • 业务异常

@Data
public class BusinessException extends RuntimeException{
    private Integer code;
}
  •  持久层异常
@Data
public class DaoException extends RuntimeException{
    private Integer code;
}
  •  系统异常
@Data
public class SystemException extends RuntimeException{
    private Integer code;
}
  • 其它异常
@Data
public class OtherException extends RuntimeException{
    private Integer code;
}
  • 定义code
public class Code {
    /**
     * 定义好协议之后,前端和后端统一按照协议执行
     */
    public static final Integer SAVE_OK = 20000;
    public static final Integer SAVE_FAIL = 20001;


    public static final Integer UPDATE_OK = 20010;
    public static final Integer UPDATE_FAIL = 20011;

    public static final Integer DELETE_OK = 20020;
    public static final Integer DELETE_FAIL = 20021;

    public static final Integer GET_OK = 20030;
    public static final Integer GET_FAIL = 20031;

    public static final Integer PAGE_OK = 20040;
    public static final Integer PAGE_FAIL = 20041;

    
    public static final Integer BUSINESS_ERR = 50001;
    public static final Integer SYSTEM_ERR = 50002;
    public static final Integer DAO_ERR = 50003;
    public static final Integer OTHER_ERR = 50005;
    
    

}
  • 统一异常处理
@RestControllerAdvice
public class ExceptionHandlerController {
    //业务异常的例子:账户名和密码错误
    @ExceptionHandler(BusinessException.class)
    public ResultResp handlerBusinessException(BusinessException e){
        return new ResultResp(e.getCode(),null,e.getMessage());
    }
    
    //需要发送短信提醒运维人员
    @ExceptionHandler(SystemException.class)
    public ResultResp handlerSystemException(SystemException e){
        //发送短信提醒业务人员的操作
        //日志打印
        return new ResultResp(e.getCode(),null,e.getMessage());
    }

    @ExceptionHandler(OtherException.class)
    public ResultResp handlerException(OtherException e){
        return new ResultResp(e.getCode(),null,e.getMessage());
    }
}
  • 控制层方法
 @GetMapping
public ResultResp list(@RequestParam(required = false) String name) {
    if (name == null || name.equals(""))
        throw new BusinessException(Code.BUSINESS_ERR,"传参不正常请重试");
    List<Item> ret = null;
    try {
        ret = service.lists(name);
    } catch (Exception e) {
        throw new SystemException(Code.SYSTEM_ERR,"系统繁忙,请稍后再试");
    }
    return ResultResp.success(ret == null ? Code.PAGE_FAIL : Code.PAGE_OK, ret);
}

4.拦截器

4.1拦截器概述

  • 概述:一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行

  • 实际开发中,静态资源(HTML/CSS)不需要交给框架处理,需要拦截的是动态资源

4.2图示

  • 图示

4.3案例实现

  • 模拟没有登录拦截操作

  • 新定义拦截器

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("登录拦截操作");
        String username = request.getParameter("username");
        if("sy".equals(username)){
            return true;
        }
        System.out.println("preHandle");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}
  •  修改SpringMvc 配置
@Configuration
@ComponentScan({"cn.sycoder.controller","cn.sycoder.interceptor"})
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {

    @Autowired
    LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor).addPathPatterns("/item","/item/*");
    }
}
  • controller 确实被拦截
@RestController
@RequestMapping("/item")
public class ItemController {

    @GetMapping("/{id}")
    public String getById(@PathVariable Long id){
        return "query"+id;
    }

    @GetMapping
    public String list(){
        return "list";
    }
}

4.4拦截器参数讲解

  • HttpServletRequest request

  • HttpServletResponse response

  • Object handler 相当于是对访问接口的一种包装

4.5拦截器链

  • 新增拦截器

@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    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("权限拦截的postHandle方法");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("权限拦截的afterCompletion方法");
    }
}
  •  添加到拦截器链中
@Configuration
@ComponentScan({"cn.sycoder.controller","cn.sycoder.interceptor"})
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {

    @Autowired
    LoginInterceptor loginInterceptor;

    @Autowired
    AuthInterceptor authInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor).addPathPatterns("/item","/item/*");
        registry.addInterceptor(authInterceptor).addPathPatterns("/item","/item/*");
    }
}
  • 拦截器链注意点

    • 拦截器有着先进后出的原则

    • preHandle : 与配置顺序保持一致的执行顺序

    • postHandle:与配置顺序相反,可能不运行

    • afterCompletion:与配置顺序相反,可能不运行

4.6面试题拦截器与过滤器的区别

  • 区别

    • 技术所属不同

      • 过滤器:Servlet 技术

      • 拦截器:SpringMVC技术

    • 拦截内容不同

      • Filter : 是对所有访问进行过滤

      • Interceptor:仅仅针对访问SpringMVC

  • 访问流程

四、SpringMVC执行流程

1.SpringMVC 常用组件

  • DispatcherServlet:前端控制器,用于对请求和响应进行统一处理

  • HandlerMapping:处理器映射器,根据 url/method可以去找到具体的 Handler(Controller)

  • Handler:具体处理器(程序员,以后开发这一部分需要)

  • HandlerAdapter:处理器适配器,进行处理器方法的执行

  • ViewResolver:处理视图相关的

2.处理流程图

3.执行流程原理分析

3.1DispatcherServlet

  • 初始化操作

protected void initStrategies(ApplicationContext context) {
        this.initMultipartResolver(context);
        this.initLocaleResolver(context);
        this.initThemeResolver(context);
        this.initHandlerMappings(context);
        this.initHandlerAdapters(context);
        this.initHandlerExceptionResolvers(context);
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);
}
  •  具体处理请求的方法
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //删掉了一系列没用方法

        try {
            //具体执行方法
            this.doService(request, response);
        } catch (IOException | ServletException var16) {
            failureCause = var16;
            throw var16;
        } catch (Throwable var17) {
            failureCause = var17;
            throw new NestedServletException("Request processing failed", var17);
        } finally {
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            this.logResult(request, response, (Throwable)failureCause, asyncManager);
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
        }

    }

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //删掉一系列干扰代码

        try {
            //具体执行
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

        }

}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    //获取处理器就是具体的需要执行的 Tontroller
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
					//获取处理器适配器
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
					//具体调用 Controller 中的方法
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }
//获取具体执行的
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            Iterator var2 = this.handlerMappings.iterator();

            while(var2.hasNext()) {
                HandlerMapping mapping = (HandlerMapping)var2.next();
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }

        return null;
    }

3.2HandlerMapping

  • dbug 的图示

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值