Spring boot 学习笔记

0 阅读7分钟

自动配置

  1. condition

condition的match方法可以实现选择性的创建Bean操作

需求:

在Spring的IOC容器中有一个User的Bean,要求:

  1. 导入jedis坐标后,加载Bean,没导入,则不加载

需求实现:

@Configuration
public class UserConfig {

    @Bean
    @Conditional(ClassCondition.class)
    public User user(){
        return new User();
    }
}
public class ClassCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //1.需求:导入Jedis坐标后创建Bean
        //思路:判断redis.clients.jedis.Jedis.class文件是否存在
        boolean flag = true;
        try {
            Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
        } catch (ClassNotFoundException e) {
            flag = false;
        }
        return flag;
    }
}
//Maven 帮你下载 jedis 的 jar 包,并加入到项目的 classpath 中,所以 JVM 才能加载到 Jedis 类,你才能在代码里使用它。
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

实现对应的类来实现condition接口,相当于是条件,里面有一个方法,这个方法决定了是否创建对应的Bean

  1. 将类的判断定义为动态的,判断哪个字节码文件存在可以动态指定

需求实现:

@Configuration
public class UserConfig {

@Bean
//@Conditional(ClassCondition.class)
@ConditionalOnClass("redis.clients.jedis.Jedis")
    public User user(){
        return new User();
    }
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
    String[] value();
}
public class ClassCondition implements Condition {
//contest:上下文对象,用于获取环境,IOC容器,classpath对象
//metadata:注解元对象,可以用于获取注解定义的属性值
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //获取注解属性值 value
Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
//System.out.println(map);
String[] value = (String[]) map.get("value");

boolean flag = true;
try {
    for (String className : value) {
        Class<?> cls = Class.forName(className);
    }
} catch (ClassNotFoundException e) {
    flag = false;
}

return flag;
    }
}

在用户配置类使用自定义的conditiononclass注解,然后在注解上配置conditional注解,并且修改conditional注解实现类的逻辑

在springboot里提供了很多ConditionOnxxx的接口

在springboo-autoconfigure的jar包里

@ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化 Bean

@ConditionalOnClass:判断环境中是否有对应字节码文件才初始化 Bean

@ConditionalOnMissingBean:判断环境中没有对应 Bean 才初始化 Bean

  1. Enable*注解

springboot提供了很多Enable开头的注解,这些注解都是用于动态启动某些功能的,底层原理上使用import注解导入一些配置类,实现Bean的动态加载

举例:

使用第三方jar包的Bean

在类上添加EnableUser,获取到对应的Bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)
public @interface EnableUser {
}

springboot告诉大家要写一个enable开头的注解去标记你要初始化的Bean,springboot初始化的时候就会去找这些标记

  1. import注解
  • 使用import导入的类会被spring加载到IOC容器中,有四种用法

    • 导入bean

    • 导入配置类

    • 导入importSelector实现类,一般用于加载配置文件中的类

    • 导入importBeanDefinitionRegistrar

idea的全局搜索某个类的快捷键:shift连续按两次(mac)

  • importSelector实现类
在启动类上添加
@import(MyImportSelector.class
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.xx.domain.User", "com.xx.domain.Role"};
    }
}
public interface ImportSelector {

    /**
     * Select and return the names of which class(es) should be imported based on
     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
     */
     //动态返回需要被 Spring 容器加载并注册的类的全限定名数组。
    String[] selectImports(AnnotationMetadata importingClassMetadata);
}

4. enableautoconfiguration

步骤

  • @EnableAutoConfiguration 注解内部使用 @Import(AutoConfigurationImportSelector.class) 来加载配置类。

  • 配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载这些配置类,初始化 Bean。

  • 并不是所有的 Bean 都会被初始化,在配置类中使用 Condition 来加载满足条件的 Bean。

监听机制

  1. 事件监听

springboot在项目启动的时候,会对几个监听器进行回调,我们可以实现这些监听器接口,在项目启动时完成一些操作

  • ApplicationContextInitializer:在 Spring 容器刷新之前执行,用于对上下文做初始化配置。

    •   ```java
        public class MyApplicationContextInitializer implements ApplicationContextInitializer {
            @Override
            public void initialize(ConfigurableApplicationContext applicationContext) {
                System.out.println("ApplicationContextInitializer....initialized");
            }
        }
        ```
      

作用:在项目还没有准备IOC容器之前,去检测资源是否存在

举例:

一、先理清核心概念:IOC 容器的 “准备阶段” vs “就绪阶段”

Spring Boot 启动流程中,IOC 容器(ApplicationContext)的生命周期可以简单分为两步:

容器准备阶段:只是创建了 ConfigurableApplicationContext 对象,但还没做任何核心初始化(比如扫描 Bean、加载配置、创建 Bean 实例);

容器就绪阶段:执行 refresh() 方法,完成 Bean 扫描、配置加载、依赖注入、Bean 初始化等核心操作,此时 IOC 容器才算真正可用。

