记一次bean加载冲突
一个工具类引发的生产失效
以下代码为SpringUtils工具类,作用是可以在spring项目中引用已经加载的bean
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import java.util.Map; /** * @author: yuyangkang * @Title: SpringUtil * @ProjectName: settle * @Description: * @date: 2021/7/25 18:20 */ @Component public class SpringUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) { SpringUtil.applicationContext = applicationContext; } public static <T> T getBean(String beanName) { if(applicationContext.containsBean(beanName)){ return (T) applicationContext.getBean(beanName); }else{ return null; } } public static <T> Map<String, T> getBeansOfType(Class<T> baseType){ return applicationContext.getBeansOfType(baseType); } }
日期回到2022年5月27日
生产环境部署TIS之后,已经生效的一个组件出现异常。异常代码为
java.lang.NullPointerException: null at com.kcsm.trade.utils.SpringUtil.getBeansOfType(SpringUtil.java:34) ~[classes!/:0.0.1-SNAPSHOT] at com.kcsm.trade.service.arrears.bo.RecoverArrearsTradeCreateRule.loadRule(RecoverArrearsTradeCreateRule.java:41) ~[classes!/:0.0.1-SNAPSHOT] at com.kcsm.trade.service.impl.BeRevoverTradeImpl.createTaskGroup(BeRevoverTradeImpl.java:78) ~[classes!/:0.0.1-SNAPSHOT] ......
"at com.kcsm.trade.utils.SpringUtil.getBeansOfType(SpringUtil.java:34) ~[classes!/:0.0.1-SNAPSHOT]"
根据抛出的栈信息可知
return applicationContext.getBeansOfType(baseType);
此行代码抛出了一个异常错误 NullPointerException
debug后可知
private static ApplicationContext applicationContext;
该变量为null。
该工具类使用许久从未发生过此情况。
ApplicationContext applicationContext;
ApplicationContext和BeanFactory是Spring的两大核心接口,而其中ApplicationContext是BeanFactory的子接口。它们都可以当做Spring的容器,Spring容器是生成Bean实例的工厂,并管理容器中的Bean。在基于Spring的Java EE应用中,所有的组件都被当成Bean处理,包括数据源,Hibernate的SessionFactory、事务管理器等。
为null。
ApplicationContext类本身就是在spring项目启动时去从spring工厂中取出;
那么此时为null,就是取出的过程出现了问题。
根据springBoot的工厂装配原理可知。
如果采取自动装配
如下Demo接口
public interface Demo{ void demo(); } @Component public class Aclass implements Demo{ @Overwrite public void demo(){ } } @Component public class Bclass implements Demo{ @Overwrite public void demo(){ } }
此时如果自动装配
@Autowirte private Demo demo;
Spring项目是无法找到唯一对应的。
代码应该为
public interface Demo{ void demo(); } @Component("demoA") public class Aclass implements Demo{ @Overwrite public void demo(){ } } @Component("demoB") public class Bclass implements Demo{ @Overwrite public void demo(){ } }
装配改为
@Resource(name = "demoB") private Demo demo;
此时变量demo 即加载Bclass
结论
综上,当我在失效项目中输入springUtil时出现如下选项
其中出现了三个相同命名的类。并且作用也是一样的。
此时可以确定问题出现在
中,因为我生产项目默认扫描的包是“com.kcsm”。
那么我trade项目本身的springUtil加载和common包内的加载由于是同名的类。其实再注入的时候,由于beanName的命名冲突有一个是无法注入到项目中的;
自然无法加载到 ApplicationContext applicationContext 变量。
解决方式
为该bean命名
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import java.util.Map; /** * @author: yuyangkang * @Title: SpringUtil * @ProjectName: settle * @Description: * @date: 2021/7/25 18:20 */ @Component("tradeSpringUtil") public class SpringUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) { SpringUtil.applicationContext = applicationContext; } public static <T> T getBean(String beanName) { if(applicationContext.containsBean(beanName)){ return (T) applicationContext.getBean(beanName); }else{ return null; } } public static <T> Map<String, T> getBeansOfType(Class<T> baseType){ return applicationContext.getBeansOfType(baseType); } }
为该修改类名
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import java.util.Map; /** * @author: yuyangkang * @Title: SpringUtil * @ProjectName: settle * @Description: * @date: 2021/7/25 18:20 */ @Component public class SpringUtilForSome implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) { SpringUtil.applicationContext = applicationContext; } public static <T> T getBean(String beanName) { if(applicationContext.containsBean(beanName)){ return (T) applicationContext.getBean(beanName); }else{ return null; } } public static <T> Map<String, T> getBeansOfType(Class<T> baseType){ return applicationContext.getBeansOfType(baseType); } }
都可解决该问题。
后记·思考
需要注入的bean是否有必要加载到公共包内。