• 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);
        }
    
  • 相关阅读:
    Java学习笔记-函数
    Java学习笔记-数组
    Git 常用命令速查表
    $.fn与$.fx什么意思; $.extend与$.fn.extend用法区别; $(function(){})和(function(){})(jQuery)
    offsetWidth的bug
    jQuery对象和DOM对象转换,解决jQuery对象不能使用js方法的问题
    1
    $().ready()与window.onload的不同
    offsetHeight在不同的浏览器下取值不同
    getElementsByName兼容ie 但并不是兼容ie下的所有标签
  • 原文地址:https://www.cnblogs.com/sky-chen/p/11170253.html
Copyright © 2020-2023  润新知