1.Java中创建对象的几种方式
Java作为面向对象语言的一种,有种说法是万物皆对象。我们的应用程序也是由一个个类或对象组成的,可以说对象是Java的灵魂。
对象并不会凭空出现,那么对象是怎么来的?你可能会说对象是new出来的,当然通多new调用构造器能够创建对象,那么是否有其他创建对象的方式呢?
答案肯定的,接下来简单介绍几种创建对象的方式(综合网络中的众多结果简单介绍,不做深入描述)
常说的五种创建对象的方式
- new创建,通过new关键字调用构造方法
- 反射创建对象,Class中的newInstance()方法和Constructor中的newInstance(),注意区分两者的区别。
- Object中的clone方法创建,记得实现Cloneable接口
- 反序列化创建,首先要有列化之后的对象资源,实现Serializable接口
- Unsafe.getUnsafe().allocateInstance(Class.class) rt.jar中sun.misc包中的native方法。
另外还有几种特殊的创建方式
- jdk动态代理 java.lang.reflect.Proxy
- cglib代理创建
- Objenesis类库 提供了多种创建对象的方式
上面列出了目前能用到的创建对象的几乎所有方式(如果还有其他的方式,基本原理应该也会与上面的类似)。
小结:Spring作为IOC容器,功能就是反转创建对象的控制权。所以了解创建对象的方式还是很有必要。具体Spring采用哪种方式创建对象,后续揭秘(当然是因为还没看到,哈哈)。
2.Java中的反射和内省
a. 先简单说一下反射
核心是构造Class(对任何对象的类的抽象描述),通过Class类的对象我们可以获取关于类更加细节的描述信息,如Constructor,Method,Field等。
这里说明一下Class中的newInstance方法,返回tmpConstructor.newInstance((Object[])null);可以看出调用Constructor中的无参构造器。
而Constructor中的newInstance方法,则调用的是ConstructorAccessor(package sun.reflect)接口定义的newInstance方法,
这些ConstructorAccessor实现中的newInstance方法:分两种
一种是NativeConstrutorAccessorImpl中的
一种是Unsafe中的
而newInstance方法实际调用了第一种实现,第二种Unsafe具体在什么情景下使用不再深究。
b.简单说一下内省
内省机制相关的主要的类或接口有:Introspector(用来操作BeanInfo),BeanInfo(定义了获取有关Bean信息的方法,bean,method,property,even等),FeatureDescriptor。
jdk提供了一个BeanInfo接口的简单实现SimpleBeanInfo,而查看源码我们发现SimpleBeanInfo并没有具体的操作,可以参考SimpleBeanInfo的一个子类Introspector.GenericBeanInfo。
可以看出内省机制的核心就是Introspector(内省),接下来简单介绍一下Introspector。
getBeanInfo()是Introspector的核心,用来获取bean,method,event,property对应的FeatureDescriptor子类,并构造出GenericBeanInfo。
其中bean和method的相对直观,重点看一下EventSetDescriptor esds[] = getTargetEventInfo();和 PropertyDescriptor pds[] = getTargetPropertyInfo();
简单来说,两者都是通过method实现的,
①在事件中通过add和remove前缀及Listener后缀,将一个方法约定为一个事件。
②在属性中通过get和set(boolean类型的is)作为前缀,构造PropertyDescriptor。
这就是为什么说内省是javabean的一种缺省表达。Class中只要有get,set方法,即使没有对应的属性,内省机制也会认为有一个属性(构造PropertyDescriptor)。
小结:内省机制是基于反射的,许多框架都使用了内省机制。内省机制可以让我们更多的关注方法,而不用过多的关注属性(这在面向接口编程的时候非常有用)。
3.Spring中的BeanFactory体系
程序设计是自顶向下的一个过程,而程序开发是自底向上的过程(研究源码也是如此)。
例如现在我们知道Spring的核心之一是BeanFactory,那么如何由BeanFactory(顶层接口)去分析BeanFactory体系的方法,应该是自底而上的通过具体的实现去了解分析整个体系。
可能我们通过一些资料也有所了解BeanFactory的底层实现是DefaultListableBeanFactory.而我们研究源码的首要问题是,我们如何去定位这个底层实现。
简单来说就是如何确定BeanFactory的实现,目前我所了解的方式就是通过查看API文档。如下所示
以前总结的学习源码的心得还是很有用的(查,看,跑,造,比,买)。在看源码之前一定要先查API。看看官方对一些概念知识的解释(网上的有些描述不太准确)是很有必要的。
从BeanFactory的介绍中我们看到,后续还要了解bean的定义和生命周期相关的知识。
结合上图我们可以清晰的看到BeanFactory的一些实现类(接口和ApplicationContext相关的后续研究)。
这里我们通过API定位出BeanFactory的三类实现:XmlBeanFactory,StaticListableBeanFactory,SimpleJndiBeanFactory.
a.StaticListableBeanFactory
查看StaticListableBeanFactory源码,我们知道这是一个基于 LinkedHashMap<String, Object>()的ListableBeanFactory单例实现,不支持原型模式和别名。
b.SimpleJndiBeanFactory
查看SimpleJndiBeanFactory源码,是基于JndiLocatorSupport的BeanFactory实现,并使用HashMap进行缓存处理。
这里需要注意,SimpleJndiBeanFactory不支持枚举的bean定义,因此没有实现ListableBeanFactory,而是直接实现的BeanFactory。
c.XmlBeanFactory/DefaultListableBeanFactory
接下来重点聊聊XmlBeanFactory系列(尽管XmlBeanFactory已经弃用了,作为学习研究还是很有必要的)。
c1. XmlBeanFactory是一个由XmlBeanDefinitionReader加载bean定义的DefaultListableBeanFactory。
c2. DefaultListableBeanFactory是一个基于bean定义的比较完善的bean工厂,是ListableBeanFactory和BeanDefinitionRegistry的默认实现。
在学习DefaultListableBeanFactory时,我们需要关注类中涉及到的BeanDefinition接口(描述bean的属性,构造参数和其他数据,接口中定义了scope和role)
,RootBeanDefinition类(更多的使用GenericBeanDefinition),AutowireCandidateResolver。
以下简单介绍DefaultListableBeanFactory系列的接口
c3. ConfigurableListableBeanFactory
此接口综合了ListableBeanFactory,AutowireCapableBeanFactory,ConfigurableBeanFactory并在此基础上扩展了对bean definitions的分析修改,以及预实例化单例bean的功能。
其中ListableBeanFactory是一种可以把所有bean列举出来的BeanFactory(而不是查找出一个结果)
AutowireCapableBeanFactory是一种可以对bean进行管理的BeanFactory,扩展了自动装配的功能。
ConfigurableBeanFactory是一个巨大的接口,继承了HierarchicalBeanFactory和SingletonBeanRegistry,并定义了许多bean相关的细节。
其他具体的实现类会在后续博文中介绍,当然相关的接口也会详细介绍。这里只是对BeanFactory体系作简单的介绍。
小结:我们可以看到,在BeanFactory的三类实现中:StaticListableBeanFactory和SimpleJndiBeanFactory相对简单稍作了解即可,DefaultListableBeanFactory是三个中最重要,最常用,也是最复杂的一种,后续会详细介绍。
4.总结
首先介绍了对象的创建,并在源码层面简单介绍了java的反射和内省机制(后续会用到这部分知识),而后基于BeanFactoryAPI自顶向下深挖实现,并以自底向上的方式分析BeanFactory体系。
本想着用一片文章介绍BeanFactory体系,但深入研究后发现这个体系有些庞大的过分,只能拆作几个部分介绍。
当然依然是按照本文的这种自底向上的分析方法:重点介绍DefaultListableBeanFactory核心分支体系中各个接口和实现。