慢慢来比较快,虚心学技术
SpingMVC的AbstractAnnotationConfigDispatcherServletInitializer提供了十分方便的注册过滤器的方法,通过重载getServletFilters()方法将我们自定义的过滤器注册到上下文中
如下代码:
public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/*AbstractAnnotationConfigDispatcherServletInitializer 会同时创
建 DispatcherServlet 和 ContextLoaderListener 。 GetServlet-ConfigClasses() 方法返回的带有 @Configuration 注解的
类将会用来定义 DispatcherServlet 应用上下文中的 bean 。 getRootConfigClasses() 方法返回的带有 @Configuration 注解的类将
会用来配置 ContextLoaderListener 创建的应用上下文中的 bean 。*/
@Override
protected Class>[] getRootConfigClasses() {
return new Class[]{RootConfig.class};
}
@Override
protected Class>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
@Override
protected String[] getServletMappings() {
logger.debug("DispatcherServlet获取匹配的前端控制器。。。。。。");
return new String[]{"/"};
}
/**
* 注册过滤器
*/
@Override
protected Filter[] getServletFilters() {
//将自定义过滤器实例数组返回
return new Filter[]{new MyFilter()};
}
}
/**
* 过滤器类
*/
public class MyFilter implements Filter {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.debug("过滤器初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.debug("执行过滤器");
Map parameterMap = servletRequest.getParameterMap();
Set keySet = parameterMap.keySet();
for(String key : keySet){
logger.debug("参数名:{},参数值:{}",key,parameterMap.get(key));
}
/**
* 执行该方法,如果有下一个过滤器则执行下一个过滤器,如果没有,则执行目标方法
* 如果不执行该方法,将无法访问目标路径请求
*/
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
页面访问结果
2019-03-07 17:19:53.859 DEBUG com.my.spring.filter.MyFilter - 执行过滤器
2019-03-07 17:19:53.860 DEBUG com.my.spring.filter.MyFilter - 参数名:data,参数值:[{'id':0}]
2019-03-07 17:19:53.863 DEBUG org.springframework.web.servlet.DispatcherServlet - GET "/SpringAction07/getBean?data={%27id%27:0}", parameters={masked}
如果是在Servlet3.0以下环境(tomcat7.0以下),使用纯注解实现SpringMVC就不可能实现了,这时候我们需要借助web.xml文件进行配置,但是我们并不希望全部使用xml进行配置,所以我们可以简单配置初始化的内容,其他配置仍使用javaConfig配置方式。
将SpittrWebAppInitializer初始化类去除,使用web.xml代替:
Archetype Created Web Application
contextClass
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
contextConfigLocation
com.my.spring.config.RootConfig
org.springframework.web.context.ContextLoaderListener
dispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextClass
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
contextConfigLocation
com.my.spring.config.WebConfig
1
dispatcherServlet
/
启动应用,访问正常!
前面所遇到的表单处理,我们处理的都是简单的字符串形式提交,但是上传文件进行提交是一个应用十分常见的需求,文件上传提交的格式是multipart格式,自然不可以像处理字符串形式参数一样去处理。SpringMVC提供了MultipartFile接口用来处理上传的文件:
public interface MultipartFile extends InputStreamSource {
String getName();
@Nullable
String getOriginalFilename();
@Nullable
String getContentType();
boolean isEmpty();
long getSize();
byte[] getBytes() throws IOException;
InputStream getInputStream() throws IOException;
default Resource getResource() {
return new MultipartFileResource(this);
}
void transferTo(File var1) throws IOException, IllegalStateException;
default void transferTo(Path dest) throws IOException, IllegalStateException {
FileCopyUtils.copy(this.getInputStream(), Files.newOutputStream(dest));
}
}
可以看到,MultipartFile接口可以用来获取文件名,文件大小等信息,还提供了一个InputStream,用来将文件以流的方式读取,还提供了一个便利的 transferTo() 方法,它能够帮助我们将上传的文件写入到文件系统中。
使用MultiparFile实现文件上传之前需要先配置Multipart解析器MultipartResolver,Spring3.1后内置两个MultipartResolver的实现供我们选择:
一般来说,StandardServletMultipartResolver会是更好的方案,因为它并不需要依赖于其他项目,使用原生的Servlet支持。只不过它只支持Servlet3.0以上的版本,如果低于等于Servlet3.0环境,需要使用CommonsMultipartResolver实现
①配置解析器,在上文代码的WebConfig中配置
public class WebConfig extends WebMvcConfigurationSupport {
/**
* 定义一个视图解析器
*
**/
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver resourceViewResolver = new InternalResourceViewResolver();
resourceViewResolver.setPrefix("/WEB-INF/view/");
resourceViewResolver.setSuffix(".jsp");
resourceViewResolver.setExposeContextBeansAsAttributes(true);
resourceViewResolver.setViewClass(org.springframework.web.servlet.view.JstlView.class);
return resourceViewResolver;
}
/**
* 配置Multipart解析器
*
*/
@Bean
public MultipartResolver multipartResolver(){
return new StandardServletMultipartResolver();
}
@Override
protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
②在SpittrWebAppInitializer中配置文件上传的初始化参数(必须)
//通过重载customizeRegistration()方法来配置multipart的默认参数
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setMultipartConfig(
new MultipartConfigElement("C:\\Users\\xxx\\Desktop\\uploads",2097152,4194304,20000000));
//设置写入的临时路径(可绝对路径)
//上传文件的最大容量(字节为单位),默认无限制。
//整个multipart请求的最大容量(字节为单位),默认无限制。
//在上传的过程中,如果文件大小达到了一个指定的最大容量,将会写入到临时文件路劲中。默认为0,也就是上传的文件都会写入到磁盘上。
}
③编写文件上传controller方法
@RequestMapping(value = "/upload",method = RequestMethod.POST)
public String upload(@RequestPart("file") MultipartFile multipartFile, Model model) throws IOException {
String fileName = new String(multipartFile.getOriginalFilename().getBytes("utf-8"));
//使用multipartFile的transferTo方法将文件存放到桌面
multipartFile.transferTo(new File("C:\\Users\\xxx\\Desktop\\"+multipartFile.getOriginalFilename()));
model.addAttribute("fileName",fileName);
model.addAttribute("fileSize",multipartFile.getSize());
return "showFile";
}
④编写上传文件的JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Hello World!
⑤编写文件信息视图:showFile.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page isELIgnored="false" %>
文件展示
文件名称:
文件大小:
KB
页面效果:
点击提交,将文件提交至服务器,上传成功
Spring内置了 CommonsMultipartResolver ,可以作为 StandardServletMultipartResolver 的替代方案,但是,由于使用的是Commons FileUpload的上传方式,需要引入jar支持:
commons-io
commons-io
2.4
commons-fileupload
commons-fileupload
1.3.1
最简单的配置方式就是其构造函数:
@Bean
public MultipartResolver multipartResolver() throws IOException
{
return new CommonsMultipartResolver();
}
配置文件上传参数,与 StandardServletMultipartResolver 有所不同,CommonsMultipart-Resolver 不会强制要求设置临时文件路径。默认情况下,这个路径就是 Servlet 容器的临时目录。不过,通过设置 uploadTempDir 属性,我们可以将其指定为一个不同的位置
@Bean
public MultipartResolver multipartResolver() throws IOException
{
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
//设置临时文件夹
multipartResolver.setUploadTempDir(new FileSystemResource("C:\\Users\\xxx\\Desktop\\uploads"));
//设置最大内存大小
multipartResolver.setMaxInMemorySize(100000);
//设置上传文件的最大容量
multipartResolver.setMaxUploadSize(2097152);
return multipartResolver;
}
经测试,上传成功
注:如果没有引入上述两个jar包,会报文件找不到的错误:
javax.servlet.ServletException: Servlet.init() for servlet dispatcher threw exception
Factory method 'multipartResolver' threw exception; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/fileupload/FileItemFactory
文件下载使用Spring提供的ResponseEntity实现
ResponseEntity:可以添加HttpStatus状态码的HttpEntity的扩展类。被用于RestTemplate和Controller层方法
①编写下载文件的controller方法:
@RequestMapping("/download")
public ResponseEntity filedownload(HttpServletRequest request, String filename) throws Exception{
//此处指定只从桌面获取文件
String path = "C:\\Users\\xxx\\Desktop\\";
File file = new File(path+File.separator+filename);
//返回头部设置
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("attachment",filename);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//获取文件输入流
InputStream is = new FileInputStream(file);
//将文件转换成byte数组
byte[] bytes = new byte[is.available()];
is.read(bytes);
//封装信息返回
return new ResponseEntity(bytes,headers, HttpStatus.OK);
}
②编写JSP文件下载资源:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page isELIgnored="false" %>
文件展示
文件名称:
文件大小:
KB
文件下载
页面展示如下,点击文件下载,弹出资源管理窗口,将其保存至目标路径
下载完成
自定义异常在SpringMVC中是十分普遍的,有时候我们需要控制异常的输出样式,而不是赤裸裸的将错误信息展示在用户面前,那样并不友好,可能还有点辣眼睛
那么,SpringMVC中,怎么捕获异常并友好输出呢?也许你会想到使用try-catch的方式去处理,但是那样耦合性太强了,SpringMVC提供了两个注解进行捕捉处理
@ExceptionHandler()注解可以捕捉当前控制器内所有方法抛出的特定异常,而不需要在控制器内每个方法上去标识
①编写自定义异常类,继承Exception
public class MyException extends Exception{
public MyException() {
super();
}
public MyException(String message) {
super(message);
}
public MyException(String message, Throwable cause) {
super(message, cause);
}
}
②编写测试Controller
@Controller
public class ExceptionController {
@ExceptionHandler(MyException.class)//捕捉当前控制器内任意方法抛出MyException
public String toError(MyException myException, Model model){
model.addAttribute("error",myException.getMessage());
//返回指定的错误视图,经过特殊编写,会比浏览器默认错误页面更美观
return "error";
}
@RequestMapping("/exception")
public String testException() throws MyException {
//为了测试抛出异常,设置条件恒为true
if(1==1){
throw new MyException("系统搞错咯");
}
return "123";
}
}
③编写错误页面:error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page isELIgnored="false" %>
错误页面
您好,系统异常,请稍后重试
测试结果:
即便如上述代码已经十分方便,我们仍有可能需要在每个有可能抛出异常的控制器内编写@ExceptionHandler()注解方法,显然Spring可以做的更好。
@ControllerAdvice注解标注的类可以捕捉应用内所有的错误,并结合@ExceptionHandler()在其内进行特殊处理,系统内抛出的所有错误,都会经过该类处理
①编写统一处理类
@ControllerAdvice
public class ExceptionHelper {
//对特殊异常进行处理
@ExceptionHandler(MyException.class)
public String toError(MyException myException, Model model){
model.addAttribute("error",myException.getMessage());
return "error";
}
}
②普通controller方法
@Controller
public class ExceptionController {
@RequestMapping("/exception")
public String testException() throws MyException {
//为了测试抛出异常,设置条件恒为true
if(1==1){
throw new MyException("系统搞错咯");
}
return "123";
}
}
测试结果:显然我们并没有在ExceptionController中对该错误进行显式处理,结果却跳转到错误页面,证明处理成功