springboot脚手架liugh-parent源码研究参考

1. liugh-parent源码研究参考

1.1. 前言

  • 这也是个开源的springboot脚手架项目,这里研究记录一些该框架写的比较好的代码段和功能
  • 脚手架地址

1.2. 功能

1.2.1. 当前用户

  • 这里它用了注解切面进行登录用户的统一注入入口参数,这个做法可以进行参考,不需要在需要使用到登录用户的地方用对象去取了
import com.liugh.annotation.CurrentUser;
import com.liugh.exception.UnauthorizedException;
import com.liugh.entity.User;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

/**
 *  增加方法注入,将含有 @CurrentUser 注解的方法参数注入当前登录用户
 * @author liugh
 * @since 2018-05-03
 */
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().isAssignableFrom(User.class)
                && parameter.hasParameterAnnotation(CurrentUser.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        User user = (User) webRequest.getAttribute("currentUser", RequestAttributes.SCOPE_REQUEST);
        if (user == null) {
            throw new UnauthorizedException("获取用户信息失败");
        }
        return user;
    }
}
/**
 * 身份认证异常
 * @author liugh
 * @since 2018-05-06
 */
public class UnauthorizedException extends RuntimeException {
    public UnauthorizedException(String msg) {
        super(msg);
    }

    public UnauthorizedException() {
        super();
    }
}
/**
 * 在Controller的方法参数中使用此注解,该方法在映射时会注入当前登录的User对象
 * @author : liugh
 * @date : 2018/05/08
 */
@Target(ElementType.PARAMETER)          // 可用在方法的参数上
@Retention(RetentionPolicy.RUNTIME)     // 运行时有效
public @interface CurrentUser {
}
  • 注入解析对象
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.util.List;

/**
 * @author liugh
 * @since 2018-05-03
 */
@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        super.addInterceptors(registry);
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(currentUserMethodArgumentResolver());
        super.addArgumentResolvers(argumentResolvers);
    }


    @Bean
    public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {
        return new CurrentUserMethodArgumentResolver();
    }
}

1.2.2. 令牌桶

  • 这是一种限流思路,令牌桶算法请自行百度,这里也不是从零实现,用到了guava中的RateLimiter限流器
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import com.liugh.annotation.AccessLimit;
import com.liugh.base.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 限流切面
 * Created by liugh on 2018/10/12.
 */
@Slf4j
public class AccessLimitAspect extends AbstractAspectManager{

    public AccessLimitAspect(AspectApi aspectApi){
        super(aspectApi);
    }

    @Override
    public Object doHandlerAspect(ProceedingJoinPoint pjp, Method method)throws Throwable {
        super.doHandlerAspect(pjp,method);
        execute(pjp,method);
        return null;
    }

    //添加速率.保证是单例的
    private static RateLimiter rateLimiter = RateLimiter.create(1000);
     //使用url做为key,存放令牌桶 防止每次重新创建令牌桶
    private static  Map<String, RateLimiter> limitMap = Maps.newConcurrentMap();

    @Override
    public Object execute(ProceedingJoinPoint pjp,Method method) throws Throwable{
        AccessLimit lxRateLimit = method.getAnnotation(AccessLimit.class);
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 或者url(存在map集合的key)
        String url = request.getRequestURI();
        if (!limitMap.containsKey(url)) {
            // 创建令牌桶
            rateLimiter = RateLimiter.create(lxRateLimit.perSecond());
            limitMap.put(url, rateLimiter);
            log.info("<<=================  请求{},创建令牌桶,容量{} 成功!!!",url,lxRateLimit.perSecond());
        }
        rateLimiter = limitMap.get(url);
        if (!rateLimiter.tryAcquire(lxRateLimit.timeOut(), lxRateLimit.timeOutUnit())) {//获取令牌
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            log.info("Error ---时间:{},获取令牌失败.", sdf.format(new Date()));
            throw new BusinessException("服务器繁忙,请稍后再试!");
        }
        return null;
    }
}
  • 切面
import com.liugh.annotation.AccessLimit;
import com.liugh.annotation.Log;
import com.liugh.annotation.ParamXssPass;
import com.liugh.annotation.ValidationParam;
import com.liugh.aspect.*;
import com.liugh.util.ComUtil;
import com.liugh.util.StringUtil;
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.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;

/**
 * 切面:防止xss攻击 记录log  参数验证
 * @author liugh
 * @since 2018-05-03
 */
@Aspect
@Configuration
public class ControllerAspect {

    @Pointcut("execution(* com.liugh.controller..*(..))  ")
    public void aspect() {
    }

    @Around(value = "aspect()")
    public Object validationPoint(ProceedingJoinPoint pjp)throws Throwable{
        Method method = currentMethod(pjp,pjp.getSignature().getName());
        //创建被装饰者
        AspectApiImpl aspectApi = new AspectApiImpl();
        //是否需要验证参数
        if (!ComUtil.isEmpty(StringUtil.getMethodAnnotationOne(method, ValidationParam.class.getSimpleName()))) {
            new ValidationParamAspect(aspectApi).doHandlerAspect(pjp,method);
        }
        //是否需要限流
        if (method.isAnnotationPresent(AccessLimit.class)) {
            new AccessLimitAspect(aspectApi).doHandlerAspect(pjp,method);
        }
        //是否需要拦截xss攻击
        if(method.isAnnotationPresent( ParamXssPass.class )){
           new ParamXssPassAspect(aspectApi).doHandlerAspect(pjp,method);
        }
        //是否需要记录日志
        if(method.isAnnotationPresent(Log.class)){
            return new RecordLogAspect(aspectApi).doHandlerAspect(pjp,method);
        }
        return  pjp.proceed(pjp.getArgs());
    }

    /**
     * 获取目标类的所有方法,找到当前要执行的方法
     */
    private Method currentMethod ( ProceedingJoinPoint joinPoint , String methodName ) {
        Method[] methods      = joinPoint.getTarget().getClass().getMethods();
        Method   resultMethod = null;
        for ( Method method : methods ) {
            if ( method.getName().equals( methodName ) ) {
                resultMethod = method;
                break;
            }
        }
        return resultMethod;
    }



}
  • 上面的代码还包含了参数验证,xss攻击拦截,日志记录,这些比较常用功能,感兴趣的自行下载代码浏览细节

1.2.3. 异步日志记录

  • 日志的异步记录,这是个好思路,日志记录不影响主任务,可以改成异步加快速度
/**
 * 线程池配置、启用异步
 * 
 * @author liugh
 *
 */
//开启异步
@EnableAsync(proxyTargetClass = true)
@Configuration
public class AsycTaskExecutorConfig {

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //核心线程数
        taskExecutor.setCorePoolSize(50);
        //最大线程数
        taskExecutor.setMaxPoolSize(100);
        return taskExecutor;
    }
}
  • 日志切面
import java.lang.reflect.Method;
import java.util.Map;

import com.alibaba.fastjson.JSONObject;
import com.liugh.annotation.Log;
import com.liugh.service.SpringContextBeanService;
import com.liugh.entity.OperationLog;
import com.liugh.service.IOperationLogService;
import com.liugh.util.ComUtil;
import com.liugh.util.JWTUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * 记录日志切面
 * @author liugh
 * @since on 2018/5/10.
 */
public class RecordLogAspect extends AbstractAspectManager {

    public RecordLogAspect(AspectApi aspectApi){
        super(aspectApi);
    }

    @Override
    public Object doHandlerAspect(ProceedingJoinPoint pjp, Method method) throws Throwable{
        super.doHandlerAspect(pjp,method);
        return execute(pjp,method);
    }

    private Logger logger = LoggerFactory.getLogger(RecordLogAspect.class);

    @Override
    @Async
    protected Object execute(ProceedingJoinPoint pjp, Method method) throws Throwable{
        Log log  = method.getAnnotation( Log.class );
        // 异常日志信息
        String actionLog = null;
        StackTraceElement[] stackTrace =null;
        // 是否执行异常
        boolean isException = false;
        // 接收时间戳
        long endTime;
        // 开始时间戳
        long operationTime = System.currentTimeMillis();
        try {
            return pjp.proceed(pjp.getArgs());
        } catch ( Throwable throwable ) {
            isException = true;
            actionLog = throwable.getMessage();
            stackTrace = throwable.getStackTrace();
            throw throwable;
        } finally {
            // 日志处理
            logHandle( pjp , method , log , actionLog , operationTime , isException,stackTrace );
        }
    }

