Redis cache implemented by AOP

Time:2021-9-27

Brief introduction to AOP

Name: Aspect Oriented Programming
Function: reduce the code coupling in the system, and expand the function of the original method without changing the original code
Formula: AOP = pointcut expression + notification method

Notification type

1.Before advice Execute before target method execution
2.Post notificationExecute after target method execution
3.Exception notificationExecuted when an exception is thrown during the execution of the target method
4.Final noticeNotice to be executed at any time

Features: the above four notification types cannot interfere with whether the target method is implementedIt is generally used to record the running state of the program. [monitoring]

5.Around Advice The notification method to be executed before and after the execution of the target method. This method can control whether the target method runs. Joinpoint. Processed(); Function as a powerful

Pointcut expression

The pointcut expression is a judgment (if) of whether a program enters the notification.
Function: when the pointcut expression is satisfied during program operation, the notification method can be executed back to realize business expansion.
Type (writing method):
1. Bean (bean name and bean ID) can only intercept a specific bean object and match only one object.
For example: bean (“itemserviceimpl”)
2. Within (package name. Class name) within (“com. JT. Service. *”) indicates that multiple objects can be matched.
3. The most powerful usage of execution (return value type, package name, class name, method name (parameter list))
For example: execution( com.jt.service... * (..) return value type all methods of all classes under any com.jt.service package will be intercepted
4. @ annotation (package name. Annotation name) matches according to the annotation

AOP introduction case

package com.jt.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect // I am an AOP aspect class
@Component // hand over the class to the spring container for management
public class CacheAOP {
    //Formula: pointcut expression + notification method

    /**
     *Instructions for using pointcut expressions
     *Coarse grain size
     *1. Bean (bean ID) is a class
     *2. Within (package name. Class name) multiple classes
     *Fine grained
     *      1.
     */
    //@Pointcut("bean(itemCatSercviceImpl)")
    //. * indicates the first level directory under the current package.
    //.. * indicates the multi-level directory under the current package
    /*@Pointcut("within(com.jt.service..*)")
    public void pointCut(){
        //Define pointcut expressions for placeholders
    }*/

    //Define the pre notification and bind it to the pointcut @ before ("pointcut()")
    //Or use a simpler method: there is no need to define the pointcut () method,
    //Directly bind bean: @ before ("bean (itemcatsercviceimpl)")
    /*The difference between the two:
    *1. @ before ("pointcut()") refers to the reference of pointcut expression, which is applicable to the case where multiple notifications use the same pointcut
    *2. @ before ("bean (itemcatsercviceimpl)") is applicable to a single notification and does not need to be reused
    * */
    /**@Before("pointCut()")
    public void before(){
        System. Out. Println ("I am a pre notice");
    }
    @AfterReturning("pointCut()")
    public void afterreturn(){
        System. Out. Println ("I am a post notification");
    }*/
    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        //Joinpoint: join point method

        //Getstaticpart () represents the method signature of the join point
        //The name of the getname() method
        String methodName = joinPoint.getSignature().getName();
        //Getdeclarangtypename() gets the full path name of the class com.jt.service.itemcatsercviceimpl
        String className = joinPoint.getSignature().getDeclaringTypeName();
        System.out.println(className+"."+methodName);
        //Gettarget () represents the target object
        //Gettarget(). Getclass() gets the type of the target object
        Class<?> targetClass = joinPoint.getTarget().getClass();
        //Gets the parameters of the object
        Object[] args = joinPoint.getArgs();
        //Get execution time
        Long startTime= System.currentTimeMillis();

        System. Out. Println ("target method type" + targetclass);
        System.out.println ("execution time: + starttime +" Ms ");
    }

    /**
     *Surround notification description
     *Precautions:
     *Proceedingjoinpoint can only be used in surround notifications
     *1. The parameter proceedingjoinpoint must be added to the surround notification
     *2. Proceedingjoinpoint can only be used around notifications
     *3. If proceedingjoinpoint is used as a parameter, it must be in the first place of the parameter
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint){
        System. Out. Println ("surround notification start!!!");
        Object result=null;
        try {
            result=joinPoint.proceed();// Execute the next notification or target method
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System. Out. Println ("end of surround notification!!!");

        return result;
    }

}

About AOP redis implementation

Custom cache annotation

Question: how to control and which methods need to use caching? [cachefind() query method]
Solution: it is defined in the form of user-defined annotation. If the method execution needs to use cache, the annotation can be identified.
Notes on notes:
1. Annotation Name: cachefind
2. Attribute parameters:

1. Key: it should be added manually by the user. Generally, after adding the business name, it is dynamically spliced to form a unique key.
2. Seconds: the user can specify the timeout time of the data.

Redis cache implemented by AOP

//Customize an annotation
@Target (ElementType. Method) // this annotation is only valid for methods
@Retention (retentionpolicy. Runtime) // the runtime is valid
public @interface CacheFind {
    String preKey();// Prefix of user ID key.
 int seconds() default 0;// If the user does not write, it means there is no need to timeout. If it is written, the user shall prevail.
}

Redis cache implemented by AOP

Edit cacheop

package com.jt.aop;
import com.jt.anno.CacheFind;
import com.jt.util.ObjectMapperUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import java.util.Arrays;
@Aspect // I am an AOP aspect class
@Component // hand over the class to the spring container for management
public class CacheAOP {
    @Autowired
 private Jedis jedis;
    /**Redis cache implemented by AOP
 *Aspect = pointcut + notification method
 *Annotation related + surround notification controls whether the target method is executed
 *
 *Difficulties:
 *1. Get the annotation object and pass the annotation as a parameter
 *2. Dynamically generate key prekey + user parameter array
 *3. How to get the return value type of a method
 */
 @Around("@annotation(cacheFind)")
    public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){
        System.out.println ("annotation interception takes effect");
        Object result=null;
        try {
            //Splicing keys of redis stored data
 Object[] args=joinPoint.getArgs();
            String key=cacheFind.preKey()+"::"+ Arrays.toString(args);
            //Judge whether there is data after querying redis
 if(jedis.exists(key)){
                //Indicates that there is data in redis, and there is no need to execute the target method
 String json = jedis.get(key);
                //Dynamically get the return value type of the method to transform up and down
 MethodSignature methodSignature=(MethodSignature)joinPoint
 .getSignature();
                //Indicates that the client returns whatever type it passes
 Class returnType = methodSignature.getReturnType();
                //You need to convert the JSON string to an object
 result=ObjectMapperUtil.toObj(json, returnType);
                System.out.println ("data in redis cache");
            }else{
                //Indicates that the data does not exist and needs to be queried in the database
 result=joinPoint.proceed();// Implementation target method and notification
 //Store the queried data in the redis cache
 String json = ObjectMapperUtil.toJson(result);
                //Determine whether timeout is required
 if(cacheFind.seconds()>0){
                    jedis.setex(key, cacheFind.seconds(), json);
                }else{
                    jedis.set(key, json);
                }
                System. Out. Println ("AOP execution target method query database");
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }
}