以下面的代码为例:

  1. @RestController
  2. public class HelloController {
  3.  
  4. @RequestMapping("/")
  5. public BillSearch hello(@RequestBody BillSearch search) {
  6.  
  7. return search;
  8. }
  9. }

前端通过Postman进行模拟:

下面开始一步步的揭开它的面纱:

先从HandlerMethodArgumentResolverComposite开始:

  1. public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
  2. NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
  3.  
  4. HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
  5. if (resolver == null) {
  6. throw new IllegalArgumentException(
  7. "Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +
  8. " supportsParameter should be called first.");
  9. }
  10. return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
  11. }

resolver为RequestResponseBodyMethodProcessor 这个类是序列化和反序列化常用到的类。下面是它的resolveArgument方法:

  1. public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
  2. NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
  3.  
  4. parameter = parameter.nestedIfOptional();
  5. Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
  6. String name = Conventions.getVariableNameForParameter(parameter);
  7.  
  8. if (binderFactory != null) {
  9. WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
  10. if (arg != null) {
  11. validateIfApplicable(binder, parameter);
  12. if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
  13. throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
  14. }
  15. }
  16. if (mavContainer != null) {
  17. mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
  18. }
  19. }
  20.  
  21. return adaptArgumentIfNecessary(arg, parameter);
  22. }
  1. readWithMessageConverters方法如下:
  1. protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
  2. Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
  3.  
  4. HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
  5. Assert.state(servletRequest != null, "No HttpServletRequest");
  6. ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
  7.  
  8. Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
  9. if (arg == null && checkRequired(parameter)) {
  10. throw new HttpMessageNotReadableException("Required request body is missing: " +
  11. parameter.getExecutable().toGenericString(), inputMessage);
  12. }
  13. return arg;
  14. }
  1.  
  1. protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
  2. Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
  3.  
  4. MediaType contentType;
  5. boolean noContentType = false;
  6. try {
  7. contentType = inputMessage.getHeaders().getContentType();
  8. }
  9. catch (InvalidMediaTypeException ex) {
  10. throw new HttpMediaTypeNotSupportedException(ex.getMessage());
  11. }
  12. if (contentType == null) {
  13. noContentType = true;
  14. contentType = MediaType.APPLICATION_OCTET_STREAM;
  15. }
  16.  
  17. Class<?> contextClass = parameter.getContainingClass();
  18. Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
  19. if (targetClass == null) {
  20. ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
  21. targetClass = (Class<T>) resolvableType.resolve();
  22. }
  23.  
  24. HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
  25. Object body = NO_VALUE;
  26.  
  27. EmptyBodyCheckingHttpInputMessage message;
  28. try {
  29. message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
  30.  
  31. for (HttpMessageConverter<?> converter : this.messageConverters) {
  32. Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
  33. GenericHttpMessageConverter<?> genericConverter =
  34. (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
  35. if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
  36. (targetClass != null && converter.canRead(targetClass, contentType))) {
  37. if (message.hasBody()) {
  38. HttpInputMessage msgToUse =
  39. getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
  40. body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
  41. ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
  42. body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
  43. }
  44. else {
  45. body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
  46. }
  47. break;
  48. }
  49. }
  50. }
  51. catch (IOException ex) {
  52. throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
  53. }
  54.  
  55. if (body == NO_VALUE) {
  56. if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
  57. (noContentType && !message.hasBody())) {
  58. return null;
  59. }
  60. throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
  61. }
  62.  
  63. MediaType selectedContentType = contentType;
  64. Object theBody = body;
  65. LogFormatUtils.traceDebug(logger, traceOn -> {
  66. String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
  67. return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
  68. });
  69.  
  70. return body;
  71. }
  1. 上一篇博客里介绍了messageConverters,在项目启动时添加了MappingJackson2HttpMessageConverter,这里主是就是找到这个converter对参数进行解析:
    再进一步追踪:在AbstractJackson2HttpMessageConverter类中,就找到了我们要找到objectMapper:
  1. private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
  2. try {
  3. if (inputMessage instanceof MappingJacksonInputMessage) {
  4. Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
  5. if (deserializationView != null) {
  6. return this.objectMapper.readerWithView(deserializationView).forType(javaType).
  7. readValue(inputMessage.getBody());
  8. }
  9. }
  10. return this.objectMapper.readValue(inputMessage.getBody(), javaType);
  11. }
  12. catch (InvalidDefinitionException ex) {
  13. throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
  14. }
  15. catch (JsonProcessingException ex) {
  16. throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage);
  17. }
  18. }

顺便再介绍一下objectMapper反序列化的主要步骤:

  1. protected Object _readMapAndClose(JsonParser p0, JavaType valueType)
  2. throws IOException
  3. {
  4. try (JsonParser p = p0) {
  5. Object result;
  6. JsonToken t = _initForReading(p, valueType);
  7. final DeserializationConfig cfg = getDeserializationConfig();
  8. final DeserializationContext ctxt = createDeserializationContext(p, cfg);
  9. if (t == JsonToken.VALUE_NULL) {
  10. // Ask JsonDeserializer what 'null value' to use:
  11. result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);
  12. } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
  13. result = null;
  14. } else {
    //com.fasterxml.jackson.databind.deser.BeanDeserializer
  15. JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
  16. if (cfg.useRootWrapping()) {
  17. result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);
  18. } else {
  19. result = deser.deserialize(p, ctxt);
  20. }
  21. ctxt.checkUnresolvedObjectId();
  22. }
  23. if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
  24. _verifyNoTrailingTokens(p, ctxt, valueType);
  25. }
  26. return result;
  27. }
  28. }
  1.  
  1. public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
  2. {
  3. // common case first
  4. if (p.isExpectedStartObjectToken()) {
  5. if (_vanillaProcessing) {
  6. return vanillaDeserialize(p, ctxt, p.nextToken());
  7. }
  8. // 23-Sep-2015, tatu: This is wrong at some many levels, but for now... it is
  9. // what it is, including "expected behavior".
  10. p.nextToken();
  11. if (_objectIdReader != null) {
  12. return deserializeWithObjectId(p, ctxt);
  13. }
  14. return deserializeFromObject(p, ctxt);
  15. }
  16. return _deserializeOther(p, ctxt, p.getCurrentToken());
  17. }
  1. if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
  2. String propName = p.getCurrentName();
  3. do {
  4. p.nextToken();
    //根据获取属性名,获取这个属性
  5. SettableBeanProperty prop = _beanProperties.find(propName);
  6. if (prop != null) { // normal case
  7. try {
  8. prop.deserializeAndSet(p, ctxt, bean);
  9. } catch (Exception e) {
  10. wrapAndThrow(e, bean, propName, ctxt);
  11. }
  12. continue;
  13. }
  14. handleUnknownVanilla(p, ctxt, bean, propName);
  15. } while ((propName = p.nextFieldName()) != null);
  16. }
  17. return bean;

针对这个属性进行反序列化解析,由于这个属性是个枚举,所以它的_valueDeserializer是com.fasterxml.jackson.databind.deser.std.EnumDeserializer

  1. public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
  2. Object instance) throws IOException
  3. {
  4. Object value;
  5. if (p.hasToken(JsonToken.VALUE_NULL)) {
  6. if (_skipNulls) {
  7. return;
  8. }
  9. value = _nullProvider.getNullValue(ctxt);
  10. } else if (_valueTypeDeserializer == null) {
           //com.fasterxml.jackson.databind.deser.std.EnumDeserializer
  11. value = _valueDeserializer.deserialize(p, ctxt);
  12. // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null
  13. if (value == null) {
  14. if (_skipNulls) {
  15. return;
  16. }
  17. value = _nullProvider.getNullValue(ctxt);
  18. }
  19. } else {
  20. value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
  21. }
  22. try {
  23. _setter.invoke(instance, value);
  24. } catch (Exception e) {
  25. _throwAsIOE(p, e, value);
  26. }
  27. }

那为什么jackson枚举的反序列化默认用的是EnumDeserializer呢?

这要回到文章开始的地方说起:在一步中会判断指定的类型是否能够进行canRead()

  1. for (HttpMessageConverter<?> converter : this.messageConverters) {
  2. Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
  3. GenericHttpMessageConverter<?> genericConverter =
  4. (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
  5. if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
  6. (targetClass != null && converter.canRead(targetClass, contentType))) {
  7. if (message.hasBody()) {
  8. HttpInputMessage msgToUse =
  9. getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
  10. body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
  11. ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
  12. body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
  13. }
  14. else {
  15. body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
  16. }
  17. break;
  18. }
  19. }

