四)初始Spring源码以及常用组件
目录
一, @Conditional 注解
- 条件注册bean,根据指定条件选择性地注册bean实例
4.1.1,什么叫做条件注册bean?
模拟以下场景: 我在当操作系统为 windows 的时候 @Bean(lison)实例, 而在linux的时候 @Bean("james")
示例代码 4.1.1:
@Conditional(WinCondition.class)
@Bean("lison")
public Person lison(){
System.out.println("给容器中添加lison.......");
return new Person("Lison",58);
}
@Conditional(LinCondition.class)
@Bean("james")//bean在容器中的ID为james, IOC容器MAP, map.put("id",value)
public Person james(){
System.out.println("给容器中添加james.......");
return new Person("james",20);
}
@Conditional(WinCondition.class) WinCondition.class 是我自己定义的一个过滤类, 这个需要实现 Condition 接口,
在Spring 源码中 拿到bean 的信息, 都是 通过 BeanFactory() , 而 注册 都是通过 FactoryBean() ,获得操作 系统 可以通过 Spring 上下文, 也可以通过 System.getProperty("os.name") , 里面 的 参数 os.name 是定义好的
public class WinCondition implements Condition{
/*
*ConditionContext: 判断条件可以使用的上下文(环境)
*AnnotatedTypeMetadata: 注解的信息
*
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO 是否为WINDOW系统
//能获取到IOC容器正在使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//获取当前环境变量(包括我们操作系统是WIN还是LINUX??)
Environment environment = context.getEnvironment();
String os_name = environment.getProperty("os.name");
if(os_name.contains("Windows")){
return true;
}
return false;
}
}
@Conditional(LinCondition.class)
public class LinCondition implements Condition{
/*
*ConditionContext: 判断条件可以使用的上下文(环境)
*AnnotatedTypeMetadata: 注解的信息
*
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO 是否为WINDOW系统
//能获取到IOC容器正在使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//获取当前环境变量(包括我们操作系统是WIN还是LINUX??)
Environment environment = context.getEnvironment();
String os_name = environment.getProperty("os.name");
if(os_name.contains("linux")){
return true;
}
return false;
}
进行测试,得到我们想要的结果:
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap7MainConfig.class);
System.out.println("IOC容器创建完成.........");
//app.getBean("person");//执行获取的时候才创建并初始化bean
String[] beanDefinitionNames = app.getBeanDefinitionNames();
for(String name:beanDefinitionNames){
System.out.println(name);
}
}
lison 已经根据我们的条件注入bean
二, @Import注解 注册组件
- 手动添加组件到Ioc容器;
- 使用 ImportSelector 自定义返回组件
- 使用 ImportBeanDefinitionRegistrar返回自定义组件
4.2.1,给容器中注册组件的方式:
- @Bean 导入第三方的类或包的组件,比如 Person 为第三方类,需要在我们的 IOC 容器 中使用,包扫描+组件的标注注解(@ComponentScan : @Contorller @Service @Reponsitory @Componet ,一般是针对我们自己写的类,使用这个)
- @Import : 快速给容器导入一个组件,注意: @Bean 有点简单,
- @Import (要导入到容器中的组件) 容器会自动注册这个组件,bean的 id为全类名,
- ImportSelector : 是一个接口,返回需要导入到容器的组件的全类名数组
- ImportBeanDefinitionRegistrar : 可以手动添加组件到IOC容器,所有Bean的注册可以使用BeanDefinitionRegeistry写JamesImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar接口即可
- 使用Spring提供的FactoryBean(工厂bean)进行注册
4.2.1 示例源码: 定义 6 个类, 里面没有任何操作:见 项目源码---》 cap6
我们使用FactoryBean(工厂bean)进行注册: 将上面我们新建的 Monkey 类 注册到 工厂bean
public class JamesFactoryBean implements FactoryBean<Monkey>{
@Override
public Monkey getObject() throws Exception {
// TODO Auto-generated method stub
return new Monkey();
}
@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return Monkey.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
我们使用 ImportBeanDefinitionRegistrar 添加组件, 实现 ImportBeanDefinitionRegistrar 接口, 重写 registerBeanDefinitions
方法 , AnnotationMetadata 参数 是当前类的注解信息, BeanDefinitionRegistry 即 BeanDefinition注册类。 将 dog, cat 两个类注册到 容器中去
public class JamesImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/*
*AnnotationMetadata:当前类的注解信息
*BeanDefinitionRegistry:BeanDefinition注册类
* 把所有需要添加到容器中的bean加入;
* @Scope
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean bean1 = registry.containsBeanDefinition("com.enjoy.cap6.bean.Dog");
boolean bean2 = registry.containsBeanDefinition("com.enjoy.cap6.bean.Cat");
//如果Dog和Cat同时存在于我们IOC容器中,那么创建Pig类, 加入到容器
//对于我们要注册的bean, 给bean进行封装,
if(bean1 && bean2){
RootBeanDefinition beanDefinition = new RootBeanDefinition(Pig.class);
registry.registerBeanDefinition("pig", beanDefinition);
}
}
@ImportSelectot 接口 返回的是一个数组, 里面可以添加多个 类, 一起注册到 容器中去,
public class JamesImportSelector implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata){
//返回全类名的bean
return new String[]{"com.enjoy.cap6.bean.Fish","com.enjoy.cap6.bean.Tiger"};
}
}
测试: -----》 项目源码: cap6Test
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap6MainConfig.class);
System.out.println("IOC容器创建完成........");
//如果返回true,说明是单例,返回false,说明是多例。。
Object bean1 = app.getBean("&jamesFactoryBean");
Object bean2 = app.getBean("jamesFactoryBean");//取Money
System.out.println("bean的类型="+bean1.getClass());
System.out.println(bean1 == bean2);
String[] beanDefinitionNames = app.getBeanDefinitionNames();
for(String name:beanDefinitionNames){
System.out.println(name);
}
三,项目 Demo地址
Spring源码深度解析,(附代码示例 码云地址: https://gitee.com/Crazycw/SpringCode.git)
参考资料: https://docs.spring.io/spring/docs/4.3.18.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/
请看下篇: Spring源码深度解析,初始Spring源码----Bean 生命周期(五)(附代码示例:)