最近在看源码过程中,发现在根据spring实现中,很多都使用了@Import
注解实现动态bean的注入实现,因此自己总结了一些@Import
使用方式,便于对Spring框架的细节的理解。
@Import
在学习@Import
注解时,我们首先看下spring中对该注解的描述信息:
/** * 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. * @since 3.0 * @see Configuration * @see ImportSelector * @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(); }
从注解中知道,主要包含了一下信息:
- 该注解主要为了实现xml配置中的
<import />
元素的功能,能够动态引入配置相关信息 - 该注解需要配合
@Configuration
注解使用 - 该注解可以与
ImportSelector
和ImportBeanDefinitionRegister
配合使用
在了解了@Import基础信息之后,接下来我们就每种使用情况以demo的方式来分别实现。
@Configuration与@Import配合使用
首先我们创建三个需要使用的实体类,便于实现, 分别为Cycle
, Rule
, ConpositeRuleCycle
三个类,分别代码如下:
package org.spring.boot.demo.context.annotations.entity; import lombok.Data; @Data public class Cycle { public Cycle() { } public Cycle(String name) { this.name = name; } private String name; }
package org.spring.boot.demo.context.annotations.entity; import lombok.Data; @Data public class Rule { private String name; public Rule() { } public Rule(String name) { this.name = name; } }
package org.spring.boot.demo.context.annotations.entity; import lombok.AllArgsConstructor; import lombok.Data; import java.util.List; @Data @AllArgsConstructor public class CompositeRuleCycle { private Cycle cycle; private List<Rule> rule; }
在有了上面的三个实体之后,我们创建一个Configuration
类型,配合@Import
注解使用,查看效果,具体源码如下:
package org.spring.boot.demo.context.annotations.configuration; import org.spring.boot.demo.context.annotations.entity.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import javax.annotation.PostConstruct; import java.util.List; import java.util.Optional; @Configuration @Import({Cycle.class, Rule.class, CompositeRuleCycle.class}) public class ImportConfiguration { @Autowired(required = false) private List<Cycle> cycles; @Autowired(required = false) private List<Rule> rule; @Autowired private CompositeRuleCycle compositeRuleCycle; @Autowired private NoOption noOption; @PostConstruct public void init() { Optional .ofNullable(cycles) .filter(cs -> { cs.forEach(c -> System.out.println(c.toString())); return true; }) .orElse(null); Optional.ofNullable(rule) .filter(rs -> { rs.forEach(r -> System.out.println(r.toString())); return true; }) .orElse(null); System.out.println(compositeRuleCycle); } }
创建执行的Application
类型,具体源码如下:
package org.spring.boot.demo.context.annotations; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ImportAnnotationApplication { public static void main(String[] args) { SpringApplication.run(ImportAnnotationApplication.class, args); } }
通过执行以上代码,我们可以发现, Rule
与Cycle
已经通过@Autowired
方式注入到了Configuration
的类里,并且能够访问。日志输出如下:
Cycle(name=null) Rule(name=null) CompositeRuleCycle(cycle=[Cycle(name=null)], rule=[Rule(name=null)])
因此我们通过@Import
的方式能够引入Class
, 并在@Autowired
时,自动的帮助我们创建Bean. 同时我们也注意到了,CompositeRuleCycle
在创建Bean的时候,实际上对Cycle
与Rule
有依赖,该层依赖也通过Spring帮助我们实现了,这本身就是Spring提供的功能。
其实通过
@Import
的方式执行时,也是将对应的Class解析成对应的BeanDefinition
对象,然后通过AnnotationConfigApplicationContext#register
方法让spring进行管理。
@Import的猜想
既然@Import能够实现动态的引入bean, 那么我在两个Configuration
中,同时@Import
相同的Class
, 是否在Spring
中也会存在两个不同的bean
呢,因此,我们再创建一个Configuration
, 验证该想法:
@Import({Cycle.class, Rule.class, NoOption.class}) @Configuration public class MultiImportConfiguration { }
我们再次执行Application
,查看输出日志:
Cycle(name=null) Rule(name=null) CompositeRuleCycle(cycle=[Cycle(name=null)], rule=[Rule(name=null)])
从日志中可以看出,实际上在Spring中只包含了一个bean对象,其实这个BeanDefinition
有关,当我们@Import的时候,对应的beanName
是与当前类的全限定名称进行绑定,例如:import org.spring.boot.demo.context.annotations.entity.Rule
。因此保证了在spring不会有多个bean的出现。
ImportSelector
该注解还可以配合ImportSelector
接口进行使用,看下该类的源码信息:
package org.springframework.context.annotation; import org.springframework.core.type.AnnotationMetadata; /** * Interface to be implemented by types that determine which @{@link Configuration} * class(es) should be imported based on a given selection criteria, usually one or * more annotation attributes. * * <p>An {@link ImportSelector} may implement any of the following * {@link org.springframework.beans.factory.Aware Aware} interfaces, * and their respective methods will be called prior to {@link #selectImports}: * <ul> * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li> * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}</li> * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}</li> * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li> * </ul> * * <p>{@code ImportSelector} implementations are usually processed in the same way * as regular {@code @Import} annotations, however, it is also possible to defer * selection of imports until all {@code @Configuration} classes have been processed * (see {@link DeferredImportSelector} for details). * * @author Chris Beams * @since 3.1 * @see DeferredImportSelector * @see Import * @see ImportBeanDefinitionRegistrar * @see Configuration */ public interface ImportSelector { /** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. */ String[] selectImports(AnnotationMetadata importingClassMetadata); }
从注释中我们了解到,该接口能够帮助我们import
多个Class
到Spring
容器中,同事包含了以下信息:
- 在
ImportSelector
实现类时,同时也可以实现spring的Aware
接口,并且Aware
接口的执行会在selectorImprots()
方法之前 - 提供了
DeferredImportSelector
接口,能够在@Configuration
执行完成之后,在执行@Conditional
的BeanDefinition
的引入
为了实现该类,我们创建一个MultiImportSelector
的类,具体源码如下:
package org.spring.boot.demo.context.annotations.entity; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import java.util.Set; public class MultiImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 打印出Annotation中的信息 System.out.println(importingClassMetadata); // 获取当前注解的数据信息 Set<String> annotationTypes = importingClassMetadata.getAnnotationTypes(); for (String annotationType : annotationTypes) { System.out.println("annotationType: " + annotationType); } // 获取注解中的属性信息 Set<String> metaAnnotationTypes = importingClassMetadata.getMetaAnnotationTypes(Import.class.getName()); for (String metaAnnotationType : metaAnnotationTypes) { System.out.println("metaAnnotationType: " + metaAnnotationType); } return new String[]{ ImportSelectorEntity.class.getName() }; } } package org.spring.boot.demo.context.annotations.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class ImportSelectorEntity { }
我们在Configuration
中,加入ImportSelectorEntity
类型的注入,并输出结果:
package org.spring.boot.demo.context.annotations.configuration; import org.spring.boot.demo.context.annotations.entity.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import javax.annotation.PostConstruct; import java.util.List; import java.util.Optional; @Configuration @Import({Cycle.class, Rule.class, MultiImportSelector.class, CompositeRuleCycle.class}) public class ImportConfiguration { @Autowired private ImportSelectorEntity importSelectorEntity; @PostConstruct public void init() { ..... System.out.println(compositeRuleCycle); System.out.println(importSelectorEntity); System.out.println(noOption); } }
我们查看输出结果:
Cycle(name=null) Rule(name=null) CompositeRuleCycle(cycle=[Cycle(name=null)], rule=[Rule(name=null)]) ImportSelectorEntity()
在结果中,我们可以看到对应的类型已经注入进来,因此通过ImportSelector
能够实现多个Class
引入的功能,避免了在@Import
注解中引入过长的配置信息。
ImportBeanDefinitionRegistrar
在上面的实践中,有一个问题,就是因为我们Entity
在创建的时候,是有内部参数的。但是通过以上方式我们都无法实现对构造器参数的设置和修改。因此Spring
提供了ImportBeanDefinitionRegistrar
接口,该接口主要为了让我们能够更加细粒度的处理我们需要的注册的BeanDefinition
对象。我们还是首先来看下该接口的源码信息:
package org.springframework.context.annotation; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.core.type.AnnotationMetadata; /** * Interface to be implemented by types that register additional bean definitions when * processing @{@link Configuration} classes. Useful when operating at the bean definition * level (as opposed to {@code @Bean} method/instance level) is desired or necessary. * * <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type * may be provided to the @{@link Import} annotation (or may also be returned from an * {@code ImportSelector}). * * <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective * methods will be called prior to {@link #registerBeanDefinitions}: * <ul> * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li> * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware} * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware} * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware} * </ul> * * <p>See implementations and associated unit tests for usage examples. * * @author Chris Beams * @since 3.1 * @see Import * @see ImportSelector * @see Configuration */ public interface ImportBeanDefinitionRegistrar { /** * Register bean definitions as necessary based on the given annotation metadata of * the importing {@code @Configuration} class. * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be * registered here, due to lifecycle constraints related to {@code @Configuration} * class processing. * @param importingClassMetadata annotation metadata of the importing class * @param registry current bean definition registry */ void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry); }
我们从方法可以得知,我们可以在读取到注解的同时,也能够直接获取到BeanDefinitionRegistry
对象,当我们在处理完Class
之后,能够直接将BeanDefinition
注册到BeanDefinitionRegistry
中。
我们创建MultiImportDefinitionRegistrar
类型,具体源码如下:
package org.spring.boot.demo.context.annotations.entity; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; public class MultiImportDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Cycle.class); builder.addConstructorArgValue("MultiImportDefinitionRegistrar"); registry.registerBeanDefinition("MultiImportDefinitionRegistrar.cycle", builder.getBeanDefinition()); } }
在这个类中,主要包含以下步骤:
- 通过
BeanDefinitionBuilder
创建了一个关于Cycle
的BeanDefinition
- 同时指定
Cycle
构建器需要传入的name
属性的值 - 将
BeanDefinition
注册到Registry
中进行保存,并指定beanName
为MultiImportDefinitionRegistrar.cycle
其实最后一步,我们可以通过指定不同的beanName
来保证Cycle
可以在Spring
中同时存在多个不同的bean.
此时我们修改Configuration
类,具体如下:
package org.spring.boot.demo.context.annotations.configuration; import org.spring.boot.demo.context.annotations.entity.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import javax.annotation.PostConstruct; import java.util.List; import java.util.Optional; @Configuration @Import({Cycle.class, MultiImportSelector.class, CompositeRuleCycle.class, MultiImportDefinitionRegistrar.class}) public class ImportConfiguration { @Autowired(required = false) private List<Cycle> cycles; @Autowired(required = false) private List<Rule> rule; @Autowired private CompositeRuleCycle compositeRuleCycle; @Autowired private NoOption noOption; @Autowired private ImportSelectorEntity importSelectorEntity; @PostConstruct public void init() { ..... } }
再次执行Application
类,查看日志输出如下:
Cycle(name=null) Cycle(name=MultiImportDefinitionRegistrar) Rule(name=null) CompositeRuleCycle(cycle=[Cycle(name=null), Cycle(name=MultiImportDefinitionRegistrar)], rule=[Rule(name=null)]) ImportSelectorEntity()
此时我们可以发现,在@Autowired
时候,实际上Cycle
的实例已经包含了多个,并且name
与我们设置的值保持一致。
@Import派生注解
在阅读源码的过程中,其实对接spring的框架,使用更多的其实是通过注解派生的方式,实现自己的注解,然后在ImportBeanDefinitionRegistrar
中对注解进行解析,然后创建自定义的BeanDefinition
对象。因此我们也通过demo
的方式,来实现我们自己的派生注解。
自定义注解
package org.spring.boot.demo.context.annotations.diy; import org.spring.boot.demo.context.annotations.entity.EntityImportDefinitionRegistrar; import org.springframework.context.annotation.Import; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(EntityImportDefinitionRegistrar.class) public @interface EntityImport { String name() default ""; Class<?> value(); }
自定义ImportBeanDefinitionRegistrar
package org.spring.boot.demo.context.annotations.entity; import org.spring.boot.demo.context.annotations.diy.EntityImport; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.StringUtils; import java.util.Map; public class EntityImportDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 获取注解属性 Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EntityImport.class.getName()); Class<?> c = (Class<?>) map.get("value"); String name = (String) map.get("name"); if (StringUtils.isEmpty(name)) { name = c.getName(); } BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(c); builder.addConstructorArgValue(name); registry.registerBeanDefinition("default." + name, builder.getBeanDefinition()); } }
修改Configuraion类
package org.spring.boot.demo.context.annotations.configuration; import org.spring.boot.demo.context.annotations.diy.EntityImport; import org.spring.boot.demo.context.annotations.entity.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import javax.annotation.PostConstruct; import java.util.List; import java.util.Optional; @Configuration @EntityImport(value = Rule.class, name = "EntityImportRule") @Import({Cycle.class, MultiImportSelector.class, CompositeRuleCycle.class, MultiImportDefinitionRegistrar.class}) public class ImportConfiguration { @Autowired(required = false) private List<Cycle> cycles; @Autowired(required = false) private List<Rule> rule; @Autowired private CompositeRuleCycle compositeRuleCycle; @Autowired private NoOption noOption; @Autowired private ImportSelectorEntity importSelectorEntity; @PostConstruct public void init() { Optional .ofNullable(cycles) .filter(cs -> { cs.forEach(c -> System.out.println(c.toString())); return true; }) .orElse(null); Optional.ofNullable(rule) .filter(rs -> { rs.forEach(r -> System.out.println(r.toString())); return true; }) .orElse(null); System.out.println(compositeRuleCycle); System.out.println(importSelectorEntity); System.out.println(noOption); } }
执行Applicaion
类,查看日志执行结果信息:
Cycle(name=null) Cycle(name=MultiImportDefinitionRegistrar) Rule(name=EntityImportRule) Rule(name=null) CompositeRuleCycle(cycle=[Cycle(name=null), Cycle(name=MultiImportDefinitionRegistrar)], rule=[Rule(name=EntityImportRule), Rule(name=null)]) ImportSelectorEntity()
至此,关于@Import
的用法到此结束。在本章中我们主要总结了该注解的几种用法方式,希望可以帮助到你。