预备知识: Java反射原理,XML及其解析
IOC:Inversion of Control,控制反转,它最主要反映的是与传统面向对象(OO)编程的不同。通常我们编程实现某种功能都需要几个对象相互作用,从编程的角度出发,也就是一个主对象要保存其他类型对象的引用,通过调用这些引用的方法来完成任务。如何获得其他类型的对象引用呢?一种方式是主对象内部主动获得所需引用;另一种方式是在主对象中设置setter 方法,通过调用setter方法或构造方法传入所需引用。后一种方式就叫IOC,也是我们常常所说的依赖注入。以下我们用一个简单的例子来说明传统OO编程与IOC编程的差别。
这个例子的目的是根据时间不同返回不同的问候字符串, 比如Good Morning, world或Good afternoon, World。
服务接口:
package com.kettas.springdev.ioc;
public interface HelloIF {
String sayHello();
}
传统实现:
package com.kettas.springdev.ioc;
import java.util.Calendar;
public class HelloIFImpl implements HelloIF{
private Calendar cal; //我们需要的引用
public HelloIFImpl(){
cal =Calendar.getInstance(); //主动获取
}
public String sayHello(){
if(cal.get(Calendar.AM_PM) == Calendar.AM) return “Good morning, World”;
else return “Good afternoon, World”;
}
}
采用IOC方式:
package com.kettas.springdev.ioc;
import java.util.Calendar;
public class HelloIFImpl implements HelloIF{
private Calendar cal;//我们需要的引用
public void setCal(Calendar cal){ this.cal = cal;} //依赖注入
public String sayHello(){
if(cal.get(Calendar.AM_PM) == Calendar.AM) return “Good morning, World”;
else return “Good afternoon, World”;
}
}
在这里你也许会问:我看不出有太大差别,并且依赖注入还需要我先创建外部的Calendar对象,然后再传到HelloIFImpl对象中。
是的,如果我们直接创建HelloIFImpl对象没有任何的优势。如果我们让一个Bean工厂来帮我们创建HelloIF类型的引用就有优势了,当然要在这样的前提下: 1. Bean工厂可以随时改变HelloIF实现的类型;2. Bean工厂在创建好对象后主动调用依赖注入的方法。所以离开Bean工厂谈IOC是没有什么意义的, 开源框架Spring就提供了灵活多样的Bean工厂。以上例子可以通过如下XML片段来告诉Bean工厂如何创建对象并注入依赖:
<beans>
<bean id=”hello” class=”com.kettas.springdev.ioc.HelloIFImpl”> <!—调用构造方法产生对象à
<property name=”cal” > <!—注入下面定义的Calendar引用-->
<ref local=”calendar”/>
</property>
</bean>
<bean id=”calendar” class=”java.util.GregorianCalendar” /> <!—产生calendar对象à
</beans>
Bean工厂通过解析以上的配置就知道如何创建对象,如何注入依赖。
那Bean工厂到底如何实现所说的功能呢?从以上的XML配置片段我们可以看出
有两种数据类型:一个是Bean的定义(BeanDefinition);一个是对Bean的属性(Property)的定义(PropertyDefinition),嵌套在Bean定义中。Bean的定义包括:id, 类名(clazz)和一到多个PropertyDefinition; Property的定义包括: name, refName(如果是引用,指向另一个Bean), value(如果是基本数据类型), ifRef(是否是引用)。Java通过反射机制可以调用构造方法创建对象,也可以调用该实例上的方法。Bean工厂通过递归调用创建配置文件里定义的Bean对象,并调用这些对象的setter方法来实现依赖注入。而依赖注入都使用的是Bean的id, 所以我们在Bean工厂里用一个Map来保存Bean的定义,该Map的key是Bean的id。创建的Bean对象都是单例的,所以我们要保存Bean对象;而客户是通过id来获取Bean对象,所以我们也用Map来缓存。以下的代码提供了上述问题的解决方案(当然这是最简单的一种:只调用没有参数的构造方法,只实现基于setter的依赖注入,注入的依赖只能是基本数据类型或引用, xml解析没有做合法性检测等)
Bean的定义:BeanDefinition,java:
package com.kettas.springdev.ioc.bf;
import java.util.HashSet;
import java.util.Set;
public class BeanDefinition {//对应<bean>元素
private String clazz; //Bean类名全路径, 对应<bean>的attribute: class
private String id; //唯一属性, 对应<bean>的attribute: id
private Set<PropertyDefinition> propertyDefinitions //依赖描述, 对应<property>元素
= new HashSet<PropertyDefinition>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
public Set<PropertyDefinition> getPropertyDefinitions() {
return propertyDefinitions;
}
public void setPropertyDefinitions(Set<PropertyDefinition> propertyDefinitions) {
this.propertyDefinitions = propertyDefinitions;
}
public void addPropertyDefinition(PropertyDefinition pd){
this.propertyDefinitions.add(pd);
}
}
Bean的属性的定义:PropertyDefinition.java:
package com.kettas.springdev.ioc.bf;
public class PropertyDefinition {//对应<property>元素
private String name; // <property> attribute: name
private String refName; // <property>子元素<ref>的attribute(如local, bean等)的值
private String value; //<property>子元素<value>的字符串子元素
private boolean isRef; //是否为引用
public boolean isRef() {
return isRef;
}
public void setRef(boolean isRef) {
this.isRef = isRef;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRefName() {
return refName;
}
public void setRefName(String refName) {
this.refName = refName;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
Bean工厂接口: BeanFactory.java:
package com.kettas.springdev.ioc.bf;
public interface BeanFactory {
Object getBean(String id);
}
BeanFactory的一个实现:XmlBeanFactory.java:
package com.kettas.springdev.ioc.bf;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class XmlBeanFactory implements BeanFactory{
private Map<String, Object> beans
= new HashMap<String, Object>();//Bean对象缓存
private Set<BeanDefinition> beanDefinitions
= new HashSet<BeanDefinition>();//Bean的定义
public XmlBeanFactory(String xmlFileClassPath){
new BeanFactoryConfiguration(beanDefinitions, xmlFileClassPath);
}
public Object getBean(String id){
if(beans.containsKey(id)) return beans.get(id);
Object bean = null;
BeanDefinition bd = getBeanDefintion(id);
try{
Class beanClass = Class.forName(bd.getClazz());
bean = beanClass.newInstance();
for(PropertyDefinition pd : bd.getPropertyDefinitions()){
String property = pd.getName();
Method m = getSetter(beanClass,property);
//System.out.println(property + " : " + pd.isRef());
if(pd.isRef()){
m.invoke(bean, getBean(pd.getRefName()));
}else{
setValue(m, bean, pd.getValue());
}
}
beans.put(id, bean);
}catch(Exception e){
throw new RuntimeException("Can't create bean " + id, e);
}
return bean;
}
private void setValue(Method m, Object bean, String value) throws Exception {
// TODO Auto-generated method stub
Class paramType = m.getParameterTypes()[0];
//System.out.println(paramType.getName());
if(paramType == byte.class || paramType == Byte.class){
Byte b = new Byte(value);
m.invoke(bean, b);
}else if(paramType == short.class || paramType == Short.class){
Short s = new Short(value);
m.invoke(bean, s);
}else if(paramType == char.class || paramType == Character.class){
Character c = new Character(value.charAt(0));
m.invoke(bean, c);
}else if(paramType == int.class || paramType == Integer.class){
Integer i = new Integer(value);
m.invoke(bean, i);
}else if(paramType == float.class || paramType == Float.class){
Float f = new Float(value);
m.invoke(bean, f);
}else if(paramType == double.class || paramType == Double.class){
Double d = new Double(value);
m.invoke(bean, d);
}else{
m.invoke(bean, value);
}
}
private Method getSetter(Class beanClass, String property) throws Exception {
// TODO Auto-generated method stub
StringBuilder sb = new StringBuilder("set");
char c = property.charAt(0);
if(c >= 'a' && c <= 'z') c -= 32;
sb.append(c).append(property.substring(1));
Method[] methods = beanClass.getMethods();
for(Method m : methods){
if(m.getName().equals(sb.toString()))
return m;
}
throw new RuntimeException("No such property: " + property);
}
private BeanDefinition getBeanDefintion(String id){
for(BeanDefinition bd : beanDefinitions){
if(bd.getId().equals(id))
return bd;
}
throw new RuntimeException("There is not [id="
+ id + "] in the xml configuration file");
}
}
解析Xml文件,生成BeanDefintion和PropertyDefinition: BeanFactoryConfiguration.java
package com.kettas.springdev.ioc.bf;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class BeanFactoryConfiguration {
public BeanFactoryConfiguration(Set<BeanDefinition> beanDefinitions, String xmlFileClassPath) {
// TODO Auto-generated constructor stub
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();
try {
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(
this.getClass().getResourceAsStream(xmlFileClassPath)
);
parse(doc, beanDefinitions);
} catch (Exception e) {
// TODO Auto-generated catch block
throw new RuntimeException("can't parse the configuration file : " + xmlFileClassPath, e);
}
}
private void parse(Document doc, Set<BeanDefinition> beanDefinitions) {
// TODO Auto-generated method stub
NodeList beans = doc.getElementsByTagName("bean");
int len = beans.getLength();
for(int i = 0; i < len; i++){
BeanDefinition bd = new BeanDefinition();
Element n = (Element)beans.item(i);
NamedNodeMap nnm = n.getAttributes();
for(int j = 0; j < nnm.getLength(); j++){
Node attr = nnm.item(j);
//System.out.println(attr.getNodeName() + " " + attr.getNodeValue());
if(attr.getNodeName().equals("id"))
bd.setId(attr.getNodeValue());
else
bd.setClazz(attr.getNodeValue());
}
NodeList pros = n.getElementsByTagName("property");
for(int j = 0; j < pros.getLength(); j++){
bd.addPropertyDefinition(createPropertyDefinition(pros.item(j)));
}
beanDefinitions.add(bd);
}
}
private PropertyDefinition createPropertyDefinition(Node node) {
// TODO Auto-generated method stub
PropertyDefinition pd = new PropertyDefinition();
NamedNodeMap nnm = node.getAttributes();
pd.setName(nnm.item(0).getNodeValue());
NodeList nl = ((Element)node).getElementsByTagName("value");
if(nl != null && nl.getLength() > 0){
pd.setValue(nl.item(0).getFirstChild().getNodeValue());
}else{
pd.setRef(true);
nl = ((Element)node).getElementsByTagName("ref");
Node ref = nl.item(0);
pd.setRefName(ref.getAttributes().item(0).getNodeValue());
}
return pd;
}
}
测试程序:
package com.kettas.springdev.ioc.bf;
import springdev.ioc.day1.HelloIF;
public class TestMyBeanFactory {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
BeanFactory bf = new XmlBeanFactory(
"/com/kettas/springdev/ioc/beans.xml"
);
HelloIF hello = (HelloIF)bf.getBean("hello");
System.out.println(hello.sayHello());
}
}
分享到:
相关推荐
**IOC容器简单实现** IOC(Inversion of Control)容器是软件设计模式中的一种,它将对象的创建和管理权从代码本身转移到一个外部容器,即IOC容器。这种反转控制使得程序更加灵活,降低了组件之间的耦合性,提高了...
本文将深入探讨一个简单的IOC容器实现,帮助我们理解DI的基本原理。 首先,理解IOC的概念至关重要。在传统的编程模式中,对象通常自行创建依赖的对象,这导致了代码之间的紧密耦合。而在IOC模式下,容器负责管理...
在这个项目中,“基于java简单模拟实现spring_ioc”显然是为了帮助开发者理解Spring的IoC容器是如何工作的,以及如何通过Java代码来模拟这个过程。 首先,让我们了解什么是Spring的IoC。IoC是一种设计模式,它将...
在Spring中,IOC通过配置文件或注解来定义对象及其依赖关系。 ### 2. 自己实现IOC容器的关键步骤 #### 2.1 定义Bean 首先,我们需要定义一个类作为Bean,这个类可以包含我们应用中的业务逻辑。通常,我们会为Bean...
IoC 容器通过 XML 配置文件或注解来定义对象及其依赖。在 XML 配置中,我们可以声明 beans(即应用程序中的对象)以及它们之间的关系。例如: ```xml ``` 这段 XML 告诉 IoC 容器如何创建 `ExampleClass` 和...
【标题】:“简单IOC容器demo”是一个演示性的项目,它旨在教授如何构建一个基础的控制反转(IOC)容器。这个容器使用注解来实现依赖注入,这是Spring框架的核心特性之一。 【描述】:该示例项目是学习过程的产物,...
现在我们来详细探讨一下如何从零开始,用纯Java实现一个简单的IoC容器。 首先,我们要理解IoC的概念。IoC是指将控制权从应用程序的代码中转移出来,交由一个外部容器进行管理。在传统的编程模式中,对象会自行创建...
在这个简单的模仿实现中,我们可能会看到一个配置文件,用于声明对象及其依赖,类似于Spring的XML配置文件。 描述中提到,该实现仅支持最基本的配置元素,如"id"、"class"和"name",以及"ref"。"id"是对象的唯一...
最后,我们可以创建一个简单的测试用例,验证自定义IoC框架是否正常工作: ```java public class Main { public static void main(String[] args) { SimpleIOCContainer container = new SimpleIOCContainer(); ...
在这个"IOC简单实现Demo"中,我们将探讨如何通过注解和包扫描技术来实现这一概念。 首先,注解(Annotation)是Java语言提供的一种元数据,它允许我们在源代码中嵌入信息,这些信息可以在编译、部署或运行时被处理...
本文将深入探讨如何使用单个.CS文件实现一个简单的IOC容器,并介绍其关键特性。 1. IOC原理: IOC,即控制反转,核心思想是将对象的创建和依赖关系的管理交给外部容器处理,而不是由对象自身负责。这样,对象间的...
**Spring 2.5 IOC(控制反转)的简单实现** Spring 框架是 Java 开发中的一个核心组件,尤其在企业级应用中广泛应用。它提供了依赖注入(Dependency Injection,DI),也就是通常所说的控制反转(Inversion of ...
依赖注入是实现IOC的一种方式,它允许对象在运行时动态地获得依赖的对象,而不是在编译时硬编码。Spring提供了多种DI方式,包括构造器注入、setter注入和接口注入。开发者可以通过注解或XML配置来指定依赖关系。 **...
根据题目中给出的部分内容,下面详细介绍如何基于Spring 3.0.5搭建一个简单的应用,并且使用Spring JDBC Template进行数据库交互。该教程将分为以下几个步骤: 1. **下载并配置所需的Spring Jar包** - **org....
现在,我们来看IoCTest这个压缩包中的文件,虽然没有具体的内容,但我们可以假设这是一个简单的IoC示例。通常,这样的测试文件可能会包含以下内容: 1. 一个接口或抽象类,定义了需要被依赖的服务。 2. 一个或多个...
在本例中,我们将探讨一个基于C#实现的简单IOC练习。 **容器机制**: 在IOC中,容器是负责管理对象生命周期和对象间依赖关系的核心组件。它会根据配置或编程方式来决定何时创建对象、如何创建对象以及对象之间的...
在`iocDemo`这个例子中,我们可能会看到如何用代码实现一个简单的IOC容器。这通常包括以下步骤: 1. **解析配置**:读取XML配置文件,解析出bean的定义。 2. **创建bean**:根据bean定义创建相应的对象,可以使用...
描述中提到的"手动实现一个IOC容器"意味着我们将探讨如何从基本概念出发,逐步构建一个简单的DI容器。这个过程通常包括以下几个步骤: 1. **定义Bean**: 首先,我们需要定义一个Bean类,它代表了我们需要管理的对象...
在这个 `IOCContainer` 类中,`register` 方法用于注册接口及其对应的实现类,而 `resolve` 方法则根据接口获取实例。如果实现类是字符串,那么 `resolve` 将使用 PHP 的 `new` 关键字来实例化对象;如果已注册为一...
本篇文章将深入探讨如何利用反射和内省技术来实现一个简单的Spring IOC。首先,我们来理解什么是反射和内省。 反射是Java提供的一种强大的动态类型机制,它允许程序在运行时检查类的信息,如类名、属性、方法等,并...