向IOC容器注册组件的几种方式
-
第一种大家都常见,配置包扫描+类上注解(@Component, @Controller, @Service, @Repository, @Resource)
也就是这样:
// 配置类 @Configuration @ComponentScan("com.example.demo.bean") public class AnnotationConfigTestImport { } // bean package com.example.demo.bean; import org.springframework.stereotype.Component; @Component public class Dog { public Dog() { System.out.println("===> dog bean created..."); } }
这样凡是扫描的包下带有以上注解的都会被注册到IOC容器。但是缺点也显而易见:不能注册第三方包中的类
-
第二种,使用@Bean,像这样:
@Configuration // @ComponentScan("com.example.demo.bean") public class AnnotationConfigTestImport { @Bean public Cat cat() { return new Cat(); } @Bean public Dog dog() { return new Dog(); } }
这种方式可以随意注册任何组件到容器,但是麻烦…每增加一个组件,我们都要在配置类里增加一个@Bean
-
第三种,在配置类上使用@Import注解,我们先看看@Import源码:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar} * or regular component classes to import. */ Class<?>[] value(); }
可以看到@Import需要我们传一个Class数组,也就是我们要注册到容器的类。那我们就这样用:
@Configuration // @ComponentScan("com.example.demo.bean") @Import({Cat.class, Dog.class}) public class AnnotationConfigTestImport { }
这种比上一种方式更加简洁一些,能少写一些代码
-
第四种,使用ImportSelector接口,我们先看看什么是ImportSelector
public interface ImportSelector { /** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. */ String[] selectImports(AnnotationMetadata importingClassMetadata); }
接口里面就一个方法,从方法的注释上我们可以看到,返回一个包含类名的数组,这些类将被注册到容器。那我们写一个ImportSelector的实现类,返回我们要注册到容器的bean:
public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.example.demo.bean.Windows"}; } }
然后在配置类中import我们写的selector即可,像这样:
@Configuration @Import({Cat.class, Dog.class, MyImportSelector.class}) public class AnnotationConfigTestImport { }
-
第五种,使用ImportBeanDefinitionRegistrar接口,我们看看这个接口是啥:
public interface ImportBeanDefinitionRegistrar { /** * Register bean definitions as necessary based on the given annotation metadata of * the importing {@code @Configuration} class. * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be * registered here, due to lifecycle constraints related to {@code @Configuration} * class processing. * @param importingClassMetadata annotation metadata of the importing class * @param registry current bean definition registry */ void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry); }
我们看到这个接口有个registerBeanDefinitions()方法,方法的参数里有个BeanDefinitionRegistry,这个就是我们注册bean的类,我们可以使用这个类来注册我们的组件。现在我们来实现这个类:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Linux.class); registry.registerBeanDefinition("linux", rootBeanDefinition); } }
然后在配置类import这个类即可向容器中注册名字为linux的bean。
@Configuration // @ComponentScan("com.example.demo.bean") @Import({Cat.class, Dog.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class}) public class AnnotationConfigTestImport { }
以上就是我学习到的注册组件的几种方式,每种都简单的介绍了一下,实际上我们还可以做更多的操作,比如第四,第五种方式,在注册bean的时候我们还可以添加更多的逻辑,这个大家下去自行探索吧~