一、Spring MVC

Spring是apache推出的开源框架,整合了springmvc框架及spring框架其他部分,是一种轻量级的、基于MVC的 Web应用框架 ,使用springMVC+Mybatis可以实现SSH几乎所有的功能,相对SSH更加的轻量,灵活。

1、SpringMVC优势

  • 清晰的角色划分:

    • 前端控制器(DispatcherServlet);
    • 请求到处理器映射(HandlerMapping);
    • 处理器适配器(HandlerAdapter);
    • 视图解析器(ViewResolver);
    • 处理器或页面控制器(Controller);
    • 验证器(Validator)
    • 命令对象(Command 请求参数绑定到的对象就叫命令对象);
    • 表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)
  • 分工明细,拓展相当灵活和Spring无缝集成,这个是其他框架不具备的,本身spring中包含了springMVC
  • 功能强大的数据验证、格式化。
  • 支持REST风格(所有的url都可当成资源)软件的架构风格
  • 基于注解的零配置,在以前时候,Struts2中需要配置大量的xml文件;但是其实后来struts2也支持注解咯.

2、MVC说明

  • Model(模型):

数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据) 和 服务层service(业务行为),也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。

  • View(视图):

负责进行模型的展示,一般就是我们见到的用户界面,展示给用户的东西,同时可以收集用户的请求数据,传递给控制器.设计的时候可以借助外部的一些框架.技术可以是html+js,jsp,freemarker,其他的前端技术等

  • Controller(控制器):

接收用户请求,调用给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。 也就是说控制器做了个调度员的工作。核心部分。

3、SpringMVC控制器说明

​ 框架的Controller 层要完成的主要工作:封装web 请求为一个数据对象、调用业务逻辑层来处理数据对象、返回处理数据结果及相应的视图给用户。

​ SpringMVC中 Controller 层框架的核心是 DispatcherServlet,它的作用是将请求分发给不同的后端处理器,也即 使用了一种被称为Front Controller 的模式(后面对此模式有简要说明)。 Spring 的C 层框架使用了后端控制器来、映射处理器和视图解析器来共同完成C 层框架的主要工作。并且spring 的C 层框架还真正地把业务层处理的数据结果和相应的视图拼成一个对象,即我们后面会经常用到的ModelAndView 对象。

​ Spring MVC是结构最清晰的MVC Model 2实现。它的控制器,称做Controller;Controller接收request, response参数,然后返回ModelAndView类型的对象(其中的Model不是Object类型,而是Map类型)。但在其它的Web Framework中,Action返回值一般都只是一个View Name;Model则需要通过其它的途径(如request.attribute,Context参数,或Action本身的属性数据)传递上去。

4、SpringMVC请求处理流程

请求过程要会描述,按照课堂讲的内容来组织自己的语言,作为面试的重点关注部分.

  1. 外部请求,找前端控制器(DispatcherServlet)
  2. 前端控制器根据配置信息,查找实际的控制器(sayHello) .可以通过代理请求来完成(实际上比较复杂 前端的解析器,前端的代理适配器等等)
  3. 找到真正的控制器(后端控制器),处理请求
  4. 创建model;功能是将数据返回给视图,数据可能是从业务一侧获取的结果
  5. 携带数据传递给前端控制器
  6. 前端控制器将结果传递给视图,渲染视图
  7. 返回结果给请求一侧(浏览器一侧 )

  1. 向服务器发送 HTTP 请求,请求被前端控制器 DispatcherServlet 捕获。
  2. DispatcherServlet 根据 -servlet.xml 中的配置对请求的 URL 进行解析,得到请求资源标识符(URI)。然后根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及 Handler 对象对应的拦截器),最后以HandlerExecutionChain 对象的形式返回。
  3. DispatcherServlet 根据获得的Handler,选择一个合适的 HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的 preHandler(...)方法)。
  4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:

    • HttpMessageConveter: 将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定的响应信息。
    • 数据转换:对请求消息进行数据转换。如String转换成IntegerDouble等。
    • 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等。
    • 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResultError中。
  5. Handler(Controller)执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象;
  6. 根据返回的ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到 Spring 容器中的ViewResolver)返回给DispatcherServlet
  7. ViewResolver 结合ModelView,来渲染视图。
  8. 视图负责将渲染结果返回给客户端。

二、非Ajax的SpringMVC基本使用

注【所需要的Jar文件】:

  • javax.servlet:javax.servlet-api:4.0.1
  • javax.servlet.jsp:javax.servlet.jsp-api:2.3.3
  • mysql:mysql-connector-java:8.0.22
  • commons-beanutils:commons-beanutils:1.9.4
  • commons-collections:commons-collections:3.2.2
  • commons-logging:commons-logging:1.2
  • junit:junit:4.12
  • com.google.code.gson:gson:2.8.6
  • org.springframework:spring-aop:5.3.6
  • org.springframework:spring-aspects:5.3.6
  • org.springframework:spring-beans:5.3.6
  • org.springframework:spring-context:5.3.6
  • org.springframework:spring-context-support:5.3.6
  • org.springframework:spring-core:5.3.6
  • org.springframework:spring-test:5.3.6
  • org.springframework:spring-web:5.3.6
  • org.springframework:spring-webmvc:5.3.6

