spring IoC容器
前言
最近做Spring相关的分享,对Spring IoC容器做个总结
概念
IoC
IoC(Inversion of Control,控制反转)其实是一种设计思想,在Spring,对象的创建和依赖关系的维护不再由应用程序代码直接控制,而是由容器来完成的。
具体理解就是由容器来控制对象的创建、销毁等生命周期及对象与对象之间的依赖关系,而不是传统的通过new的方式。你只需要在Spring IoC容器中登记,「你是什么」「需要什么」即可。所以可以看出来,对于某一个对象而言,控制对象的生存周期不再是引用它的对象,而是Spring IoC容器,这就叫做控制反转。
通俗的说,对象的创建不用自己new和注入依赖对象,而是Spring创建和管理,本质是控制权从应用代码转移到了外部容器(IoC容器),控制器的转移就是反转。
IoC容器
在Spring框架中实现控制反转,需要依赖Spring容器,又叫IoC容器,IoC容器是Spring框架的核心,Spring中IoC容器由两种类型:BeanFactory和ApplicationContext。
BeanFactory是最基本、最底层的IoC容器,提供了基本的IoC功能。
ApplicationContext是BeanFactory的子接口,提供了更多的功能,如国际化文案MessageSource、事件ApplicationEvent等等。
两者的关系属于装饰器模式的一种实现,ApplicationContext借助BeanFactory具备了IoC容器的能力,同时扩展其它功能。
DI
DI(Dependency Injection,依赖注入)应用程序在运行时依赖IoC容器来动态注入对象所需要的外部资源,它也是IoC的一个别名,IoC是一种思想,DI是一种具体的技术实现手段。
一个比喻
人、凳子、教室
人是对象,凳子是对象,上课的教室是容器
小学:人 搬凳子 到教室上课
大学:教室有凳子,人到教室上课
先不考虑人是怎么创建的,这个是科学问题。
凳子,由人创建,变成了教室具备,凳子这个资源的变化,可以理解成控制反转。
凳子,由人给自己,变成了教室把凳子这个资源赋予人,这个过程可以理解成依赖注入。
依赖注入
在Spring中,由IoC容器管理的对象都称为Bean。一个Bean包括类名、属性、依赖关系等信息。
spring提供了3种常见的注入方式:构造器注入、Setter方法注入、字段注入。
构造器注入
构造器注入是通过类构造器来注入依赖关系的。在类中定义一个构造器,参数为依赖的引用,当Spring容器实例化类时,IoC容器将自动注入这些依赖关系。
示例:
public class MyClass {
private MyDependency myDependency;
public MyClass(MyDependency myDependency) {
this.myDependency = myDependency;
}
}
Setter方法注入
Setter方法注入是通过Setter方法来注入依赖关系的。在类中定义一个Setter方法,该方法的参数是依赖关系的实例。当Spring容器实例化类时,IoC容器将自动调用这些Setter方法并注入依赖关系。
示例:
public class MyClass {
private MyDependency myDependency;
public void setMyDependency(MyDependency myDependency) {
this.myDependency = myDependency;
}
}
字段注入
字段注入是通过字段来注入依赖关系的。在类中定义一个字段,该字段被注解为@Autowired,当Spring容器实例化类时,将自动注入依赖关系。
示例:
public class MyClass {
@Autowired
private MyDependency myDependency;
}
依赖注入示例
创建User类
/**
* 用户实体
*/
@Setter
@Getter
@ToString
public class User {
private Long id;
private String userName;
}
/**
* 依赖注入demo
*/
public class DependencyInjectionDemo {
@Autowired
private User userByAutowired;
private User userByConstructor;
private User userBySetter;
public DependencyInjectionDemo(User user) {
this.userByConstructor = user;
}
@Autowired
public void setUserBySetter(User user) {
this.userBySetter = user;
}
/**
* user元数据配置
* 需要为static修饰,不然User bean需要先创建DependencyInjectionDemo,DependencyInjectionDemo创建又依赖User,导致循环引用
* 如果是static,@Bean处理时逻辑有所不同
*/
@Bean
public static User createUser () {
User user = new User();
user.setId(1L);
user.setUserName("kz");
return user;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册当前类 作为配置类
applicationContext.register(DependencyInjectionDemo.class);
// 启动上下文
applicationContext.refresh();
DependencyInjectionDemo injectionDemo = applicationContext.getBean(DependencyInjectionDemo.class);
System.out.println(injectionDemo.userByAutowired);
System.out.println(injectionDemo.userByAutowired == injectionDemo.userByConstructor);
System.out.println(injectionDemo.userByAutowired == injectionDemo.userBySetter);
// 关闭
applicationContext.close();
}
}
执行结果:
User(id=1, userName=kz)
true
true
依赖查找
是指在Spring容器中通过名称或类型查找Bean实例的过程。通过依赖查找,我们可以获取到Spring容器中创建的Bean实例,在Spring中,我们通常使用ApplicationContext或BeanFactory来进行依赖查找。
依赖查找示例
public class DependencyLookUpDemo {
@Autowired
private User userByAutowired;
/**
* bean名称为方法名:createUser
* @return
*/
@Bean
public static User createUser () {
User user = new User();
user.setId(1L);
user.setUserName("kz");
return user;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册当前类 作为配置类
applicationContext.register(DependencyLookUpDemo.class);
// 启动上下文
applicationContext.refresh();
DependencyLookUpDemo lookUpBeanDemo = applicationContext.getBean(DependencyLookUpDemo.class);
User user = applicationContext.getBean(User.class);
System.out.println(user);
User user2 = applicationContext.getBean("createUser", User.class);
System.out.println(user == lookUpBeanDemo.userByAutowired);
System.out.println(user2 == lookUpBeanDemo.userByAutowired);
Arrays.stream(applicationContext.getBeanFactory().getBeanNamesForType(User.class)).forEach(System.out::println);
// 关闭
applicationContext.close();
}
}
执行结果:
User(id=1, userName=kz)
true
true
createUser
扩展
依赖来源(支持依赖注入与依赖查找)
1、自定义 Bean (自定义BeanDefinition或单例对象)
自定义的beanDefinition包括xml、@Bean、BeanDefinitionBuilder等方式配置。
2、容器內建 BeanDefinition(如ConfigurationClassPostProcessor、CommonAnnotationBeanPostProcessor等)
具体见org.springframework.context.annotation.AnnotationConfigUtils)
3、容器內建单例对象(如Environment、MessageSource、LifecycleProcessor、ApplicationEventMulticaster)
具体见org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
org.springframework.context.support.AbstractApplicationContext#initLifecycleProcessor
org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster
org.springframework.context.support.AbstractApplicationContext#initMessageSource
另:关于依赖来源的说明:
出自Spring相关极客专栏
依赖注入与依赖查找区别
1、获取方式和时机:
依赖查找需要主动调用容器提供的方法来获取所需的Bean实例
依赖注入则是在容器创建Bean实例时自动完成的
2、Spring 内建依赖(依赖来源的一种)不支持依赖查找,只能通过依赖注入BeanFactory、ApplicationEventPublisher、ApplicationnContext、ResourceLoader。
具体见org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
示例:
/**
* 内建依赖 示例
*/
public class ResolvableDependencyDemo {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册当前类 作为配置类
applicationContext.register(ResolvableDependencyDemo.class);
// 启动上下文
applicationContext.refresh();
ResolvableDependencyDemo resolvableDependencyDemo = applicationContext.getBean(ResolvableDependencyDemo.class);
// 注入的
System.out.println("resolvableDependencyDemo.applicationEventPublisher :" + resolvableDependencyDemo.applicationEventPublisher);
// lookup,报错:org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.context.ApplicationEventPublisher' available
// System.out.println(applicationContext.getBeanFactory().getBean(ApplicationEventPublisher.class));
// 关闭
applicationContext.close();
}
}
总结
所谓IoC/DI,就是由Spring容器来管理对象生命周期及对象间依赖关系。
依赖注入相比依赖查找一个很大的区别就是,依赖注入可以注入Spring内建依赖,如BeanFactory,属于依赖来源的一种。