Skip to content

Bean的配置与管理

Bean的定义

三种配置方式

Spring 7支持三种Bean配置方式:XML、注解、Java配置。

XML配置(传统方式)

xml
<!-- beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="userService" class="com.example.service.UserService">
        <property name="userRepository" ref="userRepository"/>
    </bean>
    
    <bean id="userRepository" class="com.example.repository.UserRepositoryImpl"/>
</beans>

注解配置

java
@Repository
public class UserRepositoryImpl implements UserRepository {
}

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
}

Java配置(推荐)

java
@Configuration
public class AppConfig {
    
    @Bean
    public UserRepository userRepository() {
        return new UserRepositoryImpl();
    }
    
    @Bean
    public UserService userService() {
        return new UserService(userRepository());
    }
}

Bean的作用域

五种作用域

作用域说明使用场景
singleton单例(默认)无状态Bean
prototype多例有状态Bean
request请求级别Web应用
session会话级别Web应用
application应用级别Web应用

singleton(单例)

java
@Service
@Scope("singleton")  // 默认值,可省略
public class UserService {
    // 整个应用只有一个实例
}

prototype(多例)

java
@Service
@Scope("prototype")
public class ShoppingCart {
    // 每次获取都创建新实例
}

Web作用域

java
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestInfo {
    // 每个HTTP请求一个实例
}

@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserPreferences {
    // 每个HTTP会话一个实例
}

Bean的生命周期

生命周期阶段

实例化 → 属性赋值 → 初始化 → 使用 → 销毁

生命周期回调

java
@Service
public class UserService implements InitializingBean, DisposableBean {
    
    private UserRepository userRepository;
    
    // 属性注入
    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
        System.out.println("2. 属性注入");
    }
    
    // 初始化方法1:@PostConstruct
    @PostConstruct
    public void init() {
        System.out.println("3. @PostConstruct初始化");
    }
    
    // 初始化方法2:InitializingBean接口
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("4. afterPropertiesSet初始化");
    }
    
    // 初始化方法3:@Bean的initMethod
    // @Bean(initMethod = "customInit")
    public void customInit() {
        System.out.println("5. 自定义初始化方法");
    }
    
    // 销毁方法1:@PreDestroy
    @PreDestroy
    public void destroy() {
        System.out.println("销毁:@PreDestroy");
    }
    
    // 销毁方法2:DisposableBean接口
    @Override
    public void destroy() throws Exception {
        System.out.println("销毁:DisposableBean");
    }
    
    // 销毁方法3:@Bean的destroyMethod
    // @Bean(destroyMethod = "customDestroy")
    public void customDestroy() {
        System.out.println("销毁:自定义销毁方法");
    }
}

执行顺序

1. 构造方法
2. 属性注入
3. @PostConstruct
4. InitializingBean.afterPropertiesSet()
5. @Bean(initMethod)

   使用Bean

6. @PreDestroy
7. DisposableBean.destroy()
8. @Bean(destroyMethod)

条件化Bean

@Conditional

java
@Configuration
public class AppConfig {
    
    @Bean
    @Conditional(LinuxCondition.class)
    public UserService linuxUserService() {
        return new LinuxUserService();
    }
    
    @Bean
    @Conditional(WindowsCondition.class)
    public UserService windowsUserService() {
        return new WindowsUserService();
    }
}

常用条件注解

java
@Configuration
public class AppConfig {
    
    // 属性条件
    @Bean
    @ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true")
    public CacheService cacheService() {
        return new RedisCacheService();
    }
    
    // 类存在条件
    @Bean
    @ConditionalOnClass(name = "redis.clients.jedis.Jedis")
    public RedisClient redisClient() {
        return new JedisClient();
    }
    
    // Bean存在条件
    @Bean
    @ConditionalOnBean(DataSource.class)
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate();
    }
    
    // Bean不存在条件
    @Bean
    @ConditionalOnMissingBean
    public UserService defaultUserService() {
        return new DefaultUserService();
    }
    
    // 表达式条件
    @Bean
    @ConditionalOnExpression("${app.feature.enabled} and ${app.feature.premium}")
    public PremiumService premiumService() {
        return new PremiumService();
    }
}

Profile环境切换

定义Profile Bean

java
@Configuration
public class DataSourceConfig {
    
    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .build();
    }
    
    @Bean
    @Profile("prod")
    public DataSource prodDataSource() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        dataSource.setUsername("root");
        dataSource.setPassword("password");
        return dataSource;
    }
}

激活Profile

yaml
# application.yml
spring:
  profiles:
    active: dev
bash
# 命令行
java -jar app.jar --spring.profiles.active=prod

# 环境变量
export SPRING_PROFILES_ACTIVE=prod

# 代码方式
SpringApplication.run(App.class, "--spring.profiles.active=dev");

Profile注解

java
@Configuration
@Profile("dev")
public class DevConfig {
    // 仅在dev环境生效
}

@Configuration
@Profile("!prod")
public class NonProdConfig {
    // 非prod环境生效
}

Bean的延迟加载

@Lazy

java
@Configuration
public class AppConfig {
    
    @Bean
    @Lazy
    public UserService userService() {
        System.out.println("创建UserService");
        return new UserService();
    }
}

@Service
@Lazy
public class LazyService {
    // 首次使用时才创建
}

Bean的继承

Java配置继承

java
public class BaseService {
    protected String name;
    
    public void setName(String name) {
        this.name = name;
    }
}

@Configuration
public class AppConfig {
    
    @Bean
    public BaseService baseService() {
        BaseService service = new BaseService();
        service.setName("Base");
        return service;
    }
    
    @Bean
    public UserService userService() {
        UserService service = new UserService();
        service.setName("User");  // 覆盖父类属性
        return service;
    }
}

Bean的方法注入

@Lookup

java
@Service
public class UserService {
    
    // 每次调用都返回新的prototype Bean
    @Lookup
    protected ShoppingCart getShoppingCart() {
        return null;  // Spring会覆盖此方法
    }
    
    public void process() {
        ShoppingCart cart = getShoppingCart();
        // 使用cart
    }
}

@Component
@Scope("prototype")
public class ShoppingCart {
}

下一步

继续学习 AOP面向切面编程,了解Spring的切面编程能力。