java自定义注解annotation记录操作日志

x33g5p2x  于2021-12-01 转载在 Java  
字(5.8k)|赞(0)|评价(0)|浏览(514)

说到注解我们平常用的可以说非常多啦,说几个常用的的注解 @RestController``@Service``@Autowired
这些都是我们平常使用spring框架最常见的注解了,我们只知道它们非常好用,使用@RestController 就能构建一个restful的控制器,@Service 这个是我们常用的mvc架构中的业务层使用的注解,将类交给spring容器管理,我们要用的话直接使用@Autowired就能将类自动注入。我们都知道用了这些注解非常的方便,今天我们自己也来写一个自己的注解。

需求

一个项目,有些方法是需要被保护起来的,有写方法是开放出来不需要保护的,比如登录 注册等 都不需要保护,解决方案有很多,今天我们就使用springboot的aop 和自定义注解来解决这个需求。
在创建自己的注解之前,了解一些注解的知识。
1.首先java jdk给我们提供了meta-annotation用于自定义注解的时候使用,这四个注解为:@Target,@Retention,@Documented 和@Inherited。

  • 第一个注解@Target
    @Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方),其源码如下:
  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.ANNOTATION_TYPE)
  4. public @interface Target {
  5. ElementType[] value();
  6. }

我们可以看到它只有一个属性值,是个枚举类型的数组,我们进该枚举类型看看

  1. public enum ElementType {
  2. TYPE,//用于描述类、接口(包括注解类型) 或enum声明
  3. FIELD, //用于描述成员变量;
  4. METHOD,//用于描述方法
  5. PARAMETER,//用于描述参数
  6. CONSTRUCTOR,//用于描述构造器
  7. LOCAL_VARIABLE,//用于描述局部变量;
  8. ANNOTATION_TYPE,//注解类型声明 该注解可用于描述注解
  9. PACKAGE,//用于描述包
  10. TYPE_PARAMETER,//这个是jdk1.8后加入的类型 表示这个 Annotation 可以用在 Type 的声明式前
  11. TYPE_USE//表示这个 Annotation 可以用在所有使用 Type 的地方(如:泛型,类型转换等)
  12. }
  • 第二个注解@Retention
    @Retention:指定被描述的注解在什么范围内有效。源码如下:
  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.ANNOTATION_TYPE)
  4. public @interface Retention {
  5. RetentionPolicy value();
  6. }

可以看到,该注解也只有一个枚举的属性,我们进该枚举类型看看

  1. public enum RetentionPolicy {
  2. SOURCE,//表示描述程序编译时
  3. CLASS,//在class文件中有效(即class保留
  4. RUNTIME//在运行时有效(即运行时保留)
  5. }
  • 第三个注解:@Documented

@Documented 是一个标记注解,木有成员,用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。

  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.ANNOTATION_TYPE)
  4. public @interface Documented {
  5. }
  • 第四个注解 @Inherited

@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.ANNOTATION_TYPE)
  4. public @interface Retention {
  5. RetentionPolicy value();
  6. }

下面我们开始编写我们自己的注解

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface IgnoreToken {
  5. }
  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Inherited
  4. public @interface Log {
  5. String name() default "";
  6. }

编写aop拦截所有的controller请求

  1. @Component
  2. @Aspect
  3. @Order(1)
  4. public class TokenAspect {
  5. @Autowired
  6. private HttpServletRequest request;
  7. @Autowired
  8. private HttpServletResponse response;
  9. @Autowired
  10. private LoginController loginController;
  11. @Pointcut("within(com.niehziliang.annotation.demo.controller..*)")
  12. public void checkToken () {}
  13. @Before("checkToken()")
  14. public void checkToken (JoinPoint joinPoint) throws IOException {
  15. MethodSignature signature = (MethodSignature)joinPoint.getSignature();
  16. //获取当前访问的类方法
  17. Method targetMethod = signature.getMethod();
  18. //判断是否是注解修饰的类,如果是则不需要校验token
  19. if(!targetMethod.isAnnotationPresent(IgnoreToken.class)){
  20. String token = request.getParameter("token");
  21. if (null == token || "".equals(token)) {
  22. response.setCharacterEncoding("utf-8");
  23. response.setContentType("application/json; charset=utf-8");
  24. PrintWriter out = response.getWriter();
  25. out.print("token不能为空");
  26. out.flush();
  27. out.close();
  28. } else {
  29. if (!loginController.chkToken(token)) {
  30. response.setCharacterEncoding("utf-8");
  31. response.setContentType("application/json; charset=utf-8");
  32. PrintWriter out = response.getWriter();
  33. out.print("token不合法");
  34. out.flush();
  35. out.close();
  36. }
  37. }
  38. }
  39. }
  40. }

