前置文章:
一鹅龄、Spring原理分析-BeanFactory與ApplicationContext
二有巧、Spring原理分析-Bean生命周期
三、Spring原理分析-Bean后處理器
零政供、本文綱要
- 一、基礎準備
- 二、ConfigurationClassPostProcessor后處理器
- 三、MapperScannerConfigurer后處理器
- 四蜕提、深入ConfigurationClassPostProcessor后處理器-解析@ComponentScan的實現(xiàn)
- 五、深入ConfigurationClassPostProcessor后處理器-解析@Bean的實現(xiàn)
- 六靶端、深入Mapper接口掃描添加后處理器
一谎势、基礎準備
0、基礎依賴
<!--base-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis_plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
1躲查、待注入類
- ① com.stone.demo05.Bean1
public class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
public Bean1() {log.debug("Bean1被Spring管理啦!");}
}
- ② com.stone.demo05.component.Bean2
@Component
public class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2(){log.debug("Bean2被Spring容器管理了它浅!");}
}
- ③ com.stone.demo05.Config
@Configuration
@ComponentScan("com.stone.demo05.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.setUsername("root");
dataSource.setPassword("root");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
}
2译柏、測試類
public class Demo05 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.refresh();
for (String definitionName : context.getBeanDefinitionNames()) {
System.out.println(definitionName);
}
context.close();
}
}
可以發(fā)現(xiàn)镣煮,僅我們手動注冊的bean被添加到Spring容器了。
二鄙麦、ConfigurationClassPostProcessor后處理器
1典唇、作用
解析@ComponentScan镊折、@Bean、@Import介衔、@ImportResource 注解恨胚。
2、添加ConfigurationClassPostProcessor后處理器
// 解析@ComponentScan炎咖、@Bean赃泡、@Import、@ImportResource 注解
context.registerBean(ConfigurationClassPostProcessor.class);
三乘盼、MapperScannerConfigurer后處理器
1升熊、作用
類似于@MapperScanner注解,將@Mapper注解指定的Bean放入Spring容器绸栅。
2级野、添加MapperScannerConfigurer后處理器
// @MapperScanner
context.registerBean(MapperScannerConfigurer.class, bd -> {
bd.getPropertyValues().add("basePackage", "com/stone/demo05/mapper");
});
使用MapperScannerConfigurer后,同時我們也看到其他后處理器也被添加進來了粹胯,如上日志后面的Processor們蓖柔。
四、深入ConfigurationClassPostProcessor后處理器-解析@ComponentScan的實現(xiàn)
1风纠、獲取類上的@ComponentScan注解及其屬性
這里使用AnnotationUtils工具類來查找指定類上的@ComponentScan注解况鸣,并從注解中取出basePackages屬性內(nèi)容,如下:
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null){
for (String basePackage : componentScan.basePackages()) {
System.out.println(basePackage);
}
}
2议忽、獲取指定路徑下的資源
注意:此處內(nèi)容跟我們Spring原理分析-BeanFactory與ApplicationContext中:一懒闷、容器接口 → 3、ApplicationContext接口 → ② 實現(xiàn)ResourcePatternResolver接口 的內(nèi)容串聯(lián)栈幸。
在資源路徑下添加一個沒有@Component注解的類愤估,如下:
public class Bean3 {
private static final Logger log = LoggerFactory.getLogger(Bean3.class);
public Bean3(){log.debug("Bean2被Spring容器管理了!");}
}
從指定路徑下獲取資源速址,如下:
// com.stone.demo05.component → classpath*:com/stone/demo05/component/**/*.class
String path = PATH_PREFIX
+ basePackage.trim().replace(".", "/") + PATH_SUFFIX;
System.out.println(path);
Resource[] resources = context.getResources(path);
for (Resource resource : resources) {
System.out.println(resource);
}
常量類玩焰,如下:
public class DemoConstants {
public static final String PATH_PREFIX = "classpath*:";
public static final String PATH_SUFFIX = "/**/*.class";
}
3、獲取資源內(nèi)部元信息
通過CachingMetadataReaderFactory類獲取MetadataReader對象芍锚,來獲取元信息數(shù)據(jù)昔园,如下:
// 用戶獲取元信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
Resource[] resources = context.getResources(path);
for (Resource resource : resources) {
//System.out.println(resource);
// 加載元信息的 reader
MetadataReader metadataReader = factory.getMetadataReader(resource);
// 讀取 類名 和 注解 信息
System.out.println("CLASSNAME IS: " + metadataReader.getClassMetadata().getClassName());
System.out.println("HAS @Component ? " +
metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
System.out.println("-------------------------");
}
由上可以知道hasAnnotation方法可以幫助我們判斷是否存在指定注解,下面我們演示判斷派生注解的hasMetaAnnotation方法并炮,如下:
System.out.println("HAS MATE @Component ? " +
metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));
給Bean3添加@Controller注解或者@Configuration注解默刚,測試如下:
4、將包含指定元信息的資源轉(zhuǎn)換成BeanDefinition并注冊
獲取BeanName生成器逃魄,如下:
// 設置 BeanName 的 generator
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
通過采用如上的元信息獲取形式荤西,進行判斷,然后注冊,如下:
if (metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())
|| metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())){
// 將帶有指定 @Component 注解及其派生注解的資源轉(zhuǎn)換成 BeanDefinition
AbstractBeanDefinition bd = BeanDefinitionBuilder
.genericBeanDefinition(metadataReader.getClassMetadata().getClassName())
.getBeanDefinition();
// 通過 beanFactory 將 BeanDefinition 注冊到 Spring
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
String beanName = generator.generateBeanName(bd, beanFactory);
beanFactory.registerBeanDefinition(beanName, bd);
}
可以看到邪锌,此時我們帶有@Component注解的Bean2已經(jīng)被Spring管理了勉躺。
5、封裝自定義解析@ComponentScan注解BeanFactory后處理器
實現(xiàn)BeanFactoryPostProcessor接口觅丰,重寫postProcessBeanFactory方法饵溅,該方法在 context.refresh() 時被調(diào)用,如下:
public class ComponentScanBothMetaPostProcessor implements BeanFactoryPostProcessor {
@Override // 該方法在 context.refresh() 時被調(diào)用
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null){
for (String basePackage : componentScan.basePackages()) {
System.out.println(basePackage);
// com.stone.demo05.component → classpath*:com/stone/demo05/component/**/*.class
String path = PATH_PREFIX
+ basePackage.trim().replace(".", "/") + PATH_SUFFIX;
System.out.println(path);
// 用戶獲取元信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
// 設置 BeanName 的 generator
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (Resource resource : resources) {
// 加載元信息的 reader
MetadataReader metadataReader = factory.getMetadataReader(resource);
// 讀取元信息妇萄,判斷是否帶有 @Component 注解及其派生注解
if (metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())
|| metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())){
// 將帶有指定 @Component 注解及其派生注解的資源轉(zhuǎn)換成 BeanDefinition
AbstractBeanDefinition bd = BeanDefinitionBuilder
.genericBeanDefinition(metadataReader.getClassMetadata().getClassName())
.getBeanDefinition();
// 通過 DefaultListableBeanFactory 將 BeanDefinition 注冊到 Spring
if (beanFactory instanceof DefaultListableBeanFactory defaultListableBeanFactory){
String beanName = generator.generateBeanName(bd, defaultListableBeanFactory);
defaultListableBeanFactory.registerBeanDefinition(beanName, bd);
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
測試類中注冊該后處理器蜕企,測試:
至此我們就大致清楚整個解析@Component注解實現(xiàn)的過程:
獲取資源路徑 → 通過路徑獲取資源 → 讀取資源元信息 → 判斷符合指定元信息需求的的資源 → 注冊篩選出的資源
五、深入ConfigurationClassPostProcessor后處理器-解析@Bean的實現(xiàn)
1冠句、讀取指定路徑資源元信息
// 讀取指定資源的元信息數(shù)據(jù)
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
MetadataReader metadataReader
= factory.getMetadataReader(new ClassPathResource("com/stone/demo05/Config.class"));
2糖赔、獲取被@Bean注解標注的方法集合
// 獲取被 @Bean 注解標注的方法集合
Set<MethodMetadata> annotatedMethods
= metadataReader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
3、遍歷方法集合并注冊BeanDefinition
因為sqlSessionFactoryBean需要注入dataSource轩端,而默認是關閉自動注入的放典,我們需要配置開啟,如下:
// 遍歷被注解的方法
for (MethodMetadata annotatedMethod : annotatedMethods) {
String methodName = annotatedMethod.getMethodName();
// 設置工廠方法基茵,創(chuàng)建 BeanDefinition 并注冊
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(methodName, "config");
// 開啟 工廠方法 和 構(gòu)造方法 的自動裝配
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
AbstractBeanDefinition bd = builder.getBeanDefinition();
context.getDefaultListableBeanFactory().registerBeanDefinition(methodName, bd);
}
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
通過該方法奋构,開啟工廠方法的自動裝配。
4拱层、獲取@Bean注解中的屬性值
// 獲取 @Bean 注解中 initMethod 屬性值
String initMethod
= annotatedMethod.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
if (initMethod.length() > 0){
// 如果包含 initMethod 則設置
builder.setInitMethodName(initMethod);
}
5弥臼、封裝自定義解析@Bean注解BeanFactory后處理器
public class AtBeanPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
// 讀取指定資源的元信息數(shù)據(jù)
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
MetadataReader metadataReader
= factory.getMetadataReader(new ClassPathResource("com/stone/demo05/Config.class"));
// 獲取被 @Bean 注解標注的方法集合
Set<MethodMetadata> annotatedMethods
= metadataReader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
// 遍歷被注解的方法
for (MethodMetadata annotatedMethod : annotatedMethods) {
String methodName = annotatedMethod.getMethodName();
// 獲取 @Bean 注解中 initMethod 屬性值
String initMethod
= annotatedMethod.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
// 設置工廠方法,創(chuàng)建 BeanDefinition 并注冊
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(methodName, "config");
// 開啟 工廠方法 和 構(gòu)造方法 的自動裝配
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
if (initMethod.length() > 0){
// 如果包含 initMethod 則設置
builder.setInitMethodName(initMethod);
}
AbstractBeanDefinition bd = builder.getBeanDefinition();
if (beanFactory instanceof DefaultListableBeanFactory defaultListableBeanFactory){
defaultListableBeanFactory.registerBeanDefinition(methodName, bd);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
六根灯、深入Mapper接口掃描添加后處理器
1径缅、原生添加Mapper接口方法
@Bean
public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory){
MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}
2、自定義批量掃描添加Mapper接口后處理器
這次我們實現(xiàn)BeanDefinitionRegistryPostProcessor接口烙肺,它是BeanFactoryPostProcessor的子接口纳猪。
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
// 找到指定類的 @MapperScan 注解
MapperScan mapperScan = AnnotationUtils.findAnnotation(Config.class, MapperScan.class);
if (mapperScan != null){
String[] basePackages = mapperScan.basePackages();
for (String basePackage : basePackages) {
// 通過資源絕對路徑,解析資源
String path = PATH_PREFIX
+ basePackage.trim().replace(".", "/") + PATH_SUFFIX;
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(path);
// 通過 CachingMetadataReaderFactory 對象桃笙,從資源 resources 中獲取元信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (Resource resource : resources) {
// 獲取 resource 資源的元信息 MetadataReader
MetadataReader reader = factory.getMetadataReader(resource);
// 獲取類元信息 classMetadata氏堤,生成并注冊 BeanDefinition
ClassMetadata classMetadata = reader.getClassMetadata();
if (classMetadata.isInterface()) {
// MapperFactoryBean<?> 的 BeanDefinition
AbstractBeanDefinition bd1 = BeanDefinitionBuilder
.genericBeanDefinition(MapperFactoryBean.class)
.addConstructorArgValue(classMetadata.getClassName())
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
// Mapper 接口的 BeanDefinition
AbstractBeanDefinition bd2 = BeanDefinitionBuilder
.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
// 對應 Mapper 來生成 beanName
String beanName = generator.generateBeanName(bd2, beanFactory);
// 對應 MapperFactoryBean 來注冊 BeanDefinition
beanFactory.registerBeanDefinition(beanName, bd1);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
值得注意的地方,如下:
// MapperFactoryBean<?> 的 BeanDefinition
AbstractBeanDefinition bd1 = BeanDefinitionBuilder
.genericBeanDefinition(MapperFactoryBean.class)
.addConstructorArgValue(classMetadata.getClassName())
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
// Mapper 接口的 BeanDefinition
AbstractBeanDefinition bd2 = BeanDefinitionBuilder
.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
// 對應 Mapper 來生成 beanName
String beanName = generator.generateBeanName(bd2, beanFactory);
// 對應 MapperFactoryBean 來注冊 BeanDefinition
beanFactory.registerBeanDefinition(beanName, bd1);
由于Mapper是接口搏明,我們是通過MapperFactoryBean工廠Bean對象來獲取的鼠锈,所以其BeanDefinition的內(nèi)容是MapperFactoryBean。而每個Bean需要有不同的BeanName星著,不然會先后覆蓋购笆。所以此處兩次獲取BeanDefinition,第二次是為了通過它獲取Mapper的自己的BeanName命名虚循。最后再將beanName與bd1注冊到Spring同欠。
七为黎、結(jié)尾
以上即為BeanFactory后處理器的全部內(nèi)容,感謝閱讀行您。