Spring最成功的是其提出的理念,而不是技术本身。
概念
Spring所依赖的两个核心理念:
- 一个是控制反转(IoC)。
- 另一个是面向切面编程(Aspect Oriented Programming,AOP)。
IoC是Spring的核心,可以说Spring是一种基于IoC编程的框架。因为Spring Boot是基于注解的开发Spring IoC,所以这章我会使用全注解来讲解Spring IoC,为后续打下基础。
IoC是一种通过描述来创建或者获取对象的技术,而这个技术不是Spring甚至不是Java独有的。Java初学者更多的时候熟悉的是使用new关键字来创建对象,而Spring是通过描述来创建对象的。Spring Boot并不建议使用XML,而是通过注解的描述生成对象,所以本章主要是通过注解来介绍Spring IoC技术。
一个系统可以创建各种对象,并且这些对象都需要进行管理。此外我们应该注意到对象之间并不是孤立的,它们之间还可能存在依赖的关系。例如,一个班级是由多个老师和同学组成的,那么班级就依赖于多个老师和同学了。为此Spring IoC还提供了依赖注入的功能,使得我们能够通过描述来管理各个对象之间的关系。
为了描述上述的班级、同学和老师这3个对象关系,我们需要一个容器去管理它们和它们之间的关系。在Spring中把每一个需要管理的对象称为Spring Bean(简称Bean),而Spring管理这些Bean的容器,称为Spring IoC容器(或者简称IoC容器)。IoC容器需要具备两个基本的功能:
- 通过描述管理Bean,包括定义、发布、获取和销毁Bean;
- 通过描述完成Bean之间的依赖关系。
在使用IoC之前,需要对IoC容器有一个基本的认识。
IoC容器的两个接口——BeanFactory和ApplicationContext
IoC容器会把Spring所创建的Bean给装配进来进行管理,所以我们主要就和IoC容器打交道,那么我们首先就需要去了解IoC容器底层的内容。对于IoC容器来说,最重要的就是BeanFactory和ApplicationContext这两个接口的定义。由于容器很复杂,一般来说我们只需要知道这两个接口大概能够提供的功能即可,也就是要知道要干什么,无需过度深入理解IoC容器的内容。
IoC容器的底层接口是BeanFactory,它的源码如下:
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
}
前面我们论述了Spring IoC技术是一个容器技术,那么所有的容器都会实现这个接口,这样就可以使用这个接口的方法,从IoC容器中获取Bean的相关信息了。
这里我们主要先谈多个getBean()方法,这些方法都是从IoC容器中获取Bean的。
注意:我们可以看到存在以按名称(by name)查找Bean的,也存在按类型(by type)查找Bean的,请注意这个非常重要,后续遇到的问题和面试的问题都会集中在这里。
BeanFactory是底层接口,它的实现类很多,如图1所示。
图1中,可以看到存在很多的IoC容器,但是它们都会遵守BeanFactory的规范,我们了解BeanFactory接口,就能使用IoC容器了。
不过我们需要讨论另外一个十分重要的接口,那就是ApplicationContext,从图1可以看到它是BeanFactory的子接口。它的重要性在于扩展了许多的接口,极大的增强了IoC容器的功能
从图2,可见ApplicationContext扩展了消息国际化接口(MessageSource)、环境可配置接口(EnvironmentCapable)、应用事件发布接口(ApplicationEventPublisher)和资源模式解析接口(ResourcePatternResolver),所以它的功能会更为强大。
在Spring Boot中,因为不推荐使用XML,所以我不再介绍XML相关的内容。这里我推荐使用的是AnnotationConfigApplicationContext进行实践,因为它是一个全注解的Spring IoC容器,和Spring Boot的环境几乎一致。
开发第一个IoC容器
开发容器首先需要有存到容器的东西,那就是bean,为了创建Bean,我们先创建一个角色类,代码很简单。
package com.ykzhen.articles2.po;
public class Role {
private Long id;
private String roleName;
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
这个代码很简单,相信不用我做任何的解释。之前,我谈过IoC容器是通过描述去创建Bean的,那么如何描述呢?一般来说可以使用传统的XML,也可以使用Java配置文件,因为Spring Boot推荐使用Java配置文件,按趋势XML使用不多了,所以我这里使用Java配置文件。先给出代码,如下。
package com.ykzhen.articles2.conf;
import com.ykzhen.articles2.po.Role;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// @Configuration标明这个文件是一个配置文件
@Configuration
public class AppConfig {
// 将方法返回的值装配到IoC容器中,并且该Bean的名称为“role”
@Bean("role")
public Role initRole() {
var role = new Role();
role.setId(1L);
role.setRoleName("role_name_1");
role.setNote("note_1");
return role;
}
}
主要到下面的两个注解:
@Configuration:表示这个类是一个Java配置文件,这样就可以描述如何创建IoC容器。
@Bean:注解标注在方法上,表示将方法返回的值,装配到IoC容器中。
有了上面这两个文件,我们开始创建Spring IoC容器,代码如下。
package com.ykzhen.articles2;
import com.ykzhen.articles2.conf.AppConfig;
import com.ykzhen.articles2.po.Role;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AppMain {
public static void main(String[] args) {
// 通过配置文件,创建IoC容器
var ctx = new AnnotationConfigApplicationContext(AppConfig.class);
try {
// 获取Bean
var role = ctx.getBean(Role.class);
System.out.println(role.getRoleName());
} finally {
// 关闭IoC容器
ctx.close();
}
}
}
我们运行代码,可以得到如下结果:
15:22:23.611 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1cd072a9
15:22:23.620 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
15:22:23.711 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
15:22:23.712 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
15:22:23.713 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
15:22:23.715 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
15:22:23.719 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'
15:22:23.723 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'role'
role_name_1
15:22:23.751 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1cd072a9, started on Thu Jan 12 15:22:23 CST 2023
可见我们测试成功了。
总结:
上面,我们完成了装配Bean到IoC容器中,然后从IoC容器中去获取Bean的过程,这也是IoC容器最基本的功能。
对于装配Bean到IoC容器,我们还需要去讲述扫描的方式,还有其他相关的内容,这是后续文章需要做的事情了。