一文带你了解什么是IOC容器及IOC容器注入的方式

一文带你了解什么是IOC容器及IOC容器注入的方式

  • IOC控制反转
    • 什么是IOC
    • IOC的优势
    • IOC和DI
  • Spring中IOC注入的方式
    • 基于XML配置文件方式
      • 1、基本使用
        • 1.1 添加依赖
        • 1.2 添加spring配置文件
        • 1.3 注册bean
        • 1.4 测试获取
      • 2、从容器中获取对象的方式
        • 2.1 根据id获取对象(通过ApplicatContext获取)
        • 2.2 根据name获取对象(通过ApplicatContext获取)
        • 2.3 根据类型(通过ApplicatContext获取)
        • 2.4 通过BeanFactory接口获取ioc容器中的对象
      • 3. BeanFactory和ApplicationContext的联系
      • 5、属性注入
        • 5.1 构造注入
        • 5.2 设值注入
        • 5.3 其他类型属性注入方式
    • 基于java配置文件方式

IOC控制反转

什么是IOC

(IOC)控制反转首先而是一种思想,控制反转主要是对对象控制权的转移。springioc容器创建对象,注入依赖。然后将对象的使用权交出去。
如果想要理解控制反转,我们应该了解“谁控制谁?控制什么?为何是反转?(有反转就有正转),哪些方面反转了?”

1. 谁控制谁?控制什么:在传统程序开发过程中,主要是由程序通过new来创建对象。而控制反转思想中,是由IOC容器创建对象,并注入对象需要的依赖对象。谁控制谁?肯定是IOC容器控制了对象。控制什么?主要就是控制了外部对资源的获取。

2. 为什么是反转?哪些方面反转了?:有反转肯定就有正转。在传统程序开发过程中,由程序自己去创建对象,创建该对象依赖的对象,这就是正转。而在IOC中,由IOC容器负责对象的创建和该对象的依赖对象。这就是反转。那为什么叫反转,主要是因为IOC容器帮助我们查找和注入依赖对象,对象只是被动的接受依赖对象,所以叫做反转。

IOC的优势

首先这么做,降低了对象之间的耦合度,提高了代码的复用。创建对象不在由程序自己创建,而是交给容器。开发人员只需简单的配置就可以从容器中获取需要的对象,增加了组件的利用效率。

IOC和DI

在spring的官网中,对IOC容器的介绍,就是指IOC又可以成为DI(依赖注入),其实IOC和DI是对同一概念不同角度的理解。

1. 谁依赖谁:应用程序依赖IOC容器
2. 为什么要依赖:因为应用程序需要从IOC容器中获取外部资源
3. 谁注入谁:IOC容器注入应用程序某个对象。应用程序依赖的对象。
4. 注入了什么:肯定是注入了某个外部程序应用的资源。

Spring中IOC注入的方式

基于XML配置文件方式

1、基本使用

通过maven方式构建。

1.1 添加依赖

引用spring相关依赖


    
        org.springframework
        spring-context
        5.1.17.RELEASE
    

    
    
        junit
        junit
        4.12
        test
    


1.2 添加spring配置文件




    

1.3 注册bean

将需要IOC管理的类型通过标签来注册




    
    

1.4 测试获取

/**
     * IoC的方式获取 UserBean 对象
     */
@Test
public void fun2(){
    // 1.IoC容器的初始化操作 调用UserBean中无参构造器创建对象
    ApplicationContext ac =
        new ClassPathXmlApplicationContext("applicationContext.xml");
    // 2.从容器中获取UserBean对象 没有显示的new
    UserBean user = (UserBean) ac.getBean("userBean");
    user.say();
}

这么操作,默认调用的是Bean类的无参构造器。
如果调用有参构造器怎么做?我们直接改写xml中的配置就好。



    
        
        
    

或者:



    
        
        
    

2、从容器中获取对象的方式

2.1 根据id获取对象(通过ApplicatContext获取)

bean标签中的id声明有一个

2.2 根据name获取对象(通过ApplicatContext获取)

bean标签中的name可以声明一个或多个






id="u1,u2,u3" 只表示一个

name="n1,n2,n3" 表示会被拆分为3个name属性【拆分会根据 ',' ';' ' ' 空格 】

2.3 根据类型(通过ApplicatContext获取)

也可以根据需要调用对象的类型获取对象

    @Test
    public void fun05(){
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext-3.xml");
        Cat cat=context.getBean(Cat.class);
        System.out.println(cat);
    }

