Spring4详解系列(七)SpringMVC的高级技术

1、自定义DispatcherServlet的配置

SpitterWebinitializer实现了AbstractAnnotationConfigDispatcherServletInitializer抽象类,并重写了三个必须的方法,实际上还可对更多方法进行重写,以便实现额外的配置。

如对customizeRegistration方法进行重写,该方法是AbstractDispatcherServletInitializer的方法,无实际的方法体。当AbstractAnnotationConfigDispatcherServletInitializer将DispatcherServlet注册到Servlet容器中后,就会调用customizeRegistration方法,并将Servlet注册后得到的Registration.Dynamic传入。可通过重写customizeRegistration方法设置MultipartConfigElement,如图所示:

@Override
protected void customizeRegistration(Dynamic registration) {
      registration.setMultipartConfig(
           new MultipartConfigElement("/tmp/spittr/uploads"));
}

添加其他Servlet和Filter

AbstractAnnotationConfigDispatcherServletInitializer会创建DispatcherServlet和ContextLoaderListener,当需要添加其他Servlet和Filter时,只需要创建一个新的初始化器即可,最简单的方式是实现WebApplicationInitializer接口。

public class MyServletInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        // 注册servlet 
        Dynamic servlet = servletContext.addServlet("myServlet", MyServlet.class);
        // 映射servlet 
        servlet.addMapping("/custom/**");

        // 注册filter 
        FilterRegistration.Dynamic filter = servletContext.addFilter("myFilter", MyFilter.class);
        // 添加filter的映射路径
        filter.addMappingForUrlPatterns(null, false, "/custom/*");

    }
}

使用AbstractAnnotationConfigDispatcherServletInitializer注册Filter并将其映射到DispatcherServlet

@Override
protected Filter[] getServletFilters() {
    return new Filter[] { new MyFilter() };
}

2、处理multipart形式数据

处理multipart数据主要用于处理文件上传操作。需要配置multipart解析器读取multipart请求。

配置multipart解析器 DispatcherServlet并未实现任何解析multipart请求数据功能,它只是将任务委托给MultipartResolver策略接口实现,通过该实现解析multipart请求内容,Spring中内置了CommonsMultipartResolver和StandardServletMultipartResolver两个解析器。 使用StandardServletMultipartResolver使用Java配置如下:

@Override
protected void customizeRegistration(Dynamic registration) {
    registration.setMultipartConfig(new 
                   MultipartConfigElement("/tmp/spittr/uploads",2079152, 4194304, 0));
}

通常来讲使用StandardServletMultipartResolver是最佳的选择,设置StandardServletMultipartResolver的,但是它的设置不是在Spring配置中进行的,而是在Servlet配置中。起码要配置一下存放临时文件的位置,进一步来讲,还要将multipart配置为DispatcherServlet的一部分。

(1)继承自WebMvcConfigurerAdapter的servlet初始化类中配置的DispatcherServlet,那么就可以在servlet注册时通过调用setMultipartConfig()方法来配置multipart详情。比如:

DispatcherServlet ds = new DispatcherServlet();
Dynamic registration = context.addServlet("appServlet", ds);
registration.addMapping("/");
registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads"));

(2)继承自AbstractAnnotationConfigDispatcherServletInitializer或者AbstractDispatcherServletInitializer的servlet初始化类进行的配置,没有创建DispatcherServlet的实例或者使用servlet上下文对其进行注册。因此就没有直接的引用供Dynamicservlet注册来使用。重写customizeRegistration()方法来进行配置:

@Override
protected void customizeRegistration(Dynamic registration) {
    registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads"));
}

(3)MultipartConfigElement的唯一参数设置了上传文件时临时文件的存放位置。也可以进行其他一些设置: 
文件上传的最大值(byte),默认没有限制; 
所有multipart请求的文件最大值(byte),不管有多少个请求,默认无限制; 
直接上传文件(不需存储到临时目录)的最大值(byte),默认是0,也就是所有的文件都要写入硬盘; 
例如,你想设置文件大小不超过2MB,所有请求的总和不超过4MB,并且所有文件都要写入硬盘,那么就可以这样设置:

@Override
protected void customizeRegistration(Dynamic registration) {
    registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads", 2097152, 4194304, 0));
}

