在使用 Bean 的时候,一个公共的 Bean,交给 A用户 和 B用户 来使用,如果 A用户 偷偷修改了 Bean 的数据,那么 B用户 拿到的数据和预期的就不一样了。
先在 Spring 当中存储一个 User 对象:
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
A用户 拿到对象,并作出修改:
@Component
public class BeanScope1 {
@Autowired
private User user1;
public User getUser() {
User user = user1;
user.setName("李四");
return user;
}
}
把张三修改成了李四,再获取对象进行输出的时候:
@Component
public class BeanScope1 {
@Autowired
private User user1;
public User getUser() {
User user = user1;
user.setName("李四");
return user;
}
}
运行结果如下:
产生这样的原因是因为 Bean 在 Spring 中,默认情况下是单例状态,也就是所有人的使用都是同一个对像。这样可以很好的节约资源,避免资源的浪费。
作用域就是:Bean 在 Spring 中只有一份,它是全局共享的,那么当其他人修改了这个值之后,另外一个人读取到的就是被修改的值。
设置作用域的时候,只需要通过 @Scope 注解就可以了:
默认情况下的单例作用域:
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
BeanScope1 beanScope1 = context.getBean(BeanScope1.class);
User user1 = beanScope1.getUser();
System.out.println("BeanScope1:" + user1);
BeanScope2 beanScope2 = context.getBean(BeanScope2.class);
User user2 = beanScope2.getUser();
System.out.println("BeanScope2:" + user2);
}
}
可以发现 User 对象全被改了,防止被改的话,就在存 User 对象之前,给它设置 @Scope
也就是为了防止出现像上面这种情况,在使用的时候已经被别人修改。
@Component
public class Users {
@Bean(name = "user1")
@Scope("prototype")
public User user1() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
代码如下:
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
BeanScope1 beanScope1 = context.getBean(BeanScope1.class);
User user1 = beanScope1.getUser();
System.out.println("BeanScope1:" + user1);
BeanScope2 beanScope2 = context.getBean(BeanScope2.class);
User user2 = beanScope2.getUser();
System.out.println("BeanScope2:" + user2);
}
}
运行结果如下:
就是通过 prototype 对对象设置,每次一个请求就生成一个对象。
也就是设置 @Scope 的 ConfigurableBeanFactory.SCOPE_PROTOTYPE 来保证多例模式
@Component
public class Users {
@Bean(name = "user1")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public User user1() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
Spring 项目在运行的时候,会先启动容器,也就是我们在 main 方法里面写了这样的代码之后:
然后接下来就是根据配置完成 Bean 的初始化:
会通过这里的扫描路径,来存入 Bean 。将指定路径种带有五大类注解的普通类存入 Spring 当中,还有类里面带有方法注解的方法,其返回对象也存入 Spring 当中。
总的来说:先去启动容器,加载 xml 配置文件。然后,扫描五大类注解,随后,将具有五大类注解的类,存入 Spring 当中。如果 存入的过程中,存在属性的注入,就先执行属性的注入。然后,再继续执行 类 的 实例化。实例化之后,将其存入Spring 中。
生命周期就是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期。Bean 的生命周期有以下几步:
代码如下:
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class BeanLifeComponent implements BeanNameAware {
@PostConstruct
public void postConstruct() {
System.out.println("执⾏ PostConstruct()");
}
public void init() {
System.out.println("执⾏ BeanLifeComponent init-method");
}
@PreDestroy
public void preDestroy() {
System.out.println("执⾏:preDestroy()");
}
public void setBeanName(String s) {
System.out.println("执⾏了 setBeanName ⽅法:" + s);
}
}
XML 配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<content:component-scan base-package="com.component">
</content:component-scan>
<beans>
<bean id="beanLifeComponent"
class="com.component.BeanLifeComponent" init-method="init"></bean>
</beans>
</beans>
启动类:
import com.controller.BeanLife;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanLifeTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
BeanLife life = context.getBean(BeanLife.class);
System.out.println("执⾏ main ⽅法");
// 执⾏销毁⽅法
context.destroy();
}
}
运行结果如下:
造成两次通知,是因为 BeanLifeComponent 有注解,原先代码又配置了扫描路径,所以还会加载一次。
现设置属性,再初始化,是为了防止还没有注入的时候,就初始化,然后空指针异常。