1、@RestController注解

如果希望这个控制器中所有的方法返回值都是ajax = > json,则使用@RestController注解

@RestController
public class ArticleController {
    @GetMapping(value = "hello", produces = "application/json;charset=utf-8")
    public String sayHello(String name) {
        return "name:" + name;
    }
}

2、@Controller注解

在默认不做其他处理的情况下,没有返回ajax请求,就是普通的转发跳转,则使用@Controller注解。

测试返回这个字符串并不是数据内容,而是一个视图!

@Controller
public class BlogController {
    @GetMapping("blog")
    public String getBlgo() {
        return "blog: id = 1 ,title = 测试";
        //return "blogList";
    }
}

3、@GetMapping

@GetMapping用于将HTTP get请求映射到特定处理程序的方法注解
具体来说,@GetMapping是一个组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写。该注解将HTTP Get 映射到 特定的处理方法上。

4、@PostMapping

@PostMapping用于将HTTP post请求映射到特定处理程序的方法注解
具体来说,@PostMapping是一个组合注解,是@RequestMapping(method = RequestMethod.POST)的缩写。

5、@RequestMapping

@RequestMapping(method = RequestMethod.GET),这行代码即说明@GetMapping就是@RequestMapping附加了请求方法。同时,可以看到@GetMapping这个注解 是spring4.3版本引入,同时引入的还有@PostMapping@PutMapping@DeleteMapping@PatchMapping,一共5个注解。

所以,一般情况下用@RequestMapping(method = RequestMethod. XXXX)即可。

3、applicationContext.xml加入注解的支持

 <!--注解的支持-->
<context:annotation-config></context:annotation-config>

<context:annotation- config/>隐式地向 Spring容器注册

  • AutowiredAnnotationBeanPostProcessor
  • RequiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • PersistenceAnnotationBeanPostProcessor
  • 这4个BeanPostProcessor。如下:

4、applicationContext.xml组件扫描路径

另,在我们使用注解时一般都会配置扫描包路径选项:

<!--  spring 可以自动去扫描 base-package下面的包或子包下面的Java文件,如果扫描到有Spring的相关
注解的类,则把这些类注册为Spring的bean -->
<context:component-scan base-package="com.singerw"></context:component-scan>

5、applicationContext.xml配置视图解析器

<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--注入prefix前缀和suffix后缀!-->
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>

6、web.xml中SpringMVC配置

<!--1、加载SpringMVC配置-->
<!-- 这个 Spring Web 应用的前端控制器,负责处理所有的应用请求 -->
<servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <!--1  配置springmvc的配置文件路径 -->
        <param-value>classpath:applicationContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<!-- 将所有请求映射到 DispatcherServlet 进行处理 -->
<servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <!--2  配置url-pattern 路径 -->
    <url-pattern>/</url-pattern>
</servlet-mapping>

7、web.xml中Spring配置文件

<!--2、加载Spring配置文件-->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
  • WEB-INF里面安全级别比较高,不能直接通过浏览器访问,但是可以通过控制器转发过来进行访问。
  • 解释:classpath是指 WEB-INF文件夹下的classes目录,这里存放的的部署后的文件文件,这是一个定位资源的入口。
  • classpath 和 classpath区别:

    • classpath:只会到你的class路径中查找文件; (src ->编译 classes)
    • classpath:不仅包含class路径,还包括jar文件中(class路径)进行查找.

三、Ajax在SpringMVC基本使用

数据交互:就是Controller View之间的数据交互

控制器:Controller 如何传递数据给 View

视图:View(jsp,html,vue… )如何传递数据给 控制器Controller

先定标准,写好控制器,做好接口的测试!

1、添加Jar文件

  • com.fasterxml.jackson.core:jackson-databind:2.12.3
  • com.fasterxml.jackson.core:jackson-core:2.12.3
  • com.fasterxml.jackson.core:jackson-annotations:2.12.3

检查springmvc配置文件中加入:<mvc:annotation-driven></mvc:annotation-driven>,如果没有加入mvc-annotation的节点,可能会出现如下错误:

<!--
    检查springmvc配置文件中加入: <mvc:annotation  ….>
    如果没有加入mvc-annotation的节点,可能会出现错误:
    -->
<!--设置配置方案 -->
<mvc:annotation-driven></mvc:annotation-driven>

<mvc:annotation-driven />注解意义:

主要就是为了Spring MVC来用的,提供Controller请求转发,json自动转换等功能.。

<mvc:annotation-driven /> 是一种简写形式,完全可以手动配置替代这种简写形式,简写形式可以让初学都快速应用默认配置方案。配置一些messageconverter。即解决了@Controller注解的使用前提配置<context:annotation-config/>是对包进行扫描,实现注释驱动Bean定义,同时将bean自动注入容器中使用。即解决了@Controller标识的类的bean的注入和使用。
<mvc:annotation-driven>会自动注册RequestMappingHandlerMappingRequestMappingHandlerAdapter两个Bean,这是Spring MVC@Controller分发请求所必需的,并且提供了数据绑定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持读写XML的支持(JAXB)和读写JSON的支持(默认Jackson)等功能。
我们处理响应ajax请求时,就使用到了对json的支持。
actionJUnit单元测试时,要从spring IOC容器中取DefaultAnnotationHandlerMappingAnnotationMethodHandlerAdapter 两个bean,来完成测试,取的时候要知道是<mvc:annotation-driven />这一句注册的这两个bean

CSSJS失效,需要在applicationContext.xml中做静态资源的处理

<!--静态资源的处理,我们的前端控制器不处理静态资源 ,注意,请求还是有走前端控制器,只不过不处理 -->
<mvc:default-servlet-handler/>

2、使用注解编写控制层

在控制器层使用 @RestController返回数据 ,可以设置返回格式 application/json;

@ResponseBody : 如果方法加上了@ResponseBody注解,Spring返回值到响应体。如果这样做的话,Spring将根据请求中的 Content-Type header(私下)使用 HTTP Message converters 来将domain对象转换为响应体。

@RestController
@RequestMapping("ajax")
public class BlogControllerAjax {

    @Autowired
    private BlogService blogService;
    
    @GetMapping(value = "blog")
    public PageData<BlogEntity> getBlogListAjax() {
        int page = 1;
        int pageSize = 10;
        PageData<BlogEntity> blogList = blogService.getBlogList(page, pageSize);
        System.out.println(blogList);
        return blogList;
    }
}

3、访问Url

GET http://127.0.0.1:8080/ajax/blog

四、非Ajax的控制器与视图层数据传递

4.1、非Ajax的控制器传递数据给视图层

数据交互:就是Controller View之间的数据交互

控制器:Controller 如何传递数据给 View

视图:View(jsp,html,vue… )如何传递数据给 控制器Controller

1、方案1:原始request.setAttribute存储对象

@GetMapping("blog")
public String getBlogList(HttpServletRequest request) {
    int page = 1;
    int pageSize = 10;
    request.setAttribute("blogList",blogService.getBlogList(page,pageSize));
    return "blog";
}

2、方案2:使用并返回一个ModelAndView对象

@GetMapping("blog1")
public ModelAndView getBlogList1() {
    int page = 1;
    int pageSize = 10;
    PageData<BlogEntity> blogList = blogService.getBlogList(page, pageSize);
    ModelAndView modelAndView = new ModelAndView("blog", "blogList1", blogList);
    return modelAndView;
}

3、方案3:返回一个字符串,view视图名,数据存放在参数Model中

@GetMapping("blog2")
public String getBlogList2(Model model) {
    int page = 1;
    int pageSize = 10;
    PageData<BlogEntity> blogList = blogService.getBlogList(page, pageSize);
    //model.addAttribute(blogList);
    model.addAttribute("blogList2", blogList);
    return "blog";
}

4.2、非Ajax的视图层传递数据给控制器

数据交互:就是Controller View之间的数据交互

控制器:Controller 如何传递数据给 View

视图:View(jsp,html,vue… )如何传递数据给 控制器Controller

1、表单提交,控制器方法中直接参数位置写表单元素的name,或者直接传对象

【添加示例】:

@PostMapping("addblog")
public String addBlogList(String title, String context) {
    // 先研究参数传递,一起的玩法?怎么获取请求数据?
    // 直接将参数名写在参数的位置,要和表单的元素的name相同
    System.out.println("title:" + title + "context:" + context);
    return "blog";
}
<form action="article" method="post">
    <input type="text" name="title"><br/>
    <textarea name="content"></textarea><br/>
    <input type="submit" valu="添加">
</form>

【删除示例】:

@GetMapping("articledel")
public String delArticle(int id) {
    // 先研究参数传递,1 =>以前的玩法,怎么获取请求数据? request.getParameter
    // 2=>直接将参数名写在参数位置,要和表单元素的name相同
    //3 直接将对象作为参数
    System.out.println("id:" + id);
    return "articles2";
}
<a href="articledel?id=1">删除</a>

2、表单提交,直接将对象名作为参数

【添加示例】:

@PostMapping("addblog")
public String addBlogList(BlogEntity blogEntity) {
    // 先研究参数传递,一起的玩法?怎么获取请求数据?
    // 直接将直接将对象名作为参数写在参数的位置,要和表单的元素的name相同
    System.out.println("blogEntity:" + blogEntity);
    return "blog";
}
<form action="article2" method="post">
    <input type="text" name="title"><br/>
    <textarea name="content"></textarea><br/>
    <input type="submit" valu="添加">
</form>

五、Ajax的控制器与视图层数据传递

6.1、Ajax的控制器传递数据给视图层

Get请求通过ID查询博客文章

@GetMapping("/blog/{blogid}")
public BlogEntity getBlogByID(@PathVariable("blogid") int blogid) {
    return blogService.getBlogByID(blogid);
}

1、基于Layui的数据分页显示模糊查询

分页查询所有的博客列表,并实现模糊查询@GetMapping

Controller:

@GetMapping("/blog")
public ResponseData<BlogEntity> getBlog(
    @RequestParam(name = "keywords", required = true, defaultValue = "") String keywords,
    @RequestParam(name = "page", required = true, defaultValue = "1") int page,
    @RequestParam(name = "limit", required = true, defaultValue = "10") int limit) {
    PageData<BlogEntity> blogList = blogService.getBlogList(page, limit, keywords);
    ResponseData<BlogEntity> responseData = new ResponseData(0, "操作成功", blogList.getTotal(), blogList.getData());
    return responseData;
}

