Spring 容器是用来存储和读取 Bean 的 , 因此 Bean 是 Spring 中最核心的操作资源.
编写代码过程中 , bean 对象如果有多个属性 , 创建 Getter , Setter, 构造方法 等方法 , 会产生大量冗长的代码. 那么为了使代码更加简洁 , 我们可以使用 Lombok 框架 , 只需要一行注释 , 就可以避免大量冗长的代码. 需要注意的是,Lombok并不是Java语言的一部分,而是一个第三方库,需要在项目中引入Lombok的jar包才能使用。同时,由于Lombok是通过注解来实现代码生成的,因此在使用Lombok时需要确保IDE和编译器支持注解处理。
Maven 中复制 lombok 依赖
pom.xml 插入 lombok 依赖
代码示例:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String name;
private int age;
private String email;
}
在这个示例中,我们使用了Lombok的@Data、@NoArgsConstructor和@AllArgsConstructor注解。
@Data注解可以自动生成getter、setter、equals、hashCode和toString等方法,省去了手动编写这些方法的麻烦。
@NoArgsConstructor注解可以自动生成无参构造函数,方便我们在创建对象时使用。
@AllArgsConstructor注解可以自动生成全参构造函数,方便我们在创建对象时同时设置对象的属性值。
假设有一个公共的 Bean , 提供给用户 A 和 用户 B 使用 , 如果 A 修改了 Bean 的公共数据 , 导致 B 在使用时发生与预期不符的错误.
创建一个实体类
@Setter
@Getter
@ToString
public class User {
private int id;
private String name;
}
存储 User 对象
@Component
public class UserBeans {
@Bean
public User user(){
//伪代码
User user = new User();
user.setName("王五");
user.setId(10);
return user;
}
}
UserController 修改 Bean 的公共数据:
@Controller
public class UserController {
@Autowired
private User user;
public void sayHi(){
System.out.println(user);
//修改 User
User myUser = user;
myUser.setName("张三");
System.out.println("myUser->" + myUser.getName());
}
}
UserController2 访问 Bean 中的公共数据:
@Controller
public class UserController2 {
@Resource
private User user;
public void sayHi2(){
System.out.println("user ->" + user);
}
}
启动类中调用:
public class App {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController = context.getBean("userController", UserController.class);
userController.sayHi();
UserController2 userController2 = context.getBean("userController2", UserController2.class);
userController2.sayHi2();
}
}
结果发现公共数据被修改:
出现上述问题的原因是 , Bean 默认状态下是单例模式 , 即所有人使用的都是同一个对象 , 那么当多个用户并发执行时 , 一定会篡改公共数据.
1. singleton(单例作用域)
2. prototype(原型作用域)
3. request(请求作用域)
4. session(会话作用域)
5. application(全局作用域)
**6. websocket(Http WebSocket 作用域) **
总结:
我们可以在存储Bean对象的时候通过 @Scope 注解设置作用域.
代码示例:
我们可以通过两种方式设置作用域:
存储 Bean 时设置作用域:
@Component
public class UserBeans {
@Bean
@Scope("prototype")
public User user(){
//伪代码
User user = new User();
user.setName("王五");
user.setId(10);
return user;
}
}
注入时会创建新对象:
@Controller
public class UserController {
@Autowired
private User user;
}
再次运行, 结果与预期一致.
为什么使用这么麻烦的方式 , 因为有自动提示 , 可以防止单词拼错 以及 使用不合法的作用域.
@Component
public class UserBeans {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public User user(){
//伪代码
User user = new User();
user.setName("王五");
user.setId(10);
return user;
}
}
所谓 Bean 的生命周期就是 Bean 从创建到销毁的过程.
大致流程分为以下五步:
1.实例化 Bean (为Bean 分配内存空间)
当 Spring 容器加载配置文件时 , 会根据其中注册的 Bean 利用反射机制 Bean 实例.
2.设置属性(Bean 对象的注入和装配)
Bean 实例化之后, Spring 容器会自动将配置文件中 , 定义的属性值注入到 Bean 实例中(包括基本类型, 对象, 集合).
3.Bean 初始化
4.使用 Bean
5.销毁 Bean 对象
例如: 将买房子视为 Bean 的生命周期
由此也可以得出 , 配置文件给 Bean 注入属性 , 必须排在初始化之前 , 因为初始化可能调用属性.(装修完才能安家具)
PostConstruct 版
@Component
public class UserBeans implements BeanNameAware {
@PostConstruct
public void PostConstruct(){
System.out.println("执行了 PostConstruct");
}
@PreDestroy
public void PreDestroy(){
System.out.println("执行了 PreDestroy");
}
@Override
public void setBeanName(String s) {
System.out.println("执行了 setName" + s);
}
}
xml 版
我们可以在配置文件中设置各种属性.
@Component
public class BeanComponent implements BeanNameAware {
@Override
public void setBeanName(String s) {
System.out.println("执行了通知 BeanName ->" + s);
}
//xml 方式的初始化方法
public void myInit(){
System.out.println("XML 方式初始化");
}
public void sayHi(){
System.out.println("执行 sayHi");
}
//xml 方式的销毁
public void preDestroy(){
System.out.println("执行了销毁方法");
}
}
调用启动类:
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
BeanComponent component = context.getBean("beanComponent" , BeanComponent.class);
component.sayHi();
context.destroy();
}
}
结果与预期一致:
Spring执行流程如下:
需要注意的是,Spring执行流程中的每一步都可以进行自定义的配置和处理,以满足不同的业务需求
用程序向Spring容器请求一个bean,Spring容器根据请求的名称或类型,从容器中返回一个bean实例。