Spring MVC 实践 - Component

简介: Spring MVC 实践标签 : Java与WebConverter Spring MVC的数据绑定并非没有任何限制, 有案例表明: Spring在如何正确绑定数据方面是杂乱无章的.

Spring MVC 实践

标签 : Java与Web


Converter

Spring MVC的数据绑定并非没有任何限制, 有案例表明: Spring在如何正确绑定数据方面是杂乱无章的. 比如: Spring总是试图用默认的语言区域将日期输入绑定到java.util.Data, 如果想要使用不同的日期格式(format),就需要Converter的协助.

Spring提供了Converter接口来供开发者自定义Converter类:

/**
 * @since 3.0
 * @param <S> the source type
 * @param <T> the target type
 */
public interface Converter<S, T> {
    T convert(S source);
}
  • 自定义Converter:
/**
 * @author jifang.
 * @since 2016/6/19 7:23.
 */
public class StringDateConverter implements Converter<String, Date> {

    private String pattern;

    public StringDateConverter(String pattern) {
        this.pattern = pattern;
    }

    @Override
    public Date convert(String source) {
        try {
            return new SimpleDateFormat(pattern).parse(source);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}
  • 配置
    为了能够让Spring MVC使用我们自定义的Converter, 需要在配置文件中配置一个ConversionServiceFactoryBean:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="com.fq.mvc.converter.StringDateConverter">
                <constructor-arg type="java.lang.String" value="yyyy-MM-dd hh:mm:ss"/>
            </bean>
        </set>
    </property>
</bean>

然后为<annotation-driven/>配置conversion-service属性:

<mvc:annotation-driven conversion-service="conversionService"/>

注: 还可以使用FormattingConversionServiceFactoryBean来加载Converter, 由于其配置方法与ConversionServiceFactoryBean, 故在此就不再赘述.

  • Controller
@RequestMapping("/add_user.do")
public String addUser(User user, BindingResult binding) {
    if (binding.hasErrors()) {
        FieldError error = binding.getFieldError();
        // log ...
    }

    service.addUser(user);
    return "redirect: users.do";
}

BindingResult参数中放置了Spring的所有绑定错误.


Interceptor

Spring MVC的拦截器类似于Servlet中的Filter(关于Filter,详细可参考Servlet - Listener、Filter、Decorator),用于Controller进行预处理和后处理.

Spring提供了Interceptor接口来供开发者自定义Interceptor类:

public interface HandlerInterceptor {

    /**
     * 进入Controller方法前执行
     * 应用场景: 身份认证、身份授权等
     */
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception;

    /**
     * 进入Controller方法后, 返回ModelAndView前执行
     * 应用场景: 将公共模型数据填充到ModelAndView、统一指定视图等
     */
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;

    /**
     * 执行完Controller方法后执行
     * 应用场景: 统一日志处理、统一异常处理等
     */
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;

}

示例: 统计Controller执行耗时.
  • 自定义Interceptor
/**
 * @author jifang
 * @since 16/7/4 上午10:35.
 */
public class HandleTimeInterceptor implements HandlerInterceptor {

    private static final String START_TIME = "start_time";

    private static final String HANDLE_TIME = "handle_time";

    private static final Logger LOGGER = LoggerFactory.getLogger(HandleTimeInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        request.setAttribute(START_TIME, System.currentTimeMillis());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        long start = (long) request.getAttribute(START_TIME);
        request.setAttribute(HANDLE_TIME, System.currentTimeMillis() - start);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        String uri = request.getRequestURI();
        long consume = (long) request.getAttribute(HANDLE_TIME);
        LOGGER.info("uri: {} consume {}s", uri, consume / 1000);
    }
}
  • 配置
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.fq.mvc.interceptor.HandleTimeInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

Upload

Spring MVC提供了对Servlet 3.0文件上传的支持(关于Servlet 3.0文件上传可参考博客Servlet - Upload、Download、Async、动态注册).

Spring MVC提供了MultipartFile接口,上传到应用中的文件都被包装在一个MultipartFile对象中:

MultipartFile 描述
String getName() Return the name of the parameter in the multipart form.
String getOriginalFilename() Return the original filename in the client’s filesystem.
long getSize() Return the size of the file in bytes.
boolean isEmpty() Return whether the uploaded file is empty, that is, either no file has been chosen in the multipart form or the chosen file has no content.
String getContentType() Return the content type of the file.
byte[] getBytes() Return the contents of the file as an array of bytes.
InputStream getInputStream() Return an InputStream to read the contents of the file from.
void transferTo(File dest) Transfer the received file to the given destination file.

在Servlet 3.0及更高版本的容器中进行文件上传编程,总是围绕着@MultipartConfig注解和Part接口,处理上传文件的Servlet必须以@MultipartConfig注解标注, 但DispatcherServlet是Spring jar包已经编译好的类, 无法进行修改,值得庆幸的是Servlet 3.0还可以使用部署描述符web.xml将一个Servlet变为MultipartConfig Servlet:

<servlet>
    <servlet-name>mvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/mvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <multipart-config>
        <max-file-size>20848820</max-file-size>
        <file-size-threshold>1048576</file-size-threshold>
    </multipart-config>
</servlet>
<servlet-mapping>
    <servlet-name>mvc</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

此外, 在mvc-servlet.xml文件中配置一个MultipartResolver:

<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>

此时就可以进行文件上传编程了:

@RequestMapping("/upload.do")
public String upload(MultipartFile file) throws IOException {
    String name = file.getOriginalFilename();
    String fileName = String.format("/data/file/%s", name);
    file.transferTo(new File(fileName));
    return "file_upload";
}

Exception

系统异常包含两类: 预期异常运行时异常RuntimeException.前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生.

基于Spring MVC的DAOServiceController的异常都可以通过throw向上层抛出,最后统一由DispatcherServlet的异常处理器进行处理.

  • 自定义异常
    如果Controller/Service/DAO抛出此类异常说明是预期异常:
/**
 * @author jifang.
 * @since 2016/6/21 16:28.
 */
public class MVCException extends Exception {

    private String message;

    public MVCException(String message) {
        super(message);
        this.message = message;
    }

    @Override
    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
  • 异常处理器
/**
 * @author jifang.
 * @since 2016/6/21 16:33.
 */
public class MVCExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

        String message;
        if (ex instanceof MVCException) {
            message = ex.getMessage();
        } else {
            message = "未知异常";
        }

        return new ModelAndView("error", "message", message);
    }
}
  • error.vm
<html>
<head>
    <title>错误信息</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
</head>
<body>
${message}
</body>
</html>
  • 注册异常处理器
<bean class="com.fq.mvc.exception.MVCExceptionResolver"/>

JSON

JSON数据格式形式简单, 解析方便, 因此常用在接口调用、HTML页面中.

Spring MVC对其提供了如下支持:在Controller方法上添加@ResponseBody注解, Spring MVC会自动将Java对象转换成JSON字符串输出; 在方法形参上添加@RequestBody注解, Spring MVC会自动将JSON串转换成Java对象:

@ResponseBody
@RequestMapping("/user_json.do")
public User userJSON(@RequestBody User user) {
    return user;
}
  • fastjson
    Spring MVC默认使用jackson对request/response进行JSON转换,而在此我们选用性能更高的fastjson, 因此需要在<annotation-driven/>中另做配置.

首先, 使用fastjson需要在pom.xml中添加如下依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.7</version>
</dependency>

然后在mvc-servlet.xml中做如下配置:

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="false">
        <bean id="fastJsonHttpMessageConverter"
              class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
            <property name="supportedMediaTypes">
                <list>
                    <value>text/html;charset=UTF-8</value>
                    <value>application/json;charset=UTF-8</value>
                </list>
            </property>
            <property name="features">
                <array value-type="com.alibaba.fastjson.serializer.SerializerFeature">
                    <value>DisableCircularReferenceDetect</value>
                </array>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

Other

1. POST Encoder

在web.xml配置一个编码Filter可以解决POST乱码问题:

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

2. GET Encoder

对于GET乱码, 由于Tomcat 8.0之前版本默认使用ISO-8859-1编码, 因此有两种解决方案:

  • 修改tomcat配置文件
    修改tomcat配置文件server.xml设置编码与工程编码一致:
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
  • 重新编码
    将经Tomcat编码的内容解码后再重新编码为UTF-8:
String name = new String(request.getParamter("name").getBytes("ISO8859-1"),"utf-8");

注: Tomcat 8.0及更高版本的容器不用此配置.


3. Static Resources Mapping

如果将DispatherServlet配置成拦截所有请求<url-pattern>/</url-pattern>, 则必须额外配置静态资源的映射规则, 否则Spring MVC会对像js/css之类的文件也做转发.
Spring MVC使用<mvc:resources/>元素配置对静态资源的映射:

<mvc:resources location="/js/" mapping="/js/**"/>

目录
相关文章
|
19天前
|
数据采集 Java 数据安全/隐私保护
Spring Boot 3.3中的优雅实践:全局数据绑定与预处理
【10月更文挑战第22天】 在Spring Boot应用中,`@ControllerAdvice`是一个强大的工具,它允许我们在单个位置处理多个控制器的跨切面关注点,如全局数据绑定和预处理。这种方式可以大大减少重复代码,提高开发效率。本文将探讨如何在Spring Boot 3.3中使用`@ControllerAdvice`来实现全局数据绑定与预处理。
52 2
|
21天前
|
SQL Java 数据库
Spring Boot与Flyway:数据库版本控制的自动化实践
【10月更文挑战第19天】 在软件开发中,数据库的版本控制是一个至关重要的环节,它确保了数据库结构的一致性和项目的顺利迭代。Spring Boot结合Flyway提供了一种自动化的数据库版本控制解决方案,极大地简化了数据库迁移管理。本文将详细介绍如何使用Spring Boot和Flyway实现数据库版本的自动化控制。
19 2
|
1月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
1月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
53 2
|
2月前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
1月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
109 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
2月前
|
XML 缓存 前端开发
springMVC02,restful风格,请求转发和重定向
文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
springMVC02,restful风格,请求转发和重定向
|
2月前
|
Java 应用服务中间件 开发者
深入探索并实践Spring Boot框架
深入探索并实践Spring Boot框架
42 2
|
3月前
|
Java 开发工具 Spring
【Azure 事件中心】azure-spring-cloud-stream-binder-eventhubs客户端组件问题, 实践消息非顺序可达
【Azure 事件中心】azure-spring-cloud-stream-binder-eventhubs客户端组件问题, 实践消息非顺序可达
|
3月前
|
缓存 Java Spring
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决