Spring学习-注解驱动2

上一篇文章咱们整理的注解驱动的几种方式,今天接着学习剩下的注解驱动。

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。如果存在的话就会报一些检查异常。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,002评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,777评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,341评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,085评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,110评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,868评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,528评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,422评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,938评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,067评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,199评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,877评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,540评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,079评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,192评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,514评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,190评论 2 357