如何在自定义异常的单参构造中使用@Autowired注入的MessageSource?
这个问题我之前也碰到过,核心原因其实是Spring容器只会管理它自己创建的Bean,你通过new关键字手动实例化的MyBusinessException对象不在Spring的管辖范围内,所以@Autowired注入的MessageSource必然是null,构造注入和@PostConstruct自然也起不了作用。下面给你两种可行的解决方案,按需选择:
方案1:用Spring上下文工具类直接获取MessageSource
这种方式最直接,不需要额外的工厂类,通过一个静态工具类拿到Spring容器里的MessageSource实例:
首先创建一个Spring上下文工具类,让它实现ApplicationContextAware接口来获取容器上下文:
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class SpringContextHelper implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) { SpringContextHelper.context = applicationContext; } public static <T> T getBean(Class<T> beanType) { return context.getBean(beanType); } }
然后修改你的异常类,去掉@Autowired的MessageSource字段,在构造方法里通过工具类获取实例:
import org.springframework.context.MessageSource; import java.util.Locale; public class MyBusinessException extends RuntimeException { private String errorCode; private String messageInEnglish; public MyBusinessException(String errorCode) { this.errorCode = errorCode; // 从Spring容器获取MessageSource MessageSource messageSource = SpringContextHelper.getBean(MessageSource.class); this.messageInEnglish = messageSource.getMessage(errorCode, null, Locale.ENGLISH); // 把英文消息设置为父类的message,这样抛出异常时能直接看到内容 super(this.messageInEnglish); } // 添加上getter方法方便外部获取 public String getErrorCode() { return errorCode; } public String getMessageInEnglish() { return messageInEnglish; } }
现在你就可以直接用throw new MyBusinessException("MY-ERROR-CODE");来实例化异常,它会自动从MessageSource里加载对应的英文错误消息。
方案2:用异常工厂类(更符合Spring设计思想)
如果想避免静态工具类的依赖,让代码更符合Spring的依赖注入原则,可以创建一个由Spring托管的工厂类,专门负责创建MyBusinessException实例:
首先写工厂类,注入MessageSource:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.stereotype.Component; import java.util.Locale; @Component public class BusinessExceptionFactory { private final MessageSource messageSource; @Autowired public BusinessExceptionFactory(MessageSource messageSource) { this.messageSource = messageSource; } // 创建指定errorCode的异常,默认加载英文消息 public MyBusinessException create(String errorCode) { String englishMessage = messageSource.getMessage(errorCode, null, Locale.ENGLISH); return new MyBusinessException(errorCode, englishMessage); } // 可选:支持根据当前请求的Locale加载消息(如果是Web项目) public MyBusinessException createWithCurrentLocale(String errorCode) { Locale currentLocale = org.springframework.context.i18n.LocaleContextHolder.getLocale(); String message = messageSource.getMessage(errorCode, null, currentLocale); return new MyBusinessException(errorCode, message); } }
然后修改异常类,把构造方法设为私有,只允许工厂类创建实例:
public class MyBusinessException extends RuntimeException { private String errorCode; private String messageInEnglish; // 私有构造方法,外部不能直接new private MyBusinessException(String errorCode, String message) { super(message); this.errorCode = errorCode; this.messageInEnglish = message; } // getter方法 public String getErrorCode() { return errorCode; } public String getMessageInEnglish() { return messageInEnglish; } }
使用的时候,在Spring托管的Bean(比如Service、Controller)里注入工厂类即可:
import org.springframework.stereotype.Service; @Service public class YourBusinessService { private final BusinessExceptionFactory exceptionFactory; @Autowired public YourBusinessService(BusinessExceptionFactory exceptionFactory) { this.exceptionFactory = exceptionFactory; } public void doBusinessLogic() { // 抛出异常 throw exceptionFactory.create("MY-ERROR-CODE"); } }
两种方案对比
- 方案1:实现简单,直接就能用,适合快速解决问题;但依赖静态工具类,单元测试时需要模拟Spring上下文,稍微麻烦一点。
- 方案2:更符合Spring的依赖注入理念,代码解耦更好,单元测试时可以轻松Mock
MessageSource或者工厂类,适合中大型项目。
内容的提问来源于stack exchange,提问作者Subin Chalil
相关产品推荐
相关产品推荐

