关于对Spring Ioc的理解

3 篇文章 0 订阅
1 篇文章 0 订阅

What is Ioc(Inversion of Control)?

  • 让我们来看看官方解释
    IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes, or a mechanism such as the Service Locator pattern.

IoC也称为依赖注入(DI)。它是一个对象定义它们的过程依赖项,即它们使用的其他对象,仅通过构造函数参数对象实例构造或返回后在其上设置的工厂方法或属性从工厂方法。然后,容器在创建bean时注入这些依赖项。这流程基本上是bean本身的反向,因此称为控制反转(IoC)通过使用类的直接构造来控制其依赖项的实例化或位置机制,如服务定位器模式。

  • 解释一下这段话的含义
  • Ioc 中文翻译为控制反转,是一种编程思想,依赖注入(DI,dependency
    injection)就是Ioc的具体的实现。那么什么控制被反转了呢。其实就是bean的实例化,初始化的过程由自己(主动new)主动实现变为由Spring 容器 来替你完成。这里的bean不只是指Java对象,还包含其他的对象如Xml文件等。
  • DI中文翻译为依赖注入,依赖注入是将其他的对象传入某个对象的过程。

上代码:

 public class User(){
     private String name;
     private int age;
     private String sex;
     private GirlFirend girlFirend;
     //随便写几个属性,省略setter,getter方法,toString方法
 }
public class GirlFirend(){
     private String name;
     private int age;
     private String sex;
     //省略setter,getter方法,toString方法
     public User(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
}
public class IocContainer {
    private GirlFirend girlFirend;
    private String name;

    public IocContainer(){};

    public IocContainer(String name){
        girlFirend = new GirlFirend("丽丽",18,"女");
        this.name = name;
    }

    public GirlFirend getGirlFirend() {
        return girlFirend;
    }

