SpringBoot——启动流程、自动配置、接口、注解、生命周期

SpringBoot启动流程(简化)

    public static void run(Class<?> primaryClass) {
		// 1. 创建一个ApplicationContext实例,即我们常说的IoC容器
        ApplicationContext context = createApplicationContext();
		// 2. 将主类(primaryClass)注册到IoC容器中(简单但重要的第一-步)
        loadSourceClass(context,primaryClass);
		// 3. 递归加载并处理所有的配置类
        processConfigurationClasses(context);
		// 4. 实例化所有的单例Bean(Singleton Bean)
        instantiateSingletonBeans(context);
		// 5. 如果是web应用,则启动web服务器(例如Tomcat)
        startWebServer(context);
    }

在这里插入图片描述

在这里插入图片描述

上述步骤中递归处理的流程

在这里插入图片描述

SpringBoot自动配置(浅析)

@SpringBootApplication:继承自@Configuration,支持JavaConfig的方式进行配置

  • @EnableAutoConfiguration:开启自动配置
    • @AutoConfigurationPackage
    • @Import(AutoConfigurationImportSelector.class):返回需要导入的组件的全类名的数组
  • @ComponentScan:自动扫描组件,默认扫描该类所在包及其子包下所有带有指定注解的类,将它们自动装配到bean容器中,会被自动装配的注解包括@Controller、@Service、@Component、@Repository等。也可以指定扫描路径

@Import注解,通过AutoConfigurationImportSelector类中的selectImports()方法批量注入

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

查看getAutoConfigurationEntry()方法,可以查看到所有的配置信息都是通过该方法获得

	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        // 检查自动配置功能是否开启,默认开启
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
        // 加载自动配置的元信息
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
        // 获取候选配置类
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        // 去掉重复的配置类
		configurations = removeDuplicates(configurations);
        // 获得注解中被exclude和excludeName排除的类的集合
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        // 检查被排除类是否可实例化、是否被自动注册配置所使用,不符合条件则抛出异常
		checkExcludedClasses(configurations, exclusions);
        // 从候选配置类中去除掉被排除的类
		configurations.removeAll(exclusions);
        // 过滤
		configurations = getConfigurationClassFilter().filter(configurations);
        // 将配置类和排除类通过事件传入到监听器中
		fireAutoConfigurationImportEvents(configurations, exclusions);
        // 最终返回符合条件的自动配置类的全限定名数组
		return new AutoConfigurationEntry(configurations, exclusions);
	}

查看getCandidateConfigurations()方法得知如何获取到候选的配置类

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}
	
	protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

继续进入loadFactoryNames()方法,可以发现一个获取资源的地址:FACTORIES_RESOURCE_LOCATION,值为META-INF/spring.factories

在这里插入图片描述
在这里插入图片描述

下面是spring.factories文件的内容格式,根据它我们可以清晰地了解Map<String,List<String>>中都存了些什么。其中Key值为:org.springframework.boot.autoconfigure.EnableAutoConfiguration,Value值为后面的各种XXXAutoConfiguration
在这里插入图片描述

综上所述:getCandidateConfigurations()方法通过SpringFactoriesLoader加载器加载META-INF/spring.factories文件,首先通过这个文件获取到每个配置类的url,再通过这些url将它们封装成Properties对象,最后解析内容存于Map<String,List<String>>

备注:2.7版本之后,自动配置目录有所调整

目录调整为:

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

每一行代表一个自动配置

但是为了向前兼容,仍然保留了META-INF/spring.factories文件

在这里插入图片描述

SpringBoot接口

ApplicationRunner

SpringBoot项目启动时,若想在启动之后直接执行某一段代码,就可以实现ApplicationRunner这个接口,并实现接口里面的run()方法,方法中写上自己的想要的代码逻辑

如果有多段代码需要执行,可使用@Order注解指定执行顺序

@Component
@Order(value = 1)
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("SpringBoot项目启动后执行的代码1......");
    }
}
@Component
@Order(value = 2)
public class MyApplicationRunner2 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("SpringBoot项目启动后执行的代码2......");
    }
}

FactoryBean

FactoryBean:工厂Bean,交给spring用来生产Bean到spring容器中,可以通过前缀&来获取工厂Bean本身。

