0%

HandlerAdapter 介绍

简介

在 Spring MVC 中,HandlerAdapter 是核心组件之一,负责协调控制器(Handler)的执行流程。它的主要作用是适配不同类型的处理器(Handler),使它们能够统一处理请求。

HandlerMapping 介绍

简介

HandlerMapping 是一个接口,它定义了如何将传入的 HTTP 请求与对应的处理器(Controller 或其他处理逻辑)进行匹配和映射。它是 Spring MVC 请求处理流程中的一个关键环节,位于请求分发器(DispatcherServlet)和处理器(Handler)之间。

Aspect 动态代理方式

在Spring AOP中,如果目标类实现了至少一个接口,Spring默认会使用JDK动态代理。

JDK动态代理只能代理接口中定义的方法。普通类自身定义的方法(未在接口中声明)不会被代理,代理对象无法“看到”这个方法。

ExposeInvocationInterceptor

将 MethodInvocation 存储到 ThreadLocal 变量中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable {
	private static final ThreadLocal<MethodInvocation> invocation =
			new NamedThreadLocal<>("Current AOP method invocation");
	public static MethodInvocation currentInvocation() throws IllegalStateException {
		MethodInvocation mi = invocation.get();
        //...
    }
	@Override
	@Nullable
	public Object invoke(MethodInvocation mi) throws Throwable {
		MethodInvocation oldInvocation = invocation.get();
		invocation.set(mi);
		try {
			return mi.proceed();
		}
		finally {
			invocation.set(oldInvocation);
		}
	}
}

END

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);

描述

自动配置

概述

@EnableTransactionManagement 开启自动事务

org.springframework.transaction.annotation.EnableTransactionManagement

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

默认自动配置

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

自动配置

方式一:注解启用

注解 EnableAspectJAutoProxy启用 Spring Aspect 切面。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// org.springframework.context.annotation.EnableAspectJAutoProxy
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {}


// org.springframework.context.annotation.AspectJAutoProxyRegistrar
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // ...
        
        // 注册 AnnotationAwareAspectJAutoProxyCreator.class
		// Order 设置为最高优先级。beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    }
}

注解 EnableAspectJAutoProxy底层实现是注册 AnnotationAwareAspectJAutoProxyCreator.class BeanDefinition 到 Spring 容器中。