简单学习下springIoc以及利用反射机制是如何实现的

什么是springIoc

IOC(Inversion of Control)
意为控制反转,他是一种设计思想.并非实际的技术.最核心的思想就是将对象实例创建的控制权交给程序(IOC 容器)
IOC 容器:
一个管理所有控制反转过程中创建的对象的 key-value 容器结构(可以简单理解为:hashMap)
Spring 的 IOC(控制反转)中对象实例构建的方式有哪些?
1.无参构造创建
2.静态工厂创建:类的静态方法构造
3.实例工厂创建:对象的实例方法构造
演示代码:
spring-ioc.xml 配置文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 创建方式1:空参构造创建 -->
    <bean id="a" class="com.czy.project.demo6.A"/>
    <!--
        创建方式2:静态工厂创建
        调用A的createBObj方法来创建名为b的对象放入容器
    -->
    <bean id="b" class="com.czy.project.demo6.A" factory-method="createBObj"/>
    <!--
            创建方式3:实例工厂创建
            调用实例a的createCObj方法来创建名为c的对象放入容器
     -->
    <bean id="c" factory-bean="a" factory-method="createCObj"/>
</beans>

Class A 代码:

public class A {
    public A() {
        System.out.println("A 无参构造器被调用了.");
    }
    public static B createBObj() {
        System.out.println("A 的静态方法 createBObj 被调用了.");
        return new B();
    }
      public C createCObj() {
        System.out.println("A 的实例方法 createCObj 被调用了.");
        return new C();
    }
}

Class B 代码:

public class B {
 
}

Class C 代码:

public class C {

}

启动类的代码:

public class Main {
    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-ioc.xml");
        A a = (A) context.getBean("a");
        B b = (B) context.getBean("b");
        C c = (C) context.getBean("c");
    }
}

输出结果:

在这里插入图片描述
通过上面的案例,我们已经知道了 IOC 的概念及实例构建的方式.接下来我们来探讨一下他的核心实现方式.

手写springIoc实现思路

说明:以下代码需要的xml配置文件以及实体类还是用上边的

spring中是要在xml中配置bean的,都知道面向对象的思想,那你肯定能想到肯定是要把xml文件中的内容读取出来,并用对象接收。那么
1.创建用于接收xml中bean的配置类

/**
 * 用于接收xml中bean标签的属性
 * 没什么东西,就是根据bean标签中的属性来定义的
 * Author : czy
 */
public class BeanConfig {
    private String id;
    private String clazz;
    private String factoryMethod;
    private String factoryBean;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getClazz() {
        return clazz;
    }
    public void setClazz(String clazz) {
        this.clazz = clazz;
    }
    public String getFactoryMethod() {
        return factoryMethod;
    }
    public void setFactoryMethod(String factoryMethod) {
        this.factoryMethod = factoryMethod;
    }
    public String getFactoryBean() {
        return factoryBean;
    }
    public void setFactoryBean(String factoryBean) {
        this.factoryBean = factoryBean;
    }
}

spring中入口是不是new ClassPathXmlApplicationContext(“xml文件名”);
那咱们就用XmlApplicationContext(“xml文件名”); //Are You Ok?–>ok
2.创建XmlApplicationContext类

/**
 * 相当于Springl中ClassPathXmlApplicationContext
 * 1、解析xml文件
 * 2、获取xml文件所有的节点内容
 * 3、利用java反射机制创建实例对象
 * 4、将实例对象添加到ioc容器
 * czy say :SO EASY
 */
public class XmlApplicationContext {
    // xml路径
    private String xmlPath;
    // 存放bean(也就是ioc的容器)
    private static HashMap map = new HashMap();
    //存放从xml中解析出来的所有bean
    private List<BeanConfig> beanConfigs = new ArrayList<BeanConfig>();

    //对外的构造函数
    public XmlApplicationContext(String xmlPath) {
        this.xmlPath = xmlPath;
        try {
            //解析xml到beanConfigs
            getAllElements();
            //初始化ioc容器
            init();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取xml文件所有的节点内容,并放入beanConfigs集合
     */
    private void getAllElements() throws Exception {
        //1.指定路径加载xml到jvm,形成输入流,xml中的所有内容都在这个流中
        InputStream is = XmlApplicationContext.class.getClassLoader().getResourceAsStream(xmlPath);
        //2.创建解析对象
        SAXReader reader = new SAXReader();
        //3.获得文档对象(整个xml文件->将输入流转换成文档对象)
        Document document = reader.read(is);
        //4.获取根节点,也就是对应spring-ioc.xml中的树干beans
        Element rootElement = document.getRootElement();
        //5.获取元素迭代器,用于获取所有节点内容
        Iterator iterator = rootElement.elementIterator();
        while (iterator.hasNext()) {
            //用于接收xml中bean的属性值
            BeanConfig beanConfig = new BeanConfig();
            //获取beans的子节点bean
            Element bean = (Element) iterator.next();
            // 获取bean的属性迭代器
            Iterator beanAttributes = bean.attributeIterator();
            while (beanAttributes.hasNext()) {
                Attribute attribute = (Attribute) beanAttributes.next();
                String attributeName = attribute.getName();//属性名
                String attributeData = (String) attribute.getData();//属性值
                if ("id".equals(attributeName)) {
                    beanConfig.setId(attributeData);
                } else if ("class".equals(attributeName)) {
                    beanConfig.setClazz(attributeData);
                } else if ("factory-method".equals(attributeName)) {
                    beanConfig.setFactoryMethod(attributeData);
                } else {
                    beanConfig.setFactoryBean(attributeData);
                }
            }
            //将这条bean的属性值放入beanConfigs集合
            beanConfigs.add(beanConfig);
        }
    }
    
    /**
     * 这里初始化ioc容器是关键
     * 这里就要利用java的反射机制来创建bean实例
     *
     * @throws Exception
     */
    private void init() throws Exception {
        /**
         * 这里说一点:
         * 看spring-ioc.xml中:bean标签有class属性时就没有factory-bean属性,反之
         * 有class属性时,就要判断是空参构造创建还是静态工厂创建,也就是判断有没有factory-method属性
         * 有factory-bean时,必然是实例工厂创建-必须有factory-method
         * 这点要捋清楚
         */
        for (BeanConfig bean : beanConfigs) {
            if (null != bean.getClazz()) {
                //通过全限定类名拿到Class实例clazz
                Class clazz = Class.forName(bean.getClazz());
                if (null != bean.getFactoryMethod()) {//静态工厂创建bean实例
                    //方法名获取method对象
                    Method method = clazz.getDeclaredMethod(bean.getFactoryMethod());
                    putBean(bean.getId(), method.invoke(null));
                } else {//构造器创建bean实例
                    putBean(bean.getId(), clazz.newInstance());
                }
            } else if (null != bean.getFactoryBean()) {//实例工厂创建bean实例
                //从容器中拿到实体bean
                Object obj = getBean(bean.getFactoryBean());
                Method method = obj.getClass().getDeclaredMethod(bean.getFactoryMethod());
                putBean(bean.getId(), method.invoke(obj));
            } else {
                System.out.println("czy不知道在搞什么鬼");
            }
        }
    }

    /**
     * 添加实例对象到ioc容器
     *
     * @param id:就是bean中自定义的id
     * @param object :就是反射机制创建的实例对象
     */
    public void putBean(String id, Object object) {
        map.put(id, object);
    }

    /**
     * 通过id获取实例对象
     *
     * @param id:就是bean中自定义的id
     * @return
     */
    public Object getBean(String id) {
        return map.get(id);
    }
}

3.创建启动类Main
是不是和上边开局演示的入口代码很像。

public class Main {
    public static void main(String[] args) {
        XmlApplicationContext context = new XmlApplicationContext("spring-ioc.xml");
        A a = (A)context.getBean("a");
        B b = (B) context.getBean("b");
        C c = (C) context.getBean("c");
    }
}

那就要检验结果是不是尽人意了。
4.测试
哈哈,测试交给读者来测试吧!相信你也是很感兴趣的。
5.总结
springIoc的思路大致就是这样子的:

1、解析xml文件
2、获取xml文件所有的节点内容
3、利用java反射机制创建实例对象
4、将实例对象添加到ioc容器

但是要知道spring的内部实现可不单单是这几百行啊。
温馨提示:自己建个工程,代码直接拷下来就能用,并且读起来更有感觉。代码中的注释纯手写,自我感觉还是比较详细的,一行一行的读。相信初学者看完后肯定会知道spring是如何实现ioc的思路的。并且也能掌握如何把xml中的内容读取出来并用对象接收。

最后为了方便大家创建工程:

<dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.8.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>   
</dependencies> 
发布了17 篇原创文章 · 获赞 29 · 访问量 6542

猜你喜欢

转载自blog.csdn.net/weixin_45240169/article/details/104192705