从今天开始,我们一起过一遍Spring的官网,本文主要涉及到官网中的1.2,1.3节。

Spring容器

容器是什么?

我们先看官网中的一句话:

The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans.

翻译下来大概就是: 1. Spring IOC容器就是一个org.springframework.context.ApplicationContext的实例化对象 2. 容器负责了实例化,配置以及装配一个bean

那么我们可以说:

从代码层次来看:Spring容器就是一个实现了ApplicationContext接口的对象

从功能上来看: Spring 容器是 Spring 框架的核心,是用来管理对象的。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。

容器如何工作?

我们直接看官网上的一张图片,如下:

Spring容器通过我们提交的pojo类以及配置元数据产生一个充分配置的可以使用的系统

这里说的配置元数据,实际上我们就是我们提供的XML配置文件,或者通过注解方式提供的一些配置信息

Spring Bean

如何实例化一个Bean?

从官网上来看,主要有以下三种方法

  1. 构造方法
  2. 通过静态工厂方法
  3. 通过实例工厂方法

这三种例子,官网都有具体的演示,这里就不再贴了,我们通过自己查阅部分源码,来验证我们在官网得到的结论,然后通过debug等方式进行验证。

我们再从代码的角度进行一波分析,这里我们直接定位到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance这个方法中,具体定位步骤不再演示了,大家可以通过形如下面这段代码:

ClassPathXmlApplicationContext cc =// 这里我们通过xml配置实例化一个容器new ClassPathXmlApplicationContext("classpath:application.xml");
MyServiceImpl luBan = (MyServiceImpl) cc.getBean("myServiceImpl");

直接main方法运行,然后在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance这个方法的入口打一个断点,如图:

接下来我们对这个方法进行分析,代码如下:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// 1.获取这个bean的class属性,确保beanDefinition中beanClass属性已经完成解析// 我们通过xml从<bean>标签中解析出来的class属性在刚刚开始的时候必定是个字符串Class<?> beanClass = resolveBeanClass(mbd, beanName);// 省略异常判断代码.....// 2.通过beanDefinition中的supplier实例化这个beanSupplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}// 3.通过FactoryMethod实例化这个beanif (mbd.getFactoryMethodName() != null) {return instantiateUsingFactoryMethod(beanName, mbd, args);}// 4.下面这段代码都是在通过构造函数实例化这个Bean,分两种情况,一种是通过默认的无参构造,一种                是通过推断出来的构造函数boolean resolved = false;boolean autowireNecessary = false;if (args == null) {synchronized (mbd.constructorArgumentLock) {if (mbd.resolvedConstructorOrFactoryMethod != null) {resolved = true;autowireNecessary = mbd.constructorArgumentsResolved;}}}if (resolved) {if (autowireNecessary) {return autowireConstructor(beanName, mbd, null, null);}else {return instantiateBean(beanName, mbd);}}// Candidate constructors for autowiring?Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);}// Preferred constructors for default construction?ctors = mbd.getPreferredConstructors();if (ctors != null) {return autowireConstructor(beanName, mbd, ctors, null);}// No special handling: simply use no-arg constructor.return instantiateBean(beanName, mbd);}

我们主要关注进行实例化的几个方法:

  1. 通过BeanDefinition中的instanceSupplier直接获取一个实例化的对象。这个instanceSupplier属性我本身不是特别理解,在xml中的标签以及注解的方式都没有找到方式配置这个属性。后来在org.springframework.context.support.GenericApplicationContext这个类中找到了以下两个方法

经过断点测试,发现这种情况下,在实例化对象时会进入上面的supplier方法。下面是测试代码:

