记录一次对AOP的使用,使用AOP完成i18n多语言支持的集成

上回说到


算了。不说了。。。。之前用增强Controller完成了有i18n的支持,后来想想,要不要试试用拦截器和aop各自实现一次试试,说干就干
首先准备了依赖

依赖

文章目录

 <!-- aop -->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>
 <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>fastjson</artifactId>
     <version>1.2.47</version>
 </dependency>

定义一个切面

我们使用@Aspect注解在上将一个类定义为切面类,
使用@Pointcut(切面表达式)定义切入点
使用@Around(切入点)指定一个切面内容切入哪个切入点
方法有一个参数ProceedingJoinPoint pjp 这个就代表被切入的方法,我们可以使用pjp.proceed()使原方法执行,注意这里是使原方法执行,执行到这里就会直接执行原来的方法,如果有返回值可以直接获取;
如果不调用则不会执行原方法内容,调用了原方法之后,就会暂停在等待返回哪个阶段,,所以此时如果使用的使ResponseEntity响应的,并不会被解析为其中的泛型,返回值依然是ResponseEntity,不像增强Controller,获取到的object是解析后的泛型所指类型;
响应顺序…方法响应=>aop=>增强controller=>拦截器=>过滤器

完整代码


/**
 * 定义一个切面打印请求返回参数信息
 */

@Aspect  // 定义为一个切面
@Configuration
public class LogRecordAspect {

    private final ResultUtils resultUtils;

    public LogRecordAspect(ResultUtils resultUtils) {
        this.resultUtils = resultUtils;
    }

    private static final Logger logger = LoggerFactory.getLogger(LogRecordAspect.class);

    // 定义切点Pointcut
    @Pointcut("execution(public * com.doria.learnProject.codeStyle.LanguageMapper.*Controller.*(..))")
    public void resultPointcut() {
    }

    /**
     * 切面内容,当触发上面的切面以后就会走到这里的代码
     *
     * @param pjp 方法体
     * @return 就是方法返回值
     * @throws Throwable
     */
    @Around("resultPointcut()") // 指定切面需要插入的切点
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        // 获取当前线程所绑定的requestAttributes
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        // 强转以获取Request
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        // 获取request[其实看到这个demo我也很疑惑为什么不直接拿去注入的HttpServletRequest,但是我很菜,不敢怀疑,听大佬的]
        assert sra != null;
        HttpServletRequest request = sra.getRequest();
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String json = JSON.toJSONString(request.getParameterMap());
        logger.info("收到请求=>Uri:{},请求方式:{},参数:{}", uri, method, json);

        // 获取方法的返回值result的值就是返回值
        Object result = pjp.proceed();
        logger.info("请求结束=>返回值为:" + JSON.toJSONString(result));

        // 由于特殊需要,这里需要对参数的返回值进行处理,这里是吧code转为对应的语言填入msg
        if (result instanceof Result)
            // 调用语言转换方法,大概内容就是强转然后读取code然后通过i18n转换为msg写入msg
            return resultUtils.languageFormatToResult(result);
        return result;
    }
}

语言填充方法


@Autowired
private MessageSource messageSource;

/**
 * 语言转换方法
 * @param o 待处理对象
 * @return 处理后的对象
 */
public Object languageFormatToResult(Object o){
    // 由于调用方法前使用了instanceof确定了具体类型,我们这里可以直接强转
    Result<?> result = (Result<?>) o;
    // 强转以后取出code
    String key = result.getCode().toString();
    // 设置msg,用code作为key获取参数
    result.setMsg(messageSource.getMessage(key,null, LocaleContextHolder.getLocale()));
    return result;
}

关于切点和切点表达式

切入点

可以通过这个注解定义一个切入点,其中可以没内容

// 定义切点Pointcut
    @Pointcut("execution(public * com.doria.learnProject.codeStyle.LanguageMapper.*Controller.*(..))")
    public void resultPointcut() {
    }

切点表达式

由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。如下是execution表达式的语法:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

这里问号表示当前项可以有也可以没有,其中各项的语义如下:

modifiers-pattern:方法的可见性,如public,protected;
ret-type-pattern:方法的返回值类型,如int,void等;
declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
name-pattern:方法名类型,如buisinessService();
param-pattern:方法的参数类型,如java.lang.String;
throws-pattern:方法抛出的异常类型,如java.lang.Exception;

切入点表达式详解

上一篇:vue实现国际化(vue-i18n)


下一篇:uniapp改变底部tabBar