1. liugh-parent源码研究参考

1.1. 前言

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

1.2. 功能

1.2.1. 当前用户

  • 这里它用了注解切面进行登录用户的统一注入入口参数,这个做法可以进行参考,不需要在需要使用到登录用户的地方用对象去取了
  1. import com.liugh.annotation.CurrentUser;
  2. import com.liugh.exception.UnauthorizedException;
  3. import com.liugh.entity.User;
  4. import org.springframework.core.MethodParameter;
  5. import org.springframework.web.bind.support.WebDataBinderFactory;
  6. import org.springframework.web.context.request.NativeWebRequest;
  7. import org.springframework.web.context.request.RequestAttributes;
  8. import org.springframework.web.method.support.HandlerMethodArgumentResolver;
  9. import org.springframework.web.method.support.ModelAndViewContainer;
  10. /**
  11. * 增加方法注入,将含有 @CurrentUser 注解的方法参数注入当前登录用户
  12. * @author liugh
  13. * @since 2018-05-03
  14. */
  15. public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
  16. @Override
  17. public boolean supportsParameter(MethodParameter parameter) {
  18. return parameter.getParameterType().isAssignableFrom(User.class)
  19. && parameter.hasParameterAnnotation(CurrentUser.class);
  20. }
  21. @Override
  22. public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
  23. User user = (User) webRequest.getAttribute("currentUser", RequestAttributes.SCOPE_REQUEST);
  24. if (user == null) {
  25. throw new UnauthorizedException("获取用户信息失败");
  26. }
  27. return user;
  28. }
  29. }
  1. /**
  2. * 身份认证异常
  3. * @author liugh
  4. * @since 2018-05-06
  5. */
  6. public class UnauthorizedException extends RuntimeException {
  7. public UnauthorizedException(String msg) {
  8. super(msg);
  9. }
  10. public UnauthorizedException() {
  11. super();
  12. }
  13. }
  1. /**
  2. * 在Controller的方法参数中使用此注解,该方法在映射时会注入当前登录的User对象
  3. * @author : liugh
  4. * @date : 2018/05/08
  5. */
  6. @Target(ElementType.PARAMETER) // 可用在方法的参数上
  7. @Retention(RetentionPolicy.RUNTIME) // 运行时有效
  8. public @interface CurrentUser {
  9. }
  • 注入解析对象
  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.method.support.HandlerMethodArgumentResolver;
  4. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
  6. import java.util.List;
  7. /**
  8. * @author liugh
  9. * @since 2018-05-03
  10. */
  11. @Configuration
  12. public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
  13. @Override
  14. public void addInterceptors(InterceptorRegistry registry) {
  15. super.addInterceptors(registry);
  16. }
  17. @Override
  18. public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
  19. argumentResolvers.add(currentUserMethodArgumentResolver());
  20. super.addArgumentResolvers(argumentResolvers);
  21. }
  22. @Bean
  23. public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {
  24. return new CurrentUserMethodArgumentResolver();
  25. }
  26. }

1.2.2. 令牌桶

  • 这是一种限流思路,令牌桶算法请自行百度,这里也不是从零实现,用到了guava中的RateLimiter限流器
  1. import com.google.common.collect.Maps;
  2. import com.google.common.util.concurrent.RateLimiter;
  3. import com.liugh.annotation.AccessLimit;
  4. import com.liugh.base.BusinessException;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.aspectj.lang.ProceedingJoinPoint;
  7. import org.springframework.web.context.request.RequestContextHolder;
  8. import org.springframework.web.context.request.ServletRequestAttributes;
  9. import javax.servlet.http.HttpServletRequest;
  10. import java.lang.reflect.Method;
  11. import java.text.SimpleDateFormat;
  12. import java.util.Date;
  13. import java.util.Map;
  14. import java.util.concurrent.TimeUnit;
  15. /**
  16. * 限流切面
  17. * Created by liugh on 2018/10/12.
  18. */
  19. @Slf4j
  20. public class AccessLimitAspect extends AbstractAspectManager{
  21. public AccessLimitAspect(AspectApi aspectApi){
  22. super(aspectApi);
  23. }
  24. @Override
  25. public Object doHandlerAspect(ProceedingJoinPoint pjp, Method method)throws Throwable {
  26. super.doHandlerAspect(pjp,method);
  27. execute(pjp,method);
  28. return null;
  29. }
  30. //添加速率.保证是单例的
  31. private static RateLimiter rateLimiter = RateLimiter.create(1000);
  32. //使用url做为key,存放令牌桶 防止每次重新创建令牌桶
  33. private static Map<String, RateLimiter> limitMap = Maps.newConcurrentMap();
  34. @Override
  35. public Object execute(ProceedingJoinPoint pjp,Method method) throws Throwable{
  36. AccessLimit lxRateLimit = method.getAnnotation(AccessLimit.class);
  37. HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
  38. // 或者url(存在map集合的key)
  39. String url = request.getRequestURI();
  40. if (!limitMap.containsKey(url)) {
  41. // 创建令牌桶
  42. rateLimiter = RateLimiter.create(lxRateLimit.perSecond());
  43. limitMap.put(url, rateLimiter);
  44. log.info("<<================= 请求{},创建令牌桶,容量{} 成功!!!",url,lxRateLimit.perSecond());
  45. }
  46. rateLimiter = limitMap.get(url);
  47. if (!rateLimiter.tryAcquire(lxRateLimit.timeOut(), lxRateLimit.timeOutUnit())) {//获取令牌
  48. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  49. log.info("Error ---时间:{},获取令牌失败.", sdf.format(new Date()));
  50. throw new BusinessException("服务器繁忙,请稍后再试!");
  51. }
  52. return null;
  53. }
  54. }
  • 切面
  1. import com.liugh.annotation.AccessLimit;
  2. import com.liugh.annotation.Log;
  3. import com.liugh.annotation.ParamXssPass;
  4. import com.liugh.annotation.ValidationParam;
  5. import com.liugh.aspect.*;
  6. import com.liugh.util.ComUtil;
  7. import com.liugh.util.StringUtil;
  8. import org.aspectj.lang.ProceedingJoinPoint;
  9. import org.aspectj.lang.annotation.Around;
  10. import org.aspectj.lang.annotation.Aspect;
  11. import org.aspectj.lang.annotation.Pointcut;
  12. import org.springframework.context.annotation.Configuration;
  13. import java.lang.reflect.Method;
  14. /**
  15. * 切面:防止xss攻击 记录log 参数验证
  16. * @author liugh
  17. * @since 2018-05-03
  18. */
  19. @Aspect
  20. @Configuration
  21. public class ControllerAspect {
  22. @Pointcut("execution(* com.liugh.controller..*(..)) ")
  23. public void aspect() {
  24. }
  25. @Around(value = "aspect()")
  26. public Object validationPoint(ProceedingJoinPoint pjp)throws Throwable{
  27. Method method = currentMethod(pjp,pjp.getSignature().getName());
  28. //创建被装饰者
  29. AspectApiImpl aspectApi = new AspectApiImpl();
  30. //是否需要验证参数
  31. if (!ComUtil.isEmpty(StringUtil.getMethodAnnotationOne(method, ValidationParam.class.getSimpleName()))) {
  32. new ValidationParamAspect(aspectApi).doHandlerAspect(pjp,method);
  33. }
  34. //是否需要限流
  35. if (method.isAnnotationPresent(AccessLimit.class)) {
  36. new AccessLimitAspect(aspectApi).doHandlerAspect(pjp,method);
  37. }
  38. //是否需要拦截xss攻击
  39. if(method.isAnnotationPresent( ParamXssPass.class )){
  40. new ParamXssPassAspect(aspectApi).doHandlerAspect(pjp,method);
  41. }
  42. //是否需要记录日志
  43. if(method.isAnnotationPresent(Log.class)){
  44. return new RecordLogAspect(aspectApi).doHandlerAspect(pjp,method);
  45. }
  46. return pjp.proceed(pjp.getArgs());
  47. }
  48. /**
  49. * 获取目标类的所有方法,找到当前要执行的方法
  50. */
  51. private Method currentMethod ( ProceedingJoinPoint joinPoint , String methodName ) {
  52. Method[] methods = joinPoint.getTarget().getClass().getMethods();
  53. Method resultMethod = null;
  54. for ( Method method : methods ) {
  55. if ( method.getName().equals( methodName ) ) {
  56. resultMethod = method;
  57. break;
  58. }
  59. }
  60. return resultMethod;
  61. }
  62. }
  • 上面的代码还包含了参数验证,xss攻击拦截,日志记录,这些比较常用功能,感兴趣的自行下载代码浏览细节

