自定义Spring框架 (下)

155 阅读5分钟

自定义实现

1.定义实体类

<bean id="userDao" class="dao.impl.UserDaoImpl">
    <property name="username" value="412"></property>
    <property name="password" value="124"></property>
    <property name="age" value="123"></property>
</bean><bean id="userService" class="service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao" ></property>
</bean>

property实例类

package com.itheima.beans;
​
/**
 * @author:wuxie
 * @date:2023/2/16
 * @description:封装bean标签下property标签
 */public class PropertyValue {
​
    private String name;
​
    private String ref;
​
    private String value;
​
    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;
    }
}
​

property集合

package com.itheima.beans;
​
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
​
/**
 * @author:wuxie
 * @date:2023/2/16
 * @description:存储管理多个propertyvalue对象
 */public class MutablePropertyValues implements Iterable<PropertyValue> {
​
    //property集合
    private final List<PropertyValue> propertyValueList;
​
    //通过构造函数进行初始化
    public MutablePropertyValues() {
        this.propertyValueList=new ArrayList<PropertyValue>();
    }
​
    //也可以通过传值进行初始化
    public MutablePropertyValues(List<PropertyValue> propertyValueList) {
        
        //进行一个null判断
        this.propertyValueList = propertyValueList != null ? propertyValueList : new ArrayList<>();
    }
​
    //获得所有property(数组方式)
    public PropertyValue[] getPropertyValues() {
        // 返回类型
        return this.propertyValueList.toArray(new PropertyValue[0]);
    }
​
    //根据名称获取property
    public PropertyValue getPropertyValueByName(String name) {
        for (PropertyValue propertyValue : propertyValueList) {
            if (propertyValue.getName().equals(name)) {
                return propertyValue;
            }
        }
        return null;
    }
​
    //添加property
    public MutablePropertyValues addProperty(PropertyValue propertyValue) {
        for (int i = 0; i < propertyValueList.size(); i++) {
            //先判断该property是否存在,如果存在就进行覆盖
            if              (propertyValueList.get(i).getName().equals(propertyValue.getName())) {
                propertyValueList.set(i, propertyValue);
            }
        }
        //如果不存在就直接添加
        propertyValueList.add(propertyValue);
        return this;
    }
​
    //判断是否有某个property
​
    public boolean contains(String name) {
        return getPropertyValueByName(name) != null;
    }
​
    //判断为空
    public boolean isEmpty() {
        return propertyValueList.isEmpty();
    }
​
​
    //继承Iterable,后续可以使用foreach遍历
    @Override
    public Iterator<PropertyValue> iterator() {
        return propertyValueList.iterator();
    }
​
}
​

bean实体类

package com.itheima.beans;
​
/**
 * @author:wuxie
 * @date:2023/2/16
 * @description:封装Bean标签
 */public class BeanDefinition {
​
    private String id;
    private String className;
​
    //bean对象里面的property
    private MutablePropertyValues mutablePropertyValues;
​
​
    public BeanDefinition() {
        mutablePropertyValues=new MutablePropertyValues();
    }
​
    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 MutablePropertyValues getMutablePropertyValues() {
        return mutablePropertyValues;
    }
​
    public void setMutablePropertyValues(MutablePropertyValues mutablePropertyValues) {
        this.mutablePropertyValues = mutablePropertyValues;
    }
}
​

2.定义注册表相关类

spring里面存在BeanDefinitionRegister注册表, 其作用是存储Bean,创建bean后就存储在注册表里面,并且实现对bean的增删等一系列操作

可以查看spring源码,其提供多种Register,所以设置一个接口

BeanDefinitionRegister 接口

package com.itheima.beans.factory.support;
​
import com.itheima.beans.BeanDefinition;
​
/**
 * @Author:wuxie
 * @Date:2023/2/16
 * @Description:Bean注册中心
 * @ClassName:BeanDefinitionRegistry
 */
