目录
前言
案例
案例分析
作用域的定义
Bean对象的6种作用域
Singleton
prototype
设置作用域
编辑延迟初始化
Spring的执行流程
Bean的生命周期
我们可以类比一下普通变量的生命周期和作用域, 大多数变量的生命周期和作用域都被限定在了花括号内 {}, 除了这个大括号, 这个变量也就会被销毁, 与之对应的内存也会被JVM回收.
对于类中的变量, 即使是出了{}, 也仍然能被访问到.
前面的内容是如何存储和获取Bean对象, 因此spring中最核心的资源就是Bean对象, 那么Bean的作用域是怎么样的呢?
下面我们通过一个案例来演示一下:
假设现在有一个公共的Bean对象提供给用户A和用户B使用, 然后在使用的图中,用户A 却悄咪咪的修改了公共的Bean资源, 导致B在使用的时候发生了预期之外的逻辑错误.
下面是代码实现:
首先我们实现Bean对象所属的类:
public class User {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
创建Bean对象:
@Component
public class Users {
@Bean
public User getUser() {
User user = new User();
user.setAge(18);
user.setName("如花");
return user;
}
}
属性注入:
// controller1 相当于A
@Controller
public class Controller1 {
@Autowired
public User user;
public void useUser() {
User user1 = user;
System.out.println("公共类原来的名字为:" + user1.getName());
System.out.println("A 使用之前将其修改为 翠花");
user1.setName("翠花");
System.out.println("现在公共类的名字为:" + user1.getName());
}
}
@Controller
public class Controller2 {
@Autowired
public User user;
public void userUser() {
User user1 = user;
System.out.println("B 使用这个公共Bean类的名字为:" + user1.getName());
System.out.println("但是B想念的是如花");
}
}
添加启动类:
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("springConfig.xml");
Controller1 controllerA = context.getBean("controller1", Controller1.class);
controllerA.useUser();
Controller2 controllerB = context.getBean("controller2", Controller2.class);
controllerB.userUser();
}
}
-- 输出:
上面问题的原因就是Bean默认情况下是单例状态的(singleton), 也就是所有人使用的都是同一个对象, 之前我们学过单例模式的时候就知道, 使用单例模式可以极大的提高性能, 但是同时也带来了新的安全问题.
spring中Bean的作用域默认也是singleton的单例模式.
限定程序中变量的可用范围, 我们把这种行为叫做修改作用域, 也就是说这个可用范围对应于这个作用域, 或者说是在源代码中定义变量的某个区域就叫做作用域.
而Bean的作用域是指在spring整个framework框架中的某个行为, 所以Bean通过不同的行为类型, 来决定定义域, 比如singleton单例作用域, 就表示这个Bean对象是在整个spring中只有一份, 他是全局共享的, 那么其他人修改这个值之后, 另外一个人读取到这个值他就是被修改的值.
由Spring容器创建的Bean的生存周期被称为Bean的作用域, 默认情况下, 由spring容器创建的所有Bean对象都是Singleton作用域, 换句话说, 针对Bean定义只创建了一个Bean对象, 并有spring容器可以在整个应用程序生存期中使用该实例, 当不同的Bean与不同的层(控制层, 服务层 和 数据访问对象层) 相对应时, 可以使用Singleton作用域.
spring支持的第二种作用域就是prototype, 类似于java代码中使用new操作来创建Bean对象, 每次需要在容器中或者通过Bean引用其他Bean定义中, 又或者通过显示Bean查询(getBean) 从应用程序中访问Prototype作用域时就会创建他们:
注意这里的scope = "prototype"
spring 容器在初始化一个Bean实例的时候, 同时会指定其作用域, spring有6 种作用域, 最后四种是基于spring MVC生效的:
注意后面四种是spring MVC中的值, 在普通的spring项目中, 只有目前两种
下面介绍Spring中常用的两种作用域 >>
使用@Scope标签就可以用来声明Bean的作用域, 比如设置Bean的作用域, 代码如下:
@Component
public class Users {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public User getUser() {
User user = new User();
user.setAge(18);
user.setName("如花");
return user;
}
}
@Scope标签即可以修饰方法, 也可以修饰类, @Scope有两种设置方法:
或者是在xml配置文件中的Bean对象中添加scope属性:
默认情况下, spring容器在启动阶段创建Bean, 该过程被称为预先Bean初始化. 它的有点就是可以尽早发现配置错误. 例如在基于XML文件的配置中, Bean的定义的类属性出现了错别字, 或者引用了一个不可用的Bean定义等.
但是从另外一方面, 如果存在大量的Bean定义, 或者一些特殊的Bean, 例如Hibernate SessionFactory 或者JPA EntityManagerFactory(这些Bena 的初始化会占用大量时间), 那么初始化可能会降低启动的速度, 一些Bean可能只在特定的场合中使用, 在其他情况下并不被需要, 在这种情况下的话, 预先初始化会导致不必要的内存消耗和性能消耗.
spring还支持延迟Bean初始化, 如果开发人员将Bean的配置为延迟创建, 那么容器将会延迟Bean的创建, 直到被需要的时候才被创建, 通过从一个已经创建的另外一个Bean中的引用, 或者是显式的调用getBean来查找Bean, 都可以触发Bean的创建.
@Controller
public class Controller1 {
@Autowired
public User user;
public void useUser() {
User user1 = user;
System.out.println("公共类原来的名字为:" + user1.getName());
System.out.println("A 使用之前将其修改为 翠花");
user1.setName("翠花");
System.out.println("现在公共类的名字为:" + user1.getName());
}
}
例如上述代码, 如果这里的Controller1这个类中的属性user使用的是延迟初始化, 那么只有在使用的时候才会被创建, 例如执行到User1 = user的时候就会被创建和初始化.
在基于XML的配置中, 可以在
如果想在一个XML文件中将所有的Bean对象都定义为延迟初始化, 可以使用
延迟Bean的创建的优点是加快了容器的启动时间, 并且占用了较少的内存, 但是另外一方面, 如果在元数据中存在Bean的配置错误, 在对方案进行测试之前, 将无法发现这些错误.
将Bean定义为延迟初始化的时候需要格外注意, 如果其中一个依赖Bean被定义为预先初始化, 那么延迟定义将会没有任何作用, 预先Bean定义在启动期间被处理, 所以它也会触发处理延迟Bean的定义.
spring的执行流程分为下面几点:
总结下来就是:
->启动 Spring 容器
-> 实例化 Bean(分配内存空间,从⽆到有)
-> Bean 注册到 Spring 中(存操作)
-> 将 Bean 装配到需要的类中(取操作, getBean等)
所谓生命周期就是一个对象从诞生到销毁的整个生命过程, 我们把这个过程就叫做一个对象的生命周期.
Bean的生命周期分为以下五部分: