Spring注解驱动开发学习总结4:组件注册之@Import、FactoryBean详解

1、使用@Import快速导入组件

给容器中注入组件的3种方式
1、报扫描+组件标注注解(@Controller/ @Service/ @Reposiroty/ @Component)
这种方式适合导入自己创建的组件
2、@Bean
这种方式适合导入第三方的组件。
3、@Import
由于@Bean,每次需要new一个组件的实例,如果我们只需要导入该组件,并不需要初始化等操作,那么就不用使用@Bean了,可以直接在配置类的上面使用@Import快速导入一个组件
  3.1 Import:容器可以自动注册组件
  查看Import注解,可以看到参数value可以可以传入Configuration, ImportSelector, ImportBeanDefinitionRegistrar

public @interface Import {
	/**
	 * {@link Configuration}, {@link ImportSelector}, 
	 * {@link ImportBeanDefinitionRegistrar}
	 */
	Class<?>[] value();
}

  3.2 自定义ImportSelector:返回需要导入的组件的全类名的数组
    查看ImportSelector接口,可以看到需要实现selectImports方法,该方法会返回需要导入的组件名的数组。
    参数AnnotationMetadata:标注了@Import的类的所有的注解信息

public interface ImportSelector {
	/**
	 * Select and return the names of which class(es) should be imported based on
	 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);
}

  3.3 自定义ImportBeanDefinitionRegistrar:该类只需要实现registerBeanDefinitions方法,该方法有2个入参:
    参数importingClassMetadata:当前类的注解信息。
    参数registry:所有bean定义的注册类。可以使用该参数的registerBeanDefinition方法,将需要注入容器的类进行手工导入

public interface ImportBeanDefinitionRegistrar {
	/**
	 * @param importingClassMetadata annotation metadata of the importing class
	 * @param registry current bean definition registry
	 */
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

1.1 构建外部类

新建5个颜色类和一个彩虹类,我们假设这6个类是外部类,等会我们需要导入他们到组件中。

新建5个颜色类:
红色类:com/example/bean/Red.java,
黄色类:com/example/bean/Yellow.java,
蓝色类:com/example/bean/Blue.java,
绿色类:com/example/bean/Green.java,
紫色类:com/example/bean/Purple.java
然后再新建一个彩虹类:com/example/bean/Rainbow.java

package com.example.bean;

public class Red {
}

1.2 构建配置类

新构建配置类:com/example/config/MainConfig3.java
通过@Bean的方法注入红色类。
然后通过@Import注解的方式注入黄色类和蓝色类。

package com.example.config;

import com.example.bean.Blue;
import com.example.bean.Red;
import com.example.bean.Yellow;
import com.example.condition.MyImportSelector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Import({Yellow.class, Blue.class})
@Configuration
public class MainConfig3 {

    @Bean
    public Red red() {
        return new Red();
    }
}

1.3 构建自定义ImportSelector实现类

构建自定义ImportSelector实现类:com/example/condition/MyImportSelector.java。

该类只需要实现一个方法:selectImports。该方法也只有一个入参AnnotationMetadata:该参数是标注了@Import的类的所有的注解信息。我们在这里打印以下对应的信息。

返回的是需要导入的组件的全类名的数组,因此这里我们写上绿色类和紫色类的数组。相当于导入了这2个类。

package com.example.condition;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.Set;

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 打印当前类的注解信息
        String className = importingClassMetadata.getClassName();
        Set<String> annotationTypes = importingClassMetadata.getAnnotationTypes();
        System.out.println("MyImportSelector-当前类的名称为: "+className);
        System.out.println("MyImportSelector-当前类的注解有: "+annotationTypes);

        return new String[]{"com.example.bean.Green", "com.example.bean.Purple"};
    }
}

1.4 构建自定义ImportBeanDefinitionRegistrar实现类

构建自定义ImportBeanDefinitionRegistrar实现类:com/example/condition/MyImportBeanDefinitionRegistrar.java。

该类只需要实现一个方法:registerBeanDefinitions。

registerBeanDefinitions方法有2个参数:
  参数importingClassMetadata:当前类的注解信息。
  参数registry:所有bean定义的注册类。可以使用该参数的registerBeanDefinition方法,将需要注入容器的类进行手工导入

这里,我们利用registry类判断下容器中是否有红黄蓝绿紫这5种颜色的类,如果同时都存在,那么就将Rainbow类注入,并且将id设置为"彩虹bean"。

package com.example.condition;

import com.example.bean.Rainbow;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean red = registry.containsBeanDefinition("red");
        boolean yellow = registry.containsBeanDefinition("com.example.bean.Yellow");
        boolean blue = registry.containsBeanDefinition("com.example.bean.Blue");
        boolean green = registry.containsBeanDefinition("com.example.bean.Green");
        boolean purple = registry.containsBeanDefinition("com.example.bean.Purple");

        // 如果红色、黄色、蓝色、绿色、紫色都存在的情况下,就给容器注入彩红bean
        if (red && yellow && blue && green && purple) {
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Rainbow.class);
            registry.registerBeanDefinition("彩虹bean", rootBeanDefinition);
        }
    }
}

