Boost Spring Performance: AOP Logging, Caching, Thread Pools & DB Optimizations

This article presents practical Spring performance optimization techniques—including AOP‑based logging, second‑level caching with @Cacheable, reducing database queries via fetch strategies, employing thread‑pool executors, and general database query tuning—to improve application speed and resource efficiency.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Boost Spring Performance: AOP Logging, Caching, Thread Pools & DB Optimizations

Environment: Spring 5.3.23. Spring is widely adopted, but without proper performance tuning it can cause slowdowns. The following examples demonstrate practical Spring performance optimizations.

1. Optimize Logging with AOP

Using Aspect‑Oriented Programming to filter log statements reduces the overhead of logging every request. An aspect defines pointcuts and advice that log only when certain conditions are met.

@Aspect
@Component
public class UserServiceAspect {
  @Pointcut("execution(* query*(long))")
  private void log() {}

  @Before("log()")
  public void logBefore(JoinPoint joinPoint) {
    long userId = (int) joinPoint.getArgs()[0];
    // Log only when userId is invalid
    if (userId <= 0) {
      log.info("queryById - start - userId: {}", userId);
    }
  }

  @AfterReturning(pointcut = "execution(public User query*(long))", returning = "user")
  public void logAfter(JoinPoint joinPoint, User user) {
    // Log only when a user is found
    if (user != null) {
      long userId = (int) joinPoint.getArgs()[0];
      log.info("queryById - end - userId={}, user info={}", userId, user);
    }
  }
}

2. Use Second‑Level Cache

Spring’s caching abstraction can store method results in memory, avoiding repeated database access. The @Cacheable annotation marks a method as cacheable, and a CacheManager manages the cache.

@Configuration
public class CacheConfig {
  @Bean
  public CacheManager cacheManager() {
    return new ConcurrentMapCacheManager("user");
  }
}
@Service
@CacheConfig(cacheManager = "cacheManager") // inject CacheManager
public class UserService {
  @Resource
  private UserRepository userRepository;
  @Autowired
  private CacheManager cacheManager;

  // Cache the result of queryById
  @Cacheable(value = "user", key = "#userId")
  public User queryById(long userId) {
    User user = userRepository.findById(userId);
    return user;
  }
}

3. Reduce Database Queries

Fetching related entities in a single query using a custom @Select reduces the number of SQL statements executed for each request.

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
  @Select("SELECT o.*, od.* FROM Order o LEFT JOIN o.orderDetails od WHERE o.id = ?1")
  Order findWithOrderDetailsByOrderId(Long orderId);
}

4. Employ Thread‑Pool Executors

Using a thread‑pool executor allows concurrent processing of tasks, preventing the creation of a new thread per request and improving throughput.

@Service
public class UserService {
  @Resource
  private UserRepository userRepository;

  private ThreadPoolExecutor pool;

  @Override
  public List<User> getUsers() {
    int coreThreads = 10; // core thread count
    int maxThreads = 20; // maximum thread count
    long keepAliveTime = 60L; // seconds
    ThreadPoolExecutor pool = new ThreadPoolExecutor(
        coreThreads,
        maxThreads,
        keepAliveTime,
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>());

    List<User> users = userRepository.findAll();
    for (final User user : users) {
      pool.execute(() -> {
        // TODO: process user
      });
    }
    pool.shutdown(); // close the pool
    return users;
  }
}

5. General Database Query Tuning

Use indexes, preferably composite indexes, to accelerate lookups.

Avoid SELECT *; query only required columns to enable covering indexes.

Apply pagination to limit the amount of data retrieved per request.

Batch operations to reduce the number of round‑trips to the database.

Utilize a connection pool to reuse connections and avoid frequent creation/destruction.

Applying these techniques helps Spring applications achieve better performance, scalability, and reliability.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

performanceaopdatabasespringThreadPoolcaching
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.