7.1、简介

在编写可视化界面项目时,我们通常需要对数据进行类型转换、验证及格式化。

一、在Spring3之前,我们使用如下架构进行类型转换、验证及格式化:


流程:

①:类型转换:首先调用PropertyEditor的setAsText(String),内部根据需要调用setValue(Object)方法进行设置转换后的值;

②:数据验证:需要显示调用Spring的Validator接口实现进行数据验证;

③:格式化显示:需要调用PropertyEditor的getText进行格式化显示。

使用如上架构的缺点是:

(1、PropertyEditor被设计为只能String<——>Object之间转换,不能任意对象类型<——>任意类型,如我们常见的Long时间戳到Date类型的转换是办不到的;

(2、PropertyEditor是线程不安全的,也就是有状态的,因此每次使用时都需要创建一个,不可重用;

(3、PropertyEditor不是强类型的,setValue(Object)可以接受任意类型,因此需要我们自己判断类型是否兼容;

(4、需要自己编程实现验证,Spring3支持更棒的注解验证支持;

(5、在使用SpEL表达式语言或DataBinder时,只能进行String<--->Object之间的类型转换;

(6不支持细粒度的类型转换/格式化,如UserModel的registerDate需要转换/格式化类似“2012-05-01”的数据,而OrderModel的orderDate需要转换/格式化类似“2012-05-01 15:11:13”的数据,因为大家都为java.util.Date类型,因此不太容易进行细粒度转换/格式化。

在Spring Web MVC环境中,数据类型转换、验证及格式化通常是这样使用的:


流程:

①、类型转换:首先表单数据(全部是字符串)通过WebDataBinder进行绑定到命令对象,内部通过PropertyEditor实现;

②:数据验证:在控制器中的功能处理方法中,需要显示的调用Spring的Validator实现并将错误信息添加到BindingResult对象中;

③:格式化显示:在表单页面可以通过如下方式展示通过PropertyEditor格式化的数据和错误信息:

Java代码 
  1. <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>
  2. <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

首先需要通过如上taglib指令引入spring的两个标签库。

Java代码 
  1. //1、格式化单个命令/表单对象的值(好像比较麻烦,真心没有好办法)
  2. <spring:bind path="dataBinderTest.phoneNumber">${status.value}</spring:bind>
Java代码 
  1. //2、通过form标签,内部的表单标签会自动调用命令/表单对象属性对应的PropertyEditor进行格式化显示
  2. <form:form commandName="dataBinderTest">
  3. <form:input path="phoneNumber"/><!-- 如果出错会显示错误之前的数据而不是空 -->
  4. </form:form>
Java代码 
  1. //3、显示验证失败后的错误信息
  2. <form:errors></form:errors>

如上PropertyEditor和验证API使用起来比较麻烦,而且有许多缺点,因此Spring3提供了更强大的类型转换(Type Conversion)支持,它可以在任意对象之间进行类型转换,不仅仅是String<——>Object;也提供了强大的数据验证支持;同时提供了强大的数据格式化支持。

二、从Spring3开始,我们可以使用如下架构进行类型转换、验证及格式化:

流程:

①:类型转换:内部的ConversionService会根据S源类型/T目标类型自动选择相应的Converter SPI进行类型转换,而且是强类型的,能在任意类型数据之间进行转换;

②:数据验证:支持JSR-303验证框架,如将@Valid放在需要验证的目标类型上即可;

③:格式化显示:其实就是任意目标类型---->String的转换,完全可以使用Converter SPI完成。

Spring为了更好的诠释格式化/解析功能提供了Formatter SPI,支持根据Locale信息进行格式化/解析,而且该套SPI可以支持字段/参数级别的细粒度格式化/解析,流程如下:

①:类型解析(转换):String---->T类型目标对象的解析,和PropertyEditor类似;

③:格式化显示:任意目标类型---->String的转换,和PropertyEditor类似。

Formatter SPI最大特点是能进行字段/参数级别的细粒度解析/格式化控制,即使是Converter SPI也是粗粒度的(到某个具体类型,而不是其中的某个字段单独控制),目前Formatter SPI还不是很完善,如果您有好的想法可以到Spring官网提建议。

Formatter SPI内部实现实际委托给Converter SPI进行转换,即约束为解析/格式化String<---->任意目标类型。

在Spring Web MVC环境中,数据类型转换、验证及格式化通常是这样使用的:

①、类型转换:首先表单数据(全部是字符串)通过WebDataBinder进行绑定到命令对象,内部通过Converter SPI实现;

②:数据验证:使用JSR-303验证框架进行验证;

③:格式化显示:在表单页面可以通过如下方式展示通过内部通过Converter SPI格式化的数据和错误信息:

Java代码 
  1. <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>
  2. <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

首先需要通过如上taglib指令引入spring的两个标签库。

Java代码 
  1. //1、格式化单个命令/表单对象的值(好像比较麻烦,真心没有好办法)
  2. <spring:bind path="dataBinderTest.phoneNumber">${status.value}</spring:bind>
Java代码 
  1. //2、<spring:eval>标签,自动调用ConversionService并选择相应的Converter SPI进行格式化展示
  2. <spring:eval expression="dataBinderTest.phoneNumber"></spring:eval>

如上代码能工作的前提是在RequestMappingHandlerMapping配置了ConversionServiceExposingInterceptor,它的作用是暴露conversionService到请求中以便如<spring:eval>标签使用。

Java代码 
  1. //3、通过form标签,内部的表单标签会自动调用命令/表单对象属性对应的PropertyEditor进行格式化显示
  2. <form:form commandName="dataBinderTest">
  3. <form:input path="phoneNumber"/><!-- 如果出错会显示错误之前的数据而不是空 -->
  4. </form:form>
Java代码 
  1. //4、显示验证失败后的错误信息
  2. <form:errors></form:errors>

接下来我们就详细学习一下这些知识吧。

7.2、数据类型转换

7.2.1、Spring3之前的PropertyEditor

PropertyEditor介绍请参考【4.16.1、数据类型转换】。

一、测试之前我们需要准备好测试环境:

(1、模型对象,和【4.16.1、数据类型转换】使用的一样,需要将DataBinderTestModel模型类及相关类拷贝过来放入cn.javass.chapter7.model包中。

(2、控制器定义:

Java代码 
  1. package cn.javass.chapter7.web.controller;
  2. //省略import
  3. @Controller
  4. public class DataBinderTestController {
  5. @RequestMapping(value = "/dataBind")
  6. public String test(DataBinderTestModel command) {
  7. //输出command对象看看是否绑定正确
  8. System.out.println(command);
  9. model.addAttribute("dataBinderTest", command);
  10. return "bind/success";
  11. }
  12. }

(3、Spring配置文件定义,请参考chapter7-servlet.xml,并注册DataBinderTestController:

Java代码 
  1. <beanclass="cn.javass.chapter7.web.controller.DataBinderTestController"/>

(4、测试的URL:

http://localhost:9080/springmvc-chapter7/dataBind?username=zhang&bool=yes&schooInfo.specialty=computer&hobbyList[0]=program&hobbyList[1]=music&map[key1]=value1&map[key2]=value2&phoneNumber=010-12345678&date=2012-3-18 16:48:48&state=blocked

二、注解式控制器注册PropertyEditor:

1、使用WebDataBinder进行控制器级别注册PropertyEditor(控制器独享)

Java代码 
  1. @InitBinder
  2. //此处的参数也可以是ServletRequestDataBinder类型
  3. public void initBinder(WebDataBinder binder) throws Exception {
  4. //注册自定义的属性编辑器
  5. //1、日期
  6. DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  7. CustomDateEditor dateEditor = new CustomDateEditor(df, true);
  8. //表示如果命令对象有Date类型的属性,将使用该属性编辑器进行类型转换
  9. binder.registerCustomEditor(Date.class, dateEditor);
  10. //自定义的电话号码编辑器(和【4.16.1、数据类型转换】一样)
  11. binder.registerCustomEditor(PhoneNumberModel.classnewPhoneNumberEditor());
  12. }

和【4.16.1、数据类型转换】一节类似,只是此处需要通过@InitBinder来注册自定义的PropertyEditor。

2、使用WebBindingInitializer批量注册PropertyEditor

和【4.16.1、数据类型转换】不太一样,因为我们的注解式控制器是POJO,没有实现任何东西,因此无法注入WebBindingInitializer,此时我们需要把WebBindingInitializer注入到我们的RequestMappingHandlerAdapter或AnnotationMethodHandlerAdapter,这样对于所有的注解式控制器都是共享的。

Java代码 
  1. <beanclass="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
  2. <property name="webBindingInitializer">
  3. <beanclass="cn.javass.chapter7.web.controller.support.initializer.MyWebBindingInitializer"/>
  4. </property>
  5. </bean>

此时我们注释掉控制器级别通过@InitBinder注册PropertyEditor的方法。

3、全局级别注册PropertyEditor(全局共享)

和【4.16.1、数据类型转换】一节一样,此处不再重复。请参考【4.16.1、数据类型转换】的【全局级别注册PropertyEditor(全局共享)】。

接下来我们看一下Spring3提供的更强大的类型转换支持。

7.2.2、Spring3开始的类型转换系统

Spring3引入了更加通用的类型转换系统,其定义了SPI接口(Converter等)和相应的运行时执行类型转换的API(ConversionService等),在Spring中它和PropertyEditor功能类似,可以替代PropertyEditor来转换外部Bean属性的值到Bean属性需要的类型。

该类型转换系统是Spring通用的,其定义在org.springframework.core.convert包中,不仅仅在Spring Web MVC场景下。目标是完全替换PropertyEditor,提供无状态、强类型且可以在任意类型之间转换的类型转换系统,可以用于任何需要的地方,如SpEL、数据绑定。

Converter SPI完成通用的类型转换逻辑,如java.util.Date<---->java.lang.Long或java.lang.String---->PhoneNumberModel等。

7.2.2.1、架构

1、类型转换器:提供类型转换的实现支持。

一个有如下三种接口:

(1、Converter:类型转换器,用于转换S类型到T类型,此接口的实现必须是线程安全的且可以被共享。

Java代码 
  1. package org.springframework.core.convert.converter;
  2. public interface Converter<S, T> { //① S是源类型 T是目标类型
  3. T convert(S source); //② 转换S类型的source到T目标类型的转换方法
  4. }

示例:请参考cn.javass.chapter7.converter.support.StringToPhoneNumberConverter转换器,用于将String--->PhoneNumberModel。

此处我们可以看到Converter接口实现只能转换一种类型到另一种类型,不能进行多类型转换,如将一个数组转换成集合,如(String[] ----> List<String>、String[]----->List<PhoneNumberModel>等)。

(2、GenericConverter和ConditionalGenericConverter:GenericConverter接口实现能在多种类型之间进行转换,ConditionalGenericConverter是有条件的在多种类型之间进行转换。

Java代码 
  1. package org.springframework.core.convert.converter;
  2. public interface GenericConverter {
  3. Set<ConvertiblePair> getConvertibleTypes();
  4. Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
  5. }

getConvertibleTypes:指定了可以转换的目标类型对;

convert:在sourceType和targetType类型之间进行转换。

Java代码 
  1. package org.springframework.core.convert.converter;
  2. public interface ConditionalGenericConverter extends GenericConverter {
  3. boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
  4. }

matches:用于判断sourceType和targetType类型之间能否进行类型转换。

示例:如org.springframework.core.convert.support.ArrayToCollectionConverter和CollectionToArrayConverter用于在数组和集合间进行转换的ConditionalGenericConverter实现,如在String[]<---->List<String>、String[]<---->List<PhoneNumberModel>等之间进行类型转换。

对于我们大部分用户来说一般不需要自定义GenericConverter, 如果需要可以参考内置的GenericConverter来实现自己的。

(3、ConverterFactory:工厂模式的实现,用于选择将一种S源类型转换为R类型的子类型T的转换器的工厂接口。

Java代码 
  1. package org.springframework.core.convert.converter;
  2. public interface ConverterFactory<S, R> {
  3. <T extends R> Converter<S, T> getConverter(Class<T> targetType);
  4. }

S:源类型;R目标类型的父类型;T:目标类型,且是R类型的子类型;

getConverter:得到目标类型的对应的转换器。

示例:如org.springframework.core.convert.support.NumberToNumberConverterFactory用于在Number类型子类型之间进行转换,如Integer--->Double, Byte---->Integer, Float--->Double等。

对于我们大部分用户来说一般不需要自定义ConverterFactory,如果需要可以参考内置的ConverterFactory来实现自己的。

2、类型转换器注册器、类型转换服务:提供类型转换器注册支持,运行时类型转换API支持。

一共有如下两种接口:

(1、ConverterRegistry:类型转换器注册支持,可以注册/删除相应的类型转换器。

Java代码 
  1. package org.springframework.core.convert.converter;
  2. public interface ConverterRegistry {
  3. void addConverter(Converter<?, ?> converter);
  4. void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter);
  5. void addConverter(GenericConverter converter);
  6. void addConverterFactory(ConverterFactory<?, ?> converterFactory);
  7. void removeConvertible(Class<?> sourceType, Class<?> targetType);
  8. }

可以注册:Converter实现,GenericConverter实现,ConverterFactory实现。

(2、ConversionService:运行时类型转换服务接口,提供运行期类型转换的支持。

Java代码 
  1. package org.springframework.core.convert;
  2. public interface ConversionService {
  3. boolean canConvert(Class<?> sourceType, Class<?> targetType);
  4. boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
  5. <T> T convert(Object source, Class<T> targetType);
  6. Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
  7. }

convert:将源对象转换为目标类型的目标对象。

Spring提供了两个默认实现(其都实现了ConverterRegistry、ConversionService接口):

DefaultConversionService:默认的类型转换服务实现;

DefaultFormattingConversionService:带数据格式化支持的类型转换服务实现,一般使用该服务实现即可。

7.2.2.2、Spring内建的类型转换器如下所示:

类名

说明

第一组:标量转换器

StringToBooleanConverter

String----->Boolean

true:true/on/yes/1; false:false/off/no/0

ObjectToStringConverter

Object----->String

调用toString方法转换

StringToNumberConverterFactory

String----->Number(如Integer、Long等)

NumberToNumberConverterFactory

Number子类型(Integer、Long、Double等)<——> Number子类型(Integer、Long、Double等)

StringToCharacterConverter

String----->java.lang.Character

取字符串第一个字符

NumberToCharacterConverter

Number子类型(Integer、Long、Double等)——> java.lang.Character

CharacterToNumberFactory

java.lang.Character ——>Number子类型(Integer、Long、Double等)

StringToEnumConverterFactory

String----->enum类型

通过Enum.valueOf将字符串转换为需要的enum类型

EnumToStringConverter

enum类型----->String

返回enum对象的name()值

StringToLocaleConverter

String----->java.util.Local

PropertiesToStringConverter

java.util.Properties----->String

默认通过ISO-8859-1解码

StringToPropertiesConverter

String----->java.util.Properties

默认使用ISO-8859-1编码

第二组:集合、数组相关转换器

ArrayToCollectionConverter

任意S数组---->任意T集合(List、Set)

CollectionToArrayConverter

任意T集合(List、Set)---->任意S数组

ArrayToArrayConverter

任意S数组<---->任意T数组

CollectionToCollectionConverter

任意T集合(List、Set)<---->任意T集合(List、Set)

即集合之间的类型转换

MapToMapConverter

Map<---->Map之间的转换

ArrayToStringConverter

任意S数组---->String类型

StringToArrayConverter

String----->数组

默认通过“,”分割,且去除字符串的两边空格(trim)

ArrayToObjectConverter

任意S数组---->任意Object的转换

(如果目标类型和源类型兼容,直接返回源对象;否则返回S数组的第一个元素并进行类型转换)

ObjectToArrayConverter

Object----->单元素数组

CollectionToStringConverter

任意T集合(List、Set)---->String类型

StringToCollectionConverter

String----->集合(List、Set)

默认通过“,”分割,且去除字符串的两边空格(trim)

CollectionToObjectConverter

任意T集合---->任意Object的转换

(如果目标类型和源类型兼容,直接返回源对象;否则返回S数组的第一个元素并进行类型转换)

ObjectToCollectionConverter

Object----->单元素集合

第三组:默认(fallback)转换器:之前的转换器不能转换时调用

ObjectToObjectConverter

Object(S)----->Object(T)

首先尝试valueOf进行转换、没有则尝试new 构造器(S)

IdToEntityConverter

Id(S)----->Entity(T)

查找并调用public static T find[EntityName](S)获取目标对象,EntityName是T类型的简单类型

FallbackObjectToStringConverter

Object----->String

ConversionService作为恢复使用,即其他转换器不能转换时调用(执行对象的toString()方法)

S:代表源类型,T:代表目标类型

如上的转换器在使用转换服务实现DefaultConversionService和DefaultFormattingConversionService时会自动注册。

7.2.2.3、示例

(1、自定义String----->PhoneNumberModel的转换器

Java代码 
  1. package cn.javass.chapter7.web.controller.support.converter;
  2. //省略import
  3. public class StringToPhoneNumberConverter implements Converter<String, PhoneNumberModel> {
  4. Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d{7,8})$");
  5. @Override
  6. public PhoneNumberModel convert(String source) {
  7. if(!StringUtils.hasLength(source)) {
  8. //①如果source为空 返回null
  9. return null;
  10. }
  11. Matcher matcher = pattern.matcher(source);
  12. if(matcher.matches()) {
  13. //②如果匹配 进行转换
  14. PhoneNumberModel phoneNumber = new PhoneNumberModel();
  15. phoneNumber.setAreaCode(matcher.group(1));
  16. phoneNumber.setPhoneNumber(matcher.group(2));
  17. return phoneNumber;
  18. else {
  19. //③如果不匹配 转换失败
  20. throw new IllegalArgumentException(String.format("类型转换失败,需要格式[010-12345678],但格式是[%s]", source));
  21. }
  22. }
  23. }

String转换为Date的类型转换器,请参考cn.javass.chapter7.web.controller.support.converter.StringToDateConverter。

(2、测试用例(cn.javass.chapter7.web.controller.support.converter.ConverterTest)

Java代码 
  1. @Test
  2. public void testStringToPhoneNumberConvert() {
  3. DefaultConversionService conversionService = newDefaultConversionService();
  4. conversionService.addConverter(new StringToPhoneNumberConverter());
  5. String phoneNumberStr = "010-12345678";
  6. PhoneNumberModel phoneNumber = conversionService.convert(phoneNumberStr, PhoneNumberModel.class);
  7. Assert.assertEquals("010", phoneNumber.getAreaCode());
  8. }

类似于PhoneNumberEditor将字符串“010-12345678”转换为PhoneNumberModel。

Java代码 
  1. @Test
  2. public void testOtherConvert() {
  3. DefaultConversionService conversionService = newDefaultConversionService();
  4. //"1"--->true(字符串“1”可以转换为布尔值true)
  5. Assert.assertEquals(Boolean.valueOf(true), conversionService.convert("1", Boolean.class));
  6. //"1,2,3,4"--->List(转换完毕的集合大小为4)
  7. Assert.assertEquals(4, conversionService.convert("1,2,3,4", List.class).size());
  8. }

其他类型转换器使用也是类似的,此处不再重复。

7.2.2.4、集成到Spring Web MVC环境

(1、注册ConversionService实现和自定义的类型转换器

Java代码 
  1. <!-- ①注册ConversionService -->
  2. <bean id="conversionService" class="org.springframework.format.support.
  3. FormattingConversionServiceFactoryBean">
  4. <property name="converters">
  5. <list>
  6. <bean class="cn.javass.chapter7.web.controller.support.
  7. converter.StringToPhoneNumberConverter"/>
  8. <bean class="cn.javass.chapter7.web.controller.support.
  9. converter.StringToDateConverter">
  10. <constructor-arg value="yyyy-MM-dd"/>
  11. </bean>
  12. </list>
  13. </property>
  14. </bean>

FormattingConversionServiceFactoryBean:是FactoryBean实现,默认使用DefaultFormattingConversionService转换器服务实现;

converters:注册我们自定义的类型转换器,此处注册了String--->PhoneNumberModel和String--->Date的类型转换器。

(2、通过ConfigurableWebBindingInitializer注册ConversionService

Java代码 
  1. <!-- ②使用ConfigurableWebBindingInitializer注册conversionService -->
  2. <bean id="webBindingInitializer"class="org.springframework.web.bind.support.
  3. ConfigurableWebBindingInitializer">
  4. <property name="conversionService" ref="conversionService"/>
  5. </bean>

此处我们通过ConfigurableWebBindingInitializer绑定初始化器进行ConversionService的注册;

3、注册ConfigurableWebBindingInitializer到RequestMappingHandlerAdapter

Java代码 
  1. <bean class="org.springframework.web.servlet.mvc.method.annotation.
  2. RequestMappingHandlerAdapter">
  3. <property name="webBindingInitializer" ref="webBindingInitializer"/>
  4. </bean>

通过如上配置,我们就完成了Spring3.0的类型转换系统与Spring Web MVC的集成。此时可以启动服务器输入之前的URL测试了。

此时可能有人会问,如果我同时使用PropertyEditor和ConversionService,执行顺序是什么呢?内部首先查找PropertyEditor进行类型转换,如果没有找到相应的PropertyEditor再通过ConversionService进行转换。

如上集成过程看起来比较麻烦,后边我们会介绍<mvc:annotation-driven>和@EnableWebMvc,ConversionService会自动注册,后续章节再详细介绍。

注解形式控制器 数据验证,类型转换相关推荐

  1. 注解形式控制器 数据验证,类型转换(2)

    7.3.数据格式化 在如Web /客户端项目中,通常需要将数据转换为具有某种格式的字符串进行展示,因此上节我们学习的数据类型转换系统核心作用不是完成这个需求,因此Spring3引入了格式化转换器(Fo ...

  2. 注解形式控制器 数据验证,类型转换(3)

    7.4.数据验证 7.4.1.编程式数据验证 Spring 2.x提供了编程式验证支持,详见[4.16.2 数据验证]章节,在此我们重写[4.16.2.4.1.编程式验证器]一节示例. (1.验证器实 ...

  3. 注解形式控制器(4) 数据绑定

    到目前为止,请求已经能交给我们的处理器进行处理了,接下来的事情是要进行收集数据啦,接下来我们看看我们能从请求中收集到哪些数据,如图6-11: 图6-11 1.@RequestParam绑定单个请求参数 ...

  4. 注解形式控制器配置(5) 数据绑定2

    6.6.2.@RequestParam绑定单个请求参数值 @RequestParam用于将请求参数区数据映射到功能处理方法的参数上. Java代码  public String requestpara ...

  5. 注解形式控制器配置(3)

    6.6.5.生产者.消费者限定 6.6.5.1.基本概念 首先让我们看一下通过HTTP协议传输的媒体类型及如何表示媒体类型: 一.Media Type: 互联网媒体类型,一般就是我们所说的MIME类型 ...

  6. 注解形式控制器配置(2)

    6.5.请求映射 处理器定义好了,那接下来我们应该定义功能处理方法,接收用户请求处理并选择视图进行渲染.首先我们看一下图6-1: http请求信息包含六部分信息: ①请求方法,如GET或POST,表示 ...

  7. Spring MVC 数据验证——validate注解方式

    1.说明 学习注解方式之前,应该先学习一下编码方式的spring注入.这样便于理解验证框架的工作原理.在出错的时候,也能更好的解决这个问题.所以本次博客教程也是基于编码方式.仅仅是在原来的基础加上注解 ...

  8. 自定义注解做数据验证

    为了工作也为了更加深入了解掌握java注解的使用,决定自定义注解来实现数据验证. 最开始也考虑使用jsr-303规范来实现功能,但是对于开发人员来说比较累,因为要去实体类对象中添加验证字段注解,而且要 ...

  9. SpringMVC数据验证——第七章 注解式控制器的数据验证、类型转换及格式化——SpringMVC

    7.4.数据验证 7.4.1.编程式数据验证 Spring 2.x提供了编程式验证支持,详见[4.16.2 数据验证]章节,在此我们重写[4.16.2.4.1.编程式验证器]一节示例. (1.验证器实 ...

最新文章

  1. jQuery操作input
  2. D - 小Y上学记——要迟到了!
  3. IDEA svn 更换项目,拉新项目的时候 提示 No appropriate protocol
  4. java properties 路径问题_Java 读取Properties文件时应注意的路径问题
  5. 全国计算机等级考试题库二级C操作题100套(第50套)
  6. Batch Normalization批量归一化
  7. bzoj4602 [Sdoi2016]齿轮 边权并查集
  8. 关于python的垃圾回收机制_Python中的垃圾回收机制
  9. 目标检测——使用OpenCV读取图片要注意进行维度变换
  10. UVA10190 Divide, But Not Quite Conquer!【等差数列】
  11. 【论文阅读】2018-基于深度学习的网络流量分类及异常检测方法研究_王伟
  12. chrome open axure 自动跳转到axure插件
  13. 汽车电子行业常见缩略词(前视摄像头相关)
  14. UML入门以及Plant UML工具介绍
  15. 3GPP 5GNR 物理层协议梳理
  16. 不讲武德的Python反爬神器『fake_useragent』
  17. 新电脑从另外一台电脑完整拷贝环境,不需要安装环境
  18. 最优化理论与方法-牛顿迭代法
  19. 电脑游戏灌输的70个山寨逻辑
  20. dp,dpi,px知识补充

热门文章

  1. android小米通知不显示电量,Android开发笔记——小米通知‘坑’ app的通知一直显示在不重要通知里 ......
  2. 支付验签失败_微信支付提示支付验证签名失败
  3. c语音学习-输入一个小写字母,输出其对应的大写字母
  4. ECS之System系统
  5. Leecode刷题热题HOT100(3)——无重复字符最长子串
  6. java编程executor框架_Java并发编程 - Executor框架(一)Executor,
  7. mysql sync es 异步双写_mysql数据同步es方案思考
  8. echart仪表盘旋转_使用echart仪表盘
  9. python关于row的规范_Python DB-API 2.0规范
  10. IIS安装2个SSL_SSL的申请与https使用