JS:

table.render({
    elem: '#user-table',
    url: '../../../../admin/getarticle',
    page: true,
    cols: cols,
    skin: 'line',
    toolbar: '#user-toolbar',
    defaultToolbar: [{
        title: '刷新',
        layEvent: 'refresh',
        icon: 'layui-icon-refresh',
    }, 'filter', 'print', 'exports']
});

6.2、Ajax的视图层传递数据控制器

1、普通值的传递

【示例】基于Layui删除一篇文章

JS:

window.remove = function (obj) {
    layer.confirm('确定要删除此文章', {
        icon: 3,
        title: '提示'
    }, function (index) {
        layer.close(index);
        let loading = layer.load();
        $.ajax({
            url: '../../../../admin/delarticle/' + obj.data['article_id'],
            dataType: 'json',
            type: 'delete',
            success: function (result) {
                layer.close(loading);
                layer.msg(result.msg, {
                    icon: 1,
                    time: 1000
                });
            }
        })
    });
}

Contorller:

@DeleteMapping("/delarticle/{articleID}")
public ResponseData deleteArticleByID(@PathVariable("articleID") int articleID) {
    ResponseData responseData = articleService.deleteArticleByID(articleID);
    return responseData;
}

【示例】基于Layui修改文章状态

JS:

form.on('switch(article-status)', function (obj) {
    let status = obj.elem.checked ? 1 : 0
    $.ajax({
        url: '../../../../admin/updatest/'+this.value+"/"+status,
        type: 'put',
        success: function (res) {
            layer.tips(res.msg, obj.othis)
        },
    });
});

Controller:

@PutMapping("/updatest/{articleStatus}/{articleID}")
public ResponseData updateArticleStatus(
    @PathVariable("articleStatus") int articleStatus,
    @PathVariable("articleID") int articleID) {
    ResponseData responseData = articleService.updateArticleStatus(articleID, articleStatus);
    return responseData;
}

6.3、富文本编辑器向控制器传值

TinyMCE:功能强大、所见即所得的富文本编辑器:ghost:http://tinymce.ax-z.cn/

【示例】:Layui.cssLayui.js

<!-- 引入 layui.css -->
<link rel="stylesheet" href="//unpkg.com/layui@2.6.8/dist/css/layui.css">

<!-- 引入 layui.js -->
<script src="//unpkg.com/layui@2.6.8/dist/layui.js">

【示例】:HTML

<!--layui富文本编辑器-->
<div class="layui-card-body">
    <fieldset class="layui-elem-field layui-field-title" style="margin-top: 10px;">
        <legend>文章内容</legend>
    </fieldset>
    <textarea id="edit" cols="30" rows="10" name="article_content"></textarea>
    <div class="layui-btn-group" style="margin-top: 10px">
        <button class="layui-btn" lay-submit lay-filter="form-fabu">发布</button>
    </div>
</div>

【示例】:Js

<!--Layui富文本编辑器-->
    <script>
    layui.use(['tinymce', 'form', 'jquery'], function () {
    let form = layui.form;
    let $ = layui.jquery;

    var tinymce = layui.tinymce
    var edit = tinymce.render({
        elem: "#edit",
        height: 400
    });
    //获取文本框中的值
    edit.getContent();
    form.on('submit(form-fabu)', function (data) {
        data.field.article_content = edit.getContent()
        console.log(data.field.article_content)
        let roleIds = "";
        $('input[type=checkbox]:checked').each(function () {
            roleIds += $(this).val() + ",";
        });
        roleIds = roleIds.substr(0, roleIds.length - 1);
        data.field.roleIds = roleIds;
        console.log(data.field);
        $.ajax({
            url: '../../../../admin/addarticle',
            data: JSON.stringify(data.field),
            dataType: 'json',
            contentType: 'application/json',
            type: 'post',
            success: function (result) {
                if (result.success) {
                    layer.msg(result.msg, {icon: 2, time: 1000});
                } else {
                    layer.msg(result.msg, {icon: 1, time: 1000});
                }
            }
        });
        return false;
    });
});
</script>

【示例】:添加文章后端Controller控制器

@PostMapping("/addarticle")
public AjaxOperateResponse addArticle(@RequestBody ArticleEntity article) {
    AjaxOperateResponse response = new AjaxOperateResponse(200, articleService.addArticle(article) ? "增加成功" : "增加失败");
    return response;
}

6.4、其他操作值传递

增加操作@PostMapping

@PostMapping("/blog")
public AjaxOperateResponse addBlog(BlogEntity blog) {
    AjaxOperateResponse response = new AjaxOperateResponse(200, "增加成功");
    return response;
}
@PostMapping("/addarticle")
public AjaxOperateResponse addArticle(@RequestBody ArticleEntity article) {
    AjaxOperateResponse response = new AjaxOperateResponse(200, articleService.addArticle(article) ? "增加成功" : "增加失败");
    return response;
}

修改操作@PutMapping