如果IOC容器中,注入了多个同类型的对象,就会报错。



    
        
        
    

    
        
        
    

    @Test
    public void fun06() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-4.xml");
        Cat cat = context.getBean(Cat.class);
        System.out.println(cat);
    }

报错如下:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.iocdemo.projo.Cat' available: expected single matching bean but found 2: cat1,cat2

	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1166)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:416)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:349)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1124)
	at com.iocdemo.test.MaiinTest.fun06(MaiinTest.java:64)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

这个时候如果获取对象时,只指定一个Cat类型,还是不行的,必须加上id或name:

    @Test
    public void fun06() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-4.xml");
        Cat cat = context.getBean("cat1",Cat.class);
        System.out.println(cat);
    }

结果如下:

Cat{nick='小猫咪', color='red'}

除了以上的方法外,我们还可以在xml配置文件中,给bean标签增加primary。设置 primary属性为true,那么当同一类型有多个对象时,会优先返回primary属性的对象



    
        
        
    

    
        
        
    

2.4 通过BeanFactory接口获取ioc容器中的对象

在上方几个获取IOC容器对象的方法中,其实代码中主要默认的是使用ApplicationContext类来获取。接下来讲一个BeanFactory接口实现获取对象的方法。前面ApplicationContext类通过id,name,类型获取的方法,BeanFactory接口都有这些方法。获取方式如下:

   @Test
    public void fun08() {
        ClassPathResource classPathResource = new ClassPathResource("applicationContext-4.xml");
        BeanFactory beanFactory = new XmlBeanFactory(classPathResource);
        System.out.println("===========");
        Cat cat = (Cat) beanFactory.getBean("cat1");
        System.out.println(cat);


    }

在这里,我们主要讲一下BeanFactory和ApplicationContext的联系和区别。

3. BeanFactory和ApplicationContext的联系

一文带你了解什么是IOC容器及IOC容器注入的方式_第1张图片
通过类图我们可以看出,BeanFactory是个接口,而ApplicationContext继承了BeanFactory。所以BeanFactory中有的功能,ApplicationContext也有。同时ApplicationContext拓展了很多BeanFactory不具备的功能【事件广播,资源加载,web支持等等…】以下是两个类的具体区别。
BeanFactory:
是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和获取IOC容器对象的功能;
ApplicationContext:
应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;

  1. 国际化(MessageSource)
  2. 访问资源,如URL和文件(ResourceLoader)
  3. 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
  4. 消息发送、响应机制(ApplicationEventPublisher)
  5. AOP(拦截器
    两者装载bean的区别:
    BeanFactory:
    BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;
    代码如下:
    @Test
    public void fun08() {
        ClassPathResource classPathResource = new ClassPathResource("applicationContext-4.xml");
        BeanFactory beanFactory = new XmlBeanFactory(classPathResource);
        System.out.println("===========");
        Cat cat = (Cat) beanFactory.getBean("cat1");
        System.out.println(cat);
    }

结果:

===========//先执行的打印
调用的无参构造器//再执行的构造函数,说明BeanFactory在初始化的时,不会构造对象,而是当引用对象时,才会进行构建。
Cat{nick='小猫咪', color='red'}
Process finished with exit code 0

ApplicationContext:
ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;
代码:

    @Test
    public void fun06() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-4.xml");
        System.out.println("=======");
        Cat cat = context.getBean(Cat.class);
        System.out.println(cat);
    }
调用的无参构造器//先构造对象
======= //再进行打印,说明ApplicationContext在初始化IOC容器时,会先构建对象,
Cat{nick='小猫咪', color='red'}

当然我们在使用ApplicationContext初始化IOC对象时,也可以通过指定属性lazy-init=true来使其达到BeanFactory延迟加载的效果。如下:
applicationContext.xml文件内容:

    
        
        
    

再次运行下方代码:

    @Test
    public void fun06() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-4.xml");
        System.out.println("=======");
        Cat cat = context.getBean(Cat.class);
        System.out.println(cat);
    }

结果实现了延迟加载的效果

=======
调用的无参构造器
Cat{nick='小猫咪', color='red'}

5、属性注入

属性注入主要是指如何给对象中的属性赋值

5.1 构造注入

通过构造方法注入

首先得提供对应的构造方法

既可以通过name也可以通过index来指定要赋值的参数

xml配置文件如下:



    
        
        
        
        
    

    @Test
    public void fun09() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-5.xml");
        Cat cat = (Cat) context.getBean("cat");
        System.out.println(cat);
    }

运行结果如下:

调用的有参构造器
Cat{nick='red', color='小猫咪'}

这种做法要求实体类中有相应的构造函数。否则就会报如下错误:

"C:\Program Files\Java\jdk1.8.0_261\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:E:\IDEA\IntelliJ IDEA 2019.1.4\lib\idea_rt.jar=9600:E:\IDEA\IntelliJ IDEA 2019.1.4\bin" -Dfile.encoding=UTF-8 -classpath "E:\IDEA\IntelliJ IDEA 2019.1.4\lib\idea_rt.jar;E:\IDEA\IntelliJ IDEA 2019.1.4\plugins\junit\lib\junit-rt.jar;E:\IDEA\IntelliJ IDEA 2019.1.4\plugins\junit\lib\junit5-rt.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\rt.jar;E:\01大圣工作\A55项目\车型导入\2021022301\p5\ioc_xmldemo\target\test-classes;E:\01大圣工作\A55项目\车型导入\2021022301\p5\ioc_xmldemo\target\classes;C:\Users\gacneadmin\.m2\repository\org\springframework\spring-context\5.1.17.RELEASE\spring-context-5.1.17.RELEASE.jar;C:\Users\gacneadmin\.m2\repository\org\springframework\spring-aop\5.1.17.RELEASE\spring-aop-5.1.17.RELEASE.jar;C:\Users\gacneadmin\.m2\repository\org\springframework\spring-beans\5.1.17.RELEASE\spring-beans-5.1.17.RELEASE.jar;C:\Users\gacneadmin\.m2\repository\org\springframework\spring-core\5.1.17.RELEASE\spring-core-5.1.17.RELEASE.jar;C:\Users\gacneadmin\.m2\repository\org\springframework\spring-jcl\5.1.17.RELEASE\spring-jcl-5.1.17.RELEASE.jar;C:\Users\gacneadmin\.m2\repository\org\springframework\spring-expression\5.1.17.RELEASE\spring-expression-5.1.17.RELEASE.jar;C:\Users\gacneadmin\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\gacneadmin\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.iocdemo.test.MaiinTest,fun09
五月 16, 2021 9:17:34 下午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cat' defined in class path resource [applicationContext-5.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cat' defined in class path resource [applicationContext-5.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)

	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:268)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1338)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:554)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:514)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:319)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:863)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
	at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:144)
	at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:85)
	at com.iocdemo.test.MaiinTest.fun09(MaiinTest.java:95)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)


Process finished with exit code -1

5.2 设值注入

注入的属性必须提供对应的setter方法
配置如下:



    
        
        
    

    
        
        
    

5.3 其他类型属性注入方式

假如实体类如下:

package com.iocdemo.projo;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * @program: ioc_xmldemo
 * @description:
 * @author: 
 * @create: 2021-05-16 21:11
 */

public class User {
    private Integer id;

    private String userName;

    private Cat cat;

    private String[] favorites;

    private List cats;

    private Map map;

    private Properties props;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public String[] getFavorites() {
        return favorites;
    }

    public void setFavorites(String[] favorites) {
        this.favorites = favorites;
    }

    public List getCats() {
        return cats;
    }

    public void setCats(List cats) {
        this.cats = cats;
    }

    public Map getMap() {
        return map;
    }

    public void setMap(Map map) {
        this.map = map;
    }

    public Properties getProps() {
        return props;
    }

    public void setProps(Properties props) {
        this.props = props;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", cat=" + cat +
                ", favorites=" + Arrays.toString(favorites) +
                ", cats=" + cats +
                ", map=" + map +
                ", props=" + props +
                '}';
    }
}



    
        
        
    
    
        
        
            
                
                    
                    
                
                
                    
                    
                
            
        
        
            
                篮球
                登山
                游泳
            
        
        
            
                
                
            
        
        
            
                root
                123456
            
        
    

基于java配置文件方式

实体类:

public class User {
}

java配置类:

@Configuration //通过@Configuration初始化IOC容器
public class UserConfig {

    @Bean(name = {"a1", "a2"})
    public User getUser() {
        User user = new User();
        return user;
    }

测试类:

public class MainTest01 {

    @Test
    public void fun01() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(UserConfig.class);
        User user = (User) ac.getBean("a1");
        System.out.println(user);
    }
}

你可能感兴趣的:(Spring,java,spring,ioc)