aop日志拦截

  1. @Component
  2. @Aspect
  3. @Order(2)
  4. public class LogAspect {
  5. @Autowired
  6. private HttpServletRequest request;
  7. @Autowired
  8. private LoginController loginController;
  9. @Pointcut("@annotation(com.niehziliang.annotation.demo.annos.Log)")
  10. public void saveLog() {}
  11. @Around("saveLog()")
  12. public Object saveLog(ProceedingJoinPoint point) throws Throwable {
  13. long start = System.currentTimeMillis();
  14. MethodSignature signature = (MethodSignature) point.getSignature();
  15. Method method = signature.getMethod();
  16. Log logAnnotation = method.getAnnotation(Log.class);
  17. String name = null;
  18. if (logAnnotation != null) {
  19. // 注解上的描述
  20. name = logAnnotation.name();
  21. }
  22. // 请求的方法名
  23. String className = point.getTarget().getClass().getName();
  24. String methodName = signature.getName();
  25. // 请求的方法参数值
  26. Object[] args = point.getArgs();
  27. LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
  28. String[] paramNames = u.getParameterNames(method);
  29. String params = "";
  30. if (args != null && paramNames != null) {
  31. for (int i = 0; i < args.length; i++) {
  32. params += " " + paramNames[i] + ": " + args[i];
  33. }
  34. }
  35. String ip = IpUtils.getIpAddress(request);
  36. long time = System.currentTimeMillis() - start;
  37. StringBuffer log = new StringBuffer();
  38. log.append("注解上的name:").append(name).append("=======")
  39. .append("请求的方法:").append(className).append(".").append(methodName).append("=====")
  40. .append("请求参数:").append(params).append("=======")
  41. .append("请求的ip:").append(ip).append("耗时:").append(time).append("ms");
  42. System.out.println(log.toString());
  43. loginController.saveLog(log.toString());
  44. return point.proceed();
  45. }
  46. }

编写controller代码, 登录方法加了我自己定义的注解@IgnoreToken aop在拦截的时候判断有我们自己定义注解就不会去校验token啦,获取密码的方法则会进行token的校验

  1. @RestController
  2. public class LoginController {
  3. public static Map<String,String> map = new HashMap<>();
  4. public static List<String> logList = new ArrayList<>();
  5. public static Set<String> tokenSet = new HashSet<>();
  6. @RequestMapping(value = "login")
  7. @IgnoreToken
  8. @Log
  9. public String login(String userName,String password) {
  10. map.put(userName,password);
  11. //保存token
  12. tokenSet.add(userName+password);
  13. //返回token
  14. return userName+password;
  15. }
  16. @RequestMapping(value = "query")
  17. @Log(name = "获取密码")
  18. public String getPassword(String userName) {
  19. //获取用户密码
  20. return map.get(userName);
  21. }
  22. @RequestMapping(value = "logs")
  23. @Log(name = "获取日志信息")
  24. public String getLogMap() {
  25. return JSON.toJSONString(logList);
  26. }
  27. }

下面我在浏览器输入请求登录地址进行登录

  1. // 登录获取token
  2. http://127.0.0.1:8080/login?userName=admin&password=adminadmin

不带token访问受保护的方法

  1. // 获取用户密码
  2. http://127.0.0.1:8080/query?userName=admin

带正确的token访问受保护的犯法

  1. // 获取用户密码
  2. http://127.0.0.1:8080/query?userName=admin&token=adminadminadmin
  3. 12

获取用户访问日志信息

相关文章

最新文章

更多