Spring IOC

WHAT:什么是IOC(Inversion of Control)

IOC中文翻译叫控制反转,可以说是spring最核心的部分。是spring家族任意组件的基本。控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递(注入)给它。
依赖注入
在软件工程中,依赖注入(dependency injection)的意思为,给予调用方它所需要的事物。 “依赖”是指可被方法调用的事物。依赖注入形式下,调用方不再直接指使用“依赖”,取而代之是“注入” 。“注入”是指将“依赖”传递给调用方的过程。在“注入”之后,调用方才会调用该“依赖”。传递依赖给调用方,而不是让调用方直接获得依赖,这个是该设计的根本需求。

注:编程语言层次下,“调用方”为对象和类,“依赖”为变量。在提供服务的角度下,“调用方”为客户端,“依赖”为服务。

该设计的目的是为了分离关注点,分离调用方和依赖,从而提高可读性以及代码重用性。

依赖注射是控制反转的最为常见的一种技术。
附上wiki中对ioc的介绍:https://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC

WHY:为什么要用IOC

1.减少代码的维护成本

在这里插入图片描述
举个例子,设计行李箱,行李箱依赖箱体,箱体依赖底盘,底盘依赖轮子。这样的设计看起来没问题,但是可维护性很低。加入要改变轮子的尺寸,这样上层所有的零件都要重做。

用代码来展示一下设计行李箱:
在这里插入图片描述
这时需求来了,把轮子的大小改成动态的,就会变成这样:
在这里插入图片描述
非常的麻烦,项目上真的这样写,代码基本上可以说无法维护了。

如果使用依赖注入则会变成这样:
在这里插入图片描述
如果要改变轮子的大小,现在只需要改变轮子就可以了。现在是用过上层控制下层。代码会变这样:
在这里插入图片描述
这次要让轮子能改变大小,只需要这样:
在这里插入图片描述

2.利于协同开发和单元测试

假设这四个零件是四个开发小组,只要定义好了接口,可以同时进行开发,而不相互受到限制。
对于单元测试来讲,如果要写某个零件的单元测试,只需要mock出所需要的的类传入就可以了,而不用把所有零件都new一遍。

WHEN:什么时候用

HOW:怎么用

在这里插入图片描述
实现控制反转主要有两种方式:依赖注入和依赖查找。两者的区别在于,前者是被动的接收对象,在类A的实例创建过程中即创建了依赖的B对象,通过类型或名称来判断将不同的对象注入到不同的属性中,而后者是主动索取相应类型的对象,获得依赖对象的时间也可以在代码中自由控制。

依赖注入

依赖注入有如下实现方式:

基于接口

实现特定接口以供外部容器注入所依赖类型的对象。

基于构造器
基于 set 方法

实现特定属性的public set方法,来让外部容器调用传入所依赖类型的对象。
基于构造函数。实现特定参数的构造函数,在新建对象时传入所依赖类型的对象。

基于注解

基于Java的注解功能,在私有变量前加“@Autowired”等注解,不需要显式的定义以上三种代码,便可以让外部容器传入对应的对象。该方案相当于定义了public的set方法,但是因为没有真正的set方法,从而不会为了实现依赖注入导致暴露了不该暴露的接口(因为set方法只想让容器访问来注入而并不希望其他依赖此类的对象访问)。

依赖查找

依赖查找更加主动,在需要的时候通过调用框架提供的方法来获取对象,获取时需要提供相关的配置文件路径、key等信息来确定获取对象的状态

WHERE:在哪可以用到

在这里插入图片描述
当我们想用到设计模式的依赖倒置原则时。依赖注入就好的实践。
在这里插入图片描述

Spring IOC组件注入

Spring本身是一个轻量级的框架,但是随着项目的不断扩大,如果仍然只使用配置文件形式开发,维护成本会变得越来越高。
这也引出了使用注解的方式进行配置,其中最常用的就是使用注解将组件注入到IOC容器。

使用@ComponentScan

使用包扫描的方式进行组件注入

1.首先新建一个maven工程,引入依赖

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.3.2</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

工程结构

在这里插入图片描述

2.在Config中编写代码

@ComponentScan("com.gcl")
public class Myconfig {
}

意为将com.gcl下的所有带@Component注解的组件进行注入

3.新建测试类
一直使用的都是这一个方法

public class MyTest {
	@Test
	public void test01() {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Myconfig.class);
		String[] names = applicationContext.getBeanDefinitionNames();
		for (String name : names) {
			System.out.println(name);
		}
	}
}

打印结果为

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myconfig
myController

前面是spring的组件,myconfig是配置类,myController加了@Controller注解

使用@Bean

编写配置类代码

@Configuration
public class Myconfig {
	@Bean
	public Person person(){
		return new Person();
	}
}

打印结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myconfig
person

person被注入进了ioc容器中

使用@Import

其中有三种方式

1.@Import

在配置类中编写代码

@Configuration
@Import(Person.class)
public class Myconfig {
}

打印结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myconfig
com.gcl.bean.Person

person类被注入

2.ImportSelector

编写一个类,实现ImportSelector接口,代码如下

public class MySelector implements ImportSelector {
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		return new String[]{"com.gcl.bean.Person"};
	}
}

配置类

@Configuration
@Import(MySelector.class)
public class Myconfig {
}

测试类打印结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myconfig
com.gcl.bean.Person

person同样被注入

3.ImportBeanDefinitionRegistrar
写一个类实现ImportBeanDefinitionRegistrar ,在其中设置一个规则,如果red和green都注入了,再注入blue

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
		boolean definition1 = beanDefinitionRegistry.containsBeanDefinition("com.gcl.bean.Red");
		boolean definition2 = beanDefinitionRegistry.containsBeanDefinition("com.gcl.bean.Green");
		if (definition1 && definition2){
			RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Blue.class);
			beanDefinitionRegistry.registerBeanDefinition("blue",rootBeanDefinition);
		}
	}
}

配置类

@Configuration
@Import({MyImportBeanDefinitionRegistrar.class, Red.class, Green.class})
public class Myconfig {
}

打印结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myconfig
com.gcl.bean.Red
com.gcl.bean.Green
blue

FactoryBean

创建一个类,实现FactoryBean接口,代码如下:

public class MyFactoryBean implements FactoryBean<Red> {
	public Red getObject() throws Exception {
		return new Red();
	}

	public Class<?> getObjectType() {
		return Red.class;
	}

	public boolean isSingleton() {
		return true;
	}
}

修改配置类

@Configuration
public class Myconfig {
	@Bean
	public MyFactoryBean myFactoryBean(){
		return new MyFactoryBean();
	}
}

修改测试类

public class MyTest {
	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Myconfig.class);

	@Test
	public void test01() {
		printBeans(applicationContext);
		Object factoryBean1 = applicationContext.getBean("myFactoryBean");
		Object factoryBean2 = applicationContext.getBean("myFactoryBean");
		System.out.println(factoryBean1.getClass());
		System.out.println(factoryBean1 == factoryBean2);
	}

	private void printBeans(AnnotationConfigApplicationContext applicationContext) {
		String[] names = applicationContext.getBeanDefinitionNames();
		for (String name : names) {
			System.out.println(name);
		}
	}
}

测试结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myconfig
myFactoryBean
class com.gcl.bean.Red
true

工厂生成的是Red而不是它本身
如果想获取工厂本身
加一个前缀&

Object factoryBean3 = applicationContext.getBean("&myFactoryBean");

打印结果

com.gcl.config.MyFactoryBean@411f53a0
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值