Appconfig.java
@ComponentScan("com.yuanma.beanDefinition")
@Configuration
public class Appconfig {
}
X.java
@Component
public class X {
public X(){
System.out.println("X Constructor");
}
}
Y.java
public class Y {
}
Test.java
public class Test{
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new
AnnotationConfigApplicationContext();
ac.register(Appconfig.class);
ac.refresh();
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(Appconfig.class);
applicationContext.refresh();
}
首先通过一张图简单的理解一下spring容器启动的时候执行调用BeanFactoryPostProcessor后置处理器的大概的方法执行顺序:
上图大概分为④步(这里只是讨论spring如何调用BeanFactoryPostProcessor,在调用之前到底执行了那些方法,上图并不是spring容器启动的所有步骤)
步骤1:启动main方法
①启动main方法。
步骤2:实例化Context,注册内置处理器
②在main方法里面调用AnnotationConfigApplicationContext的无参构造方法。
public AnnotationConfigApplicationContext() {
//调用构造方法时需要首先调用父类GenericApplicationContext的构造方法
//在父类的构造方法里会初始化一个DefaultListableBeanFactory
//this.beanFactory = new DefaultListableBeanFactory();
//创建一个BeanDefinition读取器 可以根据注解读取BeanDefinition
this.reader = new AnnotatedBeanDefinitionReader(this);
//创建一个BeanDefinition扫描器 能够扫描一个类或者包 转换成bd
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
2.1实例化DefaultListableBeanFactory
AnnotationConfigApplicationContext继承自GenericApplicationContext,所以调用自己之前会先调用父类GenericApplicationContext的构造方法。
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
2.2实例化AnnotatedBeanDefinitionReader
this.reader = new AnnotatedBeanDefinitionReader(this);
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
//registry 代表的是AnnotationConfigApplicationContext
this(registry, getOrCreateEnvironment(registry));
}
2.2.1构造方法注册了2个BFPP2个BPP
这里很重要,因为ConfigurationClassPostProcessor就是在这里被注入的,当前只是先看一下,后面细讲!
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry,Environment environment){
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
/*
这里很重要 因为ConfigurationClassPostProcessor就是在这里被注入的!
这里很重要 因为ConfigurationClassPostProcessor就是在这里被注入的!
这里很重要 因为ConfigurationClassPostProcessor就是在这里被注入的!
//BeanFactoryPostProcessor
RootBeanDefinition def = new
RootBeanDefinition(ConfigurationClassPostProcessor.class);
//BeanPostProcessor
RootBeanDefinition def = new
RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
//BeanPostProcessor
RootBeanDefinition def = new
RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
//EventListenerMethodProcessor
RootBeanDefinition def = new
RootBeanDefinition(EventListenerMethodProcessor.class);
RootBeanDefinition def = new
RootBeanDefinition(DefaultEventListenerFactory.class);
*/
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
2.3实例化了ClassPathBeanDefinitionScanner
这个对象顾名思义就是能够用来完成spring的classpath的扫描功能。
//创建一个BeanDefinition扫描器 能够扫描一个类或者包 转换成bd
this.scanner = new ClassPathBeanDefinitionScanner(this);
在这里创建了但是并没有用到,只有使用下面的构造方法才能用到。
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
这里提一句:在扫描我们指定的包路径的时候用的是AnnotationConfigApplicationContext的构造方法中构造的ClassPathBeanDefinitionScanner。但是spring内部完成扫描功能并不是用的这个对象,而是在扫描的时候会new新的ClassPathBeanDefinitionScanner对象。
换言之,这里在AnnotationConfigApplicationContext的构造方法中new的对象我们假设他为a,但是spring在真正完成扫描的时候会new一个b,它们是同一个类都是ClassPathBeanDefinitionScanner,为什么需要两个?
1.每个scaner的includeFilter和excludeFilter属性不相同,扫描的包的路径也不同。所以需要不同的参数构造不同的ClassPathBeanDefinitionScanner
2.每个@ConponentScan的包含过滤器和排除过滤器不一样,是独一无二的 需要把每个@ConponentScan的包含过滤器和排除过滤器 复制到对应的ClassPathBeanDefinitionScanner扫描器中
步骤3:注册配置类为BeanDefintion
applicationContext.register(Appconfig.class);
@Override
public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
//使用AnnotatedBeanDefinitionReader 读取器 注册bd
this.reader.register(componentClasses);
}
可以看到会把Appconfig类解析成为一个beanDefintion对象。
这里其实涉及到AnnotatedBeanDefinitionReader对象的意义,给解析出来的beanDefinition对象设置一些默认属性,继而put到beanDefintionMap当中。
为什么需要put到beanDefintionMap呢?
在上一篇我们已经解释过这个map就是单纯用来存储beanDefinition的,spring后面会遍历这个map根据map当中的beanDefinition来实例化bean,如果Appconfig类的beanDefintion存在map当中那么他必然会被spring容器实例化成为一个bean。
为什么Appconfig会需要实例化呢?
因为Appconfig当中有很多加了@Bean的方法,这些方法需要被调用,故而需要实例化,但是Appconfig类的实例化很复杂比一般类实例化过程复杂很多,涉及到代理涉及到cglib等等,这个我们后面文章解释。
为什么Appconfig类是通过register(Appconfig.class);手动put到map当中呢?为什么不是扫描出来的呢?
其实也很简单,普通的类都是通过扫描出来的。但是因为Appconfig无法扫描自己,普通的类是spring通过解析Appconfig上的@ComponentScan注解然后被扫描到,但是配置类无法扫描自己,相当于是1个入口方法。
也就是Spring需要扫描哪些包是通过配置类来配置的,所以配置类相当于是1个入口。通过扫描配置类配置的扫描包扫描出来的类就是普通类。当然这些普通类上面也可能配置了扫描包或者导入了其他的xml配置。这个时候这些普通类也会被当做配置类处理,这是1个递归的过程。
步骤4:后面分析
接下来便是第④步,④-1到④-4以后分析。
步骤5:执行spring中的bean工厂后置处理器
④-5便是我们上篇文章说的执行spring当中的bean工厂后置处理器,也是本文重点讨论的;下图是对上述文字的一个说明——spring容器启动的执行顺序:
AnnotatedBeanDefinitionReader作用
先看一下这个类的javadoc,看看作者怎么来解释这个类的,这个类作用分为以下两个。
作用一:动态注册带注解的bean
可用于编程式动态注册一个带注解的bean,什么意思呢?
比如我们有一个类A存在com.shadow包下面,并且是一个加注解的类。比如加了@Component,正常情况下这个类A是可以被spring扫描出来的,但是有不正常情况,比如spring并没有扫描到com.shadow包,那么类A就无法被容器实例化。
有人可能会问为什么没有扫描到com.shadow包?什么情况下不会扫描到?
1.假设这个类是动态生成,在容器实例化的时候不存在那么肯定不存在.
2.包下有N多类但是只有一个类加了注解,那么其实你不需要去扫描,只需要使用AnnotatedBeanDefinitionReader去注册这个类即可。
3.某个类是你和第三方系统交互后得到的。那么这个时候我们可以把这个类通过AnnotatedBeanDefinitionReader的register(Class clazz)方法把一个带注解的类注册给spring。
这里的注册其实就是上一篇文章中说的把一个类解析成BeanDefintion对象,然后把这个对象put到beanDefinitionMap当中。
作用二:注册配置类
AnnotatedBeanDefinitionReader可以代替ClassPathBeanDefinitionScanner这个类,具备相同的注解解析功能。
ClassPathBeanDefinitionScanner是spring完成扫描的核心类,这个我后面会分析。
简而言之,spring完成扫描主要是依靠ClassPathBeanDefinitionScanner这个类的对象,但是AnnotatedBeanDefinitionReader可以替代他完成相同的注解解析,意思就是通过ClassPathBeanDefinitionScanner扫描出来的类A和通过AnnotatedBeanDefinitionReader显式注册的类A在spring内部会一套相同的解析规则。
那么AnnotatedBeanDefinitionReader除了动态显示注册一些spring扫描不到的类之外还有什么功能?
在初始化spring容器的过程中他主要干了什么事情呢?
或者这么说:假设程序中没有需要动态显示注册的类他就没用了吗?
再或者AnnotatedBeanDefinitionReader这个类的对象除了注册一些自己的类还有什么应用场景呢?
答案是作为读取器,注册我们的配置类,所谓的配置类就是那个加了@Configuration和@ComponentScan的那个类。也就是Appconfig.java。
那么问题来了,为什么配置类需要手动注册呢?
很简单,因为配置类无法扫描出来,所以需要我们手动注册。为什么无法扫描呢?
比如spring完成扫描是需要解析Appconfig.java当中的@ComponentScan注解的值(一般是一个包名),得到这个值之后去扫描这个值所代表的包下面的所有bean。
简单的说就是spring如果想要完成扫描。必须先提供Appconfig.java,所以Appconfig.java要在一开始就手动注册给spring,spring得到Appconfig.class之后把他解析成BeanDefintion对象,继而去获取@ComponentScan的值然后才能开始扫描其他bean。
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Appconfig.class);
}
//可以发现构造方法里已经帮我们做了register和refresh和文章开头的并无区别。
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
this.register(componentClasses);
this.refresh();
}
除了使用上面这种方式,也可以通过以下方式指定扫描的包名。
@Configuration
public class AppConfig {
public AppConfig() {
System.out.println("AppConfig开始被构造");
}
@Bean
public Object getObject(){
return new Object();
}
}
AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext("指定扫描的包名");
//我们可以看到也可以使用扫描器扫描到AppConfig
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
如何注册
针对AnnotatedBeanDefinitionReader应用场景的第2点,我在啰嗦几句,一般程序员在初始化spring容器的时候代码有很多种写法,但都是换汤不换药的,我这里举2个例子:
第一种写法
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext();
//动态注册一个配置类 通过配置类来进行扫描包配置
ac.register(Appconfig.class);
//调用refresh方法
//这里很多资料都叫做刷新spring容器
//但是我觉得不合适,这是硬核翻译,比较生硬
//笔者觉得理解为初始化spring容器更加精准
//至于为什么以后慢慢更新再说
ac.refresh();
第二种写法
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(Appconfig.class);
这两种写法都初始化spring容器,代码上的区别无非就是:
第一种写法是调用AnnotationConfigApplicationContext()的无参构造方法。
第二种写法是调用了AnnotationConfigApplicationContext(Class<?>... annotatedClasses)有参构造方法。
但是你如果翻阅源码,第二种写法的内部也是首先调用无参构造方法的,内部继续调用register(Appconfig.class)方
法,最后调用refresh()方法。和第一种写法的区别是register和refresh是程序员调用的。
笔者更推荐第一种写法,因为第一种写法可以在初始化spring容器之前得到AnnotationConfigApplicationContext的对象也就是代码里面的ApplicationContext对象。
其实第一种方法和第二种方法都可以得到ApplicationContext对象,那么为什么第一种写法笔者推荐呢?
首先第二种方法是在spring容器完成初始化之后的到的ac对象,容器已经初始化了,这个时候得到这个对象能干了事情少了很多,第一种方法在初始化之前得到的,那么能干的事情可多了。
①比如我们可以在容器初始化之前动态注册一个自己的bean,就是上文提到的 AnnotatedBeanDefinitionReader的应用场景,
②再比如可以利用ApplicationContext对象来关闭或者开启spring的循环依赖,
③还比如程序员可以在容器初始化之前注册自己实例化的BeanDefinition对象。如果你精通spring源码你会发觉提前得到这个ApplicationContext对象可以做的事情太多了,笔者这里说的三个比如都必须在spring容器初始化之前做才有意义,简而言之就是需要在spring调用refresh方法之间做才有意义。
AnnotatedBeanDefinitionReader源码
ConfigurationClassPostProcessor对应的BeanDefinition就是在AnnotatedBeanDefinitionReader构造的。
我们看下AnnotatedBeanDefinitionReader具体做了什么,看这个构造方法,传入的是一个BeanDefinitionRegistry类型,就是可以注册BeanDefinition的,内部要注册就需要用到:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
//registry 代表的是AnnotationConfigApplicationContext
this(registry, getOrCreateEnvironment(registry));
}
getOrCreateEnvironment获得或者创建环境,如果BeanDefinitionRegistry是EnvironmentCapable的话就可以直接获取,否则就创建一个标准环境,其实就是获取一些系统的变量。
比如可以配置dev环境,test环境,pro环境等等。
private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry){
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry instanceof EnvironmentCapable) {
return ((EnvironmentCapable) registry).getEnvironment();
}
return new StandardEnvironment();
}
接下来执行的是这一句代码:this(registry, getOrCreateEnvironment(registry));
继续看AnnotatedBeanDefinitionReader另一个构造方法:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment){
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
//关键代码
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
前面4句代码是在断言判空和赋值。对BeanDefinitionRegistry进行了保存和把environment封装进了ConditionEvaluator,ConditionEvaluator可以理解成一个条件过滤器,与@Conditional有关,如果有了这个注解,就先判断条件成不成立,不成立的话有些操作就不做了。
registerAnnotationConfigProcessors
最重要的操作是registerAnnotationConfigProcessors。registerAnnotationConfigProcessors一看方法就知道要加一些处理器,来处理我们的注解配置,也就是说,spring有些内部的处理器需要注册进来,这里可以想到spring应该是为了统一处理处理器,所以也是按注册,处理这样的流程来,无论是自己内部的,还是用户自定义的,我们来看看里面是怎么做的,我们跟进这个方法的源码:
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
// 获取beanFactory也就是DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory =
unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator()
instanceof AnnotationAwareOrderComparator)) {
/**
* AnnotationAwareOrderComparator主要能解析@Order和@Priority
*/
beanFactory.setDependencyComparator
(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver()
instanceof ContextAnnotationAutowireCandidateResolver)) {
/**
* ContextAnnotationAutowireCandidateResolver提供处理延迟加载的功能
*/
beanFactory.setAutowireCandidateResolver
(new ContextAnnotationAutowireCandidateResolver());
}
}
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
/**
* 下面非常非常重要
* spring默认的BeanDefinition的注册,很重要,需要理解每个bean的类型
*/
/*
* 1.注册ConfigurationClassPostProcessor对应的bd
* 注册的bd的名字是internalConfigurationAnnotationProcessor
* ConfigurationClassPostProcessor是一个工厂后置处理器
* 这个后置处理器非常重要,基本上类上面的注解都在这里面判断并解析
* spring的包扫描也在里面完成
*/
if(!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)){
RootBeanDefinition def = new
RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
//添加ConfigurationClassPostProcessor对应的bd
beanDefs.add(registerPostProcessor(
registry,
def,
CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); }
// 2.注册AutowiredAnnotationBeanPostProcessor
//顾名思义就是处理@Autowired的,它是一个bean的后置处理器,在bean的属性注入的时候会用到
if(!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new
RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def,
AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
//3.注册CommonAnnotationBeanPostProcessor
//顾名思义就是处理一些公共注解的,它是一个bean的后置处理器
//可以处理@PostConstruct和@PreDestroy还有@Resource等
//提示: 这里有一个jsr250Present校验
if (jsr250Present &&
!registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)){
RootBeanDefinition def = new
RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def,
COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 4.注册PersistenceAnnotationProcessor
if (jpaPresent && !registry.containsBeanDefinition
(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass
(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Cannot load optional framework class: ");
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def,
PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 5.注册EventListenerMethodProcessor
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new
RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def,
EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
// 6.注册DefaultEventListenerFactory
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new
RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def,
EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
主要流程就是获取DefaultListableBeanFactory。
1.注册AnnotationAwareOrderComparator顺序比较器,排序用的。
2.注册ContextAnnotationAutowireCandidateResolver自动装配解析器,解析自动装配相关配置用。
3.注册CommonAnnotationBeanPostProcessor 顾名思义就是处理一些公共注解的,它是一个bean的后置处理器 可以处理@PostConstruct和@PreDestroy还有@Resource等
3.注册ConfigurationClassPostProcessor,他是前面配置类处理的关键,所以这里先添加bean定义。
4.AutowiredAnnotationBeanPostProcessor,处理自动装配的。
5.EventListenerMethodProcessor和DefaultEventListenerFactory。
6.注册CommonAnnotationBeanPostProcessor。
我们再来看看AnnotatedBeanDefinitionReader的重要属性:
private final BeanDefinitionRegistry registry;
//类名生成器
private BeanNameGenerator beanNameGenerator =
AnnotationBeanNameGenerator.INSTANCE;
//socpe注解解析器
private ScopeMetadataResolver scopeMetadataResolver = new
AnnotationScopeMetadataResolver();
//条件评估器
private ConditionEvaluator conditionEvaluator;
可以看到,内部的bean定义全部都是用RootBeanDefinition。
至此读取器创建完成,其实就是注册了需要处理注解的处理器bean定义,此时还没有创建bean哦。后面ConfigurationClassPostProcessor发挥着很大的作用,用来解析配置类。
ClassPathBeanDefinitionScanner为什么要2个?
因为每个@ConponentScan的包含过滤器和排除过滤器不一样,是独一无二的 需要把每个@ConponentScan的包含过滤器和排除过滤器 复制到ClassPathBeanDefinitionScanner扫描器中