(4)传统的web.xml的方式来设置的DispatcherServlet,那么就需要使用多个元素,其默认值和MultipartConfigElement相同,并且是必填项:

<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <multipart-config>
        <location>/tmp/spittr/uploads</location>
        <max-file-size>2097152</max-file-size>
        <max-request-size>4194304</max-request-size>
    </multipart-config>
</servlet>

(5)配置Jakarta Commons FileUpload解析器CommonsMultipartResolver 
这里设置了文件的最大大小为2MB,最大的内存中大小为0,即每个上传文件都会直接写入磁盘的。但是它是无法设置multipart请求总的文件大小的:

@Bean
public MultipartResolver multipartResolver() throws IOException {
    CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
    multipartResolver.setUploadTempDir(new FileSystemResource("/tmp/spittr/uploads"));
    multipartResolver.setMaxUploadSize(2097152);
    multipartResolver.setMaxInMemorySize(0);
    return multipartResolver;
}

3、处理multipart请求 

可在控制器的方法参数上添加@RequestPart注解,如下所示:

@RequestMapping(value="/register", method=POST)
public String processRegistration(
    @RequestPart("profilePicture") byte[] profilePicture,
    @Valid Spittr spitter,
    Errors errors) {
    profilePicture.transferTo(new File("/data/spittr" + profilePicture.getOriginalFilename()));
}

在SpringBoot项目中使用完成文件上传:(此案例只展示核心代码,如有需要,留言即可)

<!-- 添加产品 Modal -->
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true" onclick="autoRefresh()">&times;</span></button>
                    <h4 class="modal-title" id="myModalLabel">添加产品</h4>
                </div>
                <form action="/saveGoods" method="post" enctype="multipart/form-data"  class="form-horizontal" name="goodsForm" id="goodsForm">
                <div class="modal-body">
                   <!-- sssssssssssssssssssssssssss -->
                    <div class="form-group">
                        <label class="col-sm-3 control-label">产品名称</label>
                        <div class="col-sm-8">
                            <input type="text" name="goodsName" class="form-control" id="goodsName" placeholder="产品名称">
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-sm-3 control-label">产品图样</label>
                        <div class="col-sm-8">
                            <input type="file" name="goodsImgs" class="form-control" id="goodsImgs" accept="image/jpg, image/png, image/jpeg, image/gif">
                        </div>
                    </div>
                    <!--eeeeeeeeeeeeeeeeeeeeeeeeeee-->
                </div>
                <div class="modal-footer">
                    <input type="reset" value="重置" class="btn btn-danger">
                    <button type="button" class="btn btn-default" onclick="autoRefresh()" data-dismiss="modal">取消</button>
                    <button type="button" id="btn-submit" onclick="saveGoods()" class="btn btn-primary">确定</button>
                </div>
                </form>
            </div>
        </div>
    </div>

<script th:inline="javascript">
// 保存产品
    function saveGoods(){
        // document.getElementById("goodsForm").submit();
        $("#goodsForm").bootstrapValidator('validate');//提交验证
        if ($("#goodsForm").data('bootstrapValidator').isValid()) {//获取验证结果,如果成功,执行下面代码
            // 点击添加确定按钮的时候,判断产品ID是否存在,存在就是修改,不存在就是添加
            var gid = $("#titleSSID").val();
            var pic = $('#goodsImgs')[0].files[0];
            var formData = new FormData();
            formData.append('goodsId',gid);
            formData.append('goodsName',$("#goodsName").val());
            formData.append('goodsCode',$("#goodsCode").val());
            formData.append('goodsImg',pic);
            $.ajax({
                //几个参数需要注意一下
                type: "POST",//方法类型
                dataType: "json",//预期服务器返回的数据类型
                cache: false, //上传文件不需要缓存
                url: "/saveGoods",//url
                // data: $('#goodsForm').serialize(),
                data:formData,
                cache: false,
                contentType: false,
                processData: false,
                success: function (result) {
                    if (result.succ == true) {
                        if(confirm("添加成功,是否继续添加?")){
                            document.getElementById("goodsName").value = "";
                            document.getElementById("goodsCode").value = "";
                        }else {
                            location.replace(location.href);
                        }
                    }else {
                        alert(result.message);
                    }
                },
                error : function() {
                    alert("系统繁忙,请稍后重试!");
                }
            });
        }
    }
