• SpringMVC


    学习流程图

    四个学习模块:

    1、SpringMVC入门

    2、请求与响应

    3、rest风格

     

    4、ssm整合

     

    5、拦截器

    第一章、SpringMVC入门

    1、简介

     SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架。

               

    SpringMVC的开发步骤:

                                ① 导入SpringMVC相关坐标

                                ② 配置SpringMVC核心控制器DispathcerServlet

                                ③ 创建Controller类和视图页面

                                ④ 使用注解配置Controller类中业务方法的映射地址

                                ⑤ 配置SpringMVC核心文件 spring-mvc.xml

                                ⑥ 客户端发起请求测试 

    2、SpringMVC入门案例

     

    1、导入servlet-api与spring-webmvc依赖坐标

    1. javax.servlet
    2. javax.servlet-api
    3. 3.1.0
    4. provided
    5. org.springframework
    6. spring-webmvc
    7. 5.2.1.RELEASE

    2、创建SpringMVC控制器类(等同于Servlet接受请求与响应)

    1. @Controller //定义spring的bean
    2. public class UserController {
    3. @RequestMapping("/save")// 设置当前操作的访问URL路径
    4. @ResponseBody //设置以下的方法是执行响应的方法
    5. // 定义具体的处理请求操作,向外返回JSON数据
    6. public String save(){
    7. System.out.println("此处是处理请求的方法");
    8. return "{'module':'springmvc'}";
    9. }
    10. }

    3、创建spring配置类,扫描bean

    1. //创建spring配置类,扫描controller对应的bean
    2. @Configuration
    3. @ComponentScan("com.itheima.controller")
    4. public class SpringmvcConfig {
    5. }

    4、初始化Servlet容器,加载配置类,设置请求方法

    创建一个web容器并加载spring配置类完成注册;设置请求映射路径,直接”/“表示全部

    1. //定义一个Servlet容器启动的配置类,在里面加载spring的配置
    2. public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
    3. //让Tomcat能够加载SpringMVC容器的配置
    4. @Override
    5. protected WebApplicationContext createServletApplicationContext() {
    6. AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
    7. ctx.register(SpringmvcConfig.class);
    8. return ctx;
    9. }
    10. //设置哪些请求归属SpringMVC处理
    11. @Override
    12. protected String[] getServletMappings() {
    13. return new String[]{"/"};
    14. //设置所有请求归属SpringMVC处理
    15. }
    16. @Override //加载spring配置类的
    17. protected WebApplicationContext createRootApplicationContext() {
    18. return null;
    19. }
    20. }

    三个实现方法详解:

    5、总结:

    <基本目录>

    <常用注解>

    @Controller //类注解:将该类定义为spring的bean;

    @RequestMapping("/save")// 方法注解:写在方法上面,设置当前方法的访问URL路径,         可类比  Servlet开发中web.xml配置的servlet-mapping标签作用;

    @ResponseBody //方法注解:设置以下的方法的返回值作为响应体数据反馈给浏览器;

    3、SpringMVC工作流程分析

    《1》容器初始化 (ServletContext对象看做Web程序实例化对象)

    《2》单次请求

    4、controller加载控制

    SpringmvcConfig配置类和SpringConfig配置类功能一样,就是扫描bean,只是扫描的范围不同。但是存在着重复扫描加载,那bean就不晓得听谁话了!

    《1》问题:

     《2》解决方案:

    方案二中SpringConfig配置类

    1. @Configuration
    2. //@ComponentScan({"com.itheima.dao","com.itheima.service"})//设定精确包扫描范围
    3. @ComponentScan(
    4. value = "com.itheima",
    5. excludeFilters = @ComponentScan.Filter(
    6. type = FilterType.ANNOTATION,
    7. classes = Controller.class
    8. )
    9. )
    10. //解释:我去扫描包com.itheima里面的所有的包,设置过滤器,按照注解排除,排除掉指定classes的包
    11. public class SpringConfig {
    12. }

    SpringMvcConfig配置类

    1. @Configuration
    2. @ComponentScan("com.itheima.controller")
    3. public class SpringMvcConfig {
    4. }

    Servlet容器配置类 ServletContainersInitConfig :加载Spring与SpringMVC容器

    1. public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
    2. // springMVC容器加载配置
    3. @Override
    4. protected WebApplicationContext createServletApplicationContext() {
    5. AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
    6. ctx.register(SpringMvcConfig.class);
    7. return ctx;
    8. }
    9. // Spring容器加载配置
    10. @Override
    11. protected WebApplicationContext createRootApplicationContext() {
    12. AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
    13. ctx.register(SpringConfig.class);
    14. return ctx;
    15. }
    16. @Override
    17. protected String[] getServletMappings() {
    18. return new String[]{"/"};
    19. }
    20. }

    5、PostMan

    网页调试与发送http请求的插件。

    作用:常用于接口测试。

    第二章、请求与响应

    1、请求映射路径

    在类上统一设置当前控制器方法请求访问路径前缀。

    1. @Controller
    2. @RequestMapping("/user") //访问路径前缀 ,目的是区别不同的controller类
    3. public class UserController {
    4. @RequestMapping("/save") //具体的方法的请求路径
    5. @ResponseBody
    6. public String save(){
    7. System.out.println("此处是usercontroller类中的save方法");
    8. return "{'我要吃饭了':'真的好饿'}";
    9. }
    10. @RequestMapping("/delete")
    11. @ResponseBody
    12. public String delete(){
    13. System.out.println("此处是us二controller内的delete方法");
    14. return "{'我真的要饿死了':'一点也不好玩'";
    15. }
    16. }

     

    2、请求参数

    直接在方法内写入形式参数即可。在发出请求的时候输入参数就会传入。

    《1、普通参数》

    请求url(?表示接下来的是参数  使用&分隔不同的参数)

    http://localhost:8080/springMVC_02_bean_load_war/commonParam?name=我是你爹 & age=100

            方法写入形参

    URL中参数名方法内的形参名会自动映射,相同则直接传参。 如果不匹配使用@RequestParam(”里面写上前端的对应的名称“)

    1. @RequestMapping("/commonParam")
    2. @ResponseBody
    3. public String commonParam(String name,int age,String text){
    4. System.out.println("接收到的普通参数name是:=============》" + name);
    5. System.out.println("接收到的普通参数age是:=============》" + age);
    6. System.out.println("接收到的普通参数text是:=============》" + text);
    7. return "{'module':'commonParam'}";
    8. }

            post请求参数乱码处理

    在 Web容器配置类ServletContainersInitConfig类中使用过滤器处理

    1. //乱码处理 重写getServletFilters()方法
    2. @Override
    3. protected Filter[] getServletFilters() {
    4. CharacterEncodingFilter filter = new CharacterEncodingFilter();
    5. filter.setEncoding("UTF-8");
    6. return new Filter[] {filter};
    7. }

    post请求方法----参数在请求体

    《2、POJO参数》

    实际开发中,是接收大量参数组成一个实体类

    定义一个实体类,声明属性、toString()以及setter()&getter()

    将此实体类对象作为方法内的 形参

    1. //POJO参数
    2. @RequestMapping("/pojoParam")
    3. @ResponseBody
    4. public String pojoParam(User user){
    5. System.out.println("pojo参数传递user --》" + user);
    6. return "{'module':'pojo param'}";
    7. }

    基于本框架,前端页面传的所有参数属性,会自动根据名称和实体类的属性匹配、赋值。

    《3、POJO嵌套》

    对应引用类型传参,正常在后端就正常定义实体类就行了。

    但在前端传参的时候,aa.bb 前者是引用对象实例,后者是传参属性

    《4、数组参数》

    对于数组参数,只需要在前端传参时将名称全部写为数组名即可。

    1. //数组参数
    2. @RequestMapping("/arrayParam")
    3. @ResponseBody
    4. public String arrayParam(String[] arr){
    5. System.out.println("数组参数传递为:" + Arrays.toString(arr));
    6. return "{'module':'arrayparam'";
    7. }

    《5、集合参数》

    1. //集合参数
    2. @RequestMapping("/listParam")
    3. @ResponseBody
    4. public String listParam(@RequestParam List likes){
    5. System.out.println("数组参数传递为:" + likes);
    6. return "{'module':'arrayparam'";
    7. }

    3、日期类型参数传递

     

    4、JSON数据传参

    基本四步骤:

    1、添加依赖坐标 --> 2、SpringMVC配置类添加注解  --> 3、注意前端传参改为JSON数据 --> 4、方法内形参前添加注解 @RequestBody(接收json数据)

    在SpringMVC配置类SpringMvcConfig中添加注解 实现前端JSON数据可以转化为被后端接收的数据

    @EnableWebMvc //开启由JSON数据转换为List集合数据

    《1、集合参数:json格式》 

    1. //集合参数 JSON数据格式
    2. @RequestMapping("/listParamForJson")
    3. @ResponseBody //由于传过来的JSON数据在请求体内,所以参数要使用RequestBody注解
    4. public String listParamForJson(@RequestBody List likes){
    5. System.out.println("数组参数传递为:" + likes);
    6. return "{'module':'list common for json param'}";
    7. }

    《2、pojo参数:json格式》

    1. //pojo参数:json格式
    2. @RequestMapping("/pojoParamForJson")
    3. @ResponseBody //由于传过来的JSON数据在请求体内,所以参数要使用RequestBody注解-把请求体内的json数据塞到参数中
    4. public String pojoParamForJson(@RequestBody User user){
    5. System.out.println("数组参数传递为:" + user);
    6. return "{'module':'list common for json param'}";
    7. }

    《3、集合对象参数:json格式》

    1. //集合pojo参数:json格式
    2. @RequestMapping("/listpojoParamForJson")
    3. @ResponseBody //由于传过来的JSON数据在请求体内,所以参数要使用RequestBody注解-把请求体内的json数据塞到参数中
    4. public String listpojoParamForJson(@RequestBody List list){
    5. System.out.println("数组参数传递为:" + list);
    6. return "{'module':'list common for json param'}";
    7. }

     

    5、响应

    @ResponseBody 作用:设置当前控制器返回值为响应体

    响应页面--返回页面文件名

    1. //响应页面、跳转页面
    2. @RequestMapping("/toJumpPage")
    3. public String toJumpPage(){
    4. System.out.println("成功跳转页面");
    5. return "page.jsp";
    6. }

    响应数据--文本数据

    文本数据要放在响应体内!!!使用 @ResponseBody 标明!!!1

    1. // 响应文本数据
    2. @RequestMapping("/toText")
    3. @ResponseBody
    4. public String toText(){
    5. System.out.println("响应返回文本数据");
    6. return "Response text";
    7. }

    响应数据--json数据

    json数据要放在响应体内

    1. // 响应pojo对象
    2. @RequestMapping("toJsonPOJO")
    3. @ResponseBody
    4. public User toJsonPOJO(){
    5. System.out.println("响应返回json对象数据");
    6. User user = new User();
    7. user.setName("我是你大爷的太祖宗");
    8. user.setAge(100);
    9. return user;
    10. }
    11. // 响应pojo对象
    12. @RequestMapping("toJsonlistPOJO")
    13. @ResponseBody
    14. public List toJsonlistPOJO(){
    15. System.out.println("响应返回json对象数据");
    16. User user1 = new User();
    17. user1.setName("我是你大爷的太太太太太太祖宗");
    18. user1.setAge(100);
    19. User user2 = new User();
    20. user2.setName("我是你大爷的怎嫩嗯嗯嗯嗯嗯嗯嗯嗯祖宗");
    21. user2.setAge(500);
    22. User user3 = new User();
    23. user3.setName("我是你大爷的顺丰到付大幅度太祖宗");
    24. user3.setAge(1000);
    25. List users = new ArrayList<>();
    26. users.add(user1);
    27. users.add(user2);
    28. users.add(user3);
    29. return users;
    30. }

     HttpMessageConverter接口:将文本类型、集合等数据转换为json数据类型

    第三章、REST风格

    1、REST简介

    REST见文知意,他的目的就是程序员约定俗成的”规范“。也就是主流的代码风格。

    《1》REST风格就是描述资源的访问形式

    《2》REST 内部通过访问资源时 行为方式 区分资源操作方式。

    资源操作方式=资源路径 + 请求方式。

    在实际开发中,@RequestMapping 的路径要改为类名 ;并且指定访问方法

    1. @RequestMapping(value = "/users",method = RequestMethod.POST) //具体的方法的请求路径
    2. @ResponseBody
    3. public String save(){
    4. System.out.println("此处是usercontroller类中的save方法");
    5. return "{'我要吃饭了':'真的好饿'}";
    6. }

    传参:

    value要指定参数路径位置/路径/{参数名};形参要添加@PathVariable 表示参数从路径中得到

    1. @RequestMapping(value = "/users",method = RequestMethod.POST) //具体的方法的请求路径
    2. @ResponseBody
    3. public String save(){
    4. System.out.println("此处是usercontroller类中的save方法");
    5. return "{'我要吃饭了':'真的好饿'}";
    6. }
    7. @RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
    8. @ResponseBody
    9. public String delete(@PathVariable Integer id){
    10. System.out.println("此处是us二controller内的delete方法" + id);
    11. return "{'我真的要饿死了':'一点也不好玩'";
    12. }
    13. @RequestMapping(value = "/users",method = RequestMethod.PUT)
    14. @ResponseBody
    15. public String update(@RequestBody User user){
    16. System.out.println("此处是us二controller内的update方法" + user);
    17. return "{'module':'erertrgtrt'}";
    18. }

    以后开发中,基本上都是将数据封装成pojo,然后通过json串传递。

     

    2、RESTful入门案例

    1. @RequestMapping(value = "/users",method = RequestMethod.POST) //具体的方法的请求路径
    2. @ResponseBody
    3. public String save(){
    4. System.out.println("此处是usercontroller类中的save方法");
    5. return "{'我要吃饭了':'真的好饿'}";
    6. }
    7. @RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
    8. @ResponseBody
    9. public String delete(@PathVariable Integer id){
    10. System.out.println("此处是us二controller内的delete方法" + id);
    11. return "{'我真的要饿死了':'一点也不好玩'";
    12. }
    13. @RequestMapping(value = "/users",method = RequestMethod.PUT)
    14. @ResponseBody
    15. public String update(@RequestBody User user){
    16. System.out.println("此处是us二controller内的update方法" + user);
    17. return "{'module':'erertrgtrt'}";
    18. }

    3、REST快速开发(简化代码)

    对于相同的注解、mapping,直接在类前面写。

    @RestController = @Controller + @ResponseBody

    对于映射直接写请求方法+mapping,需要单独添加参数后面加。

    1. @RestController
    2. @RequestMapping("/books")
    3. public class BookController {
    4. @PostMapping
    5. public String save(@RequestBody Book book) {
    6. System.out.println("此处是控制层save方法");
    7. return "{'module':'你爹爹的}";
    8. }
    9. @DeleteMapping("/{id}")
    10. public String delete(@PathVariable Integer id) {
    11. System.out.println("此处是delete方法接收到id为" + id);
    12. return "{'module':'你爹爹的}";
    13. }
    14. @PutMapping
    15. public String update(@RequestBody Book book) {
    16. System.out.println("此处是控制层update方法更新的数据为" + book );
    17. return "{'module':'update'}";
    18. }
    19. @GetMapping("/{id}")
    20. public String getById(@PathVariable Integer id) {
    21. System.out.println("此处是控制层得到的id的数据为" + id );
    22. return "{'module':'getbyid'}";
    23. }

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

    实现功能:前端页面的数据可以提交到后台,后台的数据可以传到前台显示。

    对于css、pages、js等静态资源,应当是由Tomcat服务器直接传送,而不要被SpringMVC拦截,所以要单独设置配置类一旦扫描到指定的资源就放行。

    没有解决SpringMVC拦截静态资源 API都是原样子写的咋就不行了呢》?》

    第四章、SSM整合

    1、SSM整合

    企业开发停下来做测试:

    业务层写完进行junit测试;控制层写完使用postman进行测试前后端数据交互。

    配置事务三部曲:

    1、开启注解式事务驱动

    在SpringConfig配置类中添加 @EnableTransactionManagement

    2、配置事务管理器

    在jdbcConfig配置类中

    1. @Bean
    2. public PlatformTransactionManager transactionManager(DataSource dataSource){
    3. DataSourceTransactionManager ds = new DataSourceTransactionManager();
    4. ds.setDataSource(dataSource);
    5. return ds;
    6. }

    3、配置事务,把事务配到接口上

    业务层接口内上方添加     @Transactional

    2、表现层数据封装

    返回数据,可能是空、报错、返回数据、返回消息等多种数据格式。

    于是数据封装成统一格式。包含三个信息:code、data、msg。

    根据code判断返回结果的        请求方法以及正误

    根据code是否进行获取data以及msg。

    创建含有不同属性的构造方法,针对不同请求方法返回不同结果

    1. public Result() {
    2. }
    3. public Result(Integer code,Object data) {
    4. this.data = data;
    5. this.code = code;
    6. }
    7. public Result(Integer code,Object data, String msg) {
    8. this.data = data;
    9. this.code = code;
    10. this.msg = msg;
    11. }

    创建状态码类,以便针对前端确认        请求方法及正误

    1. public class Code {
    2. public static final Integer SAVE_OK = 20011;
    3. public static final Integer DELETE_OK = 20021;
    4. public static final Integer UPDATE_OK = 20031;
    5. public static final Integer GET_OK = 20041;
    6. public static final Integer SAVE_ERR = 20010;
    7. public static final Integer DELETE_ERR = 20020;
    8. public static final Integer UPDATE_ERR = 20030;
    9. public static final Integer GET_ERR = 20040;
    10. }

    返回结果实例:(get id= 11)

     将最终返回结果都封装成指定的格式

    1. public Result save(@RequestBody Book book) {
    2. boolean flag = bookService.save(book);
    3. return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR,flag);
    4. }
    5. @PutMapping
    6. public Result update(@RequestBody Book book) {
    7. boolean flag = bookService.update(book);
    8. return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERR,flag);
    9. }
    10. @DeleteMapping("/{id}")
    11. public Result delete(@PathVariable Integer id) {
    12. boolean flag = bookService.delete(id);
    13. return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERR,flag);
    14. }
    15. @GetMapping("/{id}")
    16. public Result getById(@PathVariable Integer id) {
    17. Book book = bookService.getById(id);
    18. Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
    19. String msg = book != null ? "" : "数据查询失败,请重试!";
    20. return new Result(code,book,msg);
    21. }

    3、异常处理器

    异常要分类处理、放在表现层、使用AOP思想

    1. @RestControllerAdvice //声明此类为异常处理器类 = @ResponseBody + @Component
    2. public class ProjectExceptionAdvice {
    3. @ExceptionHandler(Exception.class) //定义处理的是哪一种异常 括号内为异常种类
    4. public Result doException(Exception ex){ //将异常对象传入处理方法内
    5. System.out.println("此处已经拦截到异常");
    6. return new Result(110,null,"产生异常啦");
    7. }
    8. }

    4、项目异常处理方案

    第一步、针对BussinessException、SystemException写配置类,内容相同。

    1. public class BussinessException extends RuntimeException {
    2. private Integer code;
    3. public Integer getCode() {
    4. return code;
    5. }
    6. public void setCode(Integer code) {
    7. this.code = code;
    8. }
    9. public BussinessException(String message, Integer code) {
    10. super(message);
    11. this.code = code;
    12. }
    13. public BussinessException(String message, Throwable cause, Integer code) {
    14. super(message, cause);
    15. this.code = code;
    16. }

    第二步、针对可能出现异常地方处理

    1. public Book getById(Integer id) {
    2. if(id == 1){
    3. throw new BussinessException(Code.BUSSINESS_ERR,"出现错误了");
    4. }
    5. //将可能出现的异常进行包装,转换成自定义异常
    6. try{
    7. int i = 1 / 0;
    8. }catch(Exception e){
    9. throw new SystemException(Code.SYSTEM_TIMEOUT_ERR,"服务器坏;额",e);
    10. }
    11. return bookDao.getById(id);
    12. }

    第三步、异常处理器分类三种异常,针对性处理

    1. @RestControllerAdvice //声明此类为异常处理器类 = @ResponseBody + @Component
    2. public class ProjectExceptionAdvice {
    3. @ExceptionHandler(SystemException.class) //定义处理的是哪一种异常 括号内为异常种类
    4. public Result doSystemExceptionn(SystemException ex){ //将异常对象传入处理方法内
    5. //记录日志
    6. //发送消息给运维
    7. //发送邮件给开发人员 ex对象发送
    8. return new Result(ex.getCode(),null,ex.getMessage());
    9. }
    10. @ExceptionHandler(BussinessException.class) //定义处理的是哪一种异常 括号内为异常种类
    11. public Result doBussinessException(BussinessException ex){ //将异常对象传入处理方法
    12. return new Result(ex.getCode(),null,ex.getMessage());
    13. }
    14. //其他异常
    15. @ExceptionHandler(Exception.class) //定义处理的是哪一种异常 括号内为异常种类
    16. public Result doException(Exception ex){ //将异常对象传入处理方法内
    17. System.out.println("此处已经拦截到异常");
    18. return new Result(SYSTEM_UNKNOW_ERR,null,"系统繁忙请稍后!");
    19. }
    20. }

    5、案例:SSM整合标准开发

    至于处理SpringMVC拦截静态资源,就利用api处理。

    把下面这个类在SpringMvcConfig配置类中扫描到就好了

    1. @Configuration
    2. public class SpringMvcSupport extends WebMvcConfigurationSupport {
    3. @Override
    4. protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    5. registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
    6. registry.addResourceHandler("/css/**").addResourceLocations("/css/");
    7. registry.addResourceHandler("/js/**").addResourceLocations("/js/");
    8. registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
    9. }
    10. }

    也就是页面操作-----前端没学 没法做喽

    第五章、拦截器

    1、拦截器概念

     

    2、入门案例

    《1、》制作拦截器功能类

    将功能类写在controller下,SpringMVC扫描controller时可以直接扫描到

    重写API三个方法

    1. @Component
    2. public class ProjectInterceptor implements HandlerInterceptor {
    3. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    4. System.out.println("ProjectInterceptor...");
    5. return true;
    6. //此处若返回false则终止原始方法的执行,接下来的所有都不执行了
    7. }
    8. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    9. System.out.println("postHandle...");
    10. }
    11. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    12. System.out.println("afterCompletion...");
    13. }
    14. }

    《2、》配置拦截器的执行位置

    也就是去确定在执行什么请求时执行拦截器

    在SpringMvcSupport配置类中重写API ProjectInterceptor 的方法 addInterceptors

    来确定要拦截的请求路径

    1. @Configuration
    2. public class SpringMvcSupport extends WebMvcConfigurationSupport {
    3. @Override
    4. protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    5. registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
    6. registry.addResourceHandler("/css/**").addResourceLocations("/css/");
    7. registry.addResourceHandler("/js/**").addResourceLocations("/js/");
    8. registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
    9. }
    10. @Autowired
    11. private ProjectInterceptor projectInterceptor;
    12. protected void addInterceptors(InterceptorRegistry registry) {
    13. registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
    14. }
    15. }

    3、拦截器参数

     

    4、拦截器工作流程分析

    5、拦截器链

     

  • 相关阅读:
    Windows卸载Nessus与安装Nessus
    jQuery
    十一月29号作业
    Java基础面试题
    java虚拟机详解篇六(类的加载机制)
    Java SSM Spring MVC 响应数据和结果视图+文件上传+异常处理+拦截器
    Java毕业设计-二手车交易平台管理系统 二手车交易管理系统 二手汽车交易系统 汽车销售系统 汽车商城
    WPF列表性能提高技术
    黑马瑞吉外卖之新增分类
    NTP时钟同步服务器(卫星授时服务)在云计算数据机房的应用
  • 原文地址:https://blog.csdn.net/m0_61843855/article/details/132569682