SSM学习笔记_SpringMVC


SSM所需的前置知识为Javaweb,同时也是SpringBoot所需前置知识

SpringMVC

1. SpringMVC简介

  SpringMVC技术与Servlet技术功能相同,均属于web层开发技术,但SpringMVC技术更简洁,能用更少的代码进行开发

1.1 SpringMVC概述

  • SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
  • 功能
    • 用于进行表现层功能开发
  • 优点
    • 使用简单,开发便捷(相比于Servlet)
    • 灵活性强

1.2 入门案例★

  1. 使用SpringMVC技术需要先导入SpringMVC坐标与Servlet坐标
    <!--1.导入springmvc和servlet的坐标-->
    <dependency>
    	<groupId>javax.servlet</groupId>
    	<artifactId>javax.servlet-api</artifactId>
    	<version>3.1.0</version>
    	<scope>provided</scope>
    </dependency>
    
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-webmvc</artifactId>
    	<version>5.2.10.RELEASE</version>
    </dependency>
    
  2. 创建SpringMVC控制器类(等同于Servlet功能)
    /*2.定义controller类同时将其标记成bean*/
    @Controller   
    public class UserController {
    	@RequestMapping("/save")   // 设置当前操作的访问路径
    	@ResponseBody   // 设置当前操作的返回值类型
    	public String save() {
    		System.out.println("user save ...");
    		return "{'module':'springmvc'}";
    	}
    }
    
  3. 初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean
    /*3.创建springmvc的配置文件,扫描加载controller对应的bean*/
    @Configuration
    @ComponentScan("com.wang.controller")
    public class SpringMvcConfig{
    }
    
  4. 初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理请求
    /*4.定义一个servlet容器启动的配置类,在里面加载spring的配置*/
    public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
        // 加载springMVC容器配置
        @Override
        protected WebApplicationContext createServletApplicationContext() {
            AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
            ctx.register(SpringMvcConfig.class);
            return ctx;
        }
        
        // 设置哪些请求归属springMVC处理
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
        
    	// 加载spring容器配置
        @Override
        protected WebApplicationContext createRootApplicationContext() {
            return null;
        }
    }
    

入门案例用到的注解

名称类型位置作用
@Controller类注解SpringMVC控制器定义上方设置SpringMVC的核心控制器bean
@ResquestMapping方法注释SpringMVC控制器方法定义上方设置当前控制器方法请求访问路径
@ResponseBody方法注释SpringMVC控制器方法定义上方设置当前控制器方法响应内容为当前返回值,无需解析

SpringMVC开发总结(1+N)

  • 一次性工作
    • 创建工程,设置服务器,加载工程
    • 导入坐标
    • 创建web容器启动类,加载SpringMVC配置,并设置SpringMVC请求拦截路径
    • SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器bean)
  • 多次工作
    • 定义处理请求的控制器类
    • 定义处理请求的控制器方法,并配置映射路径(@RequestMapping)与返回json数据(@ResponseBody)

Servlet容器配置类

  • AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化web3.0容器的抽象类
  • AbstractDispatcherServletInitializer提供三个接口方法供用户实现
    • createServletAppilcationContext()方法,创建servlet容器时,加载SpringMVC核心配置类对应的bean并放入WebApplicationContext对象中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围

      protected WebApplicationContext createServletApplicationContext(){
      	AnnotattionConfigWebApplicationContext ctx= new AnnotattionConfigWebAppklicationtContext();
      	ctx.register(SpringMvcConfig.class);
      	return ctx;
      }
      
    • getServletMappings()方法,设定SpringMVC对应的请求映射路径,设置为" / "表示拦截所有请求,任意请求都将转入到SpringMVC进行处理

      protected String[] getServletMapping(){
      	return new String[]("/");
      }
      
    • createRootApplicationContext()方法,如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式同createServletAppilcationContext()

      protected WebApplicationContext createRootApplicationContext(){
      	return null;
      }
      

1.3 工作流程分析★

  • 启动服务器初始化过程
    • 服务器启动,执行ServletContainersInitConfig类,初始化web容器
    • 执行createServletApplicationContext方法,创建了WebApplicationContext对象
    • 加载SpringMvcConfig
    • 执行@ComponentScan加载对应的bean
    • 加载UserController,每个@RequestMapping的名称对应一个具体的方法
    • 执行getServletMappings方法,定义所有请求都通过SpringMVC
  • 单次请求过程
    • 发送请求localhost/save
    • web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
    • 解析请求路径/save
    • 由/save匹配执行对应的方法save()
    • 执行save()
    • 检测到有@ResponseBody直接将save()方法的返回值作为响应体返回给请求方