就从canRead()方法说起:

  1. public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
  2. if (!canRead(mediaType)) {
  3. return false;
  4. }
  5. JavaType javaType = getJavaType(type, contextClass);
  6. AtomicReference<Throwable> causeRef = new AtomicReference<>();
  7. if (this.objectMapper.canDeserialize(javaType, causeRef)) {
  8. return true;
  9. }
  10. logWarningIfNecessary(javaType, causeRef.get());
  11. return false;
  12. }
  1. public boolean canDeserialize(JavaType type, AtomicReference<Throwable> cause)
  2. {
  3. return createDeserializationContext(null,
  4. getDeserializationConfig()).hasValueDeserializerFor(type, cause);
  5. }
  1. public boolean hasValueDeserializerFor(JavaType type, AtomicReference<Throwable> cause) {
  2. try {
  3. return _cache.hasValueDeserializerFor(this, _factory, type);
  4. } ...
  5. }
  1. public boolean hasValueDeserializerFor(DeserializationContext ctxt,
  2. DeserializerFactory factory, JavaType type)
  3. throws JsonMappingException
  4. {
  5. /* Note: mostly copied from findValueDeserializer, except for
  6. * handling of unknown types
  7. */
  8. JsonDeserializer<Object> deser = _findCachedDeserializer(type);
  9. if (deser == null) {
  10. deser = _createAndCacheValueDeserializer(ctxt, factory, type);
  11. }
  12. return (deser != null);
  13. }

注意这个名称createAndCache它是会缓存的,也就是这个类型只会找一次,找到之后,就它的反序列化类就绑定了,缓存起来了,

这们有时配置 jackson的objectMapper,可能会添加很多反序列化的模块,都会注册到_factoryConfig.deserializers()

  1. protected JsonDeserializer<Object> _findCustomBeanDeserializer(JavaType type,
  2. DeserializationConfig config, BeanDescription beanDesc)
  3. throws JsonMappingException
  4. {
  5. for (Deserializers d : _factoryConfig.deserializers()) {
  6. JsonDeserializer<?> deser = d.findBeanDeserializer(type, config, beanDesc);
  7. if (deser != null) {
  8. return (JsonDeserializer<Object>) deser;
  9. }
  10. }
  11. return null;
  12. }
  1. private final JsonDeserializer<?> _find(JavaType type) {
  2. if (_classMappings == null) {
  3. return null;
  4. }
  5. return _classMappings.get(new ClassKey(type.getRawClass()));
  6. }
  1. 每个模块都有_classMappings这样的集合,记录着类与它的序列化类的对应关系。由于我们没有定义这样的关系,那它就有一个默认的。

再找到这个类的反序列化类以后,再开始找类里面的属性的:

同样是在DeserializerCache类中的_createAndCache2方法:

  1. protected JsonDeserializer<Object> _createAndCache2(DeserializationContext ctxt,
  2. DeserializerFactory factory, JavaType type)
  3. throws JsonMappingException
  4. {
  5. JsonDeserializer<Object> deser;
  6. try {
  7. deser = _createDeserializer(ctxt, factory, type);
  8. } catch (IllegalArgumentException iae) {
  9. // We better only expose checked exceptions, since those
  10. // are what caller is expected to handle
  11. throw JsonMappingException.from(ctxt, ClassUtil.exceptionMessage(iae), iae);
  12. }
  13. if (deser == null) {
  14. return null;
  15. }
  16. /* cache resulting deserializer? always true for "plain" BeanDeserializer
  17. * (but can be re-defined for sub-classes by using @JsonCachable!)
  18. */
  19. // 27-Mar-2015, tatu: As per [databind#735], avoid caching types with custom value desers
  20. boolean addToCache = !_hasCustomHandlers(type) && deser.isCachable();
  21.  
  22. /* we will temporarily hold on to all created deserializers (to
  23. * handle cyclic references, and possibly reuse non-cached
  24. * deserializers (list, map))
  25. */
  26. /* 07-Jun-2010, tatu: Danger: [JACKSON-296] was caused by accidental
  27. * resolution of a reference -- couple of ways to prevent this;
  28. * either not add Lists or Maps, or clear references eagerly.
  29. * Let's actually do both; since both seem reasonable.
  30. */
  31. /* Need to resolve? Mostly done for bean deserializers; required for
  32. * resolving cyclic references.
  33. */
  34. if (deser instanceof ResolvableDeserializer) {
  35. _incompleteDeserializers.put(type, deser);
  36. ((ResolvableDeserializer)deser).resolve(ctxt);
  37. _incompleteDeserializers.remove(type);
  38. }
  39. if (addToCache) {
  40. _cachedDeserializers.put(type, deser);
  41. }
  42. return deser;
  43. }

