**# Spring Boot从入门到精通(完整版)

前言

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。

本文将从入门基础讲到高级原理,涵盖实际项目开发中的核心技术和最佳实践,帮助读者真正掌握Spring Boot的精髓。


第一章:Spring Boot入门

1.1 Spring Boot简介

Spring Boot是基于Spring框架开发的,它并不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。它通过“约定优于配置”的理念,让你的项目快速运行起来。

Spring Boot的核心特点:

  • 可以创建独立的Spring应用程序
  • 内嵌Tomcat、Jetty等Servlet容器
  • 提供自动配置的“starter”依赖
  • 尽可能自动配置Spring
  • 提供生产就绪功能,如指标、健康检查等
  • 无需生成代码,无需XML配置

1.2 开发环境准备

1.2.1 安装JDK

Spring Boot 2.x需要JDK 8或更高版本,Spring Boot 3.x需要JDK 17或更高版本。推荐使用JDK 17(LTS长期支持版本)。

1.2.2 安装Maven或Gradle

推荐使用Maven作为构建工具。

Maven配置(settings.xml):

<mirrors>
    <mirror>
        <id>aliyunmaven</id>
        <mirrorOf>*</mirrorOf>
        <name>阿里云公共仓库</name>
        <url>https://maven.aliyun.com/repository/public</url>
    </mirror>
</mirrors>
1.2.3 安装IDE

推荐使用IntelliJ IDEA(社区版或终极版)或Eclipse。

1.3 创建第一个Spring Boot项目

方式一:使用Spring Initializr
  1. 访问 https://start.spring.io/
  2. 选择项目类型(Maven/Gradle)
  3. 选择语言(Java/Kotlin/Groovy)
  4. 选择Spring Boot版本(推荐2.7.x或3.x)
  5. 填写项目信息(Group、Artifact)
  6. 添加依赖(Spring Web)
  7. 点击生成,下载项目压缩包
方式二:使用IDEA创建
  1. 打开IDEA,选择New Project
  2. 选择Spring Initializr
  3. 填写项目信息
  4. 选择依赖
  5. 完成创建

1.4 项目结构解析

demo
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           └── demo
│   │   │               ├── controller      # 控制器层
│   │   │               ├── service         # 服务层
│   │   │               ├── repository      # 数据访问层
│   │   │               ├── entity          # 实体类
│   │   │               ├── config          # 配置类
│   │   │               └── DemoApplication.java  # 主类
│   │   └── resources
│   │       ├── application.yml             # 配置文件
│   │       ├── static                      # 静态资源
│   │       └── templates                   # 模板文件
│   └── test
│       └── java
│           └── com
│               └── example
│                   └── demo
│                       └── DemoApplicationTests.java
└── pom.xml

主要文件说明:

  • DemoApplication.java:主类,程序入口
  • application.yml:配置文件(推荐使用yml格式)
  • pom.xml:Maven配置文件

1.5 编写第一个Controller

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @GetMapping("/hello")
    public String hello() {
        return "Hello, Spring Boot!";
    }
}

1.6 运行项目

  1. 直接运行主类的main方法
  2. 或使用Maven命令:mvn spring-boot:run
  3. 或打包后运行:mvn package && java -jar target/demo-0.0.1-SNAPSHOT.jar
  4. 访问 http://localhost:8080/hello

第二章:Spring Boot核心配置

2.1 配置文件

Spring Boot支持两种配置文件格式:

  • application.properties
  • application.yml(推荐)
properties格式示例
# 服务器端口
server.port=8080
# 应用名称
spring.application.name=demo
# 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
yml格式示例
server:
  port: 8080
spring:
  application:
    name: demo
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456

2.2 自定义配置

2.2.1 使用@Value注入
@RestController
public class MyController {

    @Value("${myapp.name}")
    private String appName;

    @Value("${myapp.description:默认描述}")
    private String description;

    @GetMapping("/info")
    public String info() {
        return appName + ": " + description;
    }
}
2.2.2 使用@ConfigurationProperties
@Component
@ConfigurationProperties(prefix = "myapp")
public class MyAppProperties {
    private String name;
    private String description;
    private int version;

    // getters and setters
}

2.3 多环境配置

创建不同环境的配置文件:

  • application-dev.yml(开发环境)
  • application-test.yml(测试环境)
  • application-prod.yml(生产环境)

在主配置文件中激活:

spring:
  profiles:
    active: dev

各环境配置示例:

# application-dev.yml
server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/dev_db
    username: root
    password: 123456
# application-prod.yml
server:
  port: 80
spring:
  datasource:
    url: jdbc:mysql://prod-host:3306/prod_db
    username: prod_user
    password: ${DB_PASSWORD}  # 使用环境变量

第三章:Spring Boot Web开发

