Spring源码学习笔记——核心注解

一、Configuration

被此注解标注的类会成为一个配置类,相当于 spring 的 spring.xml 配置文件

1、@Bean 

  • 在配置类中可以通过 @Bean 注解将对象注入 IOC 容器
    • 返回值类型相当于 xml 配置文件中的 class 属性
    • 方法名相当于 xml 配置文件中的 id 属性
// 标注该类是个配置类,相当于spring的xml配置文件
@Configuration
public class WxConfig {
    /**
     * @Bean 注解会将返回的对象注入到IOC容器中
     * class 就是返回值类型
     * id 就是方法名
     */
    @Bean
    public Person wxPerson() {
        return new Person("wx", 24);
    }
}

2、@ComponentScan

  • 在配置类中可以通过 @ComponentScan 注解指定包扫描
  • 该注解支持自定义扫描规则
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
  • includeFilters() 属性和 excludeFilters() 属性可以配置包含/排除扫描的包,其配置类型是一个 Filter 型数组,在 Filter 类中可以指定使用哪种方式扫描,默认是通过注解扫描包,如下所示
FilterType type() default FilterType.ANNOTATION;
  • 我们可以点进 FiterType 中,可以看到有一个 CUSTOM 的枚举类型,这个类型就表示我们使用自定义的过滤规则
/** Filter candidates using a given custom
 * {@link org.springframework.core.type.filter.TypeFilter} implementation.
 */
CUSTOM

2.1、自定义过滤规则

  • 使用自定义过滤规则需要创建一个 TypeFilter 接口的实现类,实现未实现的 match() 方法
public class MyTypeFilter implements TypeFilter {
    public boolean match(MetadataReader metadataReader, 
            MetadataReaderFactory metadataReaderFactory) throws IOException {
        return false;
    }
}
  • match() 方法中有两个参数,分别是 metadataReader 和 metadataReaderFactory
    • metadataReader :读取到的当前被扫描到的类信息
    • metadataReaderFactory :可以读取到其他类的信息

因此,我们可以通过 metadataReader 对象获取到当前类被标注的注解的信息,当前被扫描的类信息,当前被扫描的类资源信息,如下代码所示

/**
 * @param metadataReader : 读取到的当前被扫描到的类信息
 * @param metadataReaderFactory : 可以获取到其他类的信息
 */
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
    // 获取当前类注解的信息
    AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
    // 获取当前被扫描的类的信息
    ClassMetadata classMetadata = metadataReader.getClassMetadata();
    // 获取当前类资源信息
    Resource resource = metadataReader.getResource();
    // 为 true 则放行,表示匹配成功
    return false;
}
  • 我们可以自定义过滤规则,如 :只有名称中带 tro 的才扫描
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
    // 获取当前被扫描的类的信息
    ClassMetadata classMetadata = metadataReader.getClassMetadata();
    String className = classMetadata.getClassName();
    if (className.contains("tro")) {
        return true;
    }
    return false;
}

验证以上自定义过滤规则,我们可以通过 applicationContext 对象调用 getBeanDefinitionNames() 方法,获取到 IOC 容器中 bean 的一些定义信息

public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(WxConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String definitionName : beanDefinitionNames) {
            System.out.println("--->" + definitionName);
        }
    }

打印结果:

--->wxConfig
--->wxController

这里可以看到,除了符合过滤条件的 wxController 成功注入容器了,配置类 wxConfig 也注入了,这是因为 wxConfig 是配置类,在该配置类中配置了自定义过滤类,只有配置类先被加载,才会加载配置类中的内容,所以 wxConfig 也会注入到 IOC 容器中

3、@Conditional

该注解可以配置注册条件,满足条件时,才会注册进 IOC 容器中

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
   /**
    * All {@link Condition}s that must {@linkplain Condition#matches match}
    * in order for the component to be registered.
    */
   Class<? extends Condition>[] value();
}
  • 可以看到,该注解可以标注在类上,也可以标注在方法上
  • 其中有一个 Condition 数组类型的属性
public interface Condition {
   /**
    * Determine if the condition matches.
    * @param context the condition context
    * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
    * or {@link org.springframework.core.type.MethodMetadata method} being checked.
    * @return {@code true} if the condition matches and the component can be registered
    * or {@code false} to veto registration.
    */
   boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
  • 因此我们想要自定义注册规则,就需要创建 Condition 接口的实现类,并实现 matchs() 方法。matchs() 方法中有两个参数 :
    • ConditionContext :判断条件能使用的上下文
    • AnnotatedTypeMetadata :当前标注了 @Conditional 注解的一些注释信息
  • 我们可以从 ConditionContext 中获取到一些信息
/**
 * ConditionContext :判断条件能使用的上下文
 * AnnotatedTypeMetadata :当前标注了 @Conditional 注解的一些注释信息
 */
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    // IOC 容器使用到的 beanFactory 对象
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    // IOC 容器中 bean 定义的一些注册信息
    BeanDefinitionRegistry registry = context.getRegistry();
    // 类加载器
    ClassLoader classLoader = context.getClassLoader();
    // 获取当前环境信息,如 :运行时的一些信息,虚拟机中的变量等
    Environment environment = context.getEnvironment();
    // 获取当前操作系统名称
    String osName = environment.getProperty("os.name");
    return false;
}