遍历每一个属性:

  1. for (SettableBeanProperty prop : _beanProperties) {
  2. if (!prop.hasValueDeserializer()) {
  3. // [databind#125]: allow use of converters
  4. JsonDeserializer<?> deser = findConvertingDeserializer(ctxt, prop);
  5. if (deser == null) {
  6. deser = ctxt.findNonContextualValueDeserializer(prop.getType());
  7. }
  8. SettableBeanProperty newProp = prop.withValueDeserializer(deser);
  9. _replaceProperty(_beanProperties, creatorProps, prop, newProp);
  10. }
  11. }
  1. type.isEnumType()这里是关键:
  1. protected JsonDeserializer<?> _createDeserializer2(DeserializationContext ctxt,
  2. DeserializerFactory factory, JavaType type, BeanDescription beanDesc)
  3. throws JsonMappingException
  4. {
  5. final DeserializationConfig config = ctxt.getConfig();
  6. // If not, let's see which factory method to use:
  7. if (type.isEnumType()) {
  8. return factory.createEnumDeserializer(ctxt, type, beanDesc);
  9. }
  10. 。。。
  11. }
  1. public JsonDeserializer<?> createEnumDeserializer(DeserializationContext ctxt,
  2. JavaType type, BeanDescription beanDesc)
  3. throws JsonMappingException
  4. {
  5. final DeserializationConfig config = ctxt.getConfig();
  6. final Class<?> enumClass = type.getRawClass();
  7. // 23-Nov-2010, tatu: Custom deserializer?
  8. JsonDeserializer<?> deser = _findCustomEnumDeserializer(enumClass, config, beanDesc);
  9.  
  10. if (deser == null) {
  11. ValueInstantiator valueInstantiator = _constructDefaultValueInstantiator(ctxt, beanDesc);
  12. SettableBeanProperty[] creatorProps = (valueInstantiator == null) ? null
  13. : valueInstantiator.getFromObjectArguments(ctxt.getConfig());
  14. // May have @JsonCreator for static factory method:
    //这里是重点,如上面的注释,如果在enum中定义了工厂方法,找打上了JsonCreator的话,那就算指定了反序列化的方法了,会通过反射执行反序列化
  1. for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
  2. if (_hasCreatorAnnotation(ctxt, factory)) {
  3. if (factory.getParameterCount() == 0) { // [databind#960]
  4. deser = EnumDeserializer.deserializerForNoArgsCreator(config, enumClass, factory);
  5. break;
  6. }
  7. Class<?> returnType = factory.getRawReturnType();
  8. // usually should be class, but may be just plain Enum<?> (for Enum.valueOf()?)
  9. if (returnType.isAssignableFrom(enumClass)) {
  10. deser = EnumDeserializer.deserializerForCreator(config, enumClass, factory, valueInstantiator, creatorProps);
  11. break;
  12. }
  13. }
  14. }
  15.  
  16. // Need to consider @JsonValue if one found
  17. if (deser == null) {
  18. deser = new EnumDeserializer(constructEnumResolver(enumClass,
  19. config, beanDesc.findJsonValueAccessor()),
  20. config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS));
  21. }
  22. }
  23.  
  24. // and then post-process it too
  25. if (_factoryConfig.hasDeserializerModifiers()) {
  26. for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
  27. deser = mod.modifyEnumDeserializer(config, type, beanDesc, deser);
  28. }
  29. }
  30. return deser;
  31. }
  1. _findCustomEnumDeserializer又云objectMapper中的配置中找:由于没有配置,所以返回null
  1. protected JsonDeserializer<?> _findCustomEnumDeserializer(Class<?> type,
  2. DeserializationConfig config, BeanDescription beanDesc)
  3. throws JsonMappingException
  4. {
  5. for (Deserializers d : _factoryConfig.deserializers()) {
  6. JsonDeserializer<?> deser = d.findEnumDeserializer(type, config, beanDesc);
  7. if (deser != null) {
  8. return deser;
  9. }
  10. }
  11. return null;
  12. }

由一没找到,所以就指定了EnumDeserializer为枚举的默认反序列化类了。

