map) {
190 | return "The password changed.";
191 | }
192 | ```
193 |
194 | 格式:`@ + Bean名称`,如果没有特殊处理使用注解注册到`IOC`容器内的Bean名称首字母都为小写,所以`@userService`就代表了`UserService`类的Bean实例。
195 |
196 | ## 表达式函数
197 |
198 | 表达式函数必须是`static`修饰的方法才可以定义,如果不是`static`在访问时会报错,主要是因为反射调用方法时如果不是静态的需要方法所属类的实例才可以。
199 |
200 | **配置ExpressionFunctionFactory类**
201 |
202 | ```java
203 | @Bean
204 | ExpressionFunctionFactory expressionFunctionFactory() {
205 | // 可以传递多个basePackage
206 | return new ExpressionFunctionFactory(Arrays.asList("org.minbox.framework.grace.sample","com.yuqiyu"));
207 | }
208 | ```
209 |
210 | > 通过配置实例化`ExpressionFunctionFactory`类来加载指定包名下配置`@GraceFunctionDefiner`注解的类。
211 |
212 | ```java
213 | @GraceFunctionDefiner
214 | public class StringUtils {
215 | @GraceFunction
216 | public static String reverseString(String input) {
217 | StringBuilder backwards = new StringBuilder();
218 | for (int i = 0; i < input.length(); i++) {
219 | backwards.append(input.charAt(input.length() - 1 - i));
220 | }
221 | return backwards.toString();
222 | }
223 | }
224 | ```
225 |
226 | `@GraceFunctionDefiner`注解只是起到了一个过滤表达式函数定义的作用,只要使用该注解的类才可以执行进一步解析表达式函数的逻辑。
227 |
228 | `@GraceFunction`注解则是标识方法为表达式函数,`ExpressionFunctionFactory`在实例化后会把表达式函数缓存到内存集合中,在解析操作日志的`SpEL`表达式时进行注册使用。
229 |
230 | ## 使用返回值
231 |
232 | 每次执行`@GraceRecorder`配置的方法时,AOP拦截器都会在目标方法执行完成后将结果添加到上下文的变量集合内,使用`result`作为Key,如果我们需要使用返回值的内容来格式化日志可以直接使用`#result`来访问数据。
233 |
234 | ```java
235 | @GraceRecorder(category = "User", success = "用户:{#userId} 查询到的昵称为:{#result}")
236 | public String getUserRealName(String userId) {
237 | return "恒宇少年";
238 | }
239 |
240 | @GraceRecorder(category = "User", success = "用户:{#userId},年龄:{#result.age}")
241 | public User getUserById(String userId) {
242 | return new User();
243 | }
244 |
245 | @Data
246 | public static class User {
247 | private String userId;
248 | private String userName;
249 | private int age;
250 | }
251 | ```
252 |
253 |
254 |
255 | ## 条件判断
256 |
257 | `@GraceRecorder`注解有个`condition`条件属性,支持`SpEL`表达式配置,如果配置了该属性,只有表达式解析结果为`true`时才会记录操作日志。
258 |
259 | ```java
260 | @GraceRecorder(category = "User", condition = "{#age>20 and #age<60}", success = "用户:{@userService.getUserRealName(#userId)},年龄超过{#age}")
261 | public void updateAgeById(String userId, int age) {
262 | System.out.println(age);
263 | }
264 | ```
265 |
--------------------------------------------------------------------------------
/grace-bom/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | grace
7 | org.minbox.framework
8 | ${revision}
9 |
10 | 4.0.0
11 | pom
12 | grace-bom
13 |
14 |
15 | 11
16 | 11
17 |
18 |
19 |
20 |
21 | org.minbox.framework
22 | grace-core
23 | ${revision}
24 |
25 |
26 | org.minbox.framework
27 | grace-expression
28 | ${revision}
29 |
30 |
31 | org.minbox.framework
32 | grace-processor
33 | ${revision}
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/grace-core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | grace-bom
7 | org.minbox.framework
8 | ${revision}
9 | ../grace-bom
10 |
11 | 4.0.0
12 | jar
13 | grace-core
14 |
15 |
16 | 11
17 | 11
18 |
19 |
20 |
21 | org.springframework
22 | spring-aop
23 | true
24 |
25 |
26 | org.springframework
27 | spring-context
28 | true
29 |
30 |
31 | org.springframework
32 | spring-beans
33 | true
34 |
35 |
36 | org.minbox.framework
37 | grace-expression
38 | true
39 |
40 |
41 | org.minbox.framework
42 | grace-processor
43 | true
44 |
45 |
46 | org.minbox.framework
47 | minbox-core
48 |
49 |
50 |
--------------------------------------------------------------------------------
/grace-core/src/main/java/org/minbox/framework/grace/core/GraceConstants.java:
--------------------------------------------------------------------------------
1 | package org.minbox.framework.grace.core;
2 |
3 | import org.minbox.framework.grace.expression.ExpressionVariables;
4 |
5 | /**
6 | * 常量定义
7 | *
8 | * @author 恒宇少年
9 | */
10 | public interface GraceConstants {
11 | /**
12 | * 定义方法返回值在{@link ExpressionVariables}变量集合内的Key
13 | */
14 | String RESULT_VARIABLE_KEY = "result";
15 | /**
16 | * 定义方法参数索引值在{@link ExpressionVariables}变量集合的格式
17 | */
18 | String PARAMETER_INDEX_VALUE_FORMAT = "p%d";
19 | /**
20 | * 日志产生位置的格式
21 | */
22 | String LOG_GENERATED_LOCATION_FORMAT = "%s#%s";
23 | }
24 |
--------------------------------------------------------------------------------
/grace-core/src/main/java/org/minbox/framework/grace/core/GraceRecorderAnnotationDataExtractor.java:
--------------------------------------------------------------------------------
1 | package org.minbox.framework.grace.core;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.Getter;
5 | import lombok.NoArgsConstructor;
6 | import org.aopalliance.intercept.MethodInvocation;
7 | import org.minbox.framework.grace.core.operator.GraceLoadOperatorService;
8 | import org.minbox.framework.grace.expression.annotation.GraceRecorder;
9 | import org.springframework.aop.support.AopUtils;
10 | import org.springframework.core.annotation.AnnotationUtils;
11 | import org.springframework.util.Assert;
12 | import org.springframework.util.ClassUtils;
13 | import org.springframework.util.ObjectUtils;
14 |
15 | import java.lang.reflect.Method;
16 | import java.lang.reflect.Parameter;
17 | import java.util.HashMap;
18 | import java.util.Map;
19 |
20 | import static org.minbox.framework.grace.core.GraceConstants.LOG_GENERATED_LOCATION_FORMAT;
21 |
22 | /**
23 | * {@link GraceRecorder}注解数据提取者
24 | *
25 | * 跟方法切面对象实例{@link MethodInvocation}进行提取解析日志内容所需要的数据
26 | *
27 | * @author 恒宇少年
28 | */
29 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
30 | public class GraceRecorderAnnotationDataExtractor {
31 | private GraceRecorder graceRecorder;
32 | @Getter
33 | private Class> targetClass;
34 | @Getter
35 | private Method specificMethod;
36 | private Object[] arguments;
37 | private Parameter[] parameters;
38 | private GraceLoadOperatorService operatorService;
39 |
40 | public GraceRecorderAnnotationDataExtractor(MethodInvocation invocation, GraceLoadOperatorService operatorService) {
41 | this.graceRecorder = AnnotationUtils.getAnnotation(invocation.getMethod(), GraceRecorder.class);
42 | Assert.notNull(graceRecorder, "@GraceRecorder注解实例不可以为空.");
43 | this.targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
44 | Assert.notNull(targetClass, "无法获取切面方法所属目标类.");
45 | this.specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
46 | this.arguments = invocation.getArguments();
47 | this.parameters = this.specificMethod.getParameters();
48 | this.operatorService = operatorService;
49 | }
50 |
51 | /**
52 | * 获取参数定义以及值的映射集合
53 | * 通过{@link MethodInvocation}实例获取切面目标的{@link Method},通过{@link MethodInvocation#getArguments()}获取各个参数的传递值
54 | *
55 | * @return 定义参数的值集合
56 | */
57 | public Map getParameterValues() {
58 | Map parameterValues = new HashMap<>();
59 | if (ObjectUtils.isEmpty(parameters)) {
60 | return parameterValues;
61 | }
62 | for (int i = 0; i < parameters.length; i++) {
63 | Parameter parameter = parameters[i];
64 | Object value = this.arguments[i];
65 | parameterValues.put(parameter.getName(), value);
66 | String parameterIndexName = String.format(GraceConstants.PARAMETER_INDEX_VALUE_FORMAT, i);
67 | parameterValues.put(parameterIndexName, value);
68 | }
69 | return parameterValues;
70 | }
71 |
72 | public String getSuccessTemplate() {
73 | return this.graceRecorder.success();
74 | }
75 |
76 | public String getFailText() {
77 | return this.graceRecorder.fail();
78 | }
79 |
80 | public String getBizNo() {
81 | return this.graceRecorder.bizNo();
82 | }
83 |
84 | public String getCategory() {
85 | return this.graceRecorder.category();
86 | }
87 |
88 | public String[] getTags() {
89 | return this.graceRecorder.tags();
90 | }
91 |
92 | public String getConditionExpression() {
93 | return this.graceRecorder.condition();
94 | }
95 |
96 | public String getOperator() {
97 | String operator = this.graceRecorder.operator();
98 | return ObjectUtils.isEmpty(operator) && !ObjectUtils.isEmpty(operatorService) ? operatorService.getOperatorName()
99 | : operator;
100 | }
101 |
102 | public String getOperatorId() {
103 | return !ObjectUtils.isEmpty(this.operatorService) ? this.operatorService.getOperatorId() : null;
104 | }
105 |
106 | public String getGeneratedLocation() {
107 | return String.format(LOG_GENERATED_LOCATION_FORMAT, targetClass.getName(), specificMethod.getName());
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/grace-core/src/main/java/org/minbox/framework/grace/core/GraceRecorderAopBeanDefinitionRegistrar.java:
--------------------------------------------------------------------------------
1 | package org.minbox.framework.grace.core;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.minbox.framework.util.BeanUtils;
5 | import org.springframework.beans.factory.support.BeanDefinitionRegistry;
6 | import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
7 | import org.springframework.core.type.AnnotationMetadata;
8 |
9 | /**
10 | * 配置日志采集注解切面相关类
11 | *
12 | * @author 恒宇少年
13 | * @see GraceRecorderMethodInterceptor
14 | * @see GraceRecorderPointcutAdvisor
15 | */
16 | @Slf4j
17 | public class GraceRecorderAopBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
18 | @Override
19 | public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
20 | BeanUtils.registerInfrastructureBeanIfAbsent(registry, GraceRecorderMethodInterceptor.BEAN_NAME, GraceRecorderMethodInterceptor.class);
21 | BeanUtils.registerInfrastructureBeanIfAbsent(registry, GraceRecorderPointcutAdvisor.BEAN_NAME, GraceRecorderPointcutAdvisor.class);
22 | log.info("The registration of the Grace log aspect class is completed.");
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/grace-core/src/main/java/org/minbox/framework/grace/core/GraceRecorderMethodInterceptor.java:
--------------------------------------------------------------------------------
1 | package org.minbox.framework.grace.core;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.aopalliance.intercept.MethodInterceptor;
5 | import org.aopalliance.intercept.MethodInvocation;
6 | import org.minbox.framework.grace.core.operator.GraceLoadOperatorService;
7 | import org.minbox.framework.grace.core.resolve.GraceRecorderResolveProcessor;
8 | import org.minbox.framework.grace.expression.ExpressionVariables;
9 | import org.minbox.framework.grace.expression.GraceCachedExpressionEvaluator;
10 | import org.minbox.framework.grace.expression.GraceEvaluationContext;
11 | import org.minbox.framework.grace.expression.GraceRecordContext;
12 | import org.minbox.framework.grace.expression.annotation.GraceRecorder;
13 | import org.minbox.framework.grace.processor.GraceLogObject;
14 | import org.minbox.framework.grace.processor.GraceLogStorageProcessor;
15 | import org.minbox.framework.util.StackTraceUtil;
16 | import org.springframework.beans.BeansException;
17 | import org.springframework.beans.factory.BeanFactory;
18 | import org.springframework.beans.factory.BeanFactoryAware;
19 | import org.springframework.beans.factory.ObjectProvider;
20 | import org.springframework.context.expression.AnnotatedElementKey;
21 | import org.springframework.context.expression.BeanFactoryResolver;
22 | import org.springframework.util.Assert;
23 | import org.springframework.util.ObjectUtils;
24 |
25 | import java.util.Map;
26 |
27 | /**
28 | * 日志记录者切点后的方法拦截器
29 | *
30 | * 执行目标方法之前构建表达式函数、变量到上下文{@link GraceEvaluationContext}实例中
31 | * 执行目标方法后进行格式化日志模板内容并处理日志数据
32 | *
33 | * @author 恒宇少年
34 | */
35 | @Slf4j
36 | public class GraceRecorderMethodInterceptor implements MethodInterceptor, BeanFactoryAware {
37 | /**
38 | * The bean name of the current class registered in the ioc
39 | */
40 | public static final String BEAN_NAME = GraceRecorderMethodInterceptor.class.getSimpleName();
41 | private BeanFactoryResolver beanFactoryResolver;
42 | private GraceLogStorageProcessor storageProcessor;
43 | private GraceLoadOperatorService operatorService;
44 |
45 | public GraceRecorderMethodInterceptor(ObjectProvider storageProcessorProvider,
46 | ObjectProvider operatorServiceProvider) {
47 | this.storageProcessor = storageProcessorProvider.getIfAvailable();
48 | this.operatorService = operatorServiceProvider.getIfAvailable();
49 | Assert.notNull(storageProcessor, "无法注入GraceLogStorageProcessor接口实现类实例,请实现该接口.");
50 | }
51 |
52 | /**
53 | * 执行目标方法切面业务逻辑处理
54 | *
55 | * 切面前:
56 | * 提取{@link GraceRecorder#bizNo()}、{@link GraceRecorder#category()}、{@link GraceRecorder#operator()}数据
57 | * 提取参数列表并写入到{@link ExpressionVariables}变量集合内
58 | * 目标方法执行:
59 | * 将目标方法返回结果使用"result"变量名写入到{@link ExpressionVariables}
60 | * 切面后:
61 | * 提取{@link GraceRecorder#condition()}配置的条件表达,如果该条件的表达式解析结果为"true"则执行操作日志的解析处理
62 | * 提取{@link GraceRecorder#success()}并解析执行成功后的日志模板
63 | * 如果执行出现异常则提取{@link GraceRecorder#fail()}的文本内容
64 | * 封装{@link GraceLogObject}并交付给"processor"进行后续的数据持久化处理
65 | *
66 | * @param invocation 目标方法
67 | * @return 目标方法执行返回的数据结果
68 | * @throws Throwable
69 | */
70 | @Override
71 | public Object invoke(MethodInvocation invocation) throws Throwable {
72 | GraceRecorderAnnotationDataExtractor extractor = null;
73 | Map customizeVariables = null;
74 | Object result;
75 | boolean executionSucceed = true;
76 | String exceptionStackTrace = null;
77 | try {
78 | extractor = new GraceRecorderAnnotationDataExtractor(invocation, this.operatorService);
79 | Map parameterValueMap = extractor.getParameterValues();
80 | ExpressionVariables variables = ExpressionVariables.initialize();
81 | if (!ObjectUtils.isEmpty(operatorService) && !ObjectUtils.isEmpty(operatorService.getExtra())) {
82 | variables.addVariables(operatorService.getExtra());
83 | }
84 | variables.addVariables(parameterValueMap);
85 | GraceRecordContext.pushExpressionVariables(variables);
86 | result = invocation.proceed();
87 | customizeVariables = GraceVariableContext.getCustomizeVariables();
88 | if (!ObjectUtils.isEmpty(customizeVariables)) {
89 | variables.addVariables(customizeVariables);
90 | }
91 | variables.addVariable(GraceConstants.RESULT_VARIABLE_KEY, result);
92 | } catch (Exception e) {
93 | executionSucceed = false;
94 | exceptionStackTrace = StackTraceUtil.getStackTrace(e);
95 | throw e;
96 | } finally {
97 | // 如果执行目标方法遇到了异常,把遇到异常之前设置的自定义变量读取出来
98 | customizeVariables = ObjectUtils.isEmpty(customizeVariables) ? GraceVariableContext.getCustomizeVariables() : customizeVariables;
99 | GraceVariableContext.remove();
100 | GraceCachedExpressionEvaluator evaluator = new GraceCachedExpressionEvaluator();
101 | ExpressionVariables variables = GraceRecordContext.popExpressionVariables();
102 | GraceEvaluationContext evaluationContext = evaluator.createEvaluationContext(variables);
103 | evaluationContext.setBeanResolver(this.beanFactoryResolver);
104 | AnnotatedElementKey elementKey = new AnnotatedElementKey(extractor.getSpecificMethod(), extractor.getTargetClass());
105 | boolean conditionExecute = true;
106 | if (!ObjectUtils.isEmpty(extractor.getConditionExpression().trim())) {
107 | conditionExecute = evaluator.parseExpression(Boolean.class, extractor.getConditionExpression(), elementKey, evaluationContext);
108 | }
109 | if (conditionExecute) {
110 | GraceRecorderResolveProcessor resolveProcessor =
111 | new GraceRecorderResolveProcessor(extractor, evaluator, evaluationContext, elementKey, executionSucceed, customizeVariables);
112 | GraceLogObject graceLogObject = resolveProcessor.processing();
113 | graceLogObject.setExceptionStackTrace(exceptionStackTrace);
114 | storageProcessor.storage(graceLogObject);
115 | }
116 | }
117 | return result;
118 | }
119 |
120 | @Override
121 | public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
122 | if (!ObjectUtils.isEmpty(beanFactory) && ObjectUtils.isEmpty(this.beanFactoryResolver)) {
123 | this.beanFactoryResolver = new BeanFactoryResolver(beanFactory);
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/grace-core/src/main/java/org/minbox/framework/grace/core/GraceRecorderPointcutAdvisor.java:
--------------------------------------------------------------------------------
1 | package org.minbox.framework.grace.core;
2 |
3 | import org.aopalliance.aop.Advice;
4 | import org.minbox.framework.grace.expression.annotation.GraceRecorder;
5 | import org.springframework.aop.Pointcut;
6 | import org.springframework.aop.support.AbstractPointcutAdvisor;
7 | import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
8 | import org.springframework.beans.BeansException;
9 | import org.springframework.beans.factory.BeanFactory;
10 | import org.springframework.beans.factory.BeanFactoryAware;
11 | import org.springframework.core.Ordered;
12 | import org.springframework.util.Assert;
13 |
14 | /**
15 | * 配置日志采集者支持{@link GraceRecorder}注解的AOP切入点
16 | *
17 | * @author 恒宇少年
18 | */
19 | public class GraceRecorderPointcutAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
20 | /**
21 | * The bean name of the current class registered in the ioc
22 | */
23 | public static final String BEAN_NAME = GraceRecorderPointcutAdvisor.class.getSimpleName();
24 | private BeanFactory beanFactory;
25 | private Advice advice;
26 | private Pointcut pointcut;
27 |
28 | /**
29 | * 构建删除初始化全局数据
30 | *
31 | * {@link Pointcut}注解切入点配置使用类上方法的注解,只生效方法注解
32 | *
33 | * @param methodInterceptor {@link GraceRecorder}注解方法拦截器
34 | */
35 | public GraceRecorderPointcutAdvisor(GraceRecorderMethodInterceptor methodInterceptor) {
36 | this.pointcut = AnnotationMatchingPointcut.forMethodAnnotation(GraceRecorder.class);
37 | this.advice = methodInterceptor;
38 | if (this.advice instanceof BeanFactoryAware) {
39 | ((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);
40 | }
41 | }
42 |
43 | @Override
44 | public Pointcut getPointcut() {
45 | return this.pointcut;
46 | }
47 |
48 | @Override
49 | public Advice getAdvice() {
50 | Assert.notNull(this.advice, "The implementation instance of advice cannot be empty.");
51 | return this.advice;
52 | }
53 |
54 | @Override
55 | public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
56 | this.beanFactory = beanFactory;
57 | }
58 |
59 | @Override
60 | public int getOrder() {
61 | return Ordered.LOWEST_PRECEDENCE;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/grace-core/src/main/java/org/minbox/framework/grace/core/GraceVariableContext.java:
--------------------------------------------------------------------------------
1 | package org.minbox.framework.grace.core;
2 |
3 | import org.springframework.util.CollectionUtils;
4 | import org.springframework.util.ObjectUtils;
5 |
6 | import java.util.Map;
7 |
8 | /**
9 | * 操作日志变量上下文
10 | *
11 | * @author 恒宇少年
12 | */
13 | public class GraceVariableContext {
14 | private static final ThreadLocal