SpringMVC 简单总结

三层架构

  • 表现层:负责数据展示

  • 业务层:负责业务处理

  • 数据层:负责从数据库中获取数据

MVC 简介

MVC(Model View Controller):一种用于设计Web应用程序表现层的模式

  • Model(模型):数据模型,用于封装数据

  • View(视图):页面视图,用于展示数据(jsp, html)

  • Controller(控制器):处理用户交互的调度器,用于根据用户需求处理程序逻辑

SpringMVC

SpringMVC是一种基于Java实现MVC模型的轻量级Web框架

优点

自动封装参数

灵活性强

性能突出(相比现有的表现层框架:Struts2)

入门案例制作

①导入SpringMVC相关坐标

<dependencies>
 <!-- servlet3.1规范的坐标 -->
 <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>3.1.0</version>
   <scope>provided</scope>
 </dependency>
 <!--jsp坐标-->
 <dependency>
   <groupId>javax.servlet.jsp</groupId>
   <artifactId>jsp-api</artifactId>
   <version>2.1</version>
   <scope>provided</scope>
 </dependency>

 <!--springmvc的坐标-->
 <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>5.1.9.RELEASE</version>
 </dependency>

</dependencies>

在pom.xml中添加tomcat7-maven-plugin插件,配置端口80、访问路径/、编码格式UTF-8

<build>
 <!--设置插件-->
 <plugins>
   <!--具体的插件配置-->
   <plugin>
     <groupId>org.apache.tomcat.maven</groupId>
     <artifactId>tomcat7-maven-plugin</artifactId>
     <version>2.1</version>
     <configuration>
       <port>80</port>
       <path>/</path>
       <uriEncoding>UTF-8</uriEncoding>
     </configuration>
   </plugin>
 </plugins>
</build>
<configuration>
   <port>80</port>
   <path>/</path>
   <uriEncoding>UTF-8</uriEncoding>
</configuration>

配置打包方式和编译版本:

<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>

②定义表现层业务处理器Controller,并配置成spring的bean(等同于Servlet):@Controller

@Controller
public class UserController {

   public void save(){
       System.out.println("user mvc controller is running ...");
  }
}

在Spring MVC配置文件src\main\resources\spring-mvc.xml中添加包扫描

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:mvc="http://www.springframework.org/schema/mvc"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
   
   <!--扫描加载所有的控制类-->
   <context:component-scan base-package="com.itheima"/>

</beans>

③web.xml中配置SpringMVC核心控制器,用于将请求转发到对应的具体业务处理器Controller中

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                     http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
        version="3.1">

<servlet>
   <servlet-name>DispatcherServlet</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <init-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>classpath*:spring-mvc.xml</param-value>
   </init-param>
</servlet>
<servlet-mapping>
   <servlet-name>DispatcherServlet</servlet-name>
   <url-pattern>/</url-pattern>
</servlet-mapping>
   
</web-app>

④设定具体Controller中指定方法的访问路径@RequestMapping("/save")

//设定当前方法的访问映射地址
@RequestMapping("/save")
public void save(){
   System.out.println("user mvc controller is running ...");
}

⑤设置返回页面,默认路径:src\main\webapp

JSP页面头信息:设置编码格式

<%@page pageEncoding="UTF-8" language="java" contentType="text/html;UTF-8" %>
//设定当前方法的访问映射地址
@RequestMapping("/save")
//设置当前方法返回值类型为String,用于指定请求完成后跳转的页面
public String save(){
   System.out.println("user mvc controller is running ...");
   //设定具体跳转的页面
   return "success.jsp";
}

 

SpringMVC六大核心组件

  • DispatcherServlet:前端控制器, 是整体流程控制的中心,由其调用其它组件处理用户的请求, 有 效的降低了组件间的耦合性

  • HandlerMapping:处理器映射器, 负责根据用户请求找到对应具体的Handler处理器

  • Handler:处理器,业务处理的核心类,通常由开发者编写,描述具体的业务

  • HandlrAdapter:处理器适配器,通过它对处理器进行执行

  • View Resolver:视图解析器, 将处理结果生成View视图

  • View:视图,最终产出结果, 常用视图如jsp、html

Controller加载控制

SpringMVC的处理器对应的bean必须按照规范格式开发,为避免加入无效的bean可通过bean加载过滤器进行包含设定或排除设定,表现层bean标注通常设定为@Controller

在spring-mvc.xml中添加包扫描过滤配置:

<context:component-scan base-package="com.itheima">
   <context:include-filter
                           type="annotation"
                           expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

静态资源加载

简写方式:在spring-mvc中添加如下配置

<!--SpringMVC提供的通用资源放行方式-->
<mvc:default-servlet-handler/>

<!--必须组合使用-->
<mvc:annotation-driven/>

字符集过滤器-中文乱码

SpringMVC提供专用的中文字符过滤器: CharacterEncodingFilter,用于处理中文乱码问题

配置在 web.xml 里面

<!--乱码处理过滤器,与Servlet中使用的完全相同,差异之处在于处理器的类由Spring提供-->
<filter>
   <filter-name>CharacterEncodingFilter</filter-name>
   <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
   <init-param>
       <param-name>encoding</param-name>
       <param-value>UTF-8</param-value>
   </init-param>
</filter>
<filter-mapping>
   <filter-name>CharacterEncodingFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

 

请求

普通类型参数传参

在UserController中添加如下方法:

1.参数名与Controller中的方法参数名保持一致 http://localhost/requestParam1?name=itheima&age=14

@RequestMapping("/requestParam1")
public String requestParam1(String name ,String age){
   System.out.println("name="+name+",age="+age);
   return "page.jsp";
}

2.@RequestParam 的使用  类型:形参注解  位置:方法形参前方  作用:绑定请求参数与对应处理方法形参间的关系

http://localhost/requestParam2?userName=Jock

@RequestMapping("/requestParam2")
public String requestParam2(@RequestParam(
                           name = "userName",
                           required = true,
                           defaultValue = "itheima") String name){
   System.out.println("name="+name);
   return "page.jsp";
}

POJO类型参数传参

1.当POJO中使用简单类型属性时, 参数名称与POJO类属性名保持一致,SpringMVC自动封装参数

访问URL: http://localhost/requestParam3?name=itheima&age=14

