BeanFactory后处理器的作用
先来看看没有BeanFactory后处理器时有哪些常见的注解不能解析
public class A05Application {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// 初始化容器
context.refresh();
// 打印容器中所有的bean
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
context.close();
}
}
@Configuration
@ComponentScan("com.shuttle.spring5")
public class Config {
@Bean
public Bean01 bean01() {
return new Bean01();
}
// SqlSessionFactoryBean
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
// Druid连接池
@Bean(initMethod = "init")
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
}
public class Bean01 {
public Bean01() {
}
}
执行结果如下:
config?
可以看到就只有手工注册的config对象,标注了@ComponentScan、@Bean注解等都没有生效。现在我们给启动类中加入BeanFactory后处理器。
context.registerBean(ConfigurationClassPostProcessor.class);
再次执行结果如下:
config org.springframework.context.annotation.ConfigurationClassPostProcessor bean01 sqlSessionFactoryBean dataSource?
可以发现注解已经生效。
ConfigurationClassPostProcessor后处理器其实并不只能解析以上注解,还包括@Import、@ImportResource注解等等,有兴趣的大佬可以课后了解下。
再来看看实现@MapperScan注解的后处理器 - MapperScannerConfigurer
创建两个Mapper接口并给类上添加@Mapper注解
@Mapper
public interface Mapper01 {
}
@Mapper
public interface Mapper02 {
}
在不添加额外后处理器的情况下执行结果并没有看到有接口bean对象,也就是说明@Mapper注解需要额外的后处理器来解析,现在我们给容器中添加?MapperScannerConfigurer 后处理器并按照如下配置【basePackage:扫描的包范围】
context.registerBean(MapperScannerConfigurer.class, beanDefinition -> {
beanDefinition.getPropertyValues().add("basePackage", "com.shuttle.spring5");
});
可以发现已经有了mapper接口bean对象的出现
模拟实现常见后处理器
模拟实现解析Component注解
@Component
public class Bean01 {
public Bean01() {
}
}
@Component
public class Bean02 {
public Bean02() {
}
}
@Controller
public class Bean03 {
public Bean03() {
}
}
现在来模拟下解析@Component注解及其派生注解,之后将其注册进容器中。
// 利用Spring提供的工具方法找到指定类上有没有标注指定注解
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
// 若不为空则进行解析
if (componentScan != null) {
// 获取扫描包范围
for (String path : componentScan.basePackages()) {
// 将解析出来的路径转换为可以获取的资源路径
// path -> com.shuttle.spring5 -> classpath*:com/shuttle/spring5/**/*.class
String classPath = "classpath*:" + path.replace(".", "/") + "/**/*.class";
// 帮助获取类信息
CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
// 帮助生成beanName
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
// 获取资源
Resource[] resources = context.getResources(classPath);
for (Resource resource : resources) {
// 获取类元信息
MetadataReader reader = readerFactory.getMetadataReader(resource);
// 检查是否含有Component注解【只会解析Component,不会解析Component派生注解,例如Controller】
boolean hasComponent = reader.getAnnotationMetadata().hasAnnotation(Component.class.getName());
// 解析只会解析Component派生注解,例如Controller
boolean hasMetaComponent = reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName());
// 如果包含Component或者其派生注解
if (hasComponent || hasMetaComponent) {
// 根据类元信息生成BeanDefinition信息
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
// 获取BeanFactory
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
// 生成beanName
String name = generator.generateBeanName(beanDefinition, beanFactory);
// 注册beanDefinition
beanFactory.registerBeanDefinition(name, beanDefinition);
}
}
}
}
将?context.registerBean(ConfigurationClassPostProcessor.class); 注释后打印结果如下:
可以看到手动写的方法生效了,但现在离成功还有一步之遥,那就是将其抽取成自定义的BeanFactory后处理器。
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
// 利用Spring提供的工具方法找到指定类上有没有标注指定注解
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
// 若不为空则进行解析
if (componentScan != null) {
// 获取扫描包范围
for (String path : componentScan.basePackages()) {
// 将解析出来的路径转换为可以获取的资源路径
// path -> com.shuttle.spring5 -> classpath*:com/shuttle/spring5/**/*.class
String classPath = "classpath*:" + path.replace(".", "/") + "/**/*.class";
// 帮助获取类信息
CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
// 帮助生成beanName
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
// 获取资源
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(classPath);
for (Resource resource : resources) {
// 获取类元信息
MetadataReader reader = readerFactory.getMetadataReader(resource);
// 检查是否含有Component注解【只会解析Component,不会解析Component派生注解,例如Controller】
boolean hasComponent = reader.getAnnotationMetadata().hasAnnotation(Component.class.getName());
// 解析只会解析Component派生注解,例如Controller
boolean hasMetaComponent = reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName());
// 如果包含Component或者其派生注解
if (hasComponent || hasMetaComponent) {
// 根据类元信息生成BeanDefinition信息
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
// 生成beanName
String name = generator.generateBeanName(beanDefinition, beanFactory);
// 注册beanDefinition
beanFactory.registerBeanDefinition(name, beanDefinition);
}
}
}
}
} catch (Exception e) {
System.out.println("error...");
}
}
}
将上述后处理器注册进容器中
context.registerBean(ComponentScanPostProcessor.class);
?达到了和原来一样的效果~?
模拟实现解析@Bean注解
public class MyBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@SneakyThrows
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@SneakyThrows
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
// 读取Config类元数据信息
CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
MetadataReader reader = readerFactory.getMetadataReader(new ClassPathResource("com/shuttle/spring5/Config.class"));
// 找到标有@Bean注解的方法
Set<MethodMetadata> methodMetadata = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata method : methodMetadata) {
// 获取Bean属性 initMethod
String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(method.getMethodName(), "config");
// 对于构造方法的参数以及工厂方法的参数自动注入模式统一设置
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
// 如果initMethod不为空则设置
if (initMethod.length() > 0) {
builder.setInitMethodName(initMethod);
}
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
// 注册bean
beanFactory.registerBeanDefinition(method.getMethodName(), beanDefinition);
}
}
}
模拟解析Mapper接口?
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@SneakyThrows
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
// 查找路径资源
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:com/shuttle/spring5/mapper/**/*.class");
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
for (Resource resource : resources) {
// 获取类元信息
MetadataReader reader = readerFactory.getMetadataReader(resource);
ClassMetadata classMetadata = reader.getClassMetadata();
// 若类为接口
if (classMetadata.isInterface()) {
// 构建beanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
// 添加构造器参数值
.addConstructorArgValue(classMetadata.getClassName())
// 设置自动注入模式,按照类型注入
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
// 为了生成bean的名称,默认为mapperFactoryBean,会造成覆盖
AbstractBeanDefinition beanDefinitionForName = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
String name = generator.generateBeanName(beanDefinitionForName, beanFactory);
beanFactory.registerBeanDefinition(name, beanDefinition);
}
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
将上述后处理器全部注册到容器中可以达到和Spring一样的效果。?
context.registerBean(ComponentScanPostProcessor.class);
context.registerBean(MyBeanPostProcessor.class);
context.registerBean(MapperPostProcessor.class);
config com.shuttle.spring5.processor.ComponentScanPostProcessor com.shuttle.spring5.processor.MyBeanPostProcessor com.shuttle.spring5.processor.MapperPostProcessor bean01 bean02 bean03 sqlSessionFactoryBean dataSource mapper01 mapper02
到这里就算是大功告成了~
?
?
写在最后
学到这里我真的想给满老师点个大大的赞,不同于其它Spring源码课程,满老师采用的是模拟实现的方式一步步引领我们体会后处理器的作用,对新手小白也十分友好,有兴趣的朋友可以课后手动实现下对于其它常见注解的解析。
下次见咯~
|