紧接上一篇文章《轻松了解Spring中的控制反转和依赖注入》讲解了SpringIOC和DI的基本概念,这篇文章我们模拟一下SpringIOC的工作机制,使我们更加深刻的理解其中的工作。

  类之间的结构图如下

    

  以下是代码

  

   BeanFactor接口:在Spring源码中的定义是:持有对一定数量的Bean的定义,同时每个Bean都被唯一标识的对象(类),需要实现这个接口。根据对Bean的定义,该工厂将会返回一个包含Bean定义的对象的独立实例(原型设计模式),或者单例共享(一个不错的单例设计模式,)范围是整个工厂的范围(也可以理解成是整个容器下),返回哪种类型的实例依赖于Bean工厂的配置:API是相同的。因为Spring2.0中扩大了依赖范围,可以根据具体应用上下问(如在Web环境中的请求和会话),BeanFactory是应用程序组件的中央注册中心和集中配置。简单的来说该接口定义了获取Bean的方法,由子类去实现。

 

package ioc.factory;

/**
* Created by zzf on 2016/10/26.
*/
public interface BeanFactory {
/**
* 根据对象的ID标识获取对象实例
* @param name
* @return
*/
Object getBean(String name);
}

  BeanInfo类:使用Hash Map进行存储Bean的信息,注意主要是存储了Bean定义的Class类路径,这样才能通过取得type从而利用反射实例化所定义的Bean。

  

package ioc.info;

import java.lang.Object;

import java.util.HashMap;
import java.util.Map; /**
* Created by zzf on 2016/10/26.
*/
public class BeanInfo {
private String id;
private String type;
private Map<String,Object> properties=new HashMap<String,Object>(); public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getType() {
return type;
} public void setType(String type) {
this.type = type;
} public Map<String, Object> getProperties() {
return properties;
} public void setProperties(Map<String, Object> properties) {
this.properties = properties;
}
public void addProperty(String name, Object object)
{
this.properties.put(name, object);
}
}   Person:xml文件中定义的Bean package ioc.bean; /**
* Created by zzf on 2016/10/26.
*/
public class Person {
private String username;
private String password; public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
}
}

 AbstractBeanFactory接口:是实现BeanFactory接口的抽象基类。实现获取Bean定义的方法。是实现逻辑的最关键一个类,注册、读取、分析、注入都是在这个类上的方法调用的,具体可以根据方法查找。是最顶层的IOC,实现该类负责从注册器中取出注册对象,和实现从对象描述信息转换为对象实例的过程实现根据名称获取对象的方法。

  抽象方法setReader:由子类决定如果使用什么样的注册读取器,这里使用了模板方法,父类定义抽象方法,但交给子类自由去设计方法内容,即交由子类自己去选择SourceReader的实现类。

  parseBead方法:解析生成并生成对象实例,主要通过反射完成,根据类名,加载指定类,并取得该类的Class对象使用Class对象实例化该类,获取一个对象。逐个设置对象字段的值,这里采用反射调用setter Method方式

  loadBeans方法:在SourceReader的实现类中所实现,主要作用是加载Xml中所定义的Bean内容,并将其属性信息存入BeanInfo类中。

  parseBean(beanInfo):分析在loadBeans方法所存入BeanInfo,并通过反射调用注入到Person类中。

  

package ioc.factory;

import ioc.info.BeanInfo;
import ioc.reader.SourceReader; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map; /**
* Created by zzf on 2016/10/26.
*/
public abstract class AbstractBeanFactory implements BeanFactory {
private String filePath;
private Map<String,BeanInfo> container;
protected SourceReader reader;
public AbstractBeanFactory(String filePath){
this.filePath=filePath; } /**
*
* @param reader
*/
protected abstract void setReader(SourceReader reader); //注册bean
public void registerBeans(){
this.container=this.reader.loadBeans(filePath);
} @Override
public Object getBean(String name) {
BeanInfo beanInfo=this.container.get(name);
if(beanInfo==null){
return null; }else {
return this.parseBean(beanInfo);
} } /**
*
* @param beanInfo 指定对象的描述信息
* @return
*/
protected Object parseBean(BeanInfo beanInfo){
Class clazz;
try {
//加载Bean的实例
clazz=Class.forName(beanInfo.getType());
Object bean=clazz.newInstance(); //获取该对象下的所有方法,包括私有
Method[] methods=clazz.getDeclaredMethods(); for (String property:beanInfo.getProperties().keySet()){
String setter="set"+ firstCharToUp(property);
for(Method method:methods){
String methodName=method.getName(); if(methodName.equals(setter)){
Object value=beanInfo.getProperties().get(property);
//使用反射调用set方法
method.invoke(bean,value);
}
}
}
return bean; } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
} private String firstCharToUp(String property) {
System.out.println(property);
char [] c=property.toCharArray();
String first=String.valueOf(c[0]).toUpperCase();
c[0]=first.charAt(0);
System.out.println(String.valueOf(c));
return String.valueOf(c);
}
}
  
SourceReader:注册读取器接口,设计顶层读取器的抽象方法,负责读取用户注册的对象,继承该接口的类可以实现多种读取方式,如从配置文件中读取,根据标注读取,从网络中读取等等
package ioc.reader;

import ioc.info.BeanInfo;

import java.util.Map;

/**
* Created by zzf on 2016/10/26.
*
*/
public interface SourceReader {
Map<String,BeanInfo> loadBeans(String filePath);
}

  XMLContext:XML的上下文,继承了AbstractBeanFactory,里面比较重要的方法是setReader(),在父类中该方法是抽象方法, 这样的做的意义是交由子类实现自己所想要的读取方式。该方法中指明注册读取器(这里采用的XML,读者可以根据兴趣去实现另外的方式如注解)并在构造该方法时一次性加载注册的对象。

  

package ioc.context;

import ioc.factory.AbstractBeanFactory;
import ioc.reader.SourceReader;
import ioc.reader.XMLSourceReader; /**
* Created by zzf on 2016/10/26.
* 上下文的构造方法
*
*/
public class XMLContext extends AbstractBeanFactory{ public XMLContext(String filePath){
super(filePath);
this.setReader(new XMLSourceReader());
super.registerBeans();
} @Override
protected void setReader(SourceReader reader) {
super.reader=reader;
}
}

  XmlContext类:继承了AbstractBeanFactory抽象类,进行Bean的注册和注册XML的读取器,注意返回的是Map<String, BeanInfo>

  

package ioc.reader;

import ioc.info.BeanInfo;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader; import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; /**
* Created by zzf on 2016/10/26.
* 使用 dom4j进行Xml的读取操作
*/
public class XMLSourceReader implements SourceReader { @Override
public Map<String, BeanInfo> loadBeans(String filePath) {
//读取指定的配置文件
SAXReader reader = new SAXReader();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); //从class目录下获取指定的xml文件
InputStream ins = classLoader.getResourceAsStream(filePath);
Document doc = null;
try {
doc = reader.read(ins);
} catch (DocumentException e) {
e.printStackTrace();
}
//获得根节点
Element root = doc.getRootElement();
Map<String,BeanInfo>beanInfoMap=new HashMap<String, BeanInfo>();
//遍历bean
for (Iterator i = root.elementIterator("bean"); i.hasNext();){
Element element = (Element) i.next();
//获取bean的属性id和class
Attribute id = element.attribute("id");
Attribute cls = element.attribute("class");
try {
//利用Java反射机制,通过class的名称获取Class对象
Class bean=Class.forName(cls.getText());
//获取对应class的信息
java.beans.BeanInfo info= Introspector.getBeanInfo(bean);
//获取其属性描述
PropertyDescriptor [] propertyDescriptors=info.getPropertyDescriptors();
Method method;
Object object=bean.newInstance();
BeanInfo beanInfo=new BeanInfo();
for(Iterator iterator=element.elementIterator("property");iterator.hasNext();){
Element foo2= (Element) iterator.next();
//获取该property的name属性
Attribute name = foo2.attribute("name"); String value = null;
//获取该property的子元素value的值
for(Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) {
Element node = (Element) ite1.next();
value = node.getText();
break;
}
System.out.println("name:"+name.getText()+"value"+value);
for (int j=0;j<propertyDescriptors.length;j++){
if(propertyDescriptors[j].getName().equalsIgnoreCase(name.getText())){
method=propertyDescriptors[j].getWriteMethod();
//利用Java的反射极致调用对象的某个set方法,并将值设置进去
method.invoke(object,value);
//将获取的对象属性信息存入我们自定义的BeanInfo当中
beanInfo.addProperty(name.getText(),value);
}
} beanInfo.setId(id.getText());
beanInfo.setType(cls.getText());
beanInfoMap.put(id.getText(),beanInfo);
}
return beanInfoMap;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IntrospectionException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} return null;
}
}

  

<?xml version="1.0" encoding="UTF-8"?>

<beans>
<bean id="person" class="ioc.bean.Person">
<property name="username">
<value>zzf</value>
</property>
<property name="password">
<value>12345678</value>
</property>
</bean>
</beans>
package ioc;

import ioc.bean.Person;
import ioc.context.XMLContext;
import ioc.factory.BeanFactory; /**
* Created by zzf on 2016/10/26.
*/
public class test {
public static void main(String[] args) {
BeanFactory factory=new XMLContext("configuration/config.xml");
Person person= (Person) factory.getBean("person");
System.out.println(person.getPassword());
System.out.println(person.getUsername());
}
}

 执行main方法后,控制台成功输出xml所定义的属性。

  整体的实现过程主要分以下几步,1.读取Xml文件,怎么读取?(看代码) 2.读取完之后呢?应该有个容器临时存放一下,从而实现方便传递,这里使用的HashMap,从中也可以看到为什么要用BeanInfo类来存放类的信息,3.分析Map<String,BeanInfo>容器,提取里面的Bean属性,4.提取之后,通过反射方法注入到Person类中,从而便实现了username和password属性的注入了。

  再结合我第一篇所讲的SpringIOC概念讲解,相信读者应该能够清晰的认识的IOC的作用和大概的实现过程。

轻松了解Spring中的控制反转和依赖注入(二)的更多相关文章

  1. 轻松了解Spring中的控制反转和依赖注入(一)

    我们回顾一下计算机的发展史,从最初第一台计算机的占地面积达170平方米,重达30吨,到现如今的个人笔记本,事物更加轻量功能却更加丰富,这是事物发展过程中的一个趋势,在技术领域中同样也是如此,企业级Ja ...

  2. Spring中的控制反转和依赖注入

    Spring中的控制反转和依赖注入 原文链接:https://www.cnblogs.com/xxzhuang/p/5948902.html 我们回顾一下计算机的发展史,从最初第一台计算机的占地面积达 ...

  3. Spring IOC&DI 控制反转和依赖注入

    控制反转(Inversion of Control,缩写为IOC),它是把你设计好的对象交给spring控制,而不再需要你去手动 new Object(); 网上对于IOC的解释很多,对程序员而言,大 ...

  4. Spring框架之控制反转和依赖注入

    学Spring框架必须理解控制反转和依赖注入.下面各自举一个例子,来说明控制反转和依赖注入. IOC(控制反转):应用本身创建和维护的依赖对象:现在交由外部容器(Spring)来创建和维护:这个控制权 ...

  5. Spring 04: IOC控制反转 + DI依赖注入

    Spring中的IOC 一种思想,两种实现方式 IOC (Inversion of Control):控制反转,是一种概念和思想,指由Spring容器完成对象创建和依赖注入 核心业务:(a)对象的创建 ...

  6. Spring是什么+控制反转和依赖注入

    Spring是一个开源框架,是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架. 原因: (1)通过控制反转(IOC)达到松耦合,IOC也就是把控制权交出去,在使用中直接得到对象 (2)提 ...

  7. Spring学习02——控制反转、依赖注入

    有两个人,张三和李四 package com.su.service; public class ZhangSan implements Tester{ public void test(){ Syst ...

  8. Spring的IOC控制反转和依赖注入-重点-spring核心之一

    IoC:Inverse of Control(控制反转): 读作"反转控制",更好理解,不是什么技术,而是一种设计思想,好比于MVC.就是将原本在程序中手动创建对象的控制权,交由S ...

  9. 在DoNetMVC中使用控制反转和依赖注入【DI】

    本次是在MVC5中使用Autofac 第一步:程序包管理器控制台 Install-Package Autofac.MVC5 引入nuget包 这样成功之后,会在引用中出现两个DLL,分别是Autofa ...

随机推荐

  1. java多线程学习-多个线程访问对象共享数据的方式

    public class MulitThreadShareData { public static void main(String[] args) { final ShareData1 data1 ...

  2. Python环境下NIPIR(ICTCLAS2014)中文分词系统使用攻略

    一.安装 官方链接:http://pynlpir.readthedocs.org/en/latest/installation.html 官方网页中介绍了几种安装方法,大家根据个人需要,自行参考!我采 ...

  3. hdu 5720 Wool

    hdu 5720 问题描述 黎明时,Venus为Psyche定下了第二个任务.她要渡过河,收集对岸绵羊身上的金羊毛. 那些绵羊狂野不驯,所以Psyche一直往地上丢树枝来把它们吓走.地上现在有n n ...

  4. IT公司笔试题(一)

    1.  已知一个递归算法的算法复杂度计算公式为T(n) = T(n/2) + n,则T(n)的算法复杂度为多少? 解:O(n) T(n) = T(n/2) + n = T(n/4) + n/2 + n ...

  5. nginx配置文件nginx.conf超详细讲解

    #nginx进程,一般设置为和cpu核数一样worker_processes 4;                        #错误日志存放目录 error_log  /data1/logs/er ...

  6. Android NDK构建资料

    Cmake http://blog.csdn.net/u012527560/article/details/51752070  http://wenku.baidu.com/link?url=ENJF ...

  7. wxPython--Python GUI编程参考链接

    原文链接http://www.cnblogs.com/coderzh/archive/2008/11/23/1339310.html

  8. 编写出色的GNU/Linux程序

    http://advancedlinuxprogramming.com提供了本书电子版的免费下载. 1 与执行环境交互 关于参数 C语言程序的main()函数使用两个参数和执行环境交互--(int)a ...

  9. win7电脑共享VPN连接教程

    互通网络VPN服务器不限制连接数,如果仅仅是电脑连接的话有点浪费,如何只在笔记本电脑上设置一次VPN,然后手机.平板等都可以直接共享使用呢?为什么需要笔记本电脑,因为笔记本电脑内置的无线网卡可以设置w ...

  10. springMVC验证码程序

    原文地址:http://my.oschina.net/u/1757031/blog/488322 import java.awt.Color; import java.awt.Font; import ...