@springBootConfiguration
主配置类的注解,将标注类所在包及其子包下所有的组件扫描到spring容器
如果想改变扫描路径:scanBasePackages=“com.lun”
@ConditionalOnMissingBean(name="tom")
没有tom名字的Bean时,Bean才能生效
@ImportResource
引入配置文件
@ConfigurationProperties
@ConfigurationProperties + @Component
只有在容器中的组件,功能才生效
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String branch;
private String price;
}
# application.yaml
server:
port: 8888
mycar:
branch: "雅阁"
price: "20w"
@RestController
public class myController {
@Autowired
Car car;
@GetMapping("/car")
public Car getMyCar(){
return car;
}
}
@EnableConfigurationProperties
@EnableConfigurationProperties(Car.class) + @ConfigurationProperties
开启car配置的绑定功能;将car添加到spring容器(所以使用时,需要将car注入使用类中-构造器注入或者set注入)
@EnableConfigurationProperties(Car.class)
public class MyConfig {
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String branch;
private String price;
}
@ConditionalOnProperty
通过配置文件的属性值来判定configuration/bean是否被注入
内部实现:@Conditionl(OnPropertyCondition.class)
拓展 - SpringBoot读取配置的其他方式
1、通过Environment注入获取配置
2、通过@PropertySource(value = "db/properties")读取配置文件
@AutoConfigureAfter(A.class)
表示该自动配置类需要在自动配置类A配置完成之后
@Import
注入bean
1、引入其他@Configureration+@Bean,优先于本身的类定义加载
2、指定其他普通的类,优先于本身的类定义加载
3、指定实现ImportSelector,优先于本身的类定义加载
4、指定实现DeferredImportSelector,这样selectImports方法返回的类是最后加载
5、
参考博文: Spring全解系列 - @Import注解 - 知乎
@SpringBootConfiguration
@AutoConfigurationPackage:让包中的类以及子包中的类能够被自动扫描到spring容器中。其实就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。因此我们要把DemoApplication放在项目的最高级中(最外层目录)。@SpringBootApplication(scanBasePackages = {""}):如果有scanBasePackages ,就不扫描默认得路径。
所以将bean加入spring容器:放在启动类得包路径下面+@component;@ComponentScan(basePackages = {""})+@component;从META-INF/spring.factories中获取资源;@Configuration+@Bean
@Import(AutoConfigurationImportSelector.class),@Import注解就是给Spring容器中导入一些组件,这里传入了一个组件的选择器:AutoConfigurationImportSelector。ImportSelector有一个方法为:selectImports。将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中。
会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件。有了自动配置类,免去了我们手动编写配置注入功能组件等的工作。
会从META-INF/spring.factories中获取资源,然后通过Properties加载资源。可以知道SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。
BeanDefinitionRegistry
该接口主要是对BeanDefintion的增删查改
BeanDefinitionRegistryPostProcessor
调用时序:
该接口的调用时序是非常靠前的,基本上容器初始化的一些操作就是为了调用这个接口而做准备的。有一个特别关键的实现类,就是ConfigurationClassPostProcessor 类,在这里它作为一个普通的实现类去执行,但也是关键的创建bd的代码。
执行时机:在所有bean定义信息将要被加载,bean实例还未创建时;
它优先于BeanFactoryPostProcessor执行,利用BeanDefinitionRegistryPostProcessor给容器中再额外添加一些组件;
原理:
1)IOC创建对象;
2)refresh()-->invokeBeanFactoryPostProcessors(BeanFactory);
3)从容器中获取到所有的BeanDefinitionregistryPostProcessor组件。
3.1)依次触发所有的postProcessBeanDefinitionRegistry()方法;
3.2)再来触发postProcessBeanFactory ;
4)再来从容器中找到beanFactoryPostProcessor组件,然后依次触发;
spring中我们常用注解配置以及xml文件来管理我们的bean。其实在spring的生命周期中,bean在实例化之前都是无差别的被当做资源加载进来的,并被封装成一个个Beandefinition。在spring启动时,所有bean实例化的注解和xml文件都会被加载进来,并注册成Beandefinition。准备后续的bean实例化。那么在注册成Beandefinition这步,spring其实提供给了我们不少后门进行操作。常见的后置处理器BeanDefinitionRegistryPostProcessor就是一个比较常见的自定义操作bean的接口。BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口,BeanFactoryPostProcessor的作用是在bean的定义信息已经加载但还没有初始化的时候执行方法postProcessBeanFactory()方法,而BeanDefinitionRegistryPostProcessor是在BeanFactoryPostProcessor的前面执。
模拟springBoot调用
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationTest.class);
Spring后置处理器
BeanPostProcessor:bean级别的处理,针对某个具体的bean进行处理。接口提供了两个方法,分别是初始化前和初始化后执行方法。
需要注意一点,我们定义一个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进行处理。
BeanFactoryPostProcessor:BeanFactory级别的处理,是针对整个Bean的工厂进行处理。
此接口只提供了一个方法,方法参数为ConfigurableListableBeanFactory 。
其中有个方法名为getBeanDefinition的方法,我们可以根据此方法,找到我们定义bean的BeanDefinition对象。然后我们可以对定义的属性进行修改。
@ComponentScan
会将包下@Component注解的类注册进Spring。(通过ConfigurationClassPostProcessor
) 。@Service、@Controller、@Configuration、@Repository也会,因为有@Component注解。
Spring中最!最!最!重要的后置处理器!没有之一!!! - 知乎 (zhihu.com)
SpringBoot中@Import注解的使用方式_java_脚本之家 (jb51.net)
AutowiredAnnotationBeanPostProcessor
这个bean的后置处理器用来处理@Autowired、@Value的注入
在
AutowiredAnnotationBeanPostProcessor
初始化的时, autowiredAnnotationTypes 中添加了三个注解@Autowired
、@Value
、 和通过反射得到的javax.inject.Inject
。
getConstructors:只返回公共构造函数
getDeclaredConstructor:返回所有的构造函数
Candidate 候选
determineCandidateConstructor:方法是 SmartInstantiationAwareBeanPostProcessor
接口中的方法。其作用是从 注入bean的所有构造函数中过滤出可以作为构造注入的构造函数列表。
1、构造器注入@AutoWired(required=true)有且只能有一个,同时不可有required=false的
2、可以有多个@AutoWired(required=false)修饰的构造函数(如果有无参构造方法,也会一起返回)
3、如果当前没有@Autowired注解,且类里只有一个public有参构造函数,且没有@AutoWired注解,就会返回这个
4、上面都不符合,返回null:比如只有个默认无参构造函数
CommonAnnotationBeanPostProcessor
用来处理如@Resource,@PostConstruct等符合JSR-250规范的注解
refresh()
方法
是整个Spring
容器的核心,在这个方法中进行了bean
的实例化、初始化、自动装配、AOP
等功能。
public void refresh() throws BeansException, IllegalStateException {
2 synchronized (this.startupShutdownMonitor) {
3 // Prepare this context for refreshing.
4 // 初始化属性配置文件、检验必须属性以及监听器
5 prepareRefresh();
6 // Tell the subclass to refresh the internal bean factory.
7 // 给beanFactory设置序列化id
8 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
9 // 向beanFactory中注册了两个BeanPostProcessor,以及三个和环境相关的bean
10 // 这两个后置处理器为ApplicationContextAwareProcessor和ApplicationListenerDetector
11 // 前一个后置处理是为实现了ApplicationContextAware接口的类,回调setApplicationContext()方法,
12 // 后一个处理器时用来检测ApplicationListener类的,当某个Bean实现了ApplicationListener接口的bean被创建好后,会被加入到监听器列表中
13 prepareBeanFactory(beanFactory);
14 try {
15 // Allows post-processing of the bean factory in context subclasses.
16 // 空方法,由子类实现
17 postProcessBeanFactory(beanFactory);
18 // 执行所有的BeanFactoryPostProcessor,包括自定义的,以及spring内置的。默认情况下,容器中只有一个BeanFactoryPostProcessor,即:Spring内置的,ConfigurationClassPostProcessor(这个类很重要)
19 // 会先执行实现了BeanDefinitionRegistryPostProcessor接口的类,然后执行BeanFactoryPostProcessor的类
20 // ConfigurationClassPostProcessor类的postProcessorBeanFactory()方法进行了@Configuration类的解析,@ComponentScan的扫描,以及@Import注解的处理
21 // 经过这一步以后,会将所有交由spring管理的bean所对应的BeanDefinition放入到beanFactory的beanDefinitionMap中
22 // 同时ConfigurationClassPostProcessor类的postProcessorBeanFactory()方法执行完后,向容器中添加了一个后置处理器————ImportAwareBeanPostProcessor
23 invokeBeanFactoryPostProcessors(beanFactory);
24 // 注册所有的BeanPostProcessor,因为在方法里面调用了getBean()方法,所以在这一步,实际上已经将所有的BeanPostProcessor实例化了
25 // 为什么要在这一步就将BeanPostProcessor实例化呢?因为后面要实例化bean,而BeanPostProcessor是用来干预bean的创建过程的,所以必须在bean实例化之前就实例化所有的BeanPostProcessor(包括开发人员自己定义的)
26 // 最后再重新注册了ApplicationListenerDetector,这样做的目的是为了将ApplicationListenerDetector放入到后置处理器的最末端
27 registerBeanPostProcessors(beanFactory);
28 // Initialize message source for this context.
29 // 初始化MessageSource,用来做消息国际化。在一般项目中不会用到消息国际化
30 initMessageSource();
31 // Initialize event multicaster for this context.
32 // 初始化事件广播器,如果容器中存在了名字为applicationEventMulticaster的广播器,则使用该广播器
33 // 如果没有,则初始化一个SimpleApplicationEventMulticaster
34 // 事件广播器的用途是,发布事件,并且为所发布的时间找到对应的事件监听器。
35 initApplicationEventMulticaster();
36
37 // Initialize other special beans in specific context subclasses.
38 // 执行其他的初始化操作,例如和SpringMVC整合时,需要初始化一些其他的bean,但是对于纯spring工程来说,onFresh方法是一个空方法
39 onRefresh();
40
41 // Check for listener beans and register them.
42 // 这一步会将自定义的listener的bean名称放入到事件广播器中
43 // 同时还会将早期的ApplicationEvent发布(对于单独的spring工程来说,在此时不会有任何ApplicationEvent发布,但是和springMVC整合时,springMVC会执行onRefresh()方法,在这里会发布事件)
44 registerListeners();
45 // 实例化剩余的非懒加载的单例bean(注意:剩余、非懒加载、单例)
46 // 为什么说是剩余呢?如果开发人员自定义了BeanPosrProcessor,而BeanPostProcessor在前面已经实例化了,所以在这里不会再实例化,因此这里使用剩余一词
47 finishBeanFactoryInitialization(beanFactory);
48 // 结束refresh,主要干了一件事,就是发布一个事件ContextRefreshEvent,通知大家spring容器refresh结束了。
49 finishRefresh();
50 }
51 catch (BeansException ex) {
52 // 出异常后销毁bean
53 destroyBeans();
54 // Reset 'active' flag.
55 cancelRefresh(ex);
56 // Propagate exception to caller.
57 throw ex;
58 }
59 finally {
60 // 在bean的实例化过程中,会缓存很多信息,例如bean的注解信息,但是当单例bean实例化完成后,这些缓存信息已经不会再使用了,所以可以释放这些内存资源了
61 resetCommonCaches();
62 }
63 }
64}
默认使用的ApplicationContext
AnnotationConfigServletWebServerApplicationContext。构造函数中会创建 AnnotatedBeanDefinitionReader。而在 AnnotatedBeanDefinitionReader 构造函数中会调用 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);,该方法将一些必要Bean(如ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor 等)注入到了容器中。
GenericApplicationContext中有初始化的BeanFactory
这个类里,BeanDefinitionRegistry对BeanDefinition的增删改查,其实是在父类GenericApplicationContext,父类其实是调用的DefaultListableBeanFactory。
InstantiationAwareBeanPostProcessor
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
接口的主要作用在于目标对象的实例化过程中需要处理的事情,包括实例化对象的前后过程以及实例的属性设置 。
postProcessBeforeInstantiation方法是最先执行的方法,它在目标对象实例化之前调用,该方法的返回值类型是Object,我们可以返回任何类型的值。由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用(创建对象结束);否则按照正常的流程走。
postProcessAfterInstantiation方法在目标对象实例化之后调用,这个时候对象已经被实例化,但是该实例的属性还未被设置,都是null。默认返回true,如果返回false,则不需要进行后面的依赖注入。
AnnotatedBeanDefinition
AnnotatedBeanDefinition 是 BeanDefinition 子接口之一,该接口扩展了 BeanDefinition 的功能,其用来操作注解元数据。一般情况下,通过注解方式得到的 Bean(@Component、@Bean)
该接口可以返回两个元数据的类:
AnnotationMetadata:主要对 Bean 的注解信息进行操作,如:获取当前 Bean 标注的所有注解、判断是否包含指定注解。
MethodMetadata:方法的元数据类。提供获取方法名称、此方法所属类的全类名、是否是抽象方法、判断是否是静态方法、判断是否是final方法等。
ScannedGenericBeanDefinition
该类继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述标注 @Component 注解的 Bean,其派生注解如 @Service、@Controller 也同理。
ConfigurationClassBeanDefinition
该类继承自 RootBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述在标注 @Configuration 注解的类中,通过 @Bean 注解实例化的 Bean。
其功能特点如下:
1、如果 @Bean 注解没有指定 Bean 的名字,默认会用方法的名字命名 Bean。
2、标注 @Configuration 注解的类会成为一个工厂类,而标注 @Bean 注解的方法会成为工厂方法,通过工厂方法实例化 Bean,而不是直接通过构造方法初始化。
3、标注 @Bean 注解的类会使用构造方法自动装配??
此bean会有如下重要属性:
通过factory-method属性创建对象
一种静态(方法必须静态),一种动态。
<!--生成对象的工厂-->
<bean id="stuFactory" class="com.dragon.study.study20190618.spring.springFactoryMethod.StuFactory"/>
<!--动态获取对象-->
<bean id="dynamicStu" factory-bean="stuFactory" factory-method="getDynamicStu">
<!--传入getDynamicStu方法的参数-->
<constructor-arg value="11"/>
</bean>
public class StuFactory {
static Map<String,Stu> stuMap = new HashMap<>();
static{
//初始化
Stream.iterate(1,n->n+1).limit(5).map(String::valueOf).forEach(t-> stuMap.put(t, new Stu(t)));
}
//动态创建类
public Stu getDynamicStu(String stuId){
return new Stu(stuId);
}
}
Spring(四)核心容器 - BeanDefinition 解析 - 龙四丶 - 博客园
springboot的启动类,就是放在AnnotatedGenericBeanDefinition,内含BeanClass和metadata。在main方法->SpringApplication.run(MultipleApplication.class, args) -> prepareContext -> Set<Object> sources = getAllSources() -> load(context, sources.toArray(new Object[0])) -> 注册启动类的AnnotatedGenericBeanDefinition。
启动类,必须加上@Configuration--好像也不一定。。。。
ConfigurationClassPostProcessor
1、首先,获取启动类的BeanDefinition,找到启动类上的ComponentScan注解,扫描basePackage下的@component标注的类,生成ScannedGenericBeanDefinition。遍历扫描到的bean,其实是递归遍历扫描到的类,有没有被@ComponentScan、@Import等注解标注。再扫描启动类上有没有加@Import注解,找到@Import注解里的类,实现ImportBeanDefinitionRegistrar接口的,就会生成ImportBeanDefinitionRegistrar对象,实现ImportSelector接口的会先生成ImportSelector对象,再执行它的selectImports方法,遍历方法返回的类,同样递归遍历,看这些类有没有被@ComponentScan、@Import等注解标注。或者@Import里面的是个普通类,同样看这些类有没有被@ComponentScan、@Import等注解标注、里面方法有没有被@Bean注解标注。再查看主启动类上方法有没有被@Bean注解标注。
parser.parse(candidates):通过@ComponentScan注解扫描的类才会加入到BeanDefinitionMap中/ 通过其他注解(例如@Import、@Bean)的方式,在parse()方法这一步并不会将其解析为BeanDefinition放入到BeanDefinitionMap中,而是先解析成ConfigurationClass类.真正放入到map中是在下面的this.reader.loadBeanDefinitions()方法中实现的。
createBeanInstance
1、如果RootBeanDefinition 中存在 factoryMethodName 属性,或者在配置文件中配置了factory-method,Spring会尝试使用 instantiateUsingFactoryMethod 方法,根据RootBeanDefinition 中的配置生成bean实例。需要注意的是,如果一个类中中的方法被 @Bean注解修饰,那么Spring则会将其封装成一个 ConfigurationClassBeanDefinition。此时 factoryMethodName 也被赋值。所以也会调用instantiateUsingFactoryMethod 方法通过反射完成方法的调用,并将结果注入Spring容器中。
2、构造函数筛选
// 调用 SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors 方法来筛选构造函数。
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 有候选构造函数 || 构造方式为构造注入 || 有构造函数入参 || 用于构造函数或工厂方法调用的显式参数args 不为null
// 则调用 autowireConstructor 方法
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
// 如果有 确定用于默认构造的首选构造函数(如果有)。如有必要,构造函数参数将自动装配。
// RootBeanDefinition 的实现是 null 。这里仅在 GenericApplicationContext.ClassDerivedBeanDefinition 中有过解析
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
// 使用默认方式进行bean 构造
return instantiateBean(beanName, mbd);
ctors != null : determineConstructorsFromBeanPostProcessors 方法筛选出的候选构造函数不为null。对于默认的Spring来说,实际返回的是 AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors 方法的返回值。而其作用简单来说就是返回被 @Autowired 注解修饰的构造函数。
mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR
:这里的配置是通过xml 来配置bean时指定的装配方式。
mbd.hasConstructorArgumentValues()
:这里表示是否给当前bean定义了构造函数入参。通过xml配置bean的时候可以通过 <constructor-arg>
标签指定构造函数入参。
属性注入
Spring 默认是通过 AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues
的实现来完成的属性的注入。AutowiredAnnotationBeanPostProcessor 中完成了@Autowired、@Value 注解的自动注入功能。大概逻辑是,获取被 @Autowired 修饰的 属性或者方法,如果是属性,则通过getBean 获取bean并注入,如果是方法,则获取方法参数后,invoke 方法(调用该方法,因为我们一般写的都是set方法,给属性注入赋值)。
Bean的初始化
顺序:PostConstruct(在CommonAnnotationBeanPostProcessor实现)>实现InitializingBean接口>
调用 init-method
指定的方法
SpringBoot启动时自动执行的方式:
实现接口CommandLineRunner
和ApplicationRunner
接口时,可以通过在类上添加@Order注解来设定运行顺序。