【Spring大揭秘】系统性能提升!带你解锁系统优化大法
创始人
2025-07-04 11:40:17
0

环境:Spring5.3.23

Spring在各大公司基本上都是标配,它提供了丰富的功能和灵活性,但在使用过程中如果不注意性能优化,可能会导致系统运行缓慢或出现其他问题。以下是一些Spring编程中性能优化的实际案例:

使用AOP实现日志记录优化

在Spring中,可以使用AOP(面向切面编程)来实现日志记录的优化。在系统中有大量的日志记录时,如果每个请求都进行日志记录,会占用大量的系统资源,导致系统性能下降。因此,可以使用AOP技术,根据一定的条件对日志记录进行筛选和优化。例如,可以定义一个切面(Aspect),在切面中实现日志记录的功能,并根据一定的条件判断是否需要进行日志记录。这样可以避免每个请求都进行日志记录,从而提高系统的性能。示例代码如下:

优化前:

@Service  
public class UserService {  
  private static final Logger logger = LoggerFactory.getLogger(UserService.class) ;
  @Resource  
  private UserRepository userRepository ;  


  public User queryById(long userId) {  
    User user = this.userRepository.findById(userId) ;
    log.info("queryById - User - {}", user) ;
    return user ;  
  }   
}

在优化前的代码中,我们直接打印用户信息到日志中。

接下来,我们将使用AOP来实现日志记录的优化。首先,我们需要定义一个切面(Aspect),在切面中实现日志记录的功能,并根据一定的条件判断是否需要进行日志记录。以下是优化后的代码示例:

优化后:

@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] ;  
    // 只有当userId不合规才打印日志 
    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) {  
    // 只有查询到用户了才记录用户信息到日志 
    if (user != null) { 
      long userId = (int) joinPoint.getArgs()[0] ;  
      log.info("queryById - end - userId={}, user info={}", userId, user);  
    }  
  } 
}

通过切面,我们就可以根据条件筛选出需要日志记录的请求,避免了对所有请求都进行日志记录,从而提高系统的性能。

使用二级缓存

在Spring框架中,可以使用二级缓存来优化数据的访问性能。二级缓存是指将数据缓存在内存中,以避免频繁的数据库访问操作。在Spring中,可以使用@Cacheable注解将一个方法标记为可缓存的,这样该方法的返回值就会被缓存在内存中。当同一个方法被调用时,直接从缓存中获取返回值,而不需要再次访问数据库。这样可以减少数据库访问次数,从而提高系统的性能。

优化前:

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


  public User queryById(long userId) {  
    User user = this.userRepository.findById(userId) ;
    return user ;  
  }   
}

优化前每次获取用户都会从数据库中查询。

接下来,我们将使用二级缓存来实现数据访问的优化。首先,我们需要定义一个缓存管理器(CacheManager),用于管理缓存。以下是优化后的代码示例:

优化后:

// 为了方便演示,这里我们自定义一个缓存管理器
@Configuration  
public class CacheConfig {  
  @Bean  
  public CacheManager cacheManager() {  
    return new ConcurrentMapCacheManager("user") ;  
  }  
}

接下来,我们需要在UserService中注入CacheManager,并使用@Cacheable注解将queryById方法标记为可缓存的。以下是优化后的代码示例:

@Service  
@CacheConfig(cacheManager = "cacheManager") // 注入CacheManager  
public class UserService {  
      
  @Resource  
  private UserRepository userRepository ;
  @Autowired  
  private CacheManager cacheManager;  


  // 标记为可缓存的,并指定缓存值和键  
  @Cacheable(value = "user", key = "#userId") 
  public User queryById(long userId) {  
    User user = userRepository.findById(userId) ;  
    return user ; 
  }  
}

这样,该方法的返回值会被缓存在内存中,当同一个方法的调用时,直接从缓存中获取返回值,而不需要再次访问数据库。这样可以减少数据库访问次数,从而提高系统的性能。

减少数据库查询次数

在一个订单管理系统中,有一个订单详细信息(OrderDetail)实体,该实体与订单表(Order)在数据库中有1对1的关系。在获取订单详细信息时,不需要每次都查询Order表。通过使用JPA的fetch属性,可以将Order表的数据在一次查询中一并获取。这样,每个订单详细信息实体只会引发一次数据库查询,而不是之前的每次获取都查询。

优化前:

@Repository  
public interface OrderRepository extends JpaRepository {  
  Order findByOrderId(Long orderId);  
}
@Service  
public class OrderService {  


  @Resource
  private OrderRepository orderRepository;  
  public Order getOrderById(Long orderId) {  
    return orderRepository.findByOrderId(orderId);
  }  


}

上面每次获取订单都会发送多条SQL进行数据查询。优化后:

@Repository  
public interface OrderRepository extends JpaRepository {  


  @Select("SELECT o.*, od.* FROM Order o LEFT JOIN o.orderDetails od WHERE o.id = ?1")  
  Order findWithOrderDetailsByOrderId(Long orderId);  


}

这样,我们只需一次数据库查询就可以获取到订单及其所有详细信息。

使用多线程池

在Spring框架中,可以使用多线程池来优化任务的执行性能。当系统中有大量的异步任务需要执行时,如果每个任务都创建一个新的线程来执行,会导致系统资源浪费和性能下降。因此,可以使用多线程池来管理任务的执行。在Spring中,可以使用ThreadPoolTaskExecutor来实现多线程池的配置和管理。这样可以避免每个任务都创建新的线程,从而提高系统的性能。

优化前:

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


  @Override  
  public List getUsers() {  
    List users = userRepository.findAll();  
    for (User user : users) {  
      // 处理用户数据  
    }  
    return users ;  
  }


}

优化前处理用户在一个线程中执行,时间可能会很长影响系统整体性能。接下来,我们将使用多线程池来实现并发处理的优化。可以考虑使用Java中的Executor框架来管理线程池。我们可以创建ThreadPoolExecutor类来定义线程池,并指定线程池的核心线程数、最大线程数和线程存活时间等参数。在处理每个用户时,我们可以将任务分配给线程池中的一个线程进行处理,这样可以同时处理多个用户,提高系统的并发性能。以下是优化后的代码示例:

优化后:

@Service  
public class UserService {  
  
  @Resource  
  private UserRepository userRepository ;
  
  private ThreadPoolExecutor pool;  
   
  @Override  
  public List getUsers() {  
    int coreThreads = 10; // 核心线程数      
    int maxThreads = 20; // 最大线程数  
    long keepAliveTime = 60L; // 线程存活时间(单位:秒)  
    ThreadPoolExecutor pool = new ThreadPoolExecutor(
        coreThreads, 
        maxThreads,
        keepAliveTime,
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>()) ;
    List users = userRepository.findAll() ;  
    for (final User user : users) {  
      pool.execute(() -> {  
        // TODO
      });  
    }  
    pool.shutdown(); // 关闭
    return users;  
  }  
}

注意要在最后调用pool的shutdown方法来关闭线程池(非阻塞)。这样,系统可以同时处理多个用户,提高并发性能。

优化数据库查询

在Spring框架中,数据库查询是常见的高负载点之一。因此,优化数据库查询是提高系统性能的关键。可以从以下几个方面对数据库查询进行优化:

  • 使用索引:为数据库中的字段添加索引(根据情况最好是联合索引)可以加速查询速度。
  • 避免使用SELECT *:避免查询所有字段,只查询需要的字段可以提高查询速度,尽可能的应用覆盖索引。
  • 分页查询:使用分页查询可以减少查询的数据量,从而提高查询速度。
  • 批量操作:尽可能减少与数据库的交互次数,可以批量操作来减少查询次数。
  • 使用连接池:连接池可以管理数据库连接,避免频繁的创建和销毁连接,从而提高性能。

以上是一些Spring编程中性能优化的实际案例。通过对这些案例的分析和学习,可以更好地应用Spring框架,提高系统的性能和可靠性。

相关内容

热门资讯

PHP新手之PHP入门 PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的...
网络中立的未来 网络中立性是什... 《牛津词典》中对“网络中立”的解释是“电信运营商应秉持的一种原则,即不考虑来源地提供所有内容和应用的...
各种千兆交换机的数据接口类型详... 千兆交换机有很多值得学习的地方,这里我们主要介绍各种千兆交换机的数据接口类型,作为局域网的主要连接设...
粉嫩如何诠释霸道 东芝M805... “霸道粉”是个什么玩意东芝M805拿过来的时候,笔者扑哧笑了,不是笑这款笔记本,而是笑这款产品的颜色...
什么是大数据安全 什么是大数据... 在《为什么需要大数据安全分析》一文中,我们已经阐述了一个重要观点,即:安全要素信息呈现出大数据的特征...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
全面诠释网络负载均衡 负载均衡的出现大大缓解了服务器的压力,更是有效的利用了资源,提高了效率。那么我们现在来说一下网络负载...
如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
30分钟搞定iOS自定义相机 最近公司的项目中用到了相机,由于不用系统的相机,UI给的相机切图,必须自定义才可以。就花时间简单研究...
Intel将Moblin社区控... 本周二,非营利机构Linux基金会宣布,他们将担负起Moblin社区的管理工作,而这之前,Mobli...