@PutMapping("/blog/{blogid}")
public AjaxOperateResponse updateBlog(@PathVariable("blogid") int blogid, BlogEntity blog) {
    AjaxOperateResponse response = new AjaxOperateResponse(200, "增加成功");
    return response;
}
@PutMapping("/updatest/{articleStatus}/{articleID}")
public ResponseData updateArticleStatus(
    @PathVariable("articleStatus") int articleStatus,
    @PathVariable("articleID") int articleID) {
    ResponseData responseData = articleService.updateArticleStatus(articleID, articleStatus);
    return responseData;
}

删除操作@DeleteMapping

@DeleteMapping("/blog/{blogid}")
public AjaxOperateResponse deleteBlogByID(@PathVariable("blogid") int blogid) {
    AjaxOperateResponse response = new AjaxOperateResponse(200, "删除成功");
    return response;
}

删除所有@DeleteMapping

@DeleteMapping("/blog/")
public AjaxOperateResponse deleteAllBlogByID() {
    AjaxOperateResponse response = new AjaxOperateResponse(200, "批量删除成功");
    return response;
}

六、RestFul风格案例

RestFul通用风格

@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。

请求方式请求路径功能
GET /api/emp/ 返回员工列表
GET /api/emp/1 返回员工编号为1的员工对象
POST /api/emp 增加一个员工,参数为Emp对象JSON格式
PUT /api/emp/1 更新员工,参数empno为1,json格式的员工
DELETE /api/emp/1 删除编号为1的员工对象
DELETE /api/emp/ 删除所有员工

1、【示例代码】RestFul风格参数传递

@RestController
@RequestMapping("/admin")
public class ArticleController {

    @Autowired
    private ArticleService articleService;

    /**
     * @param page
     * @param limit
     * @param keywords
     * @return
     * @Author CodeSleep
     * @Date: 2021/8/19 21:27
     * @Description: //TODO 分页和模糊查询所有文章控制器
     * GET http://127.0.0.1:8080/admin/getarticle
     */
    @GetMapping("/getarticle")
    public ResponseData<ArticleAndTypeDto> getAllArticle(
        @RequestParam(name = "page", required = true, defaultValue = "1") int page,
        @RequestParam(name = "limit", required = true, defaultValue = "10") int limit,
        @RequestParam(name = "keywords", required = true, defaultValue = "") String keywords) {
        ResponseData<ArticleAndTypeDto> articleList = articleService.getArticleList(page, limit, keywords);
        return articleList;
    }

    /**
     * @param articleID
     * @return
     * @Author CodeSleep
     * @Date: 2021/8/19 21:27
     * @Description: //TODO 通过ID删除文章控制器
     * DELETE http://127.0.0.1:8080/admin/delarticle/{{articleID}}
     */
    @DeleteMapping("/delarticle/{articleID}")
    public ResponseData deleteArticleByID(@PathVariable("articleID") int articleID) {
        ResponseData responseData = articleService.deleteArticleByID(articleID);
        return responseData;
    }

    /**
     * @param articleID
     * @return
     * @Author CodeSleep
     * @Date: 2021/8/20 10:06
     * @Description: //TODO 文章批量删除
     */
    @DeleteMapping("/delarticle/{articleID}")
    public ResponseData deleteAllArticleByID(@PathVariable("articleID") int articleID) {
        ResponseData responseData = articleService.deleteArticleByID(articleID);
        return responseData;
    }

    /**
     * @param article
     * @return
     * @Author CodeSleep
     * @Date: 2021/8/19 21:28
     * @Description: //TODO 增加文章控制器
     * POST http://127.0.0.1:8080/admin/addarticle
     */
    @PostMapping("/addarticle")
    public AjaxOperateResponse addArticle(@RequestBody ArticleEntity article) {
        AjaxOperateResponse response = new AjaxOperateResponse(200, articleService.addArticle(article) ? "增加成功" : "增加失败");
        return response;
    }

    /**
     * @param articleStatus
     * @param articleID
     * @return
     * @Author CodeSleep
     * @Date: 2021/8/20 9:08
     * @Description: //TODO 修改文章状态
     * PUT http://127.0.0.1:8080/admin/updatest/{{articleStatus}}/{{articleID}}
     */
    @PutMapping("/updatest/{articleStatus}/{articleID}")
    public ResponseData updateArticleStatus(
        @PathVariable("articleStatus") int articleStatus,
        @PathVariable("articleID") int articleID) {
        ResponseData responseData = articleService.updateArticleStatus(articleID, articleStatus);
        return responseData;
    }
}

2、【示例1】传递参数

@RequestMapping(value="/add3/{userId}/{userName}/a")
public String requestParam3(@PathVariable(value="userId") int userId,@PathVariable("userName") String userName)
{
    System.out.println("userId :"+userId+" ,userName :"+userName);
    return "add";
}

说明:当浏览器地址栏输入: http://localhost:8080/SpringMvcDemo03/hc5/add3/888/laowang/a

可以访问到该方法:同时控制台可以得到:

userId :888

userName :laowang

即通过访问该方法同时传递参数userId :888 ;userName :laowang

3、【示例2】传递参数

