Spring Boot学习知识点大全(二)

简介: 教程来源 https://app-a7illrp9pngh.appmiaoda.com/ 本文详解Spring Boot核心机制:自动配置原理(@EnableAutoConfiguration、条件注解、自定义配置)、Web开发(RESTful API、统一响应、全局异常处理、文件上传下载)及数据访问(JDBC/JPA、MyBatis、多数据源、Redis集成与缓存管理),覆盖企业级应用开发关键实践。

四、自动配置原理

4.1 自动配置机制

// @EnableAutoConfiguration 核心
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

// AutoConfigurationImportSelector 加载 META-INF/spring.factories 中的配置
public class AutoConfigurationImportSelector {
    protected List<String> getCandidateConfigurations() {
        // 从 META-INF/spring.factories 读取
        return SpringFactoriesLoader.loadFactoryNames(
            getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    }
}

// META-INF/spring.factories 示例
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration

4.2 条件注解

// @Conditional - 条件注解
@Configuration
public class ConditionalConfig {

    // 类路径存在指定类
    @ConditionalOnClass(name = "redis.clients.jedis.Jedis")
    @Bean
    public RedisTemplate redisTemplate() {
        return new RedisTemplate();
    }

    // 类路径不存在指定类
    @ConditionalOnMissingClass("com.mysql.cj.jdbc.Driver")
    @Bean
    public DataSource h2DataSource() {
        return new H2DataSource();
    }

    // Bean 存在
    @ConditionalOnBean(DataSource.class)
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    // Bean 不存在
    @ConditionalOnMissingBean(name = "cacheManager")
    @Bean
    public CacheManager defaultCacheManager() {
        return new ConcurrentMapCacheManager();
    }

    // 配置属性
    @ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
    @Bean
    public CacheManager redisCacheManager() {
        return new RedisCacheManager();
    }

    // Web 应用
    @ConditionalOnWebApplication
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new CustomWebMvcConfigurer();
    }

    // 非 Web 应用
    @ConditionalOnNotWebApplication
    @Bean
    public CommandLineRunner commandLineRunner() {
        return args -> System.out.println("非 Web 应用启动");
    }

    // Java 版本
    @ConditionalOnJava(JavaVersion.EIGHT)
    @Bean
    public Java8Service java8Service() {
        return new Java8Service();
    }

    // 表达式
    @ConditionalOnExpression("${feature.enabled:true} && ${another.enabled:false}")
    @Bean
    public FeatureService featureService() {
        return new FeatureService();
    }

    // 资源存在
    @ConditionalOnResource(resources = "classpath:config.xml")
    @Bean
    public XmlParser xmlParser() {
        return new XmlParser();
    }
}

4.3 自定义自动配置

// 自定义自动配置类
@Configuration
@ConditionalOnClass(RedisTemplate.class)
@ConditionalOnProperty(name = "custom.cache.enabled", havingValue = "true")
@EnableConfigurationProperties(CustomCacheProperties.class)
public class CustomCacheAutoConfiguration {

    @Autowired
    private CustomCacheProperties properties;

    @Bean
    @ConditionalOnMissingBean
    public CacheManager customCacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofSeconds(properties.getTtl()))
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair.fromSerializer(
                    new GenericJackson2JsonRedisSerializer()
                )
            );

        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .build();
    }

    @Bean
    @ConditionalOnBean(CacheManager.class)
    public CacheService cacheService(CacheManager cacheManager) {
        return new CacheService(cacheManager);
    }
}

// 配置属性
@ConfigurationProperties(prefix = "custom.cache")
public class CustomCacheProperties {
    private boolean enabled = true;
    private long ttl = 60;
    private int maxSize = 1000;
    private String keyPrefix = "custom:";
    // getter/setter
}

// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.autoconfigure.CustomCacheAutoConfiguration

五、Web 开发

5.1 RESTful API 开发

