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); 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); app.registerBean("tom" , Cat.class, 0 ); app.registerBean("tom" , Cat.class, 1 ); app.registerBean("tom" , Cat.class, 2 ); System.out.println(app.getBean("tom" )); } }
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); } } }
(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) { 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); } } }
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 { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Cat.class).getBeanDefinition(); 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) 做的笔记
基本使用
实现FactoryBean接口,将自定义的FactoryBean注入容器
从容器中获取
1 2 3 4 5 6 7 8 9 10 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 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 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" ); System.out.println(userService); System.out.println(customFactoryBean); } }
注意:
自定义的FactoryBean会向容器中注入两个Bean,一个是自身,一个是getObject()方法的返回值;
要想获取自定义的FactoryBean本身,需要在BeanName前面加上&
符号,applicationContext.getBean("&customFactoryBean")
源码
refresh()
创建容器的finishBeanFactoryInitialization
步骤,会调用到beanFactory.preInstantiateSingletons()
来实例化单例Bean
preInstantiateSingletons()
如果是FactoryBean的实例,就通过getBean()
来获取或创建Bean,且在BeanName前加了个&
之后会判断isEagerInit
来创建getObject()
里的Bean
getObjectForBeanInstance()
在getBean()
中会调用doGetBean()
,然后调用getObjectForBeanInstance()
在这个方法中,会判断Bean是否是FactoryBean
的实例,如果是则判断缓存中是否有对应的实例,如果没有才会调用getObjectFromFactoryBean()
getObjectFromFactoryBean()
通过doGetObjectFromFactoryBean()
来调用FactoryBean的getObject()
方法,并且判断是否为单例来决定要不要放到缓存中
doGetObjectFromFactoryBean()
最终在这个方法中调用了getObject()
总结
通过判断FactoryBean
、getObject()里返回的对象
是否被创建过,只有都没有创建过才会最终创建Bean,保证单例性
在Mybatis里的使用场景 Spring中使用Mybatis
mybatis中,通过sqlSessionFactoryBean
来创建sqlSessionFactory
MapperScannerRegistrar
会将Mapper文件扫描出来生成BeanDefinition
,然后会生成一个MapperFactoryBean
,通过MapperFactoryBean
最终会生成一个Mapper文件对应的代理对象
SpringBoot整合Mybatis
在SpringBoot中Mybatis的自动装配由MybatisAutoConfiguration
这个类完成,里面调用了SqlSessionFactoryBean
的getObject()方法来注入了sqlSessionFactory