将类添加至容器中的四种方法

将类添加至容器中的四种方法

在Spring中,有四种方法可以将现有的类的实例添加到容器中.在了解之前需要了解以下两种作用域.

Spring在创建对象的时候共有四种作用域可选.分别为常用的singletonprototype.和在web应用开发中的requestsession.

下面针对常用的singletonprototype来了解bean的作用域(SCOPE)

Spring的作用域

1. Singleton 单例

Singleton为Spring对象的默认的创建方式,也就是说Spring在创建Bean的时候默认是单例的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/准备对象Person
@Configurable
public class TestConfig {
@Bean
public Person person() {
System.out.println("创建Bean实例!");
return new Person("li", "lei");
}
}
//测试
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig.class);
// 获取两次对象,并且在这是就会打印 「创建Bean实例」
Person person = (Person) applicationContext.getBean("person");
Person person_copy = (Person) applicationContext.getBean("person");
// 这里返回true,说明两个对象是相同的对象
System.out.println(person == person_copy);

运行结果:

1
2
3
创建Bean实例!
容器创建成功。。接下来创建对象!
true

并且需要注意的时,在Spring创建Singleton对象的时候,是在容器创建后立即创建的.

2. prototype 原型

同样上述的代码,在@Bean下面增加@Scope("prototype")后,会注意到结果的不同.运行时在进行结果的观察:

1
2
3
4
容器创建成功。。接下来创建对象!
创建Bean实例!
创建Bean实例!
false
类型 是否单例 创建时间 是否销毁
Singleton 单例 默认在容器启动时创建 容器关闭后销毁
prototype 多实例 在获取对象时创建 不会调用销毁方法

一、Configuration注解和Bean注解

代替了原来的Spring配置文件,在类上标注Configuration注解说明这个类是Spring的配置类.

在方法上使用Bean注解,则会将返回值作为实例添加到容器中,方法名称作为Bean的ID.

一般使用这种方法将第三方的包中的类加入到容器中,如Shiro的配置类.

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
使用@Confiuration注解声明配置类
*/
@Configurable
@ComponentScan("com.nanyin")
public class TestConfig {
@Bean
@Scope("prototype") //声明多实例
public Person person() {
System.out.println("创建Bean实例!");
return new Person("li", "lei");
}
}

二、componentScan包扫描注解

在面对自己写的@Component,@Service,@Controller,@Repository则不需要使用第一个中的使用@Bean注解一个一个的添加到配置类中,只需要配置@ComponentSacn就行了.它会自动扫描路径下的所有这些注解的类加入到容器中.

1
2
3
4
5
@Configurable
@ComponentScan("com.nanyin")
public class TestConfig {

}

三、import注解

1. 直接导入@Configuration配置类

能够使用@Import(value=xxx.class) 其中value是数组,可以一次性导入多个配置类.批量的导入配置文件中的所有声明的bean.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 新的实体
public class Dog {
}

@Configurable
// 需要导入TestConfig中配置的所有bean
@Import(value=TestConfig.class)
public class ImportConfig {
@Bean
public Dog dog() {
System.out.println("创建Bean实例!");
return new Dog();
}
}

@Configurable
public class TestConfig {
// 这里配置了一个person
@Bean
public Person person() {
System.out.println("创建Bean实例!");
return new Person("li", "lei");
}
}

// 测试
@Test
public void importTest(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ImportConfig.class);
String[] names = applicationContext.getBeanDefinitionNames();
// 打印出所有添加到容器中的名称
for (String temp :
names) {
System.out.println(temp);
}
}

结果:
importConfig
person
com.nanyin.config.TestConfig
dog

导入了TestConfig配置类和其中的person实例

2. 使用importSelector自定义导入

除了使用 @import 直接导入指定的类外,还可以导入 继承importSelector接口的类,并且在实现类中进行自定义操作.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MyImportSelector implements ImportSelector {

public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 可以通过importingClassMetadata获取类注解上的所有信息
return new String[]{Dog.class.getName()};
}
}

@Configurable
// @Import 可以导入数组(多个) class,如上面提到的 TestConfig配置类,这里把MyImportSelector也添加进去
@Import(value={TestConfig.class,MyImportSelector.class})
public class ImportConfig {

}

public void importTest(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ImportConfig.class);
String[] names = applicationContext.getBeanDefinitionNames();
for (String temp :
names) {
System.out.println(temp);
}
((AnnotationConfigApplicationContext) applicationContext).close();;
}

//执行测试后
// importConfig
// person
// com.nanyin.config.TestConfig
// com.nanyin.entity.Dog

3. 使用 importBeanDefinitionRegister

可以使用 importBeanDefinitionRegister 自定义注册bean,因为在接口中有BeanRegiest作为参数,通过参数调用Registe相关方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MyimportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotatedGenericBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(Dog.class);
// 前面字符串是ID,后面是BeanDefinition
registry.registerBeanDefinition("dogx",beanDefinition);
}
}

@Import(MyimportBeanDefinitionRegistrar.class)
public class ImportConfig {
@Bean
public Dog dog() {
System.out.println("创建Bean实例!");
return new Dog();
}
}
// 结果通过 registry 注册了 dogx
// importConfig
// dog
// dogx

后面的两种方法在SpringBoot源码中会经常看到,所以还是挺重要的.


四、使用FactoryBean注册bean

用于创建复杂的bean时,会用到FactoryBean.

如果继承 FactoryBean 接口实现,则这个就不能当作一个普通的bean使用了,他暴露出来的获取的对象是 getObject()方法获取的工厂中的对象,而不是他自身.默认创建的时FactoryBean的对象实例,而不是工厂实例,如果想获取工厂的实例,在getBean的时候在对象ID前加上&符号。

具体实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyFactoryBean implements FactoryBean<Dog> {

// 返回的实例对象
public Dog getObject() throws Exception {
return new Dog();
}
// 返回的类型
public Class<?> getObjectType() {
return Dog.class;
}

// 默认为true,如果时false则为多实例
public boolean isSingleton() {
return true;
}
}
-------------本文结束感谢您的阅读-------------

本文标题:将类添加至容器中的四种方法

文章作者:NanYin

发布时间:2019年12月01日 - 12:12

最后更新:2019年12月11日 - 20:12

原始链接:https://nanyiniu.github.io/2019/12/01/%EF%BC%88%E4%B8%80%EF%BC%89%20%E5%B0%86%E7%B1%BB%E5%8A%A0%E5%85%A5%E5%88%B0%E5%AE%B9%E5%99%A8%E4%B8%AD%E7%9A%84%E6%96%B9%E5%BC%8F/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。