1、使用@Bean注解:
之前的一篇文章讲解过关于如何使用@Bean给容器中注册组件,可以参考:
Spring注解(一):@Configuration、@Bean给容器中注册组件
2、使用包扫描、组件标注注解的方式
采用@Controller/@Service/@Repository/@Component注解,这种方式只局限于自己写的类,不能用于导入第三方包,可以参考之前的文章:
Spring注解(二):@ComponentScan自动扫描组件
3、使用@import注册组件:
首先这里可以声明一个Role类:
package com.xinyi.bean; public class Role { } 复制代码
通过测试类输出容器中注入的bean:
@Test public void test4() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig1.class); String[] names = applicationContext.getBeanDefinitionNames(); for(String name:names) { System.out.println(name); } } 复制代码
这是很可以发现Role并没有被注入进IOC容器。
接着在配置类上添加@Import注解:
@Import(Role.class) 复制代码
具体的代码如下所示:
@Configuration @Import(Role.class) public class MyConfig1 { @Bean("Lin Sin") @Lazy public Person person() { return new Person("李青",18); } @Bean("Yasuo") public Person person1() { return new Person("亚索",23); } @Bean("Zed") public Person person2() { return new Person("劫",32); } } 复制代码
此时Role被注入进了IOC容器,id为类的全类名。
这里使用的是@Import注解,import注解的源码如下:
public @interface Import { /** * {@link Configuration @Configuration}, {@link ImportSelector}, * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import. */ //数组 Class<?>[] value(); } 复制代码
根据源码可以发现@import注解后面的参数值可以是数组,所以使用@import注解可以同时注入多个组件,比如
@Import(Role1.class,Role2.class,......) 复制代码
除了直接使用@import注解之外,还可使用ImportSelector(导入选择器)来根据条件进行注册bean,ImportSelector返回需要注册的全类名,这也是SpringBoot开发中bean注册的主要方式。
声明一个MyImportSelector类并实现ImportSelector接口,自定义导入选择器,返回需要注册的组件。
//自定义导入选择器,返回需要注册的组件 public class MyImportSelector implements ImportSelector { //返回值就是匹配的要注册的组件(全类名) //AnnotationMetadata:当前标注@import注解的类的所有注解信息 public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.xinyi.bean.Role1","com.xinyi.bean.Role2"}; } } 复制代码
ImportSelector 接口的源码如下,返回的是一个string数组,数组中就是匹配的要注册的组件(全类名):
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); } 复制代码
在注册类中@import注解添加MyImportSelector:
@Import(Role.class,MyImportSelector.class) 复制代码
返回结果如下,Role1和Role2被注入IOC容器中,bean的名称则是组件全类名。
另外还可以使用ImportBeanDefinitionRegistrar接口来手动自定义注册的bean名称。
新建一个Role3类和MyImportBeanDefinitionRegistrar 类并实现ImportBeanDefinitionRegistrar接口:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{ /** * AnnotationMetadata:当前类的注解信息 * BeanDefinitionRegistry:bean定义的注册类信息 * 需要注册到容器中的bean调用BeanDefinitionRegistry.registryBeanDefinition */ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //设置注入条件:containsBeanDefinition的参数是组件的全类名 boolean flag = registry.containsBeanDefinition("com.xinyi.bean.Role"); //如果容器中包含Role组件,则将Role3注入进IOC容器 if(flag) { RootBeanDefinition beanDefinition = new RootBeanDefinition(Role3.class); registry.registerBeanDefinition("自定注入的Bean名称", beanDefinition); } } } 复制代码
在注册类文件中的@import注解增加MyImportBeanDefinitionRegistrar.class参数
@Import({Role.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class}) 复制代码
因为包含了com.xinyi.bean.Role,所以Role3组件也被注入进容器,并且组件的名称是自定义的bean的名称(自定义注入的bean的名称)。
4、使用spring提供的FactoryBean进行组件注册
声明一个RoleBean类并实现FactoryBean接口
//创建spring定义的FactoryBean类 public class RoleBean implements FactoryBean<Role> { //返回一个role对象并添加到容器中 public Role getObject() throws Exception { // TODO Auto-generated method stub return new Role(); } public Class<?> getObjectType() { // TODO Auto-generated method stub return Role.class; } } 复制代码
FactoryBean接口的源码如下:
public interface FactoryBean<T> { /** * Return an instance (possibly shared or independent) of the object * managed by this factory. * <p>As with a {@link BeanFactory}, this allows support for both the * Singleton and Prototype design pattern. * <p>If this FactoryBean is not fully initialized yet at the time of * the call (for example because it is involved in a circular reference), * throw a corresponding {@link FactoryBeanNotInitializedException}. * <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null} * objects. The factory will consider this as normal value to be used; it * will not throw a FactoryBeanNotInitializedException in this case anymore. * FactoryBean implementations are encouraged to throw * FactoryBeanNotInitializedException themselves now, as appropriate. * @return an instance of the bean (can be {@code null}) * @throws Exception in case of creation errors * @see FactoryBeanNotInitializedException */ @Nullable T getObject() throws Exception; /** * Return the type of object that this FactoryBean creates, * or {@code null} if not known in advance. * <p>This allows one to check for specific types of beans without * instantiating objects, for example on autowiring. * <p>In the case of implementations that are creating a singleton object, * this method should try to avoid singleton creation as far as possible; * it should rather estimate the type in advance. * For prototypes, returning a meaningful type here is advisable too. * <p>This method can be called <i>before</i> this FactoryBean has * been fully initialized. It must not rely on state created during * initialization; of course, it can still use such state if available. * <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return * {@code null} here. Therefore it is highly recommended to implement * this method properly, using the current state of the FactoryBean. * @return the type of object that this FactoryBean creates, * or {@code null} if not known at the time of the call * @see ListableBeanFactory#getBeansOfType */ @Nullable Class<?> getObjectType(); /** * Is the object managed by this factory a singleton? That is, * will {@link #getObject()} always return the same object * (a reference that can be cached)? * <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object, * the object returned from {@code getObject()} might get cached * by the owning BeanFactory. Hence, do not return {@code true} * unless the FactoryBean always exposes the same reference. * <p>The singleton status of the FactoryBean itself will generally * be provided by the owning BeanFactory; usually, it has to be * defined as singleton there. * <p><b>NOTE:</b> This method returning {@code false} does not * necessarily indicate that returned objects are independent instances. * An implementation of the extended {@link SmartFactoryBean} interface * may explicitly indicate independent instances through its * {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean} * implementations which do not implement this extended interface are * simply assumed to always return independent instances if the * {@code isSingleton()} implementation returns {@code false}. * <p>The default implementation returns {@code true}, since a * {@code FactoryBean} typically manages a singleton instance. * @return whether the exposed object is a singleton * @see #getObject() * @see SmartFactoryBean#isPrototype() */ //默认是true:Bean是单例,在容器中保存一份 //当为false:多实例,每次创建都会获取一个新的bean default boolean isSingleton() { return true; } 复制代码
接着在配置类中注册RoleBean
@Bean public RoleBean roleBean() { return new RoleBean(); } 复制代码
在测试方法中添加下述代码返回RoleBean的类型
Object beanObject = applicationContext.getBean("roleBean"); System.out.println("roleBean的类型是:"+beanObject.getClass()); 复制代码
RoleBean的类型是Role,因为RoleBean调用的是getObject方法创建的对象,而getObject方法返回的是new Role()。
可以通过在RoleBean组件名称前加“&”符号来表明注册的是RoleBean组件本身。
public interface BeanFactory { /** * Used to dereference a {@link FactoryBean} instance and distinguish it from * beans <i>created</i> by the FactoryBean. For example, if the bean named * {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject} * will return the factory, not the instance returned by the factory. */ //factorybean前缀符:返回的是工厂类,而不是调用getObject方法产生的对象 String FACTORY_BEAN_PREFIX = "&"; 复制代码
Object beanObject1 = applicationContext.getBean("&roleBean"); System.out.println("获取roleBean本身:"+beanObject1.getClass()); 复制代码
以上就是在IOC容器中进行组件注册的四种方式。