目录
通过一个案例来理解Bean作用域的问题
Bean作用域定义
Bean的6种作用域
1.singleton
2.prototype
3.request
4.session
5.application
6.websocket
Bean的作用域的设置
Spring的执行流程
Bean的生命周期
打开一个Spring core项目:
创建一个User的实体类:
package com.java.demo.entity; import lombok.Getter; import lombok.Setter; import lombok.ToString; //lombok @Setter @Getter @ToString public class User { private int id; public String name; }
创建一个UserBeans类,将User存入Spring容器:
package com.java.demo.component; import com.java.demo.entity.User; import com.java.demo.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.Component; @Component public class UserBeans { @Bean public User user() { // 伪代码 User user = new User(); user.setId(1); user.setName("张三"); return user; } }
创建一个UserController类,通过属性注入的方式获取到Bean对象:
在这段代码里获取到对象之后,在方法内部将user赋值给myUser,通过myUser对对象进行修改.
那么这里修改myUser会不会对user产生影响呢?
package com.java.demo.controller; import com.java.demo.entity.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import javax.annotation.PostConstruct; @Controller public class UserController { @Autowired private User user; public void printUser() { System.out.println(user); // 修改 User User myUser = user; myUser.setName("悟空"); System.out.println("myUser -> " + myUser); System.out.println("user -> " + user); } }
在创建一个UserController2:
在这里我们也获取User对应的Bean对象,我们预期得到的是张三,而不是修改之后的悟空.
package com.java.demo.controller; import com.java.demo.entity.User; import org.springframework.stereotype.Controller; import javax.annotation.Resource; @Controller public class UserController2 { @Resource private User user; public void printUser2() { System.out.println("user -> " + user); } }
在启动类中获取到controller对象:
import com.java.demo.controller.UserController; import com.java.demo.controller.UserController2; 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"); UserController userController = context.getBean("userController",UserController.class); userController.printUser(); UserController2 userController2 = context.getBean("userController2",UserController2.class); userController2.printUser2(); } }
打印结果:
可以看到的是:UserController中myUser和user指向的是同一个对象,所以说都打印了悟空.
从最后一条打印结果可以看到:UserController2 获取的User的Bean对象是修改之后的悟空,而不是预期的张三,也就是说它们得到的是同一个Bean对象.
出现以上问题的原因就是:Bean默认情况下是单例状态,也就是所有人使用的都是同一个对象.使用单例可以很大程度上提高性能,所以在Spring中Bean的作用域默认也是singleton单例模式.
限定程序中变量的可用范围叫做作用域.而Bean的作用域是指Bean在Spring整个框架中的某种行为模式.
比如singleton单例作用域就表示Bean在整个Spring中只有一份,它是全局共享的,当其他人修改了这个值后,另一个人读取到的就是修改之后的值.
Spring容器在初始化一个Bean的实例时,同时也会指定该实例的作用域.
Bean作用域(Scope)包括:
单例作用域,默认的作用域,出于性能的考虑.该作用域下的Bean在IoC容器中只存在一个实例.通常无状态的Bean使用该作用域.无状态表示Bean对象的属性状态不需要更新.
原型作用域(多例作用域),每次对该作用域下的Bean的请求都会创建新的实例,及获取Bean和装配Bean都是新的实例.通常有状态的Bean会使用该作用域.
请求作用域,每次Http请求,都会创建一个Bean对象.适用于Spring MVC/Spring Web.
会话作用域,每次Session会话共享一个Bean.适用于Spring MVC.
全局作用域,一个http servlet context 中共享一个Bean.适用于Spring MVC.
HTTP WebSocket作用域,网络长连接,只适用于Spring WebSocket项目.
作用域的设置实在存储Bean对象的时候进行设置的,而不是在取的时候.
我们使用@Scope标签就可以来设置Bean的作用域.@Scope标签既可以修饰方法也可以修饰类.
有两种设置方式:
再次启动测试类:
Spring的执行流程:启动Spring容器 ->实例化Bean(分配内存空间,从无到有) ->Bean注册到Spring中(存) ->将Bean装配到需要的类中(取).
Bean从诞生到销毁的过程.
1.开辟内存空间:实例化.实例化不等于初始化.
2.设置属性(注入属性)
3.初始化
4.使用Bean
5.销毁Bean对象
代码演示:
package com.java.demo.component; import org.springframework.beans.factory.BeanNameAware; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; public class BeanComponent implements BeanNameAware { @Override public void setBeanName(String s) { System.out.println("执行了通知 BeanName ->" + s ); } //xml方式的初始化方法 public void myInit(){ System.out.println("xml方式初始化"); } @PostConstruct public void doPostConstruct(){ System.out.println("注解的初始化方法"); } public void sayHi(){ System.out.println("执行 sayHi()"); } @PreDestroy public void doPreDestroy(){ System.out.println("do PreDestroy"); } }
import com.java.demo.component.BeanComponent; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); BeanComponent beanComponent = context.getBean("beanComponent",BeanComponent.class); beanComponent.sayHi(); context.destroy(); } }
打印: