1、关于@Import
翻看spring的源码,@Import注解几乎随处可见,因此有必要了解一下@Import注解是用来干什么的,不然你都看不懂spring的源码。
首先看一下@Import注解的源码
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates one or more <em>component classes</em> to import — typically
* {@link Configuration @Configuration} classes.
*
* <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
* Allows for importing {@code @Configuration} classes, {@link ImportSelector} and
* {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component
* classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).
*
* <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be
* accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired}
* injection. Either the bean itself can be autowired, or the configuration class instance
* declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly
* navigation between {@code @Configuration} class methods.
*
* <p>May be declared at the class level or as a meta-annotation.
*
* <p>If XML or other non-{@code @Configuration} bean definition resources need to be
* imported, use the {@link ImportResource @ImportResource} annotation instead.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.0
* @see Configuration
* @see ImportSelector
* @see ImportBeanDefinitionRegistrar
* @see ImportResource
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}
类注释比较清楚地说明了@Import注解的作用,这里我翻译一下。
-
声明一个或多个Component
-
提供等同于spring的xml配置中<import/>元素的功能,允许导入@Configuration类、ImportSelector的实现和ImportBeanDefinitionRegistrar的实现,当然也可以导入一个常规的类作为Component(类似于4.2版本后,利用AnnotationConfigApplicationContext类的register方法导入一个普通的类作为Component)
-
在@Configuration中声明的Bean应该被@Autowired注解注入,要么Bean自己被自动装配,要么声明bean的配置类被自动装配,后者允许在@Configuration类的方法之间进行显式的、IDE友好的导航
-
可以声明在类级别上,或者作为一个元注解
-
可以用@ImportResource注解代替@Import注解,去导入xml配置
根据上面的翻译,我总结一下@Import注解的用法
-
导入一个或多个Bean
-
导入@Configuration类
-
导入ImportSelector的实现类
-
导入ImportBeanDefinitionRegistrar的实现类
这些用法或许只有一个目的,就是导入Bean。那为什么要这么麻烦,手动导入Bean呢,@ComponentScan不是可以自动扫描包注册Bean嘛?其实这里的原因有两点:
-
@ComponentScan一般只会扫到自己项目中的Bean,第三方jar包中的@Bean扫不到
-
@Import注解可以结合@Conditional注解使用,即条件导入,@Conditional在spring源码中也是大量用到,这个我后面会专题介绍
2、@Import实战
@Import注解位于spring-context包下面,所以我们只需要普通的spring环境即可,当然springboot环境更可以,环境搭建这里我不介绍了,直接上例子。
2.1、声明普通Java类
由于前面介绍有四种方案可以通过@Import导入Bean,所以我先声明四个普通类(不打@Component)
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";
}
}
2.2、创建ImportBeanByConfig
注意:ImportBeanByConfig类不能被@ComponentScan扫描到,否则用@Import注解导入它就没有意义了
@Configuration
public class ImportBeanByConfig {
@Bean
public TestBean2 testBean2(){
return new TestBean2();
}
}
2.3、创建ImportSelector实现
public class ImportBeanByImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.bobo.group.extra.TestBean3"};
}
}
2.4、创建ImportBeanDefinitionRegistrar实现
public class ImportBeanByImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestBean4.class);
registry.registerBeanDefinition("TestBean4", rootBeanDefinition);
}
}
2.5、创建用@Import注解声明的类
注意:ImportTest类是需要被@ComonentScan扫到的,否则@Import直接不会生效
@Import({TestBean1.class,
ImportBeanByConfig.class,
ImportBeanByImportSelector.class,
ImportBeanByImportBeanDefinitionRegistrar.class})
@Configuration
public class ImportTest {
}
2.6、写一个Controller方法测试
import com.bobo.group.extra.TestBean1;
import com.bobo.group.extra.TestBean2;
import com.bobo.group.extra.TestBean3;
import com.bobo.group.extra.TestBean4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
@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);
}
}
访问HTTP接口,控制台打印日志如下:
将ImportTest类的@Import注释掉,再次访问HTTP接口,控制台打印日志如下:
可以看到两者的效果是不一样的,至于为什么大家都懂。spring源码博大精深,必须一步一个脚印才能更好地掌握与运用。