Spring 核心组件工作原理简析
2017-07-01 11:46
309 查看
Spring Framework 的核心组件有三个: Spring Core,Spring Context 和 Spring Beans,它们奠定了 Spring 的基础并撑起了 Spring 的框架结构。Spring 的其它功能特性例如 Web、AOP、JDBC 等都是在其基础上发展实现的。
I. Bean 组件
Spring 使用工厂模式来管理程序中使用的对象(Bean),Bean 工厂最上层的接口为 BeanFactory,简单来看,工厂就是根据需要返回相应的 Bean 实例。
在工厂模式中,在工厂的实现类中生成 Bean 返回给调用客户端,这就要求客户端提供生成自己所需类实例的工厂类,增加客户负担。Spring 结合控制反转和依赖注入为客户端提供所需的实例,简化了客户端的操作。具体的实现方式大致如下。
beanDefinitionMap 作为具体的 Bean 容器,Spring 创建的对象实例保存其中。客户端需要时,使用工厂的 getBean 方法去试图得到相应的实例,如果实例已存在,则返回该实例;如果实例不存在,则首先产生相应实例并通过 registerBeanDefinition 方法将其保存在 beanDefinitionMap 中(Lazy Initialization),然后返回该实例给客户端。
Spring Bean 工厂的继承关系
Spring 中的相关代码包是 org.springframework.beans
beanDefinitionMap 并不直接保存实例本身,而是将实例封装在 BeanDefinition 对象后进行保存。BeanDefinition 包含了实例的所有信息,其简化版的定义如下。
Spring Bean 工厂生产 Bean 时
1) 先将实例的类型参数保存到 beanClass 和 beanClassName,将需要初始化的字段名和值保存到 beanPropertyValues 中,这个过程 Spring 通过控制反转来实现,本文第二小节将予以简要说明
2) 生成 bean 实例,并利用反射机制将需要初始化的字段值写入 bean 实例,将实例保存在 bean 中,完成 BeanDefinition 的构建。
假设我们已经完成了步骤 1) 的操作,之后的过程用代码表述如下所示。
II. Context 组件
一般地,传统的程序设计中,无论是使用工厂创建实例,或是直接创建实例,实例调用者都要先主动创建实例,而后才能使用。控制反转(Inverse of Control) 将实例的创建过程交由容器实现,调用者将控制权交出,是所谓控制反转。
依赖注入(Dependence Injection) 在控制反转的基础上更进一步。如果没有依赖注入,容器创建实例并保存后,调用者需要使用 getBean(String beanName) 才能获取到实例。使用依赖注入时,容器会将 Bean 实例自动注入到完成相应配置的调用者,供其进一步使用。
Context 组件借助上述的控制反转和依赖注入,协助实现了 Spring 的 Ioc 容器。下面我们以一个 Service 类作为所需的 Bean 实例进行说明。实际应用中,我们会需要 Spring 管理很多 Bean 实例。
在程序运行过程中,需要一个 SampleService ,我们不让调用者 new 一个实例,而是在配置文件中表明该 SampleService 的实例交由 Spring 容器进行管理,并指定其初始化参数。配置文件即资源,其内容如下。
Spring Core 组件提供 ResourceLoader 接口,便于读入 xml 文件或其他资源文件。其核心功能代码应该提供如下方法。
即加载资源文件,并以数据流的形式返回。Context 根据资源中的定义,生成相应的 bean 并保存在容器中,bean 的名字是 sampleService ,供程序进一步使用。这样就完成了控制反转的工作。接下来就需要把 sampleService 注入到需要使用它的地方,亦即完成依赖注入操作。
现在假设 SampleController 中使用 SampleService 的对象,Spring 提供三种依赖注入的方式,构造器注入、setter 注入和注解注入。
三种注入方式在配置文件中对应不同的配置方式,在前面 xml 文件的基础上,我们可以分别实现这三种注入方式。需要注意的是,这里 SampleController 也是使用 Spring 的 Ioc 容器生成管理的。
参考文献 - Spring 框架的设计理念与设计模式分析
I. Bean 组件
Spring 使用工厂模式来管理程序中使用的对象(Bean),Bean 工厂最上层的接口为 BeanFactory,简单来看,工厂就是根据需要返回相应的 Bean 实例。
public interface BeanFactory { //... Object getBean(String name); }
在工厂模式中,在工厂的实现类中生成 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){ //... } }
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; //... }
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()); } }
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; } }
在程序运行过程中,需要一个 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>
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(); } }
即加载资源文件,并以数据流的形式返回。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; }*/ }
三种注入方式在配置文件中对应不同的配置方式,在前面 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>
参考文献 - Spring 框架的设计理念与设计模式分析
相关文章推荐
- Spring 核心组件工作原理简析
- Spring的工作原理核心组件和应用
- Spring3.0核心组件的源码简单分析
- Spring学习10-SpringMV核心组件2及SpringMVC项目示例
- spring源码:核心组件(li)
- Spring基础特性总结一--核心组件Bean的使用
- Spring核心组件详解(Bean、Context、Core)
- Spring 框架的的核心组件
- Spring核心三(基于注解的组件扫描)
- Spring核心技术(九)——Spring管理的组件和Classpath扫描
- Spring核心技术(九)——Spring管理的组件和Classpath扫描
- Spring Cloud构建微服务架构(五)服务网关 原创 2016-07-12 翟永超 Spring Cloud 被围观 53984 次 通过之前几篇Spring Cloud中几个核心组件的介
- spring核心组件
- Spring3.0核心组件的源码简单分析
- Spring核心组件-IOC的介绍
- Spring学习10-SpringMVC原理及核心组件1
- spring Namespace和核心组件简介
- Spring学习10-SpringMV核心组件2及SpringMVC项目示例
- Spring核心框架IOC---- 使用注解完成IOC配置---- 使用注解配置spring组件
- Struts2工作原理和核心组件