自定义注解,实现记录接口的调用日志,此注解可以实现传递伪动态参数。
一、需要引入的jar包:
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><!-- test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-test</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!-- json --><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.4</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.8.0</version></dependency><dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.2.1</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.1</version></dependency><dependency><groupId>net.sf.json-lib</groupId><artifactId>json-lib</artifactId><version>2.4</version></dependency></dependencies>
二、自定义注解:
package com.example.demo.annotation;import java.lang.annotation.*;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface ApiOperationLog {String resourceId() default "";String operationType();String description() default "";}
三、定义切面:
package com.example.demo.aspect;import com.example.demo.annotation.ApiOperationLog;import net.sf.json.JSONObject;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.stereotype.Component;import java.util.HashMap;import java.util.Map;@Aspect@Componentpublic class ApiOperationAspect {@Pointcut("@annotation ( com.example.demo.annotation.ApiOperationLog)")public void apiLog() {}@AfterReturning(pointcut = "apiLog()")public void recordLog(JoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();// 获取方法上的指定注解ApiOperationLog annotation = signature.getMethod().getAnnotation(ApiOperationLog.class);// 获取注解中的参数String resourceId = getAnnotationValue(joinPoint, annotation.resourceId());String operationType = getAnnotationValue(joinPoint, annotation.operationType());String description = getAnnotationValue(joinPoint, annotation.description());System.out.println("resourceId:" + resourceId);System.out.println("operationType:" + operationType);System.out.println("description:" + description);// 将注解中测参数值保存到数据库,实现记录接口调用日志的功能(以下内容省略...)}/*** 获取注解中传递的动态参数的参数值** @param joinPoint* @param name* @return*/public String getAnnotationValue(JoinPoint joinPoint, String name) {String paramName = name;// 获取方法中所有的参数Map<String, Object> params = getParams(joinPoint);// 参数是否是动态的:#{paramName}if (paramName.matches("^#\\{\\D*\\}")) {// 获取参数名paramName = paramName.replace("#{", "").replace("}", "");// 是否是复杂的参数类型:对象.参数名if (paramName.contains(".")) {String[] split = paramName.split("\\.");// 获取方法中对象的内容Object object = getValue(params, split[0]);// 转换为JsonObjectJSONObject jsonObject = JSONObject.fromObject(object);// 获取值Object o = jsonObject.get(split[1]);return String.valueOf(o);}// 简单的动态参数直接返回return String.valueOf(getValue(params, paramName));}// 非动态参数直接返回return name;}/*** 根据参数名返回对应的值** @param map* @param paramName* @return*/public Object getValue(Map<String, Object> map, String paramName) {for (Map.Entry<String, Object> entry : map.entrySet()) {if (entry.getKey().equals(paramName)) {return entry.getValue();}}return null;}/*** 获取方法的参数名和值** @param joinPoint* @return*/public Map<String, Object> getParams(JoinPoint joinPoint) {Map<String, Object> params = new HashMap<>(8);Object[] args = joinPoint.getArgs();MethodSignature signature = (MethodSignature) joinPoint.getSignature();String[] names = signature.getParameterNames();for (int i = 0; i < args.length; i++) {params.put(names[i], args[i]);}return params;}}
四:测试前的准备内容:
// 实体类package com.example.demo.model;public class User {private Long id;private String name;private int age;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';}}// controller层内容package com.example.demo.controller;import com.example.demo.annotation.ApiOperationLog;import com.example.demo.model.User;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class LoginController {@ApiOperationLog(resourceId = "#{user.id}",operationType = "SAVE",description = "测试注解传递复杂动态参数")public void saveUser(User user,String id){System.out.println("测试注解...");}@ApiOperationLog(resourceId = "#{id}",operationType = "UPDATE",description = "测试注解传递简单动态参数")public void updateUser(User user,String id){System.out.println("测试注解...");}}
五、测试类:
package com.example.demo.aspect;import com.example.demo.DemoApplication;import com.example.demo.controller.LoginController;import com.example.demo.model.User;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTest(classes = DemoApplication.class)public class ControllerTest {@Autowiredprivate LoginController loginController;@Testpublic void test(){User user = new User();user.setId(1L);user.setName("test");user.setAge(20);loginController.saveUser(user,"123");loginController.updateUser(user,"666");}}
测试结果:
测试注解...resourceId:1operationType:SAVEdescription:测试注解传递复杂动态参数测试注解...resourceId:666operationType:UPDATEdescription:测试注解传递简单动态参数