1.4 Controller加载控制

  • SpringMVC相关bean
    • 表现层bean(Controller)
  • Spring控制的bean
    • 业务层bean(Service)
    • 功能bean(DataSource等)

  • SpringMVC相关bean加载控制
    • SpringMVC加载的bean对应的包均在com.wang.controller包内
  • Spring相关bean加载控制
    • 方式一:Spring加载的bean设定扫描范围为com.wang,排除包含@controller注解的bean

      @Configuration
      @ConmponentScan(value = "com.wang", 
      	excludeFilters = @ComponentScna.Filter(
      		type = FilterType.ANNOTATION,
      		classer = Controller.class
      		)
      	)
      public class SpringConfig{}
      
    • 方式二:Spring加载的bean设定扫描范围为精准范围,例如service包,dao包等

      @Configuretion
      /* dao层在MyBatis中通过自动代理得到实现对象,可以不扫描加载,
      但为了适配所有的数据层技术实现标准开发还是扫描加载了更合适 */
      @ComponentScan({"com.wang.service", "com.wang.dao"})   
      public class SpringConfig{}
      
    • 方式三:不区分Spring与SpringMVC的环境,加载到同一环境中(见后续简化开发部分)

bean的加载格式

//4.定义一个servlet容器启动的配置类,在里面加载spring的配置
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
    // 加载springMVC容器配置
    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(SpringMvcConfig.class);
        return ctx;
    }
    
    // 设置哪些请求归属springMVC处理
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
    
	// 加载spring容器配置
    @Override
    protected WebApplicationContext createRootApplicationContext() {
		AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(SpringConfig.class);
        return ctx;
    }
}

简化开发

public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }

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

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

注解深入学习

名称@ComponentScan
类型类注解
位置Spring配置类上方
属性excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes)
includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)

范例:

@Configuration
@ConmponentScan(value="com.wang",
	excludeFilters = @ComponentScan.Filter(
		type = FilterType.ANNOTATION,
		classes = Controller.class	
	)
)
public class SpringConfig{}

1.5 Postman

2. 请求与响应

2.1 请求映射路径

名称@RequestMapping
类型方法注解/类注解
位置SpringMVC控制器类上方或类中方法定义上方
作用设置当前控制器方法请求访问路径,如果设置在类上统一设置当前类中所有方法的请求访问路径前缀
属性value(默认):请求访问路径
method:HTTP请求动作(POST/GET/PUT/DELETE)

范例:

@Controller
@RequestMapping("/user")
public class UserController{
	@RequestMapping("/save")
	@ResponseBody
	public String save(){
		System.out.println("user save ...");
		return "{'module':'user save'}";
	}
}

2.2 请求参数

Post请求中文乱码处理

  • 在Servlet容器ServletContainersInitConfig.java中添加过滤器并指定字符集,Spring-web包中提供了专用的字符过滤器类
    public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class[]{SpringMvcConfig.class};
        }
    
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class[]{SpringConfig.class};
        }
    
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
        
    	// 乱码处理
        @Override
        protected Filter[] getServletFilters() {
            CharacterEncodingFilter filter = new CharacterEncodingFilter();
            filter.setEncoding("UTF-8");
            return new Filter[]{filter};
        }
    }
    

参数种类

  • 普通参数:URL地址传参

    • 地址参数名与形参变量名相同,定义形参即可接收参数
      @RequestMapping("/commonParam")
      @ResponseBody
      public String commonParam(String name, int age) {
      	System.out.println("普通参数传递 name ==> " + name);
      	System.out.println("普通参数传递 age ==> " + age);
      	return "{'module':'common param'}";
      }
      
    • 地址参数名与形参变量名不同,使用注解接受对应参数
      名称@RequestParam
      类型形参注解
      位置SpringMVC控制器方法形参定义前面
      作用绑定请求参数与处理器方法形参间的关系
      参数required: 是否为必传参数
      defaultValue:参数默认值
      @RequestMapping("/commonParamDifferentName")
      @ResponseBody
      public String commonParamDifferentName(@RequestParam("name") String userName, int age){
       	System.out.println("普通参数 userName ==>" + userName);
       	System.out.println("普通参数 userName ==>" + age);
       	return "{'module':'common param different name'}";
      }
      
  • POJO参数:请求参数名与形参对象属性名相同,定义POJP类型形参即可接收参数

    @RequestMapping("/pojoParam")
    @ResponseBody
    public String pojoParam(User user){
        System.out.println("pojo参数传递 user ==>" + user);
        return "{'module':'pojo param'}";
    }
    
  • 嵌套POJO参数:同POJO类型参数,其中嵌套的POJO属性在URL路径中通过对象.属性来绑定参数关系

    @RequestMapping("/pojoContainPojoParam")
    @ResponseBody
    public String pojoContainPojoParam(User user){
        System.out.println("pojo嵌套pojo参数传递 user ==>" + user);
        return "{'module':'pojo contain pojo param'}";
    }
    
  • 数组类型参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型形参即可接收参数

    @RequestMapping("/arrayParam")
    @ResponseBody
    public String arrayParam(String[] likes){
        System.out.println("数组参数传递 likes ==>" + Arrays.toString(likes));
        return "{'module':'array param'}";
    }
    
  • 集合类型参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系

    @RequestMapping("/listParam")
    @ResponseBody
    public String listParam(@RequestParam List<String> likes){
        System.out.println("集合参数传递 likes ==>" + likes);
        return "{'module':'list param'}";
    }
    