@PutMapping("/updatest/{articleStatus}/{articleID}")
public ResponseData updateArticleStatus(
    @PathVariable("articleStatus") int articleStatus,
    @PathVariable("articleID") int articleID) {
    ResponseData responseData = articleService.updateArticleStatus(articleID, articleStatus);
    return responseData;
}

说明:当浏览器地址栏输入: http://localhost:8080/updatest/1/0

可以访问到该方法:同时控制台可以得到:

articleStatus:0

articleID:1

即通过访问该方法同时传递参数articleStatus:0, articleID:1可修改文章ID为1的文章状态为0

4、【示例3】传递参数

@DeleteMapping("/delarticle/{articleID}")
public ResponseData deleteArticleByID(@PathVariable("articleID") int articleID) {
    ResponseData responseData = articleService.deleteArticleByID(articleID);
    return responseData;
}

说明:当浏览器地址栏输入: http://localhost:8080/delarticle/1

可以访问到该方法:同时控制台可以得到:

articleID:1

即通过访问该方法同时传递参数articleID:1,并删除ID为1的这篇文章

七、页面转发和重定向

1、转发示例1

@GetMapping("f01")
public String f01(){
    return "f01";
}

2、转发示例2

@GetMapping("f02")
public String f02(){
    return "forward:f01";
}

3、转发示例3

@GetMapping("f03")
public String f03(){
    return "forward:f01.jsp";
}

4、重定向示例1

@GetMapping("f03")
public String f03(){
    return "redirect:f01";
}

5、重定向示例2

@GetMapping("f03")
public String f03(){
    return "redirect:redirect.html";
    return "redirect:https://singerw.com";
}

八、SpringMVC实现文件上传下载

1、文件上传的步骤实现:

操作步骤1,添加jar包到工程中来;

<!-- 文件上传 commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
<!-- 文件上传 commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

步骤2:在SpringMVC配置文件中定义一个bean 类型为CommonsMultipartResolver

在SpringMVC中专门封装了一个类CommonsMultipartResolver来处理文件上传,所以需要在 SpringMVC的配置件中加入一个bean;配置bean的代码如下:

<!--文件上传-->
<!--加入一个bean用来处理文件上传,这里采用的是commons-fileupload:commons-fileupload:1.4-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

</bean>

步骤3:编写前台html页面:特别要注意这个action的值,确保是正确的url路径;

步骤4:编写文件上传的控制器代码:

@Controller
public class UpLoadController {

