SpringMVC 相关注解【学习笔记】


若文章内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系博主删除。



资料中已经给了十分详细的笔记(Markdown 文档),此处只是汇集了一下(只是为了方便在线阅览,无其他用途)


1.Bean 的加载控制


@Controller


我们一般使用 @Autowired 注解让 Spring 容器帮我们自动装配 bean。该注解根据类型查找容器中的 bean

如果在容器中存在多个相同类型的 bean,注入参数的属性名又和容器中 bean 的名称不一致,此时就需要借助 @Qualifier 注解来指定注入哪个名称的 bean 对象。@Qualifier 注解后的值就是需要注入的 bean 的名称。

@Autowired
@Qualifier("bookDao1")
private BookDao bookDao;
  • 注意@Qualifier 不能独立使用,必须和 @Autowired 一起使用。

@Component 是一个通用的注解,可标注任意类为 Spring 组件。

如果一个 bean 不知道属于哪个层的时候,可以使用 @Component 注解标注。

  • 注意@Component 注解是不可以添加在接口上的,因为接口是无法创建对象的。

对于 @Component 注解,还衍生出了其他三个注解 @Controller@Service@Repository

方便我们后期在编写类的时候能很好的区分出这个类是属于 表现层业务层 还是 数据层 的类。


@Configuration 注解一般用来声明配置类,可以使用 @Component 注解替代。


名称@Controller
类型类注解
位置SpringMVC 控制器类定义上方
作用设定 SpringMVC 的核心控制器 bean
  • @Controller + @RequestMapping + @ResponseBody
@Controller
public class UserController {
    @RequestMapping("/save")
    @ResponseBody
    public String save(){
        System.out.println("user save ...");
        return "{'info':'springmvc'}";
    }
}

@ComponentScan