Controller

@RequestMapping("/requestParam3")
public String requestParam3(User user){
   System.out.println(user);
   return "page.jsp";
}

2.参数冲突 当POJO类型属性与其他形参出现同名问题时,将被同时赋值 访问URL: http://localhost/requestParam4?name=itheima&age=14&minAge=12

@RequestMapping("/requestParam4")
public String requestParam4(User user,String age){
   System.out.println("user.age="+user.getAge()+",age="+age);
   return "page.jsp";
}

使用@RequestParam注解进行区分

@RequestMapping("/requestParam4")
public String requestParam4(User user, @RequestParam("minAge") int age) {
   System.out.println("user=" + user + ",age=" + age);
   return "page.jsp";
}

3.POJO中的对象属性

当POJO中出现对象属性时,参数名称与对象层次结构名称保持一致 访问URL: http://localhost/requestParam5?address.province=beijing

public class User {
   private String name;
   private Integer age;

   private Address address;
   
//生成get,set方法
}
public class Address {
   private String province;
   private String city;
   private String address;
   
   //生成get,set方法
}
@RequestMapping("/requestParam5")
public String requestParam5(User user){
   System.out.println("user.address="+user.getAddress().getProvince());
   return "page.jsp";
}

4.POJO中有简单的集合类型参数时,属性名写多个即可 访问URL:http://localhost/requestParam6?nick=Jock1&nick=Jockme&nick=zahc

private List<String> nick;
//http://localhost/requestParam6?nick=Jock1&nick=Jockme&nick=zahc
@RequestMapping("/requestParam6")
public String requestParam6(User user) {
   System.out.println(user);
   return "page.jsp";
}

5.当POJO中有复杂的集合类型参数时,参数名称与对象层次结构保持一致,使用数组格式描述集合中对象的位置

访问URL:http://localhost/requestParam7?addresses[0].city=beijing&addresses[1].province=hebei

private List<Address> addresses;
//http://localhost/requestParam7?addresses[0].city=beijing&addresses[1].province=hebei
@RequestMapping("/requestParam7")
public String requestParam7(User user){
   System.out.println("user.addresses="+user.getAddress());
   return "page.jsp";
}

6.当POJO中出现Map时,参数名称与对象层次结构名称保持一致,使用映射格式描述集合中对象的位置

访问URL:http://localhost/requestParam8?addressMap['job'].city=beijing&addressMap['home'].province=henan

private Map<String,Address> addressMap;
@RequestMapping("/requestParam8")
//http://localhost/requestParam8?addressMap['job'].city=beijing&addressMap['home'].province=henan
public String requestParam8(User user){
   System.out.println("user.addressMap="+user.getAddressMap());
   return "page.jsp";
}

数组与集合类型参数传参

1.数组类型参数

请求参数名与处理器方法形参名保持一致

访问URL: http://localhost/requestParam9?nick=Jockme&nick=zahc

@RequestMapping("/requestParam9")
public String requestParam9(String[] nick){
   System.out.println(nick[0]+","+nick[1]);
   return "page.jsp";
}

2.集合类型参数

需要使用@RequestParam("name")定义参数名 访问URL: http://localhost/requestParam10?nick=Jockme&nick=zahc

@RequestMapping("/requestParam10")
public String requestParam10(@RequestParam("nick") List<String> nicks) {
   System.out.println(nicks);
   return "page.jsp";
}

注意:

  • 使用@RequestParam注解

  • url中的参数名和@RequestParam中的保持一致

     

日期类型格式转换

第二种方式日期类型格式转换(常用)  名称: @DateTimeFormat  类型: 形参注解、成员变量注解  位置:形参前面 或 成员变量上方  作用:为当前参数或变量指定类型转换规则  范例: http://localhost/requestParam12?date=1999-09-09

@RequestMapping("/requestParam11")
public String requestParam11(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
   System.out.println(date);
   return "page.jsp";
}

配置注解驱动支持

<mvc:annotation-driven />  

如果POJO中有Date类型:http://localhost/requestParam11?name=zhangsan&birthday=1999-09-09

@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@RequestMapping("/requestParam11")
public String requestParam13(User user) {
   System.out.println(user);
   return "page.jsp";
}

请求映射

方法注解

名称: @RequestMapping  类型: 方法注解  位置:处理器类中的方法定义上方  作用:绑定请求地址与对应处理方法间的关系  范例:  访问路径:http://localhost/requestURL1

@Controller
public class UserController {
   
   @RequestMapping("requestURL1")
   public String requestURL1() {
       return "page.jsp";
  }
}

类注解

名称: @RequestMapping  类型: 类注解  位置:处理器类定义上方  作用:为当前处理器中所有方法设定公共的访问路径前缀  范例:  访问路径: http://localhost/user/requestURL2

@Controller
@RequestMapping("/user")
public class UserController {
   
   //带有类映射地址访问格式,需要将类映射地址作为前缀添加在实际映射地址的前面
   //最终返回的页面如果未设定绝对访问路径,将从类映射地址所在目录中查找
   //http://localhost/user/requestURL2     (注意:要配合类上定义的路径使用)
   @RequestMapping("/requestURL2")
   public String requestURL2() {
       return "page.jsp";
  }
}  

响应

页面跳转

两种跳转方式:转发和重定向,区别在于:转发forward后地址栏不会变化

  • 转发(默认),服务器跳转

@RequestMapping("/showPage1")
public String showPage1() {
   System.out.println("user mvc controller is running ...");
   return "forward:page.jsp";
}
  • 重定向,客户端跳转

@RequestMapping("/showPage2")
public String showPage2() {
System.out.println("user mvc controller is running ...");
return "redirect:page.jsp";
}

 

页面访问快捷设定

InternalResourceViewResolver:展示页面的保存位置通常固定且结构相似的,可以设定通用的访问路径,简化页面配置格式

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <property name="prefix" value="/WEB-INF/pages/"/>
   <property name="suffix" value=".jsp"/>
</bean>
public String showPage3() {
   return "page";
}

设置之后,默认使用forward,不能再添加forward和redirect

注意:如果未设定了返回值,使用void类型,则默认使用访问路径作页面地址的前缀和后缀

//最简页面配置方式,使用访问路径作为页面名称,省略返回值
@RequestMapping("/showPage5")
public void showPage5() {
   System.out.println("user mvc controller is running ...");
}

##

带数据页面跳转

  • 方式一:使用HttpServletRequest类型形参进行数据传递

    http://localhost/showPageAndData1

    @RequestMapping("/showPageAndData1")
    public String showPageAndData1(HttpServletRequest request) {
       request.setAttribute("name","SpringMVC入门案例");
       request.setAttribute("price",66.66d);
       return "page";
    }
  • 方式二:使用Model类型形参进行数据传递

    http://localhost/showPageAndData2

    @RequestMapping("/showPageAndData2")
    public String showPageAndData2(Model model) {
       model.addAttribute("name","itheima");
       
       Book book = new Book();
       book.setName("SpringMVC入门实战");
       book.setPrice(66.6d);
       
       model.addAttribute("book",book);
       return "page";
    }
  • 最佳实践方式三:使用ModelAndView类型形参进行数据传递,将该对象作为返回值传递给调用者

    http://localhost/showPageAndData3

    //使用ModelAndView形参传递参数,该对象还封装了页面信息
    @RequestMapping("/showPageAndData3")
    public ModelAndView showPageAndData3(ModelAndView modelAndView) {
       //ModelAndView mav = new ModelAndView();   替换形参中的参数
       Book book  = new Book();
       book.setName("SpringMVC入门案例");
       book.setPrice(66.66d);
       //添加数据的方式,key对value
       modelAndView.addObject("book",book);
       //添加数据的方式,key对value
       modelAndView.addObject("name","Jockme");
       //设置页面的方式,该方法最后一次执行的结果生效
       modelAndView.setViewName("page");
       //返回值设定成ModelAndView对象
       return modelAndView;
    }

##

返回JSON数据

添加jackson依赖:

<!--json相关坐标3个-->
<dependency>
 <groupId>com.fasterxml.jackson.core</groupId>
 <artifactId>jackson-core</artifactId>
 <version>2.9.0</version>
</dependency>

<dependency>
 <groupId>com.fasterxml.jackson.core</groupId>
 <artifactId>jackson-databind</artifactId>
 <version>2.9.0</version>
</dependency>

<dependency>
 <groupId>com.fasterxml.jackson.core</groupId>
 <artifactId>jackson-annotations</artifactId>
 <version>2.9.0</version>
</dependency>
  • 方法二:使用SpringMVC提供的消息类型转换器将对象与集合数据自动转换为JSON数据:

    @ResponseBody

    //使用SpringMVC注解驱动,对标注@ResponseBody注解的控制器方法进行结果转换,由于返回值为引用类型,自动调用jackson提供的类型转换器进行格式转换
    @RequestMapping("/showData4")
    //响应正文
    @ResponseBody
    public Book showData4() {
       Book book  = new Book();
       book.setName("SpringMVC入门案例");
       book.setPrice(66.66d);
       return book;
    }

    开启注解驱动

    <!--开启springmvc注解驱动,对@ResponseBody的注解进行格式增强,追加其类型转换的功能,具体实现由MappingJackson2HttpMessageConverter进行-->
    <mvc:annotation-driven/>

     

返回中文字符串

@RequestMapping("/showData6")
@ResponseBody
public String showData2() {
   //return "{'name':'Jock', age : 10}";
   return "我是中文";
}

在spring-mvc.xml中配置编码格式

<!--开启springmvc注解驱动,对@ResponseBody的注解进行格式增强,
   追加其类型转换的功能,具体实现由MappingJackson2HttpMessageConverter进行-->
<mvc:annotation-driven>
   <mvc:message-converters>
       <bean class="org.springframework.http.converter.StringHttpMessageConverter">
           <constructor-arg value="UTF-8" />
       </bean>
   </mvc:message-converters>
</mvc:annotation-driven>

 

 

异步调用(重点)

接受异步请求参数(重点)

 名称: @RequestBody  类型: 形参注解  位置:处理器类中的方法形参前方  作用:将异步提交数据组织成标准请求参数格式,并赋值给形参  范例:

@RequestMapping("/ajaxController")
public String ajaxController(@RequestBody String message){
   System.out.println(message);
   return "page.jsp";
}

检查pom.xml是否导入jackson的坐标

<!--json相关坐标3个-->
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-core</artifactId>
   <version>2.9.0</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.9.0</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-annotations</artifactId>
   <version>2.9.0</version>
</dependency>

 

*注意:请求数据格式与POJO中的属性对应*

public class User {
   private String name;
   private Integer age;
   
   //添加get,set方法
}
{"name":"Jock","age":39}
//为id="testAjaxPojo"的组件绑定点击事件
$("#testAjaxPojo").click(function(){
   $.ajax({
       type:"POST",
       url:"ajaxPojoToController",
       data:'{"name":"Jock","age":39}',
       dataType:"text",
       contentType:"application/json",
  });
});

将@RequestBody注解添加到Pojo参数前方

{"name":"Jock","age":39}
  @RequestMapping("/ajaxPojoToController")
 //如果处理参数是POJO,且页面发送的请求数据格式与POJO中的属性对应,@RequestBody注解可以自动映射对应请求数据到POJO中
 public String ajaxPojoToController(@RequestBody User user){
     System.out.println("controller pojo :"+user);
     return "page.jsp";
}
  • 注解添加到集合参数前方时,封装的异步提交数据按照集合的存储结构进行关系映射 注意:页面发送的数据是JSON格式的对象数组

    [{"name":"Jock","age":39},{"name":"Jockme","age":40}]
    //为id="testAjaxList"的组件绑定点击事件
    $("#testAjaxList").click(function(){
       $.ajax({
          type:"POST",
          url:"ajaxListToController",
          data:'[{"name":"Jock","age":39},{"name":"Jockme","age":40}]',
          dataType:"text",
          contentType:"application/json",
      });
    });
    @RequestMapping("/ajaxListToController")
    //如果处理参数是List集合且封装了POJO,且页面发送的数据是JSON格式的,数据将自动映射到集合参数中
    public String ajaxListToController(@RequestBody List<User> userList){
       System.out.println("controller list :"+userList);
       return "page.jsp";
    }

##

