什么是Spring中的IOC思想

一 介绍

所谓IOC,说句大白话就是,不用我们自己new对象了,DI呢?就是对象里的属性能自动注入了,网上东扯西扯其实就这点事…


二 理解IOC

前后端交互的过程大致如下:
在这里插入图片描述可以看到Service和Dao层,以及Dao层和queryRunner是相互依赖的,说白了Service这个类里面一定有一个属性是Dao层实现类的对象,而Dao层实现类里也一定有一个一个属性是queryRunner的对象,而queryRunner里面还会有dataSource,dataSource构造方法还需要四个基本的参数(Driver,url,user,password)
想象一下如果我们自己一次一次new 各种对象 比如Service里面的dao实现类有一天我们需要换一种技术更换一下实现类…再或者有一天我们需要更换一下数据库…再或者压根不用queryRunner了改Mybatis了等等等头疼的情况…我们怎么做?
而且有时候项目已经跑在服务器上了,而服务器上可能压根都没有编译环境,那我们怎么办,所以每一次我们不得不从服务器把代码拿回来,修改源码,编译,打成jar包,再上线回去…这个过程是很麻烦的.那么如果频繁这么操作呢?
显然我们必须通过一种不修改源码就能顺利改变依赖的方式,而这种思想就被成为IOC.而Spring的两大思想其中之一就是IOC思想
今天我们手写一下Spring freamwork中beanFactory看看IOC思想下的代码是怎样的

三 技术支撑

  • xml解析 --DOM4J
  • java反射

四 项目目录

在这里插入图片描述
我们主要研究beans.xml和BeanFactory,用这两个东西来得到各种实体类的对象,先来一个最简单的例子,我们就是向单纯的调用一下Student空参的构造方法在创建一个对象,那么.xml👇

<?xml version="1.0"  encoding="GBK" ?>
<beans>
     <bean id="stu" class="com.etoak.student.entity.Student">
     </bean>
</beans>

beanFactory

public class BeanFactory {
	public static Object getBean(String id){
        Object target = null;
        try {
            InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
            //这里用到的是DOM4J解析xml,网上一搜一堆,下个jar包导进来就行
            SAXReader reader = new SAXReader();
            Document doc = reader.read(is);
            //根元素
            Element root = doc.getRootElement();
            //获得根元素下的所有一级子元素
            List<Element> firsts = root.elements("bean");//拿到所有的一级元素了
			for(Element first:firsts){
				//id属性值
                String current_id = first.attributeValue("id");
                if(current_id.equals(id)){
                	String className = first.attributeValue("class");
					Class clazz = Class.forName(className);//反射
					target = clazz.newInstance();//构造对象
				}
			}
		}catch(Exception e){
            e.printStackTrace();
        }
        return target;
	}
}

测试代码👇

 Student stu = (Student)BeanFactory.getBean("stu");//执行查看stu是否有地址

说明我们的工厂初步是可以使用的,我们继续添加功能,倘若有个别情况我需要单例模式开发呢?这个学生对象可以重复被拿过来使用吗?
我们改改xml代码 加上一个scope

<?xml version="1.0"  encoding="GBK" ?>
<beans>
     <bean id="stu" class="com.etoak.student.entity.Student" scope="singleton">
     </bean>
</beans>

哎,看看工厂,首先需要一个Map集合存放已有的对象

public class BeanFactory {
 	private static Map<String,Object> objectMap = new ConcurrentHashMap<>();
	public static Object getBean(String id){
        Object target = null;
        try {
            InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
            //这里用到的是DOM4J解析xml,网上一搜一堆,下个jar包导进来就行
            SAXReader reader = new SAXReader();
            Document doc = reader.read(is);
            //根元素
            Element root = doc.getRootElement();
            //获得根元素下的所有一级子元素
            List<Element> firsts = root.elements("bean");//拿到所有的一级元素了
			for(Element first:firsts){
				//id属性值
                String current_id = first.attributeValue("id");
                if(current_id.equals(id)){
                	String className = first.attributeValue("class");
                	 //获得scope属性值
                    String scope = first.attributeValue("scope");
					if("singleton".equals(scope)){
                        //需要一个对象
                        target = objectMap.get(className);
                    }else{
						Class clazz =class.forName(className);
						target=clazz.newInstance()
						objectMap.put(className, target);
					}
					
				}
			}
		}catch(Exception e){
            e.printStackTrace();
        }
        return target;
	}
}

测试代码

 Student stu = (Student)BeanFactory.getBean("stu");
 Student stu1 = (Student)BeanFactory.getBean("stu");
 System.out.println(stu==stu1);//true

总而言之单例模式加个scope开关就解决了,我们再继续,那么一个类里面有其他实体类对象怎么办呢?哎这就引入了,Service和dao层的关系了,能否解耦合呢?我们看一下,这次我们直接上Service来说事儿
xml👇

<?xml version="1.0"  encoding="GBK" ?>
<beans>
     <bean id="stu" class="com.etoak.student.entity.Student" scope="prototype">
          <property name="name" value="etoak"></property>
     </bean>
     <bean id="service" class="com.etoak.student.service.StudentService">
          <property name="dao" >
               <ref beanid="dao"></ref>
          </property>
     </bean>
     <bean id="dao" class="com.etoak.student.dao.StudentDaoImpl">
          <property name="qr" >
               <ref beanid="qr"></ref>
          </property>
     </bean>
     <bean id="qr" class="org.apache.commons.dbutils.QueryRunner">
          <!--不加该标签使用无参构造方法,加上该标签 需要调用带参构造方法-->
          <constructor-args type="javax.sql.DataSource" ref="ds"></constructor-args>
     </bean>
     <bean id="ds" class="org.apache.commons.dbcp.BasicDataSource">
          <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
          <property name="url" value="jdbc:mysql://localhost:3306/et2009"></property>
          <property name="username" value="root"></property>
          <property name="password" value="etoak"></property>
     </bean>
</beans>

这里的意思是

先去找
先去找
先去找
先去找
倒回来
倒回来
倒回来
倒回来
创建Service对象
DAO实现类
queryRunner
DataSource
四个参数

beanFactory

//BeanFactory 专门负责对外提供对象
public class BeanFactory {
    //存放对象的
    private static Map<String,Object> objectMap = new ConcurrentHashMap<>();
    public static Object getBean(String id){
        Object target = null;
        try {
            InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
            //DOM4J
            SAXReader reader = new SAXReader();
            Document doc = reader.read(is);
            //根元素
            Element root = doc.getRootElement();
            //获得根元素下的所有一级子元素
            List<Element> firsts = root.elements("bean");
            for(Element first:firsts){
                //id属性值
                String current_id = first.attributeValue("id");
                //当前遍历到的id的值 匹配传入的id的值,说明就是该类的对象
                if(current_id.equals(id)){
                    //获得类名字
                    String className = first.attributeValue("class");
                    //获得scope属性值
                    String scope = first.attributeValue("scope");
                    if("singleton".equals(scope)){
                        //需要一个对象
                        target = objectMap.get(className);
                    }else {
                        if (null == className) {
                            return null;
                        }
                        Class clazz = Class.forName(className);
                        //试图寻找构造方法参数如果有 则使用带参数的构造方法来构造对象
                        List<Element> cons_args= first.elements("constructor-args");
                        if(cons_args!=null && cons_args.size()>0){
                        //这俩数组为下面的Coustructor服务需要参数类型和参数
                            Class[] parameterTypes = new Class[cons_args.size()];
                            Object[] parameters  = new Object[cons_args.size()];
                            //遍历<constructor-arg>标签
                            int k=0;
                            for(Element ca:cons_args){
                                //构造方法参数类型
                                String typeName = ca.attributeValue("type");
                                String ref = ca.attributeValue("ref");
                                Object obj = getBean(ref);
                                parameterTypes[k] = Class.forName(typeName);
                                parameters[k] = obj;
                                k++;
                            }
                           Constructor c =  clazz.getDeclaredConstructor(parameterTypes);
                            target = c.newInstance(parameters);
                        }else {
                            //构造对象
                            target = clazz.newInstance();
                            //获得当前匹配到的<bean>元素下的<property>元素
                            List<Element> props = first.elements("property");
                            for(Element pro:props){
                                //属性名字
                                String proName = pro.attributeValue("name");
                                //属性类型
                                Class fieldType = clazz.getDeclaredField(proName).getType();
                                //属性对应的setter方法的名字
                                String setterName = "set"+proName.substring(0,1)
                                        .toUpperCase()+proName.substring(1);
                                //setter方法
                                Method setter = clazz.getDeclaredMethod(setterName,fieldType);
                                //判断<property>元素是否有子元素
                                if(pro.isTextOnly()) { //没有子元素
                                    //属性值
                                    String value = pro.attributeValue("value");

                                    //执行
                                    setter.invoke(target, value);
                                }else{
                                 //有子元素 <ref>
                                    Element third = (Element)pro.elements().get(0);
                                    //引用其他属性  1.首先获得其他id对应的对象 2.给当前对象中的属性赋值
                                    if("ref".equals(third.getName())){
                                        String beanid = third.attributeValue("beanid");
                                        Object obj = getBean(beanid);
                                        setter.invoke(target,obj);
                                    }
                                }

                            }
                        }
                        objectMap.put(className, target);
                    }
                }
            }

        }catch(Exception e){
            e.printStackTrace();
        }

        return target;
    }
}

我们beanFactory这个工厂就基本可以对标Spring里的哪个IOC的意思,那么刚上来我们哪个图就变成这个模式👇
在这里插入图片描述

而倘若我们需要改里面注入的依赖,我们直接修改xml配置文件就可以了,源码完全不需要修改,这也就免了重新编译运行的麻烦,这也就是IOC思想下的开发模式

结论

Spring之所以能十几年不衰,是有它自己的独到之处的,我们需要看看人家好的思想来提高自己的水平,多思考,原创不易,喜欢系的点赞+关注哦~后续我会持续说一些Spring底层的东西

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

商朝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值