public interface BeanDefinitionRegistry {
​
    //注册BeanDefinition对象到注册表中
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
​
    //从注册表中删除指定名称的BeanDefinition对象
    void removeBeanDefinition(String beanName) throws Exception;
​
    //根据名称从注册表中获取BeanDefinition对象
    BeanDefinition getBeanDefinition(String beanName) throws Exception;
​
    boolean containsBeanDefinition(String beanName);
​
    int getBeanDefinitionCount();
​
    String[] getBeanDefinitionNames();
}

SimpleBeanDefinitionRegister 简单实现类

package com.itheima.beans.factory.support;
​
import com.itheima.beans.BeanDefinition;
import org.springframework.context.annotation.Bean;
​
import java.util.HashMap;
import java.util.Map;
​
/**
 * @Author:wuxie
 * @Date:2023/2/16
 * @Description: 注册中心接口实现类
 * @ClassName:SimpleBeanDefinitionRegistry
 */public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {
​
    //设置一个map集合,进行对bean的存储
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
​
    @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 int getBeanDefinitionCount() {
        return beanDefinitionMap.size();
    }
​
    @Override
    public String[] getBeanDefinitionNames() {
        return beanDefinitionMap.keySet().toArray(new String[0]);
    }
}
​

3.定义解析器相关类

根据上面介绍的过程得, 我们需要从application.xml里面获得相关数据, 就需要设置一个解析xml文件的工具, 即为解析器

spring里面的解析器也有很多, 所以也设置一个接口, 并且只实现一个xml的解析器

BeanDefinitionReader 接口

package com.itheima.beans.factory.support;
​
/**
 * @Author:wuxie
 * @Date:2023/2/16
 * @Description:解析配置文件
 * @ClassName:BeanDefinitionReader
 */
public interface BeanDefinitionReader {
​
    //这里定义该方法,是为了解析bean后,将其放入注册表里面
    BeanDefinitionRegistry getRegistr();
​
    //加载配置
    void loadBeanDefinitions(String configLocation) throws Exception;
}
​

解析xml使用到dom4j 工具

 Dom4j通过XMLWriter将Document对象表示的XML树写入指定的文件,并使用OutputFormat格式对象指定写入的风格和编码方法。 ::: details

<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.1</version>
</dependency>

XmlBeanDefinitionReader xml解析器

package com.itheima.beans.factory.xml;
​
import com.itheima.beans.BeanDefinition;
import com.itheima.beans.MutablePropertyValues;
import com.itheima.beans.PropertyValue;
import com.itheima.beans.factory.support.BeanDefinitionReader;
import com.itheima.beans.factory.support.BeanDefinitionRegistry;
import com.itheima.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
​
import java.io.InputStream;
import java.util.List;
​
/**
 * @Author:wuxie
 * @Date:2023/2/16
 * @Description:针对xml文件解析器
 * @ClassName:XmlBeanDefinitionReader
 */public class XmlBeanDefinitionReader implements BeanDefinitionReader {
​
    private BeanDefinitionRegistry beanDefinitionRegistry;
​
    //通过构造,初始化注册表
​
    public XmlBeanDefinitionReader() {
        beanDefinitionRegistry = new SimpleBeanDefinitionRegistry();
    }
​
    //获得注册表
    @Override
    public BeanDefinitionRegistry getRegistr() {
        return beanDefinitionRegistry;
    }
​
    //加载配置文件
​
    @Override
    public void loadBeanDefinitions(String configLocation) throws Exception {
        //当使用Class.getClassLoader.getResourceAsStream()加载资源时,是从classPath路径下进行加载,放在resources下的文件加载时不能加(“/”)。
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(configLocation);
        //dom4j的工具
        SAXReader saxReader = new SAXReader();
        Document read = saxReader.read(inputStream);
        //获得整个xml文件的根元素
        Element rootElement = read.getRootElement();
        parseBean(rootElement);
​
    }
​
    private void parseBean(Element rootElement) {
        //获得名为bean的元素
        List<Element> elements = rootElement.elements("bean");
        for (Element element : elements) {
            //获得bean里面的id和class
            String id = element.attributeValue("id");
            String className = element.attributeValue("class");
​
            //创建bean
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setId(id);
            beanDefinition.setClassName(className);
​
            //再获取该bean下的property
            List<Element> property = element.elements("property");
            MutablePropertyValues propertyValues = new MutablePropertyValues();
            for (Element element1 : property) {
                String name = element1.attributeValue("name");
                String value = element1.attributeValue("value");
                String ref = element1.attributeValue("ref");
                PropertyValue propertyValue = new PropertyValue(name, ref, value);
                propertyValues.addProperty(propertyValue);
​
            }
​
            //最后在bean下存入property       
            beanDefinition.setMutablePropertyValues(propertyValues);
            beanDefinitionRegistry.registerBeanDefinition(id, beanDefinition);
​
        }
​
    }
}
​

