Spring框架自定义实现IOC基础功能/IDEA如何手动实现IOC功能

15 篇文章 0 订阅

继续整理记录这段时间来的收获,详细代码可在我的Gitee仓库Java设计模式克隆下载学习使用!

7.4 自定义Spring IOC

创建新模块,结构如图![[Pasted image 20230210173222.png]]

7.4.1 定义bean相关POJO类

7.4.1.1 定义propertyValue类
/**  
 * @Author:Phil  
 * @ClassName: PropertyValue  
 * @Description:  
 * 用来封装bean标签下的property标签属性  
 * name属性  
 * ref属性  
 * value属性:给基本数据类型及String类型赋值  
 * @Date 2023/2/8 21:45  
 * @Version: 1.0  
 **/public class propertyValue {  
    private String name;  
    private String ref;  
    private String value;  
    public propertyValue() {  
    }  
    public propertyValue(String name, String ref, String value) {  
        this.name = name;  
        this.ref = ref;  
        this.value = value;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public String getRef() {  
        return ref;  
    }  
    public void setRef(String ref) {  
        this.ref = ref;  
    }  
    public String getValue() {  
        return value;  
    }  
    public void setValue(String value) {  
        this.value = value;  
    }  
}
7.4.1.2 定义MultiplePropertyValue类

一个bean 标签可以有多个property子标签,故用multiplePropertyValue类来存储PropertyValue对象

public class MultiplePropertyValues implements Iterable<PropertyValue>{  
//    定义list集合对象,用来存储PropertyValue对象  
    private final List<PropertyValue> propertyValueList;  
  
    public MultiplePropertyValues() {  
        this.propertyValueList = new ArrayList<PropertyValue> ();  
    }  
  
    public MultiplePropertyValues(List<PropertyValue> propertyValueList) {  
        if(propertyValueList == null)  
            this.propertyValueList = new ArrayList<PropertyValue>();  
        else            this.propertyValueList = propertyValueList;  
    }  
//    获取所有propertyValue对象,以数组形式返回  
    public PropertyValue[] getPropertyValues(){  
        return propertyValueList.toArray(new PropertyValue[0]);  
    }  
//    根据name属性值返回对应PropertyValue对象  
    public PropertyValue getPropertyValues(String propertyName){  
//        遍历集合返回  
        for (PropertyValue propertyValue : propertyValueList) {  
            if(propertyValue.getName().equals(propertyName))  
                return propertyValue;  
        }  
        return null;  
    }  
//    判断集合是否为空  
    public boolean isEmpty(){  
        return propertyValueList.isEmpty();  
    }  
//    添加PropertyValue对象  
    public MultiplePropertyValues addPropertyValue(PropertyValue pv){  
//        若有则进行覆盖  
        for (int i = 0; i < propertyValueList.size(); i++) {  
            if(propertyValueList.get(i).getName().equals(pv.getName())){  
                propertyValueList.set(i,pv);  
                return this;//目的是链式编程  
            }  
        }  
//            添加新的  
        this.propertyValueList.add(pv);  
        return this;    }  
//    判断是否有指定name的PropertyValue对象  
    public boolean contains(String propertyName){  
        return getPropertyValues(propertyName) != null;  
    }  
//    获取迭代器对象  
    @Override  
    public Iterator<PropertyValue> iterator() {  
        return propertyValueList.iterator();  
    }  
}
7.1.4.3 BeanDefinition类

BeanDefinition类用来封装bean信息,主要包含id(bean 名称),class(bean全类名)及子标签property对象数据

public class BeanDefinition {  
    private String id;  
    private String className;  
    private MultiplePropertyValues multiplePropertyValues;  
  
    public BeanDefinition() {  
        multiplePropertyValues = new MultiplePropertyValues();  
    }  
  
    public String getId() {  
        return id;  
    }  
  
    public void setId(String id) {  
        this.id = id;  
    }  
  
    public String getClassName() {  
        return className;  
    }  
  
    public void setClassName(String className) {  
        this.className = className;  
    }  
  
    public MultiplePropertyValues getMultiplePropertyValues() {  
        return multiplePropertyValues;  
    }  
  
    public void setMultiplePropertyValues(MultiplePropertyValues multiplePropertyValues) {  
        this.multiplePropertyValues = multiplePropertyValues;  
    }  
}

7.4.2 定义注册表类

7.4.2.1 定义BeanDefinitionRegistry接口

BeanDefinitionRegistry接口定义了注册表相关操作,定义如下功能:

  • 注册BeanDefinition对象到注册表中
  • 根据名称从注册表中获取后去BeanDefinition对象
  • 从注册表中删除指定名称的BeanDefinition对象
  • 判断注册表中是否包含指定名称的BeanDefinition对象
  • 获取注册表中BeanDefinition对象个数
  • 获取注册表中所有的Bean
public interface BeanDefinitionRegistry {  
    //往注册表中注册bean  
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) ;  
    //从注册表删掉指定名称bean  
    void removeBeanDefinition(String beanName) throws Exception;  
    //获取指定名称bean  
    BeanDefinition getBeanDefinition(String beanName) throws Exception;  
    //判断是否包含指定名称bean  
    boolean containsBeanDefinition(String beanName);  
    //获取所有bean  
    String[] getBeanDefinitionNames();  
  
    int getBeanDefinitionCount();  
  
    boolean isBeanNameInUse(String var1);  
}
7.4.2.2 SimpleBeanDefinitionRegistry类
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry{  
//    创建容器,用于存储  
    Map<String,BeanDefinition> beanDefinitionMap = new HashMap<String,BeanDefinition>();  
    @Override  
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {  
        beanDefinitionMap.put(beanName,beanDefinition);  
    }  
  
    @Override  
    public void removeBeanDefinition(String beanName) throws Exception {  
        beanDefinitionMap.remove(beanName);  
    }  
  
    @Override  
    public BeanDefinition getBeanDefinition(String beanName) throws Exception {  
        return beanDefinitionMap.get(beanName);  
    }  
  
    @Override  
    public boolean containsBeanDefinition(String beanName) {  
        return beanDefinitionMap.containsKey(beanName);  
    }  
  
    @Override  
    public String[] getBeanDefinitionNames() {  
        return beanDefinitionMap.keySet().toArray(new String[0]);  
    }  
  
    @Override  
    public int getBeanDefinitionCount() {  
        return beanDefinitionMap.size();  
    }  
  
    @Override  
    public boolean isBeanNameInUse(String var1) {  
        return beanDefinitionMap.containsKey(var1);  
    }  
}

