论坛首页 Java企业应用论坛

自己动手写一个Spring (Spring 到底是怎么跑起来的)【20090708更新】

浏览 45634 次
该帖已经被评为精华帖
作者 正文
   发表时间:2009-07-30  
首先楼主这个很好很强大
我认真看了几遍
debug了一下
发现个不知道是不是问题:

(【20090708更新】版的代码中)

public class Cat implements Animal {

	private String name;

	public void say() {
		System.out.println("I am " + this.name + "!");
	}

	public void setName(String name) {
	    System.out.println("##################################################");
		this.name = name;
	}

}


控制台输出:

2009/07/30 11:24:53 phz.springframework.core.XmlBeanDefinitionReader readXmlFile
情報: Loading XML bean definitions from file [C:/eclipse/workspace/springframework/bin/applicationContext.xml]
##################################################
2009/07/30 11:26:00 phz.springframework.core.XmlBeanFactory <init>
情報: phz.springframework.test.Cat --> animal
2009/07/30 11:27:33 phz.springframework.core.XmlBeanFactory <init>
情報: phz.springframework.test.AnimalSay --> animalSay
##################################################
I am kitty!


这里出现两次“#”字符串
说明Cat的setName方法被调用两次
是不是因为有两个Cat的实例
怀疑了一下

// Object phz.springframework.core.XmlBeanFactory.getObject(String name)
	private Object getObject(String name) {
		Bean bean = (Bean) beansProsMap.get(name);
		Object obj = BeanProcesser.newInstance(bean.getType());
		setProperty(bean, obj);
		return obj;
	}


是不是应该改成

	private Object getObject(String name) {
		Bean bean = beansProsMap.get(name);
		Object obj = beansMap.get(name);
		if (obj == null) {
			obj = BeanProcesser.newInstance(bean.getType());
			setProperty(bean, obj);
		}
		return obj;
	}


我也是新手
共同研究
0 请登录后投票
   发表时间:2009-07-30   最后修改:2009-07-30
真的非常感谢qbq朋友,这的确是一个很大问题,非常感谢你的修正,谢谢!!!
0 请登录后投票
   发表时间:2009-08-05  
短小精悍,学习了
0 请登录后投票
   发表时间:2009-08-05  
挺有帮助, 不错
0 请登录后投票
   发表时间:2009-08-25  
Email发的,是看了旧版本

发现新版本也同样存在问题。BUG如下:

1、 我觉得readXmlFile()的安全性工作做的不够。
如:                  
String beanId = beanElm.attribute("id").getStringValue();
           String beanType = beanElm.attribute("class").getStringValue();
                      // if beanId or beanType is non-value??


           String name = beanProperty.attributeValue("name");
           //  如果name为null value不为null,就变成了beanProperty“有名无值”了




2、 对于list & map类型,你只处理了ref情况,难道不支持普通list,map么?如:
<property name="animalList">
           <list>
              <value>abc</value>
              <value>efg</value>
           </list>
</property>

if (value instanceof List) {
              System.out.println("In list");
              Iterator<?> valuesIterator = ((List<?>) value).iterator();
              List<Object> valuesList = new ArrayList<Object>();
              while (valuesIterator.hasNext()) {
                  Object valueElem = (Object) valuesIterator.next();
                  if (valueElem instanceof String[]) {
                     valuesList.add(getBean(((String[]) valueElem)[0]));
                  }
                  // 只能处理String[]类型?
                  // 是否需要加上如下代码:
                  /*
                   * if (valueElem instanceof String){
                   * valuesList.add(valueElem);
                   * }
                   */
              }
              BeanProcesser.setProperty(obj, property, valuesList);
           }     

             

3、 bean与bean之间出现死锁问题。如:

   <bean id="animal" class="phz.springframework.test.Cat">
    
       <property name="animalSay">
           <ref bean="animalSay" />
       </property>
    </bean>
    
    <bean id="animalSay" class="phz.springframework.test.AnimalSay">
       <property name="animal">
           <ref bean="animal" />
       </property>
       
</bean>

Exception: Exception in thread "main" java.lang.RuntimeException

Animal的ref有animalSay,animalSay的ref有animal.


4、多线程
0 请登录后投票
   发表时间:2009-08-25  
Pro Spring2.5 一书有一小节就是这个例子。
建议E文不错的去啃啃 几章下来 对Spring原理就比较清晰了
0 请登录后投票
   发表时间:2009-08-26  
写的很精彩!不过缺少生动的实例
0 请登录后投票
   发表时间:2009-08-27  
phz50 写道
  看到这个标题大家可能又想:哎,又一个重新发明轮子的人。在这里很想先声明一下,写这篇文章只是想让大家了解一下Spring到底是怎么运行的,并不是想重造轮子噢,希望大家看完这篇文章后能对Spring有更深入的了解,希望这篇文章对你有所帮助喔!好,言归正传,让我们来一起探索吧!
我们先从最常见的例子开始吧
public static void main(String[] args) {
		ApplicationContext context = new FileSystemXmlApplicationContext(
				"applicationContext.xml");
		Animal animal = (Animal) context.getBean("animal");
		animal.say();
	}

这段代码你一定很熟悉吧,不过还是让我们分析一下它吧,首先是applicationContext.xml
<bean id="animal" class="phz.springframework.test.Cat">
		<property name="name" value="kitty" />
	</bean>

他有一个类phz.springframework.test.Cat
public class Cat implements Animal {
	private String name;
	public void say() {
		System.out.println("I am " + name + "!");
	}
	public void setName(String name) {
		this.name = name;
	}
}

实现了phz.springframework.test.Animal接口
public interface Animal {
	public void say();
}

很明显上面的代码输出I am kitty!

那么到底Spring是如何做到的呢?
接下来就让我们自己写个Spring 来看看Spring 到底是怎么运行的吧!

首先,我们定义一个Bean类,这个类用来存放一个Bean拥有的属性
/* Bean Id */
	private String id;
	/* Bean Class */
	private String type;
	/* Bean Property */
	private Map<String, Object> properties = new HashMap<String, Object>();

一个Bean包括id,type,和Properties。

接下来Spring 就开始加载我们的配置文件了,将我们配置的信息保存在一个HashMap中,HashMap的key就是Bean 的 Id ,HasMap 的value是这个Bean,只有这样我们才能通过context.getBean("animal")这个方法获得Animal这个类。我们都知道Spirng可以注入基本类型,而且可以注入像List,Map这样的类型,接下来就让我们以Map为例看看Spring是怎么保存的吧

Map配置可以像下面的
<bean id="test" class="Test">
		<property name="testMap">
			<map>
				<entry key="a">
					<value>1</value>
				</entry>
				<entry key="b">
					<value>2</value>
				</entry>
			</map>
		</property>
	</bean>

Spring是怎样保存上面的配置呢?,代码如下:
if (beanProperty.element("map") != null) {
					Map<String, Object> propertiesMap = new HashMap<String, Object>();
					Element propertiesListMap = (Element) beanProperty
							.elements().get(0);
					Iterator<?> propertiesIterator = propertiesListMap
							.elements().iterator();
					while (propertiesIterator.hasNext()) {
						Element vet = (Element) propertiesIterator.next();
						if (vet.getName().equals("entry")) {
							String key = vet.attributeValue("key");
							Iterator<?> valuesIterator = vet.elements()
									.iterator();
							while (valuesIterator.hasNext()) {
								Element value = (Element) valuesIterator.next();
								if (value.getName().equals("value")) {
									propertiesMap.put(key, value.getText());
								}
								if (value.getName().equals("ref")) {
									propertiesMap.put(key, new String[] { value
											.attributeValue("bean") });
								}
							}
						}
					}
					bean.getProperties().put(name, propertiesMap);
				}


接下来就进入最核心部分了,让我们看看Spring 到底是怎么依赖注入的吧,其实依赖注入的思想也很简单,它是通过反射机制实现的,在实例化一个类时,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中。让我们看看具体它是怎么做的吧。
首先实例化一个类,像这样
public static Object newInstance(String className) {
		Class<?> cls = null;
		Object obj = null;
		try {
			cls = Class.forName(className);
			obj = cls.newInstance();
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
		return obj;
	}

接着它将这个类的依赖注入进去,像这样
public static void setProperty(Object obj, String name, String value) {
		Class<? extends Object> clazz = obj.getClass();
		try {
			String methodName = returnSetMthodName(name);
			Method[] ms = clazz.getMethods();
			for (Method m : ms) {
				if (m.getName().equals(methodName)) {
					if (m.getParameterTypes().length == 1) {
						Class<?> clazzParameterType = m.getParameterTypes()[0];
						setFieldValue(clazzParameterType.getName(), value, m,
								obj);
						break;
					}
				}
			}
		} catch (SecurityException e) {
			throw new RuntimeException(e);
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		} catch (InvocationTargetException e) {
			throw new RuntimeException(e);
		}
}

最后它将这个类的实例返回给我们,我们就可以用了。我们还是以Map为例看看它是怎么做的,我写的代码里面是创建一个HashMap并把该HashMap注入到需要注入的类中,像这样,
if (value instanceof Map) {
				Iterator<?> entryIterator = ((Map<?, ?>) value).entrySet()
						.iterator();
				Map<String, Object> map = new HashMap<String, Object>();
				while (entryIterator.hasNext()) {
					Entry<?, ?> entryMap = (Entry<?, ?>) entryIterator.next();
					if (entryMap.getValue() instanceof String[]) {
						map.put((String) entryMap.getKey(),
								getBean(((String[]) entryMap.getValue())[0]));
					}
				}
				BeanProcesser.setProperty(obj, property, map);
			}

好了,这样我们就可以用Spring 给我们创建的类了,是不是也不是很难啊?当然Spring能做到的远不止这些,这个示例程序仅仅提供了Spring最核心的依赖注入功能中的一部分。
本文参考了大量文章无法一一感谢,在这一起感谢,如果侵犯了你的版权深表歉意,很希望对大家有帮助!

附件中包含该山寨Spring的源码,核心只有五个类,还有一个测试程序,phz.springframework.test.AnimalSayApp,可以直接运行。

0 请登录后投票
   发表时间:2009-09-20   最后修改:2009-09-20
你好,因为本人刚接触spring,看了你的文章,有个问题没看明白。
AnimalSay animalSay = (AnimalSay) context.getBean("animalSay");

问题1 根据上面的Bean Id(animalSay) 生成 Bean (AnimalSay ),读取applicationContext.xml文件bean  animalSay的ref="animal"值,spring可以通过ref这个属性就可以访问已经定义的animal这个bean的value值。这是什么原理啊

animalSay.say()调用AnimalSay里面声明的      
          public void say() {
animal.say();
}
函数,而这个函数调用了接口Animal类的接口函数。只有Cat类的实现了该接口。
问题 调用接口的方法时,接口对象会自动寻找实现该接口的类吗?
0 请登录后投票
   发表时间:2009-09-21   最后修改:2009-09-21
horusrui 写道
你好,因为本人刚接触spring,看了你的文章,有个问题没看明白。
AnimalSay animalSay = (AnimalSay) context.getBean("animalSay");

问题1 根据上面的Bean Id(animalSay) 生成 Bean (AnimalSay ),读取applicationContext.xml文件bean  animalSay的ref="animal"值,spring可以通过ref这个属性就可以访问已经定义的animal这个bean的value值。这是什么原理啊

animalSay.say()调用AnimalSay里面声明的      
          public void say() {
animal.say();
}
函数,而这个函数调用了接口Animal类的接口函数。只有Cat类的实现了该接口。
问题 调用接口的方法时,接口对象会自动寻找实现该接口的类吗?


-不是自动实现;
-是LZ的代码中已经指定了实现类
见:
	<bean id="animal" class="phz.springframework.test.Cat"> // 指定生成cat实例

// 见XmlBeanDefinitionReader.java
String beanType = beanElm.attribute("class").getStringValue();// 获得XML文件中class的值,所以id="animal"的bean由Cat实现。


// 见FileSystemXmlApplication.java
Object obj = BeanProcesser.newInstance(bean.getType()); // 获得某一个bean的实例


所以当然会有say()的实现呀


0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics