Spring系列之IOC的原理及手动实现
目录
导语
Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。也是几乎所有Java工作者必须要掌握的框架之一,其优秀的设计思想以及其代码实现上的艺术也是我们需要掌握的。要学习Spring,除了在我们的项目中使用之外,也需要对它的源码进行研读,但是Spring的实现涵盖的知识很多,在加上其中的类的数量也是非常的多,在我们阅读源码时可能会在几十个类之间穿插阅读,很有可能一不小心就导致思维混乱。有鉴于此,我这里先对Spring中的几个重要的模块进行一个手动的简易实现,一是熟悉这些模块的原理,同时也是仿造Spring中的结构来对后面阅读源码打下基础。
IOC(Inversion of Control)
Inversion of Control即控制反转,其意思是将我们之前由客户端代码来创建的对象交由IOC容器来进行控制,对象的创建,初始化以及后面的管理都由IOC完成。
IOC的好处
- 解耦:IOC的出现解决了类于类之间的耦合,我们在Web开发的Servlet时代,如果一个Servlet需要依赖另一个类的某些实现,那么我们需要在当前类对依赖的类进行创建和初始化,如果其他类也依赖了这个类,那也需要进行创建和初始化,而交给了IOC来管理的话,那么在需要的时候只需向IOC进行申请,而不需要重复的创建和初始化。当然,IOC也允许每次都重新创建一个新的对象。
- 方便与AOP进行配合:AOP也是一个使用十分频繁的功能,通过IOC可以十分方便的与AOP进行配合。
IOC中设计的设计模式
工厂模式。IOC容器来负责创建管理类实例对象,在需要时向IOC进行申请,从IOC中获取。所以IOC容器也称为bean工厂。
工厂模式是一种比较简单易懂的设计模式,这里就不在介绍了,如果有需要的可以看看这个:工厂模式。
IOC的手动实现
Bean定义
IOC的主要的功能便是对Bean进行管理,包括创建、初始化、管理以及销魂的工作。首先我们面对的问题就是我们怎么让IOC能够创建一个Bean?为了创建Bean我们需要提供一些什么?
如何创建Bean
在不手动通过new关键字创建的情况下创建类实例的对象方法有两种:
- 反射:通过反射的方法可以创建类的实例:
clazz.getClass().newInstance();
。 - 工厂模式:工厂模式可以让我们在不接触实例类的情况下创建出实例。
public class PersonFactory{
public Person getPerson(){
return new Person();
}
}
为了创建Bean我们需要提供什么
通过分析上面的两种方法可以轻松得出答案。
对于反射的方式我们仅需提供实例的Class对象。
对于工厂方法我们需要提供的就是创建该类的工厂名(factoryName)和方法名(methodName);
除了创建bean还需要做些什么
IOC容器是对bean的整个生命周期进行管理,除了创建之外还需要对bean进行初始化,以及不需要时对bean进行销毁的工作(如释放资源等)。所以我们还需要提供初始化和销毁等操作。
到这里创建bean需要的基本分析完了,看类图:
Bean工厂
Bean的定义解决了,但是这个bean定义以及创建好的Bean实例放在哪里呢,我们需要一个统一的地方来存放这些东西以方便我们要用的时候方便取。
我们定义一个Bean工厂来存放bean,在需要的时候懂bean工厂中取即可,bean工厂对外提供的也仅仅是一个获取bean的方法即可,由于bean的类型不定,所以返回值定位Object。
注册Bean定义
到了现在我们有了创建bean的Bean定义,有了存放和管理bean的Bean工厂,现在需要考虑的事怎么来联系这两个类,我们还需要另外一个接口,接口的功能是让我们能注册和获取bean定义,这里我们通过beanName来区分不同的bean。
代码实现
到这里我们实现一个简易的IOC容器的需要的东西基本准备完成了,看下基本类图:
基本代码实现:
DefaultBeanDefinition:
public class DefaultBeanDefinition implements BeanDefinition{
private Class<?> clazz;
private String beanFactoryName;
private String createBeanMethodName;
private String staticCreateBeanMethodName;
private String beanInitMethodName;
private String beanDestoryMethodName;
private boolean isSingleton;
// setter
public void setSingleton(boolean singleton) {
isSingleton = singleton;
}
@Override
public Class<?> getBeanClass() {
return this.clazz;
}
@Override
public String getBeanFactory() {
return this.beanFactoryName;
}
@Override
public String getCreateBeanMethod() {
return this.createBeanMethodName;
}
@Override
public String getStaticCreateBeanMethod() {
return this.staticCreateBeanMethodName;
}
@Override
public String getBeanInitMethodName() {
return this.beanInitMethodName;
}
@Override
public String getBeanDestoryMethodName() {
return this.beanDestoryMethodName;
}
@Override
public String getScope() {
return this.isSingleton?BeanDefinition.SINGLETION :BeanDefinition.PROTOTYPE;
}
@Override
public boolean isSingleton() {
return this.isSingleton;
}
@Override
public boolean isPrototype() {
return !this.isSingleton;
}
}
DefaultBeanFactory:
public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {
private Log log = LogFactory.getLog(this.getClass());
//ConcurrentHashMap应对并发环境
private Map<String, BeanDefinition> bdMap = new ConcurrentHashMap<>();
private Map<String, Object> beanMap = new ConcurrentHashMap<>();
@Override
public void register(BeanDefinition bd, String beanName) {
Assert.assertNotNull("beanName不能为空 beanName", beanName);
Assert.assertNotNull("BeanDefinition不能为空", bd);
if(bdMap.containsKey(beanName)){
log.info("[" + beanName + "]已经存在");
}
if(!bd.validate()){
log.info("BeanDefinition不合法");
}
if(!bdMap.containsKey(beanName)){
bdMap.put(beanName, bd);
}
}
@Override
public boolean containsBeanDefinition(String beanName) {
return bdMap.containsKey(beanName);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) {
if(!bdMap.containsKey(beanName)){
log.info("[" + beanName + "]不存在");
}
return bdMap.get(beanName);
}
public Object doGetBean(String beanName) throws InstantiationException, IllegalAccessException {
if(!beanMap.containsKey(beanName)){
log.info("[" + beanName + "]不存在");
}
Object instance = beanMap.get(beanName);
if(instance != null){
return instance;
}
//不存在则进行创建
if(!this.bdMap.containsKey(beanName)){
log.info("不存在名为:[" + beanName + "]的bean定义");
}
BeanDefinition bd = this.bdMap.get(beanName);
Class<?> beanClass = bd.getBeanClass();
if(beanClass != null){
instance = createBeanByConstruct(beanClass);
if(instance == null){
instance = createBeanByStaticFactoryMethod(bd);
}
}else if(instance == null && StringUtils.isNotBlank(bd.getStaticCreateBeanMethod())){
instance = createBeanByFactoryMethod(bd);
}
this.doInit(bd, instance);
if(instance != null && bd.isSingleton()){
beanMap.put(beanName, instance);
}
return instance;
}
private void doInit(BeanDefinition bd, Object instance) {
Class<?> beanClass = instance.getClass();
if(StringUtils.isNotBlank(bd.getBeanInitMethodName())){
try {
Method method = beanClass.getMethod(bd.getBeanInitMethodName(), null);
method.invoke(instance, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 构造方法创建实例
* @param beanClass
* @return
*/
private Object createBeanByConstruct(Class<?> beanClass) {
Object instance = null;
try {
instance = beanClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
/**
* 普通工厂方法创建实例
* @param bd
* @return
*/
private Object createBeanByFactoryMethod(BeanDefinition bd) {
Object instance = null;
try {
//获取工厂类
Object factory = doGetBean(bd.getBeanFactory());
//获取创建实例的方法
Method method = factory.getClass().getMethod(bd.getCreateBeanMethod());
//执行方法
instance = method.invoke(factory, null);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
/**
* 静态方法创建实例
* @param bd
* @return
*/
private Object createBeanByStaticFactoryMethod(BeanDefinition bd) {
Object instance = null;
try {
Class<?> beanClass = bd.getBeanClass();
//获取创建实例的方法
Method method = beanClass.getMethod(bd.getStaticCreateBeanMethod());
instance = method.invoke(beanClass, null);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
@Override
public Object getBean(String beanName) {
if(!beanMap.containsKey(beanName)){
log.info("[" + beanName + "]不存在");
}
return beanMap.get(beanName);
}
@Override
public void close() throws IOException {
Set<Map.Entry<String, BeanDefinition>> entries = bdMap.entrySet();
for(Map.Entry<String, BeanDefinition> entry: entries){
BeanDefinition value = entry.getValue();
String destoryMethodName = value.getBeanDestoryMethodName();
try {
Method method = value.getBeanClass().getMethod(destoryMethodName, null);
method.invoke(value.getBeanClass(), null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
简单测试一下:
实例bean:
public class User {
private String name;
private int age;
//getter setter
public void init(){
System.out.println("init...");
}
public void destory(){
System.out.println("destory...");
}
}
工厂类:
public class TestFactory {
public Object createMethod(){
return new User();
}
public static Object staticCreateMethod(){
return new User();
}
}
测试类:
public class MySpringTest {
static DefaultBeanFactory factory = new DefaultBeanFactory();
@Test
public void test() throws IllegalAccessException, InstantiationException {
DefaultBeanDefinition bd = new DefaultBeanDefinition();
bd.setClazz(User.class);
bd.setSingleton(true);
bd.setBeanFactoryName("TestFactory");
bd.setCreateBeanMethodName("createMethod");
bd.setStaticCreateBeanMethodName("staticCreateMethod");
bd.setBeanInitMethodName("init");
factory.register(bd, "user");
System.out.println(factory.doGetBean("user"));
}
}
小结
一个简易的容器就这样实现了,当然我们这里只是具备了基本的功能,实际上还差的远,比如带参数的bean的实例化等功能。但是IOC的基本原理已经表达出来了,后面我们只需在这个基础上添加新的功能即可。
Spring系列之IOC的原理及手动实现的更多相关文章
- Spring系列之DI的原理及手动实现
目录 Spring系列之IOC的原理及手动实现 Spring系列之DI的原理及手动实现 前言 在上一章中,我们介绍和简单实现了容器的部分功能,但是这里还留下了很多的问题.比如我们在构造bean实例的时 ...
- Spring系列之AOP的原理及手动实现
目录 Spring系列之IOC的原理及手动实现 Spring系列之DI的原理及手动实现 引入 到目前为止,我们已经完成了简易的IOC和DI的功能,虽然相比如Spring来说肯定是非常简陋的,但是毕竟我 ...
- 理解Spring:IOC的原理及手动实现
Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架.也是几乎所有Java工作者必须要掌握的框架之一,其优秀的设计思想以及其代码实现上的艺术也是我们需要掌握的. ...
- Spring系列-SpringBase+IOC
Spring 一.前言 Thinking is more important than learning 本文主要讲述spring 以及它的 IOC原理 代码地址:https://gitee.com/ ...
- Spring系列(1)--IOC 和 DI
IOC 和 DI IOC 原理 xml 配置文件配置 bean dom4j 读取配置文件 工厂设计模式 反射机制创建对象 applicationContext.xml 配置文件,该配置文件名可自定义: ...
- Spring系列之IOC容器
一.概述 IOC容器就是具有依赖注入功能的容器,IOC容器负责实例化.定位.配置应用程序中的对象及建立这些对象之间的依赖.应用程序无需直接在代码中new 相关的对象,应用程序由IOC容器进行组装.在S ...
- Spring系列之手写一个SpringMVC
目录 Spring系列之IOC的原理及手动实现 Spring系列之DI的原理及手动实现 Spring系列之AOP的原理及手动实现 Spring系列之手写注解与配置文件的解析 引言 在前面的几个章节中我 ...
- Spring系列之手写注解与配置文件的解析
目录 Spring系列之IOC的原理及手动实现 Spring系列之DI的原理及手动实现 Spring系列之AOP的原理及手动实现 引入 在前面我们已经完成了IOC,DI,AOP的实现,基本的功能都已经 ...
- 通俗化理解Spring3 IoC的原理和主要组件(spring系列知识二总结)
♣什么是IoC? ♣通俗化理解IoC原理 ♣IoC好处 ♣工厂模式 ♣IoC的主要组件 ♣IoC的应用实例 ♣附:实例代码 1.什么是IoC(控制反转)? Spring3框架的核心是实现控制反转(Io ...
随机推荐
- python-mysql驱动64位
安装Python-MySQL驱动一直没有成功: https://pypi.python.org/pypi/MySQL-python/1.2.5#downloads 上面网站下的版本安装都不能通过 提示 ...
- REdis命令处理流程处理分析
分析版本:REdis-5.0.4. REdis命令处理流程可分解成三个独立的流程(不包括复制和持久化): 1) 接受连接请求流程: 2) 接收请求数据和处理请求流程,在这个过程并不会发送处理结果给Cl ...
- 如何在已安装Python解释器的Linux上更新Python
在Linux环境下升级Python (附:解决pip报错 subprocess.CalledProcessError: Command '('lsb_release', '-a')' returned ...
- VUE项目 npm run build卡住不动,也不报错
npm config set registry http://registry.cnpmjs.org try again npm run build 居然能动了....
- 前端基础之 html
---恢复内容开始--- web服务本质 import socket sock=socket.socket() sock.bind(()) sock.listen() where True: conn ...
- spring配置问题
产生原因缺少包common-logging-1.2.jar 在该字段所在的类中没有提供该字段的set方法.
- 【ProtoBuffer】windows上安装ProtoBuffer3.1.0 (附已编译资源)
------- 17.9.17更新 --- 以下这些方法都是扯淡,对我的机器不适用,我后来花了最后成功安装并亲测可用的方法不是靠vs编过的,vs生成的库引入后函数全部报undefine refere ...
- msfconlose基本命令
命令 简介 back 从当前上下文 banner 显示显示一个令人敬畏的metasploit横幅 cd 更改当前工作目录 color 切换颜色 connect 与主机通信 edit 使用$ VISUA ...
- hbase删除table时,显示table不存在
hbase删除table时,显示table不存在,但是创建table时,显示table存在. 解决方案: 清空zookeeper数据.(重新安装zookeeper)
- vue图片上传
之前花了两个月用vue做了一个建筑照片我的webApp,前端就我一人,负责用vue写页面对接接口,后台一个程序员负责写接口,嵌套个安卓ios的壳.搞的是风风火火,过程也是很心累,大多不在技术,在于所谓 ...