Spring实战第七章————SpringMVC配置的替代方案
2021-07-04 08:06
标签:app 使用 pre rtu position 创建 max 模版 post 我们之前在SpittrWebAppInitializer所编写的三个方法仅仅是必须要重载的abstract方法。但还有更多的方法可以进行重载,从而实现额外的配置。 例如customizeRegistration()。在AbstractAnnotationConfigDispatcherServletInitializer将DispatcherServlet主车道Servlet容器后,就会调用该方法,并将Servlet注册后得到的Registration.Dynamic传递进来。例如稍后我们将要计划使用Servlet3.0对multipart的支持,那么需要使用DispatcherServlet的registration来启用multipart请求。 借助customizeRegistration()方法的ServletRegistration.Dynamic来设置MultipartConfigElement。 基于Java的初始化器(initializer)的一个好处就在于我们可以定义任意数量的初始化器类。 这段代码注册了一个Servlet并将其映射到一个路径上。我们也可以用这个方式来手动注册DispatcherServlet。类似的我们也可以这样注册Filter和Listener。 WebApplicationInitializer是一个在注册servlet、filter、listener时比较推荐的方式,当然你是使用基于Java的配置方式并将应用部署在Servlet3.0容器上的。如果你仅仅需要注册一个filter并将其映射到DispatcherServlet,那么也可以使用AbstractAnnotationConfigDispatcherServletInitializer。要注册多个filter并将它们映射到DispatcherServlet,你所要做的仅仅是重写getServletFilters()方法。比如: 如你所见,该方法返回了一个javax.servlet.Filter的数组,这里仅仅返回了一个filter,但是它可以返回很多个。同时这里不再需要为这些filter去声明映射,因为通过getServletFilters()返回的filter会自动地映射到DispatcherServlet。 在之前我们是使用AbstractAnnoatationConfigDispatcherServletInitializer自动注册DispatcherServlet和ContextLoaderListener。但也可以按传统方法在web.xml中注册。 设置web.xml使用基于Java的配置 在WEB应用中用户经常会上传内容。而Spittr应用在两个地方需要文件上传。当新用户注册应用的时候,会需要他们上传一张图片。而当他们提交新的Spittle时可能会上传图片。一般表单提交形成的请求结果很简单,就是以&为分割符的多个name-value。尽管这种编码形式很简单,但对像图片这样的二进制数据就不合适了。而multipart格式的数据会将一个表单后拆分为多个部分,每个部分对应一个输入域。下面展现mulrtipart的请求体: 尽管multipart看起来复杂,但在SpringM中处理却很容易。而首先需要要我们配置一个multipart解析器。 DispatcherServlet并没有任何实现解析multipart请求数据的功能。他将这任务委托给Spring中MultipartResolver策略接口的实现,从Spring3.1开始,Spring内置了两个MultipartResolver的实现供我们选择: 一般选用StandardServletMultipartResolver。兼容Servlet3.0的StandardServletMultipartResolver没有构造器参数,也没有要设的属性。因此在Spring上下文中将其声明为bean会非常简单,如下所示: 那么如何配置StandardServletMultipartResolver的限制条件呢?我们会在Servlet中指定multipart的限定条件。至少也要写入文件上传的过程中所写入得临时文件路径。如果不设定这个最基本配置的话,StandardServletMultipartResolver就无法正常工作。所以我们会在web.xml或Servlet初始化类中将multipart的具体细节作为DispatcherServlet配置的一部分。采用Servlet初始化类的方式来配置: 如果配置的Servlet初始化类继承了AbstractAnnotationConfigDispatcherServletInitializer或AbstractDispatcherServletInitializer的话,可以重载customizeRegistration()方法来配置multipart的具体细节。 MultipartConfigElement构造器也可以进行其他一些设置: 如果你是使用的传统的web.xml的方式来设置的DispatcherServlet,那么就需要使用多个 当配置好了对multipart请求的处理,接下来要编写控制器方法来接受上传的文件。实现这点最常见的方法就是在某个控制器方法参数上添加@Requestpart注解。 当注册表单提交时,请求部分的数据就会赋予到profilePicture属性中,如果用户没有选中一个文件,那么该数组就会是一个空值(不是null)。既然已经获取到上传的文件,下面所需要的就是将文件保存。 不管发生什么事情Servlet请求的输出都是一个Servlet响应,所以如果出现异常,那么它的输出依旧是Servlet响应,一场必须是以某种方式转换为响应。Spring提供了多种方式将一场转换为响应: 在默认情况下,Spring将自身的一些异常转换为合适的状态码。 将异常映射为特定的状态码 将异常映射为状态码大多数情况下是比较简单有效的,但是如果想让响应不仅仅只有一个状态码呢?也许你想对异常进行一些处理,就行处理请求一样。 例如,SpittleRepository的save()方法在用户重复创建Spittle时抛出了一个DuplicateSpittleException,那么SpittleController的saveSpittle()方法就需要处理该异常。如下面的代码所示,saveSpittle()方法可以直接处理该异常: 这个方法有两个路径,我们可以用别的方法处理异常,那这个方法可以简单点。首先处理正确路径的saveSpittle方法: 现在在SpittleController中添加一个新的方法: @ExceptionHandler注解应用在handleDuplicateSpittle()方法上,用来指定在有DuplicateSpittleException异常抛出时执行。而值得注意的是,@ExceptionHandler注解的方法在同一个控制器里是通用的额,即无论SpittleController的哪一个方法抛出DuplicateSpittleException异常,handleDuplicateSpittle()方法都可以对其进行处理,而不再需要在每一个出现异常的地方进行捕获。那么,@ExceptionHandler注解的方法能不能捕获其他controller里的异常啊?在Spring3.2里是可以的,但仅仅局限于定义在控制器通知类里的方法。 如果controller类的特定切面可以跨越应用的所有controller进行使用,那么这将会带来极大的便捷。例如,@ExceptionHandler方法就可以处理多个controller抛出的异常了。如果多个controller类都抛出同一个异常,也许你会在这些controller进行重复的@ExceptionHandler方法编写。或者,你也可以编写一个异常处理的基类,供其他@ExceptionHandler方法进行继承。 Spring3.2带来了另外一种解决方法:控制器通知。控制器通知是任意带有@ControllerAdvice注解的类,这个类会包含一个或多个如下类型的方法: @ControllerAdvice注解的类中的这些方法会在整个应用中的所有controller的所有@RequestMapping注解的方法上应用。 在处理完POST请求过后通常应该执行重定向。这样可以避免用户点击浏览器的刷新按钮或后退按钮时,客户端重新执行危险的POST请求。在第五章中,已经在控制器方法返回的视图名称中使用了redirect:前缀,这时返回的String不是用来寻找视图,而是浏览器进行跳转的路径: 也许你认为Spring处理重定向只能这样了,但是:Spring还可以做得更多。 明显的,现在不能再redirect时使用model来传递数据了。但是还有其他方法用来从重定向的方法中获取数据: 返回的重定向String并没有什么变化,但是由于model中的spitterId属性并没有映射到URL中的占位符,它会自动作为查询参数。 我们传递了一个Spitter对象给addFlashAttribute()方法,在重定向之前,所有的flash属性都会复制到会话中,在重定向之后,存在会话中的flash属性会被取出,并从会话转移到模型之中。处理重定向的方法就能从模型中访问Spitter对象了 Spring实战第七章————SpringMVC配置的替代方案 标签:app 使用 pre rtu position 创建 max 模版 post 原文地址:https://www.cnblogs.com/wbw2621/p/9614105.htmlSpringMVC配置的替代方案
自定义DispatherServlet配置
@Override
protected void customizeRegistration(Dynamic registration) {
registration.setMultipartConfig(
new MultipartConfigElement("/tmp/spittr/uploads"));
}
添加其它的Servlet和Filter
因此,如果需要定义额外的组件,只需新建相应的初始化类即可。最简单的方法就是实现Spring的WebApplicationInitializer接口。import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.WebApplicationInitializer;
import com.myapp.MyServlet;
public class MyServletInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// 定义servlet
Dynamic myServlet = servletContext.addServlet("myServlet", MyServlet.class);
// 映射servlet
myServlet.addMapping("/custom/**");
}
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// 注册一个filter
javax.servlet.FilterRegistration.Dynamic filter = servletContext.addFilter("myFilter", MyFilter.class);
// 添加映射
filter.addMappingForUrlPatterns(null, false, "/custom/*");
}
@Override
protected Filter[] getServletFilters() {
return new Filter[] { new MyFilter() };
}
在web.xml中声明DispatcherServlet
处理multipart形式的数据
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="firstName"
Charles
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="lastName"
Xavier
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="email"
charles@xmen.com
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="username"
professorx
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="password"
letmein01
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="profilePicture"; filename="me.jpg"
Content-Type: image/jpeg
[[ Binary image data goes here ]]
------WebKitFormBoundaryqgkaBn8IHJCuNmiW--
配置multipart解析器
@Bean
public MultipartResolver multipartResolver() throws IOException {
return new StandardServletMultipartResolver();
}
DispatcherServlet ds=new DispatcherServlet();
Dynamic registration=context.addServlet("appServlet",ds);
registration.addMapping("/");
registration.asetMultipartConfig(
new MultipartCofigElement("/tmp/spittr/upolads"));
//设置multipart上传配置,路径,文件不超过2MB,请求不超过4MB
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration){
registration.setMultipartConfig(
new MultipartConfigElement("C:/test",2097152,4194304,0));
}
处理multipart请求
假设你想让用户可以在注册时上传图像,那么就需要对注册表单进行更改从而用户可以选择一个图片,同时还需要更改SpitterController中的processRegistration()方法以获取上传的文件。现在所需做的就是更新processRegistration()方法:@RequestMapping(value = "/register", method = RequestMethod.POST)
public String processRegistration(@RequestPart("profilePicture") byte[] profilePicture, @Valid Spitter spitter,
Errors errors) {
···
}
处理异常
将异常映射为HTTP状态码
Spring异常
HTTP状态码
BindException
400 - Bad Request
ConversionNotSupportedException
500 - Internal Server Error
HttpMediaTypeNotAcceptableException
406 - Not Acceptable
HttpMediaTypeNotSupportedException
415 - Unsupported Media Type
HttpMessageNotReadableException
400 - Bad Request
HttpMessageNotWritableException
500 - Internal Server Error
HttpRequestMethodNotSupportedException
405 - Method Not Allowed
MethodArgumentNotValidException
400 - Bad Request
MissingServletRequestParameterException
400 - Bad Request
MissingServletRequestPartException
400 - Bad Request
NoSuchRequestHandlingMethodException
404 - Not Found
TypeMismatchException
400 - Bad Request
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Spittle Not Found")
public class SpittleNotFoundException extends Exception {
}
编写异常处理的方法
@RequestMapping(method = RequestMethod.POST)
public String saveSpittle(SpittleForm form, Model model) {
try {
spittleRepository.save(new Spittle(null, form.getMessage(),
new Date(), form.getLongitude(), form.getLatitude()));
return "redirect:/spittles";
} catch (DuplicateSpittleException e) {
return "error/duplicate";
}
}
@RequestMapping(method = RequestMethod.POST)
public String saveSpittle(SpittleForm form, Model model) {
spittleRepository.save(new Spittle(null, form.getMessage(),
new Date(), form.getLongitude(), form.getLatitude()));
return "redirect:/spittles";
}
@ExceptionHandler(DuplicateSpittleException.class)
public String handleDuplicateSpittle() {
return "error/duplicate";
}
那什么是控制器通知类呢?这就是接下来要介绍的为控制器添加通知
@ControllerAdvice注解本身是使用了@Component注解的,因此,使用@ControllerAdvice注解的类会在组件扫描时进行提取,就行使用@Controller注解的类一样。@ControllerAdvice的最实用的一个功能就是将所有的@ExceptionHandler方法集成在一个类中,从而可以在一个地方处理所有controller中的异常。例如,假设你想处理应用中所有的DuplicateSpittleException异常,可以采用下面的方法:import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
// 声明控制器增强
@ControllerAdvice
public class AppWideExceptionHandler {
// 定义异常处理方法
@ExceptionHandler(DuplicateSpittleException.class)
public String handleDuplicateSpittle() {
return "error/duplicate";
}
@ExceptionHandler(SpittleNotFoundException.class)
public String handleSpittleNotFound() {
return "error/duplicate";
}
}
跨重定向请求传递数据
return "redirect:/spitter/" + spitter.getUsername();
特别是一个重定向方法如何向处理重定向的方法发送数据呢?一般的,当一个处理函数结束后,方法中的model数据都会作为request属性复制到request中,并且request会传递到视图中进行解析。因为控制器和视图面对的是同一个request,因此request属性在forward时保留了下来。
但是,当一个控制器返回的是一个redirect时,原来的request会终止,并且会开启一个新的HTTP请求。原来request中所有的model数据都会清空。新的request不会有任何的model数据。
通过URL模版进行重定向
@RequestMapping(value="/",method=POST)
public String processRegistration(Spitter spitter,Model model){
spitterRepository.save(spitter);
model.addAttribute("username",spitter.getUsername());
model.addAttribute("spitterId",spitter.getId());
return "redirect:/spitter/{username}";
}
如果username是habuma,spitterId是42,那么返回的重定向路径将是/spitter/habuma?spitterId=42。
使用路径参数和查询参数传递数据比较简单,但是它也有局限性。它只适用于传递简单值,比如String和数字,不能传递比较复杂的东西,那么我们就需要flash属性来帮忙。使用flash属性
@RequestMapping(value="/",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}";
}
上一篇:Java实现wc部分功能
文章标题:Spring实战第七章————SpringMVC配置的替代方案
文章链接:http://soscw.com/essay/101647.html