0%

AopUtils#canApply 方法匹配分析

问题描述

既然ReflectionUtils.getAllDeclaredMethods(Class<?> clazz)可以获取父类和接口继承的方法,是否有必要先调用ClassUtils.getAllInterfacesForClassAsSet(Class<?> clazz)可以获取定类(clazz)及其所有父类(包括超类)实现的所有接口,再次遍历接口获取方法?

源码分析

Advisor 排序规则

  1. Aspect Bean 顺序,根据 Bean 的 Order 注解或其它 Order 标识排序。
  2. Aspect 注解顺序 Around.class,Before.class,After.class,AfterReturning.class,AfterThrowing.class。
  3. 方法名称。

注意事项

Spring AOP Advice 场景

  1. 环绕通知(Interception Around Advice)
    1. public interface MethodInterceptor extends Interceptor
  2. 前置通知(Before Advice)
    1. public interface MethodBeforeAdvice extends BeforeAdvice
  3. 后置通知(After Advice)
    1. public interface AfterAdvice extends Advice
  4. 返回通知(After Returning Advice)
    1. public interface AfterReturningAdvice extends AfterAdvice
  5. 异常通知(Throws Advice)
    1. public interface ThrowsAdvice extends AfterAdvice
  6. 引入通知(Introduction Advice)
    1. public class DelegatingIntroductionInterceptor extends IntroductionInfoSupport implements IntroductionInterceptor
    2. public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice
    3. 引入通知的主要作用是可以让生成的代理类实现额外的接口。

Spring AOP 使用关键接口

  • PointcutAdvisor = Advice + Pointcut
  • Advice: AspectJAroundAdvice/AspectJMethodBeforeAdvice/AspectJAfterAdvice/AspectJAfterReturningAdvice/AspectJAfterThrowingAdvice
  • Pointcut: AspectJExpressionPointcut
  • PointcutAdvisor: InstantiationModelAwarePointcutAdvisorImpl instanceof PointcutAdvisor
  • Advisor refers to PointcutAdvisor

Advisor

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// org.springframework.aop.Advisor
public interface Advisor {
	Advice getAdvice();
	boolean isPerInstance();
}

// org.springframework.aop.PointcutAdvisor
public interface PointcutAdvisor extends Advisor {
	Pointcut getPointcut();
}

// org.springframework.aop.aspectj.InstantiationModelAwarePointcutAdvisor
public interface InstantiationModelAwarePointcutAdvisor extends PointcutAdvisor {}

// org.springframework.aop.IntroductionAdvisor
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
	ClassFilter getClassFilter();
	void validateInterfaces() throws IllegalArgumentException;    
}

// org.springframework.aop.IntroductionInfo
public interface IntroductionInfo {
	Class<?>[] getInterfaces();
}

Advice

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// org.aopalliance.aop.Advice
public interface Advice {}

// org.springframework.aop.BeforeAdvice
public interface BeforeAdvice extends Advice {}

// org.springframework.aop.MethodBeforeAdvice
public interface MethodBeforeAdvice extends BeforeAdvice {}

// org.springframework.aop.AfterAdvice
public interface AfterAdvice extends Advice {}

// org.springframework.aop.AfterReturningAdvice
public interface AfterReturningAdvice extends AfterAdvice {}

// org.springframework.aop.ThrowsAdvice
public interface ThrowsAdvice extends AfterAdvice {}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// org.aopalliance.intercept.Interceptor
public interface Interceptor extends Advice {}

// org.aopalliance.intercept.MethodInterceptor
public interface MethodInterceptor extends Interceptor {}

// org.springframework.aop.DynamicIntroductionAdvice
public interface DynamicIntroductionAdvice extends Advice {}

// org.springframework.aop.IntroductionInterceptor
public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice {}

// org.aopalliance.intercept.ConstructorInterceptor
public interface ConstructorInterceptor extends Interceptor {}

Pointcut

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// org.springframework.aop.Pointcut
public interface Pointcut {
	ClassFilter getClassFilter();
	MethodMatcher getMethodMatcher();
}

// org.springframework.aop.ClassFilter
public interface ClassFilter {
	boolean matches(Class<?> clazz);
}