4.定义IOC容器相关类

前面做了对bean的获取和存储,剩下的就是获取,spring里面是设置一个IOC容器,来获得bean

BeanFactory接口

package com.itheima.beans.factory;
​
/**
 * @Author:wuxie
 * @Date:2023/2/16
 * @Description:(TODO)描述该类
 * @ClassName:BeanFactory
 */
public interface BeanFactory {
    
    //根据bean名获得bean
    Object getBean(String name) throws Exception;
​
    
    //根据bean名称和类型获得bean
    <T> T getBean(String name, Class<? extends T> clazz) throws Exception;
}
​

最初使用spring的时候,有两种创建容器的方法。一是ClassPathXmlApplicationContext,二是XmlBeanFactory。两者区别在前者是无延迟加载,默认行为是在启动服务器时将所有 singleton bean提前进⾏实例化。提前实例化意味着作为初始化过程的⼀部分,ApplicationContext 实例会创建并配置所有的singleton bean。

这里我们就需要新增方法

ApplicationContext

package com.itheima.context;
​
import com.itheima.beans.factory.BeanFactory;
​
/**
 * @Author:wuxie
 * @Date:2023/2/16
 * @Description:非延时加载bean
 * @ClassName:ApplicationContext
 */
public interface ApplicationContext extends BeanFactory {
    
    //使用refresh进行实现对bean的加载和配置
    void refresh() throws IllegalStateException, Exception;
}
​

AbstractApplicationContext

ApplicationContext接口的子实现类/,用于立即加载

设置为抽象类,实现方法交给子类

package com.itheima.context.support;
​
import com.itheima.beans.BeanDefinition;
import com.itheima.beans.factory.support.BeanDefinitionReader;
import com.itheima.beans.factory.support.BeanDefinitionRegistry;
import com.itheima.context.ApplicationContext;
​
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
​
/**
 * @Author:wuxie
 * @Date:2023/2/16
 * @Description:(TODO)描述该类
 * @ClassName:AbstractApplicationContext
 */public abstract class AbstractApplicationContext implements ApplicationContext {
​
    //声明解析器
​
    protected BeanDefinitionReader beanDefinitionReader;
​
    //存储bean的容器
    protected Map<String, Object> map = new HashMap<String, Object>();
​
    //配置文件路径
​
    protected String configLocation;
​
    @Override
    public void refresh() throws IllegalStateException, Exception    {
        beanDefinitionReader.loadBeanDefinitions(configLocation);
        finishBeanInitialization();
    }
​
    private void finishBeanInitialization() throws Exception {
        //获取注册表对象
        BeanDefinitionRegistry registr = beanDefinitionReader.getRegistr();
​
        String[] beanDefinitionNames = registr.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = registr.getBeanDefinition(beanDefinitionName);
            //获得bean
            getBean(beanDefinitionName);
        }
​
    }
}
​

ClassPathXmlApplicationContext IOC实现类

