SpringBoot启动分析

SpringBoot启动分析,第1张

SpringApplicationrun方法中,实例化一个SpringApplication对象,并调用该对象的run方法。

在SpringApplication构造函数中,主要完成了这样两件事:

在run()中主要完成如下几项工作:

在refreshContext方法中实现Ioc容器的初始化和Ioc的依赖注入。其中,在invokeBeanFactoryPostProcessors()方法中完成了IoC容器初始化过程的三个步骤。

第一步:Resource定位

在SpringBoot中,我们都知道他的包扫描是从主类所在的包开始扫描的,prepareContext()方法中,会先将主类解析成BeanDefinition,然后在refresh()方法的invokeBeanFactoryPostProcessors()方法中解析主类的BeanDefinition获取basePackage的路径。这样就完成了定位的过程。其次SpringBoot的各种starter是通过SPI扩展机制实现的自动装配,SpringBoot的自动装配同样也是在invokeBeanFactoryPostProcessors()方法中实现的。还有一种情况,在SpringBoot中有很多的@EnableXXX注解,细心点进去看的应该就知道其底层是@Import注解,在invokeBeanFactoryPostProcessors()方法中也实现了对该注解指定的配置类的定位加载。

常规的在SpringBoot中有三种实现定位,第一个是主类所在包的,第二个是SPI扩展机制实现的自动装配(比如各种starter),第三种就是@Import注解指定的类。

第二步:BeanDefinition的载入

在第一步中说了三种Resource的定位情况,定位后紧接着就是BeanDefinition的分别载入。所谓的载入就是通过上面的定位得到的basePackage,SpringBoot会将该路径拼接成:classpath :org/springframework/boot/demo/ / class这样的形式,然后一个叫做PathMatchingResourcePatternResolver的类会将该路径下所有的class文件都加载进来,然后遍历判断是不是有@Component注解,如果有的话,就是我们要装载的BeanDefinition。大致过程就是这样的了。

TIPS:@Configuration,@Controller,@Service等注解底层都是@Component注解,只不过包装了一层罢了。

第三步:注册BeanDefinition

这个过程通过调用上文提到的BeanDefinitionRegister接口的实现来完成。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过上文的分析,我们可以看到,在IoC容器中将BeanDefinition注入到一个ConcurrentHashMap中,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的。比如DefaultListableBeanFactory 中的beanDefinitionMap属性。

invokeBeanFactoryPostProcessors方法中主要通过ConfigurationClassPostProcessor完成IoC容器初始化的过程。

registerBeanPostProcessors方法中注册了AutowiredAnnotationBeanPostProcessor,registerBeanPostProcessors方法中对bean进行初始化时会调用AutowiredAnnotationBeanPostProcessor的接口实现@Autowired,完成Ioc的注入。

参考:

>

这篇先说明用法,下篇分析以下场景是如何将 Bean 注册进 IOC容器的。

这种用法在项目中是非常常见的,基本上是必有。我们来看下用法:

这样一个 Bean 就注册进 IOC 容器了,Bean 的名称默认是方法名,并且是不会转换大小写的,也就是假如你的方法名是 TestBean() ,那么 Bean 的名称就是 TestBean 。当然我们也可以使用 name 或者 value 指定 Bean 的名称,比如 @Bean(value = "testBean"),如果二者同时存在则会报错。

我们来看下其他属性:

autowireCandidate:默认值是 true 。如果设置为 false 的话,那么通过 byType 的方式获取 Bean 就会报错,当然我们可以使用 Resource 注解获取。

initMethod:在 Bean 实例化后调用的初始化方法,值是 Bean 类中的方法名。

destroyMethod:在 Bean 要销毁时调用的清理方法,值是 Bean 类中的方法名。

@Bean 注解只能定义在 @Configuration 类下吗? NO NO NO,它可以定义在任意能被 IOC 扫描的注解下,比如 @Component注解,至于区别,下篇再讲。

先讲普通用法:

深度用法:

ComponentScan 注解中有两个这样的属性:includeFilters 与 excludeFilters,前一个是只包含规则,后一个是排除包含规则,他们的值是一个 @Filter 注解的形式,Filter 中的 type 有 5 中类型,分别如下。

1、ANNOTATION

第一种是以注解的形式包含或不包含,比如:

这里边要配置useDefaultFilters = false 禁用默认规则,因为默认规则是扫描所有,配只包含就没用了。这里的意思只扫描 Configuration 注解。

2、ASSIGNABLE_TYPE

