Spring中有两大核心内容:
一.IOC
二.AOP
今天我手写实现了IOC,来总结一下IOC的原理和实现方式
首先IOC底层所用到的技术
1>xml配置文件
2>dom4j解析xml
3>工厂设计模式
4>反射
5>内省
首先看一下IOC demo的流程图
运行环境
1.jdk1.8,IntelliJ IDEA
ps(如果报错报找不到xml文件,请在target目录下的classes中手动复制)
首先我们构建bean类:
package com.example.writeioc.config;
import java.util.ArrayList;
import java.util.List;
public class Bean {
private String name;
private String className;
private List<Property> properties = new ArrayList<Property>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public List<Property> getProperties() {
return properties;
}
public void setProperties(List<Property> properties) {
this.properties = properties;
}
@Override
public String toString(){
return "Bean[name = ]" + name + " , className = " + className + " , properties" + properties + " ]";
}
}
然后实例写两个bean类
package com.example.writeioc.bean;
public class A {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.example.writeioc.bean;
public class B {
private A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
然后写一个属性类(Property):
package com.example.writeioc.config;
public class Property {
private String name;
private String value;
private String ref;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
}
工厂接口:
package com.example.writeioc.main;
public interface BeanFactory {
//根据bean的name获得bean对象
Object getBean(String beanName);
}
xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean name="A" class="com.example.writeioc.bean.A">
<property name="name" value="Tom"></property>
</bean>
<bean name="B" class="com.example.writeioc.bean.B">
<property name ="a" ref="A"></property>
</bean>
</beans>
解析xml类(ConfigManager)
package com.example.writeioc.config;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ConfigManager {
//读取配置文件,并返回结果
public static Map<String,Bean> getConfig(String path){
Map<String,Bean> map = new HashMap<String,Bean>();
//1创建解析器
SAXReader saxReader = new SAXReader();
//2加载配置文件
InputStream is = ConfigManager.class.getResourceAsStream(path);
Document document = null;
try{
document = saxReader.read(is);
}catch (DocumentException e){
e.printStackTrace();
throw new RuntimeException("请检查xml配置");
}
//3定义xpath表达式去除所有bean元素
// //从任意节点选择名称为bean的节点 /从父节点选择 不写则是从当前节点选择
String xpath = "//bean";
//4对bean元素进行遍历
List<Element> list = document.selectNodes(xpath);
if(list != null){
for(Element beanFile:list){
Bean bean = new Bean();
//将class,name等属性封装到bean对象中
String name = beanFile.attributeValue("name");
String className = beanFile.attributeValue("class");
bean.setName(name);
bean.setClassName(className);
//获得bean元素下所有property元素,并将其属性封装到property子元素中
List<Element> children = beanFile.elements("property");
if(children != null){
for(Element child : children){
Property property = new Property();
String pName = child.attributeValue("name");
String pValue = child.attributeValue("value");
String pRef = child.attributeValue("ref");
property.setName(pName);
property.setRef(pRef);
property.setValue(pValue);
//将property对象封装到bean中
bean.getProperties().add(property);
}
}
//将bean对象封装到map中用于返回
map.put(name,bean);
}
}
return map;
}
}
通过内省注入bean属性类:
package com.example.writeioc.utils;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
/**
* @author Jet
*/
public class BeanUtils {
public static Method getWriteMethod(Object beanObj,String name){
//使用内省实现(基于java反射专门用于操作bean的属性的api)
Method method = null;
try{
//1:分析bean对象-->BeanInfo
BeanInfo beanInfo = Introspector.getBeanInfo(beanObj.getClass());
//2:根据BeanInfo获取所有属性的描述器
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
//3遍历描述器
if(pds != null){
for(PropertyDescriptor pd : pds){
//判断当前属性是否是我们要找的属性
String pName = pd.getName();
if(pName.equals(name)){
method = pd.getWriteMethod();
}
}
}
//4返回找到的set方法
}catch (IntrospectionException e){
e.printStackTrace();
}
//如果没有找到-->抛出异常,提示用户检查是否创建对应的set方法
if(method == null){
throw new RuntimeException("请检查 " + name + "属性的set方法是否创建");
}
return method;
}
}
装配初始化bean类(ClassPathXmlApplicationContext)
package com.example.writeioc.main;
import com.example.writeioc.config.Bean;
import com.example.writeioc.config.ConfigManager;
import com.example.writeioc.config.Property;
import com.example.writeioc.utils.BeanUtils;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class ClassPathXmlApplicationContext implements BeanFactory{
//配置信息
private Map<String,Bean> config;
//用一个Map来做spring的容器,放置spring所管理的对象
private Map<String,Object> context = new HashMap<String,Object>();
//在classPathXmlApplicationContext已创建就初始化容器
@Override
//根据Bean名称获得bean实例
public Object getBean(String beanName){
Object bean = context.get(beanName);
return bean;
}
public ClassPathXmlApplicationContext(String path){
//1读取配置文件获取初始化的bean的信息
config = ConfigManager.getConfig(path);
//2遍历配置,初始化bean
if(config != null){
for(Map.Entry<String,Bean> en : config.entrySet()){
//获取配置中的bean信息
String beanName = en.getKey();
Bean bean = en.getValue();
Object exsitBean = context.get(beanName);
//因为createBean方法也会向context中放置bean,我们在初始化的时候先要查看是否已经存在bean
//如果不存在再创建bean
if(exsitBean == null){
//根据bean配置创建bean对象
Object beanObj = createBean(bean);
//3将初始化的bean放入容器
context.put(beanName,beanObj);
}
}
}
}
//根据bean配置创建bean对象
private Object createBean(Bean bean){
//1获得要创建的bean的class
String className = bean.getClassName();
Class clazz = null;
try{
clazz = Class.forName(className);
}catch (ClassNotFoundException e){
e.printStackTrace();
throw new RuntimeException("请检查bean的class配置 " + className);
}
//将class对应的对象创建出来
Object beanObj = null;
try{
beanObj = clazz.newInstance();
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("bean没有空参构造" + className);
}
//2获得bean的属性,将其注入
if(bean.getProperties() != null){
for(Property property : bean.getProperties()){
//1:简单value注入
//获取要注入的属性名称
String name = property.getName();
//根据属性名称获得注入属性对应的set方法
Method setMethod = BeanUtils.getWriteMethod(beanObj,name);
//创建一个需要注入bean中的属性值
Object parm = null;
if(property.getValue() != null){
//获取要注入的属性值
String value = property.getValue();
parm = value;
}
//2其他bean的注入
if(property.getRef() != null){
//先从容器中查找当前要注入的bean是否已经创建并放入容器中
Object exsitBean = context.get(property.getRef());
if(exsitBean == null){
//如果容器中不存在,则要创建
exsitBean = createBean(config.get(property.getRef()));
//将创建好的bean放入容器
context.put(property.getRef(),exsitBean);
}
parm = exsitBean;
}
//调用set方法注入
try {
setMethod.invoke(beanObj,parm);
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("bean的属性 " + parm + " 没有对应的set方法,或者参数不正确" + className);
}
}
}
return beanObj;
}
}