在使用Spring框架开发应用的过程中,大家都知道使用Spring开发应用程序,我们应用程序中所有的Bean都是通过Spring的IOC容器来管理。将Bean注入到Spring IOC容器中的方式多种多样,如通过传统的XML方式注入,通过注解的方式注入等。本文我们就通过例子的形式看一看如何通过注解@Configuration和@Bean向容器中注入组件。
1、首先创建一个Maven项目,加入spring-context和Junit依赖。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
2、创建一个类Computer(我们需要注入的Bean)
public class Computer {
private String name;
private String brand;
public Computer() {
System.out.println("创建Computer对象... ...");
}
public Computer(String name, String brand) {
super();
this.name = name;
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public String toString() {
return "Computer [name="+name+",brand="+ brand +"]";
}
}
3、创建一个配置类BeanConfig
//配置类==配置文件
@Configuration //告诉Spring这是一个配置类
public class BeanConfig {
/**
* 向容器中注册一个Bean,类型为返回值类型,id默认为方法名称
* @return
*/
@Bean
public Computer computer() {
return new Computer("Latitude","Dell");
}
}
-
@Configuration:该注解所标记的类在Spring中就是一个配置类,被该注解所标记的类中包含一个或者多个@Bean注解的方法,这些被@Bean注解的方法将会被Spring容器扫描并用于构建Bean的定义,初始化Spring的IOC容器。
-
@Bean:被注解的方法将会生成一个可以被Spring IOC容器管理的Bean。
4、获得容器,测试Bean是否被注入
public class IOCTest1 {
@Test
public void testIOC(){
AnnotationConfigApplicationContext applicatoin = new AnnotationConfigApplicationContext(BeanConfig.class);
System.out.println("容器创建完成... ...");
String[] names = applicatoin.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
Object computer = applicatoin.getBean("computer");
System.out.println(computer);
Object computer1 = applicatoin.getBean("computer");
System.out.println(computer == computer1);
}
}
这里需要注意:
通过XML的方式向容器中注入Bean,我们用的是ClassPathXmlApplicationContext容器并传入XML文件的位置,通过注解的形式向容器中注入Bean时,我们用的是AnnotationConfigApplicationContext容器,传入的是Bean的配置类BeanConfig.class。
执行以上的单元测试,我们将会得到如下结果:
容器创建完成... ...
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
beanConfig
computer
Computer [name=Latitude,brand=Dell]
true
从运行结果我们可以得知如下的一些结论:
- 使用@Configuration和@Bean向容器中注入组件,Spring会在容器中注入一些注解处理的Bean。
- 在默认情况下,Spring在启动容器后会调用构造方法创建对象并放到ioc容器中。
- 默认情况下,Spring容器都是以单例模式管理Bean,所有对象在Spring容器中都只有一个实例。
- @Configuration:该注解所标记的类在Spring中就是一个配置类,被该注解所标记的类中包含一个或者多个@Bean注解的方法,这些被@Bean注解的方法将会被Spring容器扫描并用于构建Bean的定义,初始化Spring的IOC容器,该注解告诉Spring这是一个配置类。
- @Bean:被注解的方法将会生成一个可以被Spring IOC容器管理的Bean,该注解只能用在方法上。
5、Bean实例的初始化及销毁
通过注解的方式向IOC容器中注入Bean时,可以通过设置@Bean注解的属性值来指定在创建对象时的初始化方法、销毁方法以及Bean的id等。
@Bean常用的属性如下:
- name: 为 bean 起一个名字,如果默认没有写该属性,那么就使用方法的名称为该 bean 的名称;
- value:为 bean 起一个名字,如果默认没有写该属性,那么就使用方法的名称为该 bean 的名称;
- initMethod:指定Bean实例的初始化方法;
- destroyMethod:指定Bean实例的销毁方法,在调用 IoC 容器的 close() 方法时,会执行到该属性指定的方法。不过,只是单实例的 bean 才会调用该方法,如果是多实例的情况下,不会调用该方法;
修改BeanConfig类的@Bean注解如下:
@Bean(name="testComputer",initMethod="init",destroyMethod="destroy")
public Computer computer() {
return new Computer("Latitude","Dell");
}
指定Bean的名称为testComputer,初始化方法为init,销毁方法为destroy。
在Computer类中加入init、destroy方法。
public class Computer {
//... ...
protected void init() {
System.out.println("Init方法被调用... ...");
}
protected void destroy() {
System.out.println("Destroy方法被调用... ...");
}
}
修改测试方法如下:
public class IOCTest1 {
@Test
public void testIOC(){
AnnotationConfigApplicationContext applicatoin = new AnnotationConfigApplicationContext(BeanConfig.class);
System.out.println("容器创建完成... ...");
String[] names = applicatoin.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
Object computer = applicatoin.getBean("testComputer");
System.out.println(computer);
Object computer1 = applicatoin.getBean("testComputer");
System.out.println(computer == computer1);
applicatoin.close();
System.out.println("容器关闭了... ...");
}
}
执行测试方法我们可得到如下结果:
创建Computer对象... ...
Init方法被调用... ...
容器创建完成... ...
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
beanConfig
testComputer
Computer [name=Latitude,brand=Dell]
true
Destroy方法被调用... ...
容器关闭了... ...
从执行结果我们可以得知如下结论:
- 使用@Configuration和@Bean向容器中注入组件,Spring会在容器中注入一些注解处理的Bean。
- 在默认情况下,Spring在启动容器后会马上调用构造方法创建对象并放到ioc容器中。
- 对象创建完成之后,立马调用对象的初始化方法初始化对象。
- 默认情况下,Spring容器都是以单例模式管理Bean,所有对象在Spring容器中都只有一个实例。
- 对象的销毁方法在IOC容器关闭后被调用。
6、懒加载(@Lazy)
我们上面所说的Bean实例的创建都是在容器启动之后立马就会创建并执行对应的初始化方法,那么在单例模式下,如果我们希望在容器创建完成之后不用立马就创建实例,而是希望在我们第一次使用Bean时才创建对象并完成对应的初始化动作,然后再将对象放到IOC容器中。
Spring为我们提供了@Lazy注解,当我们对Bean加上该注解之后,在单例模式下,Spring IOC容器就能够控制在容器创建时就创建对象还是在第一次使用时创建对象。
- @Lazy:默认为true,即在单例模式下,在Bean上加上该注解,默认第一次使用时创建对象并初始化;当设置为false时,懒加载就失效了。
修改BeanConfig类如下:
@Configuration //告诉Spring这是一个配置类
public class BeanConfig {
/**
* 向容器中注册一个Bean,类型为返回值类型,id默认为方法名称
* @return
*/
@Bean(name="testComputer",initMethod="init",destroyMethod="destroy")
@Lazy
public Computer computer() {
return new Computer("Latitude","Dell");
}
}
我们在Bean上加了@Lazy注解,执行单元测试我们将会得到如下结果:
容器创建完成... ...
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
beanConfig
testComputer
创建Computer对象... ...
Init方法被调用... ...
Computer [name=Latitude,brand=Dell]
true
Destroy方法被调用... ...
容器关闭了... ...
从执行结果我们可以得知:
- 容器创建完成后,并没有马上创建对象并进行初始化,而是在第一次使用时才创建对象并执行相关的初始化操作。
7、多实例配置(@Scope)
默认情况下,Spring IOC容器都是以单例的模式来管理Bean,如果我们要创建多个实例怎么办?Spring提供了@Scope注解,我们通过该注解制定Bean的作用域,可以控制在IOC容器中Bean实例是以单例还是其他的方式存在。
@Scope有如下几个值:
- prototype:多实例的,ioc容器启动并不会去调用方法创建对象放在容器中,每次获取的时候才会调用方法创建对象;
- singleton:单实例的(默认值),ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就是直接从容器(map.get())中拿;
- request:同一次请求创建一个实例;
- session:同一个session创建一个实例;
修改BeanConfig类如下:
@Configuration //告诉Spring这是一个配置类
public class BeanConfig {
/**
* 向容器中注册一个Bean,类型为返回值类型,id默认为方法名称
* @return
*/
@Bean(name="testComputer",initMethod="init",destroyMethod="destroy")
@Scope("prototype")
public Computer computer() {
return new Computer("Latitude","Dell");
}
}
执行单元测试,得到如下结果:
容器创建完成... ...
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
beanConfig
testComputer
创建Computer对象... ...
Init方法被调用... ...
Computer [name=Latitude,brand=Dell]
创建Computer对象... ...
Init方法被调用... ...
false
容器关闭了... ...
根据结果我们可得出如下结论:
- 当将Bean的作用域(@Scope)设置成prototype之后,ioc容器启动并不会去调用方法创建对象放在容器中,而是每次获取的时候才会调用方法创建对象;
- 对象创建完成之后立马调用对象的初始化方法初始化对象。
- 当将Bean的作用域(scope)设置成prototype之后,每次创建的Bean都是不一样的。
- 当Bean的作用域为非单例模式(singleton)时,对象的销毁方法在IOC容器关闭时不会被调用。
小结:
本文我们通过注解的方式演示了如何向Spring IOC容器中注入Bean及如何获取一个Bean实例,同时演示了如何初始化Bean、如何销毁Bean,以及如何设置Bean的作用域及@Scope的值所代表的含义,同时介绍了在单例模式下如何设置在第一次使用Bean时才创建Bean实例,希望对大家有所帮助。