7.4.3 定义解析器类

7.4.3.1 BeanDefinitionReader接口

BeanDefinitionReader用来解析配置文件并在注册表中注册bean的信息,定义了两规范:

  • 获取注册表功能,让外界可通过该对象获取注册表对象
  • 加载配置文件,并注册bean数据
public interface BeanDefinitionReader{
	//获取注册表对象
	BeanDefinitionRegistry getRegistry();
	//加载配置文件斌在注册表中进行注册
	void loadBeanDefinitions(String configuration);
}
7.4.3.2 XmlBeanDefinitionReader类

XmlBeanDefinitionReader类是专门来解析xml配置文件,实现了BeanDefinitionReader接口的两个功能。

public class XmlBeanDefinitionReader implements BeanDefinitionReader {  
//    声明注册表对象  
    private BeanDefinitionRegistry registry;  
  
    public XmlBeanDefinitionReader() {  
        this.registry = new SimpleBeanDefinitionRegistry();  
    }  
  
    @Override  
    public BeanDefinitionRegistry getRegistry() {  
        return registry;  
    }  
  
    @Override  
    public void loadBeanDefinitions(String configuration) throws Exception{  
//        使用dom4j进行xml配置文件的解析  
        SAXReader saxReader = new SAXReader();  
//        后去类路径下的配置文件  
        InputStream resourceAsStream = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configuration);  
        Document document = saxReader.read(resourceAsStream);  
//        根据Document对象获取根标签对象(beans)  
        Element rootElement = document.getRootElement();  
//       获取根标签下所有的bean标签对象  
        List<Element> elements = rootElement.elements("bean");  
//        遍历集合  
        for (Element element : elements) {  
//            获取id属性  
            String id = element.attributeValue("id");  
//            获取className  
            String className = element.attributeValue("class");  
//          将id和className封装到BeanDefinition对象中  
//          创建BeanDefinition对象  
            BeanDefinition beanDefinition = new BeanDefinition();  
            beanDefinition.setId(id);  
            beanDefinition.setClassName(className);  
//            创建MultiplePropertyValue对象  
            MultiplePropertyValues multiplePropertyValues = new MultiplePropertyValues();  
//          获取bean标签下的所有property标签对象  
            List<Element> propertyElements = element.elements("property");  
            for (Element propertyElement : propertyElements) {  
                String name = propertyElement.attributeValue("name");  
                String ref = propertyElement.attributeValue("ref");  
                String value = propertyElement.attributeValue("value");  
                PropertyValue propertyValue = new PropertyValue(name, ref, value);  
                multiplePropertyValues.addPropertyValue(propertyValue);  
            }  
//            将multiplePropertyValues封装到BeanDefinition中  
            beanDefinition.setMultiplePropertyValues(multiplePropertyValues);  
//            将BeanDefinition注册到注册表中  
            registry.registerBeanDefinition(id,beanDefinition);  
        }  
    }  
}