自定义拦截器

  • 制作拦截功能类(通知):实现HandlerInterceptor接口

    //自定义拦截器需要实现HandleInterceptor接口
    public class MyInterceptor implements HandlerInterceptor {
       //处理器运行之前执行
       @Override
       public boolean preHandle(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler) throws Exception {
           System.out.println("前置运行----a1");
           //返回值为false将拦截原始处理器的运行
           //如果配置多拦截器,返回值为false将终止当前拦截器后面配置的拦截器的运行
           return true;
      }

       //处理器运行之后执行
       @Override
       public void postHandle(HttpServletRequest request,
                              HttpServletResponse response,
                              Object handler,
                              ModelAndView modelAndView) throws Exception {
           System.out.println("后置运行----b1");
      }

       //所有拦截器的后置执行全部结束后,执行该操作
       @Override
       public void afterCompletion(HttpServletRequest request,
                                   HttpServletResponse response,
                                   Object handler,
                                   Exception ex) throws Exception {
           System.out.println("完成运行----c1");
      }

       //三个方法的运行顺序为   preHandle -> postHandle -> afterCompletion
       //如果preHandle返回值为false,三个方法仅运行preHandle
    }
  • 编写Controller

    @Controller
    public class InterceptorController {

       @RequestMapping("/handleRun")
       public String handleRun() {
           System.out.println("业务处理器运行------------main");
           return "page.jsp";
      }
    }
  • 配置拦截器的执行位置(类似切入点)

    <!--开启拦截器使用-->
    <mvc:interceptors>
       <!--开启具体的拦截器的使用,可以配置多个-->
       <mvc:interceptor>
           <!--设置拦截器的拦截路径-->
           <mvc:mapping path="/handleRun"/>
           <!--指定具体的拦截器类-->
         <bean class="com.itheima.interceptor.MyInterceptor"/>
       </mvc:interceptor>
    </mvc:interceptors>

     

异常处理

异常处理器-视频

1.实现HandlerExceptionResolver接口(异常处理器)

@Component
public class ExceptionResolver implements HandlerExceptionResolver {
   public ModelAndView resolveException(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Object handler,
                                        Exception ex) {
       System.out.println("异常处理器正在执行中");
       ModelAndView modelAndView = new ModelAndView();
       //定义异常现象出现后,反馈给用户查看的信息
       modelAndView.addObject("msg","出错啦! ");
       //定义异常现象出现后,反馈给用户查看的页面
       modelAndView.setViewName("error.jsp");
       return modelAndView;
  }
}

2.根据异常的种类不同,进行分门别类的管理,返回不同的信息

@Component
public class ExceptionResolver implements HandlerExceptionResolver {
   @Override
   public ModelAndView resolveException(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Object handler,
                                        Exception ex) {
       System.out.println("my exception is running ...."+ex);
       ModelAndView modelAndView = new ModelAndView();
       if( ex instanceof NullPointerException){
           modelAndView.addObject("msg","空指针异常");
      }else if ( ex instanceof  ArithmeticException){
           modelAndView.addObject("msg","算数运算异常");
      }else{
           modelAndView.addObject("msg","未知的异常");
      }
       modelAndView.setViewName("error.jsp");
       return modelAndView;
  }
}

3.在Controller中模拟不同的异常进行测试

//http://localhost/save
@RequestMapping("/save")
@ResponseBody
public String save() throws Exception {
   System.out.println("user controller save is running ...");
   //模拟业务层发起调用产生了异常
   
   //除0算术异常
   //int i = 1/0;
   
   //空指针异常
   //String str = null;
   //str.length();
   
   return "";
}

 

注解开发异常处理器(重点)

使用注解实现异常分类管理  名称: @ControllerAdvice  类型: 类注解  位置:异常处理器类上方  作用:设置当前类为异常处理器类  范例:

@ControllerAdvice
public class ExceptionAdvice {
}  
  • 使用注解实现异常分类管理  名称: @ExceptionHandler  类型: 方法注解  位置:异常处理器类中针对指定异常进行处理的方法上方  作用:设置指定异常的处理方式  说明:处理器方法可以设定多个  范例:

@ExceptionHandler(NullPointerException.class)
@ResponseBody
public String doNullException(HttpServletResponse response, Exception ex) throws JsonProcessingException {
   return "Null point";
}

@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public String doArithmeticException(Exception e){
   return "ArithmeticException";
}

@ExceptionHandler(Exception.class)
@ResponseBody
public String doException(Exception ex){
   return "all";
}

 

扩展知识

1.如果标记了@ControllerAdvice类中的每个方法都使用了@ResponseBody,可以采用如下的简写方式:

//@ResponseBody
//@ControllerAdvice

@RestControllerAdvice //= @ControllerAdvice + @ResponseBody
public class ExceptionAdvice {
}

@RestControllerAdvice = @ControllerAdvice + @ResponseBody

2.@ResponseBody返回中文字符串乱码,在spring-mvc.xml配置转换器编码:

<mvc:annotation-driven>
   <mvc:message-converters>
       <bean class="org.springframework.http.converter.StringHttpMessageConverter">
           <constructor-arg value="UTF-8" />
       </bean>
   </mvc:message-converters>
</mvc:annotation-driven>

项目异常处理方案

  • 异常处理方案

    • 业务异常:  发送对应消息传递给用户,提醒规范操作

    • 系统异常:  发送固定消息传递给用户,安抚用户  发送特定消息给运维人员,提醒维护  记录日志

    • 其他异常:  发送固定消息传递给用户,安抚用户  发送特定消息给编程人员,提醒维护

      • 纳入预期范围内

       记录日志

自定义异常

  • 自定义BusinessException

    //自定义异常继承RuntimeException,覆盖父类所有的构造方法
    public class BusinessException extends RuntimeException {
       public BusinessException() {
      }

       public BusinessException(String message) {
           super(message);
      }

       public BusinessException(String message, Throwable cause) {
           super(message, cause);
      }

       public BusinessException(Throwable cause) {
           super(cause);
      }

       public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
           super(message, cause, enableSuppression, writableStackTrace);
      }
    }
  • 自定义SystemException

    //自定义异常继承RuntimeException,覆盖父类所有的构造方法
    public class SystemException extends RuntimeException {

       public SystemException() {
      }

       public SystemException(String message) {
           super(message);
      }

       public SystemException(String message, Throwable cause) {
           super(message, cause);
      }

       public SystemException(Throwable cause) {
           super(cause);
      }

       public SystemException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
           super(message, cause, enableSuppression, writableStackTrace);
      }
    }

     