1.5 配置类的@Import新增自定义的导入类

配置类的@Import新增自定义的导入类:MyImportSelector和MyImportBeanDefinitionRegistrar。

package com.example.config;

import com.example.bean.Blue;
import com.example.bean.Red;
import com.example.bean.Yellow;
import com.example.condition.MyImportBeanDefinitionRegistrar;
import com.example.condition.MyImportSelector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Import({Yellow.class, Blue.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
@Configuration
public class MainConfig3 {

    @Bean
    public Red red() {
        return new Red();
    }
}

1.6 测试

构建测试方法:testImport。在测试方法里打印以下目前容器里的所有的bean的定义名称

	@Test
    public void testImport() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
        System.out.println("\nioc容器已创建完成\n");

        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }

以下为运行结果:
1)可以看到通过@Bean导入的red,成功加入了容器中;
2)可以看到通过@Import({Yellow.class, Blue.class}),
Yellow和Blue也成功导入了,并且比@Bean的方式,少了很多代码。只不过它的bean的名称是全类路径名。而@Bean默认的bean名称是方法名。
3)可以看到通过@Import({MyImportSelector.class}),
Green和Purple也成功导入了。

同时MyImportSelector也打印了配置类MainConfig3的注解信息有:annotation.Import和annotation.Configuration。
4)可以看到通过@Import({MyImportBeanDefinitionRegistrar.class}),
Rainbow类也成功导入了,并且bean的名称就是我们设置的:彩虹bean。
在这里插入图片描述

通过上面,在导入其他类时,我们展示了4种方式:@Bean和@Import中的3种方式

2、使用FactoryBean进行注入

除了上一小结提到的3种方式,还可以通过FactoryBean的方式注入bean
1、查看FactoryBean接口,需要实现3个方法:
1)getObject:返回bean实例;
2)getObjectType:返回bean的类型;
3)isSingleton:返回该bean是否是单例的;
2、默认获取到的是工厂bean调用getObject方法返回的对象实例;
如果想要获取的是工厂bean本身的话,需要在id前面加一个&

public interface FactoryBean<T> {
	T getObject() throws Exception;
	Class<?> getObjectType();
	boolean isSingleton();
}

小节1是通过@Import导入的组件,本小节通过FactoryBean来导入。

2.1 构建一个FactoryBean

构建一个RainbowFacotryBean,让其实现FactoryBean接口。

package com.example.bean;

import org.springframework.beans.factory.FactoryBean;

public class RainbowFacotryBean implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        System.out.println("RainbowFacotryBean_getObject方法执行了");
        return new Rainbow();
    }

    @Override
    public Class<?> getObjectType() {
        System.out.println("RainbowFacotryBean_getObjectType方法执行了");
        return Rainbow.class;
    }

    @Override
    public boolean isSingleton() {
        System.out.println("RainbowFacotryBean_isSingleton方法执行了");
        return true;
    }
}

2.2 配置类中添加RainbowFacotryBean

还是在刚刚的配置类MainConfig3中进行修改,将@Import先注释掉。

package com.example.config;

import com.example.bean.Blue;
import com.example.bean.RainbowFacotryBean;
import com.example.bean.Red;
import com.example.bean.Yellow;
import com.example.condition.MyImportBeanDefinitionRegistrar;
import com.example.condition.MyImportSelector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//@Import({Yellow.class, Blue.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
@Configuration
public class MainConfig3 {
    @Bean
    public RainbowFacotryBean rainbowFacotryBean() {
        return new RainbowFacotryBean();
    }
}

2.2 构建测试方法

构建测试方法:testFactoryBean。
在该方法中打印下rainbowFacotryBean和&rainbowFacotryBean的类型,其次查看下rainbowFactoryBean1和rainbowFactoryBean2是否相等

	@Test
    public void testFactoryBean() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
        System.out.println("\nioc容器已创建完成\n");

        Object rainbowFactoryBean1 = applicationContext.getBean("rainbowFacotryBean");
        Object bean = applicationContext.getBean("&rainbowFacotryBean");
        System.out.println(rainbowFactoryBean1.getClass());
        System.out.println(bean.getClass());

        Object rainbowFactoryBean2 = applicationContext.getBean("rainbowFacotryBean");
        System.out.println(rainbowFactoryBean1==rainbowFactoryBean2);
    }

2.3 测试

运行测试方法:testFactoryBean,结果如下图。
可以看到,id为rainbowFacotryBean的对象的类型为:class com.example.bean.Rainbow,
而id为&rainbowFacotryBean的对象的类型为:class com.example.bean.RainbowFacotryBean。

另外,由于我们设置了isSingleton方法为true,因此rainbowFactoryBean1和rainbowFactoryBean2是同一个对象。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值