Seven Common Spring Boot Performance Optimization Techniques
This article presents seven practical Spring Boot performance optimization methods—including asynchronous execution, increasing Tomcat connection limits, component scanning, switching to Undertow, buffered I/O, DeferredResult handling, and AsyncHandlerInterceptor usage—each illustrated with clear code examples and explanations.
Hello everyone, I am Chen. This article introduces seven common Spring Boot performance optimization directions.
1. Asynchronous Execution
Two implementation methods are provided:
Use the asynchronous annotation @Async and add @EnableAsync to the startup class.
Leverage JDK 8's CompletableFuture class.
Example using a custom thread with CompletableFuture :
@AllArgsConstructor
public class AskThread implements Runnable {
private CompletableFuture
re = null;
public void run() {
int myRe = 0;
try {
myRe = re.get() * re.get();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(myRe);
}
public static void main(String[] args) throws InterruptedException {
final CompletableFuture
future = new CompletableFuture<>();
new Thread(new AskThread(future)).start();
Thread.sleep(1000);
future.complete(60);
}
}In this example, the AskThread starts before its data is ready, causing the call to myRe = re.get() * re.get() to block. After a simulated 1‑second delay, the result is supplied to the future , allowing the thread to continue.
Another example demonstrates CompletableFuture.supplyAsync to run a slow calc() method in a new thread while immediately returning a CompletableFuture that later provides the result:
public class Calc {
public static Integer calc(Integer para) {
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
return para * para;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
final CompletableFuture
future = CompletableFuture.supplyAsync(() -> calc(50))
.thenApply(i -> Integer.toString(i))
.thenApply(str -> "\"" + str + "\"")
.thenAccept(System.out::println);
future.get();
}
}The supplyAsync method creates a CompletableFuture that runs calc() in a new thread, returning immediately. The future acts as a contract for obtaining the eventual result.
When a controller method returns a Callable , Spring MVC dispatches it to a TaskExecutor for asynchronous processing, keeping the response open until the callable completes.
@RestController
public class HelloController {
private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
@Autowired private HelloService hello;
@GetMapping("/helloworld")
public String helloWorldController() { return hello.sayHello(); }
@GetMapping("/hello")
public Callable
helloController() {
logger.info(Thread.currentThread().getName() + " entering helloController");
Callable
callable = new Callable
() {
@Override public String call() throws Exception {
logger.info(Thread.currentThread().getName() + " entering call");
String say = hello.sayHello();
logger.info(Thread.currentThread().getName() + " from helloService");
return say;
}
};
logger.info(Thread.currentThread().getName() + " returning from helloController");
return callable;
}
}Asynchronous calls can also be handled with WebAsyncTask , allowing custom timeout handling:
@RestController
public class HelloController {
private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
@Autowired private HelloService hello;
@GetMapping("/world")
public WebAsyncTask
worldController() {
logger.info(Thread.currentThread().getName() + " entering helloController");
WebAsyncTask
webAsyncTask = new WebAsyncTask<>(3000, new Callable
() {
@Override public String call() throws Exception {
logger.info(Thread.currentThread().getName() + " entering call");
String say = hello.sayHello();
logger.info(Thread.currentThread().getName() + " from helloService");
return say;
}
});
webAsyncTask.onCompletion(() -> logger.info(Thread.currentThread().getName() + " completed"));
webAsyncTask.onTimeout(() -> { logger.info(Thread.currentThread().getName() + " onTimeout"); throw new TimeoutException("call timeout"); });
return webAsyncTask;
}
@GetMapping("/exception")
public WebAsyncTask
exceptionController() {
Callable
callable = () -> { logger.info(Thread.currentThread().getName() + " entering call"); throw new TimeoutException("call timeout!"); };
return new WebAsyncTask<>(20000, callable);
}
}2. Increase Embedded Tomcat Maximum Connections
Configuration example to raise connection and thread limits:
@Configuration
public class TomcatConfig {
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory tomcatFactory = new TomcatServletWebServerFactory();
tomcatFactory.addConnectorCustomizers(new MyTomcatConnectorCustomizer());
tomcatFactory.setPort(8005);
tomcatFactory.setContextPath("/api-g");
return tomcatFactory;
}
class MyTomcatConnectorCustomizer implements TomcatConnectorCustomizer {
@Override public void customize(Connector connector) {
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
protocol.setMaxConnections(20000);
protocol.setMaxThreads(2000);
protocol.setConnectionTimeout(30000);
}
}
}3. Use @ComponentScan()
Applying @ComponentScan() can scan packages faster than the default @SpringBootApplication annotation.
4. Switch Default Tomcat Container to Undertow
Replace the Tomcat starter with Undertow in Maven:
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>Change to:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>5. Use BufferedWriter for Buffering
Encourage using BufferedWriter to improve I/O efficiency (code omitted for brevity).
6. DeferredResult for Asynchronous Calls
@RestController
public class AsyncDeferredController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final LongTimeTask taskService;
@Autowired public AsyncDeferredController(LongTimeTask taskService) { this.taskService = taskService; }
@GetMapping("/deferred")
public DeferredResult
executeSlowTask() {
logger.info(Thread.currentThread().getName() + " entering executeSlowTask");
DeferredResult
deferredResult = new DeferredResult<>();
taskService.execute(deferredResult);
logger.info(Thread.currentThread().getName() + " returning from executeSlowTask");
deferredResult.onTimeout(() -> { logger.info(Thread.currentThread().getName() + " onTimeout"); deferredResult.setErrorResult("time out!"); });
deferredResult.onCompletion(() -> logger.info(Thread.currentThread().getName() + " onCompletion"));
return deferredResult;
}
}7. AsyncHandlerInterceptor for Intercepting Asynchronous Calls
@Component
public class MyAsyncHandlerInterceptor implements AsyncHandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(MyAsyncHandlerInterceptor.class);
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; }
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info(Thread.currentThread().getName() + " service completed, returning result to client");
}
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
if (ex != null) { System.out.println("Exception occurred:" + ex.getMessage()); }
}
@Override public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String resp = "my name is chhliu!";
response.setContentLength(resp.length());
response.getOutputStream().write(resp.getBytes());
logger.info(Thread.currentThread().getName() + " entering afterConcurrentHandlingStarted");
}
}Finally, the article invites readers to join a backend‑focused technical community for knowledge sharing and networking.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.