JSON数据传参
请求参数:JSON数组;JSON对象(POJO);JSON数组(POJO)

  1. 导入JSON的pom坐标

    <!--json数据处理-->
    <dependency>
    	<groupId>com.fasterxml.jackson.core</groupId>
    	<artifactId>jackson-databind</artifactId>
    	<version>2.12.4</version>
    </dependency>
    
  2. 设置发送JSON数据(在Postman请求中Body-raw-JSON中添加JSON数据)

  3. 在SpringMvcConfig.class上加入@EnableWebMvc,开启自动转换json数据的支持

    /*3. 创建springmvc的配置文件,加载controller对应的bean*/
    @Configuration
    @ComponentScan(value = "com.wang", 
    	excludeFilters = @ComponentScan.Filter(
    		type = FilterType.ANNOTATION,
    		classes = Controller.class
    	)
    )
    @EnableWebMvc   // 开启自动转换json数据的支持 
    public class SpringMvcConfig {
    }
    

    注意:@EnableWebMvc注解功能强大,整合了多个功能,此处仅使用了其中一部分功能,即JSON数据进行自动类型转换。

  4. 在Controller(控制层)编写代码,设置json数据

    // 集合参数:JSON格式
    @RequestMapping("/listParamForJson")
    @ResponseBody
    public String listParamForJson(@RequestBody List<String> likes){
        System.out.println("list common(json)参数传递 list ==>" + likes);
        return "{'module':'list common for json param'}";
    }
    
    // POJO参数:JSON格式
    @RequestMapping("/pojoParamForJson")
    @ResponseBody
    public String pojoParamForJson(@RequestBody User user){
        System.out.println("pojo(json)参数传递 user ==>" + user);
        return "{'module':'pojo for json param'}";
    }
    
    // POJO集合参数:JSON格式
    @RequestMapping("/listPojoParamForJson")
    @ResponseBody
    public String listPojoParamForJson(@RequestBody List<User> list){
        System.out.println("list pojo(json)参数传递 list ==>" + list);
        return "{'module':'list pojo for json param'}";
    }
    
    名称类型位置作用范例
    @EnableWebMvc配置类注解SpringMVC配置定义上方开启SpringMVC多项辅助功能见本节JSON数据传参第三步
    @RequestBody形参注解SpringMVC控制器方法形参定义前面将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次见本节JSON数据传参第四步

@RequestBody@RequestParam区别

  • 区别
    • @RequestParam用于接收url地址传参和表单传参【application/x-www-form-urlencoded】
    • @RequestBody用于接收json数据【application/json】
  • 应用
    • 后期开发中,发送json格式数据为主,@ResquestBody应用较广
    • 如果发送非json格式数据,选用@RequestParam接收请求参数

2.3 日期类型参数传递

  • 日期类型数据基于系统不同格式也不尽相同
    • 2022-08-30
    • 2022/08/30
    • 08/18/2022
  • 接收形参时,根据不同的日期格式设置不同的接收方式
@RequestMapping("/dateParam")
@ResponseBody
public String dateParam(Date date,
						@DateTimeFormat(pattern = "yyyy-MM-dd") Date date1,
						@DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss") Date date2){
	System.out.println("参数传递 date ==>" + date);
	System.out.println("参数传递 date(yyyy-MM-dd) ==>" + date1);
	System.out.println("参数传递 date(yyyy/MM/dd HH:mm:ss) ==>" + date2);
	retrun "{'module':'data param'}";				
}

日期类型注解

名称@DateTimeFormat
类型形参注解
位置SpringMVC控制器方法形参前面
作用设定日期时间型数据格式
参数pattern = “日期时间格式字符串”(eg.yyyy-MM-dd、yyyy/MM/dd HH:mm:ss)

