上一篇文章咱们整理的注解驱动的几种方式, 今天接着学习剩下的注解驱动.
1,Conditional 加载 Bean
Conditional 是一个接口, 用来按照一定的条件判断, 满足条件的 Bean 才加载进容器中. 使用起来也比较方便. 但是要注意 Conditional 和 ComponentScan 中 Filter 的区别. 区别就是咱们在使用 @ComponentScan 进行扫描的时候就会配置 Filter 看是那些 Bean 可以通过扫描加载. 通过过滤的才会进行扫描加载进容器, 但是没有满足 Filter 的 Bean 就不会进行扫描, 更不用说加载了. 但是 Conditional 的条件就是扫描了也要满足条件不然也不会进行加载. 上面我们说 Conditional 是一个接口那么我们就需要实现他. 好了上代码, 首先我们先写我们的容器加载类.
- /**
- * Created by luyang.li on 19/3/11.
- */
- @Configuration
- public class MainConfigConditional {
- /**
- * Conditional 按照一定的条件进行判断, 满足条件才加入到容器中, 注意和 Componentscan 中的 Filter 进行区分
- *
- * @return
- */
- /**
- * 如果是 Windows 容器中就只有 Bill
- *
- * @return
- */
- @Conditional({WindowsConditional.class})
- @Bean("bill")
- public PersonConditonal person01() {
- return new PersonConditonal(62, "bill");
- }
- @Conditional({LinuxConditional.class})
- @Bean("linus")
- public PersonConditonal person02() {
- return new PersonConditonal(48, "linus");
- }
- /**
- * 操作系统格式 Mac 容器中就只加载 "乔布斯"
- * Conditional 是一个注解, 只需要 Condition 数组: Conditional{Condition}
- *
- * @return
- */
- @Conditional({MacConditional.class})
- @Bean("qiaobusi")
- public PersonConditonal person03() {
- return new PersonConditonal(65, "乔布斯");
- }
- }
我们的代码中是按照操作系统进行区分, 要是那种运行环境就加载某个 Bean.
然后就是咱么的自己实现的 Conditional:
- import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
- import org.springframework.beans.factory.support.BeanDefinitionRegistry;
- import org.springframework.context.annotation.Condition;
- import org.springframework.context.annotation.ConditionContext;
- import org.springframework.core.env.Environment;
- import org.springframework.core.type.AnnotatedTypeMetadata;
- /**
- * Created by luyang.li on 19/3/12.
- */
- public class MacConditional implements Condition {
- /**
- *
- * @param context
- * @param metadata
- * @return
- */
- @Override
- public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
- //TODO 判断是否是 Mac 系统
- // 获取创建 IoC 使用的 beanFactory ,IoC 容器进行 bean 创建的工厂
- final ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
- // 获取类加载器
- final ClassLoader classLoader = context.getClassLoader();
- // 获取 bean 的定义
- final BeanDefinitionRegistry registry = context.getRegistry();
- final Environment environment = context.getEnvironment();
- if(environment.getProperty("os.name").contains("Mac")) {
- return true;
- }
- return false;
- }
- }
我这里只贴出来 Mac 系统的 Conditional 的实现. 咱们来看下具体的内容. 首选就是它的两个参数.
ConditionContext context: 只得是判断条件使用的上下文环境, 看下他的源码就知道, 它里面的一些信息:
- public interface ConditionContext {
- /**
- * Return the {@link BeanDefinitionRegistry} that will hold the bean definition
- * should the condition match.
- * @throws IllegalStateException if no registry is available (which is unusual:
- * only the case with a plain {@link ClassPathScanningCandidateComponentProvider})
- */
- BeanDefinitionRegistry getRegistry();
- /**
- * Return the {@link ConfigurableListableBeanFactory} that will hold the bean
- * definition should the condition match, or {@code null} if the bean factory is
- * not available (or not downcastable to {@code ConfigurableListableBeanFactory}).
- */
- @Nullable
- ConfigurableListableBeanFactory getBeanFactory();
- /**
- * Return the {@link Environment} for which the current application is running.
- */
- Environment getEnvironment();
- /**
- * Return the {@link ResourceLoader} currently being used.
- */
- ResourceLoader getResourceLoader();
- /**
- * Return the {@link ClassLoader} that should be used to load additional classes
- * (only {@code null} if even the system ClassLoader isn't accessible).
- * @see org.springframework.util.ClassUtils#forName(String, ClassLoader)
- */
- @Nullable
- ClassLoader getClassLoader();
- }
其中: BeanDefinitionRegistry getRegistry() 就是返回这个 Bean 的注册信息. BeanDefinitionRegistry getRegistry() 看他的定义可以看出来里面的一些关于 Bean 的方法, 注册 Bean:registerBeanDefinition, 移除 Bean:removeBeanDefinition, 获取 Bean 的定义: getBeanDefinition, 是否包含 Bean:containsBeanDefinition 等等, 关于 bean 的一切方法, 具体的实现跟进去能看到是一个 CurrentHashMap 存放咱们定义的各种 Bean. 默认的 Key 是咱们的定义的 Bean 的首字母小写, Scope 是默认的単例. 大家感兴趣的可以跟进去看看.
关于第二个参数: AnnotatedTypeMetadata metadata 他是标注了注解类中作用在该注解上的所有的的注解信息. 这里我们可以获取到注解类中的所有的注解信息. 也可以获取到该注解作用域下的方法名称等信息
咱们在 Conditional 中使用的条件是要是运行环境格式不同操作系统就加载对应的 Bean.
- @Test
- public void test04() {
- ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigConditional.class);
- final String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
- for (String beanDefinitionName : beanDefinitionNames) {
- System.out.println(beanDefinitionName);
- }
- final Map<String, PersonConditonal> beansOfType = applicationContext.getBeansOfType(PersonConditonal.class);
- System.out.println(beansOfType);
- // 获取操作系统 环境变量 Mac OS X
- final Environment environment = applicationContext.getEnvironment();
- final String property = environment.getProperty("os.name");
- System.out.println(property);
- }
咱们运行下看下返回的结果:
image.PNG
到此咱们在类加载的时候整理了使用 Conditional 方式更具条件判断加载符合条件的 Bean 进入容器中.
2, 使用 Import 加载 Bean
上面咱们整理了 Conditional 加载 Bean, 下面咱们学习整理下使用 @Import 快速加载一个 Bean 进入容器.
这里有三个关联的快速加载信息, 之前咱们加载 Bean 都是使用扫描 + 注解 或者 @Bean 进行的, 不管是根据条件筛选还是判断加载.@Import 是独立的第三种给容器加载 Bean 的方法:
2.1,
直接看代码:
- /**
- * Created by luyang.li on 19/3/12.
- */
- @Configuration
- @Import({Color.class, Red.class}) // 快速导入组件, 组件的 Id 就是类的全限定名 com.annotation.config.configImport.Color
- public class MainConfigImport {
- // @Bean
- // public Person person03() {
- // return new Person("ly", 21);
- // }
- }
我直接在注解类上添加了 @Import 注解, 他是可以接受多个添加属性的.
看源码定义是可接受类型数组. 注释中有两个特别有效的信息. 待会说.
咱们运行下单侧可以看见这两个定义的 Bean 已经加载到容器中了.
这里需要注意的一点就是她加载进容器的 Bean 是类的全限定名, 而不是普通的默认的类名首字母小写.
2.2,@ImportSelector 加载自己选择的 Bean
使用 @ImportSelector 加载 Bean 就是实现自定义的 ImportSelector 接口返回要加载类的全限定名:
- /**
- * Created by luyang.li on 19/3/12.
- */
- @Configuration
- @Import({Color.class, Red.class, MyImporSelector.class, MyImportBeanDefinitions.class}) // 快速导入组件, 组件的 Id 就是类的全限定名 com.annotation.config.configImport.Color
- public class MainConfigImport {
- }
- /**
- * 返回需要加载的组件的全类名数组.
- *
- */
- public class MyImporSelector implements ImportSelector {
- /**
- * @param importingClassMetadata 当前标注了 @Import 注解类的所有注解信息
- * @return 返回需要导入到容器中的类全限定名数组
- */
- @Override
- public String[] selectImports(AnnotationMetadata importingClassMetadata) {
- final Set<String> annotationTypes = importingClassMetadata.getAnnotationTypes();
- // if (importingClassMetadata.hasAnnotation("org.springframework.context.annotation.Import")) {
- // }
- if(importingClassMetadata.getClass().getName().equals("com.annotation.config.configImport.Blue")) {
- return new String [] {"com.annotation.config.configImport.Blue"};
- }
- return new String[] {"com.annotation.config.configImport.Blue"};
- }
- }
这样就可以加载咱们自定义的类啦.
2.3,ImportBeanDefinitions
可以根据条件来加载 Bean: 手动注册 Bean.
- public class MyImportBeanDefinitions implements ImportBeanDefinitionRegistrar {
- /**
- *
- * @param importingClassMetadata 当前类的注解信息, 和其他信息
- * @param registry BeanDefinitionRegistry 的注册类
- *
- * 把所有要添加进容器的 bean 调用, registry.registerBeanDefinition() 进行手动注册
- */
- @Override
- public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
- final boolean blue = registry.containsBeanDefinition("com.annotation.config.configImport.Blue");
- if (blue) {
- // 定义 bean 信息,(bean Bean 类型,...)
- final RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(RainBow.class);
- // 注册一个 bean, 指定 bean 名
- registry.registerBeanDefinition("rainBow", rootBeanDefinition);
- }
- }
- }
主要是这个方法: registry.registerBeanDefinition("rainBow", rootBeanDefinition); 哈市注册 bean , 只是这里是手动注册, 源码实现哈时候在 CurrentHashMap 中注册一个自定义的 Bean. 如果存在的话就会报一些检查异常.
来源: http://www.jianshu.com/p/4f595029cd7d