中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何使用Springboot自定義注解并支持SPEL表達式

發布時間:2022-02-17 13:41:29 來源:億速云 閱讀:429 作者:小新 欄目:開發技術

這篇文章主要介紹了如何使用Springboot自定義注解并支持SPEL表達式,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

Springboot自定義注解,支持SPEL表達式

舉例,自定義redis模糊刪除注解

1.自定義注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheEvictFuzzy {
    /**
     * redis key集合,模糊刪除
     * @return
     */
    String[] key() default ""; 
}

2.使用AOP攔截方法,解析注解參數

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
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;
import java.util.Set;
@Aspect
@Order(1)
@Component
public class CacheCleanFuzzyAspect {
    Logger logger = LoggerFactory.getLogger(this.getClass());
 
    @Autowired
    private RedisUtil redis;
 
    //指定要執行AOP的方法
    @Pointcut(value = "@annotation(cacheEvictFuzzy)")
    public void pointCut(CacheEvictFuzzy cacheEvictFuzzy){}
 
 
    // 設置切面為加有 @RedisCacheable注解的方法
    @Around("@annotation(cacheEvictFuzzy)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint, CacheEvictFuzzy cacheEvictFuzzy){
        return doRedis(proceedingJoinPoint, cacheEvictFuzzy);
    }
    @AfterThrowing(pointcut = "@annotation(cacheEvictFuzzy)", throwing = "error")
    public void afterThrowing (Throwable  error, CacheEvictFuzzy cacheEvictFuzzy){
        logger.error(error.getMessage());
    }
 
    /**
     * 刪除緩存
     * @param proceedingJoinPoint
     * @param cacheEvictFuzzy
     * @return
     */
    private Object doRedis (ProceedingJoinPoint proceedingJoinPoint, CacheEvictFuzzy cacheEvictFuzzy){
        Object result = null;
        //得到被切面修飾的方法的參數列表
        Object[] args = proceedingJoinPoint.getArgs();
        // 得到被代理的方法
        Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
        String[] keys = cacheEvictFuzzy.key();
        Set<String> keySet = null;
        String realkey = "";
        for (int i = 0; i < keys.length; i++) {
            if (StringUtils.isBlank(keys[i])){
                continue;
            }
            realkey = parseKey(keys[i], method, args);
            keySet = redis.keys("*"+realkey+"*");
            if (null != keySet && keySet.size()>0){
                redis.delKeys(keySet);
                logger.debug("攔截到方法:" + proceedingJoinPoint.getSignature().getName() + "方法");
                logger.debug("刪除的數據key為:"+keySet.toString());
            }
        }
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }finally {
            return result;
        }
    }
    /**
     * 獲取緩存的key
     * key 定義在注解上,支持SPEL表達式
     * @return
     */
    private String parseKey(String key, Method method, Object [] args){ 
        if(StringUtils.isEmpty(key)) return null;
 
        //獲取被攔截方法參數名列表(使用Spring支持類庫)
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = u.getParameterNames(method);
 
        //使用SPEL進行key的解析
        ExpressionParser parser = new SpelExpressionParser();
        //SPEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        //把方法參數放入SPEL上下文中
        for(int i=0;i<paraNameArr.length;i++){
            context.setVariable(paraNameArr[i], args[i]);
        }
        return parser.parseExpression(key).getValue(context,String.class);
    }
}

完事啦!

大家可以注意到關鍵方法就是parseKey

    /**
     * 獲取緩存的key
     * key 定義在注解上,支持SPEL表達式
     * @return
     */
    private String parseKey(String key, Method method, Object [] args){ 
        if(StringUtils.isEmpty(key)) return null;
 
        //獲取被攔截方法參數名列表(使用Spring支持類庫)
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = u.getParameterNames(method);
 
        //使用SPEL進行key的解析
        ExpressionParser parser = new SpelExpressionParser();
        //SPEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        //把方法參數放入SPEL上下文中
        for(int i=0;i<paraNameArr.length;i++){
            context.setVariable(paraNameArr[i], args[i]);
        }
        return parser.parseExpression(key).getValue(context,String.class);
    }