    private void logHandle (ProceedingJoinPoint joinPoint ,
                            Method method ,
                            Log log ,
                            String actionLog ,
                            long startTime  ,
                            boolean isException,
                            StackTraceElement[] stackTrace) {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        IOperationLogService operationLogService = SpringContextBeanService.getBean(IOperationLogService.class);
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        String authorization = request.getHeader("Authorization");
        OperationLog operationLog = new OperationLog();
        if(!ComUtil.isEmpty(authorization)){
            String userNo = JWTUtil.getUserNo(authorization);
            operationLog.setUserNo(userNo);
        }
        operationLog.setIp(getIpAddress(request));
        operationLog.setClassName(joinPoint.getTarget().getClass().getName() );
        operationLog.setCreateTime(startTime);
        operationLog.setLogDescription(log.description());
        operationLog.setModelName(log.modelName());
        operationLog.setAction(log.action());
        if(isException){
            StringBuilder sb = new StringBuilder();
            sb.append(actionLog+" &#10; ");
            for (int i = 0; i < stackTrace.length; i++) {
                sb.append(stackTrace[i]+" &#10; ");
            }
            operationLog.setMessage(sb.toString());
        }
        operationLog.setMethodName(method.getName());
        operationLog.setSucceed(isException == true ? 2:1);
        Object[] args = joinPoint.getArgs();
        StringBuilder sb = new StringBuilder();
        boolean isJoint = false;
        for (int i = 0; i < args.length; i++) {
            if(args[i] instanceof JSONObject){
                JSONObject parse = (JSONObject)JSONObject.parse(args[i].toString());
                if(!ComUtil.isEmpty(parse.getString("password"))){
                    parse.put("password","*******");
                }
                if(!ComUtil.isEmpty(parse.getString("rePassword"))){
                    parse.put("rePassword","*******");
                }
                if(!ComUtil.isEmpty(parse.getString("oldPassword"))){
                    parse.put("oldPassword","*******");
                }
                operationLog.setActionArgs(parse.toString());
            }else if(args[i] instanceof String
                    || args[i] instanceof Long
                    || args[i] instanceof Integer
                    || args[i] instanceof Double
                    || args[i] instanceof Float
                    || args[i] instanceof Byte
                    || args[i] instanceof Short
                    || args[i] instanceof Character){
                isJoint=true;
            }
            else if(args[i] instanceof String []
                    || args[i] instanceof Long []
                    || args[i] instanceof Integer []
                    || args[i] instanceof Double []
                    || args[i] instanceof Float []
                    || args[i] instanceof Byte []
                    || args[i] instanceof Short []
                    || args[i] instanceof Character []){
                Object[] strs = (Object[])args[i];
                StringBuilder sbArray  =new StringBuilder();
                sbArray.append("[");
                for (Object str:strs) {
                    sbArray.append(str.toString()+",");
                }
                sbArray.deleteCharAt(sbArray.length()-1);
                sbArray.append("]");
                operationLog.setActionArgs(sbArray.toString());
            }else {
                continue;
            }
        }
        if(isJoint){
            Map<String, String[]> parameterMap = request.getParameterMap();
            for (String key:parameterMap.keySet()) {
                String[] strings = parameterMap.get(key);
                for (String str:strings) {
                    sb.append(key+"="+str+"&");
                }
            }
            operationLog.setActionArgs(sb.deleteCharAt(sb.length()-1).toString());
        }
        logger.info("执行方法信息:"+JSONObject.toJSON(operationLog));
        operationLogService.insert(operationLog);
    }


    private  String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip+":"+request.getRemotePort();
    }
}

1.2.4. 启动初始化扫描

  • 启动时扫描对应包,然后做相应处理,这里是把对应接口记录后用于后续pass,也就是通过@Pass不认证处理
