前置文章:
一、Spring原理分析-BeanFactory与ApplicationContext
二、Spring原理分析-Bean生命周期
三、Spring原理分析-Bean后处理器
零、本文纲要
- 一、基础准备
- 二、ConfigurationClassPostProcessor后处理器
- 三、MapperScannerConfigurer后处理器
- 四、深入ConfigurationClassPostProcessor后处理器-解析@ComponentScan的实现
- 五、深入ConfigurationClassPostProcessor后处理器-解析@Bean的实现
- 六、深入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();
}
}
基础准备测试.png
可以发现,仅我们手动注册的bean被添加到Spring容器了。
二、ConfigurationClassPostProcessor后处理器
1、作用
解析@ComponentScan、@Bean、@Import、@ImportResource 注解。
2、添加ConfigurationClassPostProcessor后处理器
// 解析@ComponentScan、@Bean、@Import、@ImportResource 注解
context.registerBean(ConfigurationClassPostProcessor.class);
ConfigurationClassPostProcessor后处理器.png
三、MapperScannerConfigurer后处理器
1、作用
类似于@MapperScanner注解,将@Mapper注解指定的Bean放入Spring容器。
2、添加MapperScannerConfigurer后处理器
// @MapperScanner
context.registerBean(MapperScannerConfigurer.class, bd -> {
bd.getPropertyValues().add("basePackage", "com/stone/demo05/mapper");
});
MapperScannerConfigurer后处理器.png
使用MapperScannerConfigurer后,同时我们也看到其他后处理器也被添加进来了,如上日志后面的Processor们。
四、深入ConfigurationClassPostProcessor后处理器-解析@ComponentScan的实现
1、获取类上的@ComponentScan注解及其属性
这里使用AnnotationUtils工具类来查找指定类上的@ComponentScan注解,并从注解中取出basePackages属性内容,如下:
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null){
for (String basePackage : componentScan.basePackages()) {
System.out.println(basePackage);
}
}
获取类上的@ComponentScan注解中的属性.png
2、获取指定路径下的资源
注意:此处内容跟我们Spring原理分析-BeanFactory与ApplicationContext中:一、容器接口 → 3、ApplicationContext接口 → ② 实现ResourcePatternResolver接口 的内容串联。
在资源路径下添加一个没有@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";
}
扫描指定路径下的资源文件.png
3、获取资源内部元信息
通过CachingMetadataReaderFactory类获取MetadataReader对象,来获取元信息数据,如下:
// 用户获取元信息
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("-------------------------");
}
获取元信息数据.png
由上可以知道hasAnnotation方法可以帮助我们判断是否存在指定注解,下面我们演示判断派生注解的hasMetaAnnotation方法,如下:
System.out.println("HAS MATE @Component ? " +
metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));
给Bean3添加@Controller注解或者@Configuration注解,测试如下:
判断派生注解的hasMetaAnnotation方法.png4、将包含指定元信息的资源转换成BeanDefinition并注册
获取BeanName生成器,如下:
// 设置 BeanName 的 generator
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
通过采用如上的元信息获取形式,进行判断,然后注册,如下:
if (metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())
|| metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())){
// 将带有指定 @Component 注解及其派生注解的资源转换成 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);
}
将包含指定元信息的资源转换成BeanDefinition并注册.png
可以看到,此时我们带有@Component注解的Bean2已经被Spring管理了。
5、封装自定义解析@ComponentScan注解BeanFactory后处理器
实现BeanFactoryPostProcessor接口,重写postProcessBeanFactory方法,该方法在 context.refresh() 时被调用,如下:
public class ComponentScanBothMetaPostProcessor implements BeanFactoryPostProcessor {
@Override // 该方法在 context.refresh() 时被调用
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 注解及其派生注解的资源转换成 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();
}
}
}
测试类中注册该后处理器,测试:
封装自定义的BeanFactory后处理器测试.png至此我们就大致清楚整个解析@Component注解实现的过程:
获取资源路径 → 通过路径获取资源 → 读取资源元信息 → 判断符合指定元信息需求的的资源 → 注册筛选出的资源
五、深入ConfigurationClassPostProcessor后处理器-解析@Bean的实现
1、读取指定路径资源元信息
// 读取指定资源的元信息数据
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
遍历方法集合并注册BeanDefinition报错.png因为sqlSessionFactoryBean需要注入dataSource,而默认是关闭自动注入的,我们需要配置开启,如下:
// 遍历被注解的方法
for (MethodMetadata annotatedMethod : annotatedMethods) {
String methodName = annotatedMethod.getMethodName();
// 设置工厂方法,创建 BeanDefinition 并注册
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(methodName, "config");
// 开启 工厂方法 和 构造方法 的自动装配
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);
}
获取@Bean注解中的属性值.png
5、封装自定义解析@Bean注解BeanFactory后处理器
public class AtBeanPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
// 读取指定资源的元信息数据
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();
// 设置工厂方法,创建 BeanDefinition 并注册
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(methodName, "config");
// 开启 工厂方法 和 构造方法 的自动装配
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;
}
原生添加Mapper接口方法.png
2、自定义批量扫描添加Mapper接口后处理器
这次我们实现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的内容是MapperFactoryBean。而每个Bean需要有不同的BeanName,不然会先后覆盖。所以此处两次获取BeanDefinition,第二次是为了通过它获取Mapper的自己的BeanName命名。最后再将beanName与bd1注册到Spring。
七、结尾
以上即为BeanFactory后处理器的全部内容,感谢阅读。
网友评论