名称@ComponentScan
类型类注解
位置类定义上方
作用设置 Spring 配置类扫描路径,用于加载使用注解格式定义的 bean
相关属性excludeFilters:排除扫描路径中加载的 bean,需要指定类别(type)和具体项(classes

includeFilters:加载指定的 bean,需要指定类别(type)和具体项(classes
  • excludeFilters 属性:设置扫描加载 bean 时,排除的过滤规则
  • type 属性:设置排除规则,当前使用按照 bean 定义时的注解类型进行排除
    • ANNOTATION:按照注解排除
    • ASSIGNABLE_TYPE:按照指定的类型过滤
    • ASPECTJ:按照 Aspectj 表达式排除,基本上不会用
    • REGEX:按照正则表达式排除
    • CUSTOM:按照自定义规则排除
  • classes 属性:设置排除的具体注解类,当前设置排除 @Controller 定义的 bean
@Configuration
@ComponentScan(value="com.itheima",
    excludeFilters=@ComponentScan.Filter(
    	type = FilterType.ANNOTATION,
        classes = Controller.class
    )
)
public class SpringConfig { }

2.请求与响应


@RequestMapping


名称@RequestMapping
类型类注解方法注解
位置SpringMVC 控制器类或方法定义上方
作用设置当前控制器方法请求访问路径
相关属性value(默认),请求访问路径
  • 注意
    • 当类上和方法上都添加了 @RequestMapping 注解,前端发送请求的时候,要和两个注解的 value 值相加匹配才能访问到。
    • @RequestMapping 注解 value 属性前面加不加 / 都可以。

@RequestParam


名称@RequestParam
类型形参注解
位置SpringMVC 控制器方法形参定义前面
作用绑定请求参数与处理器方法形参间的关系
相关参数required:是否为必传参数

defaultValue:参数默认值

@RequestParam 可以用来获取查询参数(比如 http://localhost/user?type=student)。

此外,它还可以传递集合参数。

在这里插入图片描述

同名请求参数可以使用 @RequestParam 注解映射到对应名称的集合对象中作为数据

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

传递参数是 JSON 数据


SpringMVC 接收 JSON 数据的实现步骤为:

  • 导入 jackson
  • 使用 PostMan 发送 JSON 数据
  • 开启 SpringMVC 注解驱动,在配置类上添加 @EnableWebMvc 注解
  • Controller 方法的参数前添加 @RequestBody 注解

@EnableWebMvc


名称@EnableWebMvc
类型配置类注解
位置SpringMVC 配置类定义上方
作用开启 SpringMVC 多项辅助功能

SpringMVC 的配置类中开启 SpringMVC 的注解支持,这里面就包含了将 JSON 转换成对象的功能。

@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc // 开启 json 数据类型自动转换
public class SpringMvcConfig { }

@RequestBody


名称@RequestBody
类型形参注解
位置SpringMVC 控制器方法形参定义前面
作用将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次

在这里插入图片描述

使用 @RequestBody 注解将外部传递的 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'}";
}

如果响应 json 的方法很多的话,那么每个方法都需要加上 @ResponseBody 注解,这样一来重复性太高了。

我们可以将 @ResponseBody 提到类上面,让所有的方法都有 @ResponseBody 的功能

  • 注意:一个请求方法只可以有一个 @RequestBody(但是可以有多个 @RequestParam@PathVariable

@PathVariable


名称@PathVariable
类型形参注解
位置SpringMVC 控制器方法形参定义前面
作用绑定路径参数与处理器方法形参间的关系,要求 路径参数名形参名 一一对应
@RequestMapping(value="/users/{id}", method = RequestMethod.DELETE)
@ReponseBody
public String delete(@PathVariable Integer id){ }

@RequestBody@RequestParam


  • 区别
    • @RequestParam 用于接收 url 地址传参,表单传参【application/x-www-form-urlencoded
    • @RequestBody 用于接收 json 数据【application/json类型的数据

  • 应用
    • 后期开发中,发送 json 格式数据为主,@RequestBody 应用较广
    • 如果发送非 json 格式数据,选用 @RequestParam 接收请求参数

@RequestBody@RequestParam@PathVariable


关于接收参数的注解,目前已经讲了三个注解了:@RequestBody@RequestParam@PathVariable


  • 区别
    • @RequestParam 用于接收 URL 地址传参或表单传参
    • @RequestBody 用于接收 json 数据
    • @PathVariable 用于接收路径参数,使用 {参数名称} 描述路径参数

  • 应用
    • 后期开发中,发送请求参数超过 1 个时,以 json 格式为主,@RequestBody 应用较广
    • 如果发送非 json 格式数据,选用 @RequestParam 接收请求参数
    • 采用 RESTful 进行开发,当参数数量较少时,例如 1 个,可以采用 @PathVariable 接收请求路径变量,通常用于传递 id

@ResponseBody(响应数据是 JSON 数据)


名称@ResponseBody
类型方法注解类注解
位置SpringMVC 控制器方法定义上方和控制类上
作用设置当前控制器返回值作为响应体,写在类上,该类的所有方法都有该注解功能
相关属性pattern:指定日期时间格式字符串

说明:

  • 该注解可以写在类上或者方法上
  • 写在类上就是该类下的所有方法都有 @ReponseBody 功能
  • 当方法上有 @ReponseBody 注解后
    • 方法的返回值为字符串,会将其作为文本内容直接响应给前端
    • 方法的返回值为对象,会将对象转换成 JSON 响应给前端

此处用到了类型转换,内部是通过 Converter 接口的实现类完成的,它可以实现:

  • 对象转 JSON 数据(POJO -> json
  • 集合转 JSON 数据(Collection -> json

在这里插入图片描述

响应 POJO 集合对象

@Controller
public class UserController {
    
    @RequestMapping("/toJsonList")
    @ResponseBody
    public List<User> toJsonList(){
        System.out.println("返回json集合数据");
        User user1 = new User();
        user1.setName("传智播客");
        user1.setAge(15);

        User user2 = new User();
        user2.setName("黑马程序员");
        user2.setAge(12);

        List<User> userList = new ArrayList<User>();
        userList.add(user1);
        userList.add(user2);

        return userList;
    }
    
}

3.Rest 风格


REST 简介


当我们想表示一个网络资源的时候,可以使用两种方式

  • 传统风格 资源 描述形式
    • http://localhost/user/getById?id=1 查询 id 为 1 的用户信息
    • http://localhost/user/saveUser 保存用户信息
  • REST 风格 描述形式
    • http://localhost/user/1
    • http://localhost/user

传统方式一般是一个请求 URL 对应一种操作,这样做不仅麻烦,也不安全,因为会程序的人读取了你的请求 URL 地址,就大概知道该 URL 实现的是一个什么样的操作。查看 REST 风格的描述,你会发现请求地址变的简单了,并且光看请求 URL 并不是很能猜出来该 URL 的具体功能

总结以上情况,得出 REST 的优点

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

但是问题也随之而来,一个相同的 URL 地址即可以是新增也可以是修改或者查询,那么到底我们该如何区分该请求到底是什么操作呢?

  • 按照 REST 风格访问资源时使用行为动作区分对资源进行了何种操作
    • http://localhost/users:查询全部用户信息 GET(查询)
    • http://localhost/users/1:查询指定用户信息 GET(查询)
    • http://localhost/users:添加用户信息 POST(新增/保存)
    • http://localhost/users:修改用户信息 PUT(修改/更新)
    • http://localhost/users/1:删除用户信息 DELETE(删除)

请求的方式比较多,但是比较常用的就4种,分别是 GETPOSTPUTDELETE

按照不同的请求方式代表不同的操作类型。

  • 发送 GET 请求是用来做查询
  • 发送 POST 请求是用来做新增
  • 发送 PUT 请求是用来做修改
  • 发送 DELETE 请求是用来做删除

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

  • REST 提供了对应的架构方式,按照这种架构设计项目可以降低开发的复杂性,提高系统的可伸缩性
  • REST 中规定 GET/POST/PUT/DELETE 针对的是 查询/新增/修改/删除,但是我们如果非要用 GET 请求做删除,这点在程序上运行是可以实现的
  • 但是如果绝大多数人都遵循这种风格,你写的代码让别人读起来就有点莫名其妙了。
  • 描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:usersbooksaccounts

清楚了什么是 REST 风格后,我们后期会经常提到一个概念叫 RESTful,那什么又是 RESTful 呢?

RESTRepresentational State Transfer),表现形式状态转换,它是一种软件架构风格

后期我们在进行开发的过程中,大多是都是遵从 REST 风格来访问我们的后台服务,所以可以说咱们以后都是基于 RESTful 来进行开发的。


具体代码风格


设定 HTTP 请求动作(动词)

@RequestMapping(value="",method = RequestMethod.POST|GET|PUT|DELETE)

设定请求参数(路径变量)

@RequestMapping(value="/users/{id}",method = RequestMethod.DELETE)
@ReponseBody
public String delete(==@PathVariable== Integer id){ }

方法形参和路径参数不一致


如果方法形参的名称和路径 {} 中的值不一致,该怎么办?

在这里插入图片描述


@RestController@Controller + @ResponseBody


使用 @RestController 注解替换 @Controller@ResponseBody 注解,简化书写

名称@RestController
类型类注解
位置基于 SpringMVCRESTful 开发控制器类定义上方
作用设置当前控制器类为 RESTful 风格,等同于 @Controller@ResponseBody 两个注解组合功能

@RestController 注解标注在类上,表明这是一个控制器 bean,直接将函数的返回值填入 HTTP 响应体中。


@XXXMapping(处理常见的 HTTP 请求类型)


Rest每个方法的 @RequestMapping 注解中都要使用 method 属性定义请求方式,重复性太高。

使用 @GetMapping@PostMapping@PutMapping@DeleteMapping 代替

名称@GetMapping + @PostMapping + @PutMapping + @DeleteMapping
类型方法注解
位置基于 SpringMVCRESTful 开发控制器方法定义上方
作用设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如 @GetMapping 对应 GET 请求
相关属性value(默认):请求访问路径

4.统一异常处理


@RestControllerAdvice


名称@RestControllerAdvice
类型类注解
位置REST 风格开发的控制器增强类定义上方
作用REST 风格开发的控制器类做增强

说明:此注解自带 @ResponseBody 注解与 @Component 注解,具备对应的功能

在这里插入图片描述


@ExceptionHandler


名称@ExceptionHandler
类型方法注解
位置专用于异常处理的控制器方法上方
作用设置指定异常的处理方案,功能等同于控制器方法,
出现异常后终止原始控制器执行,并转入当前方法执行

说明:此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常。


@RestControllerAdvice + @ExceptionHandler

  • 备注:这里的 Result 就是一个统一结果封装类
@RestControllerAdvice // 用于标识当前类为 REST 风格对应的异常处理器
public class ProjectExceptionAdvice {
    @ExceptionHandler(SystemException.class) // 用于设置当前处理器类对应的异常类型
    public Result doSystemException(SystemException ex){
        // 记录日志
        // 发送消息给运维
        // 发送邮件给开发人员,ex 对象发送给开发人员
        return new Result(ex.getCode(),null,ex.getMessage());
    }

    @ExceptionHandler(BusinessException.class)
    public Result doBusinessException(BusinessException ex){
        return new Result(ex.getCode(),null,ex.getMessage());
    }

    // 除了自定义的异常处理器,该注解也可以保留对 Exception 类型的异常处理,用于处理非预期的异常
    @ExceptionHandler(Exception.class)
    public Result doOtherException(Exception ex){
        // 记录日志
        // 发送消息给运维
        // 发送邮件给开发人员,ex 对象发送给开发人员
        return new Result(Code.SYSTEM_UNKNOW_ERR, null, "系统繁忙,请稍后再试!");
    }
}

5.拦截器


本小节没有和 SpringMVC 相关的新注解。

但是拦截器是非常重要的知识点,创建它也需要同时用到好几个注解,故写在本文中。


5.1.简单介绍


拦截器Interceptor)是一种动态拦截方法调用的机制,在 SpringMVC 中动态拦截控制器方法的执行。

作用:在指定的方法调用前后执行预先设定的代码,阻止原始方法的执行。总结起来就是拦截器是用来做增强的。

在这里插入图片描述


拦截器过滤器在作用和执行顺序上也很相似,那么二者间的区别是什么?

  • 归属不同:Filter 属于 Servlet 技术,Interceptor 属于 SpringMVC 技术
  • 拦截内容不同:Filter 对 所有访问进行增强,Interceptor 仅针对 SpringMVC 的访问进行增强

在这里插入图片描述

这两个知识点是很重要的,这里讲的比较简略。

下面推荐几篇介绍拦截器过滤器的创建使用和区别的文章,诸位可自行观看。


可以只看第一篇。(反正第一篇的参考文章就是其下两篇)


5.2.拦截器开发


首先创建一个拦截器,实现 HandlerInterceptor 接口,并重写接口中的相关方法。

之后可以将上面设置的普通拦截器注入到项目 配置类 中,并设置相应拦截规则。

也可以使用 配置器支持类 + 配置类 来避免被侵入的安全问题。

此外,上述的所有类,都必须被 SpringMVC 容器扫描到。


5.2.1.创建拦截器类


创建一个普通的拦截器,实现 HandlerInterceptor 接口,并重写接口中的相关方法。

此外,拦截器类是必须被 SpringMVC 容器扫描到的,故需要加上 @Component 注解

总结@Component 注解 + 实现 HandlerInterceptor 接口,并重写接口中的方法

// 定义拦截器类,实现 HandlerInterceptor 接口
// 注意:当前类必须受 Spring 容器控制,否则没有意义

@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...");
    }
}

拦截器中的 preHandler() 方法:

  • 如果返回 true,则代表放行,会执行原始 Controller 类中要请求的方法;
  • 如果返回 false,则代表拦截,后面的就不会再执行了。

5.2.2.配置拦截器类


@Configuration 注解 + 继承 WebMvcConfigurationSupport

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
	// 注入拦截器
    @Autowired
    private ProjectInterceptor projectInterceptor;

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

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        // 配置拦截器
        registry.addInterceptor(projectInterceptor) // 添加拦截器
        		.addPathPatterns("/books","/books/*" ); // 添加拦截地址
    }
}

