Springboot在实现自动配置时,在注解@EnableAutoConfiguration中使用了@Import注解来注入AutoConfigurationImportSelector对象,这个类实现很多的自动装配逻辑,这在Springboot的自动装配实现中,是非常关键的一步。
查阅@Import官方注解说明可知,@Import 用于类上,专门用于引入被@Configuration标记的类,它的功能类似spring xml配置文件中的<import>标签,还可以用于引入ImportBeanDefinitionRegistrar、ImportSelector接口的实现(AutoConfigurationImportSelector使用的就是这种方式),功能等同于AnnotationConfigApplicationContext的register方法。@Import引入@Configuration标记的类时,类中被@Bean标记的方法的返回对象将会和类一起被引入。如果是xml配置文件或非Java编码形式定义的Bean需要引入,可以使用@ImportResource注解。具体可参考@ImportResource的使用。
/**
* 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.
*
*/
@Import注入的3种方式
(1)@Configuration标记的类
(2)接口ImportBeanDefinitionRegistrar的实现
(3)接口ImportSelector的实现
注入@Configuration标记的类
/**
* 定义注解,使用@Import注入@Configuration标记的类
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
@Import({ImportConfiguration.class})
public @interface EnableBar {
}
@Configuration
public class ImportConfiguration {
@Bean(name="bean-of-bar")
public Bar bar(){
return new Bar("Bar");
}
}
@Getter
@Setter
@ToString
public class Bar {
public Bar(String name){
this.name = name;
}
private String name;
}
从Spring Context中获取Bar对象
@Slf4j
@EnableBar
public class Bootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(Bootstrap.class)
.web(WebApplicationType.NONE).run(args);
Bar bean = context.getBean(Bar.class);
ImportConfiguration config = context.getBean(ImportConfiguration.class);
log.info("Bar:{}",bean.toString());
log.info("BarConfig:{}",config.toString());
}
}
从Spring的上下文中获取对象,此时两个对象都已经注入到Context中
注入接口ImportBeanDefinitionRegistrar的实现
/**
* 定义注解,注入ImportBeanDefinitionRegistrar的实现
*/
@Documented
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(ImportBeanDefinitionRegistrarImpl.class)
public @interface EnableDefinitionRegistrar {
}
public class ImportBeanDefinitionRegistrarImpl implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//使用RootBeanDefinition也可实现bean的注册
// RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Bar.class);
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(Bar.class.getName());
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
propertyValues.add("name","registartName");
registry.registerBeanDefinition("bar",beanDefinition);
}
}
从上下文中获取对象
@Slf4j
@EnableDefinitionRegistrar
public class Bootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(Bootstrap.class)
.web(WebApplicationType.NONE).run(args);
Bar bar = (Bar)context.getBean("bar");
log.info("Bar:{}",bar.toString());
}
}
结果可以获取到对象:
注入接口ImportSelector的实现
/**
* 定义注解,引入ImportSelector的实现类
*/
@Documented
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(ImportSelectorImpl.class)
public @interface EnableBarSelector {
}
public class ImportSelectorImpl implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//返回需要注入的bean的类名
return new String[]{Bar.class.getName()};
}
}
从上下文中获取对象
@Slf4j
@EnableBarSelector
public class Bootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(Bootstrap.class)
.web(WebApplicationType.NONE).run(args);
Bar bean = context.getBean(Bar.class);
log.info("Bar:{}",bean.toString());
}
}
结果
自定义注解结合@Import注解,可实现对象的动态注入,方便我们实现bean的自定义。当然Spring框架的核心注解还很多,这些都值得我们深入学习,这可以帮助我们解决实际开发中的很多问题,其实我们遇到的问题,编写框架的大神们都已经给我们提供了很好的解决方案,只待我们去发现而已。