上一篇文章咱们整理的注解驱动的几种方式,今天接着学习剩下的注解驱动。
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。如果存在的话就会报一些检查异常。
网友评论