Two points of attention in the realization of log output of SpringBoot basic series AOP combined with SpEL

Two points of attention in the realization of log output of SpringBoot basic series AOP combined with SpEL

[SpringBoot basic series] AOP combined with SpEL to realize log output notes one or two

Everyone is familiar with using AOP to print logs. Recently, in the process of using it, several interesting problems have been discovered. One is the analysis of SpEL, and the other is the output of parameters in JSON format.

I. Project environment

1. Project dependencies

This project uses

SpringBoot 2.2.1.RELEASE
+
maven 3.5.3
+
IDEA
Develop

Open a web service for testing

< Dependencies > < dependency > < the groupId > org.springframework.boot </the groupId > < the artifactId > Spring-Boot-Starter-Web </the artifactId > </dependency > </Dependencies > copy the code

II. AOP & SpEL

The knowledge points of AOP and SpEL have been specifically introduced before, here is an aggregation, a very simple log output section, add a comment on the method of printing the log

@Log
, Define a
key
, As a mark of log output; key supports SpEL expression

1. AOP aspect

Annotation definition

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { String key () ; } Copy code

Aspect logic

@Slf4j @Aspect @Component public class AopAspect implements ApplicationContextAware { private ExpressionParser parser = new SpelExpressionParser(); private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); @Around("@annotation(logAno)") public Object around (ProceedingJoinPoint joinPoint, Log logAno) throws Throwable { long start = System.currentTimeMillis(); String key = loadKey(logAno.key(), joinPoint); try { return joinPoint.proceed(); } finally { log.info( "key: {}, args: {}, cost: {}" , key, JSONObject.toJSONString(joinPoint.getArgs()), System.currentTimeMillis()-start); } } private String loadKey (String key, ProceedingJoinPoint joinPoint) { if (key == null ) { return key; } StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver( new BeanFactoryResolver(applicationContext)); String[] params = parameterNameDiscoverer.getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod()); Object[] args = joinPoint.getArgs(); for ( int i = 0 ; i <args.length; i++) { context.setVariable(params[i], args[i]); } return parser.parseExpression(key).getValue(context, String.class); } private ApplicationContext applicationContext; @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { this .applicationContext = applicationContext; } } Copy code

The above logic is relatively simple, and it is not much different from the familiar posture.

2. StandardEvaluationContext security issues

on

StandardEvaluationContext
If you are interested, you can check related articles; for those with higher safety verification, you can only use
SimpleEvaluationContext
, If you use it, the capabilities of SpEL are limited

Such as adding a test

@Data @Accessors(chain = true) public class DemoDo { private String name; private Integer age; } Copy code

Service class

@Service public class HelloService { @Log(key = "#demo.getName()") public String say (DemoDo demo, String prefix) { return prefix + ":" + demo; } } Copy code

In order to verify

SimpleEvaluationContext
, Let s modify the above
loadKeys
method

private String loadKey (String key, ProceedingJoinPoint joinPoint) { if (key == null ) { return key; } SimpleEvaluationContext context = new SimpleEvaluationContext.Builder().build(); String[] params = parameterNameDiscoverer.getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod()); Object[] args = joinPoint.getArgs(); for ( int i = 0 ; i <args.length; i++) { context.setVariable(params[i], args[i]); } return parser.parseExpression(key).getValue(context, String.class); } Copy code

Start the test

@SpringBootApplication public class Application { public Application (HelloService helloService) { helloService.say( new DemoDo().setName( " blog" ).setAge( 18 ), "welcome" ); } public static void main (String[] args) { SpringApplication.run(Application.class); } } Copy code

Direct prompt method can not be found! ! !

3. gson serialization problem

In the above case, FastJson is used to serialize the passed parameters, and then we use Gson to serialize

< Dependency > < the groupId > com.google.code.gson </the groupId > < the artifactId > GSON </the artifactId > </dependency > copy the code

Then add a special method

@Service public class HelloService { /** * Literal, pay attention to wrap it in single quotes * @param key * @return */ @Log(key = "'yihuihuiblog'") public String hello (String key, HelloService helloService) { return key + "_" + helloService.say( new DemoDo().setName(key).setAge( 10 ), "prefix" ); } } Copy code

Pay attention to the second parameter of the above method. What is very interesting is that the passed parameter is its own instance; execute again

public Application (HelloService helloService) { helloService.say( new DemoDo().setName( " blog" ).setAge( 18 ), "welcome" ); String ans = helloService.hello( "One gray gray" , helloService); System.out.println(ans); } Copy code

Throw an exception directly

This is very embarrassing, an auxiliary tool for outputting logs, because serialization directly causes the interface to be unavailable, which is not elegant; and as the aspect of log output, we have no way to control this parameter transfer, and no way to require it to be used. The parameters must be serialized, and extra attention is needed here (a better way is to implement toString for simple objects, and then output the result of toString; not json string)

4. Summary

Although the contents of the above list, in summary, there are only two points

  • If SpEL uses
    SimpleEvaluationContext
    , Then note that the function of spel is weakened, and some features are not supported
  • If the method parameter json serialization output, then you need to pay attention to some classes in the serialization process, may throw exceptions

(Seeing the friends here, you may wish to like it, and follow the WeChat public account "Yihuihuiblog", my public account is already lonely long )

III. Source code and related knowledge points not to be missed

0. Project

AOP series of blog posts

1. A Grey Blog

It s not as good as a letter. The above content is purely a family statement. Due to limited personal ability, it is inevitable that there will be omissions and errors. If you find a bug or have better suggestions, you are welcome to criticize and correct. I am grateful.

The following is a gray personal blog, which records all the blog posts in study and work. Welcome everyone to visit