将自定义的Bean注册到容器中有四种方式:
1、使用@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(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注解的源码如下:
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容器中进行组件注册的四种方式。