魔天记3d vivo客户端
374.22MB · 2025-12-16
简单来说,Bean就是由Spring IoC容器管理的对象。理解Bean的工作原理,就像掌握了Spring的"内功心法",能让你的编程水平更上一层楼。
刚开始学习Spring时,我以为所有Bean都是单例的,后来才发现Spring提供了多种作用域,就像给Bean赋予了不同的"生存方式"。
// 单例模式 - 就像公司里的CEO,只有一个
@Component
@Scope("singleton") // 这个注解其实可以省略,因为默认就是单例
public class SingletonService {
// 整个应用中只有一个实例,大家都共享这个CEO
}
// 原型模式 - 就像一次性纸杯,每次都需要新的
@Component
@Scope("prototype")
public class PrototypeService {
// 每次有人需要时,都会创建一个新实例
// 适合有状态的场景,比如购物车
}
// Web相关作用域
@Component
@Scope("request") // 每个HTTP请求一个实例,请求结束就销毁
public class RequestScopedService {
// 适合存储请求相关的数据
}
@Component
@Scope("session") // 每个用户会话一个实例
public class SessionScopedService {
// 适合存储用户登录信息等
}
很多同学会困惑:什么时候用单例?什么时候用原型?我总结了一个对比表:
| 场景 | 推荐作用域 | 原因 | 实际例子 |
|---|---|---|---|
| 工具类、服务类 | 单例 | 无状态,线程安全 | UserService、EmailUtil |
| 需要隔离状态的类 | 原型 | 避免线程安全问题 | ShoppingCart、UserContext |
| 成本高的对象 | 单例 | 节省资源 | DatabaseConnection、HttpClient |
| 轻量级状态对象 | 原型 | 避免内存泄漏 | RequestLogger、TransactionContext |
新手常踩的坑:在单例Bean中使用可变状态
@Service
public class PaymentService {
private double amount; // 危险!所有用户共享这个变量
// 错误示例
public void processPayment(double paymentAmount) {
this.amount = paymentAmount; // 会被其他用户覆盖
// ... 处理支付
}
// 正确做法 - 使用方法参数
public void processPayment(double amount) {
// 使用局部变量或参数,不保存状态
// ... 处理支付
}
}
虽然Spring自带的作用域已经够用,但了解自定义作用域能加深理解:
@Configuration
public class LearningConfig {
@Bean
public static CustomScopeConfigurer customScopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
Map<String, Object> scopes = new HashMap<>();
scopes.put("learning", new SimpleThreadScope()); // 线程级别作用域
configurer.setScopes(scopes);
return configurer;
}
}
@Service
@Scope("learning") // 现在每个线程都有自己的实例
public class LearningService {
// 这个Bean在每个线程中都是独立的
}
理解Bean的生命周期很重要,它能帮你解决很多诡异的问题。想象一下,一个Bean从出生到死亡经历了什么?
我画了一个简单的流程图,帮助大家理解:
Bean出生:
↓
构造函数调用 (Bean诞生)
↓
属性注入 (给Bean装备)
↓
Aware接口回调 (Bean自我认知)
↓
BeanPostProcessor前置处理 (初步加工)
↓
@PostConstruct (Bean的"满月酒")
↓
InitializingBean (Bean学会技能)
↓
自定义init方法 (特殊训练)
↓
BeanPostProcessor后置处理 (最终打磨)
↓
Bean准备就绪 (正式上岗!)
Bean死亡:
↓
容器关闭信号
↓
@PreDestroy (临终遗言)
↓
DisposableBean (交接工作)
↓
自定义destroy方法 (清理现场)
让我们通过代码来实际观察这个生命周期:
@Component
public class StudentBean implements
BeanNameAware, InitializingBean, DisposableBean {
private String name;
// 1. 构造方法 - Bean诞生
public StudentBean() {
System.out.println(" 1. 构造函数执行 - StudentBean诞生了!");
}
// 2. 属性注入 - 给Bean装备
@Autowired
public void setName(Environment env) {
this.name = "Spring学习者";
System.out.println("️ 2. 依赖注入完成 - 名字设置为: " + this.name);
}
// 3. 了解自己的名字
@Override
public void setBeanName(String name) {
System.out.println(" 3. Bean自我认知 - 我在容器中的名字是: " + name);
}
// 4. 初始化前的准备
@PostConstruct
public void postConstruct() {
System.out.println(" 4. @PostConstruct执行 - 初始化前的准备完成");
}
// 5. 属性设置后的初始化
@Override
public void afterPropertiesSet() {
System.out.println(" 5. InitializingBean执行 - 所有属性都已设置,准备就绪!");
}
// 6. 业务方法 - Bean正式工作
public void study() {
System.out.println(" 6. 业务方法执行 - 正在学习Spring...");
}
// 7. 销毁前的清理
@PreDestroy
public void preDestroy() {
System.out.println(" 7. @PreDestroy执行 - 开始清理资源...");
}
// 8. 销毁方法
@Override
public void destroy() {
System.out.println(" 8. DisposableBean执行 - StudentBean生命结束");
}
}
运行这个Bean,你会在控制台看到完整的生命周期日志!
BeanPostProcessor是Spring提供的一个扩展点,可以在Bean初始化前后进行增强:
@Component
public class LearningBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof StudentBean) {
System.out.println(" BeanPostProcessor前置处理 - 给Bean加点魔法");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof StudentBean) {
System.out.println(" BeanPostProcessor后置处理 - Bean已经准备好大展身手了!");
}
return bean;
}
}
刚开始学Spring Boot时,我最惊讶的就是:为什么我什么都没配置,应用就能跑起来?这就是自动配置的魔力!
想象一下,Spring Boot就像一个贴心的助手,它说:"如果你不告诉我具体怎么做,我就按照最常用的方式帮你搞定。"
核心机制:
@SpringBootApplication // 这个注解包含了很多魔法
public class LearningApplication {
public static void main(String[] args) {
SpringApplication.run(LearningApplication.class, args);
}
}
@SpringBootApplication 实际上包含了 @EnableAutoConfiguration,它会:
META-INF/spring.factories 文件Spring Boot不会盲目配置,它很聪明:
@Configuration
// 只有当类路径下有DataSource类时才生效
@ConditionalOnClass(DataSource.class)
// 只有当配置了数据库URL时才生效
@ConditionalOnProperty(name = "spring.datasource.url")
// 只有当没有自定义DataSource时才生效
@ConditionalOnMissingBean(DataSource.class)
public class DataSourceAutoConfiguration {
@Bean
public DataSource dataSource() {
// 自动创建数据源
return DataSourceBuilder.create().build();
}
}
这种"条件化"配置让Spring Boot既智能又灵活。
让我们创建一个学习用的自动配置,加深理解:
// 1. 定义配置属性(可以在application.yml中配置)
@ConfigurationProperties(prefix = "learning.config")
@Data
public class LearningProperties {
private String studentName = "默认学生";
private int studyHours = 8;
}
// 2. 创建学习服务
public class LearningService {
private final String studentName;
private final int studyHours;
public LearningService(String studentName, int studyHours) {
this.studentName = studentName;
this.studyHours = studyHours;
}
public void study() {
System.out.println(studentName + " 每天学习 " + studyHours + " 小时");
}
}
// 3. 创建自动配置类
@Configuration
@EnableConfigurationProperties(LearningProperties.class)
@ConditionalOnClass(LearningService.class) // 有LearningService类才生效
@ConditionalOnProperty(prefix = "learning.config", value = "enabled", matchIfMissing = true)
public class LearningAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 如果用户没有自定义,才用我们的
public LearningService learningService(LearningProperties properties) {
return new LearningService(properties.getStudentName(), properties.getStudyHours());
}
}
在 application.yml 中配置:
learning:
config:
student-name: "小明"
study-hours: 6
enabled: true
现在,你不需要手动创建 LearningService,Spring Boot会自动帮你搞定!
想知道Spring Boot背着你做了什么?打开调试模式:
# application.properties
debug=true
启动应用时,你会看到类似这样的输出:
Positive matches: (启用的自动配置)
-----------------
DataSourceAutoConfiguration matched:
- @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType' (OnClassCondition)
Negative matches: (未启用的自动配置)
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)
经过上面的学习,我们来总结一下关键点:
@Component
public class BestPracticeBean {
// 推荐:在@PostConstruct中进行轻量级初始化
@PostConstruct
public void init() {
// 数据校验、缓存预热等
validateConfig();
warmUpCache();
}
// 避免:在构造函数中进行复杂操作
public BestPracticeBean() {
// 不要在这里调用其他Bean的方法!
// 因为依赖注入还没有完成
}
// 推荐:使用@PreDestroy清理资源
@PreDestroy
public void cleanup() {
// 关闭文件、释放连接等
releaseResources();
}
}
debug=true@SpringBootApplication(exclude = {SomeAutoConfiguration.class})application.yml 中调整参数学习Spring的这些核心概念时,我最大的体会是:
记住,每个Spring高手都是从理解Bean开始的。希望这篇博客能帮助你在Spring的学习道路上走得更远!
学习路上不孤单,遇到问题多实践!
PS: 如果在学习过程中遇到问题,欢迎在评论区留言讨论,我们一起进步!