- Spring 的核心
- Bean 组件
- Spring Bean 工厂生产 Bean 时
- Context 组件
- Core组件
本文并不是博主原创的,在博主学习Spring底层原理时,看到本篇文章感觉收益颇丰,读了好几遍,每一遍都有不同的体会,所以想通过博客记录一下。
若有侵权立删。 Spring 的核心
任何实际的应用程序都是由很多组件组成的,每个组件负责整个应用功能的一部分,这些组件需要与其他的应用元素进行协调以完成自己的任务。当应用程序运行时,需要以某种方式创建并引入这些组件。
Spring framework 总共有十几个组件,但真正核心的组件只有三个:Spring Core,Spring Context 和 Spring Bean,它们奠定了 Spring 的基础并撑起了 Spring 的框架结构。Spring 的其它功能特性例如 Web、AOP、JDBC 等都是在其基础上发展实现的。
Spring之中最重要的当属Bean了,Spring实际上就是面向Bean的编程,Bean对于Spring的意义就好比Object对于OOP的意义一样。那么,三个核心组件之间是如何协同工作的呢?如果把Bean比作一场演出中的演员,那么Context就是这场演出的舞台,Core就是演出的道具,至于演出的节目,就是Spring的一系列特色功能了。
我们知道Bean包裹的是Object,而Object中必然有数据,Context就是给这些数据提供生存环境,发现每个Bean之间的关系,为他们建立并维护好这种关系。这样来说,Context就是一个Bean关系的集合,这个关系集合就是我们所说的IOC容器。那么Core又有什么作用呢?Core就是发现、建立和维护每个Bean之间的关系所需的一系列工具,就是我们经常说的Util。
Bean 组件Bean组件在Spring的org.springframework.beans包下,主要完成了Bean的创建、Bean的定义以及Bean的解析三件事。
SpringBean的创建是典型的工厂模式,其工厂的继承层次关系如图所示:
Spring 使用工厂模式来管理程序中使用的对象(Bean),Bean 工厂最上层的接口为 BeanFactory,简单来看,工厂就是根据需要返回相应的 Bean 实例。
public interface BeanFactory { //... Object getBean(String name); }
在工厂模式中,在工厂的实现类中生成 Bean 返回给调用客户端,这就要求客户端提供生成自己所需类实例的工厂类,增加客户负担。Spring 结合控制反转和依赖注入为客户端提供所需的实例,简化了客户端的 *** 作。具体的实现方式大致如下。
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { private final MapbeanDefinitionMap = new ConcurrentHashMap ; public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition){ //... } }
beanDefinitionMap 作为具体的 Bean 容器,Spring 创建的对象实例保存其中。客户端需要时,使用工厂的 getBean 方法去试图得到相应的实例,如果实例已存在,则返回该实例;如果实例不存在,则首先产生相应实例并通过 registerBeanDefinition 方法将其保存在 beanDefinitionMap 中(Lazy Initialization),然后返回该实例给客户端。
beanDefinitionMap 并不直接保存实例本身,而是将实例封装在 BeanDefinition 对象后进行保存。BeanDefinition 包含了实例的所有信息,其简化版的定义如下。
public class BeanDefinition { private Object bean; private Class> beanClass; private String beanClassName; // Bean 属性字段的初始化值 private BeanPropertyValues beanPropertyValues; //... }Spring Bean 工厂生产 Bean 时
- 先将实例的类型参数保存到 beanClass 和 beanClassName,将需要初始化的字段名和值保存到beanPropertyValues 中,这个过程 Spring 通过控制反转来实现。
- 生成 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()); } }Context 组件
前面说到,Context组件的作用是给Spring提供一个运行时的环境,用以保存各个对象的状态,我们来看一下与Context相关的类结构图。
从图中可以看出,Context类结构的顶级父类是ApplicationContext,它除了能标识一个应用环境的基本信息以外,还继承了5个接口,这5个接口主要是扩展了Context的功能。ApplicationContext的子类主要包含两个方向,图中已作说明。再往下就是构建Context的文件类型,接着就是访问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 容器进行管理,并指定其初始化参数。配置文件即资源,其内容如下。
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 { private SampleService sampleService; public SampleController(SampleService sampleService){ this.sampleService = sampleService; } //无参构造函数 public SampleController(){} // 类的核心功能 public void process(){ System.out.println(sampleService.getService()); } }
三种注入方式在配置文件中对应不同的配置方式,在前面 xml 文件的基础上,我们可以分别实现这三种注入方式。需要注意的是,这里 SampleController 也是使用 Spring 的 Ioc 容器生成管理的。
Core组件-->
Core组件一个重要的组成部分就是定义了资源的访问方式。Core组件把所有的资源都抽象成一个接口,这样,对于资源使用者来说,不需要考虑文件的类型。对资源提供者来说,也不需要考虑如何将资源包装起来交给别人使用(Core组件内所有的资源都可以通过InputStream类来获取)。另外,Core组件内资源的加载都是由ResourceLoader接口完成的,只要实现这个接口就可以加载所有的资源。
那么,Context和Resource是如何建立关系的呢?通过前面Context的介绍我们知道,Context组件里面的类或者接口最终都实现了ResourcePatternResolver接口,ResourcePatternResolver接口的作用就是加载、解析和描述资源。这个接口相当于Resource里面的一个接头人,它把Resource里的资源加载、解析和定义整合到一起,便于其他组件使用。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)