    public void setGirlFirend(GirlFirend girlFirend) {
        this.girlFirend = girlFirend;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

我们创建了三个Bean,分别是User,GirlFirend,IocContainer。
主动的方式:就是User需要自己去new一个GirlFirend

public class TestAuto {
    public static void main(String[] args) {
        //现有一个叫小王的User,他想找个女朋友
        User u = new User("小王",18,"男");
        //小王自己找了一个叫丽丽的女孩子
        GirlFirend girlFirend = new GirlFirend();
        girlFirend.setName("丽丽");
        girlFirend.setAge(18);
        girlFirend.setSex("女");
        //丽丽成为了小王的女朋友
        u.setGirlFirend(girlFirend);
        System.out.println(u);
    }
}

控制台打印的结果:
自动的方式:   User{name='小王', age=18, sex='男',
girlFirend=GirlFirend{name='丽丽', age=18, sex='女'}}

被动的方式:User告诉IocContainer,需要一个女朋友,IocContainer就会按照要求产生一个女朋友分给你。相当于找了中介帮你找,你只要把要求告诉他就行了。

public class TestAuto {
    public static void main(String[] args) {
        //现有一个叫小王的User,他想找个女朋友
        User u = new User("小王",18,"男");
        //小王找了个婚介所
        IocContainer iocContainer = new IocContainer("容易找婚介所");
        //婚介所按要求找了个叫丽丽的女孩
        GirlFirend girlFirend = iocContainer.getGirlFirend();
        //丽丽成为小王的女朋友了
        u.setGirlFirend(girlFirend);
        System.out.println("被动的方式:   "+u.toString());
        }
    }

控制台打印的结果:
被动的方式:   User{name='小王', age=18, sex='男', 
girlFirend=GirlFirend{name='丽丽', age=18, sex='女'}}

到这里,可以大概了解IOC和DI充当的就是婚介所的角色,在Spring,我们把所有的bean都交给了Spring Ioc容器去管理,在我们需要的时候通过Spring Ioc容器将所需的bean注入进来。

Why use Ioc(Inversion of Control)?

先一起看一下未使用Ioc的例子,使用传统的MVC模式
Dao

public class UserDao{
     private  String database;
     public UserDao(String database){
         this.database = database;
     }
     public void doSomething(){
        System.out.println("保存用户!");
     }
  }

Service

public class UserService{
     private  UserDao dao;
     public UserService(UserDao dao){
         this.dao = dao;
     }
     public void doSomething(){
        dao.doSomething();
     }
  }

Controller

public class Controller{
     private  UserService service;
     public Controller(UserService userService){
         this.service = userService;
     }
     public void doSomething(){
        service.doSomething();
     }
  }

接下来我们就必须手动一个一个创建对象,并将dao、service、controller依次组装起来,然后才能调用

  public static void main(String[] args) {
  
     UserDao dao = new UserDao("mysql");
     UserService service = new UserService(dao);
     Controller controller = new Controller(service);
     controller.doSomething();
}

分析一下这种做法的弊端

  • 每一个bean对象都需要我们手动的去new
  • 在这三层结构中,上层都需要下层是如何创建的,上层必须自己创建下层,这样就形成了紧密的耦合。
  • 通过new关键字生成了具体的对象,这是一种硬编码的方式,违反了面向接口编程的原则。
  • 频繁的创建对象,浪费资源。

我们在换成使用SpringIOC的情况,刚才的代码变成如下:

  public static void main(String[] args) {
  
     ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");
     Controller controller = (Controller) context.getBean("controller");
     controller.doSomething();
}

很明显,使用Ioc之后,我们只管向容器索取所需的Bean即可。Ioc解决了以下几个问题:

  • Bean之间的解耦,这种解耦体现在我们没有在代码中去硬编码bean之间的依赖。(不通过new操作依次构建对象,由SpringIOC内部帮助我们实现了依赖注入)。一方面,IOC容器将通过new对象设置以来的方式转变为运行期动态的进行设置依赖。
  • IOC容器天然的给我们提供了单例。
  • 当需要更换dao的时候,我们只需要在配置文件中更换dao的实现类,完全不会破坏到之前的代码。.
  • 上层现在不需要知道下层是如何创建的。

参考地址

How to use Ioc?

这里介绍常见的两种方式

  1. XML文件配置
  2. 注解方式

XML文件配置:

Step1 : 配置XML(注册bean)

XML配置示例:
(spring-beans.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-4.3.xsd">
 
    <import resource="引入其他bean xml配置文件" />
    <bean id="bean标识" class="类名全路径"/>
    <alias name="bean标识" alias="别名" />
    
    <!--------实例------>
    <import resource="order.xml" />
    <bean id="user6" class="com.javacode2018.lesson001.demo2.UserModel" />
    <alias name="user6" alias="user6_1" />
    <alias name="user6" alias="user6_2" />
</beans>

Step2 : 通过上下文对象实例化、初始化bean

public class IocUseXmlConfiguration(){
     public static void main(String[] args) {
  
         ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-beans.xml");
         UserModel userModel= (UserModel) context.getBean("user6");//xml中bean的id

   }
}

注解方式 :

我们先快速说下,几个注解的作用:

  1. @Configuration:告诉spring容器,这是一个配置类,类似xml中的注解
  2. @Bean:跟@Configuration配合使用,标注在方法上,实例化一个bean,默认以方法名称做bean的id,可以指定别名,初始化方法和销毁方法
  3. @Scope:跟@bean配置使用,标注在方法上,调整作用域,指定bean创建的定义
    prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象;
    singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就是直接从容器(map.get())中拿
    request:同一次请求创建一个实例(web环境下)
    session:同一个session创建一个实例(web环境下)
  4. @Lazy:懒加载,配合@Bean使用,单实例bean:默认在容器启动的时候创建对象;容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化

bean

public class Person {
    private String name;
 
    private int age;
 
    public Person() {
        super();
    }
 
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
}

Step1 : 编写配置类

配置类(相当于XML文件)

@Configuration
public class MyConfig {
 
    // 给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
    @Bean
    public Person person() {
        return new Person("xiaoli", 20);
    }
}

Step2 :

public class IocUseAnnotationConfiguration(){
     public static void main(String[] args) {
  
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
          //把传入的Class进行注册,Class既可以有@Configuration注解,也可以没有@Configuration注解
          //怎么注册? 委托给了 org.springframework.context.annotation.AnnotatedBeanDefinitionReader.register 方法进行注册
         // 传入Class 生成  BeanDefinition,然后通过注册BeanDefinitionRegistry
        ctx.register(MyConfig.class);//必须
        //刷新容器上下文
        ctx.refresh();//必须
        Person person = (Person )ctx.getBean("person");//默认为配置类下带@Bean注解的方法名,也可手动赋值@Bean(value = '')
   }
}

留下问题:
实例化bean对象需要调用一次getBean(),或者通过new来实例化,那两者有什么不同,为什么Spring要使用getBean()的方式呢?

不懂点这

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值