ApplicationContextInitializerinitialize() 方法,就执行在容器准备阶段(创建后)、refresh () 执行前—— 也就是 “IOC 容器还没准备好” 的关键节点。

二、“检测资源是否存在” 的通俗理解

这句话的核心是:在 IOC 容器正式干活(加载 Bean、初始化组件)之前,先做 “前置检查”,避免容器启动到一半因为关键资源缺失而失败,典型场景比如:

public class CheckResourceInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        // 检测核心配置文件是否存在
        Resource resource = applicationContext.getResource("classpath:application-db.yml");
        if (!resource.exists()) {
            // 提前抛异常,终止启动,避免后续无效加载
            throw new RuntimeException("核心配置文件 application-db.yml 不存在!");
        }
        System.out.println("核心配置文件检测通过");
    }
}
  • SpringApplicationRunListener:监听 Spring Boot 应用启动的整个生命周期,在不同阶段触发回调。

    •   import org.springframework.boot.SpringApplicationRunListener;
        import org.springframework.context.ConfigurableApplicationContext;
        import org.springframework.core.env.ConfigurableEnvironment;
      
        public class MySpringApplicationRunListener implements SpringApplicationRunListener {
            public MySpringApplicationRunListener(SpringApplication application, String[]  args) {
            }
      
            @Override
            public void starting() {
                System.out.println("starting...项目启动中");
            }
      
            @Override
            public void environmentPrepared(ConfigurableEnvironment environment) {
                System.out.println("environmentPrepared...环境对象开始准备");
            }
      
            @Override
            public void contextPrepared(ConfigurableApplicationContext context) {
                System.out.println("contextPrepared...上下文对象开始准备");
            }
      
            @Override
            public void contextLoaded(ConfigurableApplicationContext context) {
                System.out.println("contextLoaded...上下文对象开始加载");
            }
      
            @Override
            public void started(ConfigurableApplicationContext context) {
                System.out.println("started...上下文对象刷新完成,应用启动完成");
            }
      
            @Override
            public void running(ConfigurableApplicationContext context) {
                System.out.println("running...应用运行中,可接收请求");
            }
      
            @Override
            public void failed(ConfigurableApplicationContext context, Throwable exception) {
                System.out.println("failed...应用启动失败,异常信息:" + exception.getMessage());
            }
        }
      
  • CommandLineRunnerApplicationRunner:在 Spring Boot 应用启动完成后执行,用于执行一些初始化任务,区别在于接收的参数类型不同。

作用:可以在spring启动的时候做缓存

启动流程分析

spring创建bean流程:

  1. JVM:new 对象 → 字段初始化 → 构造器体
  2. Spring:构造器结束后 → 再做 @Autowired 注入

你的问题就在于:在「字段初始化」这一步就访问了 @Autowired 字段,而这时 Spring 还没来得及给它注入,仍然是 null。

注解

Resource

概念:把spring容器里的对象注入到当前类中

使用方法:在属性上

@Resource
private UserService userService;

Component

概念:这个类是组件,spring可以创建对象并且管理它

使用方法:在类上

@Component
public class xx{}

controller层常用注解

RestController

概念:@Controller + @ResponseBody 的结合体,专门用于标识RESTful 风格的控制器,主要作用是处理 HTTP 请求并直接返回数据(如 JSON/XML),而非跳转页面。

RequestMapping

@RequestMapping 是 Spring MVC 中最基础、最核心的请求映射注解,作用是将特定的 HTTP 请求( URL 、请求方法、参数等)绑定到控制器的某个方法上,让 Spring 知道哪个方法来处理哪个请求。

GetMapping

限定仅处理 GET 请求的简化请求映射注解

RequestBody

@RequestBody 是 Spring MVC 中用于接收 HTTP 请求体中的数据(如 JSON/XML),并自动将其转换为对应的 Java 对象的注解

Valid

在参数上执行校验

Lazy

@Lazy 是 Spring 里一个控制 Bean 创建时机的注解。

不用的时候不创建,用到的时候才创建。解决循环依赖的问题

Lombok注解

两者均为Lombok注解,自动生成构造方法,简化代码:

  1. @NoArgsConstructor:自动生成无参构造方法(无任何参数)。

  2. @RequiredArgsConstructor

    1. 只为 final 字段和 @NonNull 标注的字段生成构造函数

    2. 其他字段不会被包含在构造函数中

      • 适合用于依赖注入场景
  3. @AllArgsConstructor

    1. 为所有字段生成构造函数

    2. 不管字段是否有 final 修饰符

    3. 包含类中的所有实例字段

Builder注解

提供构建者模式的支持

HttpServletRequest 是封装 HTTP 请求所有信息的核心接口(由 Web 容器实现),能获取请求行、参数、请求头、Cookie 等所有客户端请求数据;