springIOC原理——自己实现一下不就懂了嘛

使用Spring

只需要引入对应的依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>
</dependencies>

maven自动依赖几个核心包:

测试

//DAO 层
public interface UserDao {
    void add();
}
public class UserDaoImpl implements UserDao {
    public void add() {
        System.out.println("UserDao...");
    }
}

//service 层
public interface UserService {
    void add();
}
public class UserServiceImpl implements UserService {

    private UserDao userDao;
    private String username;
    private String password;


    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void add() {
        userDao.add();
        System.out.println("UserService实现类"+username+"=="+password);
    }
}

使用xml配置文件方式,配置如下:

<beans >
    <!--创建userService,依赖注入dao-->
    <bean id="userService" class="com.sheep.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
        <property name="username" value="sheep"/>
        <property name="password" value="123456"/>
    </bean>

    <!--dao相关的bean-->
    <bean id="userDao" class="com.sheep.dao.impl.UserDaoImpl"/>
</beans>

然后就是controller,用一个main模拟:

public class MainController {
    public static void main(String[] args) throws Exception {
        //创建IOC容器,加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml");
        //获取相应的bean
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}

经过测试是可以正常输出内容的。

相关IOC接口源码

Spring中Bean的创建是典型的工厂模式,这一系列的Bean工厂,即IoC容器,为开发者管理对象之间 的依赖关系提供了很多便利和基础服务,在Spring中有许多IoC容器的实现供用户选择。

最顶层接口是一个BeanFactory,定义了IoC容器的基本功能规范。它的实现有很多。每个接口都有它的使用场合,主要是为了区分在Spring内部操作过程中对象的传递和转化,对对象的 数据访问所做的限制。

在BeanFactory里只对IoC容器的基本行为做了定义,根本不关心你的Bean是如何定义及怎样加载 的。正如我们只关心能从工厂里得到什么产品,不关心工厂是怎么生产这些产品的。 BeanFactory有一个很重要的子接口,就是ApplicationContext接口,该接口主要来规范容器中 的bean对象是非延时加载,即在创建容器对象的时候就对象bean进行初始化,并存储到一个容器中。

要知道工厂是如何产生对象的,我们需要看具体的IoC容器实现,Spring提供了许多IoC容器实现,比如:

  • ClasspathXmlApplicationContext : 根据类路径加载xml配置文件,并创建IOC容器对象。
  • FileSystemXmlApplicationContext :根据系统路径加载xml配置文件,并创建IOC容器对象。
  • AnnotationConfigApplicationContext :加载注解类配置,并创建IOC容器。

BeanDefinition解析

Spring IoC容器管理我们定义的各种Bean对象及其相互关系,而Bean对象在Spring实现中是以 BeanDefinition来描述的,如下面配置文件:

<bean id="userDao" class="com.sheep.dao.impl.UserDaoImpl"></bean>
bean标签还有很多属性:
scope、init-method、destory-method等。

解析id,class这些东西然后封装成一个BeanDefinition,被没有什么特别。

BeanDefinitionReader解析

Bean的解析过程非常复杂,功能被分得很细,因为这里需要被扩展的地方很多,必须保证足够的灵活 性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。这个解析过程主要通过 BeanDefinitionReader来完成,看看Spring中BeanDefinitionReader的类结构图,如下图所示。

总之这个接口就是规范解析配置的规范,比如像解析xml,就有对应的实现做这个事情。

BeanDefinitionRegistry解析

BeanDefinitionReader用来解析bean定义,并封装BeanDefinition对象,而我们定义的配置文 件中定义了很多bean标签,所以就有一个问题,解析的BeanDefinition对象存储到哪儿?答案就是 BeanDefinition的注册中心,而该注册中心顶层接口就是BeanDefinitionRegistry。

也没有什么特别的,这个接口只不过定义的怎么注册,实现类都会有一个map集合来保存解析出来的东西。

从上面类图可以看到BeanDefinitionRegistry接口的子实现类主要有以下几个: (所谓的注册就是保存解析的东西,这里就是解析出来的东西就是BeanDefinition)

  • DefaultListableBeanFactory 在该类中定义了如下代码,就是用来注册bean

    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    
  • SimpleBeanDefinitionRegistry 在该类中定义了如下代码,就是用来注册bean:

    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);
    

创建容器的过程

ClassPathXmlApplicationContext对Bean配置资源的载入是从refresh()方法开始的。