项目异常处理实现

  • 在UserControll中模拟触发异常

    //http://localhost/ajax.jsp
    @RequestMapping("/save")
    @ResponseBody
    public String save(@RequestBody User user) throws Exception {
       System.out.println("user controller save is running ...");
       
       //对用户的非法操作进行判定,并包装成异常对象进行处理,便于统一管理
       if(user.getName().trim().length() < 8){
           throw new BusinessException("对不起,用户名长度不满足要求,请重新输入!");
      }
       if(user.getAge() < 0){
           throw new BusinessException("对不起,年龄必须是0到100之间的数字!");
      }

       if (user.getAge() > 100) {
           throw new SystemException("我是系统异常,不能给用户看!");
      }
       
       if (true) {
           throw new RuntimeException("test");
      }
    }
  • 通过自定义异常将所有的异常现象进行分类管理,以统一的格式对外呈现异常消息

    @RestControllerAdvice //= @ControllerAdvice + @ResponseBody
    public class ProjectExceptionAdvice {

       @ExceptionHandler(BusinessException.class)
       public String doBusinessException(Exception ex){
           //业务异常出现的消息要发送给用户查看
           return ex.getMessage();
      }

       @ExceptionHandler(SystemException.class)
       public String doSystemException(Exception ex){
           System.out.println(ex.getMessage());
           //系统异常出现的消息不要发送给用户查看,发送统一的信息给用户看
           return "服务器出现问题,请联系管理员!";

           //实际的问题现象应该传递给redis服务器,运维人员通过后台系统查看
           //redisTemplate.opsForvalue.set("msg", ex.getMessage());
      }

       @ExceptionHandler(Exception.class)
       public String doException(Exception ex){
           //将ex堆栈信息保存起来
           ex.printStackTrace();
           return "太热情,请稍候……";
      }

 

实用技术

文件上传

  • 第一步:引入commons-fileupload坐标

    <dependency>
       <groupId>commons-fileupload</groupId>
       <artifactId>commons-fileupload</artifactId>
       <version>1.4</version>
    </dependency>

     

同时上传多个文件:

<form action="/fileupload" method="post" enctype="multipart/form-data">
   <%--文件上传表单的name属性值一定要与controller处理器中方法的参数对应,否则无法实现文件上传--%>
  上传LOGO:<input type="file" name="file"/><br/>
  上传照片:<input type="file" name="file1"/><br/>
  上传任意文件:<input type="file" name="file2"/><br/>
   <input type="submit" value="上传"/>
</form>

完整代码:

@RequestMapping(value = "/fileupload")
//参数中定义MultipartFile参数,用于接收页面提交的type=file类型的表单,要求表单名称与参数名相同
public String fileupload(MultipartFile file,MultipartFile file1,MultipartFile file2, HttpServletRequest request) throws IOException {
   System.out.println("file upload is running ..."+file);
   //MultipartFile参数中封装了上传的文件的相关信息
   //首先判断是否是空文件,也就是存储空间占用为0的文件
   if(!file.isEmpty()){
       //如果大小在范围要求内正常处理,否则抛出自定义异常告知用户(未实现)
       //获取原始上传的文件名,可以作为当前文件的真实名称保存到数据库中备用
       String fileName = file.getOriginalFilename();
       //设置保存的路径
       String realPath = request.getServletContext().getRealPath("/images");
       //保存文件的方法,指定保存的位置和文件名即可,通常文件名使用随机生成策略产生,避免文件名冲突问题
       file.transferTo(new File(realPath,file.getOriginalFilename()));
  }
   //测试一次性上传多个文件
   if(!file1.isEmpty()){
       String fileName = file1.getOriginalFilename();
       //可以根据需要,对不同种类的文件做不同的存储路径的区分,修改对应的保存位置即可
       String realPath = request.getServletContext().getRealPath("/images");
       file1.transferTo(new File(realPath,file1.getOriginalFilename()));
  }
   if(!file2.isEmpty()){
       String fileName = file2.getOriginalFilename();
       String realPath = request.getServletContext().getRealPath("/images");
       file2.transferTo(new File(realPath,file2.getOriginalFilename()));
  }
   return "page.jsp";
}

##

RESTful开发风格(重点)

Rest

HTTP请求的四种方式

 GET(查询) http://localhost/user/1 GET  POST(保存) http://localhost/user POST  PUT(更新) http://localhost/user/1 PUT  DELETE(删除) http://localhost/user/1 DELETE

注意:上述行为是约定方式,约定不是规范,可以打破,所以称Rest风格,而不是Rest规范

 

查询id=100的用户信息两种实现方式:

//@Controller
//@ResponseBody

//设置rest风格的控制器
@RestController //= @Controller + @ResponseBody
//设置公共访问路径,配合方法上的访问路径使用
@RequestMapping("/user")
public class UserController {
   //查询 http://localhost/user/100
   @GetMapping("/{id}")
   public String restGet(@PathVariable int id) {
       System.out.println("restful is running ....get:"+id);
       return "success.jsp";
  }
   
}

Restful开发简写方式

  1. Restful请求路径简化配置方式 : @RestController = @Controller + @ResponseBody

  2. 完整的简化后代码,@RestController = @Controller + @Response

    //设置rest风格的控制器
    @RestController
    //设置公共访问路径,配合下方访问路径使用
    @RequestMapping("/user")
    public class UserController {

       //接收GET请求简化配置方式
       @GetMapping("/{id}")
       public String get(@PathVariable Integer id){
           System.out.println("restful is running ....get:"+id);
           return "success.jsp";
      }

       //接收POST请求简化配置方式
       @PostMapping("/{id}")
       public String post(@PathVariable Integer id){
           System.out.println("restful is running ....post:"+id);
           return "success.jsp";
      }

       //接收PUT请求简化配置方式
       @PutMapping("/{id}")
       public String put(@PathVariable Integer id){
           System.out.println("restful is running ....put:"+id);
           return "success.jsp";
      }

       //接收DELETE请求简化配置方式
       @DeleteMapping("/{id}")
       public String delete(@PathVariable Integer id){
           System.out.println("restful is running ....delete:"+id);
           return "success.jsp";
      }
    }

     

 

 

 

校验框架

导入hibernate-validator校验框架的坐标

<!--导入校验的jsr303规范:接口-->
<dependency>
   <groupId>javax.validation</groupId>
   <artifactId>validation-api</artifactId>
   <version>2.0.1.Final</version>
</dependency>
<!--导入校验框架实现技术-->
<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>6.1.0.Final</version>
</dependency>

 

1. 开启校验

 名称:@Valid 、 @Validated  类型:形参注解  位置:处理器类中的实体类类型的方法形参前方  作用:设定对当前实体类类型参数进行校验  范例:

@RequestMapping(value = "/addemployee")
public String addEmployee(@Valid Employee employee) {
   System.out.println(employee);
   return "success";
}

2.设置校验规则

 名称:@NotNull, @NotBlank, @NotEmpty  类型:属性注解  位置:属性上方  作用:设定当前属性校验规则  范例:每个校验规则所携带的参数不同,根据校验规则进行相应的调整

public class Employee{
   
   @NotBlank(message = "姓名不能为空")
   private String name;//员工姓名
   
   //添加getter,setter方法
}  

易错点:必须给name,age等属性编写getter,setter方法

3.获取错误信息

通过形参Errors获取校验结果数据,通过Model接口将数据封装后传递到页面显示

@RequestMapping(value = "/addemployee")
public String addEmployee(@Valid Employee employee, Errors errors, Model model){
   System.out.println(employee);
   if(errors.hasErrors()){
       for(FieldError error : errors.getFieldErrors()){
           model.addAttribute(error.getField(),error.getDefaultMessage());
      }
       return "addemployee.jsp";
  }
   return "success.jsp";
}

在JSP页面中获取后台封装的校验结果信息

<form action="/addemployee" method="post">
  员工姓名:<input type="text" name="name"><span style="color:red">${name}</span><br/>
  员工年龄:<input type="text" name="age"><span style="color:red">${age}</span><br/>
   <input type="submit" value="提交">
</form>

 

嵌套校验

引用类型字段如何校验,比如Employee中有address属性记录地址

 名称:@Valid  类型:属性注解  位置:实体类中的引用类型属性上方  作用:设定当前应用类型属性中的属性开启校验

1.在address属性上添加@Valid注解,开启嵌套属性校验

public class Employee {
   //实体类中的引用类型通过标注@Valid注解,设定开启当前引用类型字段中的属性参与校验
   @Valid
   private Address address;
   
   //添加address属性的get,set方法
}

2.开启嵌套校验后,被校验对象内部需要添加对应的校验规则,并添加嵌套属性的get,set方法

//嵌套校验的实体中,对每个属性正常添加校验规则即可
public class Address {
   @NotBlank(message = "请输入省份名称")
   private String provinceName;//省份名称

   @NotBlank(message = "请输入城市名称")
   private String cityName;//城市名称

   @NotBlank(message = "请输入详细地址")
   private String detail;//详细住址

   @NotBlank(message = "请输入邮政编码")
   @Size(max = 6, min = 6, message = "邮政编码由6位组成")
   private String zipCode;//邮政编码
   
   //添加get,set方法
   
}
@PostMapping(value = "/addemployee")
public String addEmployee(@Valid Employee employee, Errors errors, Model m) {
   //Errors对象用于封装校验结果,如果不满足校验规则,对应的校验结果封装到该对象中,包含校验的属性名和校验不通过返回的消息
   //判定Errors对象中是否存在未通过校验的字段
   if (errors.hasErrors()) {
       //获取所有未通过校验规则的信息
       List<FieldError> fieldErrors = errors.getFieldErrors();
       for (FieldError error : fieldErrors) {
           //将校验结果信息添加到Model对象中,用于页面显示,后期实际开发中无需这样设定,返回json数据即可
           //name = 姓名不能为空 -> ${name}
           m.addAttribute(error.getField(), error.getDefaultMessage());
           //address.cityName = 请输入城市名称 -> ${requestScope['address.provinceName']}
           //m.addAttribute("address.provinceName", "请输入省份名称");
      }

       //el获取对象属性值
       Address address = new Address();
       address.setProvinceName("test省份不能为空");
       //${address.provinceName}
       m.addAttribute("address", address);

       //当出现未通过校验的字段时,跳转页面到原始页面,进行数据回显
       return "addemployee.jsp";
  }
   return "success.jsp";
}

3.在jsp页面中获取嵌套属性中的错误信息

${requestScope['address.cityName']}
<%--注意,引用类型的校验未通过信息不是通过对象进行封装的,直接使用对象名.属性名的格式作为整体属性字符串进行保存的,和使用者的属性传递方式有关,不具有通用性,仅适用于本案例--%>
省:<input type="text" name="address.provinceName"><span style="color:red">${requestScope['address.provinceName']}</span><br/>
市:<input type="text" name="address.cityName"><span
        style="color:red">${requestScope['address.cityName']}</span><br/>

 

分组校验

根据业务不同需要调整是否参与校验,比如同一个模块,新增和修改时校验规则是不一致的

  • 新增用户:新增时用户名不能为空

  • 修改用户:修改时不能修改用户名,所以不用强制用户名不能为空

解决方案:对不同种类的属性进行分组,在校验时可以指定参与校验的字段所属的组类别

  • 根据业务类型使用接口定义分组为Save, Update

    public interface Save {
    }
    public interface Update {
    }
  • 在实体类Employee中为属性设置所属组,可以设置多个

    姓名不能为空只在新增时校验

    @NotEmpty(message = "姓名不能为空",groups = {Save.class})
    private String name;//员工姓名

    年龄校验新增和修改都需要校验

    @NotEmpty(message = "年龄不能为空",groups = {Save.class, Update.class})
    private Integer age;//员工年龄
  • 在Controller的方法参数上开启分组校验,并指定使用哪一种分组:@Validated({Save.class})

    //新增Employee
    @RequestMapping("/addemployee")
    public String addEmployee(@Validated({Save.class}) Employee employee,
                                Errors errors, Model m) {
    }


    //更新Employee
    @PostMapping(value = "/updateEmployee")
    public String updateEmployee(@Validated({Update.class}) EmployeeGroup employee,
                                Errors errors, Model m) {
    }

     

 

 

Spring整合Mybatis(复习)

image-20200807162442617

在pom.xml引入相关坐标

<dependencies>
   <!--spring+springmvc环境环境-->
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
     <version>5.1.9.RELEASE</version>
   </dependency>


   <!--mybatis环境-->
   <!--mybatis环境-->
   <!--mybatis环境-->
   <dependency>
     <groupId>org.mybatis</groupId>
     <artifactId>mybatis</artifactId>
     <version>3.5.3</version>
   </dependency>

   <!--mysql环境-->
   <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.47</version>
   </dependency>

   <!--spring整合jdbc-->
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jdbc</artifactId>
     <version>5.1.9.RELEASE</version>
   </dependency>

   <!--spring整合mybatis-->
   <dependency>
     <groupId>org.mybatis</groupId>
     <artifactId>mybatis-spring</artifactId>
     <version>2.0.3</version>
   </dependency>

   <!--druid连接池-->
   <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>druid</artifactId>
     <version>1.1.16</version>
   </dependency>

   <!--分页插件坐标-->
   <dependency>
     <groupId>com.github.pagehelper</groupId>
     <artifactId>pagehelper</artifactId>
     <version>5.1.2</version>
   </dependency>

   <!--jackson-->
   <dependency>
     <groupId>com.fasterxml.jackson.core</groupId>
     <artifactId>jackson-databind</artifactId>
     <version>2.9.0</version>
   </dependency>


   <!--其他组件-->
   <!--其他组件-->
   <!--其他组件-->
   <!--junit单元测试-->
   <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>4.12</version>
   </dependency>
   <!--spring整合junit-->
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-test</artifactId>
     <version>5.1.9.RELEASE</version>
   </dependency>
 </dependencies>

 

Spring整合Mybatis

  • 创建Spring配置文件:组件扫描

  • 创建MyBatis映射文件

  • 整合MyBatis到Spring环境中

    • SqlSesionFactoryBean

    • 数据源(druid+jdbc.properties)

    • 映射扫描

  1. Spring环境配置:applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"  xmlns:context="http://www.springframework.org/schema/context"  xmlns:tx="http://www.springframework.org/schema/tx"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

       <!--开启bean注解扫描-->
       <context:component-scan base-package="com.itheima"/>

    </beans>
  2. MyBatis映射文件:UserDao.xml 注意目录:resources\com\itheima\dao\UserDao.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
           PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
           "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.itheima.dao.UserDao">

       <!--添加-->
       <insert id="save" parameterType="user">
          insert into user(userName,password,realName,gender,birthday)values(#{userName},#{password},#{realName},#{gender},#{birthday})
       </insert>

       <!--删除-->
       <delete id="delete" parameterType="int">
          delete from user where uuid = #{uuid}
       </delete>

       <!--修改-->
       <update id="update" parameterType="user">
          update user set userName=#{userName},password=#{password},realName=#{realName},gender=#{gender},birthday=#{birthday} where uuid=#{uuid}
       </update>

       <!--查询单个-->
       <select id="get" resultType="user" parameterType="int">
          select * from user where uuid = #{uuid}
       </select>

       <!--分页查询-->
       <select id="getAll" resultType="user">
          select * from user
       </select>

       <!--登录-->
       <select id="getByUserNameAndPassword" resultType="user" >
          select * from user where userName=#{userName} and password=#{password}
       </select>

    </mapper>

    在UserDao中完成登录方法参数绑定:

    /**
    * 根据用户名密码查询个人信息
    *
    * @param userName 用户名
    * @param password 密码信息
    * @return
    */
    //注意:数据层操作不要和业务层操作的名称混淆,通常数据层仅反映与数据库间的信息交换,不体现业务逻辑
    public User getByUserNameAndPassword(
           @Param("userName") String userName,
           @Param("password") String password);
  3. 整合MyBatis到Spring环境中:applicationContext.xml

  • 数据源(druid+jdbc.properties)

    • SqlSesionFactoryBean

    • 映射扫描MapperScannerConfigurer

    <!--加载properties文件-->
    <context:property-placeholder location="classpath*:jdbc.properties"/>

    <!--数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
       <property name="driverClassName" value="${jdbc.driver}"/>
       <property name="url" value="${jdbc.url}"/>
       <property name="username" value="${jdbc.username}"/>
       <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--整合mybatis到spring中-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
       <property name="dataSource" ref="dataSource"/>
       <property name="typeAliasesPackage" value="com.itheima.domain"/>
    </bean>

    <!--映射扫描-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <property name="basePackage" value="com.itheima.dao"/>
    </bean>

 

配置分页插件与事务

  1. 添加分页插件pagehelper坐标

    <!--分页插件坐标-->
    <dependency>
     <groupId>com.github.pagehelper</groupId>
     <artifactId>pagehelper</artifactId>
     <version>5.1.2</version>
    </dependency>
  2. 配置分页插件pagehelper:applicationContext.xml

    • <property name="plugins"> -> setPlugins()

    • <property name="properties"> -> setProperties()

    <!--整合mybatis到spring中-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
       <property name="dataSource" ref="dataSource"/>
       <property name="typeAliasesPackage" value="com.itheima.domain"/>
       <!--分页插件-->
       <property name="plugins">
           <array>
               <bean class="com.github.pagehelper.PageInterceptor">
                   <property name="properties">
                       <props>
                           <prop key="helperDialect">mysql</prop>
                           <!--pageNum<=0 时会查询第一页,pageNum>pageSize(超过总数时),会查询最后一页-->
                           <prop key="reasonable">true</prop>
                       </props>
                   </property>
               </bean>
           </array>
       </property>
    </bean>
  3. 在UserServiceImpl中完善分页查询

   @Override
  public PageInfo<User> getAll(int page, int size) {
      PageHelper.startPage(page, size);
      List<User> all = userDao.getAll();
      return new PageInfo<User>(all);
  }
  1. 添加事务管理:applicationContext.xml

    • 确认已添加tx命名空间

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
             http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    • 配置事务管理器

      <!--开启注解式事务-->
      <tx:annotation-driven transaction-manager="txManager"/>

      <!--事务管理器-->
      <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
         <property name="dataSource" ref="dataSource"/>
      </bean>
    • 在Service类上或方法上添加@Transactional注解

      //类或接口上
      @Transactional(readOnly = true)
      //增删改方法上
      @Transactional(readOnly = false)

 

Spring整合SpringMVC

Part4:改为Web项目后,必须让tomcat启动时通过监听器加载Spring的配置文件:applicationContext.xml

  1. web.xml加载Spring配置文件

    <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>classpath*:applicationContext.xml</param-value>
    </context-param>

    <!--启动服务器时,通过监听器加载spring运行环境-->
    <listener>
       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
  2. Controller调用Service

    @RestController
    @RequestMapping("/user")
    public class UserController {

       @Autowired
       private UserService userService;

       @PostMapping
       public boolean save(User user){
           return userService.save(user);
      }

       @PutMapping
       public boolean update(User user){
           return userService.update(user);
      }

       @DeleteMapping("/{uuid}")
       public boolean delete(@PathVariable Integer uuid){
           return userService.delete(uuid);
      }

       @GetMapping("/{uuid}")
       public User get(@PathVariable Integer uuid){
           return userService.get(uuid);
      }

       @GetMapping("/{page}/{size}")
       public PageInfo<User> getAll(@PathVariable Integer page, @PathVariable Integer size){
           return userService.getAll(page,size);
      }

       @PostMapping("/login")
       public User login(String userName,String password){
           return userService.login(userName,password);
      }

    }

##

表现层数据封装(重点)

下面进行表现层数据封装,分为如下4个步骤:

  1. 返回数据格式设计

  1. 编写返回数据封装类:Result,添加get,set方法

    public class Result {
       // 操作结果编码
       private Integer code;
       // 操作数据结果
       private Object data;
       // 消息
       private String message;
       
       
       //添加get,set方法
       
       public Result(Integer code) {
           this.code = code;
      }
       public Result(Integer code, Object data) {
           this.code = code;
           this.data = data;
      }

       public Result(Integer code, String message) {
           this.code = code;
           this.message = message;
      }
    }
  2. 状态码常量可以根据自己的业务需求设定

    public class Code {
    //   操作结果编码
       public static final Integer SAVE_OK = 20011;
       public static final Integer UPDATE_OK = 20021;
       public static final Integer DELETE_OK = 20031;
       public static final Integer GET_OK = 20041;

       public static final Integer SAVE_ERROR = 20010;
       public static final Integer UPDATE_ERROR = 20020;
       public static final Integer DELETE_ERROR = 20030;
       public static final Integer GET_ERROR = 20040;

    //   系统错误编码

    //   操作权限编码

    //   校验结果编码

    }
  3. Controller 调用

    @RestController
    @RequestMapping("/user")
    public class UserController {

       @Autowired
       private UserService userService;

       @PostMapping
       public Result save(User user){
           boolean flag = userService.save(user);
           return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERROR);
      }

       @PutMapping
       public Result update(User user){
           boolean flag = userService.update(user);
           return new Result(flag ? Code.UPDATE_OK:Code.UPDATE_ERROR);
      }

       @DeleteMapping("/{uuid}")
       public Result delete(@PathVariable Integer uuid){
           boolean flag = userService.delete(uuid);
           return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERROR);
      }

       @GetMapping("/{uuid}")
       public Result get(@PathVariable Integer uuid){
           User user = userService.get(uuid);
           return new Result(null != user ?Code.GET_OK: Code.GET_ERROR,user);
      }

       @GetMapping("/{page}/{size}")
       public Result getAll(@PathVariable Integer page, @PathVariable Integer size){
           PageInfo<User> all = userService.getAll(page, size);
           return new Result(null != all ?Code.GET_OK: Code.GET_ERROR,all);
      }

       @PostMapping("/login")
       public Result login(String userName,String password){
           User user = userService.login(userName,password);
           return new Result(null != user ?Code.GET_OK: Code.GET_ERROR,user);
      }

    }

 

