ApplicationContext接口负责实例化、配置和组装 bean,所谓Spring容器入口其实就是这个接口,代表着Spring IoC 容器的核心,ApplicationContext接口有很多实现方式,包括很早之前的ClassPathXmlApplicationContext,通过XML的方式装配bean,也包含了现在通过注解的方式AnnotationConfigApplicationContext。
AnnotationConfigApplicationContext使用 引入依赖 1 2 3 dependencies { implementation 'org.springframework:spring-context:5.3.16' }
使用AnnotationConfigApplicationContext只需要引入spring-context即可,而spring-context依赖关系如下
下面我们通过自定义一个bean并通过AnnotationConfigApplicationContext装配并获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 public class User { private String name; private int age; private boolean sex; public User () {} public User (String name, int age, boolean sex) { this .name = name; this .age = age; this .sex = sex; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } public boolean isSex () { return sex; } public void setSex (boolean sex) { this .sex = sex; } @Override public String toString () { return "User{" + "name='" + name + '\'' + ", age=" + age + ", sex=" + sex + '}' ; } } @Configuration public class UserConfig { @Bean public User user () { return new User ("张三" ,20 ,false ); } } public class AnnotationRunner { private static final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (); public static void main (String[] args) { add(UserConfig.class); User user = (User) getBean(User.class); System.out.println(user.toString()); } public static void add (Class clz) { context.register(clz); context.refresh(); } public static Object getBean (Class clz) { return context.getBean(clz); } }
通过上面的代码,我们初始化一个AnnotationConfigApplicationContext,并装配UserConfig,然后从容器中获取对象User,最后得到结果如下:
AnnotationConfigApplicationContext继承关系 先来看看AnnotationConfigApplicationContext的继承关系,可以看出整个容器由于资源加载器接口,Bean工厂接口,还有函数式接口组成。
下面是我将AnnotationConfigApplicationContext集成的接口和类的功能整理的图。通过下图可以大概分析出每个接口和类的具体作用。
AnnotationConfigApplicationContext生成对象的流程 上面提到了AnnotationConfigApplicationContext实现了AnnotationConfigRegistry接口,AnnotationConfigRegistry接口声明register和scan两个方法,register和scan的作用
AnnotationConfigRegistry声明了register方法和scan方法来添加bean,而AnnotationConfigApplicationContext实现了AnnotationConfigRegistry接口,来看看这两个方法的实现 register方法的实现如下
通过扫描class实现将bean的定义器添加到
1 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap <>(256 );
而scan方法则是通过扫描包下的所有class批量添加BeanDefinition。
初始化方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public AnnotationConfigApplicationContext () { StartupStep createAnnotatedBeanDefReader = this .getApplicationStartup().start("spring.context.annotated-bean-reader.create" ); this .reader = new AnnotatedBeanDefinitionReader (this ); createAnnotatedBeanDefReader.end(); this .scanner = new ClassPathBeanDefinitionScanner (this ); } public AnnotationConfigApplicationContext (DefaultListableBeanFactory beanFactory) { super (beanFactory); this .reader = new AnnotatedBeanDefinitionReader (this ); this .scanner = new ClassPathBeanDefinitionScanner (this ); } public AnnotationConfigApplicationContext (Class<?>... componentClasses) { this (); register(componentClasses); refresh(); } public AnnotationConfigApplicationContext (String... basePackages) { this (); scan(basePackages); refresh(); }
上面我们分析了register和scan方法主要是将class解析为BeanDefinition并添加到beanDefinitionMap中,而refresh则是刷新上下文并初始化bean的过程。这里我们来走一下bean的创建过程.
Bean的创建过程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Component @Data @NoArgsConstructor @AllArgsConstructor public class CycleD { private int age; private String name; } @Slf4j public class CycleMain { public static void main (String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext ("work.labradors.springbootdemo.entity.cycle" ); CycleD d = applicationContext.getBean(CycleD.class); log.info("执行完成..." ); } }
scan之后就是refresh方法,里面有很多操作,初始化ConfigurableListableBeanFactory,初始化消息源,时间广播器等。但是最重要的是finishBeanFactoryInitialization(beanFactory)
。他的主要功能是初始化所有非懒加载的单例。
断点进去看看,调用的是工厂的预初始化单例方法
preInstantiateSingletons
方法并没有返回值
进入preInstantiateSingletons
方法,该方法的作用是遍历beanDefinitionNames列表,并根据beanName一次调用getBean
方法,getBean
方法内部调用的是doGetBean
方法。 来看看doGetBean
方法的实现,首先从三级缓存中拿需要的对象,如下所示
getSingleton
比较重要,贴一下代码看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Nullable protected Object getSingleton (String beanName, boolean allowEarlyReference) { Object singletonObject = this .singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this .earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { synchronized (this .singletonObjects) { singletonObject = this .singletonObjects.get(beanName); if (singletonObject == null ) { singletonObject = this .earlySingletonObjects.get(beanName); if (singletonObject == null ) { ObjectFactory<?> singletonFactory = this .singletonFactories.get(beanName); if (singletonFactory != null ) { singletonObject = singletonFactory.getObject(); this .earlySingletonObjects.put(beanName, singletonObject); this .singletonFactories.remove(beanName); } } } } } } return singletonObject; }
上图中从缓存中获取bean肯定是为空的,所以sharedInstance,会执行下面这段代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
createBean
方法内调用了doCreateBean
,后面的调用链我们就不看了,主要是通过生成Constructor并实例化对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 try { Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'" ); } return beanInstance; } if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references" ); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
从上面我们看出createBean
的过程是将对象初始化并添加到三级缓存的过程。而getSingleton方法代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public Object getSingleton (String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null" ); synchronized (this .singletonObjects) { Object singletonObject = this .singletonObjects.get(beanName); if (singletonObject == null ) { try { singletonObject = singletonFactory.getObject(); newSingleton = true ; } catch (IllegalStateException ex) { singletonObject = this .singletonObjects.get(beanName); if (singletonObject == null ) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this .suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this .suppressedExceptions = null ; } afterSingletonCreation(beanName); } if (newSingleton) { addSingleton(beanName, singletonObject); } } return singletonObject; } }
上面的代码中,首先从一级缓存singletonObjects中查找,发现没有需要的对象,然后直接从singletonFactory三级缓存中查找,因为上面createBean生成了对象并将其放入了三级缓存,所以可以从三级缓存中找到singletonObject,并且最后调用addSingleton将三级缓存中的对象放入一级缓存。如下
1 2 3 4 5 6 7 8 protected void addSingleton (String beanName, Object singletonObject) { synchronized (this .singletonObjects) { this .singletonObjects.put(beanName, singletonObject); this .singletonFactories.remove(beanName); this .earlySingletonObjects.remove(beanName); this .registeredSingletons.add(beanName); } }
到这里我们的对象就初始化完成并且放入缓存中了。下图是整个对象的创建过程调用的方法链
本篇文章记录了AnnotationConfigApplicationContext的使用及bean的创建过程,方便以后回顾总结。后面有时间希望看看Spring如何使用三级缓存解决循环依赖问题。