refresh()方法是一个模板方法,规定了 IoC 容器的启动流程,有些逻辑要交给其子类实现。它对 Bean 配置资源进行载入,ClassPathXmlApplicationContext通过调用其父类 AbstractApplicationContext的refresh()方法启动整个IoC容器对Bean定义的载入过程。

自定义SpringIOC

整个工程的结构如下:
在这里插入图片描述

定义bean相关的pojo类

PropertyValue类

用于封装bean的属性,体现到上面的配置文件就是封装bean标签的子标签property标签数据。

/**
 * 每一bean可能会引用其他bean,这个用来封装被引用的bean解析后的属性
 * @Description: 封装被引用的bean解析后的属性
 *  name : 引用的名字,也就是放在容器中的key
 *  ref : 引用类型
 *  value : 基本类型的引用
 */
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;
    }
}
MutablePropertyValues类

一个bean标签可以有多个property子标签,所以再定义一个MutablePropertyValues类,用来存 储并管理多个PropertyValue对象。

/**
 * 一个bean标签可以有多个property子标签,所以再定义一个MutablePropertyValues类,用来存
 * 储并管理多个PropertyValue对象。
 */
public class MutablePropertyValues implements Iterable<PropertyValue> {

    //用一个list集合保存
    private final List<PropertyValue> propertyValueList;

    public MutablePropertyValues() {
        propertyValueList = new ArrayList<PropertyValue>();
    }

    public MutablePropertyValues(List<PropertyValue> propertyValueList) {
        this.propertyValueList = (propertyValueList != null ?
                propertyValueList : new ArrayList<PropertyValue>());
    }

    //获取所有的PropertyValue,并以数组的形式返回
    public PropertyValue[] getPropertyValues(){
        return (PropertyValue[]) propertyValueList.toArray();
    }

    //通过propertyName获取对应的PropertyValue
    public PropertyValue getPropertyValue(String propertyName){
        for (PropertyValue propertyValue : propertyValueList) {
            if (propertyName.equals(propertyValue.getName())){
                return propertyValue;
            }
        }
        return null;
    }

    //迭代器模式,用于遍历
    public Iterator<PropertyValue> iterator() {
        return propertyValueList.iterator();
    }

    //判断是否为空
    public boolean isEmpty(){
        return this.propertyValueList.isEmpty();
    }

    //是否已有这个封装的对象
    public boolean contains(String propertyName) {
        return getPropertyValue(propertyName) != null;
    }

    //添加一个propertyValue到容器中,存在的化进行覆盖
    public MutablePropertyValues addPropertyValue(PropertyValue propertyValue){
        for (int i = 0; i < propertyValueList.size(); i++) {
            PropertyValue tmp = this.propertyValueList.get(i);
            if (tmp.getName().equals(propertyValue.getName())){
                //存在就覆盖
                propertyValueList.set(i,new PropertyValue(propertyValue.getName(),
                        propertyValue.getRef(),propertyValue.getValue()));
            }
        }
        this.propertyValueList.add(propertyValue);
        return this;
    }
}
BeanDefinition类

BeanDefinition类用来封装bean信息的,主要包含id(即bean对象的名称)、class(需要交由 spring管理的类的全类名)及子标签property数据。

/**
 * 解析出来的Bean的配置属性
 */
public class BeanDefinition {
    private String id;
    private String className;

    private MutablePropertyValues propertyValues;

    public BeanDefinition() {
        propertyValues = 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 getPropertyValues() {
        return propertyValues;
    }

    public void setPropertyValues(MutablePropertyValues propertyValues) {
        this.propertyValues = propertyValues;
    }
}

定义注册表相关类

BeanDefinitionRegistry接口

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

  • 注册BeanDefinition对象到注册表中
  • 从注册表中删除指定名称的BeanDefinition对象
  • 根据名称从注册表中获取BeanDefinition对象
  • 判断注册表中是否包含指定名称的BeanDefinition对象
  • 获取注册表中BeanDefinition对象的个数
  • 获取注册表中所有的BeanDefinition的名称
/**
 * 用于注册保存BeanDefinition
 */
public interface BeanDefinitionRegistry {

    void registryBeanDefinition(String beanName, BeanDefinition beanDefinition);

    void removeBeanDefinition(String beanName) throws Exception;

    BeanDefinition getBeanDefinition(String beanName) throws Exception;

    boolean containsBeanDefinition(String beanName);

    int getBeanDefinitionCount();

