Spring Framework 的核心组件有三个: Spring Core,Spring Context 和 Spring Beans,它们奠定了 Spring 的基础并撑起了 Spring 的框架结构。Spring 的其它功能特性例如 Web、AOP、JDBC 等都是在其基础上发展实现的。 
I. Bean 组件 
Spring 使用工厂模式来管理程序中使用的对象(Bean),Bean 工厂最上层的接口为 BeanFactory,简单来看,工厂就是根据需要返回相应的 Bean 实例。

public interface BeanFactory {
//...
Object getBean(String name);
}
  • 1
  • 2
  • 3
  • 4
  • 5

在工厂模式中,在工厂的实现类中生成 Bean 返回给调用客户端,这就要求客户端提供生成自己所需类实例的工厂类,增加客户负担。Spring 结合控制反转和依赖注入为客户端提供所需的实例,简化了客户端的操作。具体的实现方式大致如下。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>;
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition){
//...
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

beanDefinitionMap 作为具体的 Bean 容器,Spring 创建的对象实例保存其中。客户端需要时,使用工厂的 getBean 方法去试图得到相应的实例,如果实例已存在,则返回该实例;如果实例不存在,则首先产生相应实例并通过 registerBeanDefinition 方法将其保存在 beanDefinitionMap 中(Lazy Initialization),然后返回该实例给客户端。

Spring Bean 工厂的继承关系
Spring 中的相关代码包是 org.springframework.beans

beanDefinitionMap 并不直接保存实例本身,而是将实例封装在 BeanDefinition 对象后进行保存。BeanDefinition 包含了实例的所有信息,其简化版的定义如下。

public class BeanDefinition {
private Object bean; private Class<?> beanClass;
private String beanClassName; // Bean 属性字段的初始化值
private BeanPropertyValues beanPropertyValues; //...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Spring Bean 工厂生产 Bean 时 
1) 先将实例的类型参数保存到 beanClass 和 beanClassName,将需要初始化的字段名和值保存到 beanPropertyValues 中,这个过程 Spring 通过控制反转来实现,本文第二小节将予以简要说明 
2) 生成 bean 实例,并利用反射机制将需要初始化的字段值写入 bean 实例,将实例保存在 bean 中,完成 BeanDefinition 的构建。 
假设我们已经完成了步骤 1) 的操作,之后的过程用代码表述如下所示。

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition){
//生成 bean 实例,并完成初始化
Object bean = createBean(beanDefinition);
//将 bean 实例保存在 beanDefinition 中
beanDefinition.setBean(bean);
//将 beanDefinition 实例保存在 Spring 容器中
beanDefinitionMap.put(beanName, beanDefinition);
} protected Object createBean(BeanDefinition beanDefinition) {
try{
Object bean = beanDefinition.getBeanClass().newInstance();
try {
setBeanPropertyValues(bean, beanDefinition);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException e) {
e.printStackTrace();
}
return bean;
}catch(InstantiationException e){
e.printStackTrace();
}catch(IllegalAccessException e){
e.printStackTrace();
} return null;
} protected void setBeanPropertyValues(Object bean, BeanDefinition beanDefinition) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
for(PropertyValue pv : beanDefinition.getBeanPropertyValues().getBeanPropertyValues()){
Field beanFiled = bean.getClass().getDeclaredField(pv.getName());
beanFiled.setAccessible(true);
beanFiled.set(bean, pv.getValue());
}
}
  • 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

II. Context 组件 
一般地,传统的程序设计中,无论是使用工厂创建实例,或是直接创建实例,实例调用者都要先主动创建实例,而后才能使用。控制反转(Inverse of Control) 将实例的创建过程交由容器实现,调用者将控制权交出,是所谓控制反转。 
依赖注入(Dependence Injection) 在控制反转的基础上更进一步。如果没有依赖注入,容器创建实例并保存后,调用者需要使用 getBean(String beanName) 才能获取到实例。使用依赖注入时,容器会将 Bean 实例自动注入到完成相应配置的调用者,供其进一步使用。