这种是包含我们给定的类型,不管是给定的类型和子类都会被包含进 IOC 容器。

然后我们发现 testBean 注册进去了,为什么我们不标注 @Component 这样的注解实例也会被注册进 IOC 呢?因为 ComponentScan 会扫描包下所有文件,只要符合我们定义的过滤规则,它就会将 Bean 注册进 IOC 容器中。

3、ASPECTJ

ASPECTJ 是使用 aspectj 表达式

4、REGEX

REGEX 是使用正则表达式

5、CUSTOM

这种呢就是我们 SpringBootApplication 注解用到的方式了,我来解释一下具体规则:这种方式是可以自己自定义扫描规则,它接受一个实现 TypeFilter 接口的类。

当它扫描类的时候扫描到了 TestBean,然后符合了我的匹配规则(也就是返回true)就注册进去了。

下面的例子中,我们直接看 Spring 源码的实现比较具有代表性一点。

我们点进 @EnableTransactionManagement 注解中,发现了这个 @Import(TransactionManagementConfigurationSelectorclass),它的作用就是将类导入,类会被注册进 IOC 容器中。

这个注解放置的位置要是 Spring 能扫描到的地方,不然 Spring 也不会主动去解析这个注解。

如果我们自己要使用注解的话,我们可以做个类似于 EnableTransactionManagement 的功能插拔式导入配置类,这样就可以实现动态开启一些 Bean 了。

我们还是来看下 TransactionManagementConfigurationSelector 这个类,看下它的继承关系发现它间接性的实现了 ImportSelector 接口,主要看它实现的这个方法:

这个方法的作用就是根据你返回的类全限定名(orgspringframeworkcontextannotationAutoProxyRegistrar)数组来创建 Bean 。

实现了 ImportSelector 的类也是需要使用 @Import 导入。

这个我们来看下 @MapperScan (orgmybatisspringannotation)导入的 MapperScannerRegistrar 发现它实现了 ImportBeanDefinitionRegistrar:

它的作用是拿到 BeanDefinitionRegistry Bean 的定义信息,然后往里面加 BeanDefinition 就会将相应的对象注册进去,它更深入的就不说了,实际上就是解析下注解属性,然后扫描相应的包下的类注册 Bean。我们自己搞个简单的。

这样就注册了一个 Bean 名称是 testBean 类型是 TestBean 类型的 Bean 了。

如果注册的是一个有参构造器呢?那就这样:

addConstructorArgValue 根据构造器参数的顺序去添加。

实现了 ImportBeanDefinitionRegistrar 的类也是需要使用 @Import 导入。

然后 TestBean 就注册进去了,打印的时候我们发现 Bean 的名称是 MyFactoryBean 的全限定名,但是它的类型是 TestBean 类型的,如果想要获取 MyFactoryBean 类型的 Bean 的话,通过 Bean 名称为 &myFactoryBean 就能获取到。

在我们的Spring Boot项目中,一般都是只扫描主类下的所有类,然后将一些被特定注解标注的类加载到IOC容器,但是如果我们将包分离,我们又如何更加方便的将其他包的类加载进来呢? spring boot提供了一种类似于Java的SPI(服务发现)机制springfactories,只要在resources目录下创建META-INF文件夹,再创建 springfactories文件,然后再里面配置

这样在导入当前包的就会自动扫描springfactories文件,解析后将里面的一些类加载到IOC容器中。具体的实现代码在spring-core的SpringFactoriesLoader类中。

这些就不讲了。

1场景描述

业务系统中,配置文件不同,使用的业务逻辑也不同。我们可以使用模板方法模式把业务分成多个service。如下图

2ConditionalOnProperty源码说明

3如何使用

AbstractServiceImpl1-- 默认使用的方法

UserServiceImpl1--

UserServiceImpl2--

使用时,直接注入service,会根据配置文件来选择哪个service生效

4上面是针对一个配置项,如果有多个配置项,可以使用@ConditionalOnExpression,来根据表达式来选择使用哪个service

ConditionalOnExpression 源码说明

使用起来也比较方便

@ConditionalOnExpression("!${sencecascadeenable:false} && ${sencecascadetest:0} == 2")

以上就是关于SpringBoot启动分析全部的内容,包括:SpringBoot启动分析、Springboot使用Jpa报错找不到bean的问题、Spring 优雅注册 Bean 的方式等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/9306339.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-27
下一篇2023-04-27

发表评论

登录后才能评论

评论列表(0条)

    保存