Spring源码解读(二)

Spring源码解读(二)

1. @Conditional条件注册bean

实现功能:当用户使用的OS是windows时,向容器中注入amos这个bean;当用户使用linux时向容器中注入amosWong这个bean。

配置类

@Configuration
public class Cap5MainConfig {

    //默认单例模式 在容器创建完成之前 创建需要注入的对象
    @Bean
    public User user(){
        System.out.println("创建对象user......");
        return new User(2L, "cap2 config");
    }
    //实现在IOC容器注入bean时,
    //当操作系统为WINDOWS时,注册amos实例; 当操作系统为LINUX时注册amosWong实例
    //此时要用得@Conditional注解进行定制化条件选择注册bean
    @Bean
    @Conditional(WinCondition.class)
    public User amos(){
        System.out.println("创建对象amos......");
        return new User(3L, "amos");
    }

    @Bean
    @Conditional(LinuxCondition.class)
    public User amosWong(){
        System.out.println("创建对象amosWong......");
        return new User(4L, "amosWong");
    }
}

LinuxCondition条件类

用来处理操作系统为Linux的bean注册

//新建LinuxCondition.java类做为条件类, 同时必须得实现spring提供的Condition接口
public class LinuxCondition implements Condition {

    /**
     *根据给定的条件进行匹配 如果返回true则表示匹配成功  返回false匹配失败
     * @param context 当前环境(上下文)
     * @param metadata 注解的信息
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //1.先获取IOC的beanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //2.获取类加载器
        ClassLoader ClassLoader = context.getClassLoader();
        //3.获取当前的系统环境
        Environment environment = context.getEnvironment();
        //4.获取bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();
        //获取系统的名称
        String property = environment.getProperty("os.name");
        //判断是否为Linux 如果是则匹配成功
        if (property.contains("Linux")){
            return true;
        }
        return false;
    }
}

WinCondition条件类

用来处理操作系统为Windows的bean注册

public class WinCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        ClassLoader classLoader = context.getClassLoader();
        Environment environment = context.getEnvironment();
        BeanDefinitionRegistry registry = context.getRegistry();
        String property = environment.getProperty("os.name");
        if (property.contains("Windows")){
            return true;
        }
        return false;
    }
}

测试类

public class Cap5Test {

    public static void main(String[] args) {
        //创建容器
        AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap5MainConfig.class);
        System.out.println("创建容器完成.....");
        //把IOC容器里的所有user实例名打印出来
        String[] beanNames = app.getBeanDefinitionNames();
        for (String beanName : beanNames){
            System.out.println(beanName);
        }
    }
}

运行结果

当前系统为win所以只会注入amos这个bean

创建对象user......
创建对象amos......
创建容器完成.....
cap5MainConfig
user
amos

使用IDEA更改测试参数 image.png

测试结果

创建对象user......
创建对象amosWong......
创建容器完成.....
cap5MainConfig
user
amosWong

2. @Import注册bean

2.1 普通方法注册bean

创建实体类

image.png

配置类

@Configuration
//通过@Import注册Cat和Dog两个bean
@Import(value = {Cat.class, Dog.class})
public class Cap6MainConfig {

    /*
     * 给容器中注册组件的方式
     * 1,@Bean: [导入第三方的类或包的组件],比如Person为第三方的类, 需要在我们的IOC容器中使用
     * 2,包扫描+组件的标注注解(@ComponentScan:  @Controller, @Service  @Repository  @ Component),一般是针对 我们自己写的类,使用这个
     * 3,@Import:[快速给容器导入一个组件] 注意:@Bean有点简单
     *      a,@Import(要导入到容器中的组件):容器会自动注册这个组件,bean 的 id为全类名
     *      b,ImportSelector:是一个接口,返回需要导入到容器的组件的全类名数组
     *      c,ImportBeanDefinitionRegistrar:可以手动添加组件到IOC容器, 所有Bean的注册可以使用BeanDifinitionRegistry
     *          写JamesImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar接口即可
     *  4,使用Spring提供的FactoryBean(工厂bean)进行注册
     *
     *
     */
    //容器启动时初始化person的bean实例
    //默认单例模式 在容器创建完成之前 创建需要注入的对象
    @Bean
    public User user(){
        System.out.println("创建对象user......");
        return new User(2L, "cap2 config");
    }
}

测试类

public class Cap6Test {

    public static void main(String[] args) {
        //创建容器
        AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap6MainConfig.class);
        System.out.println("创建容器完成.....");
        //把IOC容器里的所有实例名打印出来
        String[] beanNames = app.getBeanDefinitionNames();
        for (String beanName : beanNames){
            System.out.println(beanName);
        }
    }
}

测试结果

cap6MainConfig
pers.amos.cap6.pojo.Cat
pers.amos.cap6.pojo.Dog
user

总结

