@Bean 注解是 Spring 框架中的一个注解,用于告诉 Spring 容器需要将被注解修饰的方法的返回值注册为一个 Bean。通常情况下,Spring 容器会自动扫描并创建带有 @Component 或其他类似注解的类,并将这些类的实例注册为 Bean。但对于某些特殊情况,我们可能需要手动配置某个对象的实例,或者需要调用一些特定的初始化逻辑来创建对象。这时就可以使用 @Bean 注解。总结一下,@Bean 注解用于告诉 Spring 容器需要注册一个 Bean,并提供了一个方法来创建该 Bean 的实例。通过 @Configuration 注解的配置类中使用 @Bean 注解,可以手动配置和创建特定的 Bean 实例,并在其他地方使用依赖注入的方式获取和使用这些 Bean。
Spring 是⽤来读取和存储 Bean,因此在 Spring 中 Bean 是最核⼼的操作资源,Bean是线程不安全的。
0.Lombok
0.1 了解Lombok
Lombok是一个Java库,它可以通过注解减少Java类中的样板代码。使用Lombok可以简化代码编写,提高开发效率。
以下是Lombok库的一些常用注解及其功能:
1. `@Getter` / `@Setter`: 自动生成字段的Getter和Setter方法。
2. `@ToString`: 自动生成toString方法。
3. `@EqualsAndHashCode`: 自动生成equals和hashCode方法。
4. `@NoArgsConstructor`: 自动生成无参构造方法。
5. `@AllArgsConstructor`: 自动生成全参构造方法。
6. `@Data`: 包含了`@Getter`、`@Setter`、`@ToString`、`@EqualsAndHashCode`和`@AllArgsConstructor`的组合注解,用于快速生成Getter、Setter、toString、equals、hashCode方法和全参构造方法。
除了上述注解外,Lombok还提供了很多其他常用的注解,如 `@NonNull`(标记字段不能为空)、`@Builder`(生成Builder模式的构造器)、`@Slf4j`(自动生成Logger对象)等。
使用Lombok时,需要在项目中添加Lombok的依赖,并在IDE中安装Lombok插件,以便支持注解的自动生成。
下面是一个使用Lombok的例子:
import lombok.Data; @Data public class User { private int id; private String name; }
在上述代码中,使用了`@Data`注解,它包含了`@Getter`、`@Setter`、`@ToString`、`@EqualsAndHashCode`和`@AllArgsConstructor`注解的功能。因此,不需要手动编写Getter、Setter、toString、equals、hashCode方法和全参构造方法,Lombok会自动帮我们生成这些代码。
0.2 将Lombok配置到idea
从maven中央仓库中搜索lombok,点击引用量最高的,找到1.18.24版本,复制到pom.xml
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency>
在idea中的File的setttings中下载lombok
1. 代码感受Bean的作用域
1.1 被修改的代码
1.1.1 设计思路
公共类UserBean的作者张三,类中设置name="zhangsan",id=1;
UserController1作者李四,通过使用setname修改,使得name="lisi";
UserController2作者王五,打印user,结果发现打印出来的name="lisi",而不是我们正常逻辑上的zhangsan.由此引出我们思考.......
1.1.2 代码
//Lombok注解,先从maven仓库中引入依赖,然后在settings中下载lombok插件
//@Data注解中就包含了他的哈希,tiString,get和set方法
@Data
public class User {
int id;
String name;
String password;
}
//作者:张三
@Component
public class UserBeans {
@Bean
public User user(){
User user=new User();
user.setId(1);
user.setName("zhangsan");
user.setPassword("1234");
return user;
}
}
//作者:李四
@Controller
public class UserController1 {
@Resource
private User user1;
public void getUser(){
System.out.println("张三:"+user1);
User user2=user1;
user2.setName("lisi");
System.out.println("李四:"+user2);
}
}
//作者:王五
@Controller
public class UserController2 {
@Resource
private User user1;
public void getUser(){
System.out.println("王五:"+user1);
}
}
public class MainApp {
public static void main(String[] args) {
// 加载Spring配置文件即创建Spring上下文;
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserController1 u1=context.getBean("userController1", UserController1.class);
u1.getUser();
UserController2 u2=context.getBean("userController2", UserController2.class);
u2.getUser();
}
}
1.1.3 观察现象
1.2 分析原因
操作以上问题的原因是因为 Bean 默认情况下是单例状态(singleton),所有⼈的使⽤的都是同⼀个对象(使⽤单例可以很⼤程度上提⾼性能,所以在 Spring 中 Bean 的作⽤域默认是 singleton 单例模式。
所以在上述作者为王五时就没有打印出zhangsan,而是被修改后的name ->"lisi"!
2. Bean的六种作用域
2.1 普通Spring项目中的作用域
singleton(Spring默认的作⽤域)——单例模式
该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀个对象。 通常⽆状态的Bean使⽤该作⽤域(⽆状态:Bean对象的属性状态不需要更新)
prototype——多例模式
proto中文含义:原生的;
每次对该作用域下的Bean的请求都会创建新的实例,获取Bean和装配Bean都是新的实例对象
该作用域适用于有状态的Bean(每次请求都会创建一个新的Bean实例。每次调用getBean()方法时,都会返回一个新的实例)
2.2
请求作用域:request
每次http请求都会创建新的Bean实例,类似于原型模式
限定在MVC/Web中使用,适用于一次http请求和响应的共享Bean
会话作用域(session)
再一次http session 中定义一个Bean实例
用户共享Bean,比如记录用户的登录信息,限定在MVC中使用
全局作用域(application)
在一个http servlet Context中定义一个Bean实例
使用场景:Web应用的上下文信息,比如记录一个应用的共享信息,限定在MVC中使用
作用于servlet容器
websocket
网络长连接,在一个HTTPWebSocket的生命周期中,定义一个Bean实例
使用场景,在WebSocket的每次会话中,保存了一个Map结构的头信息,用来包裹客户端头信息,第一次初始化后,知道WebSocket结束,都是同一个Bean,限定在Spring WebSocket中使用
2.3单例作⽤域(singleton)和全局作⽤域(application)区别💀
singleton 作⽤于 IoC 的容器,⽽ application 作⽤于 Servlet 容器。
singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域;
3.设置作用域(Spring如何在并发情况下获取不完整的Bean)
以改为多例模式的prototype为例
写法一:@Scope("prototype")
写法二:@Scope(
ConfigurableBeanFactory.SCOPE_PROTOTYPE)
打印结果