【Spring】揭开Spring朦胧的面纱
2016-07-23 16:19
344 查看
使用Spring的IOC容器获取一个Bean的实质,其实可以简化成使用反射的一个过程。
注意:在第二个步骤中,不仅仅将
首先当初始化上下文的时候,非延迟加载的单例Bean先会通过
然后进行一个判断。首先从IOC容器中获取
在创建Bean实例,调用
在完成依赖关系处理之后,在该方法中接着会执行一个这个方法
那么创建Bean实例的过程可以简化成以下代码:
输出结果为:
首先
这是一个Bean的定义。Student 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`的定位。也就是说我们配置`<bean></bean>`标签的Spring`*.xml`文件的定位。或是`FileSystemResource`,或是`ClassPathContextResource`。这些Resource其实就是对Xml文件的一个封装。 2. 当定位到`Resource`之后,便可以解析 `BeanDefinition`了。该`BeanDefinition`其实是对Bean定义的一个抽象。包含你定义的Bean的一些元数据、`ClassLoader`等等。该过程又可能分成两个步骤,第一步,进行Xml文件解析,该步只是将文件当成普通的Xml文件进行解析。而第二步就是Spring按照`<bean></bean>`标签的定义对`BeanDefinition`进行解析生成`BeanDefinition`对象。这个对象由`AbstractBeanDefinition`指向,放在IOC容器中统一托管。 3. 第三步就是`BeanDefination`在IoC容器中的注册,也就是正式托管的过程。这个过程其实就是将`BeanDefination`放入`DefaultListableBeanFactory`的一个`ConcurrentHashMap`中,叫做`beanDefinitionMap`。供随后创建Bean的实体做准备。
注意:在第二个步骤中,不仅仅将
*.xml文件中定义的Bean的
BeanDefinition解析出来,而且将有
<context:component-scan/>标签的,有
@Component等注解的那些Bean的
BeanDefinition也解析出来。当然还有Spring内部自己使用的一些Bean的
BeanDefinition。
第二个方法
第二个方法finishBeanFactoryInitialization(beanFactory);的主要作用就是初始化所有剩余的非延迟加载
(non-lazy-init)的单例的Bean。注意,在这个方法的调用栈中就包含了我们会使用的
ApplicationContext.getBean()的调用。也就是说,其实所有Bean的创建都是在
getBean()方法中进行的。方法调用中有一个比较关键的方法就是
AbstractBeanFactory.doGetBean()方法。这个方法的逻辑路线是这样的。
首先当初始化上下文的时候,非延迟加载的单例Bean先会通过
Object sharedInstance = getSingleton(beanName);方法查找。当使用
getBean方法的时候,首先也是在
getSingleton(beanName)方法中查找有没有之前创建好的实例。这一步如果没有找到,当然只能找到单例,prototype类型的肯定找不到,还有
lazy-init的也会找不到。
然后进行一个判断。首先从IOC容器中获取
BeanDefination,从其中判断待获取的Bean是单例还是原型
(prototype)模式。如果是原型模式,每次创建一个Bean的实例即可。如果是单例模式,当第一次创建完实例之后需要保存到IOC容器中,第二次获取的时候就无需再次创建了。同样的,该单例Bean也是保存在
DefaultListableBeanFactory的另一个
ConcurrentHashMap中,叫做
singletonObjects。
在创建Bean实例,调用
AbstractAutowiredCapableBeanFactory.doCreateBean()方法时有两个关键的方法,分别是
createBeanInstance()和
populateBean()。其中第一个方法就是使用反射,调用类的构造方法来创建一个该类的实例对象。而第二个方法就是对该实例对象进行初始化,也就是实例中属性的初始化。有些
@Autowired或者
@Value的注解的属性的初始化都会在这里进行。并且有
@Autowired注解的属性还会触发递归调用创建Bean的方法。说白了,就是依赖关系处理的过程。
在完成依赖关系处理之后,在该方法中接着会执行一个这个方法
initializeBean(),这个方法中又有三个重要的方法,分别是
applyBeanPostProcessorsBeforeInitialization(),
invokeInitMethods()和
applyBeanPostProcessorsAfterInitialization()。其中含有
@PostConstruct注解的方法会在第一个方法中执行。第二个方法中一般执行
BeanDefinition中指定的
init-method方法或者
afterPropertitiesSet()实现方法。
创建Bean实例的简化过程
至此,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" }
相关文章推荐
- 一个jar包里的网站
- 一个jar包里的网站之文件上传
- 一个jar包里的网站之返回对媒体类型
- Spring和ThreadLocal
- Spring Boot 开发微服务
- Spring AOP动态代理-切面
- Spring整合Quartz(JobDetailBean方式)
- Spring整合Quartz(JobDetailBean方式)
- PHP strtotime函数用法、实现原理和源码分析
- jQuery 源码分析笔记(3) Deferred机制
- easyui window refresh 刷新两次的解决方法(推荐)
- jQuery 源码分析笔记(5) jQuery.support
- jQuery-1.9.1源码分析系列(十)事件系统之事件包装
- 模拟Spring的简单实现
- Spring整合WebSocket应用示例(上)
- spring+html5实现安全传输随机数字密码键盘
- Spring中属性注入详解
- 监听器获取Spring配置文件的方法
- Java利用Sping框架编写RPC远程过程调用服务的教程