@Import
这个注解非常重要,而且在springboot
项目当中随处可见,就拿springboot启动类
来说,我们经常会遇到一些@Enable
相关的注解,例如开启异步@EnableAsync
、开启缓存支持@EnableCaching
、开启定时任务@EnableScheduling
等等…
目录
- 一、@Import源码
- 二、@Import官网介绍
- 三、@Import的四种用法
- 四、@Import扩展注解
- 五、@ImportResource注解
一、@Import源码
点开源码会发现他没有使用其他特别的注解,就是用了三个元注解:
- @Target({ElementType.TYPE}): 使用范围接口、类、枚举、注解
- @Retention(RetentionPolicy.RUNTIME): @Retention是用来修饰注解的生命周期的,RetentionPolicy.RUNTIME代表的是不仅被保存到class文件中,jvm加载class文件之后,仍然存在;一直有效!
- @Documented: @Documented和@Deprecated注解长得有点像,@Deprecated是用来标注某个类或者方法不建议再继续使用,@Documented只能用在注解上,如果一个注解@B,被@Documented标注,那么被@B修饰的类,生成Javadoc文档时,会显示@B。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class<?>[] value();
}
二、@Import官网介绍
这些@Enable
注解你点进去就发现都是使用的@Import
注解,就是利用@Import
注解来将指定的配置类
加载到容器当中!
@Import
属于spring
当中的注解,在spring-context
包下。@Import源码,以及官网解释该注解:https://github.com/spring-projects/spring-framework/blob/main/spring-context/src/main/java/org/springframework/context/annotation/Import.java
(1)源码注释比较清楚地说明了@Import注解的作用,这里我翻译一下:
- 声明一个或多个Component(Component代表的是组件,或者可以理解为对象)
- 提供等同于spring的xml配置中
<import/>
元素的功能,允许导入@Configuration类、ImportSelector的实现和ImportBeanDefinitionRegistrar的实现
,当然也可以导入一个常规的类作为Component
(类似于4.2版本后,利用AnnotationConfigApplicationContext类的register方法
导入一个普通的类作为Component) - 在@Configuration中声明的Bean应该被@Autowired注解注入,可以自动连接bean本身,也可以自动连接声明bean的配置类实例,后者允许在@Configuration类的方法之间进行显式的、IDE友好的导航
- 可以声明在类级别上,或者作为一个元注解
- 可以用
@ImportResource
注解代替@Import
注解,去导入xml配置
(2)根据上面的翻译,我总结一下@Import注解的用法
- 作用:
- 导入一个或多个Bean
- 导入@Configuration类
- 导入ImportSelector的实现类
- 导入ImportBeanDefinitionRegistrar的实现类
- 使用前提:
@Import
必须要在@controller、@Service、@Component、@Configuration、@Repository
修饰的类下,并且要在springboot扫描范围内,这些注解修饰的类默认是必须和springboot启动类同包才会被扫描到,当然也可以手动指定扫描包。在启动类上使用@Import
也是可以的!使用@Bean注解间接创建的对象当中使用@Import是不可以的。如下示例就是不可以的哦!
@Configuration
public class Myconfig {
@Bean
public MyconfigTest myconfigTest(){
return new MyconfigTest();
}
}
@Import({TestBean1.class})
public class MyconfigTest {
}
(3)这些用法或许只有一个目的,就是导入Bean。那为什么要这么麻烦,手动导入Bean呢,@ComponentScan不是可以自动扫描包注册Bean嘛?其实这里的原因有两点:
- @ComponentScan一般只会扫到自己项目中的Bean,第三方jar包中的@Bean扫不到
- @Import注解可以结合@Conditional注解使用,即条件导入,@Conditional在spring源码中也是大量用到,这个我后面会专题介绍
(4)@Import等同于之前的<import/>
标签
官方解释:
<import/>
元素在Spring XML文件中用于帮助模块化配置一样,@Import
注释允许从另一个配置类加载@Bean
定义,如果您想要避免组件扫描,通过使用一些配置类作为入口点显式定义所有组件,Import
尤其有用。
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
三、@Import的四种用法
通过@Import注入对象的四种方式
理论我们都明白了,剩下来实操,首先定义四个类,我们分别用不同的方式将这四个类存到容器当中。
public class TestBean1 {
@Override
public String toString() {
return super.toString() + "--我是TestBean1";
}
}
public class TestBean2 {
@Override
public String toString() {
return super.toString() + "--我是TestBean2";
}
}
public class TestBean3 {
@Override
public String toString() {
return super.toString() + "--我是TestBean3";
}
}
public class TestBean4 {
@Override
public String toString() {
return super.toString() + "--我是TestBean4";
}
}
方式一:间接的@Configuration和@Bean注入
注意:ImportBeanByConfig类不能被@ComponentScan扫描到,如果扫描到通过@Configuration注解都可以将testBean2放入到容器当中了,用@Import注解导入它就没有意义了
@Configuration
public class ImportBeanByConfig {
@Bean
public TestBean2 testBean2(){
return new TestBean2();
}
}
方式二:创建ImportSelector实现
ImportSelector
接口只定义了一个String[] selectImports(AnnotationMetadata importingClassMetadata);
,用于指定需要注册为bean的Class名称
。使用@Import
引入了一个ImportSelector
实现类后,会把实现类中返回的Class名称都定义为bean
。
public class ImportBeanByImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 这里全类名
return new String[]{"com.gzl.cn.springbootcache.config.TestBean3"};
}
}
方式三:创建ImportBeanDefinitionRegistrar实现
public class ImportBeanByImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestBean4.class);
registry.registerBeanDefinition("TestBean4", rootBeanDefinition);
}
}
方式四:直接被注入(直接写要注入的类class)
ImportTest类是需要被@ComonentScan扫到的,否则@Import直接不会生效
@Import({TestBean1.class,
ImportBeanByConfig.class,
ImportBeanByImportSelector.class,
ImportBeanByImportBeanDefinitionRegistrar.class})
@Configuration
public class ImportTest {
}
写一个Controller方法测试
正常使用@Autowired注解的话,假如容器当中没有这个对象是会报错的,所以我们这里设置为
required = false
,这样就算没有这个对象,启动不会报错。
@RestController
public class CommonController {
@Autowired(required = false)
private TestBean1 testBean1;
@Autowired(required = false)
private TestBean2 testBean2;
@Autowired(required = false)
private TestBean3 testBean3;
@Autowired(required = false)
private TestBean4 testBean4;
@RequestMapping("/import")
public void printImportBeanInfo() {
System.out.println(testBean1);
System.out.println(testBean2);
System.out.println(testBean3);
System.out.println(testBean4);
}
}
测试结果:
将ImportTest类的@Import注释掉,再次访问HTTP接口,控制台打印日志如下:
四、@Import扩展注解
@ImportAutoConfiguration
和@EnableAutoConfiguration
就是通过@Import
注解所扩展出来的注解,@ImportAutoConfiguration应该用到的比较少,但是@EnableAutoConfiguration注解大家应该不陌生吧,他是springboot当中@SpringBootApplication启动注解当中的组合注解之一,读springboot源码基本上都需要了解这个的。
五、@ImportResource注解
早在以前我们注入bean都是 通过xml来实现的,springboot项目当中已经很少见了,都是通过注解来存放到容器当中的,@ImportResource就是将xml配置文件生效。接下来通过代码来演示他的作用。
(1)创建一个类
@Data
public class Student {
private String name;
private Integer age;
}
(2)添加applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog1" class="com.gzl.cn.springbootcache.config.Student">
<property name="name" value="zhangsan"/>
<property name="age" value="27"/>
</bean>
</beans>
(3)添加配置
@Configuration
@ImportResource(locations = "classpath:applicationContext.xml")
public class Myconfig {
}
(4)添加测试
@RestController
public class CommonController {
@Autowired(required = false)
private Student student;
@RequestMapping("/import")
public void printImportBeanInfo() {
System.out.println(student);
}
}
(5)启动访问接口:打印如下,成功从容器当中取出。
假如把@ImportResource(locations = "classpath:applicationContext.xml")
去掉打印如下: