mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Merge branch '7.0.x'
This commit is contained in:
+11
-2
@@ -32,6 +32,7 @@ import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.core.JsonEncoding;
|
||||
import tools.jackson.core.JsonGenerator;
|
||||
import tools.jackson.core.exc.JacksonIOException;
|
||||
import tools.jackson.core.util.BufferRecycler;
|
||||
import tools.jackson.core.util.ByteArrayBuilder;
|
||||
import tools.jackson.databind.JavaType;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
@@ -159,7 +160,8 @@ public abstract class AbstractJacksonEncoder<T extends ObjectMapper> extends Jac
|
||||
}
|
||||
|
||||
ObjectWriter writer = createObjectWriter(mapper, elementType, mimeType, hintsToUse);
|
||||
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.generatorFactory()._getBufferRecycler());
|
||||
BufferRecycler recycler = writer.generatorFactory()._getBufferRecycler();
|
||||
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(recycler);
|
||||
JsonEncoding encoding = getJsonEncoding(mimeType);
|
||||
JsonGenerator generator = mapper.createGenerator(byteBuilder, encoding);
|
||||
SequenceWriter sequenceWriter = writer.writeValues(generator);
|
||||
@@ -204,6 +206,11 @@ public abstract class AbstractJacksonEncoder<T extends ObjectMapper> extends Jac
|
||||
catch (JacksonIOException ex) {
|
||||
logger.error("Could not close Encoder resources", ex);
|
||||
}
|
||||
finally {
|
||||
// Release strictly after the generator and builder are done: the
|
||||
// recycler returns to a concurrent pool and may be reused at once.
|
||||
recycler.releaseToPool();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (JacksonIOException ex) {
|
||||
@@ -223,7 +230,8 @@ public abstract class AbstractJacksonEncoder<T extends ObjectMapper> extends Jac
|
||||
|
||||
ObjectWriter writer = createObjectWriter(mapper, valueType, mimeType, hints);
|
||||
|
||||
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.generatorFactory()._getBufferRecycler());
|
||||
BufferRecycler recycler = writer.generatorFactory()._getBufferRecycler();
|
||||
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(recycler);
|
||||
try {
|
||||
JsonEncoding encoding = getJsonEncoding(mimeType);
|
||||
|
||||
@@ -249,6 +257,7 @@ public abstract class AbstractJacksonEncoder<T extends ObjectMapper> extends Jac
|
||||
}
|
||||
finally {
|
||||
byteBuilder.release();
|
||||
recycler.releaseToPool();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-2
@@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import com.fasterxml.jackson.core.JsonEncoding;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.util.BufferRecycler;
|
||||
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -171,7 +172,8 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
||||
}
|
||||
|
||||
ObjectWriter writer = createObjectWriter(mapper, elementType, mimeType, null, hintsToUse);
|
||||
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.getFactory()._getBufferRecycler());
|
||||
BufferRecycler recycler = writer.getFactory()._getBufferRecycler();
|
||||
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(recycler);
|
||||
JsonEncoding encoding = getJsonEncoding(mimeType);
|
||||
JsonGenerator generator = mapper.getFactory().createGenerator(byteBuilder, encoding);
|
||||
SequenceWriter sequenceWriter = writer.writeValues(generator);
|
||||
@@ -216,6 +218,11 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
||||
catch (IOException ex) {
|
||||
logger.error("Could not close Encoder resources", ex);
|
||||
}
|
||||
finally {
|
||||
// Release strictly after the generator and builder are done: the
|
||||
// recycler returns to a concurrent pool and may be reused at once.
|
||||
recycler.releaseToPool();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (IOException ex) {
|
||||
@@ -247,7 +254,8 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
||||
writer = writer.with(filters);
|
||||
}
|
||||
|
||||
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.getFactory()._getBufferRecycler());
|
||||
BufferRecycler recycler = writer.getFactory()._getBufferRecycler();
|
||||
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(recycler);
|
||||
try {
|
||||
JsonEncoding encoding = getJsonEncoding(mimeType);
|
||||
|
||||
@@ -276,6 +284,7 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
||||
}
|
||||
finally {
|
||||
byteBuilder.release();
|
||||
recycler.releaseToPool();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+63
@@ -21,6 +21,7 @@ import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFilter;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
@@ -29,6 +30,10 @@ import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import tools.jackson.core.json.JsonFactory;
|
||||
import tools.jackson.core.util.BufferRecycler;
|
||||
import tools.jackson.core.util.JsonRecyclerPools;
|
||||
import tools.jackson.core.util.RecyclerPool;
|
||||
import tools.jackson.databind.SerializationFeature;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
import tools.jackson.databind.ser.FilterProvider;
|
||||
@@ -37,6 +42,7 @@ import tools.jackson.databind.ser.std.SimpleFilterProvider;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.testfixture.codec.AbstractEncoderTests;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.json.JacksonViewBean.MyJacksonView1;
|
||||
@@ -305,6 +311,41 @@ class JacksonJsonEncoderTests extends AbstractEncoderTests<JacksonJsonEncoder> {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void releasesBufferRecyclerToPool() {
|
||||
CountingRecyclerPool pool = new CountingRecyclerPool();
|
||||
JsonFactory factory = JsonFactory.builder().recyclerPool(pool).build();
|
||||
JacksonJsonEncoder encoder = new JacksonJsonEncoder(JsonMapper.builder(factory).build());
|
||||
|
||||
Pojo pojo = new Pojo("foo", "bar");
|
||||
ResolvableType type = ResolvableType.forClass(Pojo.class);
|
||||
|
||||
// encodeValue (Mono path)
|
||||
drain(encoder.encode(Mono.just(pojo), this.bufferFactory, type, APPLICATION_JSON, null));
|
||||
// Flux joined into a JSON array (non-streaming path)
|
||||
drain(encoder.encode(Flux.just(pojo, pojo), this.bufferFactory, type, APPLICATION_JSON, null));
|
||||
// Flux with a streaming media type (NDJSON separator path)
|
||||
drain(encoder.encode(Flux.just(pojo, pojo), this.bufferFactory, type, APPLICATION_NDJSON, null));
|
||||
|
||||
// Error termination of the streaming path must still release via doAfterTerminate
|
||||
encoder.encode(Flux.error(new IllegalStateException("boom")), this.bufferFactory, type, APPLICATION_NDJSON, null)
|
||||
.as(StepVerifier::create)
|
||||
.expectError(IllegalStateException.class)
|
||||
.verify();
|
||||
|
||||
assertThat(pool.acquired).hasPositiveValue();
|
||||
assertThat(pool.released).hasValue(pool.acquired.get());
|
||||
}
|
||||
|
||||
private static void drain(Flux<DataBuffer> output) {
|
||||
output.as(StepVerifier::create)
|
||||
.thenConsumeWhile(buffer -> {
|
||||
DataBufferUtils.release(buffer);
|
||||
return true;
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
private static class ParentClass {
|
||||
@@ -322,4 +363,26 @@ class JacksonJsonEncoderTests extends AbstractEncoderTests<JacksonJsonEncoder> {
|
||||
record JacksonFilteredBean(String property1, String property2) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static final class CountingRecyclerPool implements RecyclerPool<BufferRecycler> {
|
||||
|
||||
final AtomicInteger acquired = new AtomicInteger();
|
||||
|
||||
final AtomicInteger released = new AtomicInteger();
|
||||
|
||||
private final RecyclerPool<BufferRecycler> delegate = JsonRecyclerPools.newConcurrentDequePool();
|
||||
|
||||
@Override
|
||||
public BufferRecycler acquirePooled() {
|
||||
this.acquired.incrementAndGet();
|
||||
return this.delegate.acquirePooled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releasePooled(BufferRecycler recycler) {
|
||||
this.released.incrementAndGet();
|
||||
this.delegate.releasePooled(recycler);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user