类型转化器

  • Converter接口
    • 请求参数年龄数据(String ==>Integer)
    • 日期格式转换 (String ==> Date)
    public interface Converter<S,T>{
    	@Nullable
    	T convert(S source);
    }
    
  • @EnableWebMvc功能之一:根据类型匹配对应类型转换器

2.4 响应

响应类型包括页面、数据(文本数据、JSON数据)

  • 响应页面

    @RequestMapping("/toPage")
    public String toPage(){
    	return "page.jsp";
    }
    
  • 响应文本数据

    @RequestMapping("/toText")
    @ResponseBody
    public String toText(){
    	return "response text";
    }
    
  • 响应JSON数据(对象转JSON)

    //响应POJO对象数据
    @RequestMapping("/toJsonPOJO")
    @ResponseBody
    public User toJsonPOJO(){
        User user = new User();
        user.setName("wang");
        user.setAge(24);
        return user;
    }
    
  • 响应对象集合转JSON数组

    //响应POJO集合对象
    @RequestMapping("/toJsonList")
    @ResponseBody
    public List<User> toJsonList(){
        User user= new User();
        user.setName("wang");
        user.setAge(24);
        
        User user1= new User();
        userOne.setName("chen");
        userOne.setAge(18);
    
        List<User> userList = new ArrayList<>();
        userList.add(user);
        userList.add(user1);
        return userList;
    }
    
名称@ResponseBody
类型方法注解
位置SpringMVC控制器方法定义上方
作用设置当前控制器返回值作为响应体
范例见本节响应数据部分代码

类型转换器(HttpMessageConverter)

public interface HttpMessageConverter<T> {
    boolean canRead(Class<?> var1, @Nullable MediaType var2);

    boolean canWrite(Class<?> var1, @Nullable MediaType var2);

    List<MediaType> getSupportedMediaTypes();

    T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;

    void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}

3. REST风格

  • REST(Representational State Transfer)表现形式状态转换

    • 传统风格资源描述形式

      • http://localhost/user/getById?id=1
      • http://localhost/user/saveUser
    • REST风格描述形式

      • http://localhost/user/1
      • http://localhost/user
  • 优点:

    • 隐藏资源的访问行为,无法通过地址得知对资源的具体操作
    • 简化书写

3.1 REST简介

  按照REST风格访问资源时使用行为动作区分对资源的具体操作,根据REST风格对资源进行访问称为RESTful。

URL操作请求形式
http://localhost/users查询全部用户信息GET (查询)
http://loocalhost/users/1查询指定用户信息GET(查询)
http://localhost/users添加用户信息POST(新增/保存)
http://localhost/users修改用户信息PUT(修改/更新)
http://localhost/users/1删除用户信息DELETE(删除)

注意事项:

  • 上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范;
  • 描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源非单个资源,例如:users,books,accounts…

3.2 RESTful入门案例

  1. 设定HTTP请求动作(动词)

    @RequestMapping(value = "/users", method = RequestMethod.POST)
    @ResponseBody
    public String save(@RequestBody User user){
    	System.out.println("user save..." + user);
    	return "{'module':'user save'}";
    }
    
    @RequestMapping(value = "/users", method = RequestMethod.PUT)
    @ResponseBody
    publiic String update(@RequestBody User user){
    	System.out.println("user update ..." + user);
    	return "{'module':'user update'}";
    }
    
  2. 设置请求参数(路径变量)

    @RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
    @ResponseBody
    public String delete(@PathVariable Integer id){
    	System.out.println("user delete ..." + id);
    	return "{'module':'user deleter'}";
    }
    

注解详解

名称类型位置作用属性
@PathVariable形参注解SpringMVC控制器方法形参定义前面绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应/
@RequestMapping方法注解SpringMVC控制器定义上方设置当前控制器方法请求访问路径value(默认):请求访问路径
method:HTTP请求动作(POST/GET/PUT/DELETE)

@RequestBody @RequestParam @PathVariable对比学习

  • 区别
    • @RequestParam用于接收URL地址传参或表单传参
    • @RequestBody用于接收JSON数据
    • @PathVariable用于接收路径参数,使用{参数名称}描述路径参数
  • 应用
    • 后期开发中,发送请求参数超过一个时,以JSON格式为主,@RequestBody应用较广
    • 如果发送非json格式数据,选用@RequestParam接收请求参数
    • 采用RESTful进行开发,当参数数量较少时(例如1个),常采用@PathVariable接收路径变量,常用于传递id值

3.3 RESTful快速开发

@RestController
@RequestMapping("/books")
public class BookController{
	@PostMapping
	public String save(@RequestBody Book book){
		System.out.println("book save ..." + book);
		return "{'module':'book save'}";
	}

	@DeleteMapping("/{id}")
	public String delete(@PathVariable Integer id){
		System.out.println("book delete ..." + id);
		return "{'module':'book delete'}";
	}
	
	@PutMapping
	public String update(@RequestBody Book book){
		System.out.println("book update ..." + book);
		return "{'module':'book update'}";
	}

	@GetMapping("/{id}")
	public String getById(@PathVariable Integer id){
		System.out.println("book getById ..." + id);
		return "{'module':'book getById'}";
	}

	@GetMapping
	public String getAll(){
		System.out.println("book getAll ...");
		return "{'module':'book getAll'}";
	}

}

合并注解事项简化开发

名称类型位置作用属性
@RestController类注解基于SpringMVC的RESTful开发控制器类定义上方设置当前控制器类为RESTful风格,等同于@Controller@ResponseBody两个注解组合/
@GetMapping方法注解基于SpringMVC的RESTful开发控制器方法定义上方设置当前控制器方法请求访问路径与请求动作(Get)value(默认):请求访问路径
@PostMapping 方法注解基于SpringMVC的RESTful开发控制器方法定义上方设置当前控制器方法请求访问路径与请求动作(Post)value(默认):请求访问路径
@PutMapping 方法注解基于SpringMVC的RESTful开发控制器方法定义上方设置当前控制器方法请求访问路径与请求动作(Put)value(默认):请求访问路径
@DeleteMapping方法注解基于SpringMVC的RESTful开发控制器方法定义上方设置当前控制器方法请求访问路径与请求动作(Delete)value(默认):请求访问路径

3.4 案例:基于RESTful页面数据交互

  1. 制作SpringMVC控制类,并通过PostMan测试接口功能(SpringMvcConfig.class和ServletContainersInitConfig.class配置同之前)

    @RestController
    @RequestMapping("/books")
    public Class BookController{
    	@PostMapping
    	public String save(@RequestBody Book book) {
    		System.out.println("book save ==>" + book);
    		return "{'moudule':'book save success'}";
    	}
    	
    	@GetMapping
        public List<Book> getAll(){
    		System.out.println("book getAll is running...");
    		List<Book> bookList = new ArrayList<>();
    		Book book = new Book();
    		book.setType("计算机");
    		book.setName("SpringMVC入门教程");
    		book.setDescription("小试牛刀");
    		bookList.add(book);
    		
    		Book book1 = new Book();
    		book1.setType("计算机");
    		book1.setName("SpringMVC入门教程");
    		book1.setDescription("加油");
    		bookList.add(book1);
    		
    		return bookList;
        }
    }
    
  2. 设置对静态资源的访问放行

    @Configuration
    public class SpringMvcSupport extends WebMvcConfigurationSupport {
        @Override
        protected void addResourceHandlers(ResourceHandlerRegistry registry) {
            // 当访问/pages/*时走/pages目录下的内容访问
            registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
            // 放行js
            registry.addResourceHandler("/js/**").addResourceLocations("/js/");
    		// 放行css
            registry.addResourceHandler("/css/**").addResourceLocations("/css/");
            // 放行插件
            registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
        }
    }
    
  3. 前端页面通过异步提交访问后台控制器

    // 添加
    saveBook(){
    	axios.post("/books", this.formData).then((res)=>{
    	});
    },
    
    // 主页列表查询
    getAll(){
    	axios.get("/books").then((res)=>{
    		this.dataList = res.data;
    	});
    },
    
  4. 总结

    • 先做后台功能,开发接口并调通接口
    • 再做页面异步调用,确认功能可以正常访问
    • 最后再完成页面数据的显示
    • 补充:放行静态资源访问

4. SSM整合