5.2.3.SpringMVC 配置文件添加 SpringMvcSupport 包扫描


@Configuration + @ComponentScan(SpringMvcSupport 包) + @EnableWebMvc

@Configuration
@ComponentScan({"com.itheima.controller","com.itheima.config"})
@EnableWebMvc
public class SpringMvcConfig{ }

5.2.4.简化 SpringMvcSupport 的编写


将上面设置的普通拦截器注入到项目配置类中,并设置相应拦截规则

注意:配置类直接实现 WebMvcConfigurer 接口可以简化开发,但具有一定的侵入性

@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
    @Autowired
    private ProjectInterceptor projectInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 配置多拦截器
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
    }
}

此后就无需 SpringMvcSupport 类了。


5.2.5.拦截器的执行流程


在这里插入图片描述


5.3 拦截器参数


5.3.1 前置处理方法


原始方法之前运行 preHandle

public boolean preHandle(HttpServletRequest request,
                         HttpServletResponse response,
                         Object handler) throws Exception {
    System.out.println("preHandle");
    return true;
}
  • request:请求对象
  • response:响应对象
  • handler:被调用的处理器对象,本质上是一个方法对象,对反射中的 Method 对象进行了再包装

使用 request 对象可以获取请求数据中的内容,如获取请求头的 Content-Type

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String contentType = request.getHeader("Content-Type");
    System.out.println("preHandle..." + contentType);
    return true;
}

使用 handler 参数,可以获取方法的相关信息

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    HandlerMethod hm = (HandlerMethod) handler;
    String methodName = hm.getMethod().getName(); // 可以获取方法的名称
    System.out.println("preHandle..." + methodName);
    return true;
}

5.3.2 后置处理方法


原始方法运行后运行,如果原始方法被拦截,则不执行

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

前三个参数和上面的是一致的。

  • modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整。

因为这里现在都是返回 json 数据,所以该参数的使用率不高。


5.3.3 完成处理方法


拦截器最后执行的方法,无论原始方法是否执行

public void afterCompletion(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler,
                            Exception ex) throws Exception {
    System.out.println("afterCompletion");
}

前三个参数与上面的是一致的。

  • ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理

因为这里现在已经有全局异常处理器类了,所以该参数的使用率也不高。


在上述的三个方法中,最常用的是 preHandle(),在这个方法中可以通过返回值来决定是否要进行放行。

我们可以把业务逻辑放在该方法中,如果满足业务则返回 true 放行,不满足则返回 false 拦截。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值