最近在看源码过程中,发现在根据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的用法到此结束。在本章中我们主要总结了该注解的几种用法方式,希望可以帮助到你。