若文章内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系博主删除。
- 学习视频:黑马程序员 SSM 框架教程_Spring + SpringMVC + Maven高级 + SpringBoot + MyBatisPlus 企业实用开发技术
- 学习资料:https://pan.baidu.com/s/1eEOe5SRq1iTb1D8NKQPIfQ(提取码:1234)
资料中已经给了十分详细的笔记(Markdown 文档),此处只是汇集了一下(只是为了方便在线阅览,无其他用途)
我们一般使用 @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")@ResponseBodypublic String save(){System.out.println("user save ...");return "{'info':'springmvc'}";}
}
名称 | @ComponentScan |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置 Spring 配置类扫描路径,用于加载使用注解格式定义的 bean |
相关属性 | excludeFilters:排除扫描路径中加载的 bean,需要指定类别(type)和具体项(classes) includeFilters:加载指定的 bean,需要指定类别(type)和具体项(classes) |
@Controller
定义的 bean@Configuration
@ComponentScan(value="com.itheima",excludeFilters=@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)
)
public class SpringConfig { }
名称 | @RequestMapping |
---|---|
类型 | 类注解 或 方法注解 |
位置 | SpringMVC 控制器类或方法定义上方 |
作用 | 设置当前控制器方法请求访问路径 |
相关属性 | value(默认),请求访问路径 |
@RequestMapping
注解,前端发送请求的时候,要和两个注解的 value 值相加匹配才能访问到。@RequestMapping
注解 value 属性前面加不加 /
都可以。名称 | @RequestParam |
---|---|
类型 | 形参注解 |
位置 | SpringMVC 控制器方法形参定义前面 |
作用 | 绑定请求参数与处理器方法形参间的关系 |
相关参数 | required:是否为必传参数 defaultValue:参数默认值 |
@RequestParam
可以用来获取查询参数(比如 http://localhost/user?type=student
)。
此外,它还可以传递集合参数。
同名请求参数可以使用 @RequestParam
注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List likes){System.out.println("集合参数传递 likes ==> "+ likes);return "{'module':'list param'}";
}
SpringMVC 接收 JSON 数据的实现步骤为:
@EnableWebMvc
注解@RequestBody
注解名称 | @EnableWebMvc |
---|---|
类型 | 配置类注解 |
位置 | SpringMVC 配置类定义上方 |
作用 | 开启 SpringMVC 多项辅助功能 |
在 SpringMVC 的配置类中开启 SpringMVC 的注解支持,这里面就包含了将 JSON 转换成对象的功能。
@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc // 开启 json 数据类型自动转换
public class SpringMvcConfig { }
名称 | @RequestBody |
---|---|
类型 | 形参注解 |
位置 | SpringMVC 控制器方法形参定义前面 |
作用 | 将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次 |
使用 @RequestBody
注解将外部传递的 json 数组数据映射到形参的集合对象中作为数据
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List likes){System.out.println("list common(json)参数传递 list ==> "+likes);return "{'module':'list common for json param'}";
}
如果响应 json 的方法很多的话,那么每个方法都需要加上 @ResponseBody
注解,这样一来重复性太高了。
我们可以将 @ResponseBody
提到类上面,让所有的方法都有 @ResponseBody
的功能
@RequestBody
(但是可以有多个 @RequestParam
和 @PathVariable
)名称 | @PathVariable |
---|---|
类型 | 形参注解 |
位置 | SpringMVC 控制器方法形参定义前面 |
作用 | 绑定路径参数与处理器方法形参间的关系,要求 路径参数名 与 形参名 一一对应 |
@RequestMapping(value="/users/{id}", method = RequestMethod.DELETE)
@ReponseBody
public String delete(@PathVariable Integer id){ }
@RequestParam
用于接收 url 地址传参,表单传参【application/x-www-form-urlencoded
】@RequestBody
用于接收 json 数据【application/json类型的数据
】@RequestBody
应用较广@RequestParam
接收请求参数关于接收参数的注解,目前已经讲了三个注解了:@RequestBody
、@RequestParam
、@PathVariable
@RequestParam
用于接收 URL 地址传参或表单传参@RequestBody
用于接收 json 数据@PathVariable
用于接收路径参数,使用 {参数名称}
描述路径参数@RequestBody
应用较广@RequestParam
接收请求参数@PathVariable
接收请求路径变量,通常用于传递 id 值名称 | @ResponseBody |
---|---|
类型 | 方法注解 或 类注解 |
位置 | SpringMVC 控制器方法定义上方和控制类上 |
作用 | 设置当前控制器返回值作为响应体,写在类上,该类的所有方法都有该注解功能 |
相关属性 | pattern:指定日期时间格式字符串 |
说明:
@ReponseBody
功能@ReponseBody
注解后 此处用到了类型转换,内部是通过 Converter 接口的实现类完成的,它可以实现:
POJO -> json
)Collection -> json
)响应 POJO 集合对象
@Controller
public class UserController {@RequestMapping("/toJsonList")@ResponseBodypublic List toJsonList(){System.out.println("返回json集合数据");User user1 = new User();user1.setName("传智播客");user1.setAge(15);User user2 = new User();user2.setName("黑马程序员");user2.setAge(12);List userList = new ArrayList();userList.add(user1);userList.add(user2);return userList;}}
当我们想表示一个网络资源的时候,可以使用两种方式
http://localhost/user/getById?id=1
查询 id 为 1 的用户信息http://localhost/user/saveUser
保存用户信息http://localhost/user/1
http://localhost/user
传统方式一般是一个请求 URL 对应一种操作,这样做不仅麻烦,也不安全,因为会程序的人读取了你的请求 URL 地址,就大概知道该 URL 实现的是一个什么样的操作。查看 REST 风格的描述,你会发现请求地址变的简单了,并且光看请求 URL 并不是很能猜出来该 URL 的具体功能
总结以上情况,得出 REST 的优点
但是问题也随之而来,一个相同的 URL 地址即可以是新增也可以是修改或者查询,那么到底我们该如何区分该请求到底是什么操作呢?
http://localhost/users
:查询全部用户信息 GET(查询)http://localhost/users/1
:查询指定用户信息 GET(查询)http://localhost/users
:添加用户信息 POST(新增/保存)http://localhost/users
:修改用户信息 PUT(修改/更新)http://localhost/users/1
:删除用户信息 DELETE(删除)请求的方式比较多,但是比较常用的就4种,分别是 GET、POST、PUT、DELETE。
按照不同的请求方式代表不同的操作类型。
注意:上述行为是约定方式,约定不是规范,可以打破,所以称 REST 风格,而不是 REST 规范
清楚了什么是 REST 风格后,我们后期会经常提到一个概念叫 RESTful,那什么又是 RESTful 呢?
REST(Representational 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 |
---|---|
类型 | 类注解 |
位置 | 基于 SpringMVC 的 RESTful 开发控制器类定义上方 |
作用 | 设置当前控制器类为 RESTful 风格,等同于 @Controller 与 @ResponseBody 两个注解组合功能 |
@RestController
注解标注在类上,表明这是一个控制器 bean,直接将函数的返回值填入 HTTP 响应体中。
在 Rest每个方法的 @RequestMapping
注解中都要使用 method 属性定义请求方式,重复性太高。
使用 @GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
代替
名称 | @GetMapping + @PostMapping + @PutMapping + @DeleteMapping |
---|---|
类型 | 方法注解 |
位置 | 基于 SpringMVC 的 RESTful 开发控制器方法定义上方 |
作用 | 设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如 @GetMapping 对应 GET 请求 |
相关属性 | value(默认):请求访问路径 |
名称 | @RestControllerAdvice |
---|---|
类型 | 类注解 |
位置 | REST 风格开发的控制器增强类定义上方 |
作用 | 为 REST 风格开发的控制器类做增强 |
说明:此注解自带 @ResponseBody
注解与 @Component
注解,具备对应的功能
名称 | @ExceptionHandler |
---|---|
类型 | 方法注解 |
位置 | 专用于异常处理的控制器方法上方 |
作用 | 设置指定异常的处理方案,功能等同于控制器方法, 出现异常后终止原始控制器执行,并转入当前方法执行 |
说明:此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常。
@RestControllerAdvice
+ @RestControllerAdvice
@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, "系统繁忙,请稍后再试!");}
}
本小节没有和 SpringMVC 相关的新注解。
但是拦截器是非常重要的知识点,创建它也需要同时用到好几个注解,故写在本文中。
拦截器(Interceptor)是一种动态拦截方法调用的机制,在 SpringMVC 中动态拦截控制器方法的执行。
作用:在指定的方法调用前后执行预先设定的代码,阻止原始方法的执行。总结起来就是拦截器是用来做增强的。
拦截器和过滤器在作用和执行顺序上也很相似,那么二者间的区别是什么?
这两个知识点是很重要的,这里讲的比较简略。
下面推荐几篇介绍拦截器和过滤器的创建使用和区别的文章,诸位可自行观看。
- 【过滤器和拦截器有什么区别】
- 【SpringBoot 过滤器和拦截器详解及使用场景】
- 【过滤器与拦截器的 N 个区别】
可以只看第一篇。(反正第一篇的参考文章就是其下两篇)
首先创建一个拦截器,实现 HandlerInterceptor 接口,并重写接口中的相关方法。
之后可以将上面设置的普通拦截器注入到项目 配置类 中,并设置相应拦截规则。
也可以使用 配置器支持类 + 配置类 来避免被侵入的安全问题。
此外,上述的所有类,都必须被 SpringMVC 容器扫描到。
创建一个普通的拦截器,实现 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() 方法:
@Configuration
注解 + 继承 WebMvcConfigurationSupport 类
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {// 注入拦截器@Autowiredprivate ProjectInterceptor projectInterceptor;@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");}@Overrideprotected void addInterceptors(InterceptorRegistry registry) {// 配置拦截器registry.addInterceptor(projectInterceptor) // 添加拦截器.addPathPatterns("/books","/books/*" ); // 添加拦截地址}
}
@Configuration
+ @ComponentScan(SpringMvcSupport 包)
+ @EnableWebMvc
@Configuration
@ComponentScan({"com.itheima.controller","com.itheima.config"})
@EnableWebMvc
public class SpringMvcConfig{ }
将上面设置的普通拦截器注入到项目配置类中,并设置相应拦截规则
注意:配置类直接实现 WebMvcConfigurer 接口可以简化开发,但具有一定的侵入性
@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {@Autowiredprivate ProjectInterceptor projectInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 配置多拦截器registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");}
}
此后就无需 SpringMvcSupport 类了。
原始方法之前运行 preHandle
public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {System.out.println("preHandle");return true;
}
使用 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;
}
原始方法运行后运行,如果原始方法被拦截,则不执行
public void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView) throws Exception {System.out.println("postHandle");
}
前三个参数和上面的是一致的。
因为这里现在都是返回 json 数据,所以该参数的使用率不高。
拦截器最后执行的方法,无论原始方法是否执行
public void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) throws Exception {System.out.println("afterCompletion");
}
前三个参数与上面的是一致的。
因为这里现在已经有全局异常处理器类了,所以该参数的使用率也不高。
在上述的三个方法中,最常用的是 preHandle(),在这个方法中可以通过返回值来决定是否要进行放行。
我们可以把业务逻辑放在该方法中,如果满足业务则返回 true 放行,不满足则返回 false 拦截。