Spring如何将@RestController的对象自动转换为json_@ResponseBody 注解原理

我正在看一段代码,其中我假设spring决定在幕后使用Jackson为@RestController自动将对象转换为json

@RestController 
@RequestMapping("/api")
public class ApiController {private RoomServices roomServices;@Autowiredpublic ApiController(RoomServices roomServices) {this.roomServices = roomServices;}@GetMapping("/rooms")public List<Room> getAllRooms() {return this.roomServices.getAllRooms();}
}

Room类只是一个普通的java类,包含一些字段getter/setter。代码中没有Jackson或任何其他显式序列化。尽管这在检查url时会返回json。我试图浏览spring文档,但我不太确定我要找的是什么。在spring/这个过程是如何工作的?我试着用@Controller,结果它坏了。此功能是否来自@RestController?

如果您使用的是Spring Boot Starter Web,那么可以通过编译依赖项看到它使用的是Spring Boot StarterJSON,Jackson是Start JSON库的依赖项。所以,您的假设是对的(默认情况下,(Spring使用Jackson进行Json转换

Spring使用它的AOP机制截获@Controller中的映射方法(您可以看到@RestController实际上是带有@ResponseBody的@Controller),spring为带有@Controller注释的类创建一个代理对象(使用JDK proxy或通过cglib)。

在处理请求流时,真正调用映射方法的程序将首先被引导到代理,代理将调用真正的@Controller对象的方法,并使用Jackson库将其返回值转换为Json字符串(如果该方法用@ResponseBody注释),然后将Json字符串返回给调用程序。

@ResponseBody注解作用与原理

在Springboot中根本不需要做什么,主要返回值是一个对象,传递给浏览器后,都会将这个对象转换成json字符串,key对应对象字段名称,value对应字段值

1、概念

​ 注解 @ResponseBody,使用在控制层(controller)的方法上。

2、作用

​ 作用:将方法的返回值,以特定的格式写入到response的body区域,进而将数据返回给客户端。

​ 当方法上面没有写ResponseBody,底层会将方法的返回值封装为ModelAndView对象。

​ 如果返回值是字符串,那么直接将字符串写到客户端;如果是一个对象,会将对象转化为json串,然后写到客户端。

3、注意编码

​ 注解中我们可以手动修改编码格式,例如@RequestMapping(value=“/cat/query”,produces=“text/html;charset=utf-8”),前面是请求的路径,后面是编码格式。

4、原理

​ 控制层方法的返回值是如何转化为json格式的字符串的?其实是通过HttpMessageConverter中的方法实现的,它本是一个接口,在其实现类完成转换。如果是bean对象,会调用对象的getXXX()方法获取属性值并且以键值对的形式进行封装,进而转化为json串。如果是map集合,采用get(key)方式获取value值,然后进行封装。

@ResponseBody 注解原理

注:SpringMVC版本 5.2.15

1. 介绍

  • @ResponseBody 注解的作用是将方法的返回值通过适当的转换器转换为指定的格式之后,写入到 response 对象的 body 区,通常用来返回 JSON、XML 数据。
  • 使用了 @ResponseBody 注解标记的方法不再做视图解析

2. 作用范围

  • 标记在方法上
  • 标记在类上
    通过 @RestController 注解实现,此时所有的方法都将会被添加 @ResponseBody 注解

3. 源码分析

ServletInvocableHandlerMethod # invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);setResponseStatus(webRequest);if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}}else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");try {// 处理返回值this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;}
}

该方法中调用了 handleReturnValue() 方法去处理返回值。SpringMVC 中使用 RequestResponseBodyMethodProcessor 类来处理 @ResponseBody 标记的方法

RequestResponseBodyMethodProcessor # handleReturnValue

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {// 设置请求已经被完全处理了,则后面不再做视图解析// 后面我还会提到的mavContainer.setRequestHandled(true);ServletServerHttpRequest inputMessage = createInputMessage(webRequest);ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

该方法中通过 mavContainer.setRequestHandled(true); 设置请求已经被完全处理了,则后面不再做视图解析。然后调用了 writeWithMessageConverters() 方法。

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {// 存储响应体的信息Object body;// 返回值类型Class<?> valueType;// 目标类型Type targetType;// 返回值类型是否是 CharSequence // 是则将 返回值类型和目标类型设置为 String.classif (value instanceof CharSequence) {body = value.toString();valueType = String.class;targetType = String.class;}// 不是 CharSequence 类型,一般是我们的自定义类else {body = value;valueType = getReturnValueType(body, returnType);targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());}// 返回值类型是否是实现了 Resource 接口的资源// 这里我就不分析了if (isResourceType(value, returnType)) {outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&outputMessage.getServletResponse().getStatus() == 200) {Resource resource = (Resource) value;try {List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());body = HttpRange.toResourceRegions(httpRanges, resource);valueType = body.getClass();targetType = RESOURCE_REGION_LIST_TYPE;}catch (IllegalArgumentException ex) {outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());}}}// 选中的媒体类型MediaType selectedMediaType = null;MediaType contentType = outputMessage.getHeaders().getContentType();boolean isContentTypePreset = contentType != null && contentType.isConcrete();if (isContentTypePreset) {if (logger.isDebugEnabled()) {logger.debug("Found 'Content-Type:" + contentType + "' in response");}selectedMediaType = contentType;}else {HttpServletRequest request = inputMessage.getServletRequest();// 可接受的媒体类型List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);// 可产生的媒体类型List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);if (body != null && producibleTypes.isEmpty()) {throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);}// 将要被使用的媒体类型List<MediaType> mediaTypesToUse = new ArrayList<>();for (MediaType requestedType : acceptableTypes) {for (MediaType producibleType : producibleTypes) {if (requestedType.isCompatibleWith(producibleType)) {mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));}}}if (mediaTypesToUse.isEmpty()) {if (body != null) {throw new HttpMediaTypeNotAcceptableException(producibleTypes);}if (logger.isDebugEnabled()) {logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);}return;}MediaType.sortBySpecificityAndQuality(mediaTypesToUse);for (MediaType mediaType : mediaTypesToUse) {// 该媒体类型是否是具体的// 也就是不包含类似于 * 这样的通配符if (mediaType.isConcrete()) {// 选中要使用的媒体类型selectedMediaType = mediaType;break;}else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;break;}}if (logger.isDebugEnabled()) {logger.debug("Using '" + selectedMediaType + "', given " +acceptableTypes + " and supported " + producibleTypes);}}if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();for (HttpMessageConverter<?> converter : this.messageConverters) {GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?(GenericHttpMessageConverter<?>) converter : null);if (genericConverter != null ?((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :converter.canWrite(valueType, selectedMediaType)) {body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,(Class<? extends HttpMessageConverter<?>>) converter.getClass(),inputMessage, outputMessage);if (body != null) {Object theBody = body;LogFormatUtils.traceDebug(logger, traceOn ->"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");addContentDispositionHeader(inputMessage, outputMessage);if (genericConverter != null) {// 使用类型转换器将请求写入到 response body 中genericConverter.write(body, targetType, selectedMediaType, outputMessage);}else {((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);}}else {if (logger.isDebugEnabled()) {logger.debug("Nothing to write: null body");}}return;}}}if (body != null) {Set<MediaType> producibleMediaTypes =(Set<MediaType>) inputMessage.getServletRequest().getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {throw new HttpMessageNotWritableException("No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");}throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);}
}

该方法中通过调用 getAcceptableMediaTypes() 方法获取到 acceptableTypes,getProducibleMediaTypes() 方法获取到 producibleTypes,然后调用 isCompatibleWith() 方法比较 acceptableTypes 和 producibleTypes,获取到两者都兼容的类型。最后通过调用 isConcrete() 获取到一个具体使用的媒体类型。

AbstractMessageConverterMethodProcessor # getProducibleMediaTypes

protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {Set<MediaType> mediaTypes =(Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);if (!CollectionUtils.isEmpty(mediaTypes)) {return new ArrayList<>(mediaTypes);}else if (!this.allSupportedMediaTypes.isEmpty()) {List<MediaType> result = new ArrayList<>();// 遍历类型转化器,获取支持的媒体类型for (HttpMessageConverter<?> converter : this.messageConverters) {if (converter instanceof GenericHttpMessageConverter && targetType != null) {if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {result.addAll(converter.getSupportedMediaTypes());}}else if (converter.canWrite(valueClass, null)) {result.addAll(converter.getSupportedMediaTypes());}}return result;}else {return Collections.singletonList(MediaType.ALL);}
}

该方法中通过遍历类型转换器,根据类型转换器获取到支持的媒体类型。常见的类型转化器有 StringHttpMessageConverter 支持转换为 String 类型,MappingJackson2HttpMessageConverter 支持转换为 json 类型,MappingJackson2XmlHttpMessageConverter 支持转换为 XML 类型。
以转换为 JSON 数据为例。我们最终选择的媒体类型就是 “application/json” ,然后调用 AbstractGenericHttpMessageConverter # write 方法将数据写入到 response body 中。

AbstractGenericHttpMessageConverter # write

public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {final HttpHeaders headers = outputMessage.getHeaders();// 添加响应头// 设置 Content-Type 为	application/jsonaddDefaultHeaders(headers, t, contentType);if (outputMessage instanceof StreamingHttpOutputMessage) {StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() {@Overridepublic OutputStream getBody() {return outputStream;}@Overridepublic HttpHeaders getHeaders() {return headers;}}));}else {// 写入数据到 response body 中writeInternal(t, type, outputMessage);outputMessage.getBody().flush();}
}

该方法中设置了响应头的 Content-Type 为 application/json,然后调用 writeInternal() 方法写数据

AbstractJackson2HttpMessageConverter # writeInternal

protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException {// 获取媒体类型为 application/jsonMediaType contentType = outputMessage.getHeaders().getContentType();// 获取 JSON 数据的编码为 UTF-8JsonEncoding encoding = getJsonEncoding(contentType);// 获取到 HttpServletResponse 的输出流对象// 用于将数据写入到 response body 中OutputStream outputStream = StreamUtils.nonClosing(outputMessage.getBody());// 生成 JSON 数据的类JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputStream, encoding);try {writePrefix(generator, object);Object value = object;Class<?> serializationView = null;FilterProvider filters = null;JavaType javaType = null;if (object instanceof MappingJacksonValue) {MappingJacksonValue container = (MappingJacksonValue) object;value = container.getValue();serializationView = container.getSerializationView();filters = container.getFilters();}if (type != null && TypeUtils.isAssignable(type, value.getClass())) {// 获取 java 类型,一般是我们自定义的类javaType = getJavaType(type, null);}// 用于操作可序列化对象的类// 我们自定义的类一定要实现 Serializable 接口,并设置 get/set 方法ObjectWriter objectWriter = (serializationView != null ?this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());if (filters != null) {objectWriter = objectWriter.with(filters);}if (javaType != null && javaType.isContainerType()) {objectWriter = objectWriter.forType(javaType);}SerializationConfig config = objectWriter.getConfig();if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {objectWriter = objectWriter.with(this.ssePrettyPrinter);}// 写入数据objectWriter.writeValue(generator, value);writeSuffix(generator, object);generator.flush();generator.close();}catch (InvalidDefinitionException ex) {throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);}catch (JsonProcessingException ex) {throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);}
}

该方法中通过调用 outputMessage.getBody() 方法获取到了 HttpServletResponse 的输出流对象,用于将数据输出到 response body 中。并设置了 JSON 数据的编码格式为 UTF-8,然后通过 ObjectWriter 对象操作我们自定义的可序列化的对象,将该对象转换为 JSON 格式输出到 response body 中。

AbstractJackson2HttpMessageConverter # getJsonEncoding

protected JsonEncoding getJsonEncoding(@Nullable MediaType contentType) {if (contentType != null && contentType.getCharset() != null) {Charset charset = contentType.getCharset();JsonEncoding encoding = ENCODINGS.get(charset.name());if (encoding != null) {return encoding;}}return JsonEncoding.UTF8;
}

设置 JSON 数据的编码格式为 UTF-8

ServletServerHttpResponse # getBody

public OutputStream getBody() throws IOException {this.bodyUsed = true;writeHeaders();return this.servletResponse.getOutputStream();
}

获取 HttpServletResponse 的输出流

到这里我们已经实现了将数据转化为 JSON 格式输出到 response body 中了。

还记得前面提到的 mavContainer.setRequestHandled(true) 这个方法吗,前面我说了调用了这个方法后,就不再做视图解析了,我们这里再具体分析一下。

RequestMappingHandlerAdapter # invokeHandlerMethod

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {...invocableMethod.invokeAndHandle(webRequest, mavContainer);...return getModelAndView(mavContainer, modelFactory, webRequest);... 
}

可以看到 getModelAndView() 方法是在 invokeAndHandle() 方法之后调用了,也就是在调用 getModelAndView() 方法前,我们已经调用了 mavContainer.setRequestHandled(true) 方法了。getModelAndView() 方法就是做视图解析的,我们来看一下该方法。

RequestMappingHandlerAdapter # getModelAndView

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {modelFactory.updateModel(webRequest, mavContainer);// 是否已经完全处理了,若为 true,则直接返回 null//  mavContainer.setRequestHandled(true) 已设置为 true 了if (mavContainer.isRequestHandled()) {return null;}// 下面的代码是做视图解析ModelMap model = mavContainer.getModel();ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());if (!mavContainer.isViewReference()) {mav.setView((View) mavContainer.getView());}if (model instanceof RedirectAttributes) {Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);if (request != null) {RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);}}return mav;
}

可以看到,该方法一开始就调用了 mavContainer.isRequestHandled() 方法,如果为 true,则返回 null,并进行下面的视图解析。而 mavContainer.setRequestHandled(true) 方法已经将其设置为 true 了。这就是为什么加了 @ResponseBody 注解的方法不做视图解析的原因。

4. 总结

  • @ResponseBody 注解即可加在方法中,也可以通过 @RestController 注解加在类上
  • 类上添加了 @RestController 注解等效于为该类的所有方法上添加 @ResponseBody 注解
  • @ResponseBody 通过各种类型转换器实现数据的转换,如将数据转换为 String、JSON、XML 等格式。并将数据写入到 response body 中。而且它们使用的都是 UTF-8 编码。
  • 对于自定义的 Java 类转换为 JSON 格式的数据,该类要是可序列化的。
  • 使用了 @ResponseBody 注解标记的方法不再做视图解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/555262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jetty java web_i-jetty 下的JavaWeb开发(一)

最近需要将android端作为服务器进行开发&#xff0c;让android作为服务器&#xff0c;现阶段技术并不是很成熟&#xff0c;主要的服务器有i-jetty&#xff0c;是基于PC端的jetty的移植。i-jetty同tomcat类似&#xff0c;也是Servlet的容器&#xff0c;但是i-jetty需要使用andro…

SpringMVC @RequestBody和@ResponseBody原理解析

SpringMVC RequestBody和ResponseBody原理解析 前言 RequestBody作用是将http请求解析为对应的对象。例如&#xff1a; http请求的参数&#xff08;application/json格式&#xff09;&#xff1a; {"accountId": 10,"adGroupId": "12345678",…

java 高性能缓存_高性能Java缓存----Caffeine

简单介绍Caffeine是新出现的一个高性能的Java缓存&#xff0c;有了它完全可以代替Guava Cache&#xff0c;来实现更加高效的缓存&#xff1b;Caffeine采用了W-TinyLFU回收策略&#xff0c;集合了LRU和LFU的优点&#xff0c;提供了一个最佳的命中率&#xff0c;在效率上可以秒杀…

@ResponseBody 转化成json后与实体类字段名不一致_SpringMVC字符串解析成json对象(@RequestBody注解和@ResponseBody注解)

ResponseBody 转化成json后与实体类字段名不一致 实体类A字段名由B改成C后&#xff0c;Controller 中返回的List中字段名仍然是C 经过ResponseBody返回到前台后又变成了B 后来发现公司项目采用的是阿里的fastjson&#xff0c; 是开源的Json格式化工具库 此工具库是根据实体类…

java togglebutton_ToggleButton和Switch使用大全

本文转载自&#xff1a;Android零基础入门第21节&#xff1a;ToggleButton和Switch使用大全http://www.apkbus.com/blog-205190-68463.html(出处: 安卓巴士 - 安卓开发 - Android开发 - 安卓 - 移动互联网门户)&#xff0c;转载应备注出处&#xff0c;尊重原创上期学习了CheckB…

Jackson,实现Bean和JSON之间的灵活转换(SpringMVC默认的JSON转换器)

Jackson介绍 Jackson是Java最受欢迎的JSON类库之一&#xff0c;包含两个不同的解析器&#xff1a; Jackson ObjectMapper&#xff0c;将JSON转化为Java对象&#xff0c;或者转换为Jackson特定的树结构Jackson JsonParser&#xff0c;JSON流解析器&#xff0c;每次只解析一个J…

MyBatis之工作原理,简单实体的增加、修改、删除、查询_Mybatis-原理总结

一、MyBatis之工作原理 MyBatis是一个半自动映射框架。所谓半自动&#xff0c;是相对Hibernate全表映射而言的&#xff0c;MyBatis需要手动匹配提供POJO、SQL和映射关系。 我们知道&#xff0c;JDBC有四个核心对象&#xff1a; &#xff08;1&#xff09;DriverManager&#…

MySQL优化:如何避免回表查询?_什么是索引覆盖?

数据库表结构&#xff1a; create table user (id int primary key,name varchar(20),sex varchar(5),index(name) )engineinnodb;select id,name where nameshenjianselect id,name,sex where nameshenjian多查询了一个属性&#xff0c;为何检索过程完全不同&#xff1f; 什…

mysql提示Column count doesn‘t match value count at row 1错误

我们在对数据库进行添加信息时可能会遇到如下错误&#xff1a; Column count doesn’t match value count at row 1 该错误的意思是传入表的字段数和values值的个数不一样 我总结了一下&#xff0c;主要有3个易错点&#xff1a; 1.要传入表中的字段数和values后面的值的个数不…

java 阅发布模式_redis发布订阅模式

一 前言虽然有消息队列&#xff0c;我们还是要了解一下redis发布订阅模式哟&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;二发布订阅模式PUBLISH 命令向通道发送信息&#xff0c;此客户端称为publisher 发布者&#xff1b;SUBSCRIBE 向命令通道订阅信息&#…

把实体 转为json 数据格式---jackson 的详细用法_Jackson快速入门

首先介绍三个注解&#xff1a; JsonAutoDetect (method/field):作用于方法或字段&#xff0c;用来表明&#xff0c;当生成json的时候忽略有该annotation的方法或字段 JsonIgnore 过滤不需要转成json的属性 JsonIgnoreProperties 主要用于过滤掉一些不需要的属性 以上三个注…

python 猴子补丁_python面试题精讲——monkey patch(猴子补丁)

前言本次依然是选自python面试题系列&#xff0c;将一个比较偏的概念&#xff0c;可能很多人没怎么听说过——猴子补丁&#xff0c;其实所讲的内容很简单&#xff0c;它得益于python灵活的语法、一切皆对象的思想&#xff0c;一起来看看看看吧&#xff01;目录一、什么是monkey…

java 类的加载顺序_Java 中类的加载顺序

这其实是去年校招时我遇到的一道阿里巴巴的笔试题(承认有点久远了-。-)&#xff0c;嗯&#xff0c;如果我没记错的话&#xff0c;当时是作为Java方向的一道选做大题。当然题意没有这么直白&#xff0c;题目只要求你写出程序运行后所有System.out.println的输出结果&#xff0c;…

Jackson转换json大写_关于jackson转化json的原理_jackson序列化和反序列化Json

背景 web工程中&#xff0c;数据交互是不可避免的&#xff0c;相比xml&#xff0c;json是现在流行的数据交互。 在调试接口中&#xff0c;发现返回字段的大小写不是我所期望的&#xff0c;原本应该返回的nNum字段变成了nnum&#xff0c;这样就导致和前端约定的有出入了。 ja…

groovy+mysql数据库_使用Groovy连接到MySQL

我正在尝试使用MAC OS 10.10.5 Yosemite上的以下Groovy代码连接到MySQL数据库import groovy.sql.Sqltry{def dbURL jdbc:mysql://localhost:3306/sakiladef dbUserName rootdef dbPassword Orange27def dbDriver com.mysql.jdbc.Driverlog.info(Good)def db Sql.newInstan…

svn利用TortoiseSVN忽略文件或文件夹(目录)

忽略已经版本控制的文件 如果你不小心添加了一些应该被忽略的文件&#xff0c;你如何将它们从版本控制中去除而不会丢失它们&#xff1f;或许你有自己的IDE配置文件&#xff0c;不是项目的一部分&#xff0c;但将会花费很多时间使之按照自己的方式工作。 如果你还没有提交&am…

java打印两个小人_[原创]Java画小人与阶梯问题的解答

package test;/**#Python源代码:#By:Cat73 QQ 1901803382#2014年7月22日19:33:12#画图函数 width:台阶的宽度(至少为4) hight:台阶的高度(至少为4) count:台阶的数量(至少为3)def paint(width, hight, count):for i in range(1, count):other(width, hight, count, i)#画出最后…

SpringMVC 参数绑定详解

概述 记得之前跟前端同事联调接口的时候&#xff0c;后端SpringMVC需要接收数组类型的参数&#xff0c;然后跟前端说需要传数组类型过来。后来前端童鞋传了数组&#xff0c;但是后端接收不成功&#xff0c;联调失败。那时候由于时间关系没有仔细研究这块&#xff0c;当时想了个…

java httpcomponents_java – 如何使用Apache httpcomponents从NHttpRequ...

我正在使用Apache httpcomponents实现一个彗星式(延迟响应)http服务器.我的代码与http://hc.apache.org/httpcomponents-core-ga/examples.html的“基本非阻塞HTTP服务器”示例非常相似我使用DefaultServerIOEventDispatch和DefaultListeningIOReactor来分派请求,就像在示例代码…

java servlet是单例吗_关于java:为什么apache servlet是单例?

本问题已经有最佳答案&#xff0c;请猛点这里访问。HttpServletRequest request;HttpServletResponse response;public void doGet(HttpServletRequest request , HttpServlet response){this.request request;this.response response;}如果此servlet一次收到多个请求会发生什…