4.1 SSM整合

  1. 创建工程,并导入坐标

    <?xml version="1.0" encoding="UTF-8"?>
    
    <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/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.wang</groupId>
        <artifactId>springmvc_08_ssm</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
            </dependency>
    
            <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>
            
            <!--spring整合mybatis需要三个坐标:mybatis、mybatis-spring、mysql-connect-java-->
            <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.2</version>
            </dependency>
            
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
            
            <!--该项目中选择使用druid数据源-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.2.11</version>
            </dependency>
            
            <!--测试用-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
            
            <!--做springmvc开发时,web容器要用到的-->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
                <version>2.5</version>
                <scope>provided</scope>
            </dependency>
            
            <!--实现json文件与各数据类型之间的互相转换-->
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.9.0</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.1</version>
                    <configuration>
                        <port>8080</port>
                        <path>/</path>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    
  2. SSM整合

    • Spring

      • SpringConfig
      @Configuration
      @ComponentScan("com.wang")
      @PropertySource("classpath:jdbc.properties")
      @Import({JdbcConfig.class, MyBatisConfig.class})
      public class SpringConfig{
      }
      
    • MyBatis

      • MybatisConfig
      public class MyBatisConfig {
          @Bean
          public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
              SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
              factoryBean.setDataSource(dataSource);
              factoryBean.setTypeAliasesPackage("com.wang.domain");
              return factoryBean;
          }
      
          @Bean
          public MapperScannerConfigurer mapperScannerConfigurer(){
              MapperScannerConfigurer msc = new MapperScannerConfigurer();
              msc.setBasePackage("com.wang.dao");
              return msc;
          }
      }
      
      • JdbcConfig
      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;
      	}
          
          // 开启事务管理
          @Bean
          public PlatformTransactionManager transactionManager(DataSource dataSource){
              DataSourceTransactionManager ds = new DataSourceTransactionManager();
              ds.setDataSource(dataSource);
              return ds;
          }
      }
      
      • jdbc.properties
      jdbc.driver=com.mysql.jdbc.Driver
      jdbc.url=jdbc:mysql://localhost:3306/ssm_db
      jdbc.username=root
      jdbc.password=******
      
    • SpringMVC

      • ServletConfig
      public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
          protected Class<?>[] getRootConfigClasses() {
              return new Class[]{SpringConfig.class};
          }
      
          protected Class<?>[] getServletConfigClasses() {
              return new Class[]{SpringMvcConfig.class};
          }
      
          protected String[] getServletMappings() {
              return new String[]{"/"};
          }
      
          // 乱码处理——post表单提交时处理中文乱码
          @Override
          protected Filter[] getServletFilters() {
              CharacterEncodingFilter filter = new CharacterEncodingFilter();
              filter.setEncoding("UTF-8");
              return new Filter[]{filter};
          }
      }
      
      • SpringMvcConfig
      @Configuration
      @ComponentScan("com.wang.controller")
      @EnableWebMvc
      public class SpringMvcConfig {
      }
      
  3. 功能模块

    • domain(表与实体类)
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class Book {
        private Integer id;
        private String type;
        private String name;
        private String description;
    }
    
    • dao(接口+自动代理)
    public interface BookDao {
        /*@Insert("insert into tbl_book values(null, #{type}, #{name}, #{description})")*/
        @Insert("insert into tbl_book (type, name, description) values(#{type}, #{name}, #{description})")
        public void save(Book book);
    
        @Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}")
        public void update(Book book);
    
        @Delete("delete from tbl_book where id = #{id}")
        public void delete(Integer id);
    
        @Select("select * from tbl_book where id = #{id}")
        public Book getById(Integer id);
    
        @Select("select * from tbl_book")
        public List<Book> getAll();
    }
    
    • service(接口+实现类)

      • 接口
      @Transactional
      public interface BookService {
          /**
           * 保存
           * @param book
           * @return
           */
          public Boolean save(Book book);
      
          /**
           * 修改
           * @param book
           * @return
           */
          public Boolean update(Book book);
      
          /**
           * 根据id删除
           * @param id
           * @return
           */
          public Boolean delete(Integer id);
      
          /**
           * 根据id查询
           * @param id
           * @return
           */
          public Book getById(Integer id);
      
          /**
           * 查询全部
           * @return
           */
          public List<Book> getAll();
      }
      
      • 实现类
      @Service
      public class BookServiceImpl implements BookService {
          @Autowired   // Mybatis使用自动代理生成dao层的bean导致此处可能报错,修改IDEA配置即可解决问题
          private BookDao bookDao;
          
          public Boolean save(Book book) {
              bookDao.save(book);
              return true;
          }
      
          public Boolean update(Book book) {
              bookDao.update(book);
              return true;
          }
      
          public Boolean delete(Integer id) {
              bookDao.delete(id);
              return true;
          }
      
          public Book getById(Integer id) {
              return bookDao.getById(id);
          }
      
          public List<Book> getAll() {
              return bookDao.getAll();
          }
      }
      
      • 业务层接口测试(整合JUnit)
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration(classes = SpringConfig.class)
      public class BookServiceTest {
          @Autowired
          private BookService bookService;
          
          @Test
          public void testGetById(){
              Book book = bookService.getById(1);
              System.out.println(book);
          }
      
          @Test
          public void testGetAll(){
              List<Book> list = bookService.getAll();
              System.out.println(list);
          }
      }
      
    • controller

      @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) {
              Book book = bookService.getById(id);
              Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
              String msg = book != null ? "数据查询成功!" : "数据查询失败,请重试!";
              return new Result(code, book, msg);
          }
      
          @GetMapping
          public Result getAll() {
              List<Book> bookList = bookService.getAll();
              Integer code = bookList != null ? Code.GET_ALL_OK : Code.GET_ALL_ERR;
              String msg = bookList != null ? "全部数据查询成功" : "数据查询失败请重试";
              return new Result(code, bookList, msg);
          }
      }
      
      • 数据传输协议所要用到的bean
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @ToString
      public class Result {
          private Integer code;
          private Object data;
          private String msg;
      
          public Result(Integer code, Object data) {
              this.code = code;
              this.data = data;
          }
      }
      
      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 GET_ALL_OK = 20051;
          
          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 GET_ALL_ERR = 20050;
      }
      
      • 表现层接口测试(Apifox)

