如果你看Spring的源码,对@ImportSelector注解一定不陌生。原本想写一篇原理性的文章,但研究了一天发现还是不能完全理顺。于是,先尝试编写了一个示例,也不知道这样用正不正确……
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TestImportSelector.class)
public @interface TestImport {
boolean test() default true;
}
首先定义TestImport注解,该注解会配置在SpringBoot应用启动类上。该注解导入了TestImportSelector类
public class TestImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(TestImport.class.getName()));
boolean test = attributes.getBoolean("test");
if (test) {
return new String[]{TestServiceA.class.getName()};
} else {
return new String[]{TestServiceB.class.getName()};
}
}
}
TestImportSelector实现了ImportSelector接口。我们通过selectImports方法的参数importingClassMetadata获取TestImport注解的注解属性test,当test为true时,返回TestServiceA类的全限定名;当test为false时,返回TestServiceB类的全限定名。TestServiceA类和TestServiceB类实现了TestService接口
public interface TestService {
void test();
}
public class TestServiceA implements TestService {
@Override
public void test() {
System.out.println("A");
}
}
public class TestServiceB implements TestService {
@Override
public void test() {
System.out.println("B");
}
}
我们在TestCtrl中注入TestService
@RestController
public class TestCtrl {
@Autowired
private TestService testService;
@GetMapping("/")
public String test() {
testService.test();
return "Hello, World!";
}
}
最后看一下启动类
@TestImport(test = false)
@SpringBootApplication
public class TestImportSelectorApplication {
public static void main(String[] args) {
SpringApplication.run(TestImportSelectorApplication.class, args);
}
}
启动项目,在浏览器输入http://localhost:8080/
代码正确执行,再看下日志打印
因为我们配置了test属性为false,所以TestService注入的是TestServiceB。我们修改test属性为true,再测试下
此时TestService注入的是TestServiceA。