public static void main(String[] args) {// AnnotationConfigApplicationContext是GenericApplicationContext的一个子类AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();ac.registerBean("service", Service.class,Service::new);ac.refresh();System.out.println(ac.getBean("service"));}

可以发现进入了这个方法进行实例化

这个方法一般不常用,平常我们也使用不到,就不做过多探究,笔者认为,这应该是Spring提供的一种方便外部扩展的手段,让开发者能够更加灵活的实例化一个bean。

  1. 接下来我们通过不同的创建bean的手段,来分别验证对象的实例化方法

通过@compent,@Service等注解的方式

测试代码:

public class Main {public static void main(String[] args) {// 通过配置类扫描AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);System.out.println(ac.getBean(Service.class));}
}@Component
public class Service {}

观察debug:

可以发现,代码执行到最后一行,同时我们看代码上面的注释可以知道,当没有进行特殊的处理的时候,默认会使用无参构造函数进行对象的实例化

  • 通过普通XML的方式(同@compent注解,这里就不赘诉了)
  • 通过@Configuration注解的方式

测试代码:

public class Main {public static void main(String[] args) {// 通过配置类扫描AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);// 这里将测试对象换为config即可,同时记得将条件断点更改为beanName.equlas("config")System.out.println(ac.getBean(config.class));}
}

同样,断点也进入最后一行

通过@Bean的方式

测试代码:

@Configuration
@ComponentScan("com.dmz.official")
public class Config {@Beanpublic Service service(){return new Service();}
}public class Main {public static void main(String[] args) {AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext(Config.class);System.out.println(ac.getBean("service"));}
}

断点结果:

可以发现,通过@Bean方法创建对象时,Spring底层是通过factoryMethod的方法进行实例化对象的。Spring会在我们需要实例化的这个对象对应的BeanDefinition中记录factoryBeanName是什么(在上面的例子中factoryBeanName就是config),同时会记录这个factoryBean中创建对象的factoryMethodName是什么,最后通过factoryBeanName获取一个Bean然后反射调用factoryMethod实例化一个对象。

这里我们需要注意几个概念:

  1. 这里所说的通过静态工厂方式通过factoryBeanName获取一个Bean,注意,这个Bean,不是一个FactoryBean。也就是说不是一个实现了org.springframework.beans.factory.FactoryBean接口的Bean。至于什么是FactoryBean我们在后面的文章会认真分析
  2. 提到了一个概念BeanDefinition,它就是Spring对自己所管理的Bean的一个抽象。不懂可以暂且跳过,后面有文章会讲到。

通过静态工厂方法的方式

测试代码:

public static void main(String[] args) {ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("application.xml");System.out.println(cc.getBean("service"));
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    <bean id="myServiceImpl" class="com.dmz.official.service.Service"/>--><!-- the factory bean, which contains a method called get() --><bean id="myFactoryBean" class="com.dmz.official.service.MyFactoryBean"><!-- inject any dependencies required by this locator bean --></bean><!-- 测试实例工厂方法创建对象--><bean id="clientService"factory-bean="myFactoryBean"factory-method="get"/><!--测试静态工厂方法创建对象--><bean id="service"class="com.dmz.official.service.MyFactoryBean"factory-method="staticGet"/>
</beans>

断点如下:

可以发现,这种情况也进入了instantiateUsingFactoryMethod方法中。通过静态工厂方法这种方式特殊之处在于,包含这个静态方法的类,不需要实例化,不需要被Spring管理。Spring的调用逻辑大概是:

  1. 通过<bean>标签中的class属性得到一个Class对象
  2. 通过Class对象获取到对应的方法名称的Method对象
  3. 最后反射调用Method.invoke(null,args)

因为是静态方法,方法在执行时,不需要一个对象。

通过实例工厂方法的方式

测试代码(配置文件不变):

public static void main(String[] args) {ClassPathXmlApplicationContext cc =new ClassPathXmlApplicationContext("application.xml");System.out.println(cc.getBean("clientService"));
}

断点如下:

还是执行的这个方法。这个方法的执行过程我断点跟踪了以后,发现跟@Bean方式执行的流程是一样的。这里也不再赘述了。

到这里,这段代码我们算结合官网大致过了一遍。其实还遗留了以下几个问题:

  1. Spring是如何推断构造函数的?我们在上面验证的都是无参的构造函数,并且只提供了一个构造函数
  2. Spring是如何推断方法的?不管是静态工厂方法,还是实例工厂方法的方式,我们都只在类中提供了一个跟配置匹配的方法名,假设我们对方法进行了重载呢?

要说清楚这两个问题需要比较深入的研究代码,同时进行测试。我们在官网学习过程中,暂时不去强求这类问题。这里提出来是为了在源码学习过程中,我们可以带一定目的性去阅读。

实例化总结

  1. 对象实例化,只是得到一个对象,还不是一个完全的Spring中的Bean,我们实例化后的这个对象还没有完成依赖注入,没有走完一系列的声明周期,这里需要大家注意
  2. Spring官网上指明了,在Spring中实例化一个对象有三种方式:
  3. 构造函数
  4. 实例工厂方法
  5. 静态工厂方法
  6. 我自己总结如下结论:

Spring通过解析我们的配置元数据,以及我们提供的类对象得到一个Beanfinition对象。通过这个对象可以实例化出一个java bean对象。主要流程如图:

这篇文章到这里就结束了,主要学习了Spring官网中的1.2,1.3两小节。下篇文章,我们开始学习1.4中的知识。主要涉及到依赖注入的一些内容,也是我们Spring中非常重要的一块内容哦!下篇文章再见!

如果想第一时间看到更多更新的技术文章,请关注微信公众号:1点25

http://weixin.qq.com/r/9zlvdzbEd2sIrXIU92zl (二维码自动识别)

如何通过一个字符串来实例化一个类_Spring官网阅读(一)容器及实例化相关推荐

  1. 编写一个方法参数接收一个字符串,返回一个Date对象(在多种日期格式中找到与字符串匹配的那一个)用到解析异常ParseException

    题目:编写一个方法参数接收一个字符串,返回一个Date对象(在多种日期格式中找到与字符串匹配的那一个)用到解析异常ParseException 具体代码如下: import java.text.Par ...

  2. Java黑皮书课后题第5章:*5.46(倒排一个字符串)编写一个程序,提示用户输入一个字符串,然后以反序显示该字符串

    5.46(倒排一个字符串)编写一个程序,提示用户输入一个字符串,然后以反序显示该字符串 题目 题目概述 破题 运行示例 代码 题目 题目概述 5.46(倒排一个字符串)编写一个程序,提示用户输入一个字 ...

  3. String案例 获取一个字符串在另一个字符串中出现的次数(两种方法)

    /*思路:定义1个计数器和1个截取后的Str-->判断有无SS-->有就截取并计数-->直到截取完没有ss就停止*/ package Day12;/* 练习2 获取一个字符串在另一个 ...

  4. 输入5个学生的名字(英文),使用冒泡排序按从大到小排序。 提示:涉及到字符串数组,一个字符串是一个一维字符数组;一个 字符串数组就是一个二维字符数组。...

    输入5个学生的名字(英文),使用冒泡排序按从大到小排序. 提示:涉及到字符串数组,一个字符串是一个一维字符数组:一个 字符串数组就是一个二维字符数组. #include <stdio.h> ...

  5. php判断网址包含字符,php中判断一个字符串包含另一个字符串的方法

    第一种方法:用php的strpos() 函数判断字符串中是否包含某字符串的方法if(strpos("www.yinxi.net","jb51") !== fal ...

  6. 获取一个字符串在另一个字符串中出现的次数

    题目说明: 获取一个字符串在另一个字符串中出现的次数.比如:获取"ab"在 "abkkcadkabkebfkaabkskab" 中出现的次数 public cl ...

  7. C语言编程>第二十一周 ⑥ 请补充main函数,该函数的功能是:从键盘输入一个字符串,即一个指定,然后把这个字符及其后面的所有字符全部删除,结果仍然保存在原串中。

    例题:请补充main函数,该函数的功能是:从键盘输入一个字符串,即一个指定,然后把这个字符及其后面的所有字符全部删除,结果仍然保存在原串中. 例如:输入"1234567",指定字符 ...

  8. 7-20 判断两个字符串是否为变位词 (10 分)如果一个字符串是 另一个字符串的重新排列组合,那么这两个字符串互为变位词。比如,”heart”与”earth”互为变位 词,”Mary”与”arMy

    7-20 判断两个字符串是否为变位词 (10 分) 如果一个字符串是 另一个字符串的重新排列组合,那么这两个字符串互为变位词.比如,"heart"与"earth" ...

  9. R7-如果一个字符串是 另一个字符串的重新排列组合,那么这两个字符串互为变位词。比如,”heart”与”earth”互为变位 词,”Mary”与”arMy”也互为变位词。输入格式:第一行输入第一个字符

    如果一个字符串是 另一个字符串的重新排列组合,那么这两个字符串互为变位词.比如,"heart"与"earth"互为变位 词,"Mary"与& ...

  10. php7 list explode,使用一个字符串分割另一个字符串 - PHP 7 中文文档

    (PHP 4, PHP 5, PHP 7) explode – 使用一个字符串分割另一个字符串 说明 explode ( string $delimiter , string $string [, i ...

最新文章

  1. Delphi十进制和十六进制互转 Delphi 自带函数 IntToHex
  2. 怎么抓取屏幕截图计算机考试时间,定时抓屏快照(电脑截屏工具)V4.8 最新版
  3. 电气与计算机学院院长论坛报告,我校电子系举办2019年电子信息学科院长论坛暨工程教育新进展研讨会...
  4. 水利水电工程与计算机技术应用,水利水电施工中计算机的应用
  5. python——argsort函数
  6. E - What Is Your Grade?
  7. logistic模型预测人口python_基于python的logistic回归建模预测
  8. ADB连接手机的三种方式USB、WLAN、WIFI
  9. 我是如何出版一本书的?
  10. 常见的股票量化交易软件主要有哪几种类型?
  11. scratch编程 超有趣反应力游戏
  12. php 广告法违禁词敏感词高效替换函数
  13. 斯皮尔曼等级相关系数 matlab,斯皮尔曼等级相关(matlab专题)
  14. jquery触发键盘按下事件
  15. 《冰与火之歌》七王国的骑士
  16. 《克隆人的进攻》面向对象Java版
  17. 乔治亚理工学院计算机专业,乔治亚理工学院计算机专业成功录取
  18. 未能猜测出图片名称??告诉你一个100%有用的搜图神器!
  19. centos7解决samba提示NT_STATUS_ACCESS_DENIED
  20. windows.h 详解

热门文章

  1. Mybatis中的update动态SQL语句
  2. 华为路由器isis配置实例_华为isis协议配置实例
  3. String:字符串常量池详解
  4. MySQL-在字段上使用函数不会走索引的原因是什么?
  5. 计算机网络的简单概述以及在浏览器中输入一个网址后如何执行
  6. P3317 [SDOI2014]重建
  7. p12文件和mobileprovision文件导入到xcode如何使用
  8. 马尾物联网企业主导或参与制定修订各级标准达60项
  9. 洛谷P2671 求和 数学 前缀和
  10. 【Java NIO的深入研究2】RandomAccessFile的使用