【JavaEE】Bean的作用域和生命周期
目录
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 {@Beanpublic 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 {@Autowiredprivate 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 {@Autowiredprivate 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)@Beanpublic 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 {@Overridepublic void setBeanName(String s) {System.out.println("执行了通知Bean" + s);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("执行了postProcessAfterInitialization方法");return bean;}public void myInit() {System.out.println("执行了XML的初始化方法");}@PostConstructpublic void doConstruct() {System.out.println("执行了使用注解的初始化方法");}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("执行了postProcessBeforeInitialization方法");return bean;}public void sayHi() {System.out.println("你好");}@PreDestroypublic 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();}
}
其中初始化使用了两种方式实现:
有什么错误评论区指出,希望可以帮到你。