Spring spel获取自定义注解参数值方式

目录
  • spel获取自定义注解参数值
    • 1.注解类
    • 2.注解使用
    • 3.aop中处理
  • spel在注解中的使用
    • 1 语法说明
    • 2. 基本用法
    • 4 #{…}和${…}

spel获取自定义注解参数值

1.注解类

package com.xxx.mall.order.service.component; 
import java.lang.annotation.*;
 
/**
 * 库存不足等信息监控
 * Created by xdc on 2019/4/16 15:43
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface StockWarnCollect {
 
    /** 客户id */
    String customerId();
 
    /** 来源 */
    String source();
 
    /** 请求类型 1:详情页 2:购物车去结算 3:提交订单 */
    String pageType();
}

2.注解使用

@Override
@StockWarnCollect(customerId = "#customerId", source = "#source", pageType = "2")
public Map<String, Object> validateCarts(Long customerId, Set<Long> userSelectedIds, Short source, JSONArray couponInfo){
    // 省略
}

3.aop中处理

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;

/**
 * 下单失败、库存监控
 * Created by xdc on 2019/4/16 15:45
 */
@Aspect
@Component
@Slf4j
public class StockWarnCollectAop {
    @Pointcut(value = "@annotation(com.xxx.mall.order.service.component.StockWarnCollect)")
    public void collectStockWarn(){}

    @Around(value = "collectStockWarn()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {

        Method targetMethod = this.getTargetMethod(pjp);
        StockWarnCollect stockWarnCollect = targetMethod.getAnnotation(StockWarnCollect.class);

        // spel信息
        String customerIdSpel = stockWarnCollect.customerId();
        String sourceSpel = stockWarnCollect.source();
        Integer pageType = null;  // 操作类型,纯字符串
        if (StringUtils.isNotBlank(stockWarnCollect.pageType())) {
            pageType = Integer.valueOf(stockWarnCollect.pageType());
        }

        // 客户id、来源解析
        ExpressionParser parser = new SpelExpressionParser();
        LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
        String[] params = discoverer.getParameterNames(targetMethod);
        Object[] args = pjp.getArgs();

        EvaluationContext context = new StandardEvaluationContext();
        for (int len = 0; len < params.length; len++) {
            context.setVariable(params[len], args[len]);
        }
        Expression expression = parser.parseExpression(customerIdSpel);
        Long customerId = expression.getValue(context, Long.class);

        expression = parser.parseExpression(sourceSpel);
        Short source = expression.getValue(context, Short.class);
        log.info("collectStockWarn customerId:{}, source:{}", customerId, source);

        // 业务逻辑处理
        Object result = null;
        try {
            result = pjp.proceed();
        } catch (Throwable e) {
            log.info("collectStockWarn watchs creating order errorMsg:{}", ExceptionUtils.getStackTrace(e));
            if (e instanceof MallException) {

            } else {    // 未知错误

            }
            throw e;
        }
        try {
            if (result != null) {
            }
        } catch (Exception e) {
            log.error("collectStockWarn process error, errorMsg:{}", ExceptionUtils.getStackTrace(e));
        }
        return result;
    }
    /**
     * 获取目标方法
     */
    private Method getTargetMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException {
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature;
        Method agentMethod = methodSignature.getMethod();
        return pjp.getTarget().getClass().getMethod(agentMethod.getName(),agentMethod.getParameterTypes());
    }
}

spel在注解中的使用

SpEL(Spring Expression Language),即Spring表达式语言,是比JSP的EL更强大的一种表达式语言。为什么要总结SpEL,因为它可以在运行时查询和操作数据,尤其是数组列表型数据,因此可以缩减代码量,优化代码结构。个人认为很有用。

1 语法说明

1.1 SpEL 字面量:

  • 整数:#{8}
  • 小数:#{8.8}
  • 科学计数法:#{1e4}
  • String:可以使用单引号或者双引号作为字符串的定界符号。
  • Boolean:#{true}

1.2 SpEL引用bean , 属性和方法:

  • 引用其他对象:#{car}
  • 引用其他对象的属性:#{car.brand}
  • 调用其它方法 , 还可以链式操作:#{car.toString()}
  • 调用静态方法静态属性:#{T(java.lang.Math).PI}

1.3 SpEL支持的运算符号:

  • 算术运算符:+,-,*,/,%,^(加号还可以用作字符串连接)
  • 比较运算符:< , > , == , >= , <= , lt , gt , eg , le , ge
  • 逻辑运算符:and , or , not , |
  • if-else 运算符(类似三目运算符):?:(temary), ?:(Elvis)
  • 正则表达式:#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}'}

2. 基本用法

SpEL有三种用法:

1. 是在注解@Value中;

2. 在XML配置中;

3. 代码块中使用Expression。

2.1 @Value

    //@Value能修饰成员变量和方法形参
    //#{}内就是表达式的内容
    @Value("#{表达式}")
    public String arg;

2.2 <bean>配置

<bean id="xxx" class="com.java.XXXXX.xx">
    <!-- 同@Value,#{}内是表达式的值,可放在property或constructor-arg内 -->
    <property name="arg" value="#{表达式}">
</bean>

2.3 代码块中使用

public class SpELTest { 
    public static void main(String[] args) {
 
        //创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
        //表达式放置
        Expression exp = parser.parseExpression("表达式");
        //执行表达式,默认容器是spring本身的容器:ApplicationContext
        Object value = exp.getValue();
        
        /**如果使用其他的容器,则用下面的方法*/
        //创建一个虚拟的容器EvaluationContext
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //向容器内添加bean
        BeanA beanA = new BeanA();
        ctx.setVariable("bean_id", beanA);
        
        //setRootObject并非必须;一个EvaluationContext只能有一个RootObject,引用它的属性时,可以不加前缀
        ctx.setRootObject(XXX);
        
        //getValue有参数ctx,从新的容器中根据SpEL表达式获取所需的值
        Object value = exp.getValue(ctx);
    }
}

4 #{…}和${…}

  • #{…} 用于执行SpEl表达式,并将内容赋值给属性
  • ${…} 主要用于加载外部属性文件中的值
  • #{…} 和${…} 可以混合使用,但是必须#{}外面,${}在里面

4.1 ${…}用法 

{}里面的内容必须符合SpEL表达式,通过@Value(“${spelDefault.value}”)可以获取属性文件中对应的值,但是如果属性文件中没有这个属性,则会报错。可以通过赋予默认值解决这个问题,如@Value("${spelDefault.value:127.0.0.1}")

// 如果属性文件没有spelDefault.value,则会报错
    //  @Value("${spelDefault.value}")
    //  private String spelDefault2;
 
    // 使用default.value设置值,如果不存在则使用默认值
    @Value("${spelDefault.value:127.0.0.1}")
    private String spelDefault;

4.2 #{…}用法 

这里只演示简单用法

    // SpEL:调用字符串Hello World的concat方法
    @Value("#{'Hello World'.concat('!')}")
    private String helloWorld;
 
    // SpEL: 调用字符串的getBytes方法,然后调用length属性
    @Value("#{'Hello World'.bytes.length}")
    private String helloWorldbytes;

4.3 ${…}和#{…}混合使用 

${...}和#{...}可以混合使用,如下文代码执行顺序:通过${server.name}从属性文件中获取值并进行替换,然后就变成了 执行SpEL表达式{‘server1,server2,server3’.split(‘,’)}。

// SpEL: 传入一个字符串,根据","切分后插入列表中, #{}和${}配置使用(注意单引号,注意不能反过来${}在外面,#{}在里面)
    @Value("#{'${server.name}'.split(',')}")
    private List<String> servers;

在上文中在#{}外面,${}在里面可以执行成功,那么反过来是否可以呢${}在外面,#{}在里面,如代码

