Posted in

Bean 配置问题的案例分享_AI阅读总结 — 包阅AI

包阅导读总结

1. `Bean配置`、`Spring`、`依赖注入`、`Tair`、`问题排查`

2. 本文分享了在项目开发中遇到的bean注入不生效问题,探讨了Spring中bean配置的方式及相关注解,经过排查找到原因并给出解决方案,强调配置Bean时使用@Qualifier指定注入的bean以避免问题。

3.

– 问题描述

– 在项目中,胖客户端内配置的tair bean在应用A生效,在应用B不生效。

– 排查过程

– 排除@Value默认值未生效。

– 尝试将java配置bean方式改为xml配置生效。

– 最终发现是TairManager注入错误。

– 相关知识

– Spring配置bean的方式:XML文件配置、注解配置、JavaConfig配置。

– 注解自动装配:@Autowired、@Qualifier、@Resource等。

– 解决方案:在java配置bean时通过@Qualifier指定bean。

思维导图:

文章地址:https://mp.weixin.qq.com/s/V1i1n9XkBDpZrGljdOpF6Q

文章来源:mp.weixin.qq.com

作者:遇舟

发布时间:2024/7/22 10:13

语言:中文

总字数:3640字

预计阅读时间:15分钟

评分:83分

标签:Java开发,Spring框架,企业级应用,Spring Bean配置,依赖注入


以下为原文内容

本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com

最近在某个项目的开发过程中,遇到了一个bean注入不生效的问题,本文主要针对该问题进行展开,欢迎大家共同探讨。


该项目涉及到两个应用,其中一个应用A需要给另一个应用B打一个胖客户端(Fat Client),该胖客户端的代码在两个应用中都有调用。胖客户端内需要配置一个tair bean,但是该bean配置在A中生效,但是在B中却没有生效。


相关知识点

还记得Spring是怎么配置bean的吗?在Spring中总体来看可以通过三种方式来配置对象:

  • 使用XML文件配置

  • 使用注解来配置

  • 使用JavaConfig来配置

注解自动装配

@Autowired:用于构造器、方法、参数或字段上,表明需要自动注入一个Bean。Spring会自动装配匹配的Bean。


@Qualifier:与@Autowired一起使用时,指定要注入的Bean的名称,以避免与其他Bean的混淆。


@Resource:来自JDK,类似于@Autowired,但默认是按名称装配,也可以混合使用。


@Inject:来自javax.inject包,类似于@Autowired,属于JSR-330标准的一部分。


@Resource和@Autowired注解有什么区别呢?

@Autowired是Spring框架提供的注解,主要用于根据类型自动装配依赖项。

行为和特性

  1. 按类型装配:默认情况下,@Autowired按类型自动装配Bean。

  2. 可选依赖:如果你的依赖是可选的,可以使用required=false设置:

  3. 构造器、方法或字段:可以用在构造器,属性字段或Setter方法上。

  4. 结合@Qualifier:可以和@Qualifier结合使用以实现按名称装配。

  5. 作为Spring特有的注解,它更深度地集成在Spring的生态系统中,更适合与其他Spring注解一起使用。

@Resource是JDK提供的注解,属于Java依赖注入规范(JSR-250)的一部分。

行为和特性:

  1. 按名称装配:默认情况下,@Resource按名称装配。如果没有匹配到名称,再按类型装配。

  2. 不支持required属性:与@Autowired不同,@Resource不支持required属性。

  3. 可以用于字段和Setter方法:虽然也可以用于构造器,但不常见。通常用在字段或Setter方法上。

  4. 由于是JavaEE规范的一部分,它可以与其他JavaEE注解(如@PostConstruct和@PreDestroy)更好地配合使用。


  • @Component:标注一个类为Spring管理的组件。类似的注解还有:

  • @Service:表示服务层组件。

  • @Repository:表示DAO(数据访问层)组件。

  • @Controller:表示Spring MVC控制器组件。

  • @Primary:当一个类型有多个Bean时,在不使用@Qualifier的情况下,Spring会优先选择标注了@Primary的Bean。

  • @Scope:用于指定Bean的作用域,如singleton、prototype。

Java配置类

那么怎么通过java代码来配置bean呢?

以本次用到的bean配置为例:

@Configuration@Slf4j(topic = "config")public class XxxTairConfig {
@Value("${spring.tmg.xxx.tair.username:默认值}") private String username;
@Value("${spring.tmg.xxx.tair.namespace:默认值}") private Integer namespace;
@Bean(initMethod = "init") public TairManager tmgXxxTairManager() { MultiClusterTairManager tairManager = new MultiClusterTairManager(); tairManager.setUserName(username); tairManager.setDynamicConfig(true); return tairManager; }
@Bean public TairAccessor tmgXxxTairAccessor(@Qualifier("tmgXxxTairManager") TairManager tmgXXXTairManager) { TairAccessorImpl tairAccessor = new TairAccessorImpl(); tairAccessor.setTairManager(tmgXxxTairManager); tairAccessor.setNamespace(namespace); return tairAccessor; }}

xml配置

还是以本次用到的bean配置为例:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:context="http://www.springframework.org/schema/context"  xsi:schemaLocation="  http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="tmgXxxTairManager" class="com.taobao.tair.impl.mc.MultiClusterTairManager" init-method="init"> <property name="userName"> <value>tair用户名参数</value> </property> <property name="timeout"> <value>50</value> </property> </bean> <bean id="tmgXxxTairAccessor" class="com.alibaba.tmallg.gcommon.tair.TairAccessorImpl"> <property name="tairManager" ref="tmgXxxTairManager"/> <property name="namespace" value="tair namespace参数"/> </bean>
</beans>


问题描述


为什么我的bean配置没生效?

最早,在胖客户端中,是通过Java配置类配置的tair的bean。最早的代码如下:

@Configuration@Slf4j(topic = "config")public class XxxTairConfig {
@Value("${spring.tmg.xxx.tair.username:默认值}") private String username;
@Value("${spring.tmg.xxx.tair.namespace:默认值}") private Integer namespace;
@Bean(initMethod = "init") public TairManager tmgXxxTairManager() { MultiClusterTairManager tairManager = new MultiClusterTairManager(); tairManager.setUserName(username); tairManager.setDynamicConfig(true); return tairManager; }
@Bean public TairAccessor tmgXxxTairAccessor(TairManager tmgXxxTairManager) { TairAccessorImpl tairAccessor = new TairAccessorImpl(); tairAccessor.setTairManager(tmgXxxTairManager); tairAccessor.setNamespace(namespace); return tairAccessor; }}

在应用A中,该配置没有问题,tair也可以正常查询。


但是部署到应用B后,奇怪的问题出现了:同样的key,应用A可以查到,tair控制台也可以查到,但是应用B死活查不出来。


排查


排查过程


难道是因为username的@Value默认值没生效?因为debug可以看到namespace属性,符合预期,但是username属性看不到,我最早怀疑是username没配置成功。但namespace是生效的,理论上不应该部分不生效呀?抱着试试看的态度,遂尝试将username直接写死成目标值。重新部署后,依然不行,看来不是@Value的锅。


中间我尝试将java配置bean的方式,改成用xml配置,这样是生效的,可以解决问题。


但是为什么java配置bean的方式不生效呢?


还原后,继续debug,感觉依旧有些摸不着头脑。后来请团队同学帮忙看了下,一开始也觉得奇怪,tair各个单元都是有数据的,和单元化也没关系,后来点开tairManager属性值看到mdbcomm,问了句,我们的tair用的是mdb吗?最后打开控制台一看,是ldb……


Spring依赖注入优先级


原来是TairManager注入的不是我们在配置类中配置的tmgXxxTairManager?!

可是tmgXxxTairManager也是一个bean,不应该也是唯一的吗,难道这里不是按照bean名字注入的吗?


那这里依赖注入的优先级是什么呢?


在使用@Resource 注解进行依赖注入时,优先级规则如下:

明确指定名称

字段或属性名称匹配:


类型匹配:


类型匹配:

名称匹配结合@Qualifier


使用@Primary


按名称匹配字段或属性名称


NoUniqueBeanDefinitionException:


Spring 框架在处理@Bean 方法中的参数时,默认的行为与@Autowired 注解的工作方式是一致的。


例如本文中涉及到的tmgXxxTairManager参数,注入的并不一定是上面定义的tmgXxxTairManager bean

    @Bean    public TairAccessor tmgXxxTairAccessor(TairManager tmgXxxTairManager) {        TairAccessorImpl tairAccessor = new TairAccessorImpl();        tairAccessor.setTairManager(tmgXxxTairManager);        tairAccessor.setNamespace(namespace);        return tairAccessor;    }



xml配置bean


在使用XML配置Bean时,ref元素通常是用来引用其他已经定义的Bean,并且是通过Bean的id来进行引用和注入的。这种方法使得在 XML 配置的 Spring 应用程序中可以灵活地管理和注入依赖。


这也解释了为什么中间改成用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"  xmlns:context="http://www.springframework.org/schema/context"  xsi:schemaLocation="  http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="tmgXxxTairManager" class="com.taobao.tair.impl.mc.MultiClusterTairManager" init-method="init"> <property name="userName"> <value>tair用户名参数</value> </property> <property name="timeout"> <value>50</value> </property> </bean> <bean id="tmgXxxTairAccessor" class="com.alibaba.tmallg.gcommon.tair.TairAccessorImpl"> <property name="tairManager" ref="tmgXxxTairManager"/> <property name="namespace" value="tair namespace参数"/> </bean>
</beans>

java配置bean

通过@Qualifier指定bean,避免受@Primary等因素的影响。

@Configuration@Slf4j(topic = "config")public class XxxTairConfig {
@Value("${spring.tmg.xxx.tair.username:默认值}") private String username;
@Value("${spring.tmg.xxx.tair.namespace:默认值}") private Integer namespace;
@Bean(initMethod = "init") public TairManager tmgXxxTairManager() { MultiClusterTairManager tairManager = new MultiClusterTairManager(); tairManager.setUserName(username); tairManager.setDynamicConfig(true); return tairManager; }
@Bean public TairAccessor tmgXxxTairAccessor(@Qualifier("tmgXxxTairManager") TairManager tmgXxxTairManager) { TairAccessorImpl tairAccessor = new TairAccessorImpl(); tairAccessor.setTairManager(tmgXxxTairManager); tairAccessor.setNamespace(namespace); return tairAccessor; }}

结语

在使用配置类配置 Bean 时,@Bean 方法的参数,或者用@Autowired配置 Bean 时,最好使用@Qualifier 指定注入的bean,避免注入的bean不符合预期。@Resource则通常不存在这种烦恼。




我们是天猫国际前台技术团队,致力于通过技术能力解决人、货、场之间的高效匹配问题,持续为消费者打造优秀的进口商品购物体验。我们始终关注用户的真实需求和反馈,不断探索和应用新技术,和各个团队紧密合作,为用户提供更加智能、便捷的购物体验,将天猫国际打造成为全球消费者信赖的跨境购物平台。