【Spring大揭秘】系统性能提升!带你解锁系统优化大法

环境:Spring5.3.23

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

使用AOP实现日志记录优化

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

优化前:

java 复制代码
@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),在切面中实现日志记录的功能,并根据一定的条件判断是否需要进行日志记录。以下是优化后的代码示例:

优化后:

less 复制代码
@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注解将一个方法标记为可缓存的,这样该方法的返回值就会被缓存在内存中。当同一个方法被调用时,直接从缓存中获取返回值,而不需要再次访问数据库。这样可以减少数据库访问次数,从而提高系统的性能。

优化前:

java 复制代码
@Service  
public class UserService {  
  @Resource  
  private UserRepository userRepository ;  


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

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

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

优化后:

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

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

less 复制代码
@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表的数据在一次查询中一并获取。这样,每个订单详细信息实体只会引发一次数据库查询,而不是之前的每次获取都查询。

优化前:

kotlin 复制代码
@Repository  
public interface OrderRepository extends JpaRepository<Order, Long> {  
  Order findByOrderId(Long orderId);  
}
@Service  
public class OrderService {  


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


}

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

kotlin 复制代码
@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);  


}

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

使用多线程池

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

优化前:

kotlin 复制代码
@Service  
public class UserService {  
  
  @Resource  
  private UserRepository userRepository ; 


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


}

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

优化后:

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

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

优化数据库查询

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

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

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

介绍一款软件开发工具

JNPF开发平台,很多人都用过它,它是功能的集大成者,任何信息化系统都可以基于它开发出来。原理是将开发过程中某些重复出现的场景、流程,具象化成一个个组件、api、数据库接口,避免了重复造轮子。因而极大的提高了程序员的生产效率。

官网:www.jnpfsoft.com/?juejin ,如果你有闲暇时间,可以做个知识拓展。

这是一个基于Java Boot/.Net Core构建的简单、跨平台快速开发框架,采用业内领先的SpringBoot微服务架构、支持SpringCloud模式。前后端封装了上千个常用类,方便扩展;采用微服务、前后端分离架构,集成了代码生成器,支持前后端业务代码生成,满足快速开发;框架集成了表单、报表、图表、大屏等各种常用的Demo方便直接使用;后端框架支持Vue2、Vue3,平台即可私有化部署,也支持K8S部署。

为了支撑更高技术要求的应用开发,从数据库建模、Web API构建到页面设计,与传统软件开发几乎没有差异,只是通过低代码可视化模式,减少了构建"增删改查"功能的重复劳动。

相关推荐
It's now15 分钟前
Spring AI 基础开发流程
java·人工智能·后端·spring
code bean1 小时前
【CMake】为什么需要清理 CMake 缓存文件?深入理解 CMake 生成器切换机制
java·spring·缓存
皮卡龙2 小时前
Spring MVC 接收请求参数的核心
java·spring·mvc
没有bug.的程序员4 小时前
高频IO服务优化实战指南
java·jvm·spring·容器
222you7 小时前
Spring框架的介绍和IoC入门
java·后端·spring
利剑 -~9 小时前
Spring AI Alibaba 1.1版本
java·人工智能·spring
青蛙大侠公主10 小时前
Spring事务
java·数据库·spring
老华带你飞10 小时前
校务管理|基于springboot 校务管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
Java爱好狂.10 小时前
复杂知识简单学!Springboot加载配置文件源码分析
java·spring boot·后端·spring·java面试·后端开发·java程序员
橘子海全栈攻城狮11 小时前
【最新源码】基于springboot的会议室预订系统设计与实现 014
java·开发语言·前端·spring boot·后端·spring·自动化