属性填充与Aware接口详解

目录

  • Spring Boot自动装配机制
  • 用户属性赋值 vs 容器属性赋值
  • Aware接口
  • Aware接口修改 vs 重新生成Bean

Spring Boot自动装配机制

自动装配的流程

引入起步依赖 → 配置yaml属性 → Spring Boot自动创建Bean

即:起步依赖 + yaml配置 = 自动创建Bean

常见自动装配示例

RabbitMQ自动装配

XML


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-amqpartifactId>
dependency>

YAML

# 配置属性
spring:
  rabbitmq:
    host: 192.168.28.26
    port: 5672
    username: itcast
    password: 123321
    virtual-host: /
    publisher-confirm-type: correlated
    publisher-returns: true
    template:
      mandatory: true

自动创建的Bean:

  • RabbitTemplate
  • ConnectionFactory
  • RabbitAdmin
其他常见自动装配
  • MyBatis
    自动生成的Bean:SqlSessionFactory, SqlSessionTemplate, DataSource

  • Redis

    自动生成的Bean:RedisTemplate, StringRedisTemplate, LettuceConnectionFactory

  • Web

    自动生成的Bean:DispatcherServlet, HandlerMapping, HandlerAdapter

  • JPA
    自动生成的Bean:EntityManagerFactory, JpaTransactionManager


用户属性赋值 vs 容器属性赋值

两者在Bean生命周期中的位置:实例化—>用户属性赋值—>容器属性赋值

更完整的Bean生命周期参考链接:Bean完整生命流程

两者对比

用户属性赋值(配置属性绑定)

将yaml/properties配置文件中的配置值直接赋值到Bean的对应属性中。这是一个单纯的值拷贝过程,将外部配置文件中的字符串、数字、布尔值等基本数据类型绑定到Bean的属性上。

特点:

  • 数据来源:配置文件(yaml/properties)
  • 赋值内容:基本数据类型(String、int、boolean等)
  • 实现机制:@ConfigurationProperties注解 + Spring的配置属性绑定器
  • 本质:配置值 → Bean属性

容器属性赋值(依赖注入)

将Spring容器中已经创建好的Bean对象注入到当前Bean的属性中。这是Bean与Bean之间的对象引用关系建立,实现了组件之间的依赖关系。

特点:

  • 数据来源:Spring容器中的其他Bean对象
  • 赋值内容:Bean对象引用
  • 实现机制:@Autowired@Resource等注解 + Spring的依赖注入器
  • 本质:Bean对象 → Bean属性

两者对比总结

特征 用户属性赋值 容器属性赋值
本质 配置值拷贝 对象引用注入
数据类型 基本数据类型、字符串 Bean对象
数据来源 外部配置文件 Spring容器
关系建立 配置与Bean的绑定 Bean与Bean的依赖关系

核心区别: 用户属性赋值是"值的传递",容器属性赋值是"引用的传递"。

实际代码示例

用户属性赋值示例

配置文件

myapp:
  name: "我的应用"
  port: 8080
  enabled: true

属性赋值

@Component
@ConfigurationProperties(prefix = "myapp")
public class MyAppProperties {
    private String name;
    private int port;
    private boolean enabled;
    
    // Spring会调用这些setter进行用户属性赋值
    public void setName(String name) { this.name = name; }
    public void setPort(int port) { this.port = port; }
    public void setEnabled(boolean enabled) { this.enabled = enabled; }
}
容器属性赋值示例
@Service
public class OrderService {
    
    private UserService userService;        // ← 需要容器属性赋值
    private PaymentService paymentService;  // ← 需要容器属性赋值
    
    // Spring会调用这个构造函数进行容器属性赋值
    public OrderService(UserService userService, PaymentService paymentService) {
        this.userService = userService;         // ← 容器属性赋值
        this.paymentService = paymentService;   // ← 容器属性赋值
    }
}

Aware接口

Aware接口分类

  • 获取容器核心资源类
    • ApplicationContextAware 获取整个应用上下文
    • BeanFactoryAware 获取Bean工厂
    • EnvironmentAware 获取环境配置
  • 获取Bean元信息类
    • BeanNameAware 获取Bean名称
    • BeanClassLoaderAware 获取类加载器
  • 获取Web相关资源类
    • ServletContextAware 获取Servlet上下文
    • ApplicationEventPublisherAware 获取事件发布器

Aware接口的本质

核心观点: 所有Aware接口在技术实现上都是相同的,都是回调接口,只是通过命名进行语义化区分明确实现该接口对应的函数对应的职责

类比@Componen

  • @Component 通用组件
  • @Service 业务逻辑层 - 语义化命名
  • @Controller 控制层 - 语义化命名
  • @Repository 数据访问层 - 语义化命名

