使用@Import可以导入一个指定的@Configuration类作为一个bean(@Configuration类中定义的bean自然会注入),也可以使用@ImportResource导入other non-{@code @Configuration} bean definition resources;或者使用ImportSelector或ImportBeanDefinitionRegistrar来选择加载的bean。@Import导入的bean在所在类上使用@Autowired就可注入
比如,@SpringBootApplication类默认只导入所在包层级及以下层级的bean,在这一个包之外的@Configuration类就需要使用@Import才能注入(这种情况可以使用@ComponentScan等);比如,使用ImportSelector或ImportBeanDefinitionRegistrar,配合自定义注解,根据某些参数或者配置加载bean
如自定义starter时使用的@EnableAutoConfiguration,使用了@Import(AutoConfigurationImportSelector.class)注解,AutoConfigurationImportSelector类中从META-INF/spring.factories找出所有需要加载的configurations返回
使用@Import与ImportSelector的自定义@EnableXXX
假设某个数据源存在A、B两种连接方式,希望可以根据指定的参数选择使用的方式
连接方式相关的配置类简化如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class Conn {
private String connName;
public Conn(String connName) { this.connName = connName; }
public String getConnName() { return connName; }
public void setConnName(String connName) { this.connName = connName; } }
public class ConfigurationA { @Bean public Conn conn() { return new Conn("A"); } }
public class ConfigurationB { @Bean public Conn conn() { return new Conn("B"); } }
|
提供一个注解@EnableConn,用于导入Conn bean,并在注解上指定@Import(ConnImportSelector.class)来选择使用A或者B:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited @Import(ConnImportSelector.class) public @interface EnableConn {
ConnMode useConn(); }
public class ConnImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) {
AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(EnableConn.class.getName()));
ConnMode conn = annotationAttributes.getEnum("useConn"); switch (conn) { case A: return new String[]{ConfigurationA.class.getName()}; case B: return new String[]{ConfigurationB.class.getName()}; default: return null; } } }
public enum ConnMode { A, B }
|
这样,在@SpringBootApplication类上添加@EnableConn注解并指定useConn参数,指定使用的bean:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @EnableConn(useConn = ConnMode.B) @SpringBootApplication public class ImportTestApplication {
public static void main(String[] args) { SpringApplication.run(ImportTestApplication.class, args); }
}
@RestController public class TestController {
@Autowired private Conn conn;
@GetMapping("/test") public String test() { return conn.getConnName(); }
}
|
使用ImportBeanDefinitionRegistrar接口的方式类似,但是可以自行构建BeanDefination添加到传入的BeanDefinitionRegistry中
以上是一个简单的例子,实际上在selectImports()方法传入的参数annotationMetadata中,可以得到自定义注解所在类的其他注解的信息、类的信息,这样可以实现其他功能,比如可以实现自定义扫描路径加载bean等等