9(记一次开发中的小bug)

今天在开发过程中,测试环境报了一个错误,就是通过spring容器获取某个类获取不到,报NoSuchBeanDefinitionException错误,伪代码如下:

public interface RepayMessageService {

    void notifyRepayResult(String message);

}
public abstract class AbstractRepayMessageHelper implements RepayMessageService {

}
@Service
public class IQiYiNotifyRepayMessageHelper extends AbstractRepayMessageHelper{
    @Override
    public void notifyRepayResult(String message) {
        System.out.println("===================");
    }
}
@Service("repayProcessMessageStrategy")
public class RepayProcessMessageStrategy {

    @Autowired
    private ApplicationContext applicationContext;

    public RepayMessageService getService(String assetProductCode){
        RepayMessageServiceEnum repayMessageServiceEnum =  RepayMessageServiceEnum.getProductInstance(assetProductCode);
        if(null == repayMessageServiceEnum){
            return null;
        }
        return (RepayMessageService) applicationContext.getBean(repayMessageServiceEnum.getServiceImpl());
    }

    @Getter
    @AllArgsConstructor
    private enum RepayMessageServiceEnum {
        AQY_LOANS("AQY_LOANS", "iQiYiNotifyRepayMessageHelper"),
        AQY_LOANS2("AQY_LOANS2", "iqiYiNotifyRepayMessageHelper2");
        //.............
        private String assetProductCode;

        private String serviceImpl;

        public static RepayMessageServiceEnum getProductInstance(String assetProductCode){
            for(RepayMessageServiceEnum record : values()){
                if(record.assetProductCode.equals(assetProductCode)){
                    return record;
                }
            }
            return null;
        }
    }
}
public class Test3 extends BaseTest{
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private RepayProcessMessageStrategy repayProcessMessageStrategy;

    @Test
    public void test() {
        //打印spring容器的bean观察
        for(String bean:applicationContext.getBeanDefinitionNames()){
            if(bean.toString().contains("NotifyRepayMessageHelper")){
                System.out.println("spring容器加载的bean:"+bean);
            }
        }
        //如果类名为IQiYiNotifyRepayMessageHelper,则使用applicationContext.getBean("iQiYiNotifyRepayMessageHelper")会报错
        try{
            RepayMessageService repayMessageService1 = repayProcessMessageStrategy.getService("AQY_LOANS");
            System.out.println(repayMessageService1);
        }catch (Exception e){
            e.printStackTrace();
        }
        //解决方法1:改为applicationContext.getBean("IQiYiNotifyRepayMessageHelper")
        System.out.println("=============1"+applicationContext.getBean("IQiYiNotifyRepayMessageHelper"));
        //解决方法2:将类名改为第二个字母小写IqiYiNotifyRepayMessageHelper,此时使用applicationContext.getBean("iqiYiNotifyRepayMessageHelper")
        RepayMessageService repayMessageService = repayProcessMessageStrategy.getService("AQY_LOANS2");
        System.out.println("=============2"+repayMessageService);
        //解决方法3:使用@Service("iQiYiNotifyRepayMessageHelper3")
        System.out.println("=============3"+applicationContext.getBean("iQiYiNotifyRepayMessageHelper3"));

    }

经看源码得知:原因是取某个类名的时候第一个和第二个字母都为大写所致。当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致

源码如下:

public class AnnotationBeanNameGenerator implements BeanNameGenerator {
 
	private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";
 
 
	@Override
	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
		if (definition instanceof AnnotatedBeanDefinition) {
			String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
			if (StringUtils.hasText(beanName)) {
				// Explicit bean name found.
				return beanName;
			}
		}
		// Fallback: generate a unique default bean name.
		return buildDefaultBeanName(definition, registry);

protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
		return buildDefaultBeanName(definition);
	}
	protected String buildDefaultBeanName(BeanDefinition definition) {
		String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
		return Introspector.decapitalize(shortClassName);
	}
 

 public static String decapitalize(String name) {
        if (name == null || name.length() == 0) {
            return name;
        }
        if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                        Character.isUpperCase(name.charAt(0))){
            return name;
        }
        char chars[] = name.toCharArray();
        chars[0] = Character.toLowerCase(chars[0]);
        return new String(chars);
    }

其实还有一种解决方法,我们可以看到代码中的策略模式其实还是有缺陷的,比如,如果我要则更加多个实现类,iqiYiNotifyRepayMessageHelper3、iqiYiNotifyRepayMessageHelper4,那么在策略类中的枚举类中还是需要加上这两行代码:这显然不符合开闭原则

        AQY_LOANS3("AQY_LOANS3", "iqiYiNotifyRepayMessageHelper3"),
        AQY_LOANS4("AQY_LOANS4", "iqiYiNotifyRepayMessageHelper4");

可以改为如下代码,这样每次新建类后,不需要改动此策略类的代码,只需要新建对应的实现类即可;


    private static Map<String,RepayMessageService> serviceHashMap = new HashMap<>();

    /**
     * 每次新建一个实现类则调用此方法将类注册到serviceHashMap中
     * @param code
     * @param service
     */
    public static void registerImpl(String code,RepayMessageService service){
        serviceHashMap.put(code,service);
    }

    /**
     * 调用此方法获取code对应的类
     * @param code
     * @return
     */
    public RepayMessageService getService2(String code){
       return serviceHashMap.get(code);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值