Aware接口同理:

本质相同,但语义化命名

  • ApplicationContextAware 明确表示:我要获取ApplicationContext
  • EnvironmentAware 明确表示:我要获取Environment
  • BeanNameAware 明确表示:我要获取Bean名称

Aware接口的统一模式

// 所有Aware接口都遵循这个模式
public interface XxxAware {
    void setXxx(Xxx xxx);  // 回调方法
}

底层实现原理

// Spring源码简化版
public class AbstractAutowireCapableBeanFactory {
    
    private void invokeAwareMethods(final String beanName, final Object bean) {
        if (bean instanceof Aware) {
            // 所有Aware接口的处理逻辑都类似
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(this);
            }
            // ... 其他Aware接口处理逻辑相同
        }
    }
}

Aware接口 vs 重新生成Bean

​ 当需要使用第三方Bean时,需要对Bean进行额外的配置,这时候可以使用Aware接口对SpringBoot自动装配的Bean进行配置或者直接重新生成Bean

两种方案示例

1:Aware接口完善现有Bean
@Slf4j
@Configuration
public class CommonConfig implements ApplicationContextAware {

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        // 通过Bean容器获取Spring Boot自动创建的Bean
        RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
        
        // 对现有Bean进行修改和功能增强
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            log.error("消息发送到队列失败,响应码: {},失败原因:{},交换机:{},路由key:{},消息:{}", 
                     replyCode, replyText, exchange, routingKey, message);
        });
    }
}
2:重新生成Bean
@Configuration
public class RabbitConfig {
    
    @Bean
    @Primary  // 解决Bean冲突
    public RabbitTemplate customRabbitTemplate(
            ConnectionFactory connectionFactory,
            @Autowired(required = false) RabbitTemplateConfigurer configurer) {
        
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        
        // 应用Spring Boot的默认配置
        if (configurer != null) {
            configurer.configure(template, connectionFactory);
        }
        
        // 设置mandatory=true,否则ReturnCallback不会触发
        template.setMandatory(true);
        
        // 自定义配置
        template.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            log.error("消息发送到队列失败,响应码: {},失败原因:{},交换机:{},路由key:{},消息:{}", 
                     replyCode, replyText, exchange, routingKey, message);
        });
        
        return template;
    }
}

两种方案对比

特征 Aware接口修改 重新生成Bean
Bean数量 1个(修改现有) 可能2个
配置复杂度 简单 复杂(需要处理自动配置)
学习成本
风险程度 中(可能配置冲突)
灵活性
适用场景 简单定制 复杂定制、生产环境

重新生成Bean的潜在问题

1. Bean冲突问题

​ 由Spring Boot自动创建的第三方Bean一般带有@ConditionalOnMissingBean注解,这个注解的作用是只有当容器中没有这个类型(默认是类型,可以改为按照名称)的Bean时,才创建

比如:

@Bean
@ConditionalOnMissingBean  // 关键条件
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
}

​ 翻译成白话: 如果Spring容器中还没有RabbitTemplate类型的Bean,那我就创建一个;如果已经有了,那我就不创建了,避免冲突。

但是出于某些原因,并不是所有的第三方Bean都会有这个注解,此时如果再自己创建就可能会出现依赖冲突的问题

解决方案:

  1. @Primary

​ 在自定义Bean上同时添加@Primary注解,确保冲突时会注入自己的Bean

@Bean
@Primary  // 明确指定优先级
public RabbitTemplate customRabbitTemplate(...) {
    // 确保注入时使用这个Bean
}
  1. 使用特定的Bean名称
@Bean("myRabbitTemplate")  // 使用特定名称
public RabbitTemplate customRabbitTemplate(...) { ... }

@Service
public class MessageService {
    @Autowired
    @Qualifier("myRabbitTemplate")  // 明确指定注入哪个Bean
    private RabbitTemplate rabbitTemplate;
}
  1. 排除自动配置
@SpringBootApplication(exclude = {RabbitAutoConfiguration.class}) //完全禁用RabbitMQ自动配置
public class Application {
    // 只使用自己的配置
}
2. 自动配置丢失

​ 如果使用自己定义的Bean,不使用Spring自动创建的Bean,并且不显式的应用Spring Boot的默认配置,使用就会丢失Spring Boot的配置属性自动绑定)功能,即无法自动赋值yaml文件中定义的属性了,这部分属性也需要自己设置,一不小心就有可能丢失某些配置,比如:

  • 连接池配置
  • 序列化配置
  • 重试机制
  • 超时设置
  • 消息转换器

你可能感兴趣的:(spring,学习,java,笔记,spring,boot)