7.4.4 容器相关类

7.4.4.1 BeanFactory接口

该接口定义IOC容器的统一规范即获取bean对象

public interface BeanFactory{
	//根据bean对象的名称获取bean对象
	Object getBean(String name) throws Exception;
	//根据bean对象的名称获取bean对象,并进行类型转换
	<T> T getBean(String name,Class<? extends T> clazz) throws Exception;
}
7.4.4.2 ApplicationContext接口

该接口的子实现类对bean 对象的创建都是非延时的,所以该接口定义refresh方法,主要有两功能:

  • 加载配置文件
  • 根据注册表中BeanDefinition对象封装的数据进行bean对象的创建
public interface ApplicationContext extends BeanFactory{  
    void refresh()throws Exception;  
}
7.4.4.3 AbstractApplicationContext接口
  • 作为ApplicationContext接口的子类,故该类是非延时加载,故需要在该类中定义Map集合,作为bean对象存储容器
  • 声明BeanDefinition类型变量,用来进行xml配置文件解析,符合单一职责原则
  • BeanDefinition类型对象创建交由子类实现,子类明确创建BeanDefinitionReader
public abstract class AbstractApplicationContext implements ApplicationContext {  
//    声明解析器对象  
    protected BeanDefinitionReader beanDefinitionReader;  
//   存储bean容器,key存储的bean的id,value是bean对象  
    protected Map<String,Object> singletonObject = new HashMap<String,Object>();;  
//    存储配置文件路径  
    String configLocation;  
    public void refresh() throws Exception{  
//   加载BeanDefinition  
        beanDefinitionReader.loadBeanDefinitions(configLocation);  
//       初始化bean  
        finishBeanInitialization();  
    }  
    public void finishBeanInitialization() throws Exception{  
//       获取注册表对象  
        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();  
//        获取BeanDefinition对象  
        String [] beanNames = registry.getBeanDefinitionNames();  
//        初始化bean  
        for (String beanName : beanNames) {  
            getBean(beanName);  
        }  
    }  
}
7.4.4.4 ClassPathXmlApplicationContext接口

该类主要是加载类路径下的配置文件,并进行bean对象的创建,主要有以下功能:

  • 在构造方法中,创建BeanDefinitionReader对象
  • 在构造方法中,调用refresh方法,用于进行配置文件加载,创建bean对象并存储到容器中
  • 重写父类中的getBean方法,并实现依赖注入
public class ClassPathXmlApplicationContext extends AbstractApplicationContext{  
    public ClassPathXmlApplicationContext(String configLocation){  
        this.configLocation = configLocation;  
//        构建解析器对象  
        beanDefinitionReader = new XmlBeanDefinitionReader();  
        try {  
            this.refresh();  
        }catch (Exception exception){  
            exception.printStackTrace();  
        }  
    }  
//    根据bean对象的名称获取bean对象  
    @Override  
    public Object getBean(String name) throws Exception {  
//        判断对象容器中是否包含指定bean对象,若包含则返回,否则创建  
        Object object = singletonObject.get(name);  
        if(object != null)  
            return object;  
//        获取BeanDefinition对象  
        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();  
        BeanDefinition beanDefinition = registry.getBeanDefinition(name);  
//       获取bean信息中的className  
        String className = beanDefinition.getClassName();  
//        通过反射获取对象  
        Class<?> clazz = Class.forName(className);  
        Object instance = clazz.newInstance();  
//        进行依赖注入操作  
        MultiplePropertyValues multiplePropertyValues = beanDefinition.getMultiplePropertyValues();  
        for (PropertyValue propertyValue : multiplePropertyValues) {  
//            获取name属性值  
            String propertyValueName = propertyValue.getName();  
//            获取value值  
            String value = propertyValue.getValue();  
//            获取ref值  
            String ref = propertyValue.getRef();  
            if(ref != null && !"".equals(ref)){  
//                获取依赖的对象  
                Object bean = getBean(ref);  
//                拼接方法名  
                String setterMethodByField = StringUtils.getSetterMethodByField(propertyValueName);  
//                获取所有方法  
                Method[] methods = clazz.getMethods();  
                for (Method method : methods) {  
                    if(method.getName().equals(setterMethodByField))  
//                        执行setter方法  
                        method.invoke(instance,bean);  
                }  
            }  
            if(value != null && !"".equals(value)){  
//                拼接方法名  
                String methodName = StringUtils.getSetterMethodByField(propertyValueName);  
//                获取method对象  
                Method method = clazz.getMethod(methodName, String.class);  
                method.invoke(instance, value);  
            }  
        }  
//        在返回instance对象之前,将该对象存储到map容器中  
        singletonObject.put(name,instance);  
        return instance;  
    }  
  
    @Override  
    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {  
        Object bean = getBean(name);  
        if(bean == null)  
            return null;  
        return clazz.cast(bean);  
    }  
}
7.4.4.5 测试

将前文回顾Spring框架项目中的pom文件的spring-context依赖换为上述新建项目依赖,如图
![[Pasted image 20230210173346.png]]
运行后如图
![[Pasted image 20230210173447.png]]

7.4.5 总结

7.4.5.1 使用到的设计模式
  • 工厂模式:工厂模式+ 配置文件
  • 单例模式。Spring IOC管理的bean都是单例的,此处单例不是通过构造器进行单例构建,且框架对每个bean只创建一个对象。
  • 模板方法模式。AbstractApplicationContext类中的finishInitialization方法调用getBean方法,因为getBean实现和环境有关。
  • 迭代器模式。其中MultiplePropertyValyes类使用了迭代器模式,因为此类存储并管理PropertyValue对象,也属于一个容器。
  • 还使用了很多设计模式,如AOP使用到了代理模式,选择JDK代理或CGLIB代理使用了策略模式,还有适配器模式,装饰者模式,观察者模式等。
7.4.5.2 符合大部分设计原则
7.4.5.3 整个设计和Spring设计还有一定出入

Spring框架底层是很复杂的,进行了很深入的封装,并对外提供了很好的扩展性,自定义Spring IOC容器有两目的:

  • 了解Spring底层对对象的大体管理机制
  • 了解设计模式在具体开发中的使用
  • 以后学习Spring源码,通过该案例实现,可以降低Spring学习入门成本
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值