自定义注解,实现记录接口的调用日志,此注解可以实现传递伪动态参数。
    一、需要引入的jar包:

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter-web</artifactId>
    5. </dependency>
    6. <dependency>
    7. <groupId>org.springframework.boot</groupId>
    8. <artifactId>spring-boot-starter-test</artifactId>
    9. </dependency>
    10. <!-- test -->
    11. <dependency>
    12. <groupId>org.springframework.boot</groupId>
    13. <artifactId>spring-boot-test</artifactId>
    14. </dependency>
    15. <dependency>
    16. <groupId>org.springframework.boot</groupId>
    17. <artifactId>spring-boot-starter-aop</artifactId>
    18. </dependency>
    19. <!-- json -->
    20. <dependency>
    21. <groupId>commons-lang</groupId>
    22. <artifactId>commons-lang</artifactId>
    23. <version>2.4</version>
    24. </dependency>
    25. <dependency>
    26. <groupId>org.apache.commons</groupId>
    27. <artifactId>commons-lang3</artifactId>
    28. </dependency>
    29. <dependency>
    30. <groupId>commons-beanutils</groupId>
    31. <artifactId>commons-beanutils</artifactId>
    32. <version>1.8.0</version>
    33. </dependency>
    34. <dependency>
    35. <groupId>commons-collections</groupId>
    36. <artifactId>commons-collections</artifactId>
    37. <version>3.2.1</version>
    38. </dependency>
    39. <dependency>
    40. <groupId>commons-logging</groupId>
    41. <artifactId>commons-logging</artifactId>
    42. <version>1.1.1</version>
    43. </dependency>
    44. <dependency>
    45. <groupId>net.sf.json-lib</groupId>
    46. <artifactId>json-lib</artifactId>
    47. <version>2.4</version>
    48. </dependency>
    49. </dependencies>

    二、自定义注解:

    1. package com.example.demo.annotation;
    2. import java.lang.annotation.*;
    3. @Target(ElementType.METHOD)
    4. @Retention(RetentionPolicy.RUNTIME)
    5. @Documented
    6. public @interface ApiOperationLog {
    7. String resourceId() default "";
    8. String operationType();
    9. String description() default "";
    10. }

    三、定义切面:

    1. package com.example.demo.aspect;
    2. import com.example.demo.annotation.ApiOperationLog;
    3. import net.sf.json.JSONObject;
    4. import org.aspectj.lang.JoinPoint;
    5. import org.aspectj.lang.annotation.AfterReturning;
    6. import org.aspectj.lang.annotation.Aspect;
    7. import org.aspectj.lang.annotation.Pointcut;
    8. import org.aspectj.lang.reflect.MethodSignature;
    9. import org.springframework.stereotype.Component;
    10. import java.util.HashMap;
    11. import java.util.Map;
    12. @Aspect
    13. @Component
    14. public class ApiOperationAspect {
    15. @Pointcut("@annotation ( com.example.demo.annotation.ApiOperationLog)")
    16. public void apiLog() {
    17. }
    18. @AfterReturning(pointcut = "apiLog()")
    19. public void recordLog(JoinPoint joinPoint) {
    20. MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    21. // 获取方法上的指定注解
    22. ApiOperationLog annotation = signature.getMethod().getAnnotation(ApiOperationLog.class);
    23. // 获取注解中的参数
    24. String resourceId = getAnnotationValue(joinPoint, annotation.resourceId());
    25. String operationType = getAnnotationValue(joinPoint, annotation.operationType());
    26. String description = getAnnotationValue(joinPoint, annotation.description());
    27. System.out.println("resourceId:" + resourceId);
    28. System.out.println("operationType:" + operationType);
    29. System.out.println("description:" + description);
    30. // 将注解中测参数值保存到数据库,实现记录接口调用日志的功能(以下内容省略...)
    31. }
    32. /**
    33. * 获取注解中传递的动态参数的参数值
    34. *
    35. * @param joinPoint
    36. * @param name
    37. * @return
    38. */
    39. public String getAnnotationValue(JoinPoint joinPoint, String name) {
    40. String paramName = name;
    41. // 获取方法中所有的参数
    42. Map<String, Object> params = getParams(joinPoint);
    43. // 参数是否是动态的:#{paramName}
    44. if (paramName.matches("^#\\{\\D*\\}")) {
    45. // 获取参数名
    46. paramName = paramName.replace("#{", "").replace("}", "");
    47. // 是否是复杂的参数类型:对象.参数名
    48. if (paramName.contains(".")) {
    49. String[] split = paramName.split("\\.");
    50. // 获取方法中对象的内容
    51. Object object = getValue(params, split[0]);
    52. // 转换为JsonObject
    53. JSONObject jsonObject = JSONObject.fromObject(object);
    54. // 获取值
    55. Object o = jsonObject.get(split[1]);
    56. return String.valueOf(o);
    57. }
    58. // 简单的动态参数直接返回
    59. return String.valueOf(getValue(params, paramName));
    60. }
    61. // 非动态参数直接返回
    62. return name;
    63. }
    64. /**
    65. * 根据参数名返回对应的值
    66. *
    67. * @param map
    68. * @param paramName
    69. * @return
    70. */
    71. public Object getValue(Map<String, Object> map, String paramName) {
    72. for (Map.Entry<String, Object> entry : map.entrySet()) {
    73. if (entry.getKey().equals(paramName)) {
    74. return entry.getValue();
    75. }
    76. }
    77. return null;
    78. }
    79. /**
    80. * 获取方法的参数名和值
    81. *
    82. * @param joinPoint
    83. * @return
    84. */
    85. public Map<String, Object> getParams(JoinPoint joinPoint) {
    86. Map<String, Object> params = new HashMap<>(8);
    87. Object[] args = joinPoint.getArgs();
    88. MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    89. String[] names = signature.getParameterNames();
    90. for (int i = 0; i < args.length; i++) {
    91. params.put(names[i], args[i]);
    92. }
    93. return params;
    94. }
    95. }

    四:测试前的准备内容:

    1. // 实体类
    2. package com.example.demo.model;
    3. public class User {
    4. private Long id;
    5. private String name;
    6. private int age;
    7. public Long getId() {
    8. return id;
    9. }
    10. public void setId(Long id) {
    11. this.id = id;
    12. }
    13. public String getName() {
    14. return name;
    15. }
    16. public void setName(String name) {
    17. this.name = name;
    18. }
    19. public int getAge() {
    20. return age;
    21. }
    22. public void setAge(int age) {
    23. this.age = age;
    24. }
    25. @Override
    26. public String toString() {
    27. return "User{" +
    28. "id=" + id +
    29. ", name='" + name + '\'' +
    30. ", age=" + age +
    31. '}';
    32. }
    33. }
    34. // controller层内容
    35. package com.example.demo.controller;
    36. import com.example.demo.annotation.ApiOperationLog;
    37. import com.example.demo.model.User;
    38. import org.springframework.web.bind.annotation.RestController;
    39. @RestController
    40. public class LoginController {
    41. @ApiOperationLog(resourceId = "#{user.id}",operationType = "SAVE",description = "测试注解传递复杂动态参数")
    42. public void saveUser(User user,String id){
    43. System.out.println("测试注解...");
    44. }
    45. @ApiOperationLog(resourceId = "#{id}",operationType = "UPDATE",description = "测试注解传递简单动态参数")
    46. public void updateUser(User user,String id){
    47. System.out.println("测试注解...");
    48. }
    49. }

    五、测试类:

    1. package com.example.demo.aspect;
    2. import com.example.demo.DemoApplication;
    3. import com.example.demo.controller.LoginController;
    4. import com.example.demo.model.User;
    5. import org.junit.Test;
    6. import org.junit.runner.RunWith;
    7. import org.springframework.beans.factory.annotation.Autowired;
    8. import org.springframework.boot.test.context.SpringBootTest;
    9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    10. @RunWith(SpringJUnit4ClassRunner.class)
    11. @SpringBootTest(classes = DemoApplication.class)
    12. public class ControllerTest {
    13. @Autowired
    14. private LoginController loginController;
    15. @Test
    16. public void test(){
    17. User user = new User();
    18. user.setId(1L);
    19. user.setName("test");
    20. user.setAge(20);
    21. loginController.saveUser(user,"123");
    22. loginController.updateUser(user,"666");
    23. }
    24. }

    测试结果:

    1. 测试注解...
    2. resourceId:1
    3. operationType:SAVE
    4. description:测试注解传递复杂动态参数
    5. 测试注解...
    6. resourceId:666
    7. operationType:UPDATE
    8. description:测试注解传递简单动态参数