栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot 国际化配置项目实战(三)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot 国际化配置项目实战(三)

springboot 项目中实战演练,功能有参数非空校验、默认国际化、正常返回参数国际化、抛异常后信息国际化、全局异常后继续切面拦截国际化、动态参数国际化等后端各个方面的实战。

1、新建springboot微服务,pom中jar的基本引用


		org.springframework.boot
		spring-boot-starter-parent
		1.5.14.RELEASE
		 
	

	
		UTF-8
		UTF-8
		1.8
	

	

		
			org.springframework.boot
			spring-boot-starter-aop
		

		
			org.springframework.boot
			spring-boot-starter-web
		
		
		
			org.springframework.boot
			spring-boot-starter-thymeleaf
		
		
			net.sourceforge.nekohtml
			nekohtml
		
		  
		    org.springframework.boot  
		    spring-boot-devtools  
		    true
		
		
		
			org.springframework.boot
			spring-boot-starter-test
			test
		
        
            org.projectlombok
            lombok
        
    

	
		
			
				org.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{

	private MessageSource messageSource;
	@Value("${spring.messages.basename}")
	private String basename;

	@Value("${spring.messages.cache-seconds}")
	private long cacheMillis;

	@Value("${spring.messages.encoding}")
	private String encoding;

	protected Logger log = LoggerFactory.getLogger(this.getClass());
	
	@Override
	public Object beforeBodyWrite(Object obj, MethodParameter method,
			MediaType type, Class> converter,
			ServerHttpRequest request, ServerHttpResponse response) {
		try {
			if(obj instanceof ResponseDTO){
				ResponseDTO result = (ResponseDTO) obj;
				String resultCode = result.getResultCode();
				if(resultCode!= null){
					HttpServletRequest req = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
					String i18nMsg = getMessage(req, resultCode);
					if(null !=i18nMsg){
						result.setResultMsg(i18nMsg);
					}else {
						log.info("采用上游的赋值");
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			log.error("返回值国际化拦截异常",e);
		}
		return obj;
	}

	@Override
	public boolean supports(MethodParameter arg0,
			Class> arg1) {
		return true;
	}
	
	
	public String getMessage(HttpServletRequest request, String key){
        RequestContext requestContext = new RequestContext(request);
        String value = requestContext.getMessage(key);
        return value;
    }

	
	private MessageSource initMessageSource() {
		ReloadableResourceBundleMessageSource messageSource=new ReloadableResourceBundleMessageSource();
		log.info("baseName====>:" + this.basename);
		messageSource.setbasename(basename);
		messageSource.setDefaultEncoding(encoding);
		messageSource.setCacheMillis(cacheMillis);
		return messageSource;
	}

	
	


}
 

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

多角度,正常、异常、参数非空校验、动态传参、两种切面均可以测试。

 到此,参数国际化实战分享完毕,核心伪代码都贴出来了,有不明白的地方可以留言关注,积极反馈!

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号