public class Color {
}
public class ColorFactoryBean implements FactoryBean<Color> {

    /**
     * @return 返回一个Color对象,这个对象会添加到容器中
     * @throws Exception 异常
     */
    @Override
    public Color getObject() throws Exception {
        System.out.println("ColorFactoryBean......");
        return new Color();
    }

    @Override
    public Class<?> getObjectType() {
        return Color.class;
    }

    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
    
}
@Configuration
public class FactoryBeanConfig {

    @Bean
    public ColorFactoryBean colorFactoryBean() {
        return new ColorFactoryBean();
    }
    
}
public class FactoryBeanTest {

    @Test
    public void test() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(FactoryBeanConfig.class);
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        Arrays.stream(beanDefinitionNames).forEach(System.out::println);

        //工程Bean获取的是调用getObject创建的对象
        Object colorFactoryBean = context.getBean("colorFactoryBean");
        System.out.println("bean的类型:"+colorFactoryBean.getClass());

        //要获取工厂Bean本身,我们要在给id前面加一个&
        Object colorFactoryBean1 = context.getBean("&colorFactoryBean");
        System.out.println("bean的类型:"+colorFactoryBean1.getClass());
    }
}

// 结果如下:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
factoryBeanConfig
colorFactoryBean
ColorFactoryBean......
bean的类型:class cn.akangaroo.test.factoryBean.entity.Color
bean的类型:class cn.akangaroo.test.factoryBean.ColorFactoryBean

SpringBoot注解

自动装配注解

@Autowired:自动注入

  • 按照类型去容器中找对应的组件

  • 按照属性名称去作为组件id去找对应的组件

@Qualifier:指定默认的组件,结合@Autowried使用

@Primary:Spring进行自动装配的时候,默认使用首选的Bean。也可以使用@Qualifier来指定需要装配的Bean的名字。

@Resource(JSR250) :可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配

@Inject:和@Autowired的功能一样,但是比@Autowired少一个required=false的功能

@Configuration

@Configuration:标识为配置类

@Bean

@Bean:默认的beanId是函数名,可以用value属性进行指定,放在@Component或者@Configuration修饰的类中

@Scope

@Scope:作用域 prototype(多例) singleton(单例)

@Lazy

@Lazy:懒加载,单例Bean:默认在容器启动的时候创建对象,容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化

@Conditional

@Conditional() 按照一定的条件进行判断,满足条件给容器中注册Bean,传入Condition数组,使用时需自己创建类继承Condition然后重写match()方法

注解含义
@ConditionalOnBean在容器中有指定Bean的条件下
@ConditionalOnMissingBean在容器中没有指定Bean的条件下
@ConditionalOnClass在classpath类路径下有指定类的条件下
@ConditionalOnMissingClass在classpath类路径下没有指定类的条件下
@ConditionalOnResource类路径是否有指定的值
@ConditionalOnWebApplication在项目是一个Web项目的条件下
@ConditionalOnProperty在指定的属性有指定的值条件下

自定义一个Condition

  1. 定义一个HelloCondition,默认返回true

    public class HelloCondition implements Condition {
        /**
         * @param conditionContext 判断条件能使用的上下文(环境)
         * @param annotatedTypeMetadata 注解信息
         * @return
     	*/
        @Override
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
            return true;
        }
    }
    
  2. 定义一个实体类

    @Data
    public class Dog {
        private String name ;
        private Integer age;
    }
    
  3. 配置类中注入Dog对象,并且指定加载条件

    @Configuration
    @Conditional(HelloCondition.class)
    public class HelloConfig {
        @Bean
        public Dog dog() {
            Dog dog = new Dog();
            dog.setName("woof");
            dog.setAge(12);
            return dog;
        }
    }
    
  4. 测试(可将HelloCondition中的matach()方法的返回值改为false)

    public class Test {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext applicationContext =
                    new AnnotationConfigApplicationContext(HelloConfig.class);
            Map<String, Dog> beansOfType = applicationContext.getBeansOfType(Dog.class);
            System.out.println(beansOfType);
        }
    }
    
    // 结果如下
    {dog=Dog(name=woof, age=12)}
    

