一、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