@RestController
@RequestMapping("/api/users")
@Validated
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    // GET /api/users
    @GetMapping
    public PageResult<User> getUsers(
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size,
            @RequestParam(required = false) String keyword) {
        return userService.findByPage(page, size, keyword);
    }

    // GET /api/users/1
    @GetMapping("/{id}")
    public User getUser(@PathVariable @Min(1) Long id) {
        return userService.findById(id);
    }

    // POST /api/users
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public User createUser(@Valid @RequestBody User user) {
        return userService.create(user);
    }

    // PUT /api/users/1
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @Valid @RequestBody User user) {
        user.setId(id);
        return userService.update(user);
    }

    // PATCH /api/users/1
    @PatchMapping("/{id}")
    public User patchUser(@PathVariable Long id, @RequestBody Map<String, Object> updates) {
        return userService.patch(id, updates);
    }

    // DELETE /api/users/1
    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteUser(@PathVariable Long id) {
        userService.delete(id);
    }

    // 批量操作
    @PostMapping("/batch")
    public List<User> batchCreate(@RequestBody List<User> users) {
        return userService.batchCreate(users);
    }

    // 导出
    @GetMapping("/export")
    public void exportUsers(HttpServletResponse response) throws IOException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-Disposition", "attachment; filename=users.xlsx");
        userService.export(response.getOutputStream());
    }
}

5.2 统一响应格式

// 统一响应结果
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
    private int code;
    private String message;
    private T data;
    private long timestamp = System.currentTimeMillis();

    public static <T> Result<T> success() {
        return success(null);
    }

    public static <T> Result<T> success(T data) {
        return new Result<>(200, "success", data);
    }

    public static <T> Result<T> error(String message) {
        return error(500, message);
    }

    public static <T> Result<T> error(int code, String message) {
        return new Result<>(code, message, null);
    }

    public static <T> Result<T> error(BusinessException e) {
        return new Result<>(e.getCode(), e.getMessage(), null);
    }
}

// 分页结果
@Data
public class PageResult<T> {
    private List<T> records;
    private long total;
    private int page;
    private int size;
    private long pages;

    public PageResult(List<T> records, long total, int page, int size) {
        this.records = records;
        this.total = total;
        this.page = page;
        this.size = size;
        this.pages = (total + size - 1) / size;
    }
}