// org.springframework.aop.MethodMatcher
public interface MethodMatcher {
	/**
	 * Perform static checking whether the given method matches.
	 * <p>If this returns {@code false} or if the {@link #isRuntime()}
	 * method returns {@code false}, no runtime check (i.e. no
	 * {@link #matches(java.lang.reflect.Method, Class, Object[])} call)
	 * will be made.
	 */    
	boolean matches(Method method, Class<?> targetClass);
	/**
	 * Is this MethodMatcher dynamic, that is, must a final call be made on the
	 * {@link #matches(java.lang.reflect.Method, Class, Object[])} method at
	 * runtime even if the 2-arg matches method returns {@code true}?
	 * <p>Can be invoked when an AOP proxy is created, and need not be invoked
	 * again before each method invocation,
	 * @return whether a runtime match via the 3-arg
	 * {@link #matches(java.lang.reflect.Method, Class, Object[])} method
	 * is required if static matching passed
	 */    
	boolean isRuntime();
	/**
	 * Check whether there a runtime (dynamic) match for this method,
	 * which must have matched statically.
	 * <p>This method is invoked only if the 2-arg matches method returns
	 * {@code true} for the given method and target class, and if the
	 * {@link #isRuntime()} method returns {@code true}. Invoked
	 * immediately before potential running of the advice, after any
	 * advice earlier in the advice chain has run.
     */
	boolean matches(Method method, Class<?> targetClass, Object... args);
}

END

背景

HTTP头部默认只支持ISO-8859-1字符集,直接使用中文会导致乱码或解析错误。

文件下载响应体方案

1
2
3
4
5
6
7
8
9
String fileName =  fileNameStrMayBeZhCn + ".xlsx";
fileName = URLEncoder.encode(fileName, "UTF-8").replace("+", "%20");;
byte[] bytes = myservice.exportExcelFile();
return ResponseEntity.status(HttpStatus.OK)
        .contentType(MediaType.APPLICATION_OCTET_STREAM)
        .header(HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"" + fileName + "\";filename*=utf-8''"+fileName)
        .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(bytes.length))
        .body(bytes);

描述

结论

存储过程(Stored Procedure)与存储函数(Stored Function)在事务控制方面有较大差异。

对于存储过程,如果自动提交是开启的,并且你没有在存储过程中显式地控制事务,那么每条独立的 SQL 语句执行完毕后都会立即提交。

自动配置

概述

@EnableTransactionManagement 开启自动事务

org.springframework.transaction.annotation.EnableTransactionManagement

无论是默认自动配置事务,还是手动添加注解开启事务,都会使用 @EnableTransactionManagement注解。

默认自动配置

配置文件 org.springframework.boot.autoconfigure.AutoConfiguration.importsspring-autoconfigure-metadata.properties

Chrome 开发者控制台的 Console 页使用 fetch 命令调试接口技巧

在 Chrome 开发者控制台的 Network 页可以获取请求的 fetch 格式。

“Copy”–>“Copy as fetch”

注意事项

  1. Chrome 开发者控制台的 Console 不展示 log 级别的日志,原因未知。因此使用 error 级别的日志展示信息。
  2. 下载文件时,建议使用准确的文件扩展名。例如 Excel 格式的文件的文件名称应当使用文件扩展名 .xlsx 或 .xls,Tar.gz 格式的文件的文件名称应当使用文件扩展名 .tar.gz。
  3. 上传文件时,应当注意,需要删除Content-Type属性,完全由 "body": formData自动指定。

普通GET、PUT,文件上传、文件下载接口都可以使用。

前置知识

Java JDK 动态代理使用方法,Java CGLIB 动态代理动态

Spring 代理与 Advisor

Spring JDK 动态代理

JdkDynamicAopProxy 是 InvocationHandler 的实现类,其 invoke 方法从 Spring 容器中获取所有匹配的 Spring AOP advisor,将其转换为 Interceptor。

Concept

DFS ~ Recursive

BFS ~ Iterative

遍历方式

  • 深度优先遍历(DFS)
    • 前序遍历(Pre-Order Traversal)
    • 中序遍历(In-Order Traversal)
    • 后序遍历(Post-Order Traversal)
  • 广度优先遍历(BFS)
    • 层次遍历(Level-Order Traversal)
    • 结果唯一

代码书写方式

  • Recursion
    • Levelorder
    • Preorder
    • Inorder
    • Postorder
  • Iteration
    • Levelorder
    • Preorder
    • Inorder
    • Postorder

RecursionTraversalTemplate

递归遍历模板

SELECT语句执行顺序

  1. WITH (CTE, COMMON TABLE EXPRESSIONS)
  2. FROM
  3. ON
  4. JOIN
  5. WHERE
  6. GROUP BY
  7. HAVING
  8. SELECT, WINDOW FUNCTION
  9. SELECT DISTINCT
  10. UNION, INTERSECT, EXCEPT
  11. ORDER BY
  12. LIMIT, OFFSET
  13. FOR UPDATE, FOR NO KEY UPDATE, FOR SHARE, FOR KEY SHARE

Window functions are permitted only in the select list and ORDER BY clause. Query result rows are determined from the FROM clause, after WHERE, GROUP BY, and HAVING processing, and windowing execution occurs before ORDER BY, LIMIT, and SELECT DISTINCT.