import com.liugh.annotation.Pass;
import com.liugh.base.Constant;
import com.liugh.util.ComUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @author liugh
 * @Since 2018-05-10
 */
@Component
//日志打印 log.info
@Slf4j
public class MyCommandLineRunner implements CommandLineRunner {


    @Value("${controller.scanPackage}")
    private String scanPackage;

    @Override
    public void run(String... args) throws Exception {
        doScanner(scanPackage);
        Set<String> urlAndMethodSet  =new HashSet<>();
        for (String aClassName:Constant.METHOD_URL_SET) {
            Class<?> clazz = Class.forName(aClassName);
            String baseUrl = "";
            String[] classUrl ={};
            if(!ComUtil.isEmpty(clazz.getAnnotation(RequestMapping.class))){
                classUrl=clazz.getAnnotation(RequestMapping.class).value();
            }
            Method[] methods = clazz.getMethods();
            for (Method method:methods) {
                if(method.isAnnotationPresent(Pass.class)){
                    String [] methodUrl = null;
                    StringBuilder sb  =new StringBuilder();
                    if(!ComUtil.isEmpty(method.getAnnotation(PostMapping.class))){
                        methodUrl=method.getAnnotation(PostMapping.class).value();
                        if(ComUtil.isEmpty(methodUrl)){
                            methodUrl=method.getAnnotation(PostMapping.class).path();
                        }
                        baseUrl=getRequestUrl(classUrl, methodUrl, sb,"POST");
                    }else if(!ComUtil.isEmpty(method.getAnnotation(GetMapping.class))){
                        methodUrl=method.getAnnotation(GetMapping.class).value();
                        if(ComUtil.isEmpty(methodUrl)){
                            methodUrl=method.getAnnotation(GetMapping.class).path();
                        }
                        baseUrl=getRequestUrl(classUrl, methodUrl, sb,"GET");
                    }else if(!ComUtil.isEmpty(method.getAnnotation(DeleteMapping.class))){
                        methodUrl=method.getAnnotation(DeleteMapping.class).value();
                        if(ComUtil.isEmpty(methodUrl)){
                            methodUrl=method.getAnnotation(DeleteMapping.class).path();
                        }
                        baseUrl=getRequestUrl(classUrl, methodUrl, sb,"DELETE");
                    }else if(!ComUtil.isEmpty(method.getAnnotation(PutMapping.class))){
                        methodUrl=method.getAnnotation(PutMapping.class).value();
                        if(ComUtil.isEmpty(methodUrl)){
                            methodUrl=method.getAnnotation(PutMapping.class).path();
                        }
                        baseUrl=getRequestUrl(classUrl, methodUrl, sb,"PUT");
                    }else {
                        methodUrl=method.getAnnotation(RequestMapping.class).value();
                        baseUrl=getRequestUrl(classUrl, methodUrl, sb,RequestMapping.class.getSimpleName());
                    }
                    if(!ComUtil.isEmpty(baseUrl)){
                        urlAndMethodSet.add(baseUrl);
                    }
                }
            }
        }
        Constant.METHOD_URL_SET=urlAndMethodSet;
        log.info("@Pass:"+urlAndMethodSet);
    }

    private String  getRequestUrl(String[] classUrl, String[] methodUrl, StringBuilder sb,String requestName) {
        sb.append("/api/v1");
        if(!ComUtil.isEmpty(classUrl)){
            for (String url:classUrl) {
                sb.append(url+"/");
            }
        }
        for (String url:methodUrl) {
            sb.append(url);
        }
        if(sb.toString().endsWith("/")){
            sb.deleteCharAt(sb.length()-1);
        }
        return sb.toString().replaceAll("/+", "/")+":--:"+requestName;
    }

