近期一直想看spring的源代码,可是奈何水平太低,庞杂的源代码令我一阵阵的头晕。

非常有幸,在网上看到了黄亿华大神的<<1000行代码读懂Spring(一)- 实现一个主要的IoC容器>>

认为相当不错,就以他的代码为基础,自己又写了一个IoC容器(基本上都是黄的代码,我仅仅改了一部分)

原网页例如以下

http://my.oschina.net/flashsword/blog/192551





特此声明,本文不能算严格意义上的原创,仅仅能算是黄文章的再次解读吧。

开工

例如以下的代码不须要解释了吧。

step1

package com.myspring.factory;

public interface BeanFactory {
Object getBean(String name) throws Exception;
}
package com.myspring.bean;

/**
* bean的内容及元数据,保存在BeanFactory中,包装bean的实体
*/
public class BeanDefinition { private Object bean;
private Class<?> beanClass;
private String beanClassName;
public BeanDefinition() {
}
public BeanDefinition(Object object){
this.bean=object;
} public void setBeanClassName(String beanClassName) {
this.beanClassName = beanClassName;
try {
this.beanClass = Class.forName(beanClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//省略部分get/set方法
}
package com.myspring.factory;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import com.myspring.bean.BeanDefinition; public class AbstractBeanFactory implements BeanFactory { //存放Factory里的全部bean的具体信息
//能够理解为Factory里面的bean的信息表
//就像一个学校总会有一个学生信息表
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
//存放Factory里的全部bean的name
private final List<String> beanDefinitionNames = new ArrayList<String>(); @Override
public Object getBean(String name) throws Exception {
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
if (beanDefinition == null) {
throw new IllegalArgumentException("No bean named " + name + " is defined");
}
Object bean = beanDefinition.getBean();
return bean;
} /**
*将新增加的beanDefinition注冊到beanDefinitionMap里
*这里仅仅是将beanDefinition的定义放入"注冊表"(beanDefinitionMap) 至于beanDefinition是否有错误 以后再说 这里无论
**/
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
beanDefinitionMap.put(name, beanDefinition);
beanDefinitionNames.add(name); //为什么如今不检測beanDefinition的细节 比如有没有bean? layzload
}
}

来測试一下,以HelloWorldServiceImpl为例

package com.myspring;

public class HelloWorldServiceImpl {

        public void helloWorld2() {
System.out.println("hello");
}
}

測试代码例如以下

public void Step1() throws Exception {
BeanFactory beanFactory=new AbstractBeanFactory();
BeanDefinition beanDefinition=new BeanDefinition(new HelloWorldServiceImpl());
((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld",beanDefinition); HelloWorldServiceImpl h=(HelloWorldServiceImpl) beanFactory.getBean("helloworld");
h.helloWorld2();
}

非常easy,打印出了hello;

ok 至此 我们最简单的IoC就搭建完毕了。

如今我们就一步一步地完好我们的容器

step2

第一步的时候,beanDefinition里面我们直接放入了bean,向以下这个,我们放入classname会怎样?

@Test
public void Step2() throws Exception {
BeanFactory beanFactory=new AbstractBeanFactory();
BeanDefinition beanDefinition=new BeanDefinition();
beanDefinition.setBeanClassName("com.myspring.HelloWorldServiceImpl");
((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld",beanDefinition); HelloWorldServiceImpl h=(HelloWorldServiceImpl) beanFactory.getBean("helloworld");
h.helloWorld2();
}

解决的方法非常easy,在AbstractBeanFactory/getBean()方法返还bean之前加上例如以下代码就可以。

if (bean==null) {

     bean=beanDefinition.getBeanClass().newInstance();

}

step3

继续走,假设我们在HelloWorldServiceImpl里面有简单的參数怎么办,示意代码例如以下

private String text;

private int    a;



public void helloWorld(){

System.out.println(text+a+" ss");

}

既然有參数,那我们就设计一个PropertyValue

package com.myspring.bean;

/**
* 用于bean的属性注入
*/
public class PropertyValue {
private final String name;
private final Object value; // 省略get/set 后文对简单的get/set方法将直接省略 不再说明
}

下来就是在BeanDefinition里面添加一个List<PropertyValue> pvs=new ArrayList<PropertyValue>,毕竟不能限制一个类仅仅有一个属性吧。

一个类中不会仅仅有一个參数,那必定就是List了。

好像说的有道理,眼下我们是人为地给pvs里面加数据,用add()方法,假设一个类中,有反复的属性呢?

开玩笑,java里能出现两个变量同名吗?

当然java类里是不存在的,可我们得知道成型的spring但是从xml里面读取数据的

假设我写成这样 怎么办?

  <bean id="userService" class="com.bjsxt.services.UserService" >
<property name="userDao" bean="u" />
<property name="userDao" bean="u" />
</bean>

因此我们得再增加一个类PropertyValues

package com.myspring.bean;

import java.util.ArrayList;
import java.util.List; /**
* 包装一个对象全部的PropertyValue。<br/>
* 为什么封装而不是直接用List?由于能够封装一些操作。 */
public class PropertyValues { private final List<PropertyValue> propertyValueList = new ArrayList<PropertyValue>();
public PropertyValues() {
} public void addPropertyValue(PropertyValue pv) {
//TODO:这里能够对于反复propertyName进行推断,直接用list没法做到
// System.out.println(pv.getName()+pv.getValue());
this.propertyValueList.add(pv);
}
public List<PropertyValue> getPropertyValues() {
return this.propertyValueList;
}
}

因而在BeanDefinition里增加private PropertyValues propertyValues;就可以

对应的getBean方法也要变

@Override
public Object getBean(String name) throws Exception {
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
if (beanDefinition == null) {
throw new IllegalArgumentException("No bean named " + name
+ " is defined");
}
Object bean = beanDefinition.getBean();
if (bean == null) {
bean = beanDefinition.getBeanClass().newInstance();
}
creatBean(bean, beanDefinition); // 可不能够仅仅传一个beandefinition
// 在方法里在beanDefinition.getBean();
// 这样还能够少传递一个对象呢?
return bean;
}

大家看到了关键问题在creatBean方法上

    public void creatBean(Object bean, BeanDefinition beanDefinition)
            throws Exception {
        if (beanDefinition.getPropertyValues() != null)
            creatBeanWithProperty(bean, beanDefinition);
    } public void creatBeanWithProperty(Object bean, BeanDefinition beanDefinition) throws Exception{
        
          
            int size =beanDefinition.getPropertyValues().getPropertyValues().size();
            List<PropertyValue> list = beanDefinition.getPropertyValues().getPropertyValues();
            for (int i = 0; i <size ; i++) {
                
                //究竟是不是引用类型 得差别开
                //不差别行不行?
                if(list.get(i).getValue() instanceof BeanReference){
                    String beanName=((BeanReference)list.get(i).getValue()).getName();
                //    System.out.println("par "+list.get(i).getName());
                    Object referenceBean=getBean(beanName);
                    String ms="set"+Character.toUpperCase(list.get(i).getName().charAt(0))+list.get(i).getName().substring(1);
                
                    Method m=bean.getClass().getDeclaredMethod(ms, referenceBean.getClass());
                    m.invoke(bean, referenceBean);
                }
                else {
                    String fieldName = list.get(i).getName();
                    Object value = list.get(i).getValue();
                    Field field = bean.getClass().getDeclaredField(fieldName); // getDeclaredField是获得全部的字段(不不过public)
                    field.setAccessible(true); // 这一步必须有
                    field.set(bean, value);
                    field.setAccessible(false); // 这一步必须有
                }
            }
        
    }

ok,看看測试代码

public void Step3() throws Exception {
// 1.初始化beanfactory
BeanFactory beanFactory = new AbstractBeanFactory(); // 2.bean定义
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("com.myspring.HelloWorldServiceImpl"); // 3.设置属性
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("text","Hello World!"));
propertyValues.addPropertyValue(new PropertyValue("a",new Integer(15)));
beanDefinition.setPropertyValues(propertyValues); // 4.注冊bean
((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld", beanDefinition); HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory
.getBean("helloworld");
h.helloWorld();
}

測试结果

Hello World!15 ss

step4

上面的类里面的成员变量依旧是int,double等的基本变量(String 在这里也算基本变量),假设我在类里面加一个引用变量呢?



例如以下

private OutputService out;

public void helloWorld3(){

    out.output(text);

    }

OutputService的output方法非常easy就是输出text的内容。

那么下来,理所应当的我们会设计出一个參考类

package com.myspring;

public class BeanReference {
private String name;
private Object bean;
}

对于BeanReference,我们能够按以下的方式使用

	public void Step4() throws Exception {
// 1.初始化beanfactory
BeanFactory beanFactory = new AbstractBeanFactory(); // 2.bean定义
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("com.myspring.HelloWorldServiceImpl"); BeanDefinition beanDefinition2 = new BeanDefinition();
beanDefinition2.setBeanClassName("com.myspring.OutputService"); BeanReference beanReference=new BeanReference("outPutService");
beanReference.setBean(beanDefinition2); // 3.设置属性
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("text","Hello World! with referencebean"));
propertyValues.addPropertyValue(new PropertyValue("a",new Integer(15)));
propertyValues.addPropertyValue(new PropertyValue("out",beanReference));
beanDefinition.setPropertyValues(propertyValues); // 4.注冊bean
((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld", beanDefinition);
((AbstractBeanFactory)beanFactory).registerBeanDefinition("out", beanDefinition2); HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory
.getBean("helloworld");
h.helloWorld3();
}

看到第四步注冊bean的时候,大家应该想到假设有n个bean,我就得调用registerBeanDefinition方法n次吗?

眼下就仅仅能是这种方法了,技术用for循环,beanDefinition的名字也没办法,如今毕竟是模拟,各个变量的名字都是由人输入的,以后会从xml中读,就简单多了。

以下的麻烦的代码大家应该也能猜处理,就是creatBean部分。

int size =beanDefinition.getPropertyValues().getPropertyValues().size();
for (int i = 0; i <size ; i++) {
List<PropertyValue> list = beanDefinition.getPropertyValues().getPropertyValues();
//究竟是不是引用类型 得差别开
//不差别行不行?
if(list.get(i).getValue() instanceof BeanReference){
String beanName=list.get(i).getName();
Object referenceBean=getBean(beanName); //循环调用getBean
String ms="set"+Character.toUpperCase(beanName.charAt(0))+beanName.substring(1); Method m=bean.getClass().getDeclaredMethod(ms, referenceBean.getClass());
m.invoke(bean, referenceBean); }
else {
String fieldName = list.get(i).getName();
Object value = list.get(i).getValue();
Field field = bean.getClass().getDeclaredField(fieldName); // getDeclaredField是获得全部的字段(不不过public)
field.setAccessible(true); // 这一步必须有
field.set(bean, value);
field.setAccessible(false); // 这一步必须有
} }

还是上面的问题,假设不区分是引用类型还是基本类型能够不?

property里面的value是object类型的,假设我们给里面放的是int,直接set到bean里面,但是这个object要是BeanReference呢,还得取出BeanReference里面的value,然后在循环getbean()。你们说不区分能行吗?

測试结果

Hello World! with referencebean

Tiny Spring 分析一的更多相关文章

  1. 【转】Spring,Spring MVC及Spring Boot区别

    对于一个Java开发者来说,Spring可谓如雷贯耳,无论是Spring框架,还是Spring引领的IOC,AOP风格,都对后续Java开发产生的深远的影响,同时,Spring社区总能及时响应开发者的 ...

  2. 【TencentOS tiny】又有一个操作系统开源

    新闻 2019年9月18日,腾讯宣布将开源 自主研发的轻量级物联网实时操作系统TencentOS tiny.相比市场上其它系统,腾讯TencentOS tiny在资源占用.设备成本.功耗管理以及安全稳 ...

  3. Spring Boot入门学习

    1. Spring Boot概述 1.1.什么是Spring Boot SpringBoot是一个可使用Java构建微服务的微框架.是Spring框架及其社区对"约定优先于配置"理 ...

  4. Spring:面向切面编程的AOP

    一.前言 除了依赖注入(DI),Spring框架提供的另一个核心功能是对面向方面的编程(AOP)的支持. AOP通常被称为实现横切关注点的工具.横切关注点一词是指应用程序中的逻辑不能与应用程序的其余部 ...

  5. 学习Java Web开发

    学习DreamWaveMX中文版的网页设计技术 HTML网页设计,这是最基本的.学习XML的一些基本知识.初步掌握一些JSCRIPT的应用. 学习JAVA语言. 这应该分成2次来进行: 第1次找一本国 ...

  6. 01-Spring概述(总览)

    Spring概述 前言 Spring 发展至现在,俨然成为一个生态,但要理解其余的 Spring Boot.Spring Cloud 等框架,需要先对 Spring 的整个体系有一定的理解,因为其余的 ...

  7. spring remoting源码分析--Hessian分析

    1. Caucho 1.1 概况 spring-remoting代码的情况如下: 本节近分析caucho模块. 1.2 分类 其中以hession为例,Hessian远程服务调用过程: Hessian ...

  8. spring.net 框架分析(三)ContextRegistry.GetContext()

    我们通过ContextRegistry.GetContext()建立了一个IApplicationContext得实例,那么这个实例具体是怎么建立的了. 我们来分析一下容器实例建立的过程: 我们在配置 ...

  9. 深入理解 spring 容器,源码分析加载过程

    Spring框架提供了构建Web应用程序的全功能MVC模块,叫Spring MVC,通过Spring Core+Spring MVC即可搭建一套稳定的Java Web项目.本文通过Spring MVC ...

随机推荐

  1. pragma comment的使用

    该宏放置一个注释到对象文件或者可执行文件. #pragma comment( comment-type [,"commentstring"] ) comment-type是一个预定 ...

  2. Android Studio安装使用图文教程

    原文 http://jingyan.baidu.com/article/1876c852a25cb4890b1376fa.html Google I/O开发者大会上宣布,Android Studio ...

  3. 理解Java多态

    多态又称Polymophism,poly意思为多,polymophism即多种形态的意思.一种类型引用因为指向不同的子类,表现出不同的形态,使用不同的方法. 什么是多态 多态建议我们编码时使用comm ...

  4. 栈和队列的Java实现

    一.  栈 1.概念 栈是一种特殊的线性表,它只能在栈顶(top)进行插入(push)和删除(pop)操作. 栈的常用操作: 入栈(push):向栈顶插入元素 出栈(pop):从栈顶删除元素 访问栈顶 ...

  5. 网易云课堂_C语言程序设计进阶_第一周:数据类型:整数类型、浮点类型、枚举类型_1计算分数精确值

    1 计算分数精确值(10分) 题目内容: 由于计算机内部表达方式的限制,浮点运算都有精度问题,为了得到高精度的计算结果,就需要自己设计实现方法. (0,1)之间的任何浮点数都可以表达为两个正整数的商, ...

  6. iOS 开发的几种手势

    今天为大家介绍一下IOS 的七种手势,手势在开发中经常用到,所以就简单 通俗易懂的说下, 话不多说,直接看代码: // 初始化一个UIimageView UIImageView *imageView ...

  7. iOS开发之GCD使用总结

    GCD是iOS的一种底层多线程机制,今天总结一下GCD的常用API和概念,希望对大家的学习起到帮助作用. GCD队列的概念 在多线程开发当中,程序员只要将想做的事情定义好,并追加到DispatchQu ...

  8. (转)iOS开发ARC内存管理技术要点

    转自:http://www.cnblogs.com/flyFreeZn/p/4264220.html 本文来源于我个人的ARC学习笔记,旨在通过简明扼要的方式总结出iOS开发中ARC(Automati ...

  9. Cocos2D-X2.2.3学习笔记8(处理精灵单击、双击和三连击事件)

    我们依据上一次介绍的触屏事件和事件队列等知识来实现触屏的单击,双击,三连击事件. 下图为我们实现的效果图: 单击精灵跳跃一个高度, 双击精灵跳跃的高度比单击的高 三连击精灵跳跃的跟高 好了,開始动手吧 ...

  10. 一些DevExpress控件概况!!!!主要DocumentManager.WindowsUIView.Tile

    WinForms Controls The links below provide comprehensive information on using DevExpress WinForms pro ...