0%

Spring Boot @Import的使用

使用@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
// Conn类,代表一个数据源连接
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;
}
}

// 连接方式A的配置类
public class ConfigurationA {
@Bean
public Conn conn() {
return new Conn("A");
}
}

// 连接方式B的配置类
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
// @EnableConn,导入Conn bean,并指定使用ConnImportSelector来选择加载A或者B
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Import(ConnImportSelector.class)
public @interface EnableConn {

ConnMode useConn();
}

// ConnImportSelector,根据注解传入的参数选择加载的Conn
public class ConnImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {

AnnotationAttributes annotationAttributes =
AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(EnableConn.class.getName()));

// ConnMode是自定义的代表A、B两种连接方式的枚举
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);
}

}

// Controller中注入的Conn是@EnableConn(useConn = ConnMode.B)指定的B方式的实现
@RestController
public class TestController {

@Autowired
private Conn conn;

@GetMapping("/test")
public String test() {
return conn.getConnName();
}

}

使用ImportBeanDefinitionRegistrar接口的方式类似,但是可以自行构建BeanDefination添加到传入的BeanDefinitionRegistry中

以上是一个简单的例子,实际上在selectImports()方法传入的参数annotationMetadata中,可以得到自定义注解所在类的其他注解的信息、类的信息,这样可以实现其他功能,比如可以实现自定义扫描路径加载bean等等