一、简单认识BeanFactory后处理器
1.1 BeanFactory后处理器的作用
接前文:Spring原理学习(一):BeanFactory和ApplicationContext的原理和实现 我们已经简单介绍了 BeanFactory后处理器 的作用,今天我们先再来再次体验一下。
先准备一个主启动类:
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// GenericApplicationContext 是一个【干净】的容器,没有spring添加的各种后处理器
GenericApplicationContext context = new GenericApplicationContext();
//注册config
context.registerBean("config", Config.class);
// 初始化容器
context.refresh();
// 看看容器里被注册的bean有哪些
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
}
然后准备一个bean,上面加了@Configuration和 @Bean 注解;并且加上了@ComponentScan 注解,应该能扫描到加有@Component注解的Bean2。
@Configuration
@ComponentScan("com.itheima.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("root");
return dataSource;
}
}
@Component
public class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("我被 Spring 管理啦");
}
}
所以config容器里应该有5个bean:config本身、扫描到的bean2、自己管理的三个(bean2、sqlSessionFactory、druidDataSource)。
但是运行之后我们发现:
只有我们自己添加的一个bean。 这说明执勤啊提到的注解都没有被解析。而解析这些注解的功能就是beanFactory后处理器提供的。
1.2 ConfigurationClassPostProcessor 后处理器
我们加上ConfigurationClassPostProcessor 后处理器就可以解析这些注解了。
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
//解析@ComponentScan @Bean @Import @ImportResource
context.registerBean(ConfigurationClassPostProcessor.class);
// 初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
运行结果:
1.3 MapperScannerConfigurer 后处理器
再加上ConfigurationClassPostProcessor 后处理器就可以解析这些注解了
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
//解析@ComponentScan @Bean @Import @ImportResource
context.registerBean(ConfigurationClassPostProcessor.class);
//解析@MapperScanner
context.registerBean(MapperScannerConfigurer.class, bd -> {
bd.getPropertyValues().add("basePackage", "com.itheima.a05.mapper");
});
// 初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
Mapper1 和 Mapper2加上了@Mapper注解,Mapper3没有
@Mapper
public interface Mapper1 {
}
@Mapper
public interface Mapper2 {
}
public class Mapper3 {
}
运行结果:
二、模拟实现后处理器——解析@ComponentScan
首先要明确执行的步骤:
- 先找到加有@Component注解的是哪些包,并拿到包下的资源
- 找到包下有哪些bean加上了@Component注解或派生注解
- 生成这些资源对应的beanDefinition和beanName
- 注册进beanFactory
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override // context.refresh
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
//查找哪个类上有该注解
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null) {
for (String p : componentScan.basePackages()) {
//打印出来之后是com.itheima.a05.component这种格式的
System.out.println(p);
//我们要做格式转换 com.itheima.a05.component -> classpath*:com/itheima/a05/component/**/*.class
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
//打印出来之后是classpath*:com/itheima/a05/component/**/*.class
System.out.println(path);
//CachingMetadataReaderFactory用于读取类的源信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//getResources用于获取类路径下的所有资源
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (Resource resource : resources) {
// 读取类的源信息
MetadataReader reader = factory.getMetadataReader(resource);
// 读取类上的注解信息
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
//是否加了 @Component 注解
boolean hasAnnotation = annotationMetadata.hasAnnotation(Component.class.getName());
//是否加了 @Component 派生注解,比如@Controller
boolean hasMetaAnnotation = annotationMetadata.hasMetaAnnotation(Component.class.getName());
if (hasAnnotation || hasMetaAnnotation) {
AbstractBeanDefinition bd = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
//生成bean的名称
String name = generator.generateBeanName(bd, beanFactory);
//注册到bean工厂
beanFactory.registerBeanDefinition(name, bd);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
三、模拟实现后处理器——解析@Bean
public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//这里为了省事,写死了路径,后续优化可以考虑先找找有@Component注解的包有哪些,然后逐一读取
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itheima/a05/Config.class"));
Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata method : methods) {
System.out.println(method);
String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(method.getMethodName(), "config");
//指定自动装配模式
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
if (initMethod.length() > 0) {
builder.setInitMethodName(initMethod);
}
AbstractBeanDefinition bd = builder.getBeanDefinition();
beanFactory.registerBeanDefinition(method.getMethodName(), bd);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
四、模拟实现后处理器——MapperScannerConfigurer
需要注意的是,我们要注册进spring中的是MapperFactoryBean,而不是Mapper。
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
//扫描包下的资源
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:com/itheima/a05/mapper/**/*.class");
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
for (Resource resource : resources) {
MetadataReader reader = factory.getMetadataReader(resource);
ClassMetadata classMetadata = reader.getClassMetadata();
//判断是不是接口
if (classMetadata.isInterface()) {
//这里要注意,交给spring管理的类型是MapperFactoryBean,而不是Mapper
AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
.addConstructorArgValue(classMetadata.getClassName())
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
//bd2仅仅是为了生成名字,spring内部也是这么做的
AbstractBeanDefinition bd2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
String name = generator.generateBeanName(bd2, beanFactory);
beanFactory.registerBeanDefinition(name, bd);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}