@RquestMapping

@RquestMapping属性

  • name:相当于方法的注释,使方法更易理解。

  • value & path:指定请求的实际地址,支持通配符匹配。

  • method:指定请求的method类型(GET,POST,PUT,DELETE)等。

  • consumes:指定处理请求的提交内容类型(Context-Type)。

  • produces:指定返回的内容类型,还可以设置返回值的字符编码。

  • params:指定request中必须包含某些参数值,才让该方法处理。

  • headers:指定request中必须包含某些指定的header值,才让该方法处理请求。

@RestController
public class TestController {

    @RequestMapping(name = "测试",
            value = "/test",
            method = RequestMethod.POST,
            // flag必须指定为true,content任意值
            params = {"flag=true", "content"},
            // 自定义请求头一般以"X-"开头
            headers = "X-Custom-Header=MyCustom",
            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    public Dict test() {
        return Dict.create().set("who", "akangaroo");
    }
}

@Value

基本数值:@Value("张三")

SpEl表达式: @Value(#{1 + 2})

配置文件:@Value(${os.name})

@PropertySource

使用@PropertySource指定读取的配置文件路径

@Component
@PropertySource({"classpath:config/custom.properties"}) // 指定配置文件位置
@ConfigurationProperties(prefix = "my")
@Data
public class MyCustomProperties {
    private String name;
    private String sex;
}

resources目录下新建config文件夹,新建custom.properties文件

my.name=akangaroo
my.sex=male

@Import

@Import它的作用是提供了一种显式地从其它地方加载配置类的方式,这样可以避免使用性能较差的组件扫描(@ComponentScan)

支持导入:

  • 普通类

  • 接口ImportSelector的实现类:返回需要导入的组件的全类名的数组SpringBoot自动配置的实现

  • 接口ImportBeanDefinitionRegistrar实现类:手动注册Bean

导入普通类

  1. 首先创建一个ConfigA配置类和A类,ConfigA不使用注解注入

    public class ConfigA {
    
        @Bean
        public A a() {
            return new A();
        }
    }
    
    class A {
        
    }
    
  2. 创建一个配置类ConfigB,将上面的ConfigA导入

    @Configuration
    @Import(ConfigA.class)
    public class ConfigB {
        
    }
    
  3. 测试

    public class Test {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigB.class);
            ConfigA configA = context.getBean(ConfigA.class);
            A a = context.getBean(A.class);
            System.out.println(configA.getClass().getSimpleName());
            System.out.println(a.getClass().getSimpleName());
        }
    }
    
    // 结果如下
    ConfigA
    A
    

导入选择器ImportSelector

可以通过@Import导入一个接口ImportSelector实现类。接口ImportSelector中有个selectlmports 方法,它的返回值是一个字符串数组,数组中的每个元素分别代表一个将被导入的配置类的全限定名。

  1. 创建两个类,不加@Component相关的注解

    public class Blue {
    }
    
    public class Red {
    }
    
  2. 创建一个类实现ImportSelector接口

    public class MyImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"cn.akangaroo.test.importAnnotation.importSelector.Red",
        "cn.akangaroo.test.importAnnotation.importSelector.Blue"};
        }
    }
    
  3. 使用@Import导入ImportSelector的实现类

    @Configuration
    @Import({MyImportSelector.class})
    public class ImportTestConfig {
    }
    
  4. 测试

    public class ImportAnnotationTest {
    
        @Test
        public void test() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ImportTestConfig.class);
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        Arrays.stream(beanDefinitionNames).forEach(System.out::println);
        }
    }
    
    // 结果如下:
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    importTestConfig
    cn.akangaroo.test.importAnnotation.importSelector.Red
    cn.akangaroo.test.importAnnotation.importSelector.Blue
    

导入注册器ImportBeanDefinitionRegistrar