// SpEL: 注意不能反过来${}在外面,#{}在里面,这个会执行失败
    @Value("${#{'HelloWorld'.concat('_')}}")
    private List<String> servers2;

答案是不能。因为spring执行${}是时机要早于#{}。在本例中,Spring会尝试从属性中查找#{‘HelloWorld’.concat(‘_’)},那么肯定找不到,由上文已知如果找不到,然后报错。所以${}在外面,#{}在里面是非法操作

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Cache抽象-使用SpEL表达式解析

    目录 Spring Cache抽象-使用SpEL表达式 概述 SpEl表达式 如何让自定义注解支持SpEL表达式 使用方法 使用案例 1.准备 2.自定义注解 3.定义AOP拦截注解对方法增强进行读写缓存 4.测试 Spring Cache抽象-使用SpEL表达式 概述 在Spring Cache注解属性中(比如key,condition和unless),Spring的缓存抽象使用了SpEl表达式,从而提供了属性值的动态生成及足够的灵活性. 下面的代码根据用户的userCode进行缓存,对于ke

  • Spring表达式语言SpEL用法详解

    这篇文章主要介绍了spring表达式语言SpEL用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 (1)spring表达式语言是一个支持运行时查询和操作对象图得我强大表达式语言. (2)语言类似于EL:SpEL使用#{...}作为定界符.所有在大括号中的字符串均被认为是SpEL. (3)SpEL为bean的属性进行动态赋值提供了便利. (4)通过SpEL可以实现: 通过Bean的id对Bean进行引用 调用方法及引用对象的属性 计算表达式

  • Spring spel表达式使用方法示例

    spring in action第三版读书笔记 spring3.0引入了spring expression language(spel)语言,通过spel我们可以实现 1.通过bean的id对bean进行引用 2.调用方法以及引用对象中的属性 3.计算表达式的值 4.正则表达式的匹配 5.集合的操作 spel最终的目标是得到表达式计算之后的值,这些表达式可能是列举的一些值,引用对象的某些属性,或者是类中的某些常量,复杂的spel表达式通常都是由一些简单的元素构成的.最简单的仅仅是得到一些给出元素

  • Spring spel获取自定义注解参数值方式

    目录 spel获取自定义注解参数值 1.注解类 2.注解使用 3.aop中处理 spel在注解中的使用 1 语法说明 2. 基本用法 4 #{…}和${…} spel获取自定义注解参数值 1.注解类 package com.xxx.mall.order.service.component;  import java.lang.annotation.*;   /**  * 库存不足等信息监控  * Created by xdc on 2019/4/16 15:43  */ @Retention(R

  • 使用spring boot通过自定义注解打印所需日志

    spring boot自定义注解打印日志 在实际项目中可能需要监控每个接口的请求时间以及请求参数等相关信息,那么此时我们想到的就是两种实现方式,一种是通过拦截器实现,另一种则通过AOP自定义注解实现. 本文介绍自定义注解实现方式 自定义注解,四个元注解这次就不解释了. @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface WebLog { /** * 日志信息描述 */ String d

  • Spring AOP 实现自定义注解的示例

    自工作后,除了一些小项目配置事务使用过 AOP,真正自己写 AOP 机会很少,另一方面在工作后还没有写过自定义注解,一直很好奇注解是怎么实现他想要的功能的,刚好做项目的时候,经常有人日志打得不够全,经常出现问题了,查日志的才发现忘记打了,所以趁此机会,搜了一些资料,用 AOP + 自定义注解,实现请求拦截,自定义打日志,玩一下这两个东西,以下是自己完的一个小例子,也供需要的同学参考. 1. 注解如下: package cn.bridgeli.demo.annotation;   import j

  • Spring Boot中自定义注解结合AOP实现主备库切换问题

    摘要:本篇文章的场景是做调度中心和监控中心时的需求,后端使用TDDL实现分表分库,需求:实现关键业务的查询监控,当用Mybatis查询数据时需要从主库切换到备库或者直接连到备库上查询,从而减小主库的压力,在本篇文章中主要记录在Spring Boot中通过自定义注解结合AOP实现直接连接备库查询. 一.通过AOP 自定义注解实现主库到备库的切换 1.1 自定义注解 自定义注解如下代码所示 import java.lang.annotation.ElementType; import java.la

  • 详解使用Spring AOP和自定义注解进行参数检查

    引言 使用SpringMVC作为Controller层进行Web开发时,经常会需要对Controller中的方法进行参数检查.本来SpringMVC自带@Valid和@Validated两个注解可用来检查参数,但只能检查参数是bean的情况,对于参数是String或者Long类型的就不适用了,而且有时候这两个注解又突然失效了(没有仔细去调查过原因),对此,可以利用Spring的AOP和自定义注解,自己写一个参数校验的功能. 代码示例 注意:本节代码只是一个演示,给出一个可行的思路,并非完整的解决

  • 在spring中使用自定义注解注册监听器的方法

    接口回调 监听器本质上就是利用回调机制,在某个动作发生前或后,执行我们自己的一些代码.在Java语言中,可以使用接口来实现. 实现一个监听器案例 为了方便,直接在spring环境中定义:以工作(work)为例,定义工作开始时(或结束时)的监听器. 1. 定义回调的接口 package com.yawn.demo.listener; /** * @author Created by yawn on 2018-01-21 13:53 */ public interface WorkListener

  • 详解在Spring MVC中使用注解的方式校验RequestParams

    概述 Spring MVC支持Bean Validation,通过这个验证技术,可以通过注解方式,很方便的对输入参数进行验证,之前使用的校验方式,都是基于Bean对象的,但是在@RequestParam中,没有Bean对象,这样使得校验无法进行,可以通过使用@Validated注解,使得校验可以进行. 校验bean对象 一般校验bean对象,为了可以自动的校验属性,可以通过两步解决: 一.声明对象 package com.github.yongzhizhan.draftbox.model; im

  • 使用Spring自定义注解实现任务路由的方法

    在Spring mvc的开发中,我们可以通过RequestMapping来配,当前方法用于处理哪一个URL的请求.同样我们现在有一个需求,有一个任务调度器,可以按照不同的任务类型路由到不同的任务执行器.其本质就是通过外部参数进行一次路由和Spring mvc做的事情类似.简单看了Spring mvc的实现原理之后,决定使用自定义注解的方式来实现以上功能. 自定义TaskHandler注解 @Target({ElementType.TYPE}) @Retention(RetentionPolicy

  • springMVC自定义注解,用AOP来实现日志记录的方法

    需求背景 最近的一个项目,在项目基本完工的阶段,客户提出要将所有业务操作的日志记录到数据库中,并且要提取一些业务的关键信息(比如交易单号)体现在日志中. 为了保证工期,在查阅了资料以后,决定用AOP+自定义注解的方式来完成这个需求. 准备工作 自定义注解需要依赖的jar包有 aspectjrt-XXX.jar ,aspectjweaver-XXX.jar,XXX代表版本号. 自定义注解 在项目下单独建立了一个log包,来存放日志相关的内容 **.common.log.annotation //自

  • SpringBoot中利用AOP和拦截器实现自定义注解

    目录 前言 Spring实现自定义注解 1.引入相关依赖 2.相关类 Java实现自定义注解 通过Cglib实现 通过JDk动态代理实现 Cglib和JDK动态代理的区别 写在最后 前言 最近遇到了这样一个工作场景,需要写一批dubbo接口,再将dubbo接口注册到网关中,但是当dubbo接口异常的时候会给前端返回非常不友好的异常.所以就想要对异常进行统一捕获处理,但是对于这种service接口使用@ExceptionHandler注解进行异常捕获也是捕获不到的,应为他不是Controller的

随机推荐