6.Spring5底层原理之BeanFactory后处理器,模拟@ComponentScan
常见的BeanFactory后处理器
为了演示常见的BeanFactory后处理器,我们来创建一些类。
@Configuration
@ComponentScan("com.zhaojun.springsource.a05.component")
public class Config {
@Bean
public Bean1 bean1(){
return new Bean1();
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean(initMethod = "init")
public DruidDataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123");
return dataSource;
}
}
首先是Config类,该类用@Configuration注解声明是一个配置类,并且用@ComponentScan指定了包扫描的路径。然后在该类中注册了三个bean,分别是bean1、sqlSessionFactoryBean、dataSource。
这里需要引入Druid包和mysql数据库驱动相关的依赖。
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.22</version> </dependency>
然后是Bean1
public class Bean1 {
}
component包下有三个类分别是:
@Component()
public class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2(){
log.debug("被Spring管理,bean2");
}
}
@Controller
public class Bean3 {
private static final Logger log = LoggerFactory.getLogger(Bean3.class);
public Bean3(){
log.debug("被Spring管理,bean3");
}
}
public class Bean4 {
private static final Logger log = LoggerFactory.getLogger(Bean4.class);
public Bean4(){
log.debug("被Spring管理,bean4");
}
}
我们写个main方法测试一下
public class A05Application {
public static final Logger log = LoggerFactory.getLogger(A05Application.class);
public static void main(String[] args) throws IOException {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.refresh();
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
context.close();
}
}
GenericApplicationContext在上一节中我们已经讲过了,没有包含后处理器的,运行main方法,输出日志如下
15:35:24.224 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@42607a4f
15:35:24.262 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
config
15:35:24.322 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@42607a4f, started on Fri May 06 15:35:24 CST 2022
可以看到,只有Config类,被加载到BeanDefinitionNames中,其他的bean都没有被加载,说明@Bean没有生效。我们还记得之前讲的,解析@Bean会用到ConfigurationClassPostProcessor后处理器。我们把后处理器加上再试试。
public static void main(String[] args) throws IOException {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class); //@ComponentScan // @Configuration @Bean @Import @ImportResource
context.refresh();
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
context.close();
}
输出日志
15:47:19.522 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@42607a4f
15:47:19.551 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.ConfigurationClassPostProcessor'
15:47:19.696 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [D:\IDEAWorkSpace\spring-source\target\classes\com\zhaojun\springsource\a05\component\Bean2.class]
15:47:19.698 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [D:\IDEAWorkSpace\spring-source\target\classes\com\zhaojun\springsource\a05\component\Bean3.class]
15:47:19.825 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
15:47:19.826 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
15:47:19.826 [main] DEBUG com.zhaojun.springsource.a05.component.Bean2 - 被Spring管理,bean2
15:47:19.826 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
15:47:19.826 [main] DEBUG com.zhaojun.springsource.a05.component.Bean3 - 被Spring管理,bean3
15:47:19.826 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
15:47:19.839 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'sqlSessionFactoryBean'
15:47:19.841 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'dataSource'
15:47:20.053 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
15:47:20.053 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'sqlSessionFactoryBean' via factory method to bean named 'dataSource'
15:47:20.064 [main] DEBUG org.apache.ibatis.logging.LogFactory - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
15:47:20.071 [main] DEBUG org.mybatis.spring.SqlSessionFactoryBean - Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration
15:47:20.151 [main] DEBUG org.mybatis.spring.SqlSessionFactoryBean - Property 'mapperLocations' was not specified.
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
bean2
bean3
bean1
sqlSessionFactoryBean
dataSource
15:47:20.170 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@42607a4f, started on Fri May 06 15:47:19 CST 2022
15:47:20.171 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} closing ...
15:47:20.172 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} closed
可以看到,加了后处理器之后,@Bean生效,@ComponentScan也生效了。
模拟解析@ComponentScan
我们自己模拟解析@ComponentScan注解,了解下@ComponentScan的原理。
public class A05Application {
public static final Logger log = LoggerFactory.getLogger(A05Application.class);
public static void main(String[] args) throws IOException {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
//自己实现 解析@Componment注解
// 检验Config类,是否有ComponentScan注解
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
DefaultListableBeanFactory defaultListableBeanFactory = context.getDefaultListableBeanFactory();
if (componentScan != null) {
// 获取ComponentScan配置的basePackages
for (String basePackage : componentScan.basePackages()) {
CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
// 将包路径,转换为Spring资源路径格式
String path = "classpath*:" + basePackage.replace(".", "/") + "/**/*.class";
Resource[] resources = context.getResources(path);
for (Resource resource : resources) {
// 获取类的元信息,包含类名,注解等信息
MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// System.out.print(metadataReader.getClassMetadata().getClassName() + ":");
// 校验是否有@Component注解
// System.out.print(metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName()) + ",");
// 校验是否有Component的派生注解 比如@Controller注解
// System.out.print(metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())
) {
// 构建BeanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(metadataReader.getClassMetadata().getClassName()).getBeanDefinition();
// 创建beanName
String beanName = beanNameGenerator.generateBeanName(beanDefinition, defaultListableBeanFactory);
defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
context.refresh();
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
context.close();
}
}
输出日志
16:06:15.788 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@1990a65e
16:06:15.823 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
16:06:15.837 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
16:06:15.837 [main] DEBUG com.zhaojun.springsource.a05.component.Bean2 - 被Spring管理,bean2
16:06:15.837 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
16:06:15.838 [main] DEBUG com.zhaojun.springsource.a05.component.Bean3 - 被Spring管理,bean3
config
bean2
bean3
16:06:15.862 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@1990a65e, started on Fri May 06 16:06:15 CST 2022
流程:
- 首先,我们需要判断Config类是否被@Componment标注。
- 如果有,然后获取要扫描的包路径,将配置的包路径,转换为Spring扫描资源的路径格式。
- 搜索路径下的所有类,判断哪些类被@Component或者@Component的派生注解标注。
- 为这些类生成beanDefinition,注册到BeanFactory中。
将模拟代码封装为自定义后处理器
我们将上面的代码封装为一个BeanFactory后处理器,需要实现BeanFactoryPostProcessor接口,将上面的代码放到postProcessBeanFactory()方法中,适当调整一下,postProcessBeanFactory()方法在context.refresh()会调用。
public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
// 自己实现 解析@Componment注解
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
if (componentScan != null) {
for (String basePackage : componentScan.basePackages()) {
CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
String path = "classpath*:" + basePackage.replace(".", "/") + "/**/*.class";
//由于没有context用 new PathMatchingResourcePatternResolver() 代替,功能一样
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
for (Resource resource : resources) {
MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())
) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(metadataReader.getClassMetadata().getClassName()).getBeanDefinition();
// 由于该方法中的参数类型是ConfigurableListableBeanFactory,为DefaultListableBeanFactory的父类
// 没有registerBeanDefinition方法,所以,需要判断是否是DefaultListableBeanFactory,然后转换类型
if (beanFactory instanceof DefaultListableBeanFactory){
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
String beanName = beanNameGenerator.generateBeanName(beanDefinition, defaultListableBeanFactory);
defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用
public static void main(String[] args) throws IOException {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// 将这个bean进行注册即可使用了
context.registerBean(ComponentScanPostProcessor.class);
context.refresh();
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
context.close();
}
来看看日志输出
16:51:57.113 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@42607a4f
16:51:57.159 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'com.zhaojun.springsource.a05.ComponentScanPostProcessor'
16:51:57.377 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
16:51:57.377 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
16:51:57.377 [main] DEBUG com.zhaojun.springsource.a05.component.Bean2 - 被Spring管理,bean2
16:51:57.378 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
16:51:57.378 [main] DEBUG com.zhaojun.springsource.a05.component.Bean3 - 被Spring管理,bean3
config
com.zhaojun.springsource.a05.ComponentScanPostProcessor
bean2
bean3
16:51:57.420 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@42607a4f, started on Fri May 06 16:51:57 CST 2022
总结
这一节我们了解了BeanFactory后处理器ConfigurationClassPostProcessor的作用,还通过自己模拟@ComponentScan的执行流程,自己封装了一个beanFactory后处理器。