Spring IOC

什么是IOC


依赖注入,或者说控制反转,其实都是一个意思,其实不用想的过于复杂,在我看来,Spring的IOC其实就是将bean对
象的手动创建,托管给了Spring的bean工厂,以实现不同bean之前依赖关系的解耦,简单的理解,我们可以把Spring
工厂当成一个map集合,统一管理bean的容器,而这个容器,通过总体上2种方式,xml文件、annotation注解,Spring
通过读取对应的xml和注解,获取类信息,再通过反射的方式,生成对应的实现类。
spring对于注入的类并没有额外的限制要求,需要注意因为是反射的方式获取类信息,所以接口、抽象类不支持。

注入方式


其实不管是xml文件还是annotation注解注入,注入的方式可以分为两类,构造注入、set注入。
构造注入:通过反射的方式,获取类构造器,创建bean.默认是使用无参构造器,如果被重载,需要显式指定。
set注入:获取对应变量的名,底层获取对应的set方法注入。
构造注入需要显式指定,默认使用无参构造器。

下面是构造注入案例:先创建一个A类

public class A {
    public void call(){
        System.out.println("我的名字是A");
    }
}

A类对应的xml文件

<bean id="a1" class="com.zxy.bean.A">
</bean>

启动获取对应的类

public class IOCApplication {
    public static void main(String[] args) {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
        A a1=(A)ctx.getBean("a1");
        a1.call();
    }
}

输出:我的名字是A。
下面是set注入案例:创建一个B类,调用A中的call方法

在这里插入代码片public class B {
    public  A a;
    public void setA(A a){
      this.a=a;
    }
    public void callA(){
       a.call();
    }
}

对应xml:

    <bean id="b1" class="com.zxy.bean.B">
        <property name="a" ref="a1"></property>
    </bean>

启动类:

public class IOCApplication {
    public static void main(String[] args) {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
        B b1=(B) ctx.getBean("b1");
        b1.callA();
    }
}

因为set注入是通过反射方式获取对应参数的set方法,所以,通过xml的set注入时,必须存在对应属性的set方法,如果我们人为的变更set方法名和删除对应的set方法,都会出现异常,这里我们尝试一下。
1修改set方法名

    public void setA1(A a){
      this.a=a;
    }

在这里插入图片描述此时会出现如下信息,大概就是找不到对应的set方法
2如果我们删除对应的set方法

public class B {
    public  A a;
    public void callA(){
       a.call();
    }
}

在这里插入图片描述此时出现如下信息,不能找到对应的set方法。

通过Annotation注解注入的方式和xml又有所不同在于,annotation注解注入是通过反射,在Spring的容器内部生成对应的set方法,大概就是对应set+首字母大写构成。所以annotation的方式不需要对应的属性必须存在set方法。
此时的配置文件加上这两行:

    <!-- 打开Spring的Annotation支持 -->
    <context:annotation-config/>
    <!-- 设定Spring 去哪些包中找Annotation -->
    <context:component-scan base-package="com.zxy.bean"/>

给A类加上注解:

@Component
public class A {
    @Value("1")
    public int age ;

    public void call(){
        System.out.println("我的名字是A");
    }
}

给B类加上注解,注意此时B类中不存在set方法:

public class B {
    @Autowired
    public  A a;

    public void callA(){
       a.call();
    }
}

我们再执行main方法获取类:

    public static void main(String[] args) {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("application.xml");
        B b1=(B) ctx.getBean("b1");
        b1.callA();
    }
}

结果:我的名字是A 。 执行成功,说明annotation并不需要你显式提供set方法。

IOC的底层实现

大致流程;创建beanfactory工厂,通过BeanDefinitionReader读取类信息创建beandui对象,放入map集合,再设置beanfactorypostprocessor和beanpostprocessor增强器。下面就让我们跟着代码走吧。
首先点击启动类的ClassPathXmlApplicationContext(“”)
在方法里面只有两个方法,setConfigLocation,refresh,当我们点击setConfigLocation进入后,可以发现其主要内容,就是将我们传入的xml文件解析后放入一个string[] 数组,我们可以大胆猜想,这个数组肯定是后面获取bean信息创建的时候需要用到的,暂时不用关注。
在这里插入图片描述
然后我们点击refresh方法进入,在refresh方法前面中有这行代码。
在这里插入图片描述在这里我们能看到beanFactory在这里已经被创建了,继续点击这个obtainFreshBeanFactory(),获得如图代码
在这里插入图片描述需要注意的是,真正的beanFactory的创建是在refreshBeanFactory方法,614行的getBeanFactory其实是全局获取了这个beanFactory,点击refreshBeanFactory后如图
在这里插入图片描述这里面我们主要关注126行和129行,在126行实际是进行了beanFactory的创建,而这个beanFactory其实就是一个
DefaultListableBeanFactory对象,点击进入后,有一个 map请一定记住,这其实就是底层最终用于存储bean信息的map
在这里插入图片描述
继续回到refreshBeanFactory方法,loadBeanDefinitions方法点击进入,
在这里插入图片描述
从大体上分析,这里创建了一个XmlBeanDefinitionReader格式的解析器,并初始化,继续点击loadBeanDefinitions,后如图
在这里插入图片描述还记得最开始的时候我们将传入的文件名放入了一个string[]么,这里的getConfigLocation()方法,其实就是获取了最开始的文件数组。继续点击loadBeanDefinitions方法进入。
在这里插入图片描述
这里将传入的String转为Resource,并传入loadBeanDefinitions方法。一直往下点击进入后,会找到这个方法里,
在这里插入图片描述
看到do开头的方法,请注意这里是核心方法了,这个方法里面其实我们大概能明白,创建了一个inputStream流,然后将文件信息和流传入,继续点击进入,
在这里插入图片描述正真有作用的代码,其实就这两行,而doLoadDocument其实就是根据我们刚才传入的流和文件名信息,获取了这个文件,registerBeanDefinitions,从字面不难理解,就是根据这个doc文件进行bean的注册了。点击进入,里面存在documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
这行代码才是对bean的注册方法,点击进入后,如图
在这里插入图片描述
这里我们从doc文件获取Element元素,并传入doRegisterBeanDefinitions方法,点击进入
在这里插入图片描述
在这个方法的前半部分,只做了一件事,根据传入的element创建了一个BeanDefinitionPaserDelegate对象delegate,而这个delegate里面含有了所有解析xml文件的标签,以及解析将xml解析为beanDefinition的方法,如果我们一直点击createDelegate方法进入后会看到populateDefaults方法,这里先对xml的默认信息,命名空间解析配置,真正的解析bean是在这个方法的后半部的parseBeanDefinitions.
在这里插入图片描述
根据前面解析后的root和已经设置了默认配置信息的delegate解析生成beanDefinition,点击进入后,如图
在这里插入图片描述
这里根据root的命名空间namespace判断解析方式,点击isDefaultNamespace后可以发现,我们配置的namcespace就是默认的,所以上面会走默认的解析方式parseDefaultElement(ele, delegate);,这里分别是默认的url和xml配置文件中的namespace在这里插入图片描述在这里插入图片描述
点击默认解析方式后,存在这个 processBeanDefinition(ele, delegate);方法,这是实际解析生成并注册bean的方法,点击后进入
在这里插入图片描述这个方法是核心的解析方法需要重点说明,在299行,根据Element解析文件已经生成了beanDefinition,后面的301行则是对我们创建beanDefinition进一步封装修饰,最终在304行将生成的beanDefinition进行注册,点击304行的方法一直往后走,在registerBeanDefinition方法中在进行一系列的逻辑判断后,会走到这行代码
在这里插入图片描述
看到这是不是觉得很熟悉,没错,这个map就是最开始创建的那个beanDefinitionMap,最终所有的bean都被放入了这个map中。
所以,回过头来看,SpringIOC的整个流程,其实就是首先生成beanFactory,然后解析文件信息,获取bean信息,创建bean,最后将bean放入一个map集合。
最重要的需要继续的是下面几个方法
1创建beanFactory
在这里插入图片描述
2加载文件信息
在这里插入图片描述
3解析文件信息
在这里插入图片描述
4注册bean
在这里插入图片描述
5最终注册放入map
在这里插入图片描述
如有不足之处,请指正,谢谢。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值