spring源码深度解析和spring揭秘(1.Spring源码整体脉络及注解的使用)
spring源码深度解析和spring揭秘(1.Spring源码整体脉络及注解的使用)ORM: 如 JPA、Hibernate、iBatis 等,提供了 一个交互层。JDBC: 提供了一个 JDBC 抽象层,它可以消除冗长的JDBC编码和解析数据库厂商特有的错误代码。 这个模块包含了 Spring 对 JDBC 数据访问进行封装的所有类。Context: (处理BeanFactory,以下还ApplicationContext的作用) 构建于 Core 和 beans 模块基础之上,提供了一种类似 JNDI 注册器的框架式的对象访问方法。 Context 模块继承了 Beans 的特性,为 Spring 核心提供了大量扩展,添加了对国际化(例如资源绑定)、事件传播、资源加载和对 Context 的透明创建的支持。Context 模块同时也支持 J2EE 的一些特性, ApplicationContext 接口是 Context。BeanFactory 和 Applactio
思维导图:点击查看思维导图
文章图片:点击查看图片
模块作用: Core 和 Beans 模块是框架的基础部分,提供 IOC(控制反转)和 DI (依赖注入) 特性。 这里的基础概念是 BeanFactory,它提供对 Factory 模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。
Core: 主要包含 Spring 框架基本的核心工具类, Spring 的其他组件都要用到这个包里的类,Core 模块是其他组件的基本核心。
Beans:(BeanFacotry的作用) 它包含访问配置文件、创建和管理 bean 以及进行 Inversion Of Control / Dependency Injection ( IOC/DI )操作相关的所有类。(思考题 1)
Context: (处理BeanFactory,以下还ApplicationContext的作用) 构建于 Core 和 beans 模块基础之上,提供了一种类似 JNDI 注册器的框架式的对象访问方法。 Context 模块继承了 Beans 的特性,为 Spring 核心提供了大量扩展,添加了对国际化(例如资源绑定)、事件传播、资源加载和对 Context 的透明创建的支持。Context 模块同时也支持 J2EE 的一些特性, ApplicationContext 接口是 Context。
BeanFactory 和 ApplactionContext 的区别: (思考题 2)
- BeanFactory 是懒加载的,ApplactionContext 是非懒加载的(可以指定为懒加载)
- BeanFactory 只有一个职责就是调用 getBean() 生产 Bean,而 ApplactionContext 是 BeanFactoy 的扩展,是面向用户的,有更多的实现(包括AOP、读取资源文件、国际化、事件传播等)
SPEL(Expression Language): 提供了强大的表达式语言,用于在运行时查询和操纵对象。
2.Spring Data Access/IntegrationJDBC: 提供了一个 JDBC 抽象层,它可以消除冗长的JDBC编码和解析数据库厂商特有的错误代码。 这个模块包含了 Spring 对 JDBC 数据访问进行封装的所有类。
ORM: 如 JPA、Hibernate、iBatis 等,提供了 一个交互层。
OXM: Object/XML 映射实现包括 JAXB、 Castor、 XMLBeans、 JiBX 和 XStrearn
JMS: 包含了 一些制造和消费消息的特性
Transaction: 支持编程和声明性的事务管理,这些事务类必须实现特定的接口,并且对所有的 POJO 都适用。
3.Spring Web提供了基础的面向 Web 的集成特性。例如,多文件上传、使用 servlet listeners 初始化 IOC 容器以及一个面向 Web 的应用上下文。 它还包含 Spring 远程支持中 Web 的相关部分。
4.Spring AopAspects: 提供了对 AspectJ 的集成支持。
Instrumentation: 提供了 class instrumentation 支持和 classLoader 实现,使得可以在特定的应用服务器上使用
5.Test支持使用 JUnit 和 TestNG
二、Spring IOC 容器底层注解使用1.xml配置形式和配置类形式①基于xml的形式定义Bean的信息
<bean class="com.zhe.spring.HelloSpring">
<property name="car" ref="car"/>
</bean>
<!-- 基于xml的形式定义Bean的信息 -->
<bean id="car" class="com.zhe.spring.Car"></bean>
复制代码
// 1.基于xml的形式定义Bean的信息
// ClassPathXmlApplicationContext解析xml 去容器中读取Bean
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
log.debug(ctx.getBean("car").toString());
复制代码
②基于读取配置类的形式定义Bean信息
@Configuration
public class MainConfig {
@Bean
public Car car(){
return new Car();
}
}
复制代码
// 2.基于读取配置类的形式定义Bean信息
// 通过@Bean的形式是使用的话, bean的默认名称是方法名,也可以通过@Bean(value="bean的名称") 指定
AnnotationConfigApplicationContext atx = new AnnotationConfigApplicationContext(MainConfig.class);
log.debug(atx.getBean("car").toString());
复制代码
2.在配置类上写@CompentScan注解来进行包扫描
@Configuration
@ComponentScan(basePackages = {"com.zhe.testcompentscan"})
public class MainConfig { }
复制代码
①排除用法 excludeFilters(排除@Controller注解的,和NotScanService的)
@Configuration
@ComponentScan(basePackages = {"com.zhe.testcompentscan"} excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION value = {Controller.class})
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE value = {NotScanService.class})
})
public class MainConfig {
}
复制代码
②包含用法 includeFilters,注意,若使用包含的用法,需要把useDefaultFilters属性设置为false(true表示扫描全部的)
// 扫描Controller和Service
@Configuration
@ComponentScan(basePackages = {"com.zhe.testcompentscan"} includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION value = {Controller.class Service.class}) } useDefaultFilters = false)
public class MainConfig {
}
复制代码
public enum FilterType {
//注解形式 比如@Controller @Service @Repository @Component
ANNOTATION
//指定的类型
ASSIGNABLE_TYPE
//aspectJ形式的
ASPECTJ
//正则表达式的
REGEX
//自定义的
CUSTOM
}
复制代码
③FilterType.CUSTOM 自定义类型如何使用
public class TestFilterType implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前类的注解源信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前类的class的源信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类的资源信息
Resource resource = metadataReader.getResource();
if(classMetadata.getClassName().contains("dao")) {
return true;
}
return false;
}
复制代码
@ComponentScan(basePackages = {"com.zhe.testcompentscan"} includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM value = TestFilterType.class) } useDefaultFilters = false)
public class MainConfig {
}
复制代码
3.配置Bean的作用域对象
①在不指定 @Scope 的情况下,所有的 bean 都是单例的 bean,而且是饿汉加载(容器启动实例就创建好了)
@Bean
public Person person() {
return new Person();
}
复制代码
②指定 @Scope 为 prototype 表示为多实例的,而且还是懒加载
@Bean
@Scope(value = "prototype")
public Person person() {
return new Person();
}
复制代码
③@Scope 指定的作用域方法取值
a) singleton 单实例的(默认)
b) prototype 多实例的 不能解决循环依赖
c) request 同一次请求复用一个单例对象
d) session 同一个会话级别复用一个单例对象
e) application ServletContext的生命周期中复用一个单例对象
f) websocket websocket的生命周期中复用一个对象
复制代码
4.Bean的懒加载
@Lazy (主要针对单实例的 bean 容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象)
@Bean
@Lazy
public Person person() {
return new Person();
}
复制代码
5.@Conditional 进行条件判断
场景: 有两个组件 TestAspect 和 TestLog,TestLog 组件是依赖于 TestAspect 的组件,当容器中有 TestAspect 时,TestLog 才会实例化
应用: 自己创建一个 TestCondition 的类实现 Condition 接口
public class TestCondition implements Condition {
@Override
public boolean matches(ConditionContext context AnnotatedTypeMetadata metadata) {
//判断容器中是否有testAspect的组件
if(context.getBeanFactory().containsBean("testAspect")) {
return true;
}
return false;
}
}
复制代码
public class MainConfig {
@Bean public TestAspect testAspect() { return new TestAspect (); }
//当且 容器中有testAspect的组件,那么testingLog才会被实例化.
@Bean
@Conditional(value = TestCondition.class)
public TestLog testLog() {
return new TestLog ();
}
}
复制代码
6.往 IOC 容器中添加组件的方式6.1.通过 @CompentScan @Controller @Service @Respository @Component
适用场景: 针对我们自己写的组件可以通过该方式来进行加载到容器中
// 无论加哪个注解都是一样的,仅为提高可读性,推荐使用下面的方法
@Controller:控制器,推荐给controller层添加此注解
@Service:业务逻辑,推荐给业务逻辑层添加此注解
@Repository:仓库管理,推荐给数据访问层添加此注解
@Component:给不属于以上基层的组件添加此注解
复制代码
@Controller 是 @Component 的子组件
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
String value() default "";
}
复制代码
6.2. 通过 @Bean 的方式来导入组件(适用于导入第三方组件的类)
@Bean是一个方法级别的注解,它与 XML中的元素类似。注解支持 提供的一些属性,例如 (init-method、destroy-method、autowiring、name ) 开发者可以在 @Configuration 类或 @Component 类中使用 @Bean 注解。
使用@Configuration注解类时,这个类的目的就是作为 bean 定义的地方。此外,@Configuration类允许通过调用同一个类中的其他@Bean方法来定义 bean 间依赖关系。
6.3.通过 @Import 来导入组件 (3 种方式,导入组件的id为全类名路径)// @Import 注解允许从另一个配置类加载@Bean定义
@Configuration
@Import(value = {Person.class Car.class})
public class MainConfig { }
复制代码
通过 @Import 的 ImportSeletor 类实现组件的导入 (导入组件的id为全类名路径)
public class TestImportSelector implements ImportSelector {
//可以获取导入类的注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.zhe.testimport.compent.Dog"};
}
}
@Configuration
@Import(value = {Person.class Car.class TestImportSelector.class})
public class MainConfig { }
复制代码
通过 @Import 的 ImportBeanDefinitionRegister 导入组件 (可以指定 bean 的名称)
public class TestBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata BeanDefinitionRegistry registry) {
// 创建一个bean定义对象
rootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Cat.class);
// 把bean定义对象导入到容器中
registry.registerBeanDefinition("car" rootBeanDefinition);
}
}
@Configuration
// @Import(value = {Person.class Car.class})
// @Import(value = {Person.class Car.class TestImportSelector.class})
@Import(value = {Person.class Car.class TestImportSelector.class TestBeanDefinitionRegister.class})
public class MainConfig {
}
复制代码
6.4.通过实现 FacotryBean 接口来实现注册组件(适合复杂初始化的Bean)
public class CarFactoryBean implements FactoryBean<Car> {
//返回bean的对象
@Override
public Car getObject() throws Exception {
return new Car();
}
//返回bean的类型
@Override
public Class<?> getObjectType() {
return Car.class;
}
//是否为单例
@Override
public boolean isSingleton() {
return true;
}
}
复制代码
7.Bean的初始化方法和销毁方法
①什么是 Bean 的生命周期? bean的创建--->初始化--->销毁方法
由容器管理 Bean 的生命周期,我们可以通过自己指定 bean的 初始化方法和 bean 的销毁方法。
- 针对单实例 bean 的话,容器启动的时候,bean 的对象就创建了(默认懒加载),而且容器销毁的时候,也会调用bean 的销毁方法
- 针对多实例 bean 的话,容器启动的时候,bean 是不会被创建的而是在获取 bean 的时候被创建,而且 bean 的销毁不受 IOC 容器的管理
@Configuration
public class MainConfig {
//指定了bean的生命周期的初始化方法和销毁方法.
@Bean(initMethod = "init" destroyMethod = "destroy")
public Car car() {
return new Car();
}
public void init(){
// 初始化
}
public void destroy(){
// 销毁
}
}
复制代码
②通过 InitializingBean 和 DisposableBean 的二个接口实现 bean 的初始化以及销毁方法
@Component
public class Person implements InitializingBean DisposableBean {
public Person() {
System.out.println("Person的构造方法");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean的destroy()方法 "); }
@Override
public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean的 afterPropertiesSet方法");
}
}
复制代码
③通过JSR250规范 提供的注解 @PostConstruct 和 @ProDestory 标注的方法
@Component
public class Book {
public Book() {
System.out.println("book 的构造方法");
}
@PostConstruct
public void init() {
System.out.println("book 的PostConstruct标志的方法");
}
@PreDestroy
public void destory() {
System.out.println("book 的PreDestory标注的方法");
}
}
复制代码
④通过 Spring 的 BeanPostProcessor 的 bean 的后置处理器会拦截所有 bean 创建过程执行顺序 Spring IOC 容器实例化 Bean => 调用BeanPostProcessor 的 postProcessBeforeInitialization 方法 => 调用 bean 实例的初始化方法 => 调用 BeanPostProcessor 的 postProcessAfterInitialization 方法
- postProcessBeforeInitialization 在 init 方法之前调用
- postProcessAfterInitialization 在 init 方法之后调用
@Component
public class TestBeanPostProcessor implements BeanPostProcessor {
// init方法之前调用
@Override
public Object postProcessBeforeInitialization(Object bean String beanName) throws BeansException {
System.out.println("...postProcessBeforeInitialization:" beanName);
return bean;
}
// init方法之后调用
@Override
public Object postProcessAfterInitialization(Object bean String beanName) throws BeansException {
System.out.println("...postProcessAfterInitialization:" beanName);
return bean;
}
}
复制代码
BeanFactoryPostProcessor bean工厂的 bean 属性处理容器,用于管理我们的 Bean 工厂内所有的 Beandefinition(未实例化)数据,可以随心所欲的修改实例属性。
@Component
public class TestMyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("TestMyBeanFactoryPostProcessor...postProcessBeanFactory...");
int count = beanFactory.getBeanDefinitionCount();
String[] names = beanFactory.getBeanDefinitionNames();
System.out.println("当前BeanFactory中有" count " 个Bean");
System.out.println(Arrays.asList(names));
}
}
复制代码
8.通过 @Value @PropertySource 来给组件赋值
public class Person {
//通过普通的方式
@Value("值")
private String firstName;
//spel方式来赋值
@Value("#{38-8}")
private Integer age;
//通过读取外部配置文件的值
@Value("${person.lastName}")
private String lastName;
}
@Configuration
@PropertySource(value = {"classpath:person.properties"}) //指定外部文件的位置
public class MainConfig {
@Bean
public Person person() {
return new Person();
}
}
复制代码
9.自动装配9.1.@Autowired的使用
自动注入:
//一个Dao
@Repository
public class TestDao {
}
@Service
public class TestService {
@Autowired
private TestDao testDao;
}
复制代码
注意:
- 使用 @Autowired 注解时,自动装配的时候是根据类型实现的。
- 1、如果只找到一个,则直接进行赋值,
- 2、如果没有找到,则直接抛出异常,
- 3、如果找到多个,那么会按照变量名作为 id 继续匹配
- 1、匹配上直接进行装配
- 2、如果匹配不上则直接报异常
- 假设我们需要指定特定的组件来进行装配,我们可以通过使用@Qualifier("testDao")来指定装配的组件或者在配置类上的 @Bean 加上 @Primary 注解
- 假设我们容器中没有 那么在装配的时候就会抛出异常 不抛异常就指定@Autowired(required = false)
- /** * 当方法上有@AutoWired注解时: * 1、此方法在bean创建的时候会自动调用 * 2、这个方法的每一个参数都会自动注入值 * @param personDao */ @Autowired public void test(PersonDao personDao){ System.out.println("此方法被调用:" personDao); } /** * @Qualifier注解也可以作用在属性上,用来被当作id去匹配容器中的对象,如果没有 * 此注解,那么直接按照类型进行匹配 * @param personService */ @Autowired public void test2(@Qualifier("personServiceExt") PersonService personService){ System.out.println("此方法被调用:" personService); } 复制代码
功能和 @Autowired 的功能差不多一样,但是不支持 @Primary 和 @Qualifier。@Autowired只适合Spring 框架,而@Resource 扩展性更好。@Autowired 是 spring 中提供的注解,@Resource 是 JDK 中定义的注解,依靠的是 Java 的标准
9.3.@InJect(JSR330规范)<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
复制代码
需要导入jar包依赖。功能和支持@Primary功能 但是没有Require=false的功能
10.自己编写的组件需要使用 IOC 底层组件时(比如 ApplicationContext 等)可以通过实现XXXAware接口来实现@Component
public class TestCompent implements ApplicationContextAware BeanNameAware {
private ApplicationContext applicationContext;
@Override
public void setBeanName(String name) {
System.out.println("current bean name is :【" name "】");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
复制代码
11.@Profile注解
通过@Profile注解来根据环境来激活标识不同的Bean
- @Profile标识在类上,那么只有当前环境匹配,整个配置类才会生效
- @Profile标识在Bean上 ,那么只有当前环境的Bean才会被激活
- 没有标志为@Profile的bean 不管在什么环境都可以被激活
//标识为测试环境才会被装配
@Bean
@Profile(value = "test")
public DataSource testDs() {
return buliderDataSource(new DruidDataSource());
}
复制代码
激活切换环境的方法:
// 方法一
-Dspring.profiles.active=test|dev|prod
// 方法二
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("test" "dev");
ctx.register(MainConfig.class);
ctx.refresh();
printBeanName(ctx);
}
复制代码
三、Spring 源码的整体脉络
IOC容器的核心思想: 资源不由使用资源的双方管理,而由不使用资源的第三方管理,这可以带来很多好处。
- 资源集中管理,实现资源的可配置和易管理
- 降低了使用资源双方的依赖程度,也就是我们说的耦合度(解耦)
- 可维护性、灵活性、扩展性变高
控制反转(Inversion of control): (思考题 6) 创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的(new),而现在这种权力转移到第三方,比如转移交给了 IOC 容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖 IOC 容器了,通过 IOC 容器来建立它们之间的关系。
依赖注入(Dependency injection): 控制反转之后,获得依赖对象的过程由自身管理变为有 IOC 容器自动注入。依赖注入是实现 IOC 的方法,就是有 IOC 容器在运行期间,动态的将某种依赖关系注入到对象当中。
我们的类要生产成一个 Bean 不是一步到位的,它会涉及很多繁杂的步骤: (思考题3&4&5)
什么是 BeanDefinition 呢?
用来描述 Bean 的,存放关于 Bean 的一些列信息,例如:单例、多例、作用域、Bean对应的Class、是否懒加载等等。
- BeanDefinition 元数据信息,返回该 Bean 的来源
- AttributeAccessor 提供对 BeanDefinition 属性操作能力
1.首先要将类加载成 BeanDefinition (Bean定义)
XML 配置的类可以读取成 Bean,Annotation (注解) 配置的类也可以读取 Bean 但是这两种 Bean 配置方式又不同 ,因此引申出统一的 Bean 定义:BeanDefinition
XML 和 Annotation 都会被先被读成 BeanDenfintion (里面包含了大量属性:限定类名、单例、多例等)。这个过程也包含一些列的复杂步骤:
- a.读取配置类: BeanDefinitionReader 负责读取配置类,基于注解和 XML,又有 AnnotatedBeanDefinitionReader 、XmlBeanDefinitionReader读xml和Annotation 去读取各自的配置
- b.扫描配置类: BeanDefinitionSacnner 负责扫描配置类 @Compontent。@Component 中可以添加有一些列的限制条件,BeanDefinitionSacnner 可以将符合条件的类读取到容器中,用于注册 BeanDenfintion
- c.注册Bean定义: 最后由 BeanDefinitionRegistry 注册 BeanDenfintion 放入 BeanDefinitionMap 中
在这个过程中 ApplicationContext 还提供了扩展点来对 BeanDenfintion 进行扩展,Spring 生态除了 IOC 都需要这些扩展点来实现。它是 Spring 生态核心。如果我们需要集成使用很多其他框架(MyBatis、JPA等)都是通过这些扩展点去实现的。
修改 BeanDefinition 的扩展点
可以修改 BeanDefinition,只需要实现 BeanFactoryPostProcessor 接口,重写方法
- postProcessBeanFactory BeanDefinition 的后置处理器,可修改Bean定义
添加 BeanDefinition 的扩展点
可以添加 BeanDefinition ,只需要实现 BeanDefinitionRegistryPostProcessor 重写方法
- postProcessBeanDefinitionRegistry 注册 BeanDefinition,实现后会多注册 Bean
2.然后通过 BeanFactory 构造 Bean 存入一个 Map 中
通过 BeanFactory (简单工厂模式) 调用 getBean() 将 BeanDefinition 进行一些列的操作 (实例化、填充属性、初始化) 后将 Bean put 到一个 Map 中 (单例池缓存)。
- a.实例化 (Instantiation)
- 实例化后,此时还未自动装配,未生成 Bean 实体
- b.填充属性 (Populate)
- 在填充属性的过程中,可能会存在 A 引用了 B,B 又引用了 A 的情况,就可能产生循环依赖,Spring 为了解决这个问题引入了三级缓存 (三个 Map)
- c.初始化 (Initialization)
- 初始化的过程中还会调用一堆 Aware (初始化生命周期接口),最后将初始化好的 Bean put 到一个 Map<key value> 中去,这个Map是一个单例池缓存,实际就是一个 ConcurrentHashMap 保存起来,我们调用 getBean()就是从这个 Map 中拿
- key :bean的名称,Value bean实例
实例化的两种方式:
- 反射:Spring 自己控制 @Component 只会将类的 class 注入到 BeanDefinition 中
- 工厂方法:更灵活,可以自己去new @Bean,里面可以自由控制 Bean 实例,new,赋值等
@Bean
public Car car(){
return new Car();
}
复制代码
在调用 getBean() 过程中会涉及到九处后置处理器的调用,在创建时前后,实例化前后,填充属性前后,初始化前后等
① 实现 InstantiationAwareBeanPostProcessor
- postProcessBeforelnstantiation 直接返回 Bean 停止后面的流程
② 实现 SmartInstantiationAwareBeanPostProcessor
- determineCandidateConstructors 指定实例化构造函数
③ 实现 MergedBeanDefinitionPostProcessor
- postProcessMergedBeanDefinition @AutoWired @Value 预解析
④ 实现 SmartInstantiationAwareBeanPostProcessor
- getEarlyBeanReference 解决循环引用 AOP
⑤ 实现 InstantiationAwareBeanPostProcessor
- postProcessAfterInstantiation 终止赋值
⑥ 实现 InstantiationAwareBeanPostProcessor
- postProcessPropertyValues 注入属性PropertyValues @AutoWired 在这里进行依赖注入
⑦⑧ 实现 BeanPostProcess
- postProcessBeforeInitialization 初始化前调用@PostConstruct
- postProcessAfterInitialization 初始化后 AOP: 创建代理
⑨ DestructionAwareBeanPostProcessor
- requiresDestruction
思考题:
1.描述 BeanFactory
2.BeanFactory 和 ApplicationContext 的区别?
3.简述 SpringIOC 的加载过程
4.简述 Bean 的生命周期
5.Spring 中有哪些扩展接口及调用时机
6.控制反转和依赖注入是什么
作者:Hz488
链接:https://juejin.cn/post/7127999778390016030