1.2.3. 异步日志记录

  • 日志的异步记录,这是个好思路,日志记录不影响主任务,可以改成异步加快速度
  1. /**
  2. * 线程池配置、启用异步
  3. *
  4. * @author liugh
  5. *
  6. */
  7. //开启异步
  8. @EnableAsync(proxyTargetClass = true)
  9. @Configuration
  10. public class AsycTaskExecutorConfig {
  11. @Bean
  12. public TaskExecutor taskExecutor() {
  13. ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
  14. //核心线程数
  15. taskExecutor.setCorePoolSize(50);
  16. //最大线程数
  17. taskExecutor.setMaxPoolSize(100);
  18. return taskExecutor;
  19. }
  20. }
  • 日志切面
  1. import java.lang.reflect.Method;
  2. import java.util.Map;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.liugh.annotation.Log;
  5. import com.liugh.service.SpringContextBeanService;
  6. import com.liugh.entity.OperationLog;
  7. import com.liugh.service.IOperationLogService;
  8. import com.liugh.util.ComUtil;
  9. import com.liugh.util.JWTUtil;
  10. import org.aspectj.lang.ProceedingJoinPoint;
  11. import org.slf4j.Logger;
  12. import org.slf4j.LoggerFactory;
  13. import org.springframework.scheduling.annotation.Async;
  14. import org.springframework.web.context.request.RequestAttributes;
  15. import org.springframework.web.context.request.RequestContextHolder;
  16. import org.springframework.web.context.request.ServletRequestAttributes;
  17. import javax.servlet.http.HttpServletRequest;
  18. /**
  19. * 记录日志切面
  20. * @author liugh
  21. * @since on 2018/5/10.
  22. */
  23. public class RecordLogAspect extends AbstractAspectManager {
  24. public RecordLogAspect(AspectApi aspectApi){
  25. super(aspectApi);
  26. }
  27. @Override
  28. public Object doHandlerAspect(ProceedingJoinPoint pjp, Method method) throws Throwable{
  29. super.doHandlerAspect(pjp,method);
  30. return execute(pjp,method);
  31. }
  32. private Logger logger = LoggerFactory.getLogger(RecordLogAspect.class);
  33. @Override
  34. @Async
  35. protected Object execute(ProceedingJoinPoint pjp, Method method) throws Throwable{
  36. Log log = method.getAnnotation( Log.class );
  37. // 异常日志信息
  38. String actionLog = null;
  39. StackTraceElement[] stackTrace =null;
  40. // 是否执行异常
  41. boolean isException = false;
  42. // 接收时间戳
  43. long endTime;
  44. // 开始时间戳
  45. long operationTime = System.currentTimeMillis();
  46. try {
  47. return pjp.proceed(pjp.getArgs());
  48. } catch ( Throwable throwable ) {
  49. isException = true;
  50. actionLog = throwable.getMessage();
  51. stackTrace = throwable.getStackTrace();
  52. throw throwable;
  53. } finally {
  54. // 日志处理
  55. logHandle( pjp , method , log , actionLog , operationTime , isException,stackTrace );
  56. }
  57. }
  58. private void logHandle (ProceedingJoinPoint joinPoint ,
  59. Method method ,
  60. Log log ,
  61. String actionLog ,
  62. long startTime ,
  63. boolean isException,
  64. StackTraceElement[] stackTrace) {
  65. RequestAttributes ra = RequestContextHolder.getRequestAttributes();
  66. IOperationLogService operationLogService = SpringContextBeanService.getBean(IOperationLogService.class);
  67. ServletRequestAttributes sra = (ServletRequestAttributes) ra;
  68. HttpServletRequest request = sra.getRequest();
  69. String authorization = request.getHeader("Authorization");
  70. OperationLog operationLog = new OperationLog();
  71. if(!ComUtil.isEmpty(authorization)){
  72. String userNo = JWTUtil.getUserNo(authorization);
  73. operationLog.setUserNo(userNo);
  74. }
  75. operationLog.setIp(getIpAddress(request));
  76. operationLog.setClassName(joinPoint.getTarget().getClass().getName() );
  77. operationLog.setCreateTime(startTime);
  78. operationLog.setLogDescription(log.description());
  79. operationLog.setModelName(log.modelName());
  80. operationLog.setAction(log.action());
  81. if(isException){
  82. StringBuilder sb = new StringBuilder();
  83. sb.append(actionLog+"
  84. ");
  85. for (int i = 0; i < stackTrace.length; i++) {
  86. sb.append(stackTrace[i]+"
  87. ");
  88. }
  89. operationLog.setMessage(sb.toString());
  90. }
  91. operationLog.setMethodName(method.getName());
  92. operationLog.setSucceed(isException == true ? 2:1);
  93. Object[] args = joinPoint.getArgs();
  94. StringBuilder sb = new StringBuilder();
  95. boolean isJoint = false;
  96. for (int i = 0; i < args.length; i++) {
  97. if(args[i] instanceof JSONObject){
  98. JSONObject parse = (JSONObject)JSONObject.parse(args[i].toString());
  99. if(!ComUtil.isEmpty(parse.getString("password"))){
  100. parse.put("password","*******");
  101. }
  102. if(!ComUtil.isEmpty(parse.getString("rePassword"))){
  103. parse.put("rePassword","*******");
  104. }
  105. if(!ComUtil.isEmpty(parse.getString("oldPassword"))){
  106. parse.put("oldPassword","*******");
  107. }
  108. operationLog.setActionArgs(parse.toString());
  109. }else if(args[i] instanceof String
  110. || args[i] instanceof Long
  111. || args[i] instanceof Integer
  112. || args[i] instanceof Double
  113. || args[i] instanceof Float
  114. || args[i] instanceof Byte
  115. || args[i] instanceof Short
  116. || args[i] instanceof Character){
  117. isJoint=true;
  118. }
  119. else if(args[i] instanceof String []
  120. || args[i] instanceof Long []
  121. || args[i] instanceof Integer []
  122. || args[i] instanceof Double []
  123. || args[i] instanceof Float []
  124. || args[i] instanceof Byte []
  125. || args[i] instanceof Short []
  126. || args[i] instanceof Character []){
  127. Object[] strs = (Object[])args[i];
  128. StringBuilder sbArray =new StringBuilder();
  129. sbArray.append("[");
  130. for (Object str:strs) {
  131. sbArray.append(str.toString()+",");
  132. }
  133. sbArray.deleteCharAt(sbArray.length()-1);
  134. sbArray.append("]");
  135. operationLog.setActionArgs(sbArray.toString());
  136. }else {
  137. continue;
  138. }
  139. }
  140. if(isJoint){
  141. Map<String, String[]> parameterMap = request.getParameterMap();
  142. for (String key:parameterMap.keySet()) {
  143. String[] strings = parameterMap.get(key);
  144. for (String str:strings) {
  145. sb.append(key+"="+str+"&");
  146. }
  147. }
  148. operationLog.setActionArgs(sb.deleteCharAt(sb.length()-1).toString());
  149. }
  150. logger.info("执行方法信息:"+JSONObject.toJSON(operationLog));
  151. operationLogService.insert(operationLog);
  152. }
  153. private String getIpAddress(HttpServletRequest request) {
  154. String ip = request.getHeader("x-forwarded-for");
  155. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  156. ip = request.getHeader("Proxy-Client-IP");
  157. }
  158. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  159. ip = request.getHeader("WL-Proxy-Client-IP");
  160. }
  161. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  162. ip = request.getHeader("HTTP_CLIENT_IP");
  163. }
  164. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  165. ip = request.getHeader("HTTP_X_FORWARDED_FOR");
  166. }
  167. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  168. ip = request.getRemoteAddr();
  169. }
  170. return ip+":"+request.getRemotePort();
  171. }
  172. }

1.2.4. 启动初始化扫描

  • 启动时扫描对应包,然后做相应处理,这里是把对应接口记录后用于后续pass,也就是通过@Pass不认证处理
  1. import com.liugh.annotation.Pass;
  2. import com.liugh.base.Constant;
  3. import com.liugh.util.ComUtil;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.apache.commons.lang3.StringUtils;
  6. import org.springframework.beans.factory.annotation.Value;
  7. import org.springframework.boot.CommandLineRunner;
  8. import org.springframework.stereotype.Component;
  9. import org.springframework.web.bind.annotation.*;
  10. import java.io.File;
  11. import java.io.IOException;
  12. import java.lang.reflect.Method;
  13. import java.net.JarURLConnection;
  14. import java.net.URL;
  15. import java.util.Enumeration;
  16. import java.util.HashSet;
  17. import java.util.Set;
  18. import java.util.jar.JarEntry;
  19. import java.util.jar.JarFile;
  20. /**
  21. * @author liugh
  22. * @Since 2018-05-10
  23. */
  24. @Component
  25. //日志打印 log.info
  26. @Slf4j
  27. public class MyCommandLineRunner implements CommandLineRunner {
  28. @Value("${controller.scanPackage}")
  29. private String scanPackage;
  30. @Override
  31. public void run(String... args) throws Exception {
  32. doScanner(scanPackage);
  33. Set<String> urlAndMethodSet =new HashSet<>();
  34. for (String aClassName:Constant.METHOD_URL_SET) {
  35. Class<?> clazz = Class.forName(aClassName);
  36. String baseUrl = "";
  37. String[] classUrl ={};
  38. if(!ComUtil.isEmpty(clazz.getAnnotation(RequestMapping.class))){
  39. classUrl=clazz.getAnnotation(RequestMapping.class).value();
  40. }
  41. Method[] methods = clazz.getMethods();
  42. for (Method method:methods) {
  43. if(method.isAnnotationPresent(Pass.class)){
  44. String [] methodUrl = null;
  45. StringBuilder sb =new StringBuilder();
  46. if(!ComUtil.isEmpty(method.getAnnotation(PostMapping.class))){
  47. methodUrl=method.getAnnotation(PostMapping.class).value();
  48. if(ComUtil.isEmpty(methodUrl)){
  49. methodUrl=method.getAnnotation(PostMapping.class).path();
  50. }
  51. baseUrl=getRequestUrl(classUrl, methodUrl, sb,"POST");
  52. }else if(!ComUtil.isEmpty(method.getAnnotation(GetMapping.class))){
  53. methodUrl=method.getAnnotation(GetMapping.class).value();
  54. if(ComUtil.isEmpty(methodUrl)){
  55. methodUrl=method.getAnnotation(GetMapping.class).path();
  56. }
  57. baseUrl=getRequestUrl(classUrl, methodUrl, sb,"GET");
  58. }else if(!ComUtil.isEmpty(method.getAnnotation(DeleteMapping.class))){
  59. methodUrl=method.getAnnotation(DeleteMapping.class).value();
  60. if(ComUtil.isEmpty(methodUrl)){
  61. methodUrl=method.getAnnotation(DeleteMapping.class).path();
  62. }
  63. baseUrl=getRequestUrl(classUrl, methodUrl, sb,"DELETE");
  64. }else if(!ComUtil.isEmpty(method.getAnnotation(PutMapping.class))){
  65. methodUrl=method.getAnnotation(PutMapping.class).value();
  66. if(ComUtil.isEmpty(methodUrl)){
  67. methodUrl=method.getAnnotation(PutMapping.class).path();
  68. }
  69. baseUrl=getRequestUrl(classUrl, methodUrl, sb,"PUT");
  70. }else {
  71. methodUrl=method.getAnnotation(RequestMapping.class).value();
  72. baseUrl=getRequestUrl(classUrl, methodUrl, sb,RequestMapping.class.getSimpleName());
  73. }
  74. if(!ComUtil.isEmpty(baseUrl)){
  75. urlAndMethodSet.add(baseUrl);
  76. }
  77. }
  78. }
  79. }
  80. Constant.METHOD_URL_SET=urlAndMethodSet;
  81. log.info("@Pass:"+urlAndMethodSet);
  82. }
  83. private String getRequestUrl(String[] classUrl, String[] methodUrl, StringBuilder sb,String requestName) {
  84. sb.append("/api/v1");
  85. if(!ComUtil.isEmpty(classUrl)){
  86. for (String url:classUrl) {
  87. sb.append(url+"/");
  88. }
  89. }
  90. for (String url:methodUrl) {
  91. sb.append(url);
  92. }
  93. if(sb.toString().endsWith("/")){
  94. sb.deleteCharAt(sb.length()-1);
  95. }
  96. return sb.toString().replaceAll("/+", "/")+":--:"+requestName;
  97. }
  98. private void doScanner(String packageName) {
  99. //把所有的.替换成/
  100. URL url =this.getClass().getClassLoader().getResource(packageName.replaceAll("\\.", "/"));
  101. // 是否循环迭代
  102. if(StringUtils.countMatches(url.getFile(), ".jar")>0){
  103. boolean recursive=true;
  104. JarFile jar;
  105. // 获取jar
  106. try {
  107. jar = ((JarURLConnection) url.openConnection())
  108. .getJarFile();
  109. // 从此jar包 得到一个枚举类
  110. Enumeration<JarEntry> entries = jar.entries();
  111. while (entries.hasMoreElements()) {
  112. // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
  113. JarEntry entry = entries.nextElement();
  114. String name = entry.getName();
  115. // 如果是以/开头的
  116. if (name.charAt(0) == '/') {
  117. // 获取后面的字符串
  118. name = name.substring(1);
  119. }
  120. // 如果前半部分和定义的包名相同
  121. if (name.startsWith(packageName.replaceAll("\\.","/"))) {
  122. int idx = name.lastIndexOf('/');
  123. // 如果以"/"结尾 是一个包
  124. if (idx != -1) {
  125. // 获取包名 把"/"替换成"."
  126. packageName = name.substring(0, idx)
  127. .replace('/', '.');
  128. }
  129. // 如果可以迭代下去 并且是一个包
  130. if ((idx != -1) || recursive) {
  131. // 如果是一个.class文件 而且不是目录
  132. if (name.endsWith(".class")
  133. && !entry.isDirectory()) {
  134. // 去掉后面的".class" 获取真正的类名
  135. String className = name.substring(
  136. packageName.length() + 1, name
  137. .length() - 6);
  138. try {
  139. // 添加到classes
  140. Constant.METHOD_URL_SET.add(Class
  141. .forName(packageName + '.'
  142. + className).getName());
  143. } catch (ClassNotFoundException e) {
  144. e.printStackTrace();
  145. }
  146. }
  147. }
  148. }
  149. }
  150. return;
  151. } catch (IOException e) {
  152. e.printStackTrace();
  153. }
  154. }
  155. File dir = new File(url.getFile());
  156. for (File file : dir.listFiles()) {
  157. if(file.isDirectory()){
  158. //递归读取包
  159. doScanner(packageName+"."+file.getName());
  160. }else{
  161. String className =packageName +"." +file.getName().replace(".class", "");
  162. Constant.METHOD_URL_SET.add(className);
  163. }
  164. }
  165. }
  166. }