package com.itheima.context.support;
​
import com.itheima.beans.BeanDefinition;
import com.itheima.beans.MutablePropertyValues;
import com.itheima.beans.PropertyValue;
import com.itheima.beans.factory.support.BeanDefinitionRegistry;
import com.itheima.beans.factory.xml.XmlBeanDefinitionReader;
import com.itheima.utils.StringUtils;
import org.springframework.context.support.AbstractXmlApplicationContext;
​
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Objects;
​
/**
 * @Author:wuxie
 * @Date:2023/2/16
 * @Description:IOC容器实现类
 * @ClassName:ClassPathXmlApplicationContext
 */public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
​
    public ClassPathXmlApplicationContext(String configLocation) {
        this.configLocation = configLocation;
        beanDefinitionReader = new XmlBeanDefinitionReader();
        //在构造时进行初始化bean
        try {
            this.refresh();
        } catch (Exception e) {
        }
    }
​
    //根据bean名称获取
    @Override
    public Object getBean(String name) throws Exception {
        
        //从IOC容器里面的map里面获取bean
        Object object = map.get(name);
        if (object != null) {
            return object;
        }
​
        //
        BeanDefinitionRegistry registr = beanDefinitionReader.getRegistr();
        BeanDefinition beanDefinition = registr.getBeanDefinition(name);
        if (beanDefinition == null) {
            return null;
        }
        //通过bean的class路径创建UserDao对象
        String className = beanDefinition.getClassName();
        Class<?> clazz = Class.forName(className);
        Object beanObj = clazz.newInstance();
​
        //依赖注入,将userDao数据传入userService里面
  //<bean id="userService"class="service.impl.UserServiceImpl">
    //<property name="userDao" ref="userDao" ></property>
  //</bean>
​
        MutablePropertyValues mutablePropertyValues = beanDefinition.getMutablePropertyValues();
        for (PropertyValue mutablePropertyValue : mutablePropertyValues) {
            String propertyName = mutablePropertyValue.getName();
            String value = mutablePropertyValue.getValue();
            String ref = mutablePropertyValue.getRef();
            if (ref != null && !"".equals(ref)) {
                //注意这里又使用到了自身,后续容易导致依赖循环
                Object bean = getBean(ref);
                String setterMethodNameByFieldName = 
//通过名字来获得set方法                    
        StringUtils.getSetterMethodNameByFieldName(propertyName);
                //遍历clazz(UserDao)下的方法
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (method.getName().equals(setterMethodNameByFieldName)) {
                        //如果存在就执行
                        method.invoke(beanObj, bean);
                  //property里面ref和value只能二存一
                        break;
                    }
                }
            }
​
            if (value != null && !"".equals(value)) {
                String methodName = StringUtils.getSetterMethodNameByFieldName(propertyName);
                Method method = clazz.getMethod(methodName, String.class);
                method.invoke(beanObj, value);
            }
​
        }
        
        //存入已获得的bean
        map.put(name, beanObj);
        return beanObj;
    }
​
    @Override
    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
        
        //调用上方getbean
        Object bean = getBean(name);
        if (bean == null) {
            return null;
        }
        //Class.cast(Object obj)方法 就是作用就是强制类型转换。将obj转化成T类型。
        return clazz.cast(bean);
    }
​
}
​

StringUtils 通过变量名字来获得对应的set方法

package com.itheima.utils;
​
/**
 * @Author:wuxie
 * @Date:2023/2/16
 * @Description:通过变量名字来获得对应的set方法
 * @ClassName:StringUtils
 */public class StringUtils {
​
​
    //userDao==>setUserDao
    public static String getSetterMethodNameByFieldName(String filename) {
        String methodName="set"+filename.substring(0,1).toUpperCase()+ filename.substring(1);
        return methodName;
    }
}
​

5.导入该框架

1.注入依赖

2.添加依赖

<dependencies>
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>spring_demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

3.测试

代码与一开始spring使用一样 :::details

userDao被创建了
userService被创建了
UserService ...
UserDao ...412==124age:123

测试是否是非延迟加载

设置断点进行调试,

可见

ApplicationContext applicationContext = new 
ClassPathXmlApplicationContext("applicationContext.xml");

运行后就实例了bean

到此springIOC框架简单自定义就结束了,over