bean的加载方式(2)

bean的加载方式(2)

5.容器初始化完成后手动加载bean

通过AnnotationConfigApplicationContext的register方法,可以在容器初始化完成后手动加载bean

1
2
3
4
5
6
7
public class App5 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig5.class);
//上下文容器对象已经初始化完毕后,手工加载bean
ctx.register(Cat.class);
}
}

也可以用registerBean方法指定bean的名称和参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class App5 {
public static void main(String[] args) {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig5.class);

//容器中有同名称的bean时会覆盖之前的
app.registerBean("tom", Cat.class, 0);
app.registerBean("tom", Cat.class, 1);
app.registerBean("tom", Cat.class, 2);

System.out.println(app.getBean("tom"));
/*
结果
Cat{id='2'}
*/
}
}

6.在容器初始化过程中加载bean

(1)ImportSelector接口

实现ImportSelector接口,selectImports方法的返回值会被注入到容器中

1
2
3
4
5
6
7
8
9
10
11
public class MySelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
boolean flag = importingClassMetadata.hasAnnotation("org.springframework.context.annotation.ComponentScan");
if (flag) {
return new String[]{"com.xw.bean.Dog"};
} else {
return new String[0];
}
}
}

导入Selector配置类

1
2
3
4
@ComponentScan("com.xw.bean")
@Import(MySelector.class)
public class SpringConfig6 {
}

获取bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class App6 {
public static void main(String[] args) {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig6.class);

String[] names = app.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
/*
结果
com.xw.bean.Dog
*/
}
}
(2)ImportBeanDefinitionRegistrar接口

bean的加载不是一个简简单单的对象,spring中定义了一个叫做BeanDefinition的东西,它才是控制bean初始化加载的核心

ImportBeanDefinitionRegistrar相比于ImportSelector,对注入的bean有更大的操作空间

实现ImportBeanDefinitionRegistrar接口

1
2
3
4
5
6
7
8
9
public class MyRegister implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//获取bean的beanDefinition
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Dog.class).getBeanDefinition();
//注入到容器中
registry.registerBeanDefinition("dog", beanDefinition);
}
}

导入Registrar配置类

1
2
3
4
5
@ComponentScan("com.xw.bean")
@Import(MyRegister.class)
public class SpringConfig6 {

}

获取bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class App6 {
public static void main(String[] args) {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig6.class);

String[] names = app.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
/*
结果
dog
*/
}
}

7.对容器中bean的最终裁定

postProcessor为最后执行的对容器中bean的注册

实现BeanDefinitionRegistryPostProcessor接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//获取bean的beanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Cat.class).getBeanDefinition();
//将之前的dog删掉换成cat
registry.removeBeanDefinition("dog");
registry.registerBeanDefinition("cat", beanDefinition);
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

}
}

导入PostProcessor配置类

1
2
3
4
@ComponentScan("com.xw.bean")
@Import({MyRegister.class, MyPostProcessor.class})
public class SpringConfig6 {
}

获取bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class App6 {
public static void main(String[] args) {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig6.class);

String[] names = app.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
/*
结果
cat
*/
}
}

FactoryBean

BeanFactory的作用是完成了Bean的初始化、自动装配、存储单例Bean等功能

FactoryBean本身是一个Bean,的作用是会将自身和getObject()方法的返回值作为Bean注入到容器中

读原文FactoryBean——Spring的扩展点之一 - 掘金 (juejin.cn)做的笔记

基本使用

  1. 实现FactoryBean接口,将自定义的FactoryBean注入容器
  2. 从容器中获取
1
2
3
4
5
6
7
8
9
10
// service类,将通过FactoryBean注入容器

package com.xw.service;

public class UserService {

public UserService() {
System.out.println("userService construct");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 自定义的FactoryBean

package com.xw.component;

@Component
public class CustomFactoryBean implements FactoryBean<UserService> {

@Override
public UserService getObject() throws Exception {
return new UserService();
}

@Override
public Class<?> getObjectType() {
return UserService.class;
}
}

1
2
3
4
5
6
7
8
9
// 配置类,用于扫描组件

package com.xw.config;

@Configuration
@ComponentScan("com.xw.component")
public class AppConfig {

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 容器,获取Bean

package com.xw;

public class MainApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println("容器初始化完成");
UserService userService = applicationContext.getBean(UserService.class);
Object customFactoryBean = applicationContext.getBean("customFactoryBean");

// 通过Class类和BeanName获取的Bean竟然是同一个Bean
System.out.println(userService);
System.out.println(customFactoryBean);
}
}

/*
容器初始化完成
userService construct
com.xw.service.UserService@59d016c9
com.xw.service.UserService@59d016c9
*/

注意:

  1. 自定义的FactoryBean会向容器中注入两个Bean,一个是自身,一个是getObject()方法的返回值;

  2. 要想获取自定义的FactoryBean本身,需要在BeanName前面加上&符号,applicationContext.getBean("&customFactoryBean")

源码

  1. refresh()

    创建容器的finishBeanFactoryInitialization步骤,会调用到beanFactory.preInstantiateSingletons()来实例化单例Bean

  2. preInstantiateSingletons()

    如果是FactoryBean的实例,就通过getBean()来获取或创建Bean,且在BeanName前加了个&

    之后会判断isEagerInit来创建getObject()里的Bean

  3. getObjectForBeanInstance()

    getBean()中会调用doGetBean(),然后调用getObjectForBeanInstance()

    在这个方法中,会判断Bean是否是FactoryBean的实例,如果是则判断缓存中是否有对应的实例,如果没有才会调用getObjectFromFactoryBean()

  4. getObjectFromFactoryBean()

    通过doGetObjectFromFactoryBean()来调用FactoryBean的getObject()方法,并且判断是否为单例来决定要不要放到缓存中

  5. doGetObjectFromFactoryBean()

    最终在这个方法中调用了getObject()

  6. 总结

    通过判断FactoryBeangetObject()里返回的对象是否被创建过,只有都没有创建过才会最终创建Bean,保证单例性

在Mybatis里的使用场景

Spring中使用Mybatis

  1. mybatis中,通过sqlSessionFactoryBean来创建sqlSessionFactory
  2. MapperScannerRegistrar会将Mapper文件扫描出来生成BeanDefinition,然后会生成一个MapperFactoryBean,通过MapperFactoryBean最终会生成一个Mapper文件对应的代理对象

SpringBoot整合Mybatis

  1. 在SpringBoot中Mybatis的自动装配由MybatisAutoConfiguration这个类完成,里面调用了SqlSessionFactoryBean的getObject()方法来注入了sqlSessionFactory

bean的加载方式(2)
http://xwww12.github.io/2022/07/23/spring/bean的加载方式(2)/
作者
xw
发布于
2022年7月23日
许可协议