    private void doScanner(String packageName) {
        //把所有的.替换成/
        URL url  =this.getClass().getClassLoader().getResource(packageName.replaceAll("\\.", "/"));
        // 是否循环迭代
        if(StringUtils.countMatches(url.getFile(), ".jar")>0){
            boolean recursive=true;
            JarFile jar;
            // 获取jar
            try {
                jar = ((JarURLConnection) url.openConnection())
                        .getJarFile();
                // 从此jar包 得到一个枚举类
                Enumeration<JarEntry> entries = jar.entries();
                while (entries.hasMoreElements()) {
                    // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
                    JarEntry entry = entries.nextElement();
                    String name = entry.getName();
                    // 如果是以/开头的
                    if (name.charAt(0) == '/') {
                        // 获取后面的字符串
                        name = name.substring(1);
                    }
                    // 如果前半部分和定义的包名相同
                    if (name.startsWith(packageName.replaceAll("\\.","/"))) {
                        int idx = name.lastIndexOf('/');
                        // 如果以"/"结尾 是一个包
                        if (idx != -1) {
                            // 获取包名 把"/"替换成"."
                            packageName = name.substring(0, idx)
                                    .replace('/', '.');
                        }
                        // 如果可以迭代下去 并且是一个包
                        if ((idx != -1) || recursive) {
                            // 如果是一个.class文件 而且不是目录
                            if (name.endsWith(".class")
                                    && !entry.isDirectory()) {
                                // 去掉后面的".class" 获取真正的类名
                                String className = name.substring(
                                        packageName.length() + 1, name
                                                .length() - 6);
                                try {
                                    // 添加到classes
                                    Constant.METHOD_URL_SET.add(Class
                                            .forName(packageName + '.'
                                                    + className).getName());
                                } catch (ClassNotFoundException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
                return;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        File dir = new File(url.getFile());
        for (File file : dir.listFiles()) {
            if(file.isDirectory()){
                //递归读取包
                doScanner(packageName+"."+file.getName());
            }else{
                String className =packageName +"." +file.getName().replace(".class", "");
                Constant.METHOD_URL_SET.add(className);
            }
        }
    }

}

1.2.5. redis缓存用法

  • 缓存wiselyKeyGenerator具体用法,之前没了解到这个的具体用法,这次知道了
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import java.lang.reflect.Method;

/**
 * @author liugh
 * @since on 2018/5/11.
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /*定义缓存数据 key 生成策略的bean
   包名+类名+方法名+所有参数
   */
    @Bean("wiselyKeyGenerator")
    public KeyGenerator wiselyKeyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getSimpleName()+":");
                sb.append(method.getName()+":");
                for (Object obj : params) {
                    sb.append(obj.toString()+":");
                }
                return sb.deleteCharAt(sb.length()-1).toString();
            }
        };

    }

    /*要启用spring缓存支持,需创建一个 CacheManager的 bean,CacheManager 接口有很多实现,这里Redis 的集成,用 RedisCacheManager这个实现类
    Redis 不是应用的共享内存,它只是一个内存服务器,就像 MySql 似的,
    我们需要将应用连接到它并使用某种“语言”进行交互,因此我们还需要一个连接工厂以及一个 Spring 和 Redis 对话要用的 RedisTemplate,
    这些都是 Redis 缓存所必需的配置,把它们都放在自定义的 CachingConfigurerSupport 中
     */
    @Bean
    public CacheManager cacheManager(
            @SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
//        cacheManager.setDefaultExpiration(60);//设置缓存保留时间(seconds)
        return cacheManager;
    }
//    @Bean  springboot 2.0
//    public CacheManager cacheManager(
//            @SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
//        // 初始化缓存管理器,在这里我们可以缓存的整体过期时间什么的,我这里默认没有配置
//        RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
//                .RedisCacheManagerBuilder
//                .fromConnectionFactory(jedisConnectionFactory);
//        return builder.build();
//    }

    //1.项目启动时此方法先被注册成bean被spring管理
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}
  • 使用
    @Override
    //redis方法级别的缓存,需要做缓存打开改注解即可
    @Cacheable(value = "UserToRole",keyGenerator="wiselyKeyGenerator")
    public List<Menu> selectByIds(List<Integer> permissionIds) {
        EntityWrapper<Menu> ew = new EntityWrapper<>();
        ew.in("menu_id", permissionIds);
        return this.selectList(ew);
    }
点赞