4.2 表现层数据封装

  为了便于实现前后端数据交互,统一返回数据的格式,在Controller包中添加Result类(定义返回值类型)和Code类(定义返回值对象中对应的静态常量),并将所有返回值封装至Result对象中让Controller里面的方法都返回Result类型的结果。

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Result {
    private Integer code;
    private Object data;
    private String msg;

    public Result(Integer code, Object data) {
        this.code = code;
        this.data = data;
    }
}
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 GET_ALL_OK = 20051;
    
    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 GET_ALL_ERR = 20050;
}

4.3 异常处理器

异常处理器

  • 程序开发过程中不可避免地会出现异常
  • 出现异常现象的常见位置与常见诱因如下:
    • 框架内部抛出异常:因使用不规范导致
    • 数据层抛出的异常:因为外部服务器故障导致(例如:服务器访问超时)
    • 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
    • 表现层抛出的异常:因数据收集,校验规则导致(例如:不匹配的数据类型导致异常)
    • 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长时间未释放等)
  • 各个层级均可能出现异常,所有的异常抛出到表现层处理
  • 表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不强,因此建议通过AOP思想实现异常处理。

定义异常处理器(集中统一地处理项目中的异常)

@RestControllerAdvice
public class ProjectExceptionAdvice {
	@ExceptionHandler(Exception.class)
	public Result doExpetion(Exception ex){
		System.out.println("异常被捕获!");
		return new Result(001, null, "异常被捕获!");
	}
}
注解类型位置作用范例备注
@RestControllerAdvice类注解Rest风格开发的控制器增强类定义上方为Rest风格开发的控制器类做增强见本节代码此注解自带@ResponseBody注解与@Component注解,具备对应的功能
@ExceptionHandler方法注解专用于异常处理的控制器方法上方设定指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行见本节代码此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常

4.4 项目异常处理方案

项目异常分类

  • 业务异常(BusinessException)
    • 规范的用户行为操作产生的异常
    • 不规范的用户行为操作产生的异常
  • 系统异常(SystemException)
    • 项目运行过程中可预计且无法避免的异常
  • 其他异常(Exception)
    • 编程人员未预期到的异常

项目异常处理方案

  • 业务异常(BusinessException)
    • 发送对应消息传递给用户,提醒规范操作
  • 系统异常(SystemException)
    • 发送固定消息给用户,安抚用户
    • 发送特定消息给运维人员,提醒维护
    • 记录日志
  • 其他异常(Exception)
    • 发送固定消息给用户,安抚用户
    • 发送特定消息给编程人员,提醒维护(纳入预期范围内)
    • 记录日志