    @RequestMapping(value = "myupload", method = RequestMethod.POST)
    public String upload(HttpServletRequest request) {

        //将request 转换为 MultipartHttpServletRequest
        MultipartHttpServletRequest req = (MultipartHttpServletRequest) request;
        //得到上传的文件
        MultipartFile file = req.getFile("myfile");
        //得到文件域的名字file.getName()   得到文件名file.getOriginalFilename()
        System.out.println(file.getName()+","+file.getOriginalFilename());
        //上传的目标路径
        String path = request.getRealPath("/imgs")+"/"+file.getOriginalFilename();
        System.out.println("path :"+path);
        //创建目标文件
        File destFile = new File(path);
        try {
            //直接使用封装好的 copyInputStreamToFile 实现文件的上传功能
            FileUtils.copyInputStreamToFile(file.getInputStream(), destFile);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "succ";
    }
}

2、文件上传的问题和扩展:

测试可以发现文件能够正常上传,只有短短的几行代码,就实现了文件的上传功能,但是依然存在如下的问题列表:

1、 除了上传文件,此表单中还传递了名字和密码元素,在控制器一侧如何进行接收?

解决方案:参数列表中增加如下代码即可.

@RequestMapping(value = "myupload", method = RequestMethod.POST)
//这两个为表单元素名字
public String upload(HttpServletRequest request, String name,String pwd) {
    System.out.println("name :"+name+",pwd :"+pwd);

2、 对上传的文件要进行类型和大小的限制该如何实现?

<bean id="multipartResolver"
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 指定所上传文件的总大小不能超过200KB。注意maxUploadSize属性的限制不是针对单个文件,而是所有文件的容量之和 -->
    <!-- 如果超过200kb会报一个异常 -->
    <property name="maxUploadSize" value="200000"></property>
</bean>

3、异常

如果上传文件超过限制,会报一个异常,异常信息如下:

我们发现,在CommonsMultipartResolver的父类: CommonsFileUploadSupport

处理异常方案:

<!-- SpringMVC在超出上传文件限制时,会抛出org.springframework.web.multipart.MaxUploadSizeExceededException -->
<!-- 该异常是SpringMVC在检查上传的文件信息时抛出来的,而且此时还没有进入到Controller方法中 -->
<bean id="exceptionResolver"
      class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <!-- 遇到MaxUploadSizeExceededException异常时,自动跳转到/WEB-INF/jsp/error_fileupload.jsp页面 --> <propkey="org.springframework.web.multipart.MaxUploadSizeExceededException">error_fileupload</prop>
        </props>
    </property>
</bean>

4、文件类型的判断:

//得到上传的文件名
MultipartFile file = req.getFile("myfile");
//得到文件的contentType
System.out.println("contentType :"+file.getContentType());

5、 如果是多文件上传代码应该如何修改呢?

【参考代码】

// 前台表单中的所有<input type="file"/>的name都应该是myfiles
@RequestMapping(value = "myuploadmulti", method = RequestMethod.POST)
public String uploadMulti(HttpServletRequest request,@RequestParam MultipartFile[] myfiles,
                          String name, String pwd) {

    System.out.println("name :" + name + ",pwd :" + pwd);
    // 将request 转换为 MultipartHttpServletRequest
    for (MultipartFile file : myfiles) {

        // 得到文件的contentType
        System.out.println("contentType :" + file.getContentType());
        // 得到文件域的名字file.getName() 得到文件名file.getOriginalFilename()
        System.out.println(file.getName() + ","
                           + file.getOriginalFilename());
        // 上传的目标路径
        String path = request.getRealPath("/imgs") + "/"
            + file.getOriginalFilename();
        System.out.println("path :" + path);
        // 创建目标文件
        File destFile = new File(path);
        try {
            // 直接使用封装好的 copyInputStreamToFile 实现文件的上传功能
            FileUtils
                .copyInputStreamToFile(file.getInputStream(), destFile);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    return "succ";
}

3、文件下载

@RequestMapping(value = "dw", method = RequestMethod.GET)
public void download(HttpServletRequest request,HttpServletResponse response,@RequestParam("fileName") String fileName) throws IOException {

    String realPath = request.getServletContext().getRealPath("/");
    File file = new File(realPath,fileName);
    //response.setContentType("application/force-download");// 设置强制下载不打开
    response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);// 设置文件名
    FileInputStream is = new FileInputStream(file);
    byte[] bytes = new byte[is.available()];
    is.read(bytes);
    OutputStream os = response.getOutputStream();
    os.write(bytes);
    is.close();
    os.close();
}

4、文件上传+SpringMVC+富文本编辑器

4.1、基本思路

​ 在富文本中,选择图片后,直接使用ajax完成图片的上传,上传成功之后,我们在页面上获取图片的路径。将路劲信息和富文本的信息,再统一提交。

4.2 、前提准备

步骤1:在SpringMVC配置文件中定义一个bean 类型为CommonsMultipartResolver

在SpringMVC中专门封装了一个类CommonsMultipartResolver来处理文件上传,所以需要在 SpringMVC的配置件中加入一个bean;配置bean的代码如下:

<!--文件上传-->
<!--加入一个bean用来处理文件上传,这里采用的是commons-fileupload:commons-fileupload:1.4-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

</bean>

步骤一:导入所需Jar文件

<!-- 文件上传 commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
<!-- 文件上传 commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

步骤二:使用tinymce富文本编辑器

<!--layui-tinymce富文本编辑器-->
<div class="layui-card-body">
    <fieldset class="layui-elem-field layui-field-title" style="margin-top: 10px;">
        <legend>文章内容</legend>
    </fieldset>
    <textarea id="edit" cols="30" rows="10" name="article_content"></textarea>
    <div class="layui-btn-group" style="margin-top: 10px">
        <button class="layui-btn" lay-submit lay-filter="form-fabu">发布</button>
    </div>
</div>
<!--Layui富文本编辑器-->
    <script>
    layui.use(['tinymce', 'form', 'jquery'], function () {
    let form = layui.form;
    let $ = layui.jquery;

    //富文本编辑器
    var tinymce = layui.tinymce
    var edit = tinymce.render({
        elem: "#edit",
        //配置上传接口
        images_upload_url: '../../../../file/upload2',
        form: {
            //配置上传文件的字段名称
            name: 'file',
        },
        height: 400,
    });
    //获取文本框中的值
    edit.getContent();
    console.log(edit.getContent());

    // 数据提交(../../../../admin/addarticle)
    form.on('submit(form-fabu)', function (data) {
        //将文本框内的值赋值给data.field.article_content
        data.field.article_content = edit.getContent()
        console.log(data.field.article_content)
        let roleIds = "";
        $('input[type=checkbox]:checked').each(function () {
            roleIds += $(this).val() + ",";
        });
        roleIds = roleIds.substr(0, roleIds.length - 1);
        data.field.roleIds = roleIds;
        console.log(data.field);
        $.ajax({
            url: '../../../../admin/addarticle',
            data: JSON.stringify(data.field),
            dataType: 'json',
            contentType: 'application/json',
            type: 'post',
            success: function (result) {
                if (result.success) {
                    layer.msg(result.msg, {icon: 2, time: 1000});
                } else {
                    layer.msg(result.msg, {icon: 1, time: 1000});
                }
            }
        })
        return false;
    });
})
</script>

步骤三、自定义AjaxUploadResponse响应体

@Data
@AllArgsConstructor
@NoArgsConstructor

public class AjaxUploadResponse<T> {
    private int code = 0;
    private String msg = "";
    private String data = "";
}

步骤四、编写文件上传控制器

@RestController
@RequestMapping("/file")
public class FileController {

    @PostMapping("/upload1")
    public AjaxUploadResponse uploadfile(@RequestParam("file") Part file, HttpServletRequest request) {
        // 文件名
        System.out.println("文件名" + file.getContentType());
        // 将file上传到服务器的指定位置(webapp下的某个目录)
        String realPath = request.getRealPath("/");
        System.out.println("realPath:" + realPath);
        String fileName = (realPath + "/img/" + file.getSubmittedFileName());
        try {
            file.write(fileName);
        } catch (IOException e) {
            e.printStackTrace();
        }
        AjaxUploadResponse uploadResponse = new AjaxUploadResponse(0, "上传成功", "/img/" + file.getSubmittedFileName());
        return uploadResponse;
    }


    @PostMapping("upload2")
    public AjaxUploadResponse uploadfile(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
        // 文件类型
        System.out.println(file.getContentType());
        // 文件名
        System.out.println(file.getOriginalFilename());
        // 将file上传到服务器的指定位置(webapp下的某个目录)
        String realPath = request.getRealPath("/");
        System.out.println("realPath:" + realPath);
        File dest = new File(realPath+"/img/"+file.getOriginalFilename());
        try {
            file.transferTo(dest);
        } catch (IOException e) {
            e.printStackTrace();
        }
        AjaxUploadResponse uploadResponse = new AjaxUploadResponse(0, "上传成功", "/img/" + file.getOriginalFilename());
        return uploadResponse;
    }
}

步骤五、实现文章新增功能

1、Dao层

@Override
public boolean addArticle(ArticleEntity article) {
    String sql = "INSERT INTO g_article VALUES (NULL,?,?,'imgtest.jpg','悟空网',?,0,NOW(),0)";
    return DBUtil.exUpdate(sql, article.getArticle_title(), article.getArticle_content(), article.getActicle_type()) > 0;
}

2、Service层

@Override
public boolean addArticle(ArticleEntity article) {
    return articleDao.addArticle(article);
}

3、Controller层

@PostMapping("/addarticle")
public AjaxOperateResponse addArticle(@RequestBody ArticleEntity article) {
    AjaxOperateResponse response = new AjaxOperateResponse(200, articleService.addArticle(article) ? "增加成功" : "增加失败");
    return response;
}

4、AjaxOperateResponse

@Data
@AllArgsConstructor
@NoArgsConstructor
public class AjaxOperateResponse<T> {
    private int code = 0;
    private String msg;
}

最终运行效果:

图片上传成功:

文章内容新增成功:

数据库添加成功:

Post请求中文乱码——web.xml中配置CharacterEncodingFilter过滤器

<!-- spring提供的characterEncodingFilter配置 -->
<!--Post请求过滤器-->
<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <!--观察发现这个类 CharacterEncodingFilter 有一个属性 encoding 所以提供一个initparm以及 value -->
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!-- 给 CharacterEncodingFilter类的对象进行初始化的赋值  request.setCharacterEncoding-->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <!-- 响应编码的设置 true 设置response -->
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<!-- 过滤器对哪些资源进行过滤呀 -->
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
     <!--“/*”表示所有的请求 -->  
    <url-pattern>/*</url-pattern>
</filter-mapping>

以上处理针对于post请求,如果是get请求,则可以采用如下两种解决方案:

注:Tomcat8.5以上不用处理get请求

方案1:使用字符串的方法将获取的中文字符转换为utf-8

方案2:修改server.xml中的 <connector port="9090" URIEncoding="utf-8">或者<Connector port="9090" useBodyEncodingForURI="true">

applicationContext.xml的配置文件

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

    <!--1、注解的支持-->
    <context:annotation-config></context:annotation-config>

    <!--2、设置组件的扫描路径-->
    <context:component-scan base-package="com.singerw"></context:component-scan>

    <!--3、检查springmvc配置文件中加入: <mvc:annotation-driven>
    如果没有加入mvc-annotation的节点,可能会出现如下错误:-->
    <!--自动开启处理器映射器-->
    <!--自动开启处理器适配器-->
    <mvc:annotation-driven></mvc:annotation-driven>

    <!--4、配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/blog/"/>
        <property name="suffix" value=".html"/>
    </bean>

    <!--5、让SpringMVC不处理静态资源的处理,注意,请求还是有走前端控制器,只不过不处理 -->
    <mvc:default-servlet-handler></mvc:default-servlet-handler>
</beans>

Web.xml配置文件

<?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_4_0.xsd"
         version="4.0">

    <!--1、配置Spring文件-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc-servlet.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--2、配置DispatcherServlet,加载SpringMVC的核心(请求分发器/前端控制器)-->
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!--配置DispatcherServlet要绑定Spring的配置文件-->
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--启动级别1:和服务器一起启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- 2.1将所有请求映射到 DispatcherServlet 进行处理 -->
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <!--2.2配置url-pattern 路径
            /:只匹配所有的请求,不会匹配jsp等页面
            /*:匹配所有的请求,包括匹配jsp等页面-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--3、Post请求过滤器-->
    <!--spring提供的characterEncodingFilter配置 -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <!--观察发现这个类 CharacterEncodingFilter 有一个属性 encoding 所以提供一个initparm以及 value -->
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <!--给CharacterEncodingFilter类的对象进行初始化的赋值request.setCharacterEncoding-->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <!--响应编码的设置 true 设置response -->
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <!--3.1 过滤器对哪些资源进行过滤呀 -->
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>
最后修改:2021 年 08 月 30 日 01 : 53 AM
如果觉得我的文章对你有用,请随意赞赏