zoukankan      html  css  js  c++  java
  • Spring IoC反转控制/DI依赖注入

    Spring是一个基于IOC和AOP的结构J2EE系统的框架

    IOC 反转控制 是Spring的基础,Inversion Of Control

    简单说就是创建对象由以前的程序员自己new 构造方法来调用,变成了交由Spring创建对象

    DI 依赖注入 Dependency Inject. 简单地说就是拿到的对象的属性,已经被注入好相关值了,直接使用即可。

    原理

    以获取对象的方式来进行比较

    • 传统的方式:
      通过new 关键字主动创建一个对象

    • IOC方式:
      对象的生命周期由Spring来管理,直接从Spring那里去获取一个对象。 IOC是反转控制 (Inversion Of Control)的缩写,就像控制权从本来在自己手里,交给了Spring。

    打个比喻:
    传统方式:相当于你自己去菜市场new 了一只鸡,不过是生鸡,要自己拔毛,去内脏,再上花椒,酱油,烤制,经过各种工序之后,才可以食用。
    用 IOC:相当于去馆子(Spring)点了一只鸡,交到你手上的时候,已经五味俱全,你就只管吃就行了。

    1581749467750

    IoC与DI

    1. 首先想说说IoC(Inversion of Control,控制倒转)。

    这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。举个例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号…,想办法认识她们,投其所好送其所好,然后嘿嘿…这个过程是复杂深奥的,我们必须自己设计和面对每个环节。

    传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。

    1. 那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。

    2. Spring所倡导的开发方式就是如此:所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转

    3. IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的

    4. 那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

    • 控制(谁控制谁,控制什么)

      首先控制是容器控制了对象。控制了外部资源获取(资源不只是对象还有文件等)

    • 反转(什么是反转)

      在本来的程序设计上,我们都会再类中设计对象,都是我们主动创建对象或者直接通过对象进行获取东西,这就相当于是正转。而当从IOC的设计角度来思考,当容器帮我们创建对象及注入时依赖对象,此时对象是一个被动依赖的关系,所以是反转。

    IOC底层原理使用技术

    (1)xml配置文件

    (2)dom4j解析配置的xml文件

    (3)工厂的设计模式

    (4)反射

    1581750658296

    例子

    准备POJOs

    Plain Ordinary Java Objects

    package pojo;
    
    public class Category {
    
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        private int id;
        private String name;
    }
    
    public class Product {
    
        private int id;
        private String name;
    
        private Category category;
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Category getCategory() {
            return category;
        }
        public void setCategory(Category category) {
            this.category = category;
        }
    }
    

    applicationContext.xml

    在src目录下新建applicationContext.xml文件

    applicationContext.xml是Spring的核心配置文件

    通过关键字c即可获取Category对象,该对象获取的时候,即被注入了字符串"category 1“到name属性中

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/context     
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">
      
        <bean name="c" class="com.how2java.pojo.Category">
            <property name="name" value="category 1" />
        </bean>
        <bean name="p" class="pojo.Product">
            <property name="name" value="product1" />
            <property name="category" ref="c" />
        </bean>
      
    </beans>
    

    解释一下

    • bean是注册,其中name是标识符

    • property是类属性,name是属性名,value是指,ref是引用

    运行

    public class Test {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
    
            Product p = (Product) context.getBean("p");
            System.out.println(p.getName());
            System.out.println(p.getCategory().getName());
        }
    }
    

    通过这种方式运行,发现通过Spring拿到的Product对象已经被注入了Category对象了

    1581749802522

    使用注解方式

    @Autowired

    修改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" xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/context     
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">
      
        <context:annotation-config/>
        <bean name="c" class="com.how2java.pojo.Category">
            <property name="name" value="category 1" />
        </bean>
        <bean name="p" class="com.how2java.pojo.Product">
            <property name="name" value="product1" />
    <!--         <property name="category" ref="c" /> -->
        </bean>
      
    </beans>
    

    在product.java的private Category category;属性上或者public void setCategory方法前加上@Autowired

    public class Product {
    
        private int id;
        private String name;
    
        @Autowired
        private Category category;
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Category getCategory() {
            return category;
        }
    
    //    @Autowired
        public void setCategory(Category category) {
            this.category = category;
        }
    }
    

    运行

    1581751467032

    @Qualifier的使用

    在xml中多注册一个category

        <bean name="c2" class="pojo.Category">
            <property name="name" value="category 2" />
        </bean>
    

    发现使用上面的方法会报错,原因是Autowired是根据type来依赖注入的,如果有多个对象符合type则报错。

    我们需要在@Autowired的标签下加一个@Qualifier

        @Autowired
        @Qualifier("c2")
        private Category category;
    

    1581751608594

    @Resource

    除了@Autowired之外,@Resource也是常用的手段

    @Resource(name="c2")
    private Category category;
    

    也可以达到上面的效果

    注意

    ​ Resource需要引入的是import javax.annotation.Resource;,一开始IDEA自动创建了Spring工程一直报错,后来才发现这个需要自己手动导包

    解决方案:

    https://blog.csdn.net/zixiao217/article/details/52608160

    https://blog.csdn.net/luojinbai/article/details/46670105

    @Resource和@Autowired区别对比

    @Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。

    1、共同点

    两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。

    2、不同点

    (1)@Autowired

    @Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。

    public class TestServiceImpl {
        // 下面两种@Autowired只要使用一种即可
        @Autowired
        private UserDao userDao; // 用于字段上
        
        @Autowired
        public void setUserDao(UserDao userDao) { // 用于属性的方法上
            this.userDao = userDao;
        }
    }
    

    @Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:

    public class TestServiceImpl {
        @Autowired
        @Qualifier("userDao")
        private UserDao userDao; 
    }
    

    (2)@Resource

    @Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。

    public class TestServiceImpl {
        // 下面两种@Resource只要使用一种即可
        @Resource(name="userDao")
        private UserDao userDao; // 用于字段上
        
        @Resource(name="userDao")
        public void setUserDao(UserDao userDao) { // 用于属性的setter方法上
            this.userDao = userDao;
        }
    }
    

    注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属性,而不是直接去操作属性。

    @Resource装配顺序:

    ①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。

    ②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。

    ③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。

    ④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。

    @Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。

    @Component

    @component (把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>

    修改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" xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/context     
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">
      
        <context:component-scan base-package="pojo"/>
         
    </beans>
    

    在Product类和Category类头上加上@Component注解

    @Component("p")
    public class Product 
        
    @Component("c")
    public class Category 
    

    需要注意的是,这种方法需要在类里面初始化成员变量

    private String name = "product1";
    private String name = "category3";
    

    跑一下test发现报错了

    1581752192591

    1581752203058

    原来是Product中的Category不知道怎么指定,所以在Product类中加上

        @Autowired
        private Category category;
    

    1581752253447

    感觉这种方式很麻烦..不太好用...

    参考连接

    最好理解的: spring ioc原理讲解,强烈推荐!

    关于spring ioc底层原理(图解)+ 代码样例以及对IOC的简单理解

  • 相关阅读:
    Android中的Handler, Looper, MessageQueue和Thread
    ANR程序无响应原因及应对办法
    避免内存泄露的一些简单方法
    android内存泄露及OOM介绍
    listview异步加载图片优化
    利用convertView及viewHolder优化Adapter
    Sqlite介绍及其语句
    关于单页面和传统页面跳转的思考
    快速入门Vue
    JS编码
  • 原文地址:https://www.cnblogs.com/cpaulyz/p/12401692.html
Copyright © 2011-2022 走看看