1.BeanFactory 后处理器的作用
public class A05 {
public static void main(String[] args) {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
@Configuration
@ComponentScan("com.itheima.a05copy.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("138256");
return dataSource;
}
/*@Bean
public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}
@Bean
public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}*/
}
@Component
public class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("我被 Spring 管理啦");
}
}
config自己加config里配置的三个Bean和Bean2上加了 @Component共5个Bean,但运行只输出config
说明config的@ComponentScan("com.itheima.a05copy.component")没生效。
加上context.registerBean(ConfigurationClassPostProcessor.class);后
说明ConfigurationClassPostProcessor 可以解析 @ComponentScan @Bean @Import @ImportResource这些注解。
context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
bd.getPropertyValues().add("basePackage", "com.itheima.a05copy.mapper");
});
加上这行代码就可以解析@mapper接口了
其中mapper1mapper2均为空接口只有一个@Mapper注解。
2. 工厂处理器模拟实现
2.1 模拟解析 @ComponentScan
// context.registerBean(ConfigurationClassPostProcessor.class);// @ComponentScan @Bean @Import @ImportResource
// context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
// bd.getPropertyValues().add("basePackage", "com.itheima.a05copy.mapper");
// });
注释掉这三句模拟组建扫描
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan!=null){
for (String p : componentScan.basePackages()) {
System.out.println(p);
}
}
输出
com.itheima.a05copy.component
config
Process finished with exit code 0
扫描到了包名
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan!=null){
for (String p : componentScan.basePackages()) {
System.out.println(p);
//com.itheima.a05copy.component -> classpath*:com/itheima/a05copy/component/**/*.class
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
Resource[] resources = context.getResources(path);
for (Resource resource : resources) {
System.out.println(resource);
}
}
}
@Component
public class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("我被 Spring 管理啦");
}
}
@Component
public class Bean3 {
private static final Logger log = LoggerFactory.getLogger(Bean3.class);
public Bean3() {
log.debug("我被 Spring 管理啦");
}
}
public class Bean4 {
private static final Logger log = LoggerFactory.getLogger(Bean4.class);
public Bean4() {
log.debug("我被 Spring 管理啦");
}
}
Bean4未加@Component但也被扫描到了,因为由代码知是路径扫描。
if (componentScan!=null){
for (String p : componentScan.basePackages()) {
System.out.println(p);
//com.itheima.a05copy.component -> classpath*:com/itheima/a05copy/component/**/*.class
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
CachingMetadataReaderFactory factory=new CachingMetadataReaderFactory();
Resource[] resources = context.getResources(path);
for (Resource resource : resources) {
// System.out.println(resource);
MetadataReader reader = factory.getMetadataReader(resource);
System.out.println("类名:" + reader.getClassMetadata().getClassName());
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
}
}
}
获取Bean源的一些信息
此时若将Bean3的注解改为@Component的派生注解会扫描不到
System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (Resource resource : resources) {
// System.out.println(resource);
MetadataReader reader = factory.getMetadataReader(resource);
// System.out.println("类名:" + reader.getClassMetadata().getClassName());
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
// System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
// System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
AbstractBeanDefinition bd = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
String name = generator.generateBeanName(bd, beanFactory);
beanFactory.registerBeanDefinition(name, bd);
}
}
如果有@Component或者他的派生注解 这生成Bean
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()) {
System.out.println(p);
// com.itheima.a05copy.component -> classpath*:com/itheima/a05copy/component/**/*.class
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (Resource resource : resources) {
// System.out.println(resource);
MetadataReader reader = factory.getMetadataReader(resource);
// System.out.println("类名:" + reader.getClassMetadata().getClassName());
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
// System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
// System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
AbstractBeanDefinition bd = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
String name = generator.generateBeanName(bd, beanFactory);
beanFactory.registerBeanDefinition(name, bd);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
将模拟解析 @ComponentScan的代码抽取为ComponentScanPostProcessor
// context.registerBean(ConfigurationClassPostProcessor.class);// @ComponentScan @Bean @Import @ImportResource
// context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
// bd.getPropertyValues().add("basePackage", "com.itheima.a05copy.mapper");
// });
context.registerBean(ComponentScanPostProcessor.class); // 解析 @Bean
可见Bean2Bean3能被解析。
总结
1. Spring 操作元数据的工具类 CachingMetadataReaderFactory
2. 通过注解元数据(AnnotationMetadata)获取直接或间接标注的注解信息
3. 通过类元数据(ClassMetadata)获取类名,AnnotationBeanNameGenerator 生成 bean 名
4. 解析元数据是基于 ASM 技术
2.2 模拟解析 @Bean
public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
CachingMetadataReaderFactory factory=new CachingMetadataReaderFactory();
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itheima/a05copy/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();
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
reader.getAnnotationMetadata()获取和注解有关的元数据
reader.getAnnotationMetadata().getAnnotatedMethods()获取被注解标注的方法,接下来由这些信息定义Bean
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(method.getMethodName(),"config");这两行定义Config的工厂方法。
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);解决sqlSessionFactoryBean的自动装配的问题。
if (initMethod.length()>0){
builder.setInitMethodName(initMethod);
}解决dataSource的@Bean(initMethod = "init")初始化方法
bean1 sqlSessionFactory BeandataSource 都正常解析了。
2.3 模拟解析 Mapper 接口
1. Mapper 接口被 Spring 管理的本质:实际是被作为 MapperFactoryBean 注册到容器中
2. Spring 的诡异做法,根据接口生成的 BeanDefinition 仅为根据接口名生成 bean 名