可以通过@lmport导入一个接口ImportBeanDefinitionRegistrar的实现类。通过它,我们可以手动将多个BeanDefinition注册到IOC容器中,从而实现个性化的定制。

  1. 创建普通类Dog

    public class Dog {
    }
    
  2. 创建AnimalBeanDefinitionRegistrar类实现ImportBeanDefinitionRegistrar接口

    public class AnimalBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClass(Dog.class);
            registry.registerBeanDefinition("dog1", beanDefinition);
        }
        
    }
    
  3. 创建配置类ConfigC导入AnimalBeanDefinitionRegistrar

    @Configuration
    @Import({AnimalBeanDefinitionRegistrar.class})
    public class ConfigC {
    }
    
  4. 测试

    public class Test {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigC.class);
            Dog dog1 = context.getBean("dog1", Dog.class);
            System.out.println(dog1.getClass().getSimpleName());
        }
    }
    
    // 结果如下
    Dog
    

Bean生命周期

创建 -> 初始化 -> 销毁

创建

单实例:在容器启动的时候创建对象

多实例:在每次获取的时候创建对象

初始化

对象创建完成,并赋值好,调用初始化方法

销毁

单实例:容器关闭的时候销毁Bean

多实例:容器不会管你这个Bean;容器不会调用销毁方法。需要手动销毁

初始化和销毁的三种方式

  1. @Bean注解的initMethoddestroyMethod两个属性配置bean的初始化和销毁方法

  2. InitializingBean接口的afterPropertiesSet()实现初始化逻辑;DispoableBean接口的destroy()实现销毁逻辑;

  3. @PostConstrct,标注在方法上,对象执行构造行数,创建并赋值之后执行;@PreDestroy,标注在方法上,在容器移除对象之前执行

初始化顺序:constructor -> postConstruct -> InitializingBean-afterPropertiesSet() -> initMethod

销毁顺序:preDestroy -> destroy -> destroyMethod

public class Car implements InitializingBean, DisposableBean {
    public Car() {
        System.out.println("car...constructor");
    }

    public void initMethod() {
        System.out.println("car...initMethod");
    }

    public void destroyMethod() {
        System.out.println("car...destroyMethod");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("car...InitializingBean-afterPropertiesSet");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("car...destroy");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("car...postConstruct");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("car...preDestroy");
    }
}
@Configuration
public class Config {
    @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
    public Car car() {
        return new Car();
    }
}
public class CycleOfLifeTest {

    @Test
    public void test() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        context.getBean(Car.class);
        context.close();
    }

}

// 结果如下:
car...constructor
car...postConstruct
car...InitializingBean-afterPropertiesSet
car...initMethod
00:36:07.585 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4ae3c1cd, started on Thu Dec 01 00:36:07 CST 2022
car...preDestroy
car...destroy
car...destroyMethod

BeanPostProcessor接口

/**
 * 后置处理器:Bean的后置处理器,在Bean的初始化前后进行处理。
 *
 * @author : kangyw41748
 * @since : 2022/12/4 16:03
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    /**
     * Bean构造方法执行之后、初始化方法执行之前处理
     * @param bean bean the new bean instance
     * @param beanName beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization------" + beanName);
        return bean;
    }

    /**
     * Bean初始化之后处理
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization------" + beanName);
        return bean;
    }
}

config类上加上@ComponentScan注解注入到容器进行测试

@Configuration
@ComponentScan("cn.akangaroo.test.bean")
public class Config {
    @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
    public Car car() {
        return new Car();
    }
}
public class CycleOfLifeTest {

    @Test
    public void test() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        context.getBean(Car.class);
    }

}

// 结果如下:
16:30:22.651 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
postProcessBeforeInitialization------config
postProcessAfterInitialization------config
16:30:22.651 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'car'
car...constructor
postProcessBeforeInitialization------car
car...postConstruct
car...InitializingBean-afterPropertiesSet
car...initMethod
postProcessAfterInitialization------car

执行过程:

AbstractAutowireCapableBeanFactory.doCreateBean中调用popolulateBean为bean填充属性,然后调用initializeBean开始进行bean的初始化以及bean初始化的前后处理。

  • AbstractAutowireCapableBeanFactory.doCreateBean调用initializeBean方法:
    1. 该方法会先调用applyBeanPostProcessorsBeforeInitialization(warppedBean, beanName);初始化前前置处理
    2. 然后调用invokeInitMethods对bean进行初始化
    3. 最后调用applyBeanPostProcessorsAfterInitialization,初始化后置处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值