spring-组件注册(三)

237 阅读4分钟

今天要记录的是最后两种组件注册的方式:

  • Import
  • FactoryBean

一、Import方式-快速导入组件

@Import是spring一种导入组件的方式,根据源码:

/**
 * Indicates one or more {@link Configuration @Configuration} classes to import.
 *
 * <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 ImportResource
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

   /**
    * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
    * or regular component classes to import.
    */
   Class<?>[] value();

}

@Import可以用于:

  • 导入拥有@Configuration注解的配置类,以及配置类中的bean
  • 导入ImportSelector接口selectImports方法返回的相关bean
  • 通过实现ImportBeanDefintionRegister接口来注册bean
  • 直接导入几个普通类

主要做法,让我们来举几个栗子

1. 导入一个配置类

//配置类
@Configuration
public class SpringPropertiesValueConfig {

    @Bean
    public Cat cat() {
        return new Cat();
    }

}

在另一个配置类导入该配置类

@Configuration  //告诉spring这是一个配置类
@Import(SpringPropertiesValueConfig.class)
public class SpringConfig {

}

此时打印容器中所具有的bean

public class TestBean {

    public static void main(String[] args) {
        //通过注解的方式获取对应的bean
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        String[] beanNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanNames) {
            System.out.println(name);
        }
    }

}

运行结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
springConfig
com.xyr.configure.SpringPropertiesValueConfig
cat

SpringPropertiesValueConfig及配置类中的cat都被注册到了容器中。

2. 实现ImportSelector接口

定义实现类

public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //返回要注入的bean集合
        return new String[]{"com.xyr.model.YellowCar"};
    }
}

配置类

@Configuration  //告诉spring这是一个配置类
@Import(MyImportSelector.class)
public class SpringConfig {

}

再次调用之前的测试类,获取容器当中的bean,得到结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
springConfig
com.xyr.model.YellowCar

3. 导入普通类

@Configuration  //告诉spring这是一个配置类
@Import(BlueCar.class)
public class SpringConfig {

}

此时运行结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
springConfig
com.xyr.model.BlueCar

当然根据Import的源码可以发现,它也可以导入多个内容:

@Configuration  //告诉spring这是一个配置类
@Import({BlueCar.class, RedCar.class})
public class SpringConfig {

}

此时再次运行测试类:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
springConfig
com.xyr.model.BlueCar
com.xyr.model.RedCar

4. 实现ImportBeanDefinitionRegister接口

定义一个注册类,实现对应方法,AnnotationMetadata可以用于获取注解相关的信息,BeanDefinitionRegister可以获取到容器中注册的bean信息,也可以用于注册新的组件。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     *
     * @param importingClassMetadata 当前配置文件的注解信息
     * @param registry 组件定义注册 用于注册组件
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //判断RedCar、BlueCar是否在注册当中
        boolean match1 = registry.containsBeanDefinition("com.xyr.model.RedCar");
        boolean match2 = registry.containsBeanDefinition("com.xyr.model.BlueCar");
        if (match1 && match2) {
            //获取RainbowCar的定义
            RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBowCar.class);
            //注册需要的注解 指定bean名
            registry.registerBeanDefinition("rainbowCar", beanDefinition);
        }

    }
}

定义配置类,导入注册类:

@Configuration  //告诉spring这是一个配置类
@Import({BlueCar.class, RedCar.class,MyImportBeanDefinitionRegistrar.class})
public class SpringConfig {}

那么此时再运行测试类,得到结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
springConfig
com.xyr.model.BlueCar
com.xyr.model.RedCar
rainbowCar


二、FactoryBean的使用

根据他的名字,工厂bean,用于生成某一类型的bean,和BeanFactory不一样,BeanFactory是spring的容器,适用于创建和管理bean的。

查看FactoryBean对应的源码:

public interface FactoryBean<T> {

   @Nullable
   T getObject() throws Exception;

   @Nullable
   Class<?> getObjectType();

   default boolean isSingleton() {
      return true;
   }

}

其具有三个方法,可以用于获取对应的bean、获取bean的类型、获取是否为单例。这些都是在spring中注册一个bean所需要的信息。下面让我们来举个小栗子,来看看他是怎么注册一个bean的:

定义一个实现类

public class ColorCarFactorBean implements FactoryBean<ColorCar> {

    public ColorCar getObject() throws Exception {
        System.out.println("ColorCarFactorBean...getObject");
        return new ColorCar();
    }

    public Class<?> getObjectType() {
        return ColorCar.class;
    }

    public boolean isSingleton() {
        return true;
    }
}

在配置类中注册该类:

@Configuration  //告诉spring这是一个配置类
public class SpringConfig {

    @Bean
    public ColorCarFactorBean colorCarFactorBean() {
        return new ColorCarFactorBean();
    }

}

运行测试类:

public class TestBean {

    public static void main(String[] args) {
        
        //通过注解的方式获取对应的bean
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        Object bean = applicationContext.getBean("colorCarFactorBean");
        System.out.println(bean.getClass());
    }

}

可以发现获取到的是:

ColorCarFactorBean...getObject
class com.xyr.model.ColorCar

也就是说通过获取到的是colorCarFactorBean其getObject返回的bean。

而真正获取colorCarFactorBean对象的方式是通过如下方式进行获取:

public class TestBean {

    public static void main(String[] args) {
        //通过注解的方式获取对应的bean
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);

        Object bean = applicationContext.getBean("&colorCarFactorBean");
        System.out.println(bean.getClass());
    }

}

此时获取到的内容为:

class com.xyr.model.ColorCarFactorBean