3.1 RESTful API开发

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

    @Autowired
    private UserService userService;

    @GetMapping
    public Result<List<User>> getAllUsers() {
        return Result.success(userService.findAll());
    }

    @GetMapping("/{id}")
    public Result<User> getUserById(@PathVariable Long id) {
        return Result.success(userService.findById(id));
    }

    @PostMapping
    public Result<User> createUser(@Valid @RequestBody User user) {
        return Result.success(userService.save(user));
    }

    @PutMapping("/{id}")
    public Result<User> updateUser(@PathVariable Long id, @Valid @RequestBody User user) {
        user.setId(id);
        return Result.success(userService.save(user));
    }

    @DeleteMapping("/{id}")
    public Result<Void> deleteUser(@PathVariable Long id) {
        userService.deleteById(id);
        return Result.success();
    }
}

统一返回结果类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
    private Integer code;
    private String message;
    private T data;

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

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

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

3.2 参数校验

添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

使用校验注解:

@Data
public class User {
    @NotNull(message = "ID不能为空")
    private Long id;

    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
    private String username;

    @Email(message = "邮箱格式不正确")
    private String email;

    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;
}

3.3 全局异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {

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

    @ExceptionHandler(BusinessException.class)
    public Result<Void> handleBusinessException(BusinessException e) {
        return Result.error(e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public Result<Void> handleException(Exception e) {
        log.error("系统异常", e);
        return Result.error("系统异常,请联系管理员");
    }
}

自定义业务异常:

@Getter
public class BusinessException extends RuntimeException {
    private Integer code;

    public BusinessException(String message) {
        super(message);
        this.code = 500;
    }

    public BusinessException(Integer code, String message) {
        super(message);
        this.code = code;
    }
}

3.4 拦截器配置

@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        if (token == null || !validateToken(token)) {
            response.setStatus(401);
            response.getWriter().write("未登录或登录已过期");
            return false;
        }
        return true;
    }

    private boolean validateToken(String token) {
        // 验证token逻辑
        return true;
    }
}

注册拦截器:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/login", "/api/register");
    }
}

第四章:Spring Boot数据访问

4.1 集成MyBatis-Plus(推荐)

MyBatis-Plus是MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。

添加依赖
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
配置数据源
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.demo.entity
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
编写实体类
@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;

    private String username;

    private String email;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    @TableLogic
    private Integer deleted;
}
编写Mapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 自定义SQL方法
    @Select("SELECT * FROM user WHERE age > #{age}")
    List<User> selectByAgeGreaterThan(@Param("age") Integer age);
}
编写Service
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User findById(Long id) {
        return userMapper.selectById(id);
    }

    public List<User> findAll() {
        return userMapper.selectList(null);
    }

    public User save(User user) {
        if (user.getId() == null) {
            userMapper.insert(user);
        } else {
            userMapper.updateById(user);
        }
        return user;
    }

    public void deleteById(Long id) {
        userMapper.deleteById(id);
    }

    public List<User> findByCondition(String username, Integer minAge) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        if (username != null) {
            wrapper.like(User::getUsername, username);
        }
        if (minAge != null) {
            wrapper.ge(User::getAge, minAge);
        }
        wrapper.orderByDesc(User::getCreateTime);
        return userMapper.selectList(wrapper);
    }
}
自动填充配置
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

4.2 分页插件配置

@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

分页查询示例:

public Page<User> findByPage(int pageNum, int pageSize, String username) {
    Page<User> page = new Page<>(pageNum, pageSize);
    LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
    if (username != null) {
        wrapper.like(User::getUsername, username);
    }
    return userMapper.selectPage(page, wrapper);
}

4.3 Spring Data JPA(备选方案)

如果你更喜欢JPA风格的数据访问,可以使用Spring Data JPA。

添加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
配置JPA
spring:
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        dialect: org.hibernate.dialect.MySQL8Dialect
编写实体类
@Entity
@Table(name = "user")
@Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String email;
    private Integer age;

    @Column(name = "create_time")
    private LocalDateTime createTime;
}
编写Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // 方法名查询
    List<User> findByUsername(String username);
    Optional<User> findByEmail(String email);
    List<User> findByAgeGreaterThan(Integer age);

    // @Query查询
    @Query("SELECT u FROM User u WHERE u.username LIKE %:keyword% OR u.email LIKE %:keyword%")
    List<User> searchByKeyword(@Param("keyword") String keyword);
}

第五章:Spring Security安全认证

5.1 Spring Security基础配置

5.1.1 引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
5.1.2 JWT工具类
@Component
public class JwtTokenUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private Long expiration;

    public String generateToken(String username) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expiration);

        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(Keys.hmacShaKeyFor(secret.getBytes()), SignatureAlgorithm.HS256)
                .compact();
    }

    public String getUsernameFromToken(String token) {
        Claims claims = Jwts.parserBuilder()
                .setSigningKey(Keys.hmacShaKeyFor(secret.getBytes()))
                .build()
                .parseClaimsJws(token)
                .getBody();
        return claims.getSubject();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder()
                    .setSigningKey(Keys.hmacShaKeyFor(secret.getBytes()))
                    .build()
                    .parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
5.1.3 安全配置类
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .exceptionHandling()
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeHttpRequests()
                .requestMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated();

        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}
5.1.4 JWT认证过滤器
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String authHeader = request.getHeader("Authorization");

        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);
            String username = jwtTokenUtil.getUsernameFromToken(token);

            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);

                if (jwtTokenUtil.validateToken(token)) {
                    UsernamePasswordAuthenticationToken authentication =
                            new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }

        filterChain.doFilter(request, response);
    }
}
5.1.5 认证Controller
@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserService userService;

    @PostMapping("/login")
    public Result<Map<String, String>> login(@RequestBody LoginRequest request) {
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
        );

        SecurityContextHolder.getContext().setAuthentication(authentication);
        String token = jwtTokenUtil.generateToken(request.getUsername());

        Map<String, String> result = new HashMap<>();
        result.put("token", token);
        return Result.success(result);
    }

    @PostMapping("/register")
    public Result<Void> register(@RequestBody RegisterRequest request) {
        userService.register(request);
        return Result.success();
    }
}

第六章:Redis缓存集成

6.1 Redis基础配置

6.1.1 引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
6.1.2 配置文件
spring:
  redis:
    host: localhost
    port: 6379
    password:
    database: 0
    timeout: 3000ms
    lettuce:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0
6.1.3 Redis配置类
@Configuration
@EnableCaching
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(LaissezFaireSubTypeValidator.instance,
                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 CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .disableCachingNullValues();

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

6.2 缓存注解使用

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Cacheable(value = "user", key = "#id")
    public User findById(Long id) {
        System.out.println("从数据库查询,不是缓存");
        return userMapper.selectById(id);
    }

    @CachePut(value = "user", key = "#user.id")
    public User save(User user) {
        if (user.getId() == null) {
            userMapper.insert(user);
        } else {
            userMapper.updateById(user);
        }
        return user;
    }

    @CacheEvict(value = "user", key = "#id")
    public void deleteById(Long id) {
        userMapper.deleteById(id);
    }

    @CacheEvict(value = "user", allEntries = true)
    public void clearAllCache() {
    }
}

6.3 Redis分布式锁

@Service
public class DistributedLockService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private static final String LOCK_PREFIX = "lock:";
    private static final long DEFAULT_EXPIRE_TIME = 30;

    public boolean tryLock(String key, String value) {
        return Boolean.TRUE.equals(
                redisTemplate.opsForValue()
                        .setIfAbsent(LOCK_PREFIX + key, value,
                                DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS)
        );
    }

    public boolean releaseLock(String key, String value) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                "then return redis.call('del', KEYS[1]) " +
                "else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);
        return 1L.equals(redisTemplate.execute(
                redisScript, Collections.singletonList(LOCK_PREFIX + key), value)
        );
    }
}

第七章:Spring Boot核心原理

7.1 自动配置原理深度解析

Spring Boot的最大特点是"自动配置",其核心是@EnableAutoConfiguration注解。

7.1.1 @EnableAutoConfiguration的工作原理
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String[] exclude() default {};
    Class<?>[] excludeName() default {};
}

关键组件:

  1. AutoConfigurationImportSelector:负责加载自动配置类
  2. SpringFactoriesLoader:从META-INF/spring.factories加载配置
  3. Condition注解@ConditionalOnClass@ConditionalOnMissingBean
7.1.2 自动配置加载流程
启动Spring Boot应用
    ↓
@SpringBootApplication → @EnableAutoConfiguration
    ↓
AutoConfigurationImportSelector.selectImports()
    ↓
SpringFactoriesLoader.loadFactoryNames()
    ↓
读取 META-INF/spring.factories
    ↓
过滤自动配置类(@Conditional条件判断)
    ↓
加载符合条件的自动配置类
    ↓
创建Bean并注入Spring容器
7.1.3 自定义自动配置

创建自定义starter:

第一步:创建自动配置类

@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MyService myService(MyProperties properties) {
        return new MyService(properties);
    }
}

第二步:创建配置属性类

@ConfigurationProperties(prefix = "myapp")
public class MyProperties {
    private String name = "default";
    private int timeout = 3000;
    // getters and setters
}

第三步:创建spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.myautoconfig.MyAutoConfiguration

7.2 Spring Boot启动流程详解

7.2.1 SpringApplication.run()执行流程
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}
7.2.2 关键扩展点
扩展点 作用 示例
SpringApplicationRunListener 监听启动生命周期 自定义启动日志
ApplicationContextInitializer 刷新前初始化context 设置激活profile
ApplicationRunner 启动后执行 初始化数据
CommandLineRunner 启动后执行 接收命令行参数

第八章:生产环境最佳实践

8.1 Actuator监控端点

8.1.1 引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
8.1.2 配置暴露端点
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,env
  endpoint:
    health:
      show-details: always
8.1.3 常用端点
  • /actuator/health:健康检查
  • /actuator/info:应用信息
  • /actuator/metrics:指标信息
  • /actuator/env:环境变量

8.2 日志配置

8.2.1 logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <springProperty scope="context" name**
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