5.3 全局异常处理

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    // 参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<Map<String, String>> handleValidationException(
            MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(error -> 
            errors.put(error.getField(), error.getDefaultMessage()));
        return Result.error(400, "参数校验失败", errors);
    }

    // 绑定异常
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<String> handleBindException(BindException ex) {
        String message = ex.getBindingResult().getAllErrors().stream()
            .map(ObjectError::getDefaultMessage)
            .collect(Collectors.joining(", "));
        return Result.error(400, message);
    }

    // 约束违反异常
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<String> handleConstraintViolation(ConstraintViolationException ex) {
        String message = ex.getConstraintViolations().stream()
            .map(violation -> violation.getPropertyPath() + ": " + violation.getMessage())
            .collect(Collectors.joining(", "));
        return Result.error(400, message);
    }

    // 业务异常
    @ExceptionHandler(BusinessException.class)
    public Result<String> handleBusinessException(BusinessException ex) {
        log.warn("业务异常: {}", ex.getMessage());
        return Result.error(ex.getCode(), ex.getMessage());
    }

    // 数据访问异常
    @ExceptionHandler(DataAccessException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<String> handleDataAccessException(DataAccessException ex) {
        log.error("数据库异常", ex);
        return Result.error(500, "数据库操作失败");
    }

    // 参数类型转换异常
    @ExceptionHandler(HttpMessageConversionException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<String> handleConversionException(HttpMessageConversionException ex) {
        return Result.error(400, "请求参数格式错误");
    }

    // 资源不存在
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public Result<String> handleNotFoundException(NoHandlerFoundException ex) {
        return Result.error(404, "请求的资源不存在");
    }

    // 全局异常
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<String> handleException(Exception ex) {
        log.error("系统异常", ex);
        return Result.error(500, "系统内部错误");
    }
}

5.4 文件上传与下载

@RestController
@RequestMapping("/api/files")
@Slf4j
public class FileController {

    @Value("${file.upload-dir}")
    private String uploadDir;

    // 单文件上传
    @PostMapping("/upload")
    public Result<String> uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
        if (file.isEmpty()) {
            return Result.error("文件不能为空");
        }

        // 生成文件名
        String originalName = file.getOriginalFilename();
        String extension = originalName.substring(originalName.lastIndexOf("."));
        String fileName = UUID.randomUUID().toString() + extension;

        // 保存文件
        Path path = Paths.get(uploadDir, fileName);
        Files.createDirectories(path.getParent());
        Files.copy(file.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);

        return Result.success(fileName);
    }

    // 多文件上传
    @PostMapping("/upload/batch")
    public Result<List<String>> uploadFiles(@RequestParam("files") List<MultipartFile> files) 
            throws IOException {
        List<String> fileNames = new ArrayList<>();

        for (MultipartFile file : files) {
            if (!file.isEmpty()) {
                String fileName = saveFile(file);
                fileNames.add(fileName);
            }
        }

        return Result.success(fileNames);
    }

    // 带进度的文件上传
    @PostMapping("/upload/progress")
    public Result<String> uploadWithProgress(
            @RequestParam("file") MultipartFile file,
            HttpSession session) throws IOException {

        ProgressListener listener = new ProgressListener(session);
        MultipartFile progressFile = new ProgressMultipartFile(file, listener);

        String fileName = saveFile(progressFile);
        return Result.success(fileName);
    }

    // 文件下载
    @GetMapping("/download/{fileName}")
    public void downloadFile(@PathVariable String fileName, 
                             HttpServletResponse response) throws IOException {
        Path file = Paths.get(uploadDir, fileName);
        if (!Files.exists(file)) {
            response.setStatus(HttpStatus.NOT_FOUND.value());
            return;
        }

        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader("Content-Disposition", 
            "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
        response.setContentLengthLong(Files.size(file));

        Files.copy(file, response.getOutputStream());
    }

    // 断点续传
    @GetMapping("/download/resume")
    public void downloadWithResume(@RequestParam String fileName,
                                    @RequestHeader(required = false) String range,
                                    HttpServletResponse response) throws IOException {
        Path file = Paths.get(uploadDir, fileName);
        long fileSize = Files.size(file);

        if (range != null && range.startsWith("bytes=")) {
            // 解析 Range 头
            String[] ranges = range.substring(6).split("-");
            long start = Long.parseLong(ranges[0]);
            long end = ranges.length > 1 ? Long.parseLong(ranges[1]) : fileSize - 1;

            response.setStatus(HttpStatus.PARTIAL_CONTENT.value());
            response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileSize);
            response.setContentLengthLong(end - start + 1);

            try (RandomAccessFile raf = new RandomAccessFile(file.toFile(), "r")) {
                raf.seek(start);
                byte[] buffer = new byte[8192];
                long remaining = end - start + 1;
                while (remaining > 0) {
                    int len = (int) Math.min(buffer.length, remaining);
                    int read = raf.read(buffer, 0, len);
                    response.getOutputStream().write(buffer, 0, read);
                    remaining -= read;
                }
            }
        } else {
            response.setContentLengthLong(fileSize);
            Files.copy(file, response.getOutputStream());
        }
    }

    private String saveFile(MultipartFile file) throws IOException {
        String originalName = file.getOriginalFilename();
        String extension = originalName.substring(originalName.lastIndexOf("."));
        String fileName = UUID.randomUUID().toString() + extension;

        Path path = Paths.get(uploadDir, fileName);
        Files.createDirectories(path.getParent());
        Files.copy(file.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);

        return fileName;
    }
}

六、数据访问

6.1 JDBC 与 JPA

yaml
# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 10
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000

  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL8Dialect
        format_sql: true
        jdbc:
          batch_size: 20
        order_inserts: true
        order_updates: true
java
// JPA 实体
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false, length = 50)
    private String username;

    @Column(nullable = false)
    private String password;

    @Column(length = 100)
    private String email;

    private Integer age;

    @Enumerated(EnumType.STRING)
    private UserStatus status;

    @CreationTimestamp
    private LocalDateTime createTime;

    @UpdateTimestamp
    private LocalDateTime updateTime;

    @Version
    private Integer version;
}

// JPA Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    Optional<User> findByUsername(String username);

    List<User> findByAgeBetween(int minAge, int maxAge);

    @Query("SELECT u FROM User u WHERE u.email LIKE %:domain%")
    List<User> findByEmailDomain(@Param("domain") String domain);

    @Modifying
    @Transactional
    @Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
    int updateStatus(@Param("id") Long id, @Param("status") UserStatus status);

    @Query(value = "SELECT * FROM users WHERE age > :age", nativeQuery = true)
    List<User> findByAgeNative(@Param("age") int age);
}

// JPA 使用示例
@Service
@Slf4j
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public User createUser(User user) {
        return userRepository.save(user);
    }

    @Transactional(readOnly = true)
    public User findById(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new BusinessException("用户不存在"));
    }

    @Transactional
    public User updateUser(Long id, User user) {
        User existing = findById(id);
        existing.setUsername(user.getUsername());
        existing.setEmail(user.getEmail());
        existing.setAge(user.getAge());
        return userRepository.save(existing);
    }

    @Transactional
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }

    @Transactional
    public void batchUpdate(List<User> users) {
        userRepository.saveAll(users);
    }
}

6.2 MyBatis 集成

xml
<!-- pom.xml 依赖 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>
yaml
# application.yml
mybatis:
  mapper-locations: classpath:mapper/**/*.xml
  type-aliases-package: com.example.entity
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true
    lazy-loading-enabled: true
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
java
// Mapper 接口
@Mapper
public interface UserMapper {

    @Select("SELECT * FROM users WHERE id = #{id}")
    User selectById(Long id);

    @Insert("INSERT INTO users(username, age) VALUES(#{username}, #{age})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);

    @Update("UPDATE users SET age = #{age} WHERE id = #{id}")
    int update(User user);

    @Delete("DELETE FROM users WHERE id = #{id}")
    int deleteById(Long id);

    List<User> selectByCondition(@Param("username") String username, 
                                  @Param("age") Integer age);
}

// XML 映射文件
<!-- mapper/UserMapper.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">

    <resultMap id="userMap" type="User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="age" column="age"/>
    </resultMap>

    <select id="selectByCondition" resultMap="userMap">
        SELECT * FROM users
        <where>
            <if test="username != null and username != ''">
                AND username LIKE CONCAT('%', #{username}, '%')
            </if>
            <if test="age != null">
                AND age = #{age}
            </if>
        </where>
    </select>
</mapper>

6.3 多数据源配置

yaml
# application.yml
spring:
  datasource:
    primary:
      url: jdbc:mysql://localhost:3306/primary_db
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
    secondary:
      url: jdbc:mysql://localhost:3306/secondary_db
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
java
// 主数据源配置
@Configuration
@Primary
@ConfigurationProperties(prefix = "spring.datasource.primary")
public class PrimaryDataSourceConfig {

    @Bean
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public JdbcTemplate primaryJdbcTemplate(DataSource primaryDataSource) {
        return new JdbcTemplate(primaryDataSource);
    }

    @Bean
    @Primary
    public PlatformTransactionManager primaryTransactionManager(DataSource primaryDataSource) {
        return new DataSourceTransactionManager(primaryDataSource);
    }
}

// 从数据源配置
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public class SecondaryDataSourceConfig {

    @Bean
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public JdbcTemplate secondaryJdbcTemplate(DataSource secondaryDataSource) {
        return new JdbcTemplate(secondaryDataSource);
    }
}

6.4 Redis 集成

yaml
# application.yml
spring:
  redis:
    host: localhost
    port: 6379
    password: 
    database: 0
    timeout: 2000ms
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: -1ms
java
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LazyJackson2TypedMapper.defaultTyping(), 
            ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
        return new StringRedisTemplate(factory);
    }
}

@Service
public class RedisService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public void set(String key, Object value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    public void delete(String key) {
        redisTemplate.delete(key);
    }

    public boolean hasKey(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }

    public void increment(String key, long delta) {
        redisTemplate.opsForValue().increment(key, delta);
    }

    public void addToSet(String key, Object... values) {
        redisTemplate.opsForSet().add(key, values);
    }

    public void addToList(String key, Object value) {
        redisTemplate.opsForList().rightPush(key, value);
    }

    public List<Object> getList(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    public void addToHash(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    public Object getFromHash(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }
}

6.5 缓存管理

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(60))
            .serializeKeysWith(
                RedisSerializationContext.SerializationPair.fromSerializer(
                    new StringRedisSerializer()))
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair.fromSerializer(
                    new GenericJackson2JsonRedisSerializer()))
            .disableCachingNullValues();

        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .transactionAware()
            .build();
    }
}

@Service
public class UserService {

    @Cacheable(value = "users", key = "#id", unless = "#result == null")
    public User getUser(Long id) {
        return userRepository.findById(id).orElse(null);
    }

    @Cacheable(value = "users", key = "'list:' + #page + ':' + #size")
    public PageResult<User> getUsers(int page, int size) {
        return userRepository.findAll(PageRequest.of(page - 1, size));
    }

    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
        return userRepository.save(user);
    }

    @CacheEvict(value = "users", key = "#id")
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }

    @CacheEvict(value = "users", allEntries = true)
    public void clearCache() {
        // 清空所有缓存
    }

    @Caching(evict = {
        @CacheEvict(value = "users", key = "#user.id"),
        @CacheEvict(value = "users", key = "'list:*'", allEntries = true)
    })
    public User updateUserWithCacheClear(User user) {
        return userRepository.save(user);
    }
}

