首页 > 其他分享 >Spring-AOP根据spel获取方法参数值、Bean对象属性值

Spring-AOP根据spel获取方法参数值、Bean对象属性值

时间:2023-10-08 17:37:11浏览次数:47  
标签:Spring org spel param 获取 Bean 注解 import id

Spring-AOP根据spel获取方法参数值、Bean对象属性值,动态的获取属性值,可以用来做注解式分布式锁、注解式获取属性值等等。

第一步:自定义注解,代码如下所示

package com.example.springbootstudy.interfaces;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
/**
 * 自定义注解,在注解上的方法将执行代理流程
 */
public @interface MyAnnotation {

    String key();

}

第二步:方法上添加注解,代码如下所示:

package com.example.springbootstudy.service;

import com.example.springbootstudy.config.BizException;
import com.example.springbootstudy.config.CommonEnum;
import com.example.springbootstudy.entity.Person;
import com.example.springbootstudy.interfaces.MyAnnotation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class TestService {


    public void print() {
        throw new BizException(CommonEnum.SERVER_BUSY);
    }

    /**
     * 添加自定义aop注解 #id为el表达式,需要和被替换的参数名称相同
     * 获取参数值
     *
     * @param id
     * @param person
     */
    @MyAnnotation(key = "'param id is ' + #id")
    public void doSome(String id, Person person) {
        System.out.println("方法执行中");
    }


    /**
     * 添加自定义aop注解 #id为el表达式,需要和被替换的参数名称相同
     * 获取bean对象属性值
     *
     * @param id
     * @param person
     */
    @MyAnnotation(key = "'param id is ' + #person.name")
    public void doSome1(String id, Person person) {
        System.out.println("方法执行中");
    }


}

第三步:添加AOP,代码如下所示:

package com.example.springbootstudy.interceptors;

import com.example.springbootstudy.interfaces.MyAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
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.DefaultParameterNameDiscoverer;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.*;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @Description TODO
 * @Author Jelly
 * @Date 2023/10/8 15:56
 */
@Component
@Aspect
public class MyAspect {

    @Pointcut("@annotation(com.example.springbootstudy.interfaces.MyAnnotation)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object dosome(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入方法的对象
        Method method = signature.getMethod();
        //获取方法上的Aop注解
        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
        //获取注解上的值如 : @MyAnnotation(key = "'param id is ' + #id")
        String keyEl = annotation.key();
        //将注解的值中的El表达式部分进行替换
        //创建解析器
        SpelExpressionParser parser = new SpelExpressionParser();
        //获取表达式
        Expression expression = parser.parseExpression(keyEl);
        //设置解析上下文(有哪些占位符,以及每种占位符的值)
        EvaluationContext context = new StandardEvaluationContext();
        //获取参数值
        Object[] args = joinPoint.getArgs();
        //获取运行时参数的名称
        DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
        String[] parameterNames = discoverer.getParameterNames(method);
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i].toString());
        }

        //解析,获取替换后的结果
        try {
            String result = expression.getValue(context).toString();
            System.out.println(result);
        } catch (EvaluationException e) {
            e.printStackTrace();
        }

        test(joinPoint);

        return null;
    }


    /**
     * 获取bean对象属性值
     *
     * @param point
     */
    private void test(ProceedingJoinPoint point) {
        //获取方法签名
        MethodSignature signature = (MethodSignature) point.getSignature();
        //获取切入方法的对象
        Method method = signature.getMethod();
        //获取方法上的Aop注解
        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
        //获取注解上的值如 : @MyAnnotation(key = "'param id is ' + #id")
        String keyEl = annotation.key();

        EvaluationContext context = getContext(point.getArgs(), signature.getMethod());
        String value = getValue(context, keyEl, String.class);
        System.out.println(value);
    }


    /**
     * 获取spel 定义的参数值
     *
     * @param context 参数容器
     * @param key     key
     * @param clazz   需要返回的类型
     * @param <T>     返回泛型
     * @return 参数值
     */
    private <T> T getValue(EvaluationContext context, String key, Class<T> clazz) {
        SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
        Expression expression = spelExpressionParser.parseExpression(key);
        return expression.getValue(context, clazz);
    }


    /**
     * 获取参数容器
     *
     * @param arguments       方法的参数列表
     * @param signatureMethod 被执行的方法体
     * @return 装载参数的容器
     */
    private EvaluationContext getContext(Object[] arguments, Method signatureMethod) {
        String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(signatureMethod);
        if (parameterNames == null) {
            throw new IllegalArgumentException("参数列表不能为null");
        }
        EvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < arguments.length; i++) {
            context.setVariable(parameterNames[i], arguments[i]);
        }
        return context;
    }


}

