天下脸皮共十分
我占八分

Spring Bean Factory Post Processor and Implementation

basic code structure

bean

bean1
@Slf4j
public class Bean1 {
   public Bean1() {
     log.info("bean1 constructed");
  }
}
bean2
@Slf4j
@Component
public class Bean2 {
   public Bean2() {
     log.info("bean2 constructed");
  }
}
bean3
@Slf4j
@Controller
public class Bean3 {
   public Bean3() {
       log.info("bean1 constructed");
  }
}

config

@Configuration
@ComponentScan("tech.ityoung.spring.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 druidDataSource() {
       DruidDataSource druidDataSource = new DruidDataSource();
       druidDataSource.setUrl("jdbc:mysql://localhost:3306/world");
       druidDataSource.setUsername("root");
       druidDataSource.setPassword("22222222");
       return druidDataSource;
  }
}

mapper

@Mapper
public interface Mapper1 {
}

implement the function of bean factory post processor manually

@Slf4j
public class A05Application {
   public static void main(String[] args) throws IOException {
       // clean container
       GenericApplicationContext context = new GenericApplicationContext();

       // only register config
       context.registerBean("config", Config.class);

       // add support for @ComponentScan @Import @ImportResource @Bean
//       context.registerBean(ConfigurationClassPostProcessor.class);

       // add import for @Mapper, also add some basic bean post processor like internalCommonAnnotationProcessor, internalAutowiredAnnotationProcessor etc..
//       context.registerBean(MapperScannerConfigurer.class, beanDefinition -> beanDefinition.getPropertyValues().addPropertyValue("basePackage", "tech.ityoung.spring.a05.mapper"));

       ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);

       if (componentScan != null) {
           String[] basePackages = componentScan.basePackages();
           for (String basePackage : basePackages) {
               log.warn(basePackage);
               // tech.ityoung.a05.component -> classpath*:tech/ityoung/a05/component/**/*.class
               String path = "classpath*:" + basePackage.replace(".", "/") + "/**/*.class";
               log.error(path);

               CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
               Resource[] resources = context.getResources(path);
               for (Resource resource : resources) {
                   log.info(resource + "");
                   MetadataReader metadataReader = readerFactory.getMetadataReader(resource);
                   // get the class name, just like tech.ityoung.spring.a05.component.Bean1
                   String className = metadataReader.getAnnotationMetadata().getClassName();
                   log.info(className);
                   // with @Component annotation
                   boolean withComponent = metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName());
                   log.warn("with @Component annotation " + withComponent);
                   // with annotation from @Component, like @Controller, @Service.
                   boolean fromComponent = metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName());
                   log.warn("with annotation from @Component " + fromComponent);

                   if (withComponent || fromComponent) {
                       AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(className).getBeanDefinition();
                       DefaultListableBeanFactory defaultListableBeanFactory = context.getDefaultListableBeanFactory();
                       // generate bean name
                       String beanName = AnnotationBeanNameGenerator.INSTANCE.generateBeanName(beanDefinition, defaultListableBeanFactory);
                       defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition);
                  }
              }
          }
      }

       // initiate bean container
       context.refresh();

       String[] beanDefinitionNames = context.getBeanDefinitionNames();
       for (String beanDefinitionName : beanDefinitionNames) {
           log.warn(beanDefinitionName);
      }

       context.close();
  }
}

final output like this picture

image-20220920225214484

package the function into a ComponentScan bean factory post processor

image-20220920225655642

implement BeanFactoryPostProcessor

