欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-09 16:47:42
目录spel获取自定义注解参数值1.注解类2.注解使用3.aop中处理spel在注解中的使用1 语法说明2. 基本用法4 #{…}和${…}spel获取自定义注解参数值1....

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(‘_’)},那么肯定找不到,由上文已知如果找不到,然后报错。所以${}在外面,#{}在里面是非法操作 

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