Spring注解驱动开发学习总结4:组件注册之@Import、FactoryBean详解
1、使用@Import快速导入组件
给容器中注入组件的3种方式:
1、报扫描+组件标注注解(@Controller/ @Service/ @Reposiroty/ @Component)
这种方式适合导入自己创建的组件
2、@Bean
这种方式适合导入第三方的组件。
3、@Import
由于@Bean,每次需要new一个组件的实例,如果我们只需要导入该组件,并不需要初始化等操作,那么就不用使用@Bean了,可以直接在配置类的上面使用@Import快速导入一个组件。
3.1 @Import:容器可以自动注册组件
查看@Import注解,可以看到参数value可以可以传入Configuration, ImportSelector, ImportBeanDefinitionRegistrar
public @interface Import {
/**
* {@link Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}
*/
Class<?>[] value();
}
3.2 自定义ImportSelector:返回需要导入的组件的全类名的数组
查看ImportSelector接口,可以看到需要实现selectImports方法,该方法会返回需要导入的组件名的数组。
参数AnnotationMetadata:标注了@Import的类的所有的注解信息
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);
}
3.3 自定义ImportBeanDefinitionRegistrar:该类只需要实现registerBeanDefinitions方法,该方法有2个入参:
参数importingClassMetadata:当前类的注解信息。
参数registry:所有bean定义的注册类。可以使用该参数的registerBeanDefinition方法,将需要注入容器的类进行手工导入。
public interface ImportBeanDefinitionRegistrar {
/**
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
1.1 构建外部类
新建5个颜色类和一个彩虹类,我们假设这6个类是外部类,等会我们需要导入他们到组件中。
新建5个颜色类:
红色类:com/example/bean/Red.java,
黄色类:com/example/bean/Yellow.java,
蓝色类:com/example/bean/Blue.java,
绿色类:com/example/bean/Green.java,
紫色类:com/example/bean/Purple.java
然后再新建一个彩虹类:com/example/bean/Rainbow.java
package com.example.bean;
public class Red {
}
1.2 构建配置类
新构建配置类:com/example/config/MainConfig3.java
通过@Bean的方法注入红色类。
然后通过@Import注解的方式注入黄色类和蓝色类。
package com.example.config;
import com.example.bean.Blue;
import com.example.bean.Red;
import com.example.bean.Yellow;
import com.example.condition.MyImportSelector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Import({Yellow.class, Blue.class})
@Configuration
public class MainConfig3 {
@Bean
public Red red() {
return new Red();
}
}
1.3 构建自定义ImportSelector实现类
构建自定义ImportSelector实现类:com/example/condition/MyImportSelector.java。
该类只需要实现一个方法:selectImports。该方法也只有一个入参AnnotationMetadata:该参数是标注了@Import的类的所有的注解信息。我们在这里打印以下对应的信息。
返回的是需要导入的组件的全类名的数组,因此这里我们写上绿色类和紫色类的数组。相当于导入了这2个类。
package com.example.condition;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import java.util.Set;
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 打印当前类的注解信息
String className = importingClassMetadata.getClassName();
Set<String> annotationTypes = importingClassMetadata.getAnnotationTypes();
System.out.println("MyImportSelector-当前类的名称为: "+className);
System.out.println("MyImportSelector-当前类的注解有: "+annotationTypes);
return new String[]{"com.example.bean.Green", "com.example.bean.Purple"};
}
}
1.4 构建自定义ImportBeanDefinitionRegistrar实现类
构建自定义ImportBeanDefinitionRegistrar实现类:com/example/condition/MyImportBeanDefinitionRegistrar.java。
该类只需要实现一个方法:registerBeanDefinitions。
registerBeanDefinitions方法有2个参数:
参数importingClassMetadata:当前类的注解信息。
参数registry:所有bean定义的注册类。可以使用该参数的registerBeanDefinition方法,将需要注入容器的类进行手工导入。
这里,我们利用registry类判断下容器中是否有红黄蓝绿紫这5种颜色的类,如果同时都存在,那么就将Rainbow类注入,并且将id设置为"彩虹bean"。
package com.example.condition;
import com.example.bean.Rainbow;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean red = registry.containsBeanDefinition("red");
boolean yellow = registry.containsBeanDefinition("com.example.bean.Yellow");
boolean blue = registry.containsBeanDefinition("com.example.bean.Blue");
boolean green = registry.containsBeanDefinition("com.example.bean.Green");
boolean purple = registry.containsBeanDefinition("com.example.bean.Purple");
// 如果红色、黄色、蓝色、绿色、紫色都存在的情况下,就给容器注入彩红bean
if (red && yellow && blue && green && purple) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Rainbow.class);
registry.registerBeanDefinition("彩虹bean", rootBeanDefinition);
}
}
}
1.5 配置类的@Import新增自定义的导入类
配置类的@Import新增自定义的导入类:MyImportSelector和MyImportBeanDefinitionRegistrar。
package com.example.config;
import com.example.bean.Blue;
import com.example.bean.Red;
import com.example.bean.Yellow;
import com.example.condition.MyImportBeanDefinitionRegistrar;
import com.example.condition.MyImportSelector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Import({Yellow.class, Blue.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
@Configuration
public class MainConfig3 {
@Bean
public Red red() {
return new Red();
}
}
1.6 测试
构建测试方法:testImport。在测试方法里打印以下目前容器里的所有的bean的定义名称
@Test
public void testImport() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
System.out.println("\nioc容器已创建完成\n");
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
以下为运行结果:
1)可以看到通过@Bean导入的red,成功加入了容器中;
2)可以看到通过@Import({Yellow.class, Blue.class}),
Yellow和Blue也成功导入了,并且比@Bean的方式,少了很多代码。只不过它的bean的名称是全类路径名。而@Bean默认的bean名称是方法名。
3)可以看到通过@Import({MyImportSelector.class}),
Green和Purple也成功导入了。
同时MyImportSelector也打印了配置类MainConfig3的注解信息有:annotation.Import和annotation.Configuration。
4)可以看到通过@Import({MyImportBeanDefinitionRegistrar.class}),
Rainbow类也成功导入了,并且bean的名称就是我们设置的:彩虹bean。
通过上面,在导入其他类时,我们展示了4种方式:@Bean和@Import中的3种方式
2、使用FactoryBean进行注入
除了上一小结提到的3种方式,还可以通过FactoryBean的方式注入bean。
1、查看FactoryBean接口,需要实现3个方法:
1)getObject:返回bean实例;
2)getObjectType:返回bean的类型;
3)isSingleton:返回该bean是否是单例的;
2、默认获取到的是工厂bean调用getObject方法返回的对象实例;
如果想要获取的是工厂bean本身的话,需要在id前面加一个&
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
小节1是通过@Import导入的组件,本小节通过FactoryBean来导入。
2.1 构建一个FactoryBean
构建一个RainbowFacotryBean,让其实现FactoryBean接口。
package com.example.bean;
import org.springframework.beans.factory.FactoryBean;
public class RainbowFacotryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
System.out.println("RainbowFacotryBean_getObject方法执行了");
return new Rainbow();
}
@Override
public Class<?> getObjectType() {
System.out.println("RainbowFacotryBean_getObjectType方法执行了");
return Rainbow.class;
}
@Override
public boolean isSingleton() {
System.out.println("RainbowFacotryBean_isSingleton方法执行了");
return true;
}
}
2.2 配置类中添加RainbowFacotryBean
还是在刚刚的配置类MainConfig3中进行修改,将@Import先注释掉。
package com.example.config;
import com.example.bean.Blue;
import com.example.bean.RainbowFacotryBean;
import com.example.bean.Red;
import com.example.bean.Yellow;
import com.example.condition.MyImportBeanDefinitionRegistrar;
import com.example.condition.MyImportSelector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
//@Import({Yellow.class, Blue.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
@Configuration
public class MainConfig3 {
@Bean
public RainbowFacotryBean rainbowFacotryBean() {
return new RainbowFacotryBean();
}
}
2.2 构建测试方法
构建测试方法:testFactoryBean。
在该方法中打印下rainbowFacotryBean和&rainbowFacotryBean的类型,其次查看下rainbowFactoryBean1和rainbowFactoryBean2是否相等
@Test
public void testFactoryBean() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
System.out.println("\nioc容器已创建完成\n");
Object rainbowFactoryBean1 = applicationContext.getBean("rainbowFacotryBean");
Object bean = applicationContext.getBean("&rainbowFacotryBean");
System.out.println(rainbowFactoryBean1.getClass());
System.out.println(bean.getClass());
Object rainbowFactoryBean2 = applicationContext.getBean("rainbowFacotryBean");
System.out.println(rainbowFactoryBean1==rainbowFactoryBean2);
}
2.3 测试
运行测试方法:testFactoryBean,结果如下图。
可以看到,id为rainbowFacotryBean的对象的类型为:class com.example.bean.Rainbow,
而id为&rainbowFacotryBean的对象的类型为:class com.example.bean.RainbowFacotryBean。
另外,由于我们设置了isSingleton方法为true,因此rainbowFactoryBean1和rainbowFactoryBean2是同一个对象。