本篇文章主要分享下自己在学习Spring MVC数据转换、格式化和数据校验、Spring MVC文件上传和下载以及Spring MVC拦截器遇到的一些问题和读书笔记,希望对你有所帮助。
@Component、@Repository、@Service、@Controller的区别
在应用的分层模型中,大致上都可以分为如下几层:表现层、控制器层、业务逻辑层、DAO层、Domain Object层,了解了应用的分层模型,再来看注解之间的区别就很简单了。@Controller对应于控制器层, @Service对应于业务逻辑层,@Repository对应于持久化层(也就是DAO层),@Component对应于哪些比较中立的类(就是不属于任何一层的类) 。
注意:有这样一个问题,我把控制器类放在com.dodonew.controller包下面,把持久化层类放在com.dodonew.dao包下面,控制器类使用@Controller进行注解,持久化类使用@Repository进行注解,以前项目中扫码包配置如下所示:
还是这样配置的话就会出现一个问题,@Repository注解没有办法进行识别,因为没有被扫描到,所以包的扫描范围就要进行扩大,这点特别要注意,配置如下:
@Resource、@Autowired、@Qualifier区别
@Resource注解(该注解属于J2EE)
该注解默认是按名称来装配注入的,如果Spring找不到与名称相匹配的bean时,该注解才会按照类型来装配注入。@Resource有两个重要的属性,name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。当使用name属性时,Spring则使用byName的自动注入策略,使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,Spring将通过反射机制使用byName自动注入策略。
装配顺序:
如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则会抛出异常。
如果指定了name,则从上下文中查找名称匹配的bean进行装配,找不到抛出异常。
如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或找到多个都会抛出异常。
如果既没有指定name,也没有指定type,则自动按照byName方式进行装配;如果没有找到匹配,则会回退到一个原始类型进行匹配,如果匹配则自动装配,否则抛出异常。
@Autowired注解(该注解属于Spring)
该注解默认是按照类型装配注入的,在默认情况下,它要求依赖对象必须存在,如果允许null值,可以设定它的required属性为false,如下所示:
@Autowired(required=false) public IUserService userService;
@Qualifier注解
该注解配合@Autowired使用,因为会存在这样一种情况,当你创建了多个具有相同类型的bean时,怎么进行区分呢?在这种情况下,可以使用@Qualifier注解和@Autowired注解来进行区分,如下所示:
@Autowired @Qualifier("userServiceImpl") public IUserService userService;
Spring MVC的数据绑定流程
Spring MVC通过反射机制对目标处理方法的签名进行分析,并将请求消息绑定到处理方法的参数中,数据绑定的核心部件是DataBinder,其运行机制如下图所示:
dataBinder 1-1.png
Spring MVC框架将ServletRequest对象及处理方法的参数对象实例传递给DataBinder,DataBinder调用装配在Spring Web上下文中的ConversionService组件进行数据类型转换,数据格式化工作,并将ServletRequest中的消息填充到参数对象中,然后再调用Validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验,并最终生成数据绑定到BindingResult对象。BindingResult包含已经完成数据绑定的参数对象,还包含相应的校验错误对象,Spring MVC抽取BindingResult中的参数对象及校验错误对象,将它们赋值给处理方法的相应参数。
从图中我们可以看出在请求消息到达真正调用处理方法的这一段时间内,Spring MVC还会完成很多其他的工作,包括请求信息转换、数据转换、数据格式化及数据校验等。我们先来看下数据转换。
数据转换、数据格式化、数据校验
数据转换的实现有两种方法,第一种方法是java.beans包中提供了一个ProperyEditor接口来进行数据转换,ProperyEditor的核心功能是将一个字符串转换为一个Java对象,但是存在以下不足:
只能用于字符串和Java对象的转换,不适用于任意两个Java类型直接的转换。
对源对象及目标对象所在的上下文信息(如注解等)不敏感,在类型转换时不能利用这些上下文信息进行高级转换逻辑。
第二种方法是Spring添加了一个通用的类型转换模块,该类型转换模块位于org.springframework.core.convert包中,这种方法可以实现任意两种类型之间的转换,弥补了ProperyEditor类型转换的缺陷。
在Spring上下文配置文件中,使用了mvc:annotation-driven标签,该标签会注册一个默认的ConversionService,即FormattingConversionServiceFactoryBean,以满足大多数类型转换的需求。在实际项目开发中,一般使用默认的转换器即可满足我们日常的开发需求了,实际中使用比较多的是数据格式化。
数据格式化
Spring使用Converter转换器进行源类型对象到目标类型对象的转换,但是Spring的转换器并不承担输入以及输出信息格式化的工作。这个时候就需要用到Spring提供的数据格式化了,格式化框架位于org.springframework.format包。实现数据的格式化,有两种方法,第一种方法是手工代码实现Formatter接口,但是这种硬编码的格式化方式现在已经过时了。第二种方法是用Spring提供的注解驱动的属性对象格式化功能。在org.springframework.format.annotation包下面定义了两个格式化的注解类型:
DateTimeFormat
@DateTimeFormat注解可以对java.util.Date、java.util.Calendar等时间类型的属性进行注解。
NumberFormat
@NumberFormat可对类似数字类型的属性进行注解。
数据校验
Spring MVC提供了强大的数据校验功能,有两种方法可以实现校验,一种方法是利用Spring自带的Validation校验框架,另一种方法是利用JSR 303(Java验证规范)。但是Validation框架通过硬编码完成数据校验,比较麻烦,所以现在项目开发一般都使用JSR 303完成数据校验。
Spring MVC上传文件与下载文件
Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的,Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver,因为Spring MVC的文件上传还需要依赖Apache Commons FileUpload的组件。具体的使用在这里就不做介绍了,这里主要介绍下在实现下载文件时遇到的一个问题。先看下如下配置:
从上面的配置可以看到,设置了不使用默认的消息转换器。 但是图片在下载的过程中是以二进制数据方式在传输的,因为没有对应的消息转换器进行处理,所以下载后的图片打开的时候就提示该图片已经被损坏了。解决这个问题只需要在添加一个ByteArrayHttpMessageConverter消息转换器就可以了,它的主要作用就是读写二进制数据 ,添加后的配置如下所示:
text/html;charset=UTF-8 application/json;charset=UTF-8 WriteMapNullValue WriteNullStringAsEmpty UTF-8
如果使用的是mvc: annotation-driven,就不需要额外添加ByteArrayHttpMessageConverter,因为默认会装配这个消息转换器的。
Spring MVC拦截器
Interceptor拦截器是Spring MVC中相当重要的功能,它的主要作用是拦截用户的请求并进行相应的处理,比如通过拦截器来进行用户权限验证,或者用来判断用户是否已经登录等。Spring MVC拦截器是可拔插式的设计,如果需要使用某个拦截器,只需要在配置文件中应用该拦截器即可。如果不需要使用该拦截器,只需要在配置文件中取消应用该拦截器。不管是否应用某个拦截器,对Spring MVC框架不会有任何影响。拦截器的拦截机制如下图所示:
interceptor 1-2.png
从图中可以看到发送一个请求,首先要经过拦截器的,拦截器通过之后,才会到对应的Controller的。不过需要注意的是,在实际的项目开发中,我们是会根据业务需求设置拦截范围的,不是所有的请求都要进行拦截,比如只拦截/api/*这样的请求。
欢迎关注国士梅花