容器接口
以一个SpringBoot项目启动来举例
public class Application{ public static void main(String[] args){ SpringApplication.run(Application.class); } }
当我们点击run进入到方法内部的时候
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class[]{primarySource}, args); }
实际上是有返回值的,返回值类型是ConfigurableApplicationContext,而当打开其类图结构时会发现是这样的:
Ctrl + Alt + U(打开类图的方式)
可以看到ConfigurableApplicationContext 是一个子接口,ApplicationContext是一个父接口,而BeanFactory是ApplicationContext的父接口。
而打印ConfigurableApplicationContext 类型的返回值时,会发现,其beanFactory里面有一个singletionObjects
其实也就是说 在ConfigurableApplicationContext 加载完成,对应的beanFactory里面的singletionObjects就也加载完成了。这个就是ApplicationContext的一个很大的优势,实现了自动加载。
通过上图也可以看出,BeanFactory 与 ApplicationContext 并不仅仅是简单接口继承的关系, ApplicationContext 组合并扩展了 BeanFactory 的功能,如MessageSource 处理国际化资源的能力、ResourcePatternResolver 通配符匹配资源的能力(磁盘路径 类路径找到的文件)、ApplicationEventPublisher(发布事件对象)、EnvironmentCapable(读取Spring中的环境信息,环境变量)
System.out.println(context.getMessage("hi", null, Locale.CHINA)); System.out.println(context.getMessage("hi", null, Locale.ENGLISH)); System.out.println(context.getMessage("hi", null, Locale.JAPANESE)); System.out.println("----------------------------------------------------------"); Resource[] resources = context.getResources("classpath*:META-INF/spring.factories"); for (Resource resource : resources) { System.out.println(resource); } System.out.println("----------------------------------------------------------"); System.out.println(context.getEnvironment().getProperty("java_home")); System.out.println(context.getEnvironment().getProperty("server.port")); System.out.println("----------------------------------------------------------"); context.getBean(Component1.class).register(); @Component public class Component1 { private static final Logger log = LoggerFactory.getLogger(Component1.class); @Autowired private ApplicationEventPublisher context; public void register() { log.debug("用户注册"); context.publishEvent(new UserRegisteredEvent(this)); } } @Component public class Component2 { private static final Logger log = LoggerFactory.getLogger(Component2.class); @EventListener public void aaa(UserRegisteredEvent event) { log.debug("{}", event); log.debug("发送短信"); } }
BeanFactory
当我们双击BeanFactory进入时,ctrl + f12发现:
发现表面上只有getBean,但实际上控制反转、基本的依赖注入,直至Bean的生命周期的各种功能,其实都由其实现类提供。
进入其实现类(ctrl + N)DefaultListableBeanFactory 发现 其impl 了 ConfigurableListableBeanFactory,而ConfigurableListableBeanFactory又impl了ListableBeanFactory。
比如说在代码里创建一个DefaultListableBeanFactory 对象 如下:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
注册一组配置进去
// bean 的定义(class, scope, 初始化, 销毁) AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition(); beanFactory.registerBeanDefinition("config", beanDefinition); @Configuration static class Config { @Bean public Bean1 bean1() { return new Bean1(); } @Bean public Bean2 bean2() { return new Bean2(); } } static class Bean1 { private static final Logger log = LoggerFactory.getLogger(Bean1.class); public Bean1() { log.debug("构造 Bean1()"); } @Autowired private Bean2 bean2; public Bean2 getBean2() { return bean2; } } static class Bean2 { private static final Logger log = LoggerFactory.getLogger(Bean2.class); public Bean2() { log.debug("构造 Bean2()"); } }
此时打印输出 beanFactory.getBeanDefinitionNames() 发现只有config被注册进去了,所以也就说明了@Configuration并没有解析,beanFactory缺少了解析 @bean等注解的能力,功能并不完整。
于是应该加入 一些常用的后置处理器(主要是配置注解相关的配置)、BeanFactoryPostProcessor、BeanPostProcessor:
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> { beanFactoryPostProcessor.postProcessBeanFactory(beanFactory); }); beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream() .forEach(beanPostProcessor -> { System.out.println(">>>>" + beanPostProcessor); beanFactory.addBeanPostProcessor(beanPostProcessor); });
此时打印发现能够正常的将 bean1 bean2打印出来,也就代表着bean1 bean2被注册进去了。到了这里其实能够发现,现在BeanFactory 当我们把这些后置处理器都加上之后已经和 ApplicationContext很像了,但是还不够,在前面ApplicationContext 加载完成,beanFactory里面的singletionObjects就也加载完成了,而我们这里显然并不能做到预加载,还是需要 System.out.println(beanFactory.getBean(Bean1.class).getBean2()); 才能够进行懒加载,而ApplicationContext已经实现了预加载,那么就应该添加这个:
beanFactory.preInstantiateSingletons(); // 准备好所有单例
此时,就能够实现预加载,而此时,BeanFactory已经变得和ApplicationContext 基本一样了。
调用顺序
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
这句前面提到了其实是添加一些 注解相关的配置,进入查看找到
def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
其中分别是解析Autowired 和 Resource注解的
其实在代码中可以看到是有明显的先后顺序的,如果我们在stream中sort排序,那么实际上是比较的AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor里面的 getOrder方法
所以Autowired 和 Resource的顺序还是有讲究的。
ApplicationContext
基于ApplicationContext的扩展有很多,但是正如前面对比的那样,不仅不需要像BeanFactory那样 引入Bean后置处理器和BeanFactory后置处理器,而且还能预加载,每一个Bean都在ApplicationContext启动之后被初始化。
如下所示:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("a02.xml"); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } System.out.println(context.getBean(Bean2.class).getBean1());
<!-- 控制反转, 让 bean1 被 Spring 容器管理 --> <bean id="bean1" class="com.itheima.a02.A02.Bean1"/> <!-- 控制反转, 让 bean2 被 Spring 容器管理 --> <bean id="bean2" class="com.itheima.a02.A02.Bean2"> <!-- 依赖注入, 建立与 bean1 的依赖关系 --> <property name="bean1" ref="bean1"/> </bean>
或者不使用配置类,使用注解的方式:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } System.out.println(context.getBean(Bean2.class).getBean1());
@Configuration static class Config { @Bean public Bean1 bean1() { return new Bean1(); } @Bean public Bean2 bean2(Bean1 bean1) { Bean2 bean2 = new Bean2(); bean2.setBean1(bean1); return bean2; } }
BeanFactory VS ApplicationContext
通过上面的分析,其实也就更能清晰的知道ApplicationContext强在哪里了:
- 自动的 BeanPostProcessor 注册。
- 自动的 BeanFactoryPostProcessor 注册。
- 方便的 MessageSource、ResourcePatternResolver、ApplicationEventPublisher、EnvironmentCapable实现类使用。
- ApplicationEvent 的发布与 BeanFactory 懒加载的方式不同,它是预加载,所以,每一个 bean 都在 ApplicationContext 启动之后实例化。