</script>

Controller接收:

@PostMapping("/saveGoods")
@ResponseBody
public JSONObject saveGoods(HttpServletRequest request,String goodsId, String 
                                 goodsName, String goodsCode, MultipartFile goodsImg, ){
       JSONObject jsonObject = new JSONObject();
       // 这里我的逻辑处理是,把goodsImg转成IO流,上传到了阿里云
}

4、异常处理

Spring提供了多种方式将异常转换为响应

  • 特定的异常将会自动映射为指定的HTTP状态码。
  • 异常上可以添加@ResponseStatus注解,从而将其映射为某个HTTP状态码。
  • 在方法上可添加@ExceptionHandler注解,使其用来处理异常。

编写异常处理方法

可在请求中直接使用try/catch处理异常,其与正常Java方法中的try/catch相同,同时,也可编写处理器来处理特定异常,当出现特定异常时,将有处理器委托方法进行处理。

@ExceptionHandler(DuplicateSpittleException.class)
public String handleDuplicateSpittle() {
    return "error/duplicate";
}

为控制器添加通知

控制器通知是任意带有@ControllerAdvice注解的类,该类包含如下类型的一个或多个方法。

  • @ExceptionHandler注解标注的方法。
  • @InitBinder注解标注的方法。
  • @ModelAttribute注解标注的方法。

上面方法会运用到整个应用程序所有控制器中带有@RequestMapping注解的方法上。

@ControllerAdvice
public class AppWideExceptionHandler {
    @ExceptionHandler(DuplicateSpittleException.class)
    public String duplicateSpittleHandler() {
        return "error/duplicate";
    }
}

这样,任意控制器方法抛出了DuplicateSpittleException异常,都会调用这个duplicateSpittleHandler方法处理异常。

5、跨重定向请求传递数据

对于重定向而言,若需要从发起重定向的方法传递数据给处理重定向方法中,有如下两种方法:

  1. 使用URL模版以路径变量和或查询参数形式传递数据。
  2. 通过flash属性发送数据。

通过URL模版进行重定向

如前面讲到的通过redirect:/spitter/{username}进行重定向,该方法会直接根据username确定url,并非十分安全的做法,可使用进行如下处理:

model.addAttribute("username", spitter.getUsername());
return "redirect:/spitter/{username}";

当需要传递参数,如id时,可进行如下处理:

model.addAttribute("username", spitter.getUsername());
model.addAttribute("spitterId", spitter.getId());
return "redirect:/spitter/{username}";

若username为zhangsan;id为100。这样访问的url为/spitter/zhangsan?spitterId=100。这种方法只能传递简单的值,无法发送更为复杂的值,此时需要使用flash属性。

使用flash属性 
在重定向发生之前,将数据放在会话中,在重定向完成之后,从会话中将其取出,并将会话中的数据清理掉。而 Spring 的实现略有不同,需要使用 RedirectAttributes 对象,会话中 flash 属性会一直携带这些数据,直到下一次请求,然后才会消失。

所有通过 RedirectAttributes的addFlashAttribute 添加的属性会复制到会话中。在重定向后,存在会话中的 flash 属性会被取出,并从会话转移到模型中。处理重定向的方法就能从模型中访问 Spittle 对象了。

Spring提供了通过RedirectAttributes设置flash属性的方法,这个Spring3.1引入的Model的一个子接口。RedirectAttributes提供 了一组addFlashAttribute()方法来添加flash属性

@ReuqestMapping(value="/register", method=POST)
public String processRegistration(Spitter spitter, RedirectAttributes model) {
    spitterRepository.save(spitter);
    model.addAttribute("username", spitter.getUsername());
    model.addFlashAttribute("spitter", spitter);
    return "redirect:/spitter/{username}";
}

这样spitter对象也被传递到重定向页面中,可直接访问spitter对象。获取对象:

@RequestMapping(value="/{username}", method = RequestMethod.GET)
public String showSpitterProfile(@PathVariable String username, Model model){
    if(!model.containsAttribute("spitter")){
        model.addAttribute(spitterRepository.findByUsername(username));
    }
    return "profile";
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值