总结:spring根据spel表达式动态的获取参数值,动态的获取bean属性值,使用场景主要是在注解式REDIS分布式锁种,注解式获取bean对象值等等场景。

 

标签:Spring,org,spel,param,获取,Bean,注解,import,id
From: https://www.cnblogs.com/jelly12345/p/17749672.html

相关文章

  • 2.SpringBoot——常用注解
    Controller层//设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解的组合功能@RestController//设置当前控制器方法(模块)的请求访问路径@RequestMapping("/web/role")//依赖注入/自动装配,获取Bean@Resource@AutowiredAutowired和Resource的区别两者......
  • 1.SpringBoot——概述
    SpringBoot和SSM开发中有什么区别SpringBoot没有颠覆JavaEE开发,还是要学Spring,它是诸葛亮,提供多种用兵打仗的方案。SSM限定死了只能使用SSM开发JavaWeb应用。而SpringBoot没有与任何MVC框架绑定。一个很恰当的比喻是,SpringMVC、Websocket、Redis、MongoDB、kafka这些对应电......
  • SpringBoot简易任务栏示例
    一、概述现有这样一个需求:前端要求实现类似任务栏的东西(windows电脑的任务栏)。要求:可以向任务栏增加图标、删除图标、给任务栏中的图标排序以及加载任务栏图标列表参考样例图:规律图: 思路:(这里假设任务栏图标列表本身就是一个有序的集合,排序规则按照sort正向排序)......
  • springAMQP--DirectExchange(在监听方法上用注解声明交换机队列和key,发送消息时会带一
         ......
  • Sharding-JDBC教程:Spring Boot整合Sharding-JDBC实现分库分表+读写分离
    在工程的application中做sharding-jdbc的分库分表配置,代码如下:sharding.jdbc.datasource.names=ds-master-0,ds-master-1,ds-master-0-slave-0,ds-master-0-slave-1,ds-master-1-slave-0,ds-master-1-slave-1sharding.jdbc.datasource.ds-master-0.type=com.alibaba.dr......
  • 服务链路追踪 —— SpringCloud Sleuth
    Sleuth简介随着业务的发展,系统规模变得越来越大,微服务拆分越来越细,各微服务间的调用关系也越来越复杂。客户端请求在后端系统中会经过多个不同的微服务调用来协同产生最后的请求结果,几平每一个请求都会形成一个复杂的分布式服务调用链路,在每条链路中任何一个依赖服务出现延迟超......
  • Spring、Redis相关知识查漏补缺
    动态web页面不具有动态性×静态web页面不具有交互性√事务隔离级别是数据库自带的与Spring无关√Spring自己实现了—套与数据库无关的事务机制×软件框架是面向某个领域的、可复用的半成品软件√使用软件框架的优势是开发的灵活性和扩展性更好×拦截器......
  • Springboot中的context-path作用
    首先context-path用于构成url,我们在配置文件的时候server:servlet:context-path:/test之后在本地访问端口8080时(此处拿knife4j举列)本来要访问的是:localhost:8080/doc.html但是现在由于加了context-path,该路径便变为了:localhost:8080/tset/doc.html......
  • spring学习三:IoC概述
    IOC:控制反转,它并不是一种技术而是一种设计思想,是一个重要的面向对象编程法则,能够知道我们如何设计出松耦合,更优良的程序。 Spring通过IOC容器来管理所有java对象的实例化和初始化,控制对象与对象之间的依赖关系,我们将由IOC容器管理的java对象称为SpringBean,它与使用关键字new创......
  • 基于Spring事件驱动模式实现业务解耦
    事件驱动模式举个例子......