springBoot启动注解解析,,,,,,,,

本文深入解析Spring Boot启动过程,详细阐述@SpringBootApplication注解的作用,包括自动配置、组件扫描等核心功能,以及如何通过自定义注解实现组件的动态注册。

首先我们知道启动类的注解SpringBootApplication
点进去后会看到boot 的核心三大注解
在这里插入图片描述
java自定义注解比较简单
https://blog.youkuaiyun.com/crazyo2jam/article/details/103486320
三大注解

@SpringBootConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

其内部我们可以看出主要以@Configuration作为主体,也就是声明为spring容器注入组件的意思

@EnableAutoConfiguration

最最最核心的"自动装配"注解 我们点进去

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

我们可以看到
@AutoConfigurationPackage.
@Import({AutoConfigurationImportSelector.class})// 导入组件
在内部有一个方法selectImports返回是一个数组,通过全类名+反射的机制实例化纳入到spring容器中使用,
在这里插入图片描述
我们在点入this.getAutoConfigurationEntry看一下在这里插入图片描述
加载工厂名称
在这里插入图片描述
加载spring工厂
在这里插入图片描述
自动加载文件
在这里插入图片描述

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

值是map形式的kv值,会自动装配配置内的类,以String数组的形式返回,通过全类名+反射的方式进行实例化
在这里插入图片描述
那么每个类都要去装配吗???
我们点进一个类看一下这个装配的类具有一定的装配条件
在这里插入图片描述

我们在application.properties 写入debug=true
我们会看到

Positive matches:
-----------------     已经加载装配的

   AopAutoConfiguration matched:
      - @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
      - 一堆就不一一写了
Negative matches:
-----------------    未符合要求未装配的

   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
         -       - 一堆就不一一写了

ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class ‘javax.jms.ConnectionFactory’ (OnClassCondition)

@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)
@ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean),该注解的参数对应的类必须存在,否则不解析该注解修饰的配置类;
@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)
@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean), 该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean;可以给该注解传入参数例如@ConditionOnMissingBean(name = “example”),这个表示如果name为“example”的bean存在,这该注解修饰的代码块不执行。
@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)
@ConditionalOnNotWebApplication(不是web应用)
@ConditionalOnProperty是指在application.yml里配置的属性是否为true,其他的几个都是对class的判断

我们再来看@AutoConfigurationPackage内有一个@Import({Registrar.class})

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}

@Import({Registrar.class}) 点入注册类
这是批量的讲类注册为bean的实现方发因为实现了ImportBeanDefinitionRegistrar接口

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
        }

那么是怎么回事那,源码中太乱我们自己简单的测试下
新建一个实体类student 实现类MyImportBean
1.实体类student 实现类MyImportBean实现ImportBeanDefinitionRegistrar
2.重写ImportBeanDefinitionRegistrar的两个方法

@Component   //声明是个组件类
public class MyImportBean implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        //BeanDefinitionRegistry   bean的定义注册   可以是现在运行期间动态绑定到容器中
        //通过BeanDefinition构造出Student 的定义
        BeanDefinition beanDefinition= BeanDefinitionBuilder.rootBeanDefinition(Student.class).getBeanDefinition();
        //bean 有个 id 和class类型   参1就是bean的id 参2是注入的哪个bean
        for(int i=0;i<10;i++){//生成10个bean
            registry.registerBeanDefinition("stu"+i,beanDefinition);
            System.out.println("生成="+i);
        }
    }
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    //一定要写在上一个方法里(3个参数的)
    }
}

3在resources目录下新建META-INF目录创建文件spring.factories
(想当于在执行application.yml前执行spring.factories,也同等于了springboot注解内的功能)

org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.demo.config.MyImportBean

4启动类

@SpringBootApplication
public class SpringbootApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run=SpringApplication.run(SpringbootApplication.class, args);
        System.out.println(run.getBeansOfType(Student.class));
    }
}

打印得出

生成=0
生成=1
生成=2
生成=3
生成=4
生成=5
生成=6
生成=7
生成=8
生成=9
2019-12-11 11:17:31.326  INFO 16352 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-12-11 11:17:31.333  INFO 16352 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-12-11 11:17:31.334  INFO 16352 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.29]
2019-12-11 11:17:31.407  INFO 16352 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-12-11 11:17:31.407  INFO 16352 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 820 ms
2019-12-11 11:17:31.531  INFO 16352 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-12-11 11:17:31.653  INFO 16352 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-12-11 11:17:31.655  INFO 16352 --- [           main] cn.demo.SpringbootApplication            : Started SpringbootApplication in 1.461 seconds (JVM running for 2.96)
{stu0=cn.demo.pojo.Student@61a1ea2c, stu1=cn.demo.pojo.Student@149debbb, stu2=cn.demo.pojo.Student@25cd49a4, stu3=cn.demo.pojo.Student@5477a1ca, stu4=cn.demo.pojo.Student@3ae9d1e2, stu5=cn.demo.pojo.Student@41522537,
 stu6=cn.demo.pojo.Student@e9dc4d0, stu7=cn.demo.pojo.Student@670d4d38, stu8=cn.demo.pojo.Student@131ff6fa, stu9=cn.demo.pojo.Student@43b40233}

方法2 通过全类型注册

@Component   //声明是个组件类
public class MyImportBean2 implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"cn.demo.pojo.Student","cn.demo.pojo,Root"};
    }
}

配置文件----
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.demo.config.MyImportBean,cn.demo.\
  config.MyImportBean

##大致总结一下
AutoConfigurationImportSelector是导入组件选择器
讲所有需要导入的组件以全类名的方式返回;组件被添加到容器中;会给容器中导入非常多的自动配置类(XXXAutoConfiguration)
springboot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将值作为自动导入容器中,整合方案都在spring-boot-autoConfiguration-2.1.6,jar中

@ComponentScan(
扫描类的注解,根据注解进行装配

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值