目录
Bean的作用域
作用域分类
设置作用域
通过注解设置
通过配置文件设置
Bean的生命周期
Bean的作用域
Bean的作用域是指:在整个Spring容器中Bean的行为模式。这个模式有六种。
作用域分类
- singleton:单例作用域。
在这个模式下,容器一启动就会创建出一个实例化的Bean对象,后续使用它时,都是直接对它进行操作,直到最后容器销毁Bean才会销毁。
适用于Bean状态不需要更新的情况。- prototype:原型作用域(多例作用域)。
在这个模式下,只要请求Bean,Spring都会创建出一个新的实例返回,当实例使用完成后就会自动销毁。
适用于Bean状态需要更新的情况。
以下三种只适用于Spring MVC- request:请求作用域。
每个HTTP请求(request)中只有一个实例化的Bean对象。同一个请求中的所有处理都共享该Bean实例。该作用域下的Bean会在每个请求(request)中被创建,当请求结束时被销毁。- session:会话作用域。
每个HTTP会话(session)中只有一个实例化的Bean对象。同一个会话中所有的请求都共享该Bean实例。该作用域下的Bean会在每个会话(session)中被创建,当会话结束时被销毁。- application:全局作用域。
所有Web应用中只有一个实例化的Bean对象,同一个Web应用中的所有请求都共享该Bean实例。该作用域下的Bean会在Web应用启动时被创建,当Web应用关闭时被销毁。
和singleton的区别
- singleton是Spring Core的作用域,application是Spring MVC的
- singleton是作用于IoC容器,application是作用于Servlet容器
- websocket:HTTP WebSocket作用域。
在一个HTTP WebSocket的生命周期中,定义一个实例。
只能在HTTP WebSocket中使用。
由于我们目前的项目是Spring Core项目,目前只可以演示单例作用域和原型作用域。现在来演示一下单例作用域的效果。
package com.test.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Setter
@Getter
@ToString
public class User {
private Integer id;
private String name;
}
package com.test.repository;
import com.test.entity.User;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
@Repository
public class BuildUser {
@Bean
public User user() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
这里使用方法注解把User存到了Spring容器中。 接下来使用Controller从Spring获取到User,然后修改名字。最后Service从Spring中获取到User,查看是否和最开始的User一样。
package com.test.controller;
import com.test.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
@Autowired
private User user;
public User getUser() {
User user1 = user;
user1.setName("李四");
return user1;
}
}
package com.test.service;
import com.test.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private User user;
public User getUser() {
User user1 = user;
return user1;
}
}
最后打印一下User,看看是否符合单例的模式。
import com.test.controller.UserController;
import com.test.entity.User;
import com.test.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
User user1 = context.getBean("user", User.class);
System.out.println("直接拿最开始存的User:" + user1.toString());
UserController userController = context.getBean("userController", UserController.class);
System.out.println("在UserController修改后返回的User:" + userController.getUser().toString());
UserService userService = context.getBean("userService", UserService.class);
System.out.println("在UserService中得到开始的User:" + userService.getUser().toString());
}
}
设置作用域
默认的作用域是单例作用域,当我们想要更改其作用域是,通过注解和配置文件的方式更改。建议使用注解的方式。
通过注解设置
@Scope 使用这个注解来改变当前bean的作用域。
需要设置在其他注解的上面,因为一开始就要确认其存储方法
有两种方法可以设置,推荐第一种
- @Scope("在这里设置作用域") 比如 @Scope("ConfigurableBeanFactory.SCOPE_PROTOTYPE") 这个类中的常量是给我们设置好了,这样可以防止拼错
- @Scope("在这里设置作用域") 比如
@Scope("prototype") 这样需要自己拼写,容易拼错。
把上述的Bean从单例作用域变成原型作用域。
package com.test.repository;
import com.test.entity.User;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
@Repository
public class BuildUser {
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
public User user() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
通过配置文件设置
这里需要到配置文件中修改 bean 属性scope
<bean id="buildBean" class="com.test.repository.BuildUser" scope="prototype"></bean>
效果同上。
如果二者同时设置了,并且有冲突,注解的方法优先级高于配置文件。
Bean的生命周期
Bean的生命周期主要有五大部分。
- 实例化Bean:为Bean分配空间
- 设置属性:在Bean的注入和装配时需要设置属性
- Bean初始化
- 实现各种Aware通知方法
BeanNameAware、BeanFactoryAware、ApplicationContextAware的接口方法- 执行BeanPostProcessor初始化前置方法
- 执行PostConstruct初始化方法,在依赖注入后实现
- 执行用户自己指定的init-method(如果有)
- 执行BeanPostProcessor初始化后置⽅法
- 使用Bean
- 销毁Bean
销毁容器的各种⽅法,如 PreDestroy、DisposableBean 接⼝⽅法、destroy-method
package com.test.controller;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Controller;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Controller
public class BeanLifeController implements BeanNameAware, BeanPostProcessor {
@Override
public void setBeanName(String s) {
System.out.println("执行了通知Bean" + s);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行了postProcessAfterInitialization方法");
return bean;
}
public void myInit() {
System.out.println("执行了XML的初始化方法");
}
@PostConstruct
public void doConstruct() {
System.out.println("执行了使用注解的初始化方法");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行了postProcessBeforeInitialization方法");
return bean;
}
public void sayHi() {
System.out.println("你好");
}
@PreDestroy
public void doPreDestroy() {
System.out.println("执行了销毁Bean容器的方法");
}
}
还有一些存Bean的代码不作展示了。
import com.test.controller.BeanLifeController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App2 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
BeanLifeController beanLifeController = context.getBean("beanLifeController", BeanLifeController.class);
beanLifeController.sayHi();
beanLifeController.doPreDestroy();
}
}
其中初始化使用了两种方式实现:
有什么错误评论区指出,希望可以帮到你。