1.2.5. redis缓存用法

  • 缓存wiselyKeyGenerator具体用法,之前没了解到这个的具体用法,这次知道了
  1. import com.fasterxml.jackson.annotation.JsonAutoDetect;
  2. import com.fasterxml.jackson.annotation.PropertyAccessor;
  3. import com.fasterxml.jackson.databind.ObjectMapper;
  4. import org.springframework.cache.CacheManager;
  5. import org.springframework.cache.annotation.CachingConfigurerSupport;
  6. import org.springframework.cache.annotation.EnableCaching;
  7. import org.springframework.cache.interceptor.KeyGenerator;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.context.annotation.Configuration;
  10. import org.springframework.data.redis.cache.RedisCacheManager;
  11. import org.springframework.data.redis.connection.RedisConnectionFactory;
  12. import org.springframework.data.redis.core.RedisTemplate;
  13. import org.springframework.data.redis.core.StringRedisTemplate;
  14. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  15. import java.lang.reflect.Method;
  16. /**
  17. * @author liugh
  18. * @since on 2018/5/11.
  19. */
  20. @Configuration
  21. @EnableCaching
  22. public class RedisConfig extends CachingConfigurerSupport {
  23. /*定义缓存数据 key 生成策略的bean
  24. 包名+类名+方法名+所有参数
  25. */
  26. @Bean("wiselyKeyGenerator")
  27. public KeyGenerator wiselyKeyGenerator(){
  28. return new KeyGenerator() {
  29. @Override
  30. public Object generate(Object target, Method method, Object... params) {
  31. StringBuilder sb = new StringBuilder();
  32. sb.append(target.getClass().getSimpleName()+":");
  33. sb.append(method.getName()+":");
  34. for (Object obj : params) {
  35. sb.append(obj.toString()+":");
  36. }
  37. return sb.deleteCharAt(sb.length()-1).toString();
  38. }
  39. };
  40. }
  41. /*要启用spring缓存支持,需创建一个 CacheManager的 bean,CacheManager 接口有很多实现,这里Redis 的集成,用 RedisCacheManager这个实现类
  42. Redis 不是应用的共享内存,它只是一个内存服务器,就像 MySql 似的,
  43. 我们需要将应用连接到它并使用某种“语言”进行交互,因此我们还需要一个连接工厂以及一个 Spring 和 Redis 对话要用的 RedisTemplate,
  44. 这些都是 Redis 缓存所必需的配置,把它们都放在自定义的 CachingConfigurerSupport 中
  45. */
  46. @Bean
  47. public CacheManager cacheManager(
  48. @SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
  49. RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
  50. // cacheManager.setDefaultExpiration(60);//设置缓存保留时间(seconds)
  51. return cacheManager;
  52. }
  53. // @Bean springboot 2.0
  54. // public CacheManager cacheManager(
  55. // @SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
  56. // // 初始化缓存管理器,在这里我们可以缓存的整体过期时间什么的,我这里默认没有配置
  57. // RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
  58. // .RedisCacheManagerBuilder
  59. // .fromConnectionFactory(jedisConnectionFactory);
  60. // return builder.build();
  61. // }
  62. //1.项目启动时此方法先被注册成bean被spring管理
  63. @Bean
  64. public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
  65. StringRedisTemplate template = new StringRedisTemplate(factory);
  66. Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  67. ObjectMapper om = new ObjectMapper();
  68. om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  69. om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  70. jackson2JsonRedisSerializer.setObjectMapper(om);
  71. template.setValueSerializer(jackson2JsonRedisSerializer);
  72. template.afterPropertiesSet();
  73. return template;
  74. }
  75. }
  • 使用
  1. @Override
  2. //redis方法级别的缓存,需要做缓存打开改注解即可
  3. @Cacheable(value = "UserToRole",keyGenerator="wiselyKeyGenerator")
  4. public List<Menu> selectByIds(List<Integer> permissionIds) {
  5. EntityWrapper<Menu> ew = new EntityWrapper<>();
  6. ew.in("menu_id", permissionIds);
  7. return this.selectList(ew);
  8. }