    String[] getBeanDefinitionsName();
}
SimpleBeanDefinitionRegistry类

该类实现了BeanDefinitionRegistry接口,定义了Map集合作为注册表容器。

public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {

    //既然是需要保存,就需要用到map
    private final Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

    //注册这个BeanDefinition到表中
    public void registryBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName,beanDefinition);
    }

    //删除
    public void removeBeanDefinition(String beanName) throws Exception {
        beanDefinitionMap.remove(beanName);
    }

    //获取这个beanDefinition
    public BeanDefinition getBeanDefinition(String beanName) throws Exception {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        return beanDefinition;
    }

    public boolean containsBeanDefinition(String beanName) {
        return beanDefinitionMap.containsKey(beanName);
    }

    public int getBeanDefinitionCount() {
        return beanDefinitionMap.size();
    }

    public String[] getBeanDefinitionsName() {
        return  beanDefinitionMap.keySet().toArray(new String[0]);
    }
}

定义解析器相关类

BeanDefinitionReader接口

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

  • 获取注册表的功能,让外界可以通过该对象获取注册表对象。
  • 加载配置文件,并注册bean数据。
/**
 * 解析xml并在封装成BeanDefinition的规范接口
 */
public interface BeanDefinitionReader {
    BeanDefinitionRegistry getBeanDefinitionRegistry();
    void loadBeanDefinitions(String classPath) throws Exception;
}
XmlBeanDefinitionReader类

既然要解析xml文件,解析的包是必不可少的:

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

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

public class XmlBeanDefinitionReader implements BeanDefinitionReader {

    private BeanDefinitionRegistry registry;

    public XmlBeanDefinitionReader() {
        this.registry = new SimpleBeanDefinitionRegistry();
    }

    public BeanDefinitionRegistry getBeanDefinitionRegistry() {
        return registry;
    }

    /**
     * 加载配置文件,并解析成对应的BeanDefinition
     * @param classPath
     * @throws Exception
     */
    public void loadBeanDefinitions(String classPath) throws Exception {
        //从类路径下加载
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(classPath);
        //利用dom4j解析配置文件
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(is);
        Element rootElement = document.getRootElement();    //也就是Beans标签
        parseBean(rootElement); //解析这个根节点并封装
    }

    /**
     * 利用dom4j进行解析
     * @param rootElement
     */
    private void parseBean(Element rootElement) {
        //获取所有的标签,这里只有bean标签
        List<Element> elements = rootElement.elements();
        for (Element element : elements) {
            //遍历这些标签,逐个解析
            String id = element.attributeValue("id");
            String className = element.attributeValue("class");

            //封装这些参数
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setId(id);
            beanDefinition.setClassName(className);

            //然后继续解析可能会有的引用依赖,也就是property
            List<Element> list = element.elements("property");
            MutablePropertyValues propertyValues = new MutablePropertyValues();

            //逐个解析
            for (Element element1 : list) {
                String name = element1.attributeValue("name");
                String ref = element1.attributeValue("ref");
                String value = element1.attributeValue("value");

                PropertyValue propertyValue = new PropertyValue(name, ref, value);
                propertyValues.addPropertyValue(propertyValue);
            }
            //将所有的依赖全部注入相应的bean
            beanDefinition.setPropertyValues(propertyValues);
            registry.registryBeanDefinition(id,beanDefinition);
        }
    }
}

IOC容器相关类

BeanFactory接口

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

/**
 * IOC容器的顶级接口,规范怎么获取生产获取Bean,这就是一个典型的工厂模式
 */
public interface BeanFactory {

    Object getBean(String name) throws Exception;
    <T> T getBean(String name,Class<? extends T> clazz) throws Exception;
}
ApplicationContext接口

该接口的所以的子实现类对bean对象的创建都是非延时的,所以在该接口中定义 refresh() 方法, 该方法主要完成以下两个功能:

  • 加载配置文件。
  • 根据注册表中的BeanDefinition对象封装的数据进行bean对象的创建。
public interface ApplicationContext extends BeanFactory {

    void refresh() throws Exception;
}
AbstractApplicationContext类
  • 作为ApplicationContext接口的子类,所以该类也是非延时加载,所以需要在该类中定义一个 Map集合,作为bean对象存储的容器。

  • 声明BeanDefinitionReader类型的变量,用来进行xml配置文件的解析,符合单一职责原则。 BeanDefinitionReader类型的对象创建交由子类实现,因为只有子类明确到底创建 BeanDefinitionReader哪个儿子实现类对象。

public abstract class AbstractApplicationContext implements ApplicationContext {

    //聚合BeanDefinitionReader,用来解析XML,一个类一个功能
    protected BeanDefinitionReader beanDefinitionReader;
    //用来存储bean对象的容器 key存储的是bean的id值,value存储的是bean对象
    protected Map<String,Object> singletonObject = new ConcurrentHashMap<String, Object>();

    protected String classPath;

    public void refresh() throws Exception {
        //加载BeanDefinition
        beanDefinitionReader.loadBeanDefinitions(classPath);
        //初始化bean
        finishBeanInitialization();
    }

    private void finishBeanInitialization() throws Exception {
        BeanDefinitionRegistry registry = beanDefinitionReader.getBeanDefinitionRegistry();
        String[] beanDefinitionsName = registry.getBeanDefinitionsName();
        for (String s : beanDefinitionsName) {
            //经行解析并注册到registry中
            BeanDefinition beanDefinition = registry.getBeanDefinition(s);
            getBean(s);
        }
    }
}

注意:该类finishBeanInitialization()方法中调用getBean()方法使用到了模板方法模式。

ClassPathXmlApplicationContext类

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

  • 在构造方法中,创建BeanDefinitionReader对象。 (用它解析xml)
  • 在构造方法中,调用refresh()方法,用于进行配置文件加载、创建bean对象并存储到容器中。
  • 重写父接口中的getBean()方法,并实现依赖注入操作。
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {

    //初始化
    public ClassPathXmlApplicationContext(String configPath) {
        this.classPath = configPath;
        this.beanDefinitionReader = new XmlBeanDefinitionReader();

        try {
            //初始化容器,把解析到的bean加入IOC
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //通过名字实例化这个bean
    public Object getBean(String beaName) throws Exception {
        Object bean = singletonObject.get(beaName);
        //如果这个bean存在,则从返回,否则通过反射创建这个对象
        if (bean != null){
            return bean;
        }
        //获取registry来找到这个封装的这个BeanDefinition
        BeanDefinitionRegistry registry = beanDefinitionReader.getBeanDefinitionRegistry();
        BeanDefinition beanDefinition = registry.getBeanDefinition(beaName);
        String className = beanDefinition.getClassName();
        //通过反射构建这个对象
        Class<?> aClass = Class.forName(className);
        bean = aClass.newInstance();

        //依赖注入,如果这个对象有依赖
        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
        if (!propertyValues.isEmpty()){
            for (PropertyValue propertyValue : propertyValues) {
                String name = propertyValue.getName();
                String ref = propertyValue.getRef();
                String value = propertyValue.getValue();
                //如果是一个引用类型,则递归调用这个方法进行加载
                if (ref != null && !"".equals(ref)){
                    Object refBean = getBean(ref);
                    //然后把这个refBean通过setter方法注入引用的类
                    String methodName = StringUtil.getSetterMethodByFieldName(name);
                    Method[] methods = aClass.getMethods();
                    for (Method method : methods) {
                        if(method.getName().equals(methodName)) {
                            method.invoke(bean,refBean);
                        }
                    }

                }
                if(value != null && !"".equals(value)){
                    //然后把这个value通过setter方法注入引用的类
                    String methodName = StringUtil.getSetterMethodByFieldName(name);
                    Method method = aClass.getMethod(methodName, String.class);
                    method.invoke(bean,value);
                }

            }
        }

        //创建好了就将这个对象加入容器
        singletonObject.put(beaName,bean);
        return bean;
    }

    //强转一下类型就好了
    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
        Object bean = getBean(name);
        return clazz.cast(bean);
    }
}

至此,IOC容器差不多就是这样出来的,总结一些流程:

首先是解析xml,这里面有各种标签配置,每一个标签都有一个封装类来封装属性,比如就用BeanDefinition封装,同理还有对property封装的。然后启动容器,就是加载xml解析过后,不是调用了refresh吗,这个方法就把这些封装的信息通过反射创建出来加入到Map中。

自定义Spring IOC总结

spring框架底层是很复杂的,进行了很深入的封装,并对外提供了很好的扩展性。

比如这个自己实现的IOC,如果不考虑什么拓展性,直接几个类加上if else一把梭,当然可以写出来,而且还很简单,但是后面想维护呢,直接火葬场!!!

这里使用各种设计模式的思想,一层一层的封装,通过接口规范,想用那种方式解析配置实现接口就行了。当然这样写起来就很复杂,我自己写也是对着视频一点一点理解的,这是一个漫长的过程,并不是一定要用设计模式,而是为了达到某个目的使用某种小技巧解耦提升拓展性罢了!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值