@Slf4j
public class ComponentScanBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
       try {
           ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);

           if (componentScan != null) {
               String[] basePackages = componentScan.basePackages();
               for (String basePackage : basePackages) {
                   log.warn(basePackage);
                   // tech.ityoung.a05.component -> classpath*:tech/ityoung/a05/component/**/*.class
                   String path = "classpath*:" + basePackage.replace(".", "/") + "/**/*.class";
                   log.error(path);

                   CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
//                   Resource[] resources = context.getResources(path);
                   Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                   for (Resource resource : resources) {
                       log.info(resource + "");
                       MetadataReader metadataReader = null;

                       metadataReader = readerFactory.getMetadataReader(resource);

                       // get the class name, just like tech.ityoung.spring.a05.component.Bean1
                       String className = metadataReader.getAnnotationMetadata().getClassName();
                       log.info(className);
                       // with @Component annotation
                       boolean withComponent = metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName());
                       log.warn("with @Component annotation " + withComponent);
                       // with annotation from @Component, like @Controller, @Service.
                       boolean fromComponent = metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName());
                       log.warn("with annotation from @Component " + fromComponent);

                       if (withComponent || fromComponent) {
                           AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(className).getBeanDefinition();
                           if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) {
                               DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
                               // generate bean name
                               String beanName = AnnotationBeanNameGenerator.INSTANCE.generateBeanName(beanDefinition, defaultListableBeanFactory);
                               defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition);
                          }
                      }
                  }
              }
          }
      } catch (IOException e) {
           throw new RuntimeException(e);
      }
  }
}

main

@Slf4j
public class A05Application {
   public static void main(String[] args) throws IOException {
       // clean container
       GenericApplicationContext context = new GenericApplicationContext();

       // only register config
       context.registerBean("config", Config.class);

       // add support for @ComponentScan @Import @ImportResource @Bean
//       context.registerBean(ConfigurationClassPostProcessor.class);
       context.registerBean(ComponentScanBeanFactoryPostProcessor.class);

       // add import for @Mapper, also add some basic bean post processor like internalCommonAnnotationProcessor, internalAutowiredAnnotationProcessor etc..
//       context.registerBean(MapperScannerConfigurer.class, beanDefinition -> beanDefinition.getPropertyValues().addPropertyValue("basePackage", "tech.ityoung.spring.a05.mapper"));

       // initiate bean container
       context.refresh();

       String[] beanDefinitionNames = context.getBeanDefinitionNames();
       for (String beanDefinitionName : beanDefinitionNames) {
           log.warn(beanDefinitionName);
      }

       context.close();
  }
}

registering MapperFactoryBean

traditional way

@Bean
   public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
       MapperFactoryBean<Mapper1> mapperFactoryBean = new MapperFactoryBean<>(Mapper1.class);
       mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory);
       return mapperFactoryBean;
  }

   @Bean
   public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
       MapperFactoryBean<Mapper2> mapperFactoryBean = new MapperFactoryBean<>(Mapper2.class);
       mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory);
       return mapperFactoryBean;
  }

implement the function manually

public class MapperScannerBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {

   @Override
   public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {

       try {
           CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
           String path = "tech/ityoung/spring/a05/mapper/**/*.class";
           Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
           for (Resource resource : resources) {
               MetadataReader metadataReader = readerFactory.getMetadataReader(resource);
               ClassMetadata classMetadata = metadataReader.getClassMetadata();
               if (classMetadata.isInterface()) {
                   AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                          .genericBeanDefinition(MapperFactoryBean.class)
                          .addConstructorArgValue(classMetadata.getClassName())
                          .getBeanDefinition();
                   beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
                   // only for generating the bean name, otherwise, the bean name should be
                   AbstractBeanDefinition nameBd = BeanDefinitionBuilder
                          .genericBeanDefinition(classMetadata.getClassName())
                          .getBeanDefinition();
                   String beanName = AnnotationBeanNameGenerator.INSTANCE.generateBeanName(nameBd, beanDefinitionRegistry);
                   beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
              }

          }
      } catch (Exception e) {
           throw new RuntimeException(e);
      }
  }

   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

  }
}
赞(4) 打赏
未经允许不得转载:Stephen Young » Spring Bean Factory Post Processor and Implementation
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