项目异常处理

  1. 自定义项目系统级异常

    public class SystemException extends RuntimeException{
        private Integer code;
    
        public Integer getCode() {
            return 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;
        }
    }
    
  2. 自定义项目业务级异常

    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;
        }
    }
    
  3. 自定义异常编码(持续补充)

    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 GET_ALL_OK = 20051;
        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 GET_ALL_ERR = 20050;
    
        public static final Integer SYSTEM_UNKNOW_ERROR = 50001;
        public static final Integer SYSTEM_TIMEOUT_ERROR = 50002;
    
        public static final Integer PROJECT_VALIDATE_ERROR = 60001;
        public static final Integer PROJECT_BUSINESS_ERROR = 60002;
    }
    
  4. 触发定义异常

    @Service
    public class BookServiceImpl implements BookService {
        @Autowired
        private BookDao bookDao;
        
        public Book getById(Integer id) {
            // 模拟业务异常
            if(id < 0){
                throw new BusinessException(Code.PROJECT_BUSINESS_ERROR, "请勿进行非法操作!");
            }
            
            // 将可能出现的异常进行包装,转换成自定义异常
            try {
                int i = 1/0;
            }catch (Exception ex){
                throw new SystemException(Code.SYSTEM_TIMEOUT_ERROR, "服务器访问超时,请重试", ex);
            }
            return bookDao.getById(id);
        }
    }
    
  5. 拦截并处理异常

    @RestControllerAdvice
    public class ProjectExceptionAdvice {
    	@ExceptionHandler(SystemException.class)
    	public Result doSystemException(SystemException exception) {
    		//记录日志(错误堆栈)
    		//发送消息给运维
    		//发送邮件给开发人员,exception对象发送给开发人员
    		System.out.println("系统异常被捕获");
    		return new Result(exception.getCode(), null, exception.getMessage());
    	}
    	
    	@ExceptionHandler(BusinessException.class)
    	public Result doBusinessException(BusinessException exception) {
    		System.out.println("业务异常被捕获");
    		return new Result(exception.getCode(), null, exception.getMessage());
    	}
    	
    	@ExceptionHandler(Exception.class)
    	public Result doException(Exception exception) {
    		//记录日志(错误堆栈)
    		//发送消息给运维
    		//发送邮件给开发人员,exception对象发送给开发人员
    		System.out.println("其他异常被捕获");
    		return new Result(Code.SYSTEM_UNKNOW_ERROR, null, "系统繁忙请稍后再试!");
    	}
    }
    
  6. 异常处理效果对比

4.5 案例:SSM整合标准开发

自定义项目系统级异常

axios.get("/books").then((res)=>{});
axios.post("/books", this.formData).then((res)=>{});
axios.delete("/books/" + row.id).then((res)=>{});
axios.put("/books", this.formData).then((res)=>{});
axios.get("/books/" + row.id).then((res)=>{});

5. 拦截器

5.1 拦截器概念

拦截器

  • 拦截器(Interceptor)是一种动态拦截方法调用的机制,再SpringMVC中动态拦截控制器方法的执行
  • 作用:
    • 在指定的方法调用前后执行预先设定的代码
    • 阻止原始方法的执行
  • 拦截器和过滤器的区别
    • 归属不同:Filter(过滤器)属于Servlet技术,Interceptor(拦截器)属于SpringMvc技术
    • 拦截内容不同:Filter(过滤器)可以对所有访问进行增强,Interceptor(拦截器)仅针对SpringMVC的访问进行增强

5.2 入门案例

  1. 声明拦截器的bean,并实现HandlerInterceptor接口(注意:扫描加载bean);
    @Component
    public class ProjectInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("preHandle....");
            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......");
        }
    }
    
  2. 定义配置类,继承WebMvcConfigurationSupport,实现addInterceptor方法(注意:扫描加载配置);
    @Configuration
    public class SpringMvcSupport extends WebMvcConfigurationSupport {
        @Override
        protected void addInterceptor(InterceptorRegistry registry) {
           .....
        }
    }
    
  3. 添加拦截器并设定拦截的访问路径,路径可以通过可变参数设置多个;
    @Configuration
    public class SpringMvcSupport extends WebMvcConfigurationSupport {
        @Autowired
        private ProjectInterceptor projectInterceptor;
     
        @Override
        protected void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
        }
    }
    
  4. 使用标准接口WebMvcConfigurer简化开发(注意:侵入式较强)
    @Configuration
    @ComponentScan({"com.itheima.controller"})
    @EnableWebMvc
    //实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
    public class SpringMvcConfig implements WebMvcConfigurer {
        @Autowired
        private ProjectInterceptor projectInterceptor;
     
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
        }
    }
    

拦截器执行流程
拦截器执行流程

5.3 拦截器参数

@Component
public class ProjectInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle....");
        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......");
    }
}

具体参数

  • handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装,可用于获取拦截方法信息
  • request:请求对象
  • response:响应对象
  • modelAndView:获取页面跳转相关数据
  • ex:拿到原始程序执行过程中出现的异常,表现层出现的异常

:preHandle方法中若返回false,那么被拦截的处理器将不再继续执行。

5.4 拦截器链配置

多拦截器执行顺序
多拦截器执行顺序
拦截器链的运行顺序

  • preHandle:与配置顺序相同,必定运行
  • postHandle:与配置顺序相反,可能不运行
  • afterCompletion: 与配置顺序相反,可能不运行

解释:顺序情况下的话,走到哪里出现错误返回false,则后面的postHandle拦截器都不运行,afterComletion会运行

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值