Context 组件借助上述的控制反转和依赖注入,协助实现了 Spring 的 Ioc 容器。下面我们以一个 Service 类作为所需的 Bean 实例进行说明。实际应用中,我们会需要 Spring 管理很多 Bean 实例。

public class SampleService {
private String service;
public String getService() {
return service;
}
public void setService(String service) {
this.service= service;
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在程序运行过程中,需要一个 SampleService ,我们不让调用者 new 一个实例,而是在配置文件中表明该 SampleService 的实例交由 Spring 容器进行管理,并指定其初始化参数。配置文件即资源,其内容如下。

<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean name="sampleService " class="com.service.SampleService ">
<property name="service" value="This is a service"></property>
</bean>
</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Spring Core 组件提供 ResourceLoader 接口,便于读入 xml 文件或其他资源文件。其核心功能代码应该提供如下方法。

public class ResourceLoader {
public Resource getResource(String location){
URL resource = this.getClass().getClassLoader().getResource(location);
return new UrlResource(resource);
}
} // UrlResource 的功能代码
public class UrlResource implements Resource {
private final URL url; public UrlResource(URL url){
this.url = url;
} @Override
public InputStream getInputStream() throws IOException {
URLConnection urlConnection = url.openConnection();
urlConnection.connect();
return urlConnection.getInputStream();
} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

即加载资源文件,并以数据流的形式返回。Context 根据资源中的定义,生成相应的 bean 并保存在容器中,bean 的名字是 sampleService ,供程序进一步使用。这样就完成了控制反转的工作。接下来就需要把 sampleService 注入到需要使用它的地方,亦即完成依赖注入操作。 
现在假设 SampleController 中使用 SampleService 的对象,Spring 提供三种依赖注入的方式,构造器注入、setter 注入和注解注入。

public class SampleController {
/**
* 3. 注解注入
**/
/* @Autowired */
private SampleService sampleService; /**
* 1. 构造器注入
**/
public SampleController(SampleService sampleService){
this.sampleService = sampleService;
}
//无参构造函数
public SampleController(){} // 类的核心功能
public void process(){
System.out.println(sampleService.getService());
}
/**
* 2. setter 注入
**/
/*public void setService(SampleService service) {
this.service= service;
}*/
}
  • 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

三种注入方式在配置文件中对应不同的配置方式,在前面 xml 文件的基础上,我们可以分别实现这三种注入方式。需要注意的是,这里 SampleController 也是使用 Spring 的 Ioc 容器生成管理的。

<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean name="sampleService " class="com.service.SampleService ">
<property name="service" value="This is a service"></property>
</bean> <!-- 1. 构造器注入方式为SampleContorller 的 bean 注入 SampleService -->
<bean name="sampleContorller" class="com.controller.SampleContorller">
<!-- index 是构造方法中相应参数的顺序 -->
<constructor-arg index="0" ref="sampleService"></constructor-arg>
</bean> <!-- 2. setter 注入方式为SampleContorller 的 bean 注入 SampleService -->
<!--
<bean name="sampleContorller" class="com.controller.SampleContorller">
<property name="sampleService " ref="sampleService"></property>
</bean>
--> <!-- 3. 注解注入方式为SampleContorller 的 bean 注入 SampleService -->
<!--
<bean name="sampleContorller" class="com.controller.SampleContorller"> <!-- 不需要配置,Spring 自动按照类型注入相应的 bean -->
</bean>
-->
</beans>
  • 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

Spring 核心组件工作原理简析的更多相关文章

  1. Spring系列.@EnableRedisHttpSession原理简析

    在集群系统中,经常会需要将Session进行共享.不然会出现这样一个问题:用户在系统A上登陆以后,假如后续的一些操作被负载均衡到系统B上面,系统B发现本机上没有这个用户的Session,会强制让用户重 ...

  2. DHCP工作原理简析

    引言 DHCP是网络体系结构中应用层的一个重要协议,它可以帮助我们对要连接到互联网的计算机进行IP地址等信息的配置.本文从DHCP的原理出发,就DHCP的工作过程 进行详细的探讨. 主要报文 发现报文 ...

  3. Spring系列.AOP原理简析

    Spring AOP使用简介 Spring的两大核心功能是IOC和AOP.当我们使用Spring的AOP功能时是很方便的.只需要进行下面的配置即可. @Component @Aspect public ...

  4. virtIO之VHOST工作原理简析

    2017-07-19 一.前言 之前有分析过虚拟化环境下virtIO的实现,virtIO相关于传统的虚拟IO在性能方面的确提高了不少,但是按照virtIO虚拟网卡为例,每次虚拟机接收数据包的时候,数据 ...

  5. tomcat 工作原理简析

    https://github.com/HappyTomas/another-tutorial-about-java-web/blob/master/00-08.md 在00-02.理解HTTP中给出了 ...

  6. Java Android 注解(Annotation) 及几个常用开源项目注解原理简析

    不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...

  7. PHP的错误报错级别设置原理简析

    原理简析 摘录php.ini文件的默认配置(php5.4): ; Common Values: ; E_ALL (Show all errors, warnings and notices inclu ...

  8. Java Annotation 及几个常用开源项目注解原理简析

    PDF 版: Java Annotation.pdf, PPT 版:Java Annotation.pptx, Keynote 版:Java Annotation.key 一.Annotation 示 ...

  9. [转载] Thrift原理简析(JAVA)

    转载自http://shift-alt-ctrl.iteye.com/blog/1987416 Apache Thrift是一个跨语言的服务框架,本质上为RPC,同时具有序列化.发序列化机制:当我们开 ...

随机推荐

  1. Linux下使用expect实现跳板机自动跳转/免密登录/自动登录(转)

    shell脚本实现ssh自动登录远程服务器示例: #!/usr/bin/expect spawn ssh root@192.168.22.194 expect "*password:&quo ...

  2. iOS页面跳转及数据传递

    转: http://blog.csdn.net/wang9834664/article/details/8025571 iOS页面跳转: 第一种 [self.navigationController  ...

  3. Linux下使用GDB进行调试

    Linux下使用GDB进行调试的常用命令记于此. $ sudo su # g++ -g test.cpp -o test -pthread # gdb test         <------- ...

  4. &lt;LeetCode OJ&gt; 101. Symmetric Tree

    101. Symmetric Tree My Submissions Question Total Accepted: 90196 Total Submissions: 273390 Difficul ...

  5. [TypeScript] Use the TypeScript "unknown" type to avoid runtime errors

    The "any" type can be very useful, especially when adding types to an existing JavaScript ...

  6. Jfinal极速开发微信系列教程(三)--------------对JSP的支持以及部署Tomcat运行异常问题

    本文章主要解决以下问题: 1.Jfianl对JSP的支持2.Jfianl Maven项目部署到Tomcat,启动项目异常问题解决 第一个问题重现截图解决方案:1.在configConstant中添加视 ...

  7. Android下的数据存储与訪问 --- 以文件的形式

    Android下的数据存储与訪问 --- 以文件的形式 1.1 储存文件存放在手机内存中: // *** 储存数据到 /data/data/包名/files/jxn.txt文件里 String dat ...

  8. 使用Google-Authenticator加强serverSSH登录

    对于须要特殊加密的人群,我这里给出对应的方法来进行谷歌式加密. 过程例如以下: 准备: 首先在你的手机上准备好client(自己百度下载) 接下来依照命令做: date 查看系统时间       da ...

  9. MySQL 创建数据库及数据表

    1.创建数据库 (1) 命令行创建 [root@host]# mysqladmin -u root -p create RUNOOB Enter password:****** (2) php创建 语 ...

  10. IOS Exception2 this class is not key value coding-compliant for the key Click

    2015-06-16 23:00:53.706 MyIOSPackage[823:280049] *** Terminating app due to uncaught exception 'NSUn ...