本文属于关于Spring原理的深入学习,旨在帮助读者更好地理解Spring。
目录
BeanFactory和ApplicationContext
BeanFactory和ApplicationContext
这是一个简单的启动类
@SpringBootApplication
public class SpringCodeHhApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(SpringCodeHhApplication.class, args);
System.out.println(run);
}
}
点击run方法可以看到返回值是ConfigurableApplicationContext
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
进入类图可以看到ConfigurableApplicationContext是实现自BeanFactory。
执行:
ConfigurableApplicationContext run = SpringApplication.run(SpringCodeHhApplication.class, args);
System.out.println(run);
可以看到context中包含了beanFactory中的singletonObjects,
得出了结论:在ConfigurableApplicationContext 加载完成,对应的beanFactory里面的singletionObjects也加载完成了。这个就是ApplicationContext的一个很大的优势,实现了自动加载。所以得出结论,BeanFactory是 ApplicationContext 的父接口并且它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能
来到BeanFactory接口中:
BeanFactory支持的方法里有很多getBean方法,虽然表面上只有getBean,但实际上控制反转、基本的依赖注入,直至Bean的生命周期的各种功能,其实都由其实现类提供。
Application的几个功能介绍
MessageSource,ResourcePatternResolver,EnvironmentCapable,ApplicationEventPublisher四个接口是Application组合并扩展了BeanFactory 的功能,MessageSource用来处理国际化资源,ResourcePatternResolver能够通过磁盘路径找文件,EnvironmentCapable是用来读取spring中配置文件的功能,ApplicationEventPublisher用来发布事件对象。
用代码完成:
@SpringBootApplication
public class SpringCodeHhApplication {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext run = SpringApplication.run(SpringCodeHhApplication.class, args);
System.out.println(run);
//MessageResource
System.out.println(run.getMessage("hi", null, Locale.CHINA));
System.out.println(run.getMessage("hi", null, Locale.ENGLISH));
System.out.println(run.getMessage("hi", null, Locale.JAPANESE));
System.out.println(run.getEnvironment().getProperty("java_home"));
System.out.println(run.getEnvironment().getProperty("server.port"));
Resource[] resources = run.getResources("classpath*:META-INF/spring.factories");
for (Resource resource : resources) {
System.out.println(resource);
}
System.out.println(run.getEnvironment().getProperty("java_home"));
System.out.println(run.getEnvironment().getProperty("server.port"));
// context.publishEvent(new UserRegisteredEvent(context));
run.getBean(Component1.class).register();
}
}
Bean Factory
由以下的类图可得,DefaultListableBeanFactory是Bean Factory的最重要的实现类
使用DefaultListableBeanFactory就可以创建一个核心的Spring容器,不过刚创建好的beanFactory没有任何bean。
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
}
bean的注册
加入bean的定义:class,scope(单例,多例),初始化,销毁等描述信息。我们在这加入一组配置,并打印已经注册好的bean。
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义(class, scope, 初始化, 销毁)
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config", beanDefinition);
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@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()");
}
}
}
发现只有config被注册进去了,但通常来说加了@Configuration注解和@Bean注解的情况下,Bean1和Bean2不也应该被注册吗? 所以此处的BeanFactory缺少了解析注解的能力,那这些能力是谁提供的呢?
我们需要给Bean Factory加入一些常用的后处理器,这些处理器具备Bean Factory的扩展功能,可以来解析@Bean注解等
// 给 BeanFactory 添加一些常用的后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// BeanFactory 后处理器主要功能,补充了一些 bean 定义
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
可以看到我们添加了@Bean注解的bean已经被注册了,我们可以通俗的把beanFactory后处理器理解为他们也是一个bean,我们要先执个这些bean,才能够解析@configuration的注解,再把@Bean定义的bean加入 到容器中。
bean的依赖注入
在Bean1中用@Autowired进行了依赖注入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;
}
}
但是通过以下getBean的方法却获取到Bean2,证明了Bean Factory没有进行依赖注入的功能,依赖注入对Bean Factory来说也是一个扩展功能
beanFactory.getBean(Bean1.class).getBean2();
其实,我们可以用Bean的后处理器进行依赖注入的处理,下图的internalConfigurationAnnotationProcessor就是用来处理@Autowired或者@Value注解的。而internalCommonAnnotationProcessor是用来处理@Resource注解的。Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...
进行Bean后处理器的注册,类似于BenFactory。
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
beanFactory.getBean(Bean1.class).getBean2();
现在可以看到Bean2顺利地实现了依赖注入。
饿汉式,懒汉式的切换创建单例
默认情况是延迟创建单例,即当我们要用到的时候才去创建
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> "); //用这个来观察单例创建的时机。
beanFactory.getBean(Bean1.class).getBean2();
当加入beanFactory.preInstantiateSingletons(); 就代表一开始就创建单例,不要等到要用到的时候才去创建。
beanFactory.preInstantiateSingletons(); // 准备好所有单例
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ");
beanFactory.getBean(Bean1.class).getBean2();
从以下的执行结果来看,就可以看到已经提前示例好了这些单例对象。
可以得到结论:BeanFactory 不会主动调用 BeanFactory 后处理器,不会主动添加 Bean 后处理器,不会主动初始化单例。
Application
本节讲解几个Application中常见的方法实现注册Bean。
1.类路径下的xml注入bean
private static void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("a02.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
//xml文件
<!-- 控制反转, 让 bean1 被 Spring 容器管理 -->
<bean id="bean1" class="com.wyw.springcodehh.a02.A02.Bean1"/>
<!-- 控制反转, 让 bean2 被 Spring 容器管理 -->
<bean id="bean2" class="com.wyw.springcodehh.a02.A02.Bean2">
<!-- 依赖注入, 建立与 bean1 的依赖关系 -->
<property name="bean1" ref="bean1"/>
</bean>
2.绝对路径下的文件名注入bean
private static void testFileSystemXmlApplicationContext() {
FileSystemXmlApplicationContext context =
new FileSystemXmlApplicationContext(
"D:\\BaiduNetdiskDownload\\SpringCode\\SpringCodeHH\\src\\main\\resources\\a02.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
以上两种方式是怎么执行的呢,我们用下述代码探究
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取之前...");
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println("读取之后...");
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// reader.loadBeanDefinitions(new ClassPathResource("a02.xml")); 类路径
reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\b01.xml"));
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
其实就是用 XmlBeanDefinitionReader来进行注入。
3.基于java配置类注入bean
private static void testAnnotationConfigApplicationContext() {
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;
}
}
static class Bean1 {
}
static class Bean2 {
private Bean1 bean1;
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
public Bean1 getBean1() {
return bean1;
}
}
从执行结果来看,bean1和bean2都执行成功,还有bean2依赖注入的bean1也成功。
4.通过内嵌tomcat实现
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@Configuration
static class WebConfig {
@Bean
public ServletWebServerFactory servletWebServerFactory(){
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
//注册DispatcherServlet到Tomcat
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean("/hello")
public Controller controller1() {
return (request, response) -> {
response.getWriter().print("hello");
return null;
};
}
}