自定义异常

Part5-2:自定义异常处理

  1. 设定自定义业务异常,封装程序执行过程中出现的问题,便于表现层进行统一的异常拦截并进行处理

    在自定义异常类中添加错误编码属性code以及get方法

    public class BusinessException extends RuntimeException {
       
       //自定义异常中封装对应的错误编码,用于异常处理时获取对应的操作编码
       private Integer code;

       public Integer getCode() {
           return code;
      }

       public void setCode(Integer code) {
           this.code = code;
      }

       public BusinessException(Integer code) {
           this.code = code;
      }

       public BusinessException(String message, Integer code) {
           super(message);
           this.code = code;
      }

       public BusinessException(String message, Throwable cause,Integer code) {
           super(message, cause);
           this.code = code;
      }

       public BusinessException(Throwable cause,Integer code) {
           super(cause);
           this.code = code;
      }

       public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace,Integer code) {
           super(message, cause, enableSuppression, writableStackTrace);
           this.code = code;
      }
    }
  2. 自定义异常消息返回时需要与业务正常执行的消息按照统一的格式进行处理

    @GetMapping("/{uuid}")
    public Result get(@PathVariable Integer uuid){
       User user = userService.get(uuid);
       //模拟出现异常,使用条件控制,便于测试结果
       if (uuid == 10 ) throw new BusinessException("查询出错啦,请重试!",Code.GET_ERROR);
       return new Result(null != user ? Code.GET_OK: Code.GET_ERROR,user);
    }

     

返回消息兼容异常信息(重点)

在异常处理类中,将异常信息封装成Result对象:通过e.getCode()获取异常编码

@RestControllerAdvice
public class ProjectExceptionAdvice {

   @ExceptionHandler(BusinessException.class)
   //对出现异常的情况进行拦截,并将其处理成统一的页面数据结果格式
   public Result doBusinessException(BusinessException e) {
       return new Result(e.getCode(), e.getMessage());
  }

   @ExceptionHandler(Exception.class)
   public Result doOtherException(Exception e) {
       //redis发送真正的异常信息给运维人员e.getMessage()
       return new Result(500, "请稍候重试");
  }

}

 

posted @ 2021-03-27 20:42  java菜鸟儿  阅读(112)  评论(0编辑  收藏  举报