Spring 手动注册bean 定义

手动注册bean的两种方式:

  • 实现ImportBeanDefinitionRegistrar
  • 实现BeanDefinitionRegistryPostProcessor

1. ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar其本质也是通过BeanDefinitionRegistryPostProcessor来实现的。实现ImportBeanDefinitionRegistrar比较简单,也有多种方式,下面这个是最简单的手动注册bean的方式。

1.1 手动创建BeanDefinition
public class Foo {

    public void foo() {
        System.out.println("Foo.foo() invoked!");
    }
}

public class FooImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Foo.class);
        BeanDefinition beanDefinition = builder.getBeanDefinition();

        registry.registerBeanDefinition("foo",beanDefinition);
    }
}

@SpringBootApplication
@Import({FooImportBeanDefinitionRegistrar.class})
public class Demo implements CommandLineRunner {

    @Autowired
    private Foo foo;

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Demo.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        foo.foo();
    }
}

输出:
Foo.foo() invoked!

1.2 ClassPathBeanDefinitionScanner

借助spring类ClassPathBeanDefinitionScanner来扫描Bean并注册

public class Foo {

    public void foo() {
        System.out.println("Foo.foo() invoked!");
    }
}

public class FooImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
        scanner.addIncludeFilter(new AssignableTypeFilter(Foo.class));
        scanner.scan("com.study.demo.domain");
        //这里也可以扫描自定义注解并生成BeanDefinition并注册到Spring上下文中
    }
}

@SpringBootApplication
@Import({FooImportBeanDefinitionRegistrar.class})
public class Demo implements CommandLineRunner {

    @Autowired
    private Foo foo;

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Demo.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        foo.foo();
    }
}

输出:
Foo.foo() invoked!

1.3 ImportBeanDefinitionRegistrar

在上一个例子中我们直接使用的是ClassPathBeanDefinitionScanner,我们也可以继承ImportBeanDefinitionRegistrar并重写相关方法(一般都是扫描自定义的注解,才会继承ClassPathBeanDefinitionScanner)。这里你可以扫描自定义的注解,生成BeanDefinition(或其代理)的定义,然后注册到spring容器中。实现代码可以参考Mybatis的
org.mybatis.spring.annotation.MapperScannerRegistrar类,Mybatis扫描Mapper注解,通过MapperFactoryBean来代理了Mapper,并将其注册到spring中

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

  private ResourceLoader resourceLoader;

  /**
   * {@inheritDoc}
   */
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    // this check is needed in Spring 3.1
    if (resourceLoader != null) {
      scanner.setResourceLoader(resourceLoader);
    }

    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      scanner.setAnnotationClass(annotationClass);
    }

    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      scanner.setMarkerInterface(markerInterface);
    }

    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
    }

    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }

    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

    List<String> basePackages = new ArrayList<String>();
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }

}

一般继承ClassPathBeanDefinitionScanner需要实现这三个方法(并不绝对)

  • registerFilters
  • doScan
  • isCandidateComponent

2. BeanDefinitionRegistryPostProcessor

实现BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法。在spring源码中@Configuration @Import等配置类的注解就是通过这种方式实现的,具体实现方式可以参考spring源码

org.springframework.context.annotation.ConfigurationClassPostProcessor

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        int registryId = System.identityHashCode(registry);
        if (this.registriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                    "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
        }
        if (this.factoriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                    "postProcessBeanFactory already called on this post-processor against " + registry);
        }
        this.registriesPostProcessed.add(registryId);

        processConfigBeanDefinitions(registry);
    }

另外一个可参考的样例是Mybatis,在Mybatis中MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,在这个方法中注册bean的方式同1.3

org.mybatis.spring.mapper.MapperScannerConfigurer

  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }

    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Boot提供了多种方式来注册bean,其中一种是手动注册bean。具体步骤如下: 1. 创建一个Java类,用于定义注册bean。 2. 在该类上添加注解@Component或其他相关注解,以便Spring Boot能够扫描到该类。 3. 在该类中定义一个方法,用于创建要注册bean。 4. 在该方法上添加注解@Bean,以便告诉Spring Boot该方法返回的对象应该被注册为一个bean。 5. 在需要使用该bean的地方,使用@Autowired或其他相关注解将该bean注入到目标类中。 需要注意的是,手动注册bean需要在Spring Boot启动时进行,可以通过在启动类中添加注解@Import来引入要注册bean类。 ### 回答2: 在Spring Boot中,可以通过手动注册bean的方式来将自定义的类纳入Spring容器管理。 首先,我们需要在一个配置类中添加`@Configuration`注解,将其声明为配置类。然后,使用`@Bean`注解来标记一个方法,该方法的返回值将作为一个Spring bean进行注册。 下面是一个示例: ```java @Configuration public class MyConfiguration { @Bean public MyBean myBean() { return new MyBean(); } // 其他自定义配置及bean的注册方法 } ``` 在上述示例中,`MyConfiguration`类被声明为一个配置类,并且通过`@Bean`注解将`myBean()`方法返回的实例注册为一个Spring bean。 需要注意的是,被手动注册bean可以被其他组件自动注入或通过`@Autowired`注解来获取。 除了使用上述方式手动注册bean,Spring Boot还提供了其他的方式来注册bean,例如使用`@ComponentScan`、`@Import`、`@ImportResource`等注解,或者实现`ImportBeanDefinitionRegistrar`、`BeanFactoryPostProcessor`等自定义接口。 总的来说,Spring Boot中手动注册bean的方式很灵活,可以根据实际需求选择最适合的方式进行注册。 ### 回答3: 在Spring Boot中手动注册bean可以通过编写配置类(Configuration Class)来实现。 首先,创建一个配置类,可以使用`@Configuration`注解进行标记。在配置类中,可以使用`@Bean`注解来定义需要注册bean。例如: ```java @Configuration public class MyConfiguration { @Bean public MyBean myBean() { return new MyBean(); } // 可以同时定义多个bean // ... } ``` 上述例子中,`myBean()`方法使用`@Bean`注解定义了一个`MyBean`类的实例,然后在应用中就可以通过自动装配或者手动获取来使用该bean了。 接下来,需要在Spring Boot应用的主类上使用`@Import`注解来引入该配置类。例如: ```java @SpringBootApplication @Import(MyConfiguration.class) public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } ``` 上述例子中,`@Import`注解将`MyConfiguration`配置类引入到应用中,这样在启动应用时,定义bean就会被注册Spring容器中。 需要注意的是,手动注册bean一般适用于一些特殊情况,不推荐频繁使用该方法,因为Spring Boot已经提供了自动配置的功能,可以根据约定和配置进行自动注册bean,可以更好地减少开发工作量和提高开发效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值