springboot脚手架liugh-parent源码研究参考的更多相关文章

  1. 从源码研究如何不重启Springboot项目实现redis配置动态切换

    上一篇Websocket的续篇暂时还没有动手写,这篇算是插播吧.今天讲讲不重启项目动态切换redis服务. 背景 多个项目或微服务场景下,各个项目都需要配置redis数据源.但是,每当运维搞事时(修改 ...

  2. zepto源码研究 - zepto.js - 1

    简要:网上已经有很多人已经将zepto的源码研究得很细致了,但我还是想写下zepto源码系列,将别人的东西和自己的想法写下来以加深印象也是自娱自乐,文章中可能有许多错误,望有人不吝指出,烦请赐教. 首 ...

  3. underscore.js源码研究(6)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

  4. 阿里sentinel源码研究深入

    1. 阿里sentinel源码研究深入 1.1. 前言 昨天已经把sentinel成功部署到线上环境,可参考我上篇博文,该走的坑也都走了一遍,已经可以初步使用它的限流和降级功能,根据我目前的实践,限流 ...

  5. OAuth2学习及DotNetOpenAuth部分源码研究

    OAuth2学习及DotNetOpenAuth部分源码研究 在上篇文章中我研究了OpenId及DotNetOpenAuth的相关应用,这一篇继续研究OAuth2. 一.什么是OAuth2 OAuth是 ...

  6. Android开源项目 Universal imageloader 源码研究之Lru算法

    https://github.com/nostra13/Android-Universal-Image-Loader universal imageloader 源码研究之Lru算法 LRU - Le ...

  7. dubbo源码研究(一)

    1. dubbo源码研究(一) 1.1. dubbo启动加载过程 我们知道,现在流行注解方式,用spring管理服务,dubbo最常用的就是@Reference和@Service了,那么我首先找到这两 ...

  8. 【JavaScript】$.extend使用心得及源码研究

    最近写多了js的面向对象编程,用$.extend写继承写得很顺手.但是在使用过程中发现有几个问题. 1.深拷贝 $.extend默认是浅拷贝,这意味着在继承复杂对象时,对象中内嵌的对象无法被拷贝到. ...

  9. list源码4(参考STL源码--侯捷):transfer、splice、merge、reverse、sort

    list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考STL ...

随机推荐

  1. java中的this关键字三种作用

    1.表示类中的属性和调用方法2.调用本类中的构造方法3.表示当前对象

  2. nginx 跨域请求访问

    1.nginx跨域请求访问 location ~ .*\.(htm|html)$ { add_header Access-Control-Allow-Origin(请求域名) *(所有域名) http ...

  3. MySQL 官方测试库

    MySQL 官方测试库 github 地址 https://github.com/datacharmer/test_db MySQL 文档地址 https://dev.mysql.com/doc/em ...

  4. hdu2281&&POJ1320——Pell方程

    hdu2281 输入一个 $N$,求最大的 $n$($n \leq N$)和 $x$,使得 $x^2 = \frac{1^2+2^2+...+n^2}{n}$. 分析: 将右边式子的分子求和化简,有: ...

  5. Office Tool Plus 安装Office

    Office Tool Plus 是一款集office 卸载安装管理,激活等多功能于一体的神器. 官网:https://otp.landian.vip/en-us/ 下载 安装 值得注意的是Retai ...

  6. Kali系统改国内源配置和SSH配置

    一.Kali系统更新源 使用官网的虚拟化镜像安装,默认为英文界面,更新源也是官方源.因为官方服务器在国外,速度不是很理想,现在就来改国内源并且更新系统. 1.使用编辑器打开系统源文本(在终端内操作,先 ...

  7. 2.shell编程-函数的高级用法

    2.1.函数的定义和使用 函数基本使用 [root@VM_0_9_centos ~]# test() > {} -bash: syntax error near unexpected token ...

  8. selenium--浏览器窗口截图

    前戏 在进行web自动化的时候,只有一个报错信息是不行的,往往需要截图来帮助我们来快速的定位问题,试想一下,我们在一个弹框里添加一些数据,点击保存后,然后在操作元素,这时selenium报错,说找不到 ...

  9. 直接插入排序与缩小增量插入排序(希尔排序ShellSort)

    直接插入排序 要理解shell排序,首先要把直接插入排序的基础打扎实. 学习资料:白话经典算法系列之二 直接插入排序的三种实现.直接插入排序 根据我的思路,直接插入排序设置3重循环. 循环1:对 i= ...

  10. 奇技淫巧and板子

    目录 求第\(k\)大的数 求长度不小于L的子段使之和最大 ST表 \(O(1)\)实现能查询栈中最小元素 二分 树和图的深度优先遍历和广度优先遍历 树的dfs序 求树的重心 图的联通块划分 拓扑排序 ...