spring boot 是如何利用jackson进行反序列化的?的更多相关文章

  1. spring boot 是如何利用jackson进行序列化的?

    接上一篇:spring boot 是如何利用jackson进行反序列化的? @RestController public class HelloController { @RequestMapping ...

  2. Spring Boot 多站点利用 Redis 实现 Session 共享

    如何在不同站点(web服务进程)之间共享会话 Session 呢,原理很简单,就是把这个 Session 独立存储在一个地方,所有的站点都从这个地方读取 Session. 通常我们使用 Redis 来 ...

  3. springboot自定义消息转换器HttpMessageConverter Spring Boot - 使用Gson替换Jackson

    Jackson一直是springframework默认的json库,从4.1开始,springframework支持通过配置GsonHttpMessageConverter的方式使用Gson. 在典型 ...

  4. Spring Boot 2.0 利用 Spring Security 实现简单的OAuth2.0认证方式2

    0.前言 经过前面一小节已经基本配置好了基于SpringBoot+SpringSecurity+OAuth2.0的环境.这一小节主要对一些写固定InMemory的User和Client进行扩展.实现动 ...

  5. Spring Boot 2.0 利用 Spring Security 实现简单的OAuth2.0认证方式1

    0. 前言 之前帐号认证用过自己写的进行匹配,现在要学会使用标准了.准备了解和使用这个OAuth2.0协议. 1. 配置 1.1 配置pom.xml 有些可能会用不到,我把我项目中用到的所有包都贴出来 ...

  6. spring boot 配置 fastjson 替代 Jackson (并解决返回字符串带双引号问题)

    注:以我遇到的情况,只要发出的请求参数是map格式的,都会在前后多加一个双引号 以下代码有两个功能:1.FastJson 替换 Spring 自带的 Jackson  2.解决返回的字符串带双引号问题 ...

  7. Spring boot 集成hessian - LocalDateTime序列化和反序列化

    - 反序列化 import com.caucho.hessian.HessianException; import com.caucho.hessian.io.AbstractDeserializer ...

  8. Spring Boot(四):利用devtools实现热部署,改动代码自动生效

    一.前言 spring-boot-devtools是一个为开发者服务的一个模块,其中最重要的功能就是自动应用代码更改到最新的App上面去.原理是在发现代码有更改之后,重新启动应用,但是速度比手动停止后 ...

  9. Spring Boot + Redis实战-利用自定义注解+分布式锁实现接口幂等性

    场景 不管是传统行业还是互联网行业,我们都需要保证大部分操作是幂等性的,简单点说,就是无论用户点击多少次,操作多少遍,产生的结果都是一样的,是唯一的.而今次公司的项目里,又被我遇到了这么一个幂等性的问 ...

随机推荐

  1. Codeforces 798D Mike and distribution (构造)

    题目链接 http://codeforces.com/contest/798/problem/D 题解 前几天的模拟赛,居然出这种智商题..被打爆了QAQ 这个的话,考虑只有一个序列怎么做,把所有的排 ...

  2. 使用ElementUI创建项目

    从 0 开始搭建 element 项目 第一步,安装 Nodejs/NPM https://nodejs.org/zh-cn/download/ 下载安装即可! 第二步,安装 vue-cli 打开 c ...

  3. C++入门经典-例5.8-使用指针函数进行运算

    1:函数指针式指向函数内存的指针,一个函数在编译时被分配给一个入口地址,这个函数的入口地址就称为函数指针.可以用一个指针变量指向函数,然后通过该指针变量调用此函数. 一个函数可以返回一个整数型值.字符 ...

  4. Java多线程核心知识

    多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线程至关 ...

  5. [翻译]C#中异步方法的性能特点

    翻译自一篇博文,原文:The performance characteristics of async methods in C# 异步系列 剖析C#中的异步方法 扩展C#中的异步方法 C#中异步方法 ...

  6. 第十一周Java学习总结。

    java UI 图形界面知识梳理: ATM: 在整个AWT包中提供的所有工具类主要分为以下3种. (1)组件:Component. (2)容器:Container. (3)布局管理器:LayoutMa ...

  7. 常用IDE 教程(IntelliJ IDEA、Android Studio、Chrome)

    1.IntelliJ IDEA 使用教程 http://wiki.jikexueyuan.com/project/intellij-idea-tutorial/ 2.Chrome 开发工具指南 htt ...

  8. Android 的四大组件都需要在清单文件中注册吗?

    Activity . Service . ContentProvider 如 果 要 使 用 则 必 须 在 AndroidManifest.xml 中 进 行 注 册 , 而BroadcastRec ...

  9. c#端口扫描器wpf+socket

    布局如下 <Window x:Class="PortTest.MainWindow" xmlns="http://schemas.microsoft.com/win ...

  10. golang入门案例之http client请求

    发请求,接收接送,并解析 package main import ( "fmt" "net/http" "io/ioutil" " ...