自定義注解結合切面和spel表達式

在我們的實際開發中可能存在這么一種情況,當方法參數中的某些條件成立的時候,需要執行一些邏輯處理,比如輸出日志。而這些代碼可能都是差不多的,那么這個時候就可以結合自定義注解加上切面加上spel表達式進行處理。就比如在spring中我們可以使用@Cacheable(key="#xx")實現緩存,這個#xx就是一個spel表達式。

需求:我們需要將service層方法中方法的某個參數的值大于0.5的方法,輸出方法執行日志。(需要了解一些spel表達式的語法)

實現步驟:

1、自定義一個注解Log

2、自定義一個切面,攔截所有方法上存在@Log注解修飾的方法

3、寫一個service層方法,方法上標注@Log注解

難點:

在切面中需要拿到具體執行方法的方法名,可以使用spring提供的LocalVariableTableParameterNameDiscoverer來獲取到

自定義一個注解

注意:注解中的spel的值是必須的,且spel表達式返回的結果應該是一個布爾值

/**
 * 記錄日志信息,當spel表但是中的值為true時,輸出日志信息
 * 
 * @描述
 * @作者 huan
 * @時間 2017年10月2日 - 上午10:25:39
 */
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
	String spel();
	String desc() default "描述";
}

自定義一個service類,在需要攔截的方法上加上@Log注解

如何使用Springboot自定義注解并支持SPEL表達式

寫一個自定義切面

注意一下解析spel表達式中context的設值即可

/**
 * 日志切面,當條件滿足時輸出日志.
 * 
 * @描述
 * @作者 huan
 * @時間 2017年10月2日 - 上午10:32:16
 */
@Component
@Aspect
public class LogAspect {
	ExpressionParser parser = new SpelExpressionParser();
	LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
	@Around("@annotation(log)")
	public Object invoked(ProceedingJoinPoint pjp, Log log) throws Throwable {
		Object[] args = pjp.getArgs();
		Method method = ((MethodSignature) pjp.getSignature()).getMethod();
		String spel = log.spel();
		String[] params = discoverer.getParameterNames(method);
		EvaluationContext context = new StandardEvaluationContext();
		for (int len = 0; len < params.length; len++) {
			context.setVariable(params[len], args[len]);
		}
		Expression expression = parser.parseExpression(spel);
		if (expression.getValue(context, Boolean.class)) {
			System.out.println(log.desc() + ",在" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "執行方法," + pjp.getTarget().getClass() + "." + method.getName() 
                        + "(" + convertArgs(args) + ")");
		}
		return pjp.proceed();
	}
	private String convertArgs(Object[] args) {
		StringBuilder builder = new StringBuilder();
		for (Object arg : args) {
			if (null == arg) {
				builder.append("null");
			} else {
				builder.append(arg.toString());
			}
			builder.append(',');
		}
		builder.setCharAt(builder.length() - 1, ' ');
		return builder.toString();
	}
}

pom文件的依賴

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

測試

如何使用Springboot自定義注解并支持SPEL表達式

增加內容

1、當我們想在自己寫的spel表達式中調用spring bean 管理的方法時,如何寫。spel表達式支持使用 @來引用bean,但是此時需要注入BeanFactory

如何使用Springboot自定義注解并支持SPEL表達式

感謝你能夠認真閱讀完這篇文章,希望小編分享的“如何使用Springboot自定義注解并支持SPEL表達式”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

抚松县| 青铜峡市| 湖口县| 永年县| 华安县| 元阳县| 新晃| 永顺县| 吴江市| 武胜县| 龙州县| 赫章县| 青阳县| 和龙市| 延边| 宣化县| 安多县| 句容市| 清河县| 同德县| 集安市| 波密县| 牡丹江市| 邵阳县| 铜山县| 武冈市| 油尖旺区| 平原县| 宜君县| 乐陵市| 石屏县| 正宁县| 平塘县| 大竹县| 湖口县| 阳新县| 绍兴市| 南召县| 泸西县| 德江县| 金寨县|