springboot 项目中实战演练,功能有参数非空校验、默认国际化、正常返回参数国际化、抛异常后信息国际化、全局异常后继续切面拦截国际化、动态参数国际化等后端各个方面的实战。
1、新建springboot微服务,pom中jar的基本引用
org.springframework.boot spring-boot-starter-parent1.5.14.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-aoporg.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-thymeleafnet.sourceforge.nekohtml nekohtmlorg.springframework.boot spring-boot-devtoolstrue org.springframework.boot spring-boot-starter-testtest org.projectlombok lombokorg.springframework.boot spring-boot-maven-plugin
2、yml配置文件;
spring:
thymeleaf:
encoding=UTF-8
content-type=text/html
cache=false
mode=LEGACYHTML5
messages:
encoding: UTF-8
cache-seconds: 1
basename: static/i18n/messages
3、properties的创建:
引入配置信息:比如英文版
demo.key.null= key not null !!!
1000=hello{0}, your verification code is: {1}
4、WebMvcConfigurationSupport 类中接口的重写:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class ValidatorConfiguration extends WebMvcConfigurationSupport {
@Autowired
private MessageSource messageSource;
@Override
public Validator getValidator() {
return validator();
}
@Bean
public Validator validator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource);
return validator;
}
}
5、全局异常的创建:
@Slf4j
@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class AppExceptionHandler {
@Autowired
MessageSource messageSource;
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Object methodArgumentNotValid(HttpServletRequest req, MethodArgumentNotValidException ex) {
// Request r= new Request();源码所示
//ResultBean result = ResultBean.FAIL;
ResponseDTO result = new ResponseDTO();
FieldError fe = ex.getBindingResult().getFieldError();
String msg = fe.getDefaultMessage();
Locale locale = LocaleContextHolder.getLocale();
// LocaleContextHolder.setLocale(Locale.ENGLISH);
LocaleContextHolder.setLocale(Locale.US);
locale = LocaleContextHolder.getLocale();
String message = messageSource.getMessage(fe, LocaleContextHolder.getLocale());
//String message = messageSource.getMessage(fe, Locale.ENGLISH);
log.info("---MethodArgumentNotValidException Handler--- ERROR: {}",msg);
result.setResultMsg(msg);
// List errors =ex.getBindingResult().getAllErrors();
// StringBuffer errorMsg=new StringBuffer();
// errors.stream().forEach(x -> errorMsg.append(x.getDefaultMessage()).append(";"));
// log.error("---MethodArgumentNotValidException Handler--- ERROR: {}", errorMsg.toString());
// result.setResultMsg(errorMsg.toString());
return result;
}
@ExceptionHandler(Exception.class)
public ResponseDTO handleMgtInvalidParamException(Exception e, HttpServletRequest request) {
ResponseDTO r = new ResponseDTO(ResponseCodeEnum.FAILED,null);
return r;
}
}
6、切面 接口 ResponseBodyAdvice
import com.nandao.i18n.dto.ResponseDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import org.springframework.web.servlet.support.RequestContext;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice(basePackages={"com.nandao.i18n.controller","com.nandao.i18n.exception"})
public class I18nResponseBodyAdvice implements ResponseBodyAdvice
7、aspect切面的实现和6类似,二选一方案:
import com.nandao.i18n.constant.ResultEnglishMsg;
import com.nandao.i18n.dto.ResponseDTO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.Objects;
@Aspect
@Slf4j
@Component
public class GlobalEnglishMsgAspect {
private MessageSource messageSource;
@Value("${spring.messages.basename}")
private String basename;
@Value("${spring.messages.encoding}")
private String encoding;
@Pointcut("execution(public * com.nandao.i18n.controller.*.*(..))")
private void pointCut() {
}
@Pointcut("execution(public * com.nandao.i18n.exception.*.*(..))")
private void pointExceptionCut() {
}
@AfterReturning(returning = "returnVal", pointcut = "pointCut()")
public void doAftereReturning(JoinPoint joinPoint, Object returnVal) {
commonReturning( joinPoint, returnVal);
}
@AfterReturning(returning = "returnExceptionVal", pointcut = "pointExceptionCut()")
public void doAftereExceptionReturning(JoinPoint joinPoint, Object returnExceptionVal) {
commonReturning( joinPoint, returnExceptionVal);
}
private void commonReturning(JoinPoint joinPoint, Object returnCommonVal) {
String classMethod = null;
if (returnCommonVal instanceof ResponseDTO) {
ResponseDTO r = (ResponseDTO) returnCommonVal;
String returnCode = r.getResultCode();
if (!returnCode.equals("100000")) {
classMethod = getClassMethodName(joinPoint);
boolean flag = checkHeaderName();
if (!flag) {
log.info("此接口[{}]返回的msg不需要转化为英文,,错误码[{}]", classMethod,returnCode);
return;
}
if (ResultEnglishMsg.codeList.contains(returnCode)) {
log.info("此接口[{}]特殊处理英文msg,错误码[{}]", classMethod,returnCode);
return;
}
log.info("此接口[{}]返回的msg需要转化为英文,,错误码[{}]", classMethod,returnCode);
HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String i18nMsg = getMessage(req, returnCode);//获取国际化配置
if (Objects.nonNull(i18nMsg)) {
r.setResultMsg(i18nMsg);
} else {
log.info("英文提升为空,还用原来的中文提升,错误码code:[{}],接口名[{}]", returnCode, classMethod);
}
}
} else {
classMethod = getClassMethodName(joinPoint);
log.info("此接口[{}]返回的不是R对象,没有msg,不需要英文转化", classMethod);
}
}
private MessageSource initMessageSource() {
ReloadableResourceBundleMessageSource messageSource=new ReloadableResourceBundleMessageSource();
log.info("baseName====>:" + this.basename);
messageSource.setbasename(basename);
messageSource.setDefaultEncoding(encoding);
return messageSource;
}
public String getMessage(HttpServletRequest request,String code){
if(messageSource==null){
messageSource=initMessageSource();
}
String result=null;
try {
result = messageSource.getMessage(code, null, request.getLocale());
} catch (NoSuchMessageException e) {
log.error("Cannot find the error message of internationalization, return the original error message.[{}]",request.getRequestURI());
}
return result;
}
private boolean checkHeaderName() {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
if (Objects.isNull(ra)) {
log.info("服务里RequestAttributes对象为空");
return false;
}
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
Enumeration enumeration = request.getHeaderNames();
while (enumeration.hasMoreElements()) {
String name = enumeration.nextElement();
String value = request.getHeader(name);
if (name.equals("accept-language") && value.equals("en")) {
return true;
}
}
return false;
}
private String getClassMethodName(JoinPoint joinPoint) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (Objects.nonNull(requestAttributes)) {
HttpServletRequest request = requestAttributes.getRequest();
return request.getRequestURI();
}
//如果是子线程的话,会走下面这种情况
log.info("classMethod={}", joinPoint.getSignature().getDeclaringTypeName());
String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
String className = declaringTypeName.substring(declaringTypeName.lastIndexOf(".") + 1);
log.info("className={};Method={}", className, joinPoint.getSignature().getName());
return className + "." + joinPoint.getSignature().getName();
}
}
8、VO 和控制层代码的实现:
import lombok.Data;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.constraints.Size;
@Data
public class UserInfo {
//@Size(min = 1, max = 10, message = "姓名长度必须为1到10")
// @NotEmpty(message = "{user.name.notBlank}")
@NotEmpty(message = "{demo.key.null}")//必须配置地址,保证不能错:basename: static/i18n/messages
//@NotEmpty()
private String name;
}
控制层:
@PostMapping("/validate1")
@ResponseBody
public ResponseDTO validate1(@Valid @Validated @RequestBody UserInfo user) throws Exception {
Map map = new HashMap();
map.put("key", "这里就是数据了");
//动态传参
String message = messageSource.getMessage("1000", new Object[]{"nandao","128"}, LocaleContextHolder.getLocale());
System.out.println("国际化后的参数"+message);
if("nandao".equals(user.getName())){
throw new Exception("抛出异常!!!");
}
return new ResponseDTO(ResponseCodeEnum.SUCCESS,map);
}
9、启动服务后依次访问接口验证:http://localhost:8080/api/validate1
多角度,正常、异常、参数非空校验、动态传参、两种切面均可以测试。
到此,参数国际化实战分享完毕,核心伪代码都贴出来了,有不明白的地方可以留言关注,积极反馈!



