复习IOC容器中Bean的其他使用细节,主要学习以下三个方面:
1.如何从IOC容器中手动获取bean对象
2.bean的作用域配置
3.管理第三方的bean对象
1.获取bean
默认情况下,springboot项目在启动的时候会自动创建IOC容器(也称之为Spring容器),并且在启动的过程中会自动的将bean对象都创建好,存放在IOC容器当中。应用程序在运行时需要依赖什么bean对象,就直接进行依赖注入就可以了。
在spring容器当中提供了一些方法,可以主动从IOC容器当中获取到bean对象,有3种常用方式:
1.根据name获取bean
2.根据类型获取bean
3.根据name获取bean(带类型转换)
注意:要想从IOC容器中来获取bean对象,需要先拿到IOC容器对象,怎么才能拿到IOC容器对象呢????
测试代码:
@SpringBootTest
class SpringbootWebConfig2ApplicationTests {
@Autowired
private ApplicationContext applicationContext; //IOC容器对象
//获取bean对象
@Test
public void testGetBean(){
//根据bean的名称获取
DeptController bean1 = (DeptController) applicationContext.getBean("deptController");
System.out.println(bean1);
//根据bean的类型获取
DeptController bean2 = applicationContext.getBean(DeptController.class);
System.out.println(bean2);
//根据bean的名称 及 类型获取
DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
System.out.println(bean3);
}
}
程序运行后控制台日志:
题:输出的bean对象地址值是一样的,说明IOC容器当中的bean对象有几个?
答案:只有一个。 (默认情况下,IOC中的bean对象是单例)那么能不能将bean对象设置为非单例的(每次获取的bean都是一个新对象)?
注意事项:
-
上述所说的 【Spring项目启动时,会把其中的bean都创建好】还会受到作用域及延迟初始化影响,这里主要针对于默认的单例非延迟加载的bean而言。
2.Bean作用域
在Spring中支持五种作用域,后三种在web环境才生效:
作用域 | 说明 |
---|---|
singleton | 容器内同名称的bean只有一个实例(单例)(默认) |
prototype | 每次使用该bean时会创建新的实例(非单例) |
request | 每个请求范围内会创建新的实例(web环境中,了解) |
session | 每个会话范围内会创建新的实例(web环境中,了解) |
application | 每个应用范围内会创建新的实例(web环境中,了解) |
知道了bean的5种作用域了,我们要怎么去设置一个bean的作用域呢?
-
可以借助Spring中的@Scope注解来进行配置作用域
测试一:
//默认bean的作用域为:singleton (单例)
@Lazy //延迟加载(第一次使用bean对象时,才会创建bean对象并交给ioc容器管理)
@RestController
@RequestMapping("/depts")
public class DeptController {
@Autowired
private DeptService deptService;
public DeptController(){
System.out.println("DeptController constructor ....");
}
//省略其他代码...
}
@SpringBootTest
class SpringbootWebConfig2ApplicationTests {
@Autowired
private ApplicationContext applicationContext; //IOC容器对象
//bean的作用域
@Test
public void testScope(){
for (int i = 0; i < 10; i++) {
DeptController deptController = applicationContext.getBean(DeptController.class);
System.out.println(deptController);
}
}
}
注意事项:
-
IOC容器中的bean默认使用的作用域:singleton (单例)
-
默认singleton的bean,在容器启动时被创建,可以使用@Lazy注解来延迟初始化(延迟到第一次使用时)
测试二:
@Scope("prototype") //bean作用域为非单例
@Lazy //延迟加载
@RestController
@RequestMapping("/depts")
public class DeptController {
@Autowired
private DeptService deptService;
public DeptController(){
System.out.println("DeptController constructor ....");
}
//省略其他代码...
}
注意事项:
-
prototype的bean,每一次使用该bean的时候都会创建一个新的实例
-
实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean不需要配置scope属性
3.第三方Bean
学习完bean的获取、bean的作用域之后,接下来我们再来学习第三方bean的配置。
之前我们所配置的bean,像controller、service,dao三层体系下编写的类,这些类都是我们在项目当中自己定义的类(自定义类)。当我们要声明这些bean,也非常简单,我们只需要在类上加上@Component以及它的这三个衍生注解(@Controller、@Service、@Repository),就可以来声明这个bean对象了。 但是在我们项目开发当中,还有一种情况就是这个类它不是我们自己编写的,而是我们引入的第三方依赖当中提供的。
例如:在pom.xml文件中,引入dom4j:
<!--Dom4j-->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
dom4j就是第三方组织提供的。 dom4j中的SAXReader类就是第三方编写的。
当我们需要使用到SAXReader对象时,直接进行依赖注入是不是就可以了呢?
-
按照我们之前的做法,需要在SAXReader类上添加一个注解@Component(将当前类交给IOC容器管理)
结论:第三方提供的类是只读的。无法在第三方类上添加@Component注解或衍生注解。
那如何使用并定义第三方的Bean呢?
如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component 及衍生注解声明bean的,就需要用到@Bean注解。
解决方案1:在启动类上添加@Bean标识的方法
@SpringBootApplication
public class SpringbootWebConfig2Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfig2Application.class, args);
}
//声明第三方bean
@Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
public SAXReader saxReader(){
return new SAXReader();
}
}
xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<emp>
<name>Tom</name>
<age>18</age>
</emp>
测试类:
@SpringBootTest
class SpringbootWebConfig2ApplicationTests {
@Autowired
private SAXReader saxReader;
//第三方bean的管理
@Test
public void testThirdBean() throws Exception {
Document document = saxReader.read(this.getClass().getClassLoader().getResource("1.xml"));
Element rootElement = document.getRootElement();
String name = rootElement.element("name").getText();
String age = rootElement.element("age").getText();
System.out.println(name + " : " + age);
}
//省略其他代码...
}
Tom : 18
说明:以上在启动类中声明第三方Bean的作法,不建议使用(项目中要保证启动类的纯粹性)
解决方案2:在配置类中定义@Bean标识的方法
-
如果需要定义第三方Bean时, 通常会单独定义一个配置类
@Configuration //配置类 (在配置类当中对第三方bean进行集中的配置管理)
public class CommonConfig {
//声明第三方bean
@Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
//通过@Bean注解的name/value属性指定bean名称, 如果未指定, 默认是方法名
public SAXReader reader(DeptService deptService){
System.out.println(deptService);
return new SAXReader();
}
}
如果第三方Bean需要依赖其它bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配
@Test
public void testGetBean2(){
Object saxReader = applicationContext.getBean("reader");
System.out.println(saxReader);
}
4.Bean的生命周期
在Spring框架中,Bean的生命周期包括以下几个阶段:
(1)实例化(Instantiation):Spring 容器根据配置信息或注解创建Bean 的实例对象,这是 Bean 生命周期的起始点,通常通过反射调用 Bean 的构造方法来完成。
(2)属性赋值(Population):在实例化后,Spring会通过依赖注入(Dependency Injection)或者属性赋值的方式,将Bean的属性值设置好。这可以通过setter方法注入或者字段注入来实现。
(3)初始化(Initialization):
- 实现
InitializingBean
接口:如果 Bean 实现了InitializingBean
接口,Spring 容器会调用其afterPropertiesSet()
方法,该方法可用于执行一些初始化操作。 - 自定义初始化方法:通过配置文件或者注解指定自定义的初始化方法,Spring 容器会在合适的时机调用该方法。
(或者在配置文件中通过init-method属性指定。)
(4)使用(In Use):在初始化完成后,Bean进入可用状态,可以被其他对象引用和使用。
(5)销毁(Destruction):当Bean不再被需要时,Spring会调用Bean的销毁方法进行资源释放和清理工作。这个方法可以通过实现DisposableBean接口的destroy()方法,或者在配置文件中通过destroy-method属性指定。
Bean的销毁阶段只有在容器关闭时才会触发,或者在使用单例作用域的Bean时,当Bean不再被引用时被垃圾回收器回收。以上是Spring框架中Bean的典型生命周期阶段,每个阶段都提供了相应的扩展点,可以在不同阶段进行自定义操作。
面试题:
1.请解释Spring中的延迟初始化是什么,如何配置延迟初始化??
在Spring中,延迟初始化(Lazy Initialization)是指在容器启动时不立即创建所有的Bean实例,而是在第一次使用该Bean时才进行创建。这可以有效地减少启动时间和资源消耗,特别是对于那些初始化耗时较长或者很少被使用的Bean。
要配置延迟初始化,可以使用两种方式:
使用注解配置:(1)在类级别上使用@Lazy注解,表示该类的Bean实例需要延迟初始化。例如:@Lazy。(2)在XML配置文件中,使用lazy-init="true"属性来配置延迟初始化。例如:
<bean id="myBean" class="com.example.MyBean" lazy-init="true" />
2.Spring Bean的生命周期中,哪些方法可以进行自定义操作??
(1)初始化方法(Initialization methods):
在配置文件中通过init-method属性指定的方法。在Bean类中实现InitializingBean接口,并重写afterPropertiesSet()方法。初始化方法用于在Bean实例化后,属性注入完成之后执行一些初始化逻辑,例如初始化资源、建立连接等。您可以根据需要选择其中一种方式来定义初始化方法。
(2)销毁方法(Destruction methods):
在配置文件中通过destroy-method属性指定的方法。
在Bean类中实现DisposableBean接口,并重写destroy()方法。
销毁方法用于在容器销毁Bean之前执行一些清理操作,例如释放资源、关闭连接等。您可以根据需要选择其中一种方式来定义销毁方法。
(3)后置处理器(Post-processors):
后置处理器可以在Bean的初始化前后对Bean进行自定义处理。postProcessBeforeInitialization()方法在初始化方法调用之前被调用,postProcessAfterInitialization()方法在初始化方法调用之后被调用。通过实现这些方法,您可以对Bean进行额外的操作,例如修改属性值、添加代理等。
实现BeanPostProcessor接口,并重写postProcessBeforeInitialization()和postProcessAfterInitialization()方法。
除了上述方法外,还可以使用其他扩展机制来进行自定义操作,例如使用@PostConstruct和@PreDestroy注解来标记初始化方法和销毁方法,或者通过实现BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor接口来对Bean的定义进行修改。