@Import 究竟是干什么的?如何去使用?
从名字上我们可以直到import 是导入的意思,那么导入什么东西?spring中与导入最相关的就是bean,所以没错,我们大概猜测就是与Bean的注册有关。
那么具体如何使用呢?
我们以前编码的时候XML比较流行,但是现在一般都是使用注解,那么现在spring 是如何辨别哪些类需要被spring加载为Bean?
@Component 这个注解是最关键的,@Service,@Configuration,@SpringBootApplication等等都是@Component的派生注解,标注了@Component的类都会被spring IOC加载处理 @Import 就是放在类上面,而且需要放在能被spring加载的类上面,否则是不会生效的,我们直接看下使用
public class Bird {
public void say(){
System.out.println("啾啾~~");
}
}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@Import({Bird.class})
public class DemoAnnotationImportApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DemoAnnotationImportApplication.class, args);
Bird bird = context.getBean(Bird.class);
bird.say();
}
} 这里我们看到我们并没在Bird 类上标注@Component 或者 相关派生注解,但是spring ioc 中仍然能取到实例对象。
那么我们做个错误使用,将@Import({Bird.class}) 放到Bird 自己类上可以?
没错,已经报错了,因为没有标注@Component的类无法被spring ioc 加载,也就没有对@Import注解进行处理
我们再尝试放在其他被标注了@Component 或者其派生注解的类上面
@Configuration
@Import({Bird.class})
public class AnimalConfig {
} 也是没有问题的,这充分说明,@Import注解作用在能被spring ioc加载的地方,因为它和bean注册相关
当然我们还可以像下面这么用,功能就类似于再AnimalConfig 类上面标注@Configuration,也就是导入相应的配置信息的功能
@Import注解也可以用于导入第三方包 ,当然@Bean注解也可以,但是@Import注解快速导入的方式更加便捷,当然看你自己感觉了,你品,你细细品。
ImportSelector 使用
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import java.util.Set;
//自定义逻辑返回需要导入的组件
public class MyAnimalSelector implements ImportSelector {
private static final String DEFAULT_CLASS_NAME = "com.example.demoannotationimport.springBean.DefaultBird";
// 返回值,就是到导入到容器中的组件全类名
// AnnotationMetadata:当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//获取标注类上的所有注解信息,然后根据注解类型执行不同逻辑判定
//或者可以根据一些全局配置信息 来返回对应的逻辑,这块就很发散了
Set<String> annotationTypes = annotationMetadata.getAnnotationTypes();
System.out.println("annotationTypes");
return new String[]{DEFAULT_CLASS_NAME};
}
}
import com.example.demoannotationimport.springBean.Bird;
import com.example.demoannotationimport.springBean.DefaultBird;
import com.example.demoannotationimport.springBean.MyAnimalSelector;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@Import({MyAnimalSelector.class,Bird.class})
public class DemoAnnotationImportApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DemoAnnotationImportApplication.class, args);
Bird bird = context.getBean(Bird.class);
bird.say();
DefaultBird defaultBird = context.getBean(DefaultBird.class);
defaultBird.say();
}
} 效果如上,简单看 好像还是和spring bean 注册相关,确实也是和注册相关的注解,但是注意,因为可以根据不同的一些标志@Annotation 或者 其他的一些全局配置信息,返回一组全类名的数组,让spring ioc 进行加载,那么是不是就可以做很多事情了?对的,其实springBoot 很多地方使用了该方式。
我们来自己模拟实现一个使用
自建一个动物园扫描
public interface Animal {
void say();
} import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AnimalType {
String value();
}
@AnimalType("bird")
public class Bird implements Animal {
public void say(){
System.out.println("啾啾~~");
}
}
@AnimalType("cat")
public class Cat implements Animal {
public void say(){
System.out.println("喵喵~~");
}
} @AnimalType("dog")
public class Dog implements Animal{
public void say(){
System.out.println("汪汪~~");
}
} selector
//自定义逻辑返回需要导入的组件
public class ZooSelector implements ImportSelector {
private static final String BASE_PACKAGE = "com.example.demoannotationimport.zoo";
// 返回值,就是到导入到容器中的组件全类名
// AnnotationMetadata:当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 不使用默认的TypeFilter
ClassPathScanningCandidateComponentProvider provider =
new ClassPathScanningCandidateComponentProvider(false);
// 添加扫描规律规则,这里指定了内置的注解过滤规则
provider.addIncludeFilter(new AnnotationTypeFilter(AnimalType.class));
// 扫描指定包,如果有多个包,这个过程可以执行多次
Set<BeanDefinition> beanDefinitionSet = provider.findCandidateComponents(BASE_PACKAGE);
// 获取扫描结果的全限定类名
List<String> className = new ArrayList<>();
beanDefinitionSet.forEach(beanDefinition -> className.add(beanDefinition.getBeanClassName()));
String[] classNameArray = new String[className.size()];
String[] array = className.toArray(classNameArray);
return array;
}
} 启动开关注解
import com.example.demoannotationimport.zoo.ZooSelector;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ZooSelector.class)
public @interface EnableZoo {
}
最后使用
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
@EnableZoo
public class DemoAnnotationImportApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DemoAnnotationImportApplication.class, args);
Bird bird = context.getBean(Bird.class);
Dog dog = context.getBean(Dog.class);
Cat cat = context.getBean(Cat.class);
dog.say();
cat.say();
bird.say();
}
} 成功搞定,是不是感觉很舒适,还能有很多发散的空间,具体根据自己的项目来就行了,下面是大概的一个关系图,比较粗糙,但是大致的思想都有
ImportBeanDefinitionRegistrar 手动注册bean
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class MyBeanRegister implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类;
* 把所有需要添加到容器中的bean;调用
* BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//存在返回true
boolean definition1 = registry.containsBeanDefinition(Bird.class.getName());
boolean definition2 = registry.containsBeanDefinition(Dog.class.getName());
if (!definition1) {
// 指定Bean定义信息;(Bean的类型,Bean。。。)
RootBeanDefinition beanDefinition = new RootBeanDefinition(Bird.class);
// 注册一个Bean,指定bean名
registry.registerBeanDefinition(Bird.class.getName(), beanDefinition);
}
if (!definition2) {
// 指定Bean定义信息;(Bean的类型,Bean。。。)
RootBeanDefinition beanDefinition = new RootBeanDefinition(Dog.class);
// 注册一个Bean,指定bean名
registry.registerBeanDefinition("dog4", beanDefinition);
}
}
}
@SpringBootApplication
@Import(MyBeanRegister.class)
public class DemoAnnotationImportApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DemoAnnotationImportApplication.class, args);
Bird bird = context.getBean(Bird.class);
Dog dog = (Dog) context.getBean("dog4");
dog.say();
bird.say();
}
} 小总结
@Import ,ImportSelector 以及 ImportBeanDefinitionRegistrar总结
第一种用法:@Import({ 要导入的容器中的组件 } ):容器会自动注册这个组件,id默认是全类名
第二种用法:ImportSelector:返回需要导入的组件的全类名数组,springboot底层用的特别多【重点 】
第三种用法:ImportBeanDefinitionRegistrar:手动注册bean到容器
以上三种用法方式皆可混合在一个@Import中使用,特别注意第一种和第二种都是以全类名的方式注册,而第三中可自定义方式。
@Import注解本身在springboot中用的很多,特别是其中的第二种用法ImportSelector方式在springboot中使用的特别多,尤其要掌握!














还没有评论,来说两句吧...