spring容器相关阅读
本文基于spring 1.0 版本分析。我们将学习所有继承spring 容器的相关类。因为容器的类错综复杂, 要搞清关系,首先要找出beanFactory 比较底层的子类,就是beanFacotry的实现类,然后通过继承图分析该类的功能和使用方法。我们每次学习一个类,学习的步骤如下
- 分析底层容器的继承
- 代码实战
- 分析spring 容器api的具体实现。
- 小结
通过循序渐进的学习,大家会发现spring 也是很简单的东西, 一起来吧。
一. StaticListableBeanFactory
1. 继承分析
继承图如下
该类主要是提供允许以编程方式注册现有单例实例, 这个类的static 可以忽略。
1. BeanFactory
这个类不实详细说了, 是spring的顶级容器接口。下面是接口的方法,考虑到能学习spring 源码的人肯定会用到这几个接口,这里就不细说了。
2. ListableBeanFactory
由于一个实现可以由多个工厂BeanFactory实现(拥有多种创建方式),这个主要提供一个实例的的所有工厂类。 下面是有关ListableBeanFactory的接口方法。
getBeanDefinitionCount()
返回容器里面存在的bean的数量。不考虑使用层次工厂所创建的类。层次工厂是一种创建bean 的方式,为什么不会考虑使用层次工厂创建的类,后面会详细的说。getBeanDefinitionNames()
返回此工厂中定义的所有 bean 的名称。不考虑使用层次工厂所创建的类。getBeanDefinitionNames(Class type)
使用Class类型过滤,然后返回匹配给定类型(包括子类)的 bean 的名称。 不考虑使用层次工厂所创建的类。containsBeanDefinition(String name)
通过bean的名字判断,容器里面是否包含某个beanMap getBeansOfType(Class type, boolean includePrototypes, boolean includeFactoryBeans) throws BeansException
第一个class指定了Class精英,通过Class类型获取Bean,第二个参数: 是否也包含原型 bean 或仅包含单例(也适用于 FactoryBeans), 第三个参数表示是否包含工厂bean。
3. StaticListableBeanFactory
public void addBean(String name, Object bean)
StaticListableBeanFactory 里面增加bean的一种方法。
通过继承分析, 我们大概知道了StaticListableBeanFactory的具体功能。
2. 代码实战
既然我们已经知道了StaticLisable的功能,下面写写代码玩一玩。 现在我们假设有个学生和人物的对象,我们将这两个bean放入容器试一试相关api 功能。 在看代码的时候,记得思考一下答案。
public void testStaticListableBeanFactory() {
Person person1 = new Person("person1");
Person person2 = new Person("person2");
Student student1 = new Student("student1");
Student student2 = new Student("student2");
StaticListableBeanFactory slb = new StaticListableBeanFactory();
slb.addBean("person1", person1);
slb.addBean("person2", person2);
slb.addBean("student1", student1);
slb.addBean("student2", student2);
// 1. 首先试一试 BeanFactory 里面的功能
System.out.println(((Person) (slb.getBean("person1"))).name);
System.out.println(((Person) slb.getBean("person2", Person.class)).name);
System.out.println("容器里面是否包含person1对象:" + slb.containsBean("person1"));
System.out.println("person1是否是单例对象:" + slb.isSingleton("person1"));
// 2. 试一试 ListableBeanFactory 的功能
System.out.println("bean 定义的数量:" + slb.getBeanDefinitionCount());
System.out.println("该容器中所有bean的:" + Arrays.toString(slb.getBeanDefinitionNames()));
// 这个方法功能不就是和 BeanFactory.containsBean一样吗?
System.out.println("容器是否包含Person:" + slb.containsBeanDefinition("person1"));
// 查找所有的Person类型
Map<String, Object> beansOfType = slb.getBeansOfType(Person.class, false, false);
for (Map.Entry<String, Object> se : beansOfType.entrySet()) {
System.out.println(se.getKey() + ":" + ((Person) se.getValue()).name);
}
}
控制台打印如下:
person1
person2
容器里面是否包含person1对象:true
person1是否是单例对象:true
bean 定义的数量:4
该容器中所有bean的:[Ljava.lang.String;@7e0b37bc
容器是否包含Person:true
person2:person2
person1:person1
3. 源码分析
通过在前面的分析,大概可以猜想出StaticListableBeanFactory 里面在执行add() 方法的时候保存了bean和Type的映射关系。
3.1 addBean(String name, Object bean)
public void addBean(String name, Object bean) {
this.beans.put(name, bean);
}
该方法非常简单,直接将bean的名字作为key,实例对象作为value 放入了map里面去
3.2 public Object getBean(String name)
@Override
public Object getBean(String name) throws BeansException {
Object bean = this.beans.get(name);
if (bean instanceof FactoryBean) {
try {
return ((FactoryBean) bean).getObject();
} catch (Exception ex) {
throw new BeanCreationException("FactoryBean threw exception on object creation", ex);
}
}
if (bean == null)
throw new NoSuchBeanDefinitionException(name, "defined beans are [" +
StringUtils.collectionToCommaDelimitedString(this.beans.keySet()) + "]");
return bean;
}
首先通过实例对象的名字去map里面查找bean, 并且判断bean是否是FactoryBean ,FactoryBean是一个可以获取bean对象的bean,是一个实例,假如是FactoryBean 通过getObject() 方法获取实例对象即可, 并且通过该接口可以判断是否是单例bean。如果bean 等于空将会抛出异常,否则直接将bean 返回。
3.3 public Object getBean(String name, Class requiredType)
@Override
public Object getBean(String name, Class requiredType) throws BeansException {
Object bean = getBean(name);
if (!requiredType.isAssignableFrom(bean.getClass())) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean);
}
return bean;
}
通过名字查找bean, 再通过requiredType判断bean的接口是否是requiredType, 或者bean的类型是否是requiredType类型。 因为spring 的名字不能重复, 代码比Google juice 简单多了。
3.4 public boolean containsBean(String name)
@Override
public boolean containsBean(String name) {
return this.beans.containsKey(name);
}
不解释,相信都能看懂
3.5 public boolean isSingleton(String name)
@Override
public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
Object bean = getBean(name);
// in case of FactoryBean, return singleton status of created object
if (bean instanceof FactoryBean) {
return ((FactoryBean) bean).isSingleton();
} else {
return true;
}
}
如果不是beanfactory 肯定是单例,因为spring 名字不能重复。 如果是FactoryBean 的实现类的话,那么就需要使用 isSingleton() 方法
判断。
3.6 public int getBeanDefinitionCount()
这个方法不用解释了吧? 就是返回容器里面bean的数量
3.7 public String[] getBeanDefinitionNames()
@Override
public String[] getBeanDefinitionNames() {
return (String[]) this.beans.keySet().toArray(new String[this.beans.keySet().size()]);
}
将map对象的所有的key作为返回
3.8 getBeanDefinitionNames
@Override
public String[] getBeanDefinitionNames(Class type) {
List matches = new LinkedList();
Set keys = this.beans.keySet();
Iterator itr = keys.iterator();
while (itr.hasNext()) {
String name = (String) itr.next();
Class clazz = this.beans.get(name).getClass();
if (type.isAssignableFrom(clazz)) {
matches.add(name);
}
}
return (String[]) matches.toArray(new String[matches.size()]);
}
指定一个type类型,然后遍历容器里面的所有的bean, 如果bean的接口是type类型或者,bean的类型就是type类型,将bean加入到list 里面,最后返回。
3.9 public Map getBeansOfType(Class type, boolean includePrototypes, boolean includeFactoryBeans)
public Map getBeansOfType(Class type, boolean includePrototypes, boolean includeFactoryBeans) {
Map matches = new HashMap();
Set keys = this.beans.keySet();
Iterator itr = keys.iterator();
while (itr.hasNext()) {
String name = (String) itr.next();
Object bean = this.beans.get(name);
// 判断要查找的bean是否是factoryBean
if (bean instanceof FactoryBean && includeFactoryBeans) {
FactoryBean factory = (FactoryBean) bean;
Class objectType = factory.getObjectType();
// 这里的getObjectType 要说明一下, 单例bean 如何通过FactoryBean 创建他的类型可能是空,所以这里判断是objectType == null
if ((objectType == null && factory.isSingleton()) ||
// includePrototypes 表示该类是实例类型的原型类(比如A extend B ,这里要查找A类型对象,includePrototypes 表示type就是A.class,而不是B.class, includePrototypes 和下面的
((factory.isSingleton() || includePrototypes) && type.isAssignableFrom(objectType)) 是等价的。
// 这里表示FactoryBean 返回的对象类型就是我们要查找的对象类型,满足条件。
objectType != null && type.isAssignableFrom(objectType))) {
Object createdObject = getBean(name);
if (type.isInstance(createdObject)) {
matches.put(name, createdObject);
}
}
} else if (type.isAssignableFrom(bean.getClass())) {
matches.put(name, bean);
}
}
return matches;
}
3. 小结
我们可以知道StaticLisableBeanFactory 就是实现了BeanFactory,和ListableBeanFactory, 新增了添加Bean 的方法,和static 没有关系。 这是个历史遗留问题, 大家记得关注重点唷。
二. XmlBeanFactory
1. 继承分析
1. HierarchicalBeanFactory
BeanFactory getParentBeanFactory
该接口类只有一个方法, 就是获取父亲工厂。
层次BeanFactory, 这个是什么意思? 在spring 处理依赖的时候比如 A和B 两个实例对象, 下面有两种处理的方法
直接将A和B的实例对象增加到同一个容器里面。
A和B 的实例对象不在一个容器里面。
XmlBeanFactory xbf = new XmlBeanFactory(new ClassPathResource("autowire.xml", getClass()));
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.addPropertyValue("name", "kerry");
lbf.registerBeanDefinition("spouse", new RootBeanDefinition(TestBean.class, pvs));
xbf.setParentBeanFactory(lbf);
DependenciesBean rod1 = (DependenciesBean) xbf.getBean("rod1");
TestBean kerry = (TestBean) xbf.getBean("spouse");
// Should have been autowired
assertEquals(kerry, rod1.getSpouse()); DependenciesBean 类型里面依赖TestBean , 但是我们不直接创建TestBean的对象,而是将TestBean封装成BeanDefinition,然后注册到Bean工厂。 然后将Bean工厂作为被依赖类的父类工厂, 也就是说如果当前容器找不到实例对象, 需要去从父类里面找。
总之一句话 HierarchicalBeanFactory 就是将Bean 放在不同的容器里面,如果在当前容器找不到就去parent容器里面查找。 既然有获取父类工厂就没有设置父类工厂的功能, 那么就是下面 ConfigurableBeanFactory 接口, 并且ConfigurableBeanFactory 也继承了HierarchicalBeanFactory . 这两个类要一直记一下主要是实现了parentBeanFactory的功能。
2. ConfigurableBeanFactory
名字翻译过来就是可配置的工厂, 这个工厂很一些需要的功能,下面来看看到底有哪些功能?
1. setParentBeanFactory(BeanFactory parentBeanFactory);
设置父类bean工厂
2. registerCustomEditor(Class requiredType, PropertyEditor propertyEditor)
举个例子
TestBean tb = new TestBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
bw.registerCustomEditor(String.class, null, new PropertyEditorSupport() {
public void setAsText(String text) throws IllegalArgumentException {
setValue("prefix" + text);
}
});
try {
bw.setPropertyValue("name", "value");
bw.setPropertyValue("touchy", "value");
}
catch (BeansException ex) {
fail("Should not throw BeansException: " + ex.getMessage());
}
assertEquals("prefixvalue", bw.getPropertyValue("name"));
当调用setPropertyValue的时候将会执行setValue 方法,通过这个方法在name的加上一个前缀, 先前讲到 factoryBean 就是一个工厂bean, spring 将 factoryBean 的名字和普通实例类的名字做了区分, 如果是FactoryBean的话,会在前缀上面加上 &
, spring 也就是用registerCustomEditor 做到的。 比如StudentFactoryBean的名字是studentFactoryBean, 那么增加到
- ClassPathXmlApplicationContext
- FileSystemXmlApplicationContext
- StaticWebApplicationContext
- XmlWebApplicationContext
- ComplexWebApplicationContext
- SimpleWebApplicationContext