来源:
https://app-a7illrp9pngh.appmiaoda.com/

相关文章
|
23天前
|
XML Java Maven
Spring Boot学习知识点大全(一)
教程来源 https://app-a87ujc988w01.appmiaoda.com/ Spring Boot 是 Spring 家族中革命性框架,秉持“约定优于配置”理念,通过自动配置、起步依赖、嵌入式服务器等特性,大幅简化企业级 Java 应用开发。本文系统梳理其核心概念、注解、多环境配置与最佳实践,助初学者快速入门,为进阶开发者提供深度参考。
|
22天前
|
监控 Java 测试技术
Spring Boot学习知识点大全(三)
教程来源 https://app-a6nw7st4g741.appmiaoda.com/ 系统梳理Spring Boot核心实践:涵盖日志分级配置与异步输出、单元/集成测试、Actuator监控与自定义指标、Docker/K8s部署、Spring Boot 3.x Jakarta迁移及虚拟线程等新特性,助力构建高可用生产级应用。
|
23天前
|
存储 Prometheus 监控
Prometheus+Grafana构建企业级监控方案
Prometheus是一种开源的监控系统,通过时间序列数据库存储指标数据,支持多维数据模型和PromQL查询语言。其工作原理是通过HTTP拉取应用暴露的指标(如SpringBoot的Actuator端点),并持久化存储。示例展示了SpringBoot整合Prometheus的过程,包括依赖引入、配置暴露指标端点,以及通过Docker部署应用。最后介绍了Prometheus与Grafana的集成,通过配置数据源和仪表板实现可视化监控。整个方案适用于内网部署,支持服务发现和多种中间件监控。
|
3天前
|
JavaScript 前端开发 API
前端组件库——Element Plus知识点大全(一)
教程来源 https://tmywi.cn/category/lvxing.html Element Plus是饿了么团队打造的Vue 3官方UI库,深度集成TypeScript与Composition API,提供70+企业级组件、完善设计规范及主题定制能力。GitHub星标超2.5万,国内Vue 3中后台开发首选方案。
|
25天前
|
存储 运维 关系型数据库
MySQL学习知识点(三)
教程来源 https://app-ad0bpnnq0o3l.appmiaoda.com 详解MySQL核心高级功能:事务ACID特性、隔离级别与锁机制(表锁/行锁/间隙锁);存储过程与函数的创建调用、参数传递及流程控制;触发器(审计、自动更新)与事件调度器(定时任务)。内容实用,覆盖并发控制与自动化运维关键场景。
|
25天前
|
SQL 存储 JSON
MySQL学习知识点(一)
教程来源 https://app-ac8abncezqpt.appmiaoda.com MySQL核心知识,涵盖基础概念、安装配置、SQL命令、数据类型(数值/字符串/日期/JSON)、表操作(建删改查、索引、引擎)等,兼顾初学者入门与DBA进阶需求。
|
25天前
|
JavaScript 前端开发 安全
JavaScript学习知识点大全(终)
教程来源 https://app-abdss1rim1oh.appmiaoda.com 系统讲解JS安全实践(XSS/CSRF防护、敏感信息保护)、调试技巧(console高级用法、断点、性能分析)、单元测试(Jest)、Babel转译及Polyfill方案,助你构建健壮、兼容、可维护的前端应用。
|
23天前
|
监控 Java Maven
深入浅出地理解SpringBoot自动装配原理
摘要: SpringBoot通过起步依赖和自动配置简化了JavaWeb开发。起步依赖(如spring-boot-starter-web)集成关联依赖,减少手动配置;自动配置基于@Conditional条件装配Bean,避免繁琐声明。文章解析了自动配置源码(如@EnableAutoConfiguration导入AutoConfigurationImportSelector),并演示如何手写自定义starter。
|
25天前
|
关系型数据库 MySQL 索引
MySQL学习知识点(二)
教程来源 https://app-acda5zfcddz5.appmiaoda.com MySQL核心数据操作(CRUD)与高级查询技巧:涵盖INSERT/SELECT/UPDATE/DELETE语法及变体(REPLACE、ON DUPLICATE KEY等);JOIN连接、子查询、UNION、窗口函数(MySQL 8.0+);索引类型、最左前缀原则、EXPLAIN执行计划分析及分页优化等实战要点。
|
7月前
|
安全 Java 应用服务中间件
Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码
通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。
882 3

热门文章

最新文章

下一篇
开通oss服务