使用spring EL表達式+自定義切面封裝緩存模塊


  需求是這樣的,業務代碼需要使用到緩存功能以減少數據庫壓力,使用redis來實現,並且需要生成緩存的key由方法的傳參拼接而成(貌似也只能這樣才能保證同樣的select查詢可以使用緩存),簡單的方式就是在需要緩存的方法內加上大概這樣的邏輯:查詢緩存--->沒有則查詢數據庫 --->查詢結果以key-value形式放入緩存,這樣對業務代碼造成了侵入,並且產生大量重復的代碼,很不優雅,所以決定自己封裝一個緩存模塊,在需要緩存的地方只需要加入一個注解即可。

  首先自定義一個注解

package com.example.test.aspect;


import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface MyCache {

    String value() default "";
}

  定義切面

package com.example.test.aspect;

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.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;


@Component
@Aspect
public class MyAspect {

    //切入點,帶有Myche注解並且是UserService類下的方法
    @Pointcut("@annotation(com.example.test.aspect.MyCache)&&execution(* com.example.test.service.UserService.*(..))")
    public  void pointCut(){};

    @Around("pointCut()")
    public Object cache(ProceedingJoinPoint joinPoint)throws Throwable{
        //獲取方法簽名
        MethodSignature  methodSignature = (MethodSignature) joinPoint.getSignature();
        //獲取目標方法
        Method method = methodSignature.getMethod();
//       Method method2 = joinPoint.getTarget().getClass().getMethod(methodSignature.getName(),methodSignature.getParameterTypes());
        //獲取方法上的注解
        MyCache myCache = method.getDeclaredAnnotation(MyCache.class);
        //得到el表達式
        String el = myCache.value();
        //解析el表達式,將#id等替換為參數值
        ExpressionParser expressionParser = new SpelExpressionParser();
        Expression expression = expressionParser.parseExpression(el);
        EvaluationContext context = new StandardEvaluationContext();
        String[] parameterNames = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i <parameterNames.length ; i++) {
            context.setVariable(parameterNames[i],args[i]);
        }
        String key = expression.getValue(context).toString();
        System.out.println(key);
        //根據key從緩存中拿數據,這里省略
        
        //如果緩存中沒有則執行目標方法
        Object o =   joinPoint.proceed();
        //將結果放入緩存,這里省略
        
        return o;
        
    }
}

  測試的service

package com.example.test.service;

import com.example.test.aspect.MyCache;
import org.springframework.stereotype.Service;

@Service
public class UserService implements UserServiceInterface{
    

    @MyCache("'asd_' + #id")
    @Override
    public void addUser(String id,String name){
        System.out.println(id + "----" + name);
    }
}

  測試類,這里是springboot項目

package com.example.test;

import com.example.test.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestApplicationTests {

    @Autowired
    private UserService userService;

    @Test
    public void contextLoads() {
        System.out.println(userService.getClass().getName());

        userService.addUser("12345" ,"zhagnsan");
    }

}

  執行結果如下:

  修改UserService上的注解為:"'asd_' + #id + '_' + #name",結果如下

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM