spring---IOC和DI

Spring中的ioc和DI

ioc:控制反转,DI:依赖注入,其实,这两个是一个相同的概念,ioc偏重于是一种思想,di则是偏重于过程。

一,ioc

何为ioc?

ioc:字面意思就是控制反转,那么是谁控制谁呢,为什么是反转不是正转呢?

原来,不是spring框架的时候,我们实例化对象,都是直接在程序内部通过new来创建对象,是程序主动去创建对象,主动权在程序或者在我们手上,而Spring IoC容器,负责实例化,配置和组装上述bean,即由Ioc容器来控制对象的创建,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。所以现在控制权不在程序,而是ioc容器,控制权反转。

ioc能做什么?
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

二,DI

依赖

1.何为依赖注入

DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

谁依赖于谁:当然是应用程序依赖于IoC容器;

为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;

谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;

注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
  

2.基于构造方法的依赖注入

实体类User

    public class User {
        private String userName;
        private int age;
        private Student student;
    
        public User() {
            System.out.println("User构造函数");
        }
            
        public User(String userName,int age,Student student){
            this.userName=userName;
            this.age=age;
            this.student=student;
            System.out.println("User有参构造函数");
        }
    
        public Student getStudent() {
            return student;
        }
    
        public void setStudent(Student student) {
            this.student = student;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
实体类Student

    public class Student {
    
        private String studentName;
        private int age;
    
        public Student(String studentName, int age) {
            System.out.println("student有参构造方法");
            this.studentName = studentName;
            this.age = age;
        }
    
        public Student() {
            System.out.println("student无参构造方法");
        }
    
        public String getStudentName() {
            return studentName;
        }
    
        public void setStudentName(String studentName) {
            this.studentName = studentName;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
a.基于参数索引(默认是0开始)使用index属性显式指定构造函数参数的索引
  <!--基于构造函数参数索引 除了解决多个简单值的歧义之外,指定索引还可以解决构造函数具有相同类型的两个参数的歧义-->
      <bean id="user" class="main.java.pojo.User">
          <constructor-arg index="0" value="kevin"></constructor-arg>
          <constructor-arg index="1" value="100"></constructor-arg>
          <constructor-arg ref="student"></constructor-arg>
      </bean>
b.基于参数类型
     <!--基于构造函数参数类型 如果使用type显式指定构造函数参数的类型,则容器可以使用与简单类型匹配的类型type Spring无法确定值的类型,因此无法在没有帮助的情况下按类型进行匹配 -->
     <bean id="user" class="main.java.pojo.User">
         <constructor-arg type="java.lang.String" value="kevin"></constructor-arg>
         <constructor-arg type="int" value="100"></constructor-arg>
         <constructor-arg ref="student"></constructor-arg>
     </bean>
c.基于参数名字
     <!--基于参数名字注入 还可以使用构造函数参数名称进行值消歧    -->
      <bean id="user" class="main.java.pojo.User">
          <constructor-arg name="userName" value="kevin"></constructor-arg>
          <constructor-arg name="age" value="100"></constructor-arg>
          <constructor-arg ref="student"></constructor-arg>
      </bean>

student注入


  <bean id="student" class="main.java.pojo.Student">
          <constructor-arg index="0" value="student"></constructor-arg>
          <constructor-arg index="1" value="12"></constructor-arg>
  </bean>
3.基于setter的依赖注入
		<bean id="student" class="main.java.pojo.Student">
           <property name="age" value="12"></property>
            <property name="studentName" value="asdawd"></property>
        </bean>
        <bean id="user" class="main.java.pojo.User">
            <property name="userName" value="kevin"></property>
            <property name="age" value="18"></property>
            <property name="student" ref="student"></property>
        </bean>
1.循环依赖问题

什么是循环依赖问题?
当我们的bean A依赖于bean B,bean B也依赖于bean A 时
类似如下:

    public Student(String studentName, int age,User user) {
            System.out.println("student有参构造方法");
            this.studentName = studentName;
            this.age = age;
            this.user=user;
        }
    public User(String userName,int age,Student student){
            this.userName=userName;
            this.age=age;
            this.student=student;
            System.out.println("User有参构造函数");
        }

如果我采用构造注入的方式实例化bean此时:

a.构造器参数循环依赖
			<bean id="student" class="main.java.pojo.Student">
                <constructor-arg type="java.lang.String" value="kevinawdaw"></constructor-arg>
                <constructor-arg type="int" value="1002213"></constructor-arg>
                <constructor-arg ref="user"></constructor-arg>
            </bean>
          
            <bean id="user" class="main.java.pojo.User">
                <constructor-arg name="userName" value="kevin"></constructor-arg>
                <constructor-arg name="age" value="100"></constructor-arg>
                <constructor-arg ref="student"></constructor-arg>
            </bean>

报错信息

     Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:   
            Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference? 
为什么使用构造方法注入会出现循环依赖问题?

Spring容器会将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。
也就是说,当我们的spring容器先创建出单例的student的时候也创建了user,并且将student,user放入了当前正在创建bean池中,那么当容器接着去创建user实例时发现池中已经存在了该bean,便抛出异常。

b.setter循环依赖

那么我们使用setter注入看看会如何

   		   <bean id="student" class="main.java.pojo.Student">
               <property name="age" value="12"></property>
                <property name="studentName" value="asdawd"></property>
                <property name="user" ref="user"></property>
            </bean>
            <bean id="user" class="main.java.pojo.User">
                <property name="userName" value="kevin"></property>
                <property name="age" value="18"></property>
                <property name="student" ref="student"></property>
            </bean>

结果没有任何异常
在这里插入图片描述

为什么使用setter注入可以解决循环依赖问题?

Spring是先将Bean对象实例化之后再设置对象属性的,所以不存在上述类似构造函数注入的问题。

4.详细配置
a.普通值注入value
    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <!-- results in a setDriverClassName(String) call -->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
        <property name="username" value="root"/>
        <property name="password" value="masterkaoli"/>
    </bean>

也可以使用p命名空间简化配置

引入约束: xmlns:p="http://www.springframework.org/schema/p"

配置:

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close"
            p:driverClassName="com.mysql.jdbc.Driver"
            p:url="jdbc:mysql://localhost:3306/mydb"
            p:username="root"
            p:password="masterkaoli"/>
b.引用注入

注意:idref和ref的区别
ref是将bean的实例注入,即将student类实例注入user

   			  <bean id="user" class="main.java.pojo.User">
                    <property name="userName" value="kevin"></property>
                    <property name="age" value="18"></property>
                    <property name="student" ref="student"></property>
                </bean>

而idref是将目标bean的id注入而不是bean实例

    <bean id="theTargetBean" class="..."/>
    
    <bean id="theClientBean" class="...">
        <property name="targetName">
            <idref bean="theTargetBean"/>
        </property>
    </bean>
    
    上面的bean定义片段与以下片段完全等效(在运行时):
    <bean id="theTargetBean" class="..." />
    
    <bean id="client" class="...">
        <property name="targetName" value="theTargetBean"/>
    </bean>
内部bean

      <bean id="student" class="main.java.pojo.Student">
               <property name="age" value="12"></property>
               <property name="studentName" value="asdawd"></property>
               <property name="user">
                   <bean class="main.java.pojo.User"
                         p:age="15"
                         p:userName="12315">
                   </bean>
               </property>
           </bean>

c.集合注入

<list/>,<set/>,<map/>,和<props/>元素,你将Java的性能和参数Collection类型List,

 Set,Map,和Properties分别。

 <bean id="moreComplexObject" class="example.ComplexObject">
     <!-- results in a setAdminEmails(java.util.Properties) call -->
     <property name="adminEmails">
         <props>
             <prop key="administrator">administrator@example.org</prop>
             <prop key="support">support@example.org</prop>
             <prop key="development">development@example.org</prop>
         </props>
     </property>
     <!-- results in a setSomeList(java.util.List) call -->
     <property name="someList">
         <list>
             <value>a list element followed by a reference</value>
             <ref bean="myDataSource" />
         </list>
     </property>
     <!-- results in a setSomeMap(java.util.Map) call -->
     <property name="someMap">
         <map>
             <entry key="an entry" value="just some string"/>
             <entry key ="a ref" value-ref="myDataSource"/>
         </map>
     </property>
     <!-- results in a setSomeSet(java.util.Set) call -->
     <property name="someSet">
         <set>
             <value>just some string</value>
             <ref bean="myDataSource" />
         </set>
     </property>
 </bean>
d.空值注入

Spring将属性等的空参数视为空Strings。以下基于XML的配置元数据片段将email属性设置为空 String值(“”)。

    <bean class="ExampleBean">
        <property name="email" value=""/>
    </bean>

上面的示例等效于以下Java代码:

    exampleBean.setEmail("");

该元素处理null值。例如:

    <bean class="ExampleBean">
        <property name="email">
            <null/>
        </property>
    </bean>

以上配置等同于以下Java代码:

    exampleBean.setEmail(null);
e.懒加载

默认情况下,ApplicationContext实现会急切地创建和配置所有 单例 bean,作为初始化过程的一部分。通常,这种预先实例化是可取的,因为配置或周围环境中的错误是立即发现的,而不是几小时甚至几天后。如果不希望出现这种情况,可以通过将bean定义标记为延迟初始化来阻止单例bean的预实例化。延迟初始化的bean告诉IoC容器在第一次请求时创建bean实例,而不是在启动时。

在XML中,此行为由 元素lazy-init上的属性控制; 例如:

    <bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
    <bean name="not.lazy" class="com.foo.AnotherBean"/>

当前面的配置被a使用时ApplicationContext,命名的bean lazy在ApplicationContext启动时不会急切地预先实例化,而not.lazybean被急切地预先实例化。

但是,当延迟初始化的bean是未进行延迟初始化的单例bean的依赖项时 ,ApplicationContext会在启动时创建延迟初始化的bean,因为它必须满足单例的依赖关系。惰性初始化的bean被注入到其他地方的单独的bean中,而这个bean并不是惰性初始化的。

您还可以通过使用元素default-lazy-init上的属性来控制容器级别的延迟初始化 ; 例如:

    <beans default-lazy-init="true">
        <!-- no beans will be pre-instantiated... -->
    </beans>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值