现在我们可以实现自定义的注册条件,先创建一个配置类,在配置类中通过 @Bean 注解注入两个 Bean 到 IOC 容器中

@Configuration
public class WxConfig2 {
    @Bean("Bill")
    public Person person1() {
        return new Person("Bill gates", 60);
    }
    @Bean("Linus")
    public Person person2() {
        return new Person("Linus", 48);
    }
}
  • 现在我想要实现当前操作系统是 Windows 时,注册 Bill,当前操作系统是 Linux 时,注册 Linus,那么需要创建两个 Condition 接口的实现类
/**
 * 当前操作系统是 Windows 时,注册 Bill
 */
public class WindowsCondition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        if (environment.getProperty("os.name").contains("Windows")) {
            return true;
        }
        return false;
    }
}
/**
 * 当前操作系统是 Linux 时,注册 Linus
 */
public class LinuxCondition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        if (environment.getProperty("os.name").contains("Linux")) {
            return true;
        }
        return false;
    }
}
  • 在配置类中添加 @Conditional 注解,并在不同 Bean 上指定不同的自定义条件类
@Configuration
public class WxConfig2 {
    @Bean("Bill")
    @Conditional({WindowsCondition.class})
    public Person person1() {
        return new Person("Bill gates", 60);
    }

    @Bean("Linus")
    @Conditional({LinuxCondition.class})
    public Person person2() {
        return new Person("Linus", 48);
    }
}

执行并打印结果:

public class WxMain2 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(WxConfig2.class);
        Environment environment = applicationContext.getEnvironment();
        String property = environment.getProperty("os.name");
        System.out.println("当前操作系统为:" + property);
        String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
        for (String beanName : beanNamesForType) {
            System.out.println(beanName);
        }
    }
}

打印结果 :
当前操作系统为:Windows 10
Bill

值得注意的是,当 @Conditional 中配置了多个 Condition 的实现类时,需要多个条件都满足,才会将 bean 注册到 IOC 容器中,如下所示,在 Bean 名称为 Bill 的 @Conditional 注解中再加上 LinuxCondition.class 实现类

@Bean("Bill")
@Conditional({WindowsCondition.class, LinuxCondition.class})
public Person person1() {
    return new Person("Bill gates", 60);
}

打印结果:

当前操作系统为:Windows 10

可以看到,并没有打印出 Bill 这个 Bean 名称,因为不符合 LinuxCondition.class 的条件,因此没有注入到 IOC 容器中

此外,@Conditional 注解还支持放在类上,表示满足条件时,该类中配置的 bean 都会被注册到 IOC 容器中

@Configuration
@Conditional({WindowsCondition.class})
public class WxConfig2 {
    ... ...
}

打印结果 :
当前操作系统为:Windows 10
Bill
Linus

4、@Import

@Import 注解能够快速注入 bean 到 IOC 容器

@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 注解,并指定需要注册的 Bean

  • 使用 @Import 注解注册的 bean 在 IOC 容器中以类的全限定名作为 id
@Configuration
@Import({Red.class})
public class WxConfig3 {

}

打印结果 :
wxConfig3
com.wx.bean.Red

使用方式二:

实现 ImportSelector 接口,并指定需要注册的 bean

// ImportSelector 实现类
public class WxImportSelector implements ImportSelector {
    /**
     * AnnotationMetadata:可以获取到被标注@Import注解的类上的所有注解信息
     */
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.wx.bean.Blue", "com.wx.bean.Yellow"};
    }
}

// 配置类
@Configuration
@Import({WxImportSelector.class})
public class WxConfig3 {

}

打印结果 :
wxConfig3
com.wx.bean.Blue
com.wx.bean.Yellow

使用方式三:

实现 ImportBeanDefinitionRegistrar 接口,通过 BeanDefinitionRegistrar 注册 bean

public class WxImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * AnnotationMetadata :可以获取到标注@Import注解的类上所有注解的信息
     * BeanDefinitionRegistry :bean定义注册类,可以通过该对象手动定义
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 判断IOC容器中是否存在blue,存在则将color注册到IOC容器中
        boolean hasColor = registry.containsBeanDefinition("com.wx.bean.Blue");
        if (hasColor) {
            // RootBeanDefinition是BeanDefinition的一个实现子类,将bean注册到IOC容器中需要定义该对象
            BeanDefinition beanDefinition = new RootBeanDefinition(Color.class);
            registry.registerBeanDefinition("color", beanDefinition);
        }
    }
}

@Configuration
@Import({Blue.class, WxImportBeanDefinitionRegistrar.class})
public class WxConfig3 {

}

打印结果 :
wxConfig3
com.wx.bean.Blue
color

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值