给容器中注册组件的方式:

  • 包扫描+组件标注注解(@Controller、@Service、@Repository、@Component)

    • 应用场景:一般针对于我们自己写的类
  • @Bean

    • 导入第三方类或者包的组件,比如User类为第三方,需要我们IOC容器中使用
  • @Import[快速给容器导入一个组件] @Bean有点简单,用于构造一个无参或者有参的bean

    • @Import(要导入到容器中的组件):容器中就会自动注册这个组件,id是全类名
    • ImportSelector:是一个接口,返回需要导入的组件的全类名数组
    • ImportBeanDefinitionRegistrar的方法registerBeanDefinitions(),可以实现手动添加组件到IOC容器。所有Bean的注册都可以使用BeanDefinitionRegistry进行注册。写一个关于ImportBeanDefinitionRegistrar的实现类 AmosImportBeanDefinitionRegistrar
  • 使用Spring提供的FactoryBean(工厂bean)

    • 在FactoryBean类中,容器调用getObejct()方法返回对象;使用getObjectType()方法返回对象类型;使用isSingleton()方法控制该bean是否为单例。
    • 新建AmosFactoryBean实现FactoryBean在config里新建amosFactoryBean()方法

2.2 使用ImportSelector接口的实现类注册bean

新建实体类

image.png

ImportSelector接口的实现类

批量导入组件的全类名数组,批量导入bean到IOC容器中

/**
   * importingClassMetadata:当前标注@Import注解类的所有注解信息,
   * 不止能获取到import注解还能获取到其他注解
   * @param importingClassMetadata
   * @return 返回值就是导入到容器中的组件全限定名
 */
//自定义逻辑返回需要导入的组件
public class JamesImportSelector implements ImportSelector{
//返回的值就是导入到组件中的类全限定名
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata){
		//返回全类名的bean
		return new String[]{"pers.amos.cap6.pojo.Fish","pers.amos.cap6.pojo.Tiger"};
	}
}

配置类

@Configuration
//使用Import把四个实体类全部导入到IOC容器中
//它们的名称都为全限定名
@Import(value = {Cat.class, Dog.class, AmosImportSelector.class})
public class Cap6MainConfig {
    //容器启动时初始化person的bean实例
    //默认单例模式 在容器创建完成之前 创建需要注入的对象
    @Bean
    public User user(){
        System.out.println("创建对象user......");
        return new User(2L, "cap2 config");
    }
}

测试类

public class Cap6Test {

    public static void main(String[] args) {
        //创建容器
        AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap6MainConfig.class);
        System.out.println("创建容器完成.....");
        //把IOC容器里的所有实例名打印出来 其中通过选择器注入的两个bean的名称为其全限定名
        String[] beanNames = app.getBeanDefinitionNames();
        for (String beanName : beanNames){
            System.out.println(beanName);
        }
    }
}

测试结果


cap6MainConfig
pers.amos.cap6.pojo.Cat
pers.amos.cap6.pojo.Dog
pers.amos.cap6.pojo.Fish
pers.amos.cap6.pojo.Tiger
user

2.3 ImportBeanDefinitionRegistrar自定义注册

ImportBeanDefinitionRegistrar接口实现类

public class AmosImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    /*
     *AnnotationMetadata:当前类的注解信息
     *BeanDefinitionRegistry:BeanDefinition注册类
     *    把所有需要添加到容器中的bean加入;
     *    调用registry.registerBeanDefinitions()方法自定义手工注册
     *    @Scope
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //调用方法 判断容器中是否含有Fish和Tiger类
        boolean  definition1 = registry.containsBeanDefinition("pers.amos.cap6.pojo.Fish");
        boolean  definition2 = registry.containsBeanDefinition("pers.amos.cap6.pojo.Tiger");
        //只有当Fish和Tiger类都存在时 我们才去注入新创建的Pig类
        if (definition1 && definition2){
            //以前的bean的id都是全限定名,这次我们自定义Pig的id
            //Spring通过registerBeanDefinition方法注册bean,第二个参数为BeanDefinition
            //指定bean的定义信息 (bean类型,bean的scope)
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Pig.class);
            registry.registerBeanDefinition("pig", rootBeanDefinition);
        }

    }
}

配置类更改的内容

@Import(value = {Cat.class, Dog.class, AmosImportSelector.class, AmosImportBeanDefinitionRegistrar.class})

测试结果

可以看到pig的id为我们自定义的名称Pig而不是类的全限定名

cap6MainConfig
pers.amos.cap6.pojo.Cat
pers.amos.cap6.pojo.Dog
pers.amos.cap6.pojo.Fish
pers.amos.cap6.pojo.Tiger
user
pig

2.4 FactoryBean接口方式注册bean

FactoryBean的方法:

image.png

  • getObject()返回对象,把对象放到容器中;
  • getObjectType()返回对象类型
  • isSingleton()是否单例进行控制

创建FactoryBean的实现类

//创建一个Spring定义的工厂bean
public class AmosBeanFactory implements FactoryBean<Monkey> {

    //返回一个monkey对象,并且把这个对象添加到容器中
    @Override
    public Monkey getObject() throws Exception {
        return new Monkey();
    }

    //指定对象的类型
    @Override
    public Class<?> getObjectType() {
        return Monkey.class;
    }

    //该对象是否为单例
    @Override
    public boolean isSingleton() {
        return true;
    }
}

把自定义的AmosImportBeanDefinitionRegistrar加入配置类进行测试

@Import(value = {Cat.class, Dog.class, AmosImportSelector.class, AmosImportBeanDefinitionRegistrar.class, AmosBeanFactory.class})

测试结果

cap6MainConfig
pers.amos.cap6.pojo.Cat
pers.amos.cap6.pojo.Dog
pers.amos.cap6.pojo.Fish
pers.amos.cap6.pojo.Tiger
pers.amos.cap6.config.AmosBeanFactory
user
pig

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×