使用Spring的IOC容器获取一个Bean的实质,其实可以简化成使用反射的一个过程。
这是一个Bean的定义。
package cn.edu.au.selection.service;
public class Student {
private String name;
private int age;
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
启动Spring的时候,也就是Spring初始化的时候。有经验的人都知道ApplicationContext的初始化过程主要是在AbstractApplicationContext
类中refresh()
方法中进行。而且该方法中最重要的两个子方法分别是ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
和finishBeanFactoryInitialization(beanFactory);
。
第一个方法的主要作用就是创建可供上下文使用的基本IOC容器,也就是DefaultListableBeanFactory
。由该IOC容器对我们所注册的BeanDefinition
以及Bean进行托管。在该IOC容器的初始化过程中,主要做了三件事:
1. `BeanDefinition`的`Resource`的定位。也就是说我们配置` `标签的Spring`*.xml`文件的定位。或是`FileSystemResource`,或是`ClassPathContextResource`。这些Resource其实就是对Xml文件的一个封装。
2. 当定位到`Resource`之后,便可以解析 `BeanDefinition`了。该`BeanDefinition`其实是对Bean定义的一个抽象。包含你定义的Bean的一些元数据、`ClassLoader`等等。该过程又可能分成两个步骤,第一步,进行Xml文件解析,该步只是将文件当成普通的Xml文件进行解析。而第二步就是Spring按照` `标签的定义对`BeanDefinition`进行解析生成`BeanDefinition`对象。这个对象由`AbstractBeanDefinition`指向,放在IOC容器中统一托管。
3. 第三步就是`BeanDefination`在IoC容器中的注册,也就是正式托管的过程。这个过程其实就是将`BeanDefination`放入`DefaultListableBeanFactory`的一个`ConcurrentHashMap`中,叫做`beanDefinitionMap`。供随后创建Bean的实体做准备。
注意:在第二个步骤中,不仅仅将*.xml
文件中定义的Bean的BeanDefinition
解析出来,而且将有
标签的,有@Component
等注解的那些Bean的BeanDefinition
也解析出来。当然还有Spring内部自己使用的一些Bean的BeanDefinition
。
第二个方法finishBeanFactoryInitialization(beanFactory);
的主要作用就是初始化所有剩余的非延迟加载(non-lazy-init)
的单例的Bean。注意,在这个方法的调用栈中就包含了我们会使用的ApplicationContext.getBean()
的调用。也就是说,其实所有Bean的创建都是在getBean()
方法中进行的。方法调用中有一个比较关键的方法就是AbstractBeanFactory.doGetBean()
方法。这个方法的逻辑路线是这样的。
Object sharedInstance = getSingleton(beanName);
方法查找。当使用getBean
方法的时候,首先也是在getSingleton(beanName)
方法中查找有没有之前创建好的实例。这一步如果没有找到,当然只能找到单例,prototype类型的肯定找不到,还有lazy-init
的也会找不到。BeanDefination
,从其中判断待获取的Bean是单例还是原型(prototype)
模式。如果是原型模式,每次创建一个Bean的实例即可。如果是单例模式,当第一次创建完实例之后需要保存到IOC容器中,第二次获取的时候就无需再次创建了。同样的,该单例Bean也是保存在DefaultListableBeanFactory
的另一个ConcurrentHashMap
中,叫做singletonObjects
。AbstractAutowiredCapableBeanFactory.doCreateBean()
方法时有两个关键的方法,分别是createBeanInstance()
和populateBean()
。其中第一个方法就是使用反射,调用类的构造方法来创建一个该类的实例对象。而第二个方法就是对该实例对象进行初始化,也就是实例中属性的初始化。有些@Autowired
或者@Value
的注解的属性的初始化都会在这里进行。并且有@Autowired
注解的属性还会触发递归调用创建Bean的方法。说白了,就是依赖关系处理的过程。initializeBean()
,这个方法中又有三个重要的方法,分别是applyBeanPostProcessorsBeforeInitialization()
,invokeInitMethods()
和applyBeanPostProcessorsAfterInitialization()
。其中含有@PostConstruct
注解的方法会在第一个方法中执行。第二个方法中一般执行BeanDefinition
中指定的init-method
方法或者afterPropertitiesSet()
实现方法。至此,Spring之IOC容器的初始化过程以及依赖注入都说完了。
那么创建Bean实例的过程可以简化成以下代码:
package cn.edu.au.selection.service;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection {
private static Logger logger = LoggerFactory.getLogger(Reflection.class);
public static void main(String[] args){
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class> clazz;
String className = "cn.edu.au.selection.service.Student";
Object object = null;
String name = "Ethan Hunt";
int age = 18;
try {
clazz = classLoader.loadClass(className);
Constructor>[] ctors = clazz.getDeclaredConstructors();
for (Constructor> constructor : ctors){
constructor.setAccessible(true);
object = constructor.newInstance();
}
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(object, name);
Method method = clazz.getDeclaredMethod("setAge", int.class);
method.setAccessible(true);
method.invoke(object, age);
} catch (ClassNotFoundException e) {
logger.error("class {} cannot be found!", className);
e.printStackTrace();
} catch (InvocationTargetException e) {
logger.error("被调用的方法{}的内部抛出了异常而没有被捕获!", "Student()/setAge()");
e.printStackTrace();
} catch (InstantiationException e) {
logger.error("{}类无法被实例化!", className);
e.printStackTrace();
} catch (IllegalAccessException e) {
logger.error("方法{}或属性{}无法被访问!", "setAge()", "name");
e.printStackTrace();
} catch (NoSuchFieldException e) {
logger.error("没有这样的属性{}!", "name");
e.printStackTrace();
} catch (NoSuchMethodException e) {
logger.error("没有这样的方法{}!", "setAge()");
e.printStackTrace();
}
logger.info(JSON.toJSONString(object));
}
}
输出结果为:
{
"age": 18,
"name": "Ethan Hunt"
}