参考文献

  • 路人甲-Spring系列

AOP

  • AOP是一种编程范式,通过将横切关注点(如日志记录、安全性检查等)与核心业务逻辑进行分离,可以使代码更加模块化、可维护性更高.

  • Spring框架中的AOP模块提供了一种方便的方式来实现AOP,它采用了动态代理字节码增强技术,可以在运行时动态地将切面逻辑织入到目标对象的方法中,从而实现AOP的目的.

  • Aspect-Oriented Programming,意为面向切面编程.

  • 作用:在程序中具有公共特性的某些类/某些方法上进行拦截, 在方法执行的前面/后面/执行结果返回后增加执行一些方法.

AOP相关概念

  • 目标对象(target): 目标对象将要被增强的对象,即包含主业逻辑的类对象.
  • 连接点(join point): 连接点,程序运行的某一个点,比如执行某个方法,在Spring AOP中join point总是表示一个方法的执行.
  • 代理对象(proxy): AOP中会通过代理的方式,对目标对象生成一个代理对象,代理对象中会加入需要增强功能,通过代理对象来间接的访问目标对象,起到增强目标对象的效果.
  • 通知(advice): 是切面在连接点上执行的代码,用于实现横切关注点的功能
    • 通知中有两个重要信息:
      • 方法的什么地方(之前,之后,包裹目标对象,方法抛出异常等)
      • 执行什么操作
  • 切入点(point cut): 用来指定需要将通知使用到哪些地方,比如需要用在哪些类方法上,切入点就是做这个配置的.
  • 切面(aspect): 通知(advice)和切入点(point cut)的组合.
    • 定义在哪些地方(ponit cut)执行什么操作(advice),
    • Spring中定义为advisor(通知器)
  • 顾问(Advisor):其实它就是PointcutAdvice的组合,Advice是要增强的逻辑,而增强的逻辑要在什么地方执行是通过Pointcut来指定的,所以Advice必需与Pointcut组合在一起,这就诞生了Advisor这个类,Spring Aop中提供了一个Advisor接口将PointcutAdvice的组合起来.
    • Advisor有好几个称呼:顾问、通知器.

AOP Spring中相关接口

img

Joinpoint接口

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
public interface Joinpoint {

/**
* 转到拦截器链中的下一个拦截器
* Proceed to the next interceptor in the chain.
* <p>The implementation and the semantics of this method depends
* on the actual joinpoint type (see the children interfaces).
* @return see the children interfaces' proceed definition
* @throws Throwable if the joinpoint throws an exception
*/
@Nullable
Object proceed() throws Throwable;

/**
* 返回保存当前连接点静态部分的对象,这里一般指被代理的目标对象
* Return the object that holds the current joinpoint's static part.
* <p>For instance, the target object for an invocation.
* @return the object (can be null if the accessible object is static)
*/
@Nullable
Object getThis();

/**
* 返回此静态连接点 一般就为当前的Method(至少目前的唯一实现MethodInvocation,所以连接点的静态部分肯定是本方法)
* Return the static part of this joinpoint.
* <p>The static part is an accessible object on which a chain of
* interceptors is installed.
*/
@Nonnull
AccessibleObject getStaticPart();

}

Invocation接口

  • 此接口表示程序中调用,调用是一个连接点,可以被拦截器拦截.
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Invocation extends Joinpoint {

/**
* 将参数作为数据对象获取,可以更改此数组中的元素值以更改参数.
* 通常用来获取调用目标方法的参数
* Get the arguments as an array object.
* It is possible to change element values within this
* array to change the arguments.
* @return the argument of the invocation
*/
@Nonnull
Object[] getArguments();
}

MethodInvocation接口

  • 方法调用的描述,在方法调用时提供拦截器.方法调用是一个连接点,可以被方法拦截器拦截.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface MethodInvocation extends Invocation {

/**
* 返回正在调用的方法 返回的是当前Method对象
* 此时,效果同父类的AccessibleObject getStaticPart()这个方法
* Get the method being called.
* <p>This method is a friendly implementation of the
* {@link Joinpoint#getStaticPart()} method (same result).
* @return the method being called
*/
@Nonnull
Method getMethod();

}

通知(Advice)

  • 通知中用来实现被增强的逻辑,通知中有2个关注点:方法的什么地方,执行什么操作.
Advice接口
1
2
3
public interface Advice {

}
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
classDiagram
direction BT
class Advice {
<<Interface>>

}
class AfterReturningAdvice {
<<Interface>>

}
class MethodBeforeAdvice {
<<Interface>>

}
class MethodInterceptor {
<<Interface>>

}
class ThrowsAdvice {
<<Interface>>

}

AfterReturningAdvice --> Advice
MethodBeforeAdvice --> Advice
MethodInterceptor --> Advice
ThrowsAdvice --> Advice

AfterReturningAdvice接口
  • 方法执行后通知,需要在目标方法执行之后执行增强一些逻辑,可以通过这个实现.
  • 注意点: 目标方法正常执行后,才会回调这个接口,当目标方法有异常,那么这通知会被跳过.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface AfterReturningAdvice extends AfterAdvice {

/**
* Callback after a given method successfully returned.
* @param returnValue the value returned by the method, if any
* @param method the method being invoked
* @param args the arguments to the method
* @param target the target of the method invocation. May be {@code null}.
* @throws Throwable if this object wishes to abort the call.
* Any exception thrown will be returned to the caller if it's
* allowed by the method signature. Otherwise the exception
* will be wrapped as a runtime exception.
*/
void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;

}
MethodBeforeAdvice接口
  • 方法执行前通知,需要在目标方法执行前执行一些逻辑的,可以通过这个实现.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface MethodBeforeAdvice extends BeforeAdvice {

/**
* Callback before a given method is invoked.
* @param method the method being invoked
* @param args the arguments to the method
* @param target the target of the method invocation. May be {@code null}.
* @throws Throwable if this object wishes to abort the call.
* Any exception thrown will be returned to the caller if it's
* allowed by the method signature. Otherwise the exception
* will be wrapped as a runtime exception.
*/
void before(Method method, Object[] args, @Nullable Object target) throws Throwable;

}
ThrowsAdvice接口
1
2
3
public interface ThrowsAdvice extends AfterAdvice {

}
  • 此接口上没有任何方法,因为方法由反射调用,实现类必须实现一下形式,前三个参数可选,最后一个参数为需要匹配的异常类型.

    1
    public void afterThrowing([Method, args, target], ThrowableSubclass) 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public void afterThrowing(RemoteException ex) throws Throwable {

    }

    /** Not valid, wrong number of arguments */
    public void afterThrowing(Method m, Exception ex) throws Throwable {

    }

    // Full method signature
    public void afterThrowing(Method m, Object[] args, Object target, IOException ex) {

    }
MethodInterceptor接口
  • 方法拦截器,可以实现AfterReturningAdvice/MethodBeforeAdvice/ThrowsAdvice3种类型的通知.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {

/**
* Implement this method to perform extra treatments before and
* after the invocation. Polite implementations would certainly
* like to invoke {@link Joinpoint#proceed()}.
* @param invocation the method invocation joinpoint
* @return the result of the call to {@link Joinpoint#proceed()};
* might be intercepted by the interceptor
* @throws Throwable if the interceptors or the target object
* throws an exception
*/
@Nullable
Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;

}

切入点

  • 通知(Advice)用来指定需要增强的逻辑.但是哪些类的哪些方法中需要使用这些通知呢?这个就是通过切入点来配置的,切入点对应在Spring中一个接口.
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
public interface Pointcut {

/**
* 类过滤器,可以知道哪些类需要拦截器
* Return the ClassFilter for this pointcut.
* @return the ClassFilter (never {@code null})
*/
ClassFilter getClassFilter();

/**
* 方法匹配器,可以知道哪些方法需要拦截
* Return the MethodMatcher for this pointcut.
* @return the MethodMatcher (never {@code null})
*/
MethodMatcher getMethodMatcher();


/**
* 匹配所有对象的Pointcut,内部的2个过滤器都会返回true
* Canonical Pointcut instance that always matches.
*/
Pointcut TRUE = TruePointcut.INSTANCE;

}
ClassFilter接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@FunctionalInterface
public interface ClassFilter {

/**
* Should the pointcut apply to the given interface or target class?
* @param clazz the candidate target class
* @return whether the advice should apply to the given target class
*/
boolean matches(Class<?> clazz);


/**
* Canonical instance of a ClassFilter that matches all classes.
*/
ClassFilter TRUE = TrueClassFilter.INSTANCE;

}
MethodMatcher接口
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
44
45
46
47
48
49
50
51
52
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.
* @param method the candidate method
* @param targetClass the target class
* @return whether this method matches statically
*/
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();

/**
* 动态匹配验证的方法,比第一个matches方法多了一个参数args,这个参数是调用方法传入的参数
* 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.
* @param method the candidate method
* @param targetClass the target class
* @param args arguments to the method
* @return whether there's a runtime match
* @see MethodMatcher#matches(Method, Class)
*/
boolean matches(Method method, Class<?> targetClass, Object... args);

/**
* 匹配所有方法,这个内部的2个matches方法任何时候都返回true
* Canonical instance that matches all methods.
*/
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}
  • MethodMatcher过滤的整个过程
    • 调用boolean matches(Method method, Class<?> targetClass)方法,验证方法是否匹配
    • boolean isRuntime()方法是否为true,如果为false,则以第一步的接口为准,否则继续向下
    • 调用boolean matches(Method method, Class<?> targetClass, Object... args)方法继续验证,这个方法多了一个参数,可以对目标方法传入的参数进行校验.

顾问(Advisor)

  • 通知定义了需要做什么,切入点定义了在哪些类的哪些方法中执行通知.顾问则是将两者结合起来.
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
25
26
27
28
29
30
31
32
33
34
public interface Advisor {

/**
* Common placeholder for an empty {@code Advice} to be returned from
* {@link #getAdvice()} if no proper advice has been configured (yet).
* @since 5.0
*/
Advice EMPTY_ADVICE = new Advice() {};


/**
* Return the advice part of this aspect. An advice may be an
* interceptor, a before advice, a throws advice, etc.
* @return the advice that should apply if the pointcut matches
* @see org.aopalliance.intercept.MethodInterceptor
* @see BeforeAdvice
* @see ThrowsAdvice
* @see AfterReturningAdvice
*/
Advice getAdvice();

/**
* Return whether this advice is associated with a particular instance
* (for example, creating a mixin) or shared with all instances of
* the advised class obtained from the same Spring bean factory.
* <p><b>Note that this method is not currently used by the framework.</b>
* Typical Advisor implementations always return {@code true}.
* Use singleton/prototype bean definitions or appropriate programmatic
* proxy creation to ensure that Advisors have the correct lifecycle model.
* @return whether this advice is associated with a particular target instance
*/
boolean isPerInstance();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
classDiagram
direction BT
class Advisor {
<<Interface>>

}
class IntroductionAdvisor {
<<Interface>>

}
class PointcutAdvisor {
<<Interface>>

}

IntroductionAdvisor --> Advisor
PointcutAdvisor --> Advisor

IntroductionAdvisor接口
  • 一个Java类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能.可以通过IntroductionAdvisor给目标类引入更多接口的功能.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {

/**
* Return the filter determining which target classes this introduction
* should apply to.
* <p>This represents the class part of a pointcut. Note that method
* matching doesn't make sense to introductions.
* @return the class filter
*/
ClassFilter getClassFilter();

/**
* Can the advised interfaces be implemented by the introduction advice?
* Invoked before adding an IntroductionAdvisor.
* @throws IllegalArgumentException if the advised interfaces can't be
* implemented by the introduction advice
*/
void validateInterfaces() throws IllegalArgumentException;

}
PointcutAdvisor接口
  • 内部有个获取Pointcut方法,AOP中使用到都大部分Advisor都属于这种类型的
1
2
3
4
5
6
7
8
public interface PointcutAdvisor extends Advisor {

/**
* Get the Pointcut that drives this advisor.
*/
Pointcut getPointcut();

}

通知包装器

  • 负责将各种非MethodInterceptor类型的通知(Advice)包装MethodInterceptor类型.

  • Aop中所有的Advice最终都会转换为MethodInterceptor类型的,组成一个方法调用链,然后执行

  • 3个包装器类:

    • MethodBeforeAdviceInterceptor
    • AfterReturningAdviceInterceptor
    • ThrowsAdviceInterceptor

MethodBeforeAdviceInterceptor

  • 这个类实现了MethodInterceptor接口,负责将MethodBeforeAdvice方法前置通知包装为MethodInterceptor类型,创建这个类型的对象的时候需要传递一个MethodBeforeAdvice类型的参数,重点是invoke方法.
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
@SuppressWarnings("serial")
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {

private final MethodBeforeAdvice advice;


/**
* Create a new MethodBeforeAdviceInterceptor for the given advice.
* @param advice the MethodBeforeAdvice to wrap
*/
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}


@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable {
// 负责调用前置通知的方法
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
// 继续执行方法调用链
return mi.proceed();
}

}

AfterReturningAdviceInterceptor

  • 这个类实现了 MethodInterceptor接口,负责将 AfterReturningAdvice方法后置通知包装为MethodInterceptor类型,创建这个类型的对象的时候需要传递一个方法AfterReturningAdvice类型的参数,重点是invoke方法.
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
@SuppressWarnings("serial")
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

private final AfterReturningAdvice advice;


/**
* Create a new AfterReturningAdviceInterceptor for the given advice.
* @param advice the AfterReturningAdvice to wrap
*/
public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}


@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}

}

ThrowsAdviceInterceptor

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {

private static final String AFTER_THROWING = "afterThrowing";

private static final Log logger = LogFactory.getLog(ThrowsAdviceInterceptor.class);


private final Object throwsAdvice;

/** Methods on throws advice, keyed by exception class. */
private final Map<Class<?>, Method> exceptionHandlerMap = new HashMap<>();


/**
* Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice.
* @param throwsAdvice the advice object that defines the exception handler methods
* (usually a {@link org.springframework.aop.ThrowsAdvice} implementation)
*/
public ThrowsAdviceInterceptor(Object throwsAdvice) {
Assert.notNull(throwsAdvice, "Advice must not be null");
this.throwsAdvice = throwsAdvice;

// 获取异常通知中定义的所有方法(public、默认的、protected、private)
Method[] methods = throwsAdvice.getClass().getMethods();
// 轮询methods
for (Method method : methods) {
// 方法名称为afterThrowing && 方法参数为1或者4
if (method.getName().equals(AFTER_THROWING) &&
(method.getParameterCount() == 1 || method.getParameterCount() == 4)) {
// 获取方法的最后一个参数类型
Class<?> throwableParam = method.getParameterTypes()[method.getParameterCount() - 1];
// 判断方法参数类型是不是Throwable类型的
if (Throwable.class.isAssignableFrom(throwableParam)) {
// An exception handler to register...
// 缓存异常处理方法到map中(异常类型->异常处理方法)
this.exceptionHandlerMap.put(throwableParam, method);
if (logger.isDebugEnabled()) {
logger.debug("Found exception handler method on throws advice: " + method);
}
}
}
}
// 如果exceptionHandlerMap,抛出异常,所以最少要有一个异常处理方法
if (this.exceptionHandlerMap.isEmpty()) {
throw new IllegalArgumentException(
"At least one handler method must be found in class [" + throwsAdvice.getClass() + "]");
}
}


/**
* 获取异常通知中自定义的处理异常方法的数量
* Return the number of handler methods in this advice.
*/
public int getHandlerMethodCount() {
return this.exceptionHandlerMap.size();
}


@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 调用通知链
return mi.proceed();
}
catch (Throwable ex) {
// 获取异常通知中自定义的处理异常的方法
Method handlerMethod = getExceptionHandler(ex);
// 当处理的方法不为空
if (handlerMethod != null) {
// 调用异常处理方法
invokeHandlerMethod(mi, ex, handlerMethod);
}
// 继续向外抛出异常
throw ex;
}
}

/**
* 获取throwsAdvice中处理exception参数指定的异常的方法
* Determine the exception handle method for the given exception.
* @param exception the exception thrown
* @return a handler for the given exception type, or {@code null} if none found
*/
@Nullable
private Method getExceptionHandler(Throwable exception) {
// 获取异常类型
Class<?> exceptionClass = exception.getClass();
if (logger.isTraceEnabled()) {
logger.trace("Trying to find handler for exception of type [" + exceptionClass.getName() + "]");
}
// 循环,查询处理方法,循环条件:方法为空 && 异常类型!=Throwable
Method handler = this.exceptionHandlerMap.get(exceptionClass);
while (handler == null && exceptionClass != Throwable.class) {
// 获取异常的父类型
exceptionClass = exceptionClass.getSuperclass();
// 从缓存中查找异常对应的处理方法
handler = this.exceptionHandlerMap.get(exceptionClass);
}
if (handler != null && logger.isTraceEnabled()) {
logger.trace("Found handler for exception of type [" + exceptionClass.getName() + "]: " + handler);
}
// 将查找结果返回
return handler;
}

/**
* 通过反射调用异常通知中的异常方法
* @param mi
* @param ex
* @param method
* @throws Throwable
*/
private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable {
// 构建方法请求参数
Object[] handlerArgs;
// 若只有1个参数,参数为:异常对象
if (method.getParameterCount() == 1) {
handlerArgs = new Object[] {ex};
}
else {
// 4个参数(方法、方法请求参数、目标对象、异常对象)
handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex};
}
try {
// 通过反射调用异常通知中的方法
method.invoke(this.throwsAdvice, handlerArgs);
}
catch (InvocationTargetException targetEx) {
throw targetEx.getTargetException();
}
}

}
  • 异常通知,自定义处理异常的方法有几个特点
    • 方法名称必须为 afterThrowing.
    • 方法参数必须1个或4个,最后一个参数是 Throwable类型或其子类型.
    • 可以在异常处理中记录一些异常信息,这个还是比较有用的,但是注意一点目标方法抛出的异常最后还是会向外继续抛出

创建代理

  • 创建代理所需要的参数配置
  • 根据代理参数获取AopProxy
  • 通过AopProxy获取代理对象

创建代理所需要的参数

  • 创建代理所需要参数配置主要是通过AdvisedSupport这个类来做

根据代理参数获取AopProxy

TargetClassAware接口
img
1
2
3
4
5
6
public interface TargetClassAware {

@Nullable
Class<?> getTargetClass();

}
Advised接口
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
public interface Advised extends TargetClassAware {

/**
* 返回配置是否已冻结,被冻结之后,无法修改已创建好的代理对象中的通知
* Return whether the Advised configuration is frozen,
* in which case no advice changes can be made.
*/
boolean isFrozen();

/**
* 是否对目标类直接创建代理,而不是对接口创建代理,通俗点讲:如果是通过cglib创建代理,此方
* 法返回true,否则返回false
* Are we proxying the full target class instead of specified interfaces?
*/
boolean isProxyTargetClass();

/**
* 获取配置中需要代理的接口列表
* Return the interfaces proxied by the AOP proxy.
* <p>Will not include the target class, which may also be proxied.
*/
Class<?>[] getProxiedInterfaces();

/**
* 判断某个接口是否被代理
* Determine whether the given interface is proxied.
* @param intf the interface to check
*/
boolean isInterfaceProxied(Class<?> intf);

/**
* 设置被代理的目标源,创建代理的时候,通常需要传入被代理的对象,最终被代理的对象会被包装为TargetSource类型的
* Change the {@code TargetSource} used by this {@code Advised} object.
* <p>Only works if the configuration isn't {@linkplain #isFrozen frozen}.
* @param targetSource new TargetSource to use
*/
void setTargetSource(TargetSource targetSource);

/**
* 返回被代理的目标源
* Return the {@code TargetSource} used by this {@code Advised} object.
*/
TargetSource getTargetSource();

/**
* 设置是否需要将代理暴露在ThreadLocal中,这样可以在线程中获取到被代理对象,这个配置挺有用的
* Set whether the proxy should be exposed by the AOP framework as a
* {@link ThreadLocal} for retrieval via the {@link AopContext} class.
* <p>It can be necessary to expose the proxy if an advised object needs
* to invoke a method on itself with advice applied. Otherwise, if an
* advised object invokes a method on {@code this}, no advice will be applied.
* <p>Default is {@code false}, for optimal performance.
*/
void setExposeProxy(boolean exposeProxy);

/**
* 返回exposeProxy
* Return whether the factory should expose the proxy as a {@link ThreadLocal}.
* <p>It can be necessary to expose the proxy if an advised object needs
* to invoke a method on itself with advice applied. Otherwise, if an
* advised object invokes a method on {@code this}, no advice will be applied.
* <p>Getting the proxy is analogous to an EJB calling {@code getEJBObject()}.
* @see AopContext
*/
boolean isExposeProxy();

/**
* 设置此代理配置是否经过预筛选,以便它只包含适用的顾问(匹配此代理的目标类)。
* 默认设置是“假”。如果已经对advisor进行了预先筛选,则将其设置为“true”
* 这意味着在为代理调用构建实际的advisor链时可以跳过ClassFilter检查
*
* Set whether this proxy configuration is pre-filtered so that it only
* contains applicable advisors (matching this proxy's target class).
* <p>Default is "false". Set this to "true" if the advisors have been
* pre-filtered already, meaning that the ClassFilter check can be skipped
* when building the actual advisor chain for proxy invocations.
* @see org.springframework.aop.ClassFilter
*/
void setPreFiltered(boolean preFiltered);

/**
* Return whether this proxy configuration is pre-filtered so that it only
* contains applicable advisors (matching this proxy's target class).
*/
boolean isPreFiltered();

/**
* Return the advisors applying to this proxy.
* @return a list of Advisors applying to this proxy (never {@code null})
*/
Advisor[] getAdvisors();

default int getAdvisorCount() {
return getAdvisors().length;
}

void addAdvisor(Advisor advisor) throws AopConfigException;

void addAdvisor(int pos, Advisor advisor) throws AopConfigException;

boolean removeAdvisor(Advisor advisor);

void removeAdvisor(int index) throws AopConfigException;

int indexOf(Advisor advisor);

boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;

void addAdvice(Advice advice) throws AopConfigException;

void addAdvice(int pos, Advice advice) throws AopConfigException;

boolean removeAdvice(Advice advice);

int indexOf(Advice advice);

String toProxyConfigString();

}
ProxyConfig
  • 这个类比较关键,代理配置类,内部包含了创建代理时需要配置的各种参数

    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
    public class ProxyConfig implements Serializable {

    /** use serialVersionUID from Spring 1.2 for interoperability. */
    private static final long serialVersionUID = -8409359707199703185L;


    /**
    * 标记是否直接对目标类进行代理,而不是通过接口产生代理
    */
    private boolean proxyTargetClass = false;

    /**
    * 标记是否对代理进行优化。启动优化通常意味着在代理对象被创建后,增强的修改将不会生效,因此
    * 默认值为false。
    * 如果exposeProxy设置为true,即使optimize为true也会被忽略。
    */
    private boolean optimize = false;

    /**
    * 标记是否需要阻止通过该配置创建的代理对象转换为Advised类型,默认值为false,表示代理对象
    * 可以被转换为Advised类型
    */
    boolean opaque = false;

    /**
    * 标记代理对象是否应该被aop框架通过AopContext以ThreadLocal的形式暴露出去。
    * 当一个代理对象需要调用它自己的另外一个代理方法时,这个属性将非常有用。默认是是false,以
    * 避免不必要的拦截。
    */
    boolean exposeProxy = false;

    /**
    * 标记该配置是否需要被冻结,如果被冻结,将不可以修改增强的配置。
    * 当我们不希望调用方修改转换成Advised对象之后的代理对象时,这个配置将非常有用。
    */
    private boolean frozen = false;
    // ... 略

    }

根据配置获取AopProxy

1
2
3
4
5
6
AdvisedSupport advisedSupport = new AdvisedSupport();	
// 创建AopProxy使用了简单工厂类
AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory();
// 通过AopProxy工厂获取AopProxy对象
AopProxy aopProxy = aopProxyFactory.createAopProxy(advisedSupport);
Object proxy = aopProxy.getProxy();
  • 此阶段会根据AdvisedSupport中配置信息,判断具体是采用cglib的方式还是采用jdk动态代理方式获取代理对象.
AopProxy接口
  • 这个接口定义了一个方法,用来创建最终的代理对象,这个接口有2个实现类
    • CglibAopPoxy: 采用cglib的方式来创建代理对象
    • JdkDynamicAopPoxy: 采用jdk动态代理的方式来创建代理对象
DefaultAopProxyFactory
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
44
45
46
47
48
49
50
51
52
53
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

private static final long serialVersionUID = 7930414337282325166L;


@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
// optimize: 用来控制通过CGLIB创建的代理是否使用激进的优化策略.除非完全了解AOP代理如何处理优化,否则不推荐用户使用这个设置.目前这个属性仅用于CGLIB代理,对于JDK动态代理(默认代理)无效
// proxyTargetClass: 这个属性为true时,目标类本身被代理而不是目标类的接口.如果这个属性被设置为true,CGLIB代理将被创建,设置方式为<aop:aspectj-autoproxy proxy-target-class="true"/>
// hasNoUserSuppliedProxyInterfaces: 是否存在代理接口
/**
* 对JDK与CGLIB方式的总结:
* * 若目标对象实现了接口,默认情况下回采用JDK的动态代理实现AOP
* * 若目标对象实现了接口,可以强制使用CGLIB实现AOP
* * 若目标对象没有实现接口,必须使用CGLIB库,Spring会自动在JDK动态代理之间转换
*/
/**
* 如何强制使用CGLIB实现AOP
* 1. 添加CGLIB库
* 2. 在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
*/
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
// 采用JDK动态代理的方式
return new JdkDynamicAopProxy(config);
}
// 采用cglib动态代理的方式
return new ObjenesisCglibAopProxy(config);
}
else {
// 采用JDK动态代理的方式
return new JdkDynamicAopProxy(config);
}
}

/**
* 确定所提供的AdvisedSupport是否只指定了SpringProxy接口(或者根本没有指定代理接口)
* Determine whether the supplied {@link AdvisedSupport} has only the
* {@link org.springframework.aop.SpringProxy} interface specified
* (or no proxy interfaces specified at all).
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}

}

JdkDynamicAopProxy

  • 被创建的代理对象默认会实现SpringProxy,Advised,DecoratingProxy3个接口
  • SpringProxy这个接口中没有任何方法,只是起一个标记的作用,用来标记代理对象是使用Spring Aop创建的
  • 代理对象都会默认实现Advised接口,所以可以通过这个接口动态变更代理对象中的通知.
  • DecoratingProxy接口中定义了一个getDecoratedClass方法,用来获取被代理的原始目标对象的类型.

示例

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
@Test
public void test1() {
   //定义目标对象
   UserService target = new UserService();
   //创建pointcut,用来拦截UserService中的work方法
   Pointcut pointcut = new Pointcut() {
       @Override
       public ClassFilter getClassFilter() {
           //判断是否是UserService类型的
           return clazz -> UserService.class.isAssignableFrom(clazz);
    }
       @Override
       public MethodMatcher getMethodMatcher() {
           return new MethodMatcher() {
               @Override
               public boolean matches(Method method, Class<?> targetClass) {
                   //判断方法名称是否是work
                   return "work".equals(method.getName());
        }
               @Override
               public boolean isRuntime() {
                   return false;
}
               @Override
               public boolean matches(Method method, Class<?> targetClass, Object... args) {
                   return false;
        }
          };
    }
  };
   //创建通知,此处需要在方法之前执行操作,所以需要用到MethodBeforeAdvice类型的通知
   MethodBeforeAdvice advice = (method, args, target1) -> System.out.println("你好:" + args[0]);
   //创建Advisor,将pointcut和advice组装起来
   DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
   //通过spring提供的代理创建工厂来创建代理
   ProxyFactory proxyFactory = new ProxyFactory();
   //为工厂指定目标对象
   proxyFactory.setTarget(target);
   //调用addAdvisor方法,为目标添加增强的功能,即添加Advisor,可以为目标添加很多个Advisor
   proxyFactory.addAdvisor(advisor);
   //通过工厂提供的方法来生成代理对象
   UserService userServiceProxy = (UserService) proxyFactory.getProxy();
   //调用代理的work方法
   userServiceProxy.work("路人");

// 等价于
// 1.创建代理所需参数配置(如:采用什么方式的代理、通知列表等)
// AdvisedSupport advisedSupport = new AdvisedSupport();
// 如:添加一个前置通知
// advisedSupport.addAdvice(new MethodBeforeAdvice() {
// @Override
// public void before(Method method, Object[] args, @Nullable Object target)
// throws Throwable {
// String userName = (String) args[0];
// //如果不是路人的时候,抛出非法访问异常
// if (!"路人".equals(userName)) {
// throw new RuntimeException(String.format("[%s]非法访问!", userName));
// }
// }
// });
// 设置被代理的目标对象
// FundsService target = new FundsService();
// advisedSupport.setTarget(target);

// 2.根据配置信息获取AopProxy对象,AopProxy用来负责创建最终的代理对象
// AopProxy接口有2个实现类(JDK动态代理、cglib代理)
// 具体最终会使用哪种方式,需要根据AdvisedSupport中指定的参数来判断
// 创建AopProxy使用了简单工厂模式
// AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory();
// 通过AopProxy工厂获取AopProxy对象
// AopProxy aopProxy = aopProxyFactory.createAopProxy(advisedSupport);

// 3.通过AopProxy创建代理对象
// Object proxy = aopProxy.getProxy();


// 1.创建代理所需参数配置(如:采用什么方式的代理、通知列表等)
// ProxyFactory proxyFactory = new ProxyFactory();
// 2. 设置被代理的目标对象
// proxyFactory.setTarget(target);
// 3.如:添加一个前置通知
// proxyFactory.addAdvice(new MethodBeforeAdvice() {
// @Override
// public void before(Method method, Object[] args, @Nullable Object target)
// throws Throwable {
// String userName = (String) args[0];
// //如果不是路人的时候,抛出非法访问异常
// if (!"路人".equals(userName)) {
// throw new RuntimeException(String.format("[%s]非法访问!", userName));
// }
// }
// });
// 4.通过AopProxy创建代理对象
// Object proxy = proxyFactory.getProxy();

}                   

Aop创建代理的方式

  • 主要分为两大类
    • 手动方式: 需要通过硬编码一个个创建代理
    • 自动化方式: 也称为批量的方式,批量的方式用在Spring环境中,通过bean后置处理器来对符合条件的bean创建代理.

img

  • 左边ProxyCreateSupport下面都是手动的方式,有3个类
  • 右边AbstractAutoProxyCreator下面的类都是自动创建代理的方式

手动3种方式

ProxyFactory方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 1.创建代理所需参数配置(如:采用什么方式的代理、通知列表等)
ProxyFactory proxyFactory = new ProxyFactory();
// 2. 设置被代理的目标对象
proxyFactory.setTarget(target);
// 3. 如:添加一个前置通知
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, @Nullable Object target)
throws Throwable {
String userName = (String) args[0];
// 如果不是路人的时候,抛出非法访问异常
if (!"路人".equals(userName)) {
throw new RuntimeException(String.format("[%s]非法访问!", userName));
}
}
});
// 4. 通过AopProxy创建代理对象
Object proxy = proxyFactory.getProxy();
AspectJProxyFactory方式
ProxyFactoryBean方式
  • 创建代理,有3个信息比较关键
    • 需要增强的功能,这个放在通知(Advice)中实现
    • 目标对象(target): 表示需要给哪个对象进行增强
    • 代理对象(proxy): 将增强的功能和目标对象组合在一起,然后形成的一个代理对象,通过代理对象来访问目标对象,起到对目标对象增强的效果
  • 使用ProxyFactoryBean步骤
    1. 创建ProxyFactoryBean对象
    2. 通过ProxyFactoryBean.setTargetName设置目标对象的bean名称,目标对象是Spring容器的一个bean
    3. 通过ProxyFactoryBean.setInterceptorNames添加要增强的通知
    4. ProxyFactoryBean注册到Spring容器中,假设名称为proxyBean
    5. Spring查找名称为proxyBeanbean,这个bean就是生成好的代理对象
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;

@Configuration
public class MainConfig1 {
//注册目标对象
@Bean
public Service1 service1() {
return new Service1();
}
//注册一个前置通知
@Bean
public MethodBeforeAdvice beforeAdvice() {
MethodBeforeAdvice advice = new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, @Nullable Object
target) throws Throwable {
System.out.println("准备调用:" + method);
}
};
return advice;
}
//注册一个后置通知
@Bean
public MethodInterceptor costTimeInterceptor() {
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long starTime = System.nanoTime();
Object result = invocation.proceed();
long endTime = System.nanoTime();
System.out.println(invocation.getMethod() + ",耗时(纳秒):" +
(endTime - starTime));
return result;
}
};
return methodInterceptor;
}
//注册ProxyFactoryBean
@Bean
public ProxyFactoryBean service1Proxy() {
//1.创建ProxyFactoryBean
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
//2.设置目标对象的bean名称
proxyFactoryBean.setTargetName("service1");
//3.设置拦截器的bean名称列表,此处2个(advice1和advice2)
proxyFactoryBean.setInterceptorNames("beforeAdvice",
"costTimeInterceptor");
return proxyFactoryBean;
}
}
1
2
3
4
5
6
7
8
9
10
AnnotationConfigApplicationContext context = new 
AnnotationConfigApplicationContext(MainConfig1.class);
//获取代理对象,代理对象bean的名称为注册ProxyFactoryBean的名称,即:service1Proxy
Service1 bean = context.getBean("service1Proxy", Service1.class);
System.out.println("----------------------");
//调用代理的方法
bean.m1();
System.out.println("----------------------");
//调用代理的方法
bean.m2();

ProxyFactoryBean中的interceptorNames

  • interceptorNames用来指定拦截器的bean名称列表,常用的2种方式。

    • 批量的方式
    • 非批量的方式
  • 批量的方式,使用方法

    1
    proxyFactoryBean.setInterceptorNames("需要匹配的Bean的名称*");
    • 需要匹配的bean名称后面跟上一个*,可以用来批量的匹配,如:interceptor*,此时Spring会从容器中找到下面2中类型的所有bean,bean名称以interceptor开头的将作为增强器

      1
      2
      org.springframework.aop.Advisor
      org.aopalliance.intercept.Interceptor
    • 这个地方使用的时候需要注意,批量的方式注册的时候,如果增强器的类型不是上面2种类型,比如下面3种类型通知,需要将其包装为Advisor才可以,而MethodInterceptorInterceptor类型的,可以不用包装为Advisor类型的.

      1
      2
      3
      org.springframework.aop.MethodBeforeAdvice
      org.springframework.aop.AfterReturningAdvice
      org.springframework.aop.ThrowsAdvice
  • 非批量的方式,使用方法

    • 非批量的方式,需要注册多个增强器,需明确的指定多个增强器的bean名称,多个增强器按照参数中指定的顺序执行,如

      1
      proxyFactoryBean.setInterceptorNames("advice1","advice2");
    • advice1、advice2对应的bean类型必须为下面列表中指定的类型,类型这块比匹配的方式范围广一些

      1
      2
      3
      4
      5
      MethodBeforeAdvice(方法前置通知) 
      AfterReturningAdvice(方法后置通知)
      ThrowsAdvice(异常通知)
      org.aopalliance.intercept.MethodInterceptor(环绕通知)
      org.springframework.aop.Advisor(顾问)

AspectJ

使用步骤

  1. 创建一个类,使用@Aspect来标注
  2. @Aspect标注的类中,通过@Pointcur定义切入点
  3. @Aspect标注的类中,通过AspectJ提供的一些通知相关的注解定义通知
  4. 使用AspectJProxyFactory结合@Aspect标注的类,来生成代理对象

@Pointcut

  • 作用: 用来标注在方法上来定义切入点

  • 定义: @注解(value="表达式标签(表达式格式)")

    1
    @Pointcut(value = "@annotation(com.holelin.annotations.LoginRequired))")

表达式标签(10种)

标签 作用
execution 用于匹配方法执行的连接点
within 目标对象target的类型是否和within中指定的类型匹配
this 通过AOP创建的代理对象的类型是否和this中指定的类型匹配;注意判断的目标是代理对象;this中使用的表达式必须为类型全限定名,不支持通配符
target 判断目标对象的类型是否和指定的类型匹配;注意判断的是目标对象的类型;表达式必须是类型的全限定名,不支持通配符
args 用于匹配当前执行的方法传入的参数为指定类型的执行方法
@within 用于匹配所有持有指定注解类型的方法
@target 用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解
@args 用于匹配当前执行的方法传入的参数持有指定注解的执行
@annotation 用于匹配当前执行方法持有指定注解的方法
bean Spring AOP扩展,用于匹配特定名称的Bean对象的执行方法
execution
1
2
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
throws-pattern?)
  • 其中带?号的 modifiers-pattern?,declaring-type-pattern?,throws-pattern?是可选项
  • ret-type-pattern,name-pattern, parameters-pattern是必选项
  • modifier-pattern?修饰符匹配,如public表示匹配公有方法
  • ret-type-pattern返回值匹配,*表示任何返回值,全路径的类名等
  • declaring-type-pattern?类路径匹配
表达式 描述
public *.*(..) 任何公共方法
* cn.holelin..IService.*() cn.holelin包及所有子包下IService接口中的任何无参方法
* cn.holelin..*.*(..) cn.holelin包及所有子包下任何类的任何方法
* cn.holelin..IService.*(..)) && args(arg) cn.holelin包及所有子包下IService接口中的任何只有一个参数方法
* Service.*(String) 匹配Service中只有一个参数且参数类型是String的方法
* Service.*(*,String) 匹配Service中只有2个参数且个第二参数类型是String的方法
* Service.*(..,String) 匹配Service中最后一个参数类型是String的方法
类型匹配语法
通配符 描述
* 匹配任何数量字符
.. 匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数(0个或者多个参数)
+ 匹配指定类型及其子类型;仅能作为后缀放在类型模式后边
within
  • 匹配原则

    1
    target.getClass().equals(within表达式中指定的类型)
this
  • 匹配原则

    1
    2
    // this(x),则代理对象proxy满足下面条件则会匹配
    x.getClass().isAssignableFrom(proxy.getClass())
target
  • 匹配原则

    1
    2
    // target(x),则目标对象target满足下面条件
    x.getClass().isAssignableFrom(target.getClass())
within,this,target对比
表达式标签 判断的对象 判断规则(x:指表达式中指定的类型)
within target对象 target.getClass().equals(within表达式中指定的类型)
this proxy对象 x.getClass().isAssignableFrom(proxy.getClass())
target target对象 x.getClass().isAssignableFrom(target.getClass())
args
  • args(参数类型列表)匹配当前执行的方法传入的参数是否为args中指定的类型;注意是匹配传入的参数类型,不是匹配方法签名的参数类型;参数类型列表中的参数必须是类型全限定名,不支持通配符;args属于动态切入点,也就是执行方法的时候进行判断的,这种切入点开销非常大,非特殊情况最好不要使用。
@within
  • 调用目标方法的时候,通过java中 Method.getDeclaringClass()获取当前的方法是哪个类中定义的,然后会看这个类上是否有指定的注解。

    1
    被调用的目标方法Method对象.getDeclaringClass().getAnnotation(within中指定的注解类型) != null

@Aspect中有5种通知

通知 描述
@Before 前置通知, 在方法执行之前执行
@Aroud 环绕通知, 围绕着方法执行
@After 后置通知, 在方法执行之后执行
@AfterReturning 返回通知, 在方法返回结果之后执行
@AfterThrowing 异常通知, 在方法抛出异常之后

通知获取被调方法信息

  • 通知中想要获取被调用方法的信息,分2中情况
    • 非环绕通知,可以将org.aspectj.lang.JoinPoint作为通知方法的第一个参数,通过这个参数获取被调用方法的信息.
    • 环绕通知,可以将org.aspectj.lang.ProceedingJoinPoint作为通知方法的第一个参数,通过这个参数获取被调用方法的信息.

JoinPoint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.aspectj.lang;  
import org.aspectj.lang.reflect.SourceLocation;
public interface JoinPoint {  
    String toString();          //连接点所在位置的相关信息  
    String toShortString();     //连接点所在位置的简短相关信息  
    String toLongString();      //连接点所在位置的全部相关信息  
    Object getThis();          //返回AOP代理对象
    Object getTarget();        //返回目标对象  
    Object[] getArgs();        //返回被通知方法参数列表,也就是目前调用目标方法传入的参数  
    Signature getSignature();   //返回当前连接点签名,这个可以用来获取目标方法的详细信息,如方法Method对象等
    SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置  
    String getKind();         //连接点类型  
    StaticPart getStaticPart()//返回连接点静态部分  
}

ProceedingJoinPoint

1
2
3
4
5
6
7
8
9
public interface ProceedingJoinPoint extends JoinPoint {

// 继续执行下一个通知或目标方法的调用
Object proceed() throws Throwable;

// 继续执行下一个通知或目标方法的调用
Object proceed(Object[] args) throws Throwable;
}

Signature

  • 注意JoinPoint#getSignature()这个方法,用来获取连接点的签名信息

  • 通常情况,Spring中的Aop都是用来对方法进行拦截,所以通常情况下连接点都是一个具体的方法, Signature有个子接口

    1
    org.aspectj.lang.reflect.MethodSignature
  • JoinPoint#getSignature()都可以转换转换为MethodSignature类型,然后可以通过这个接口提供的一些方法来获取被调用的方法的详细信息。

几种通知对比

通知类型 执行时间点 可获得返回值 目标方法异常时是否会执行
@Before 方法执行之前
@Around 环绕方法执行 自己控制
@After 方法执行之后
@AfterReturning 方法执行之后
@AfterThrowing 方法发生异常后

@EnableAspectJAutoProxy

通知执行顺序

  • Spring Aop中4种通知(Advice)

    1
    2
    3
    4
    org.aopalliance.intercept.MethodInterceptor
    org.springframework.aop.MethodBeforeAdvice
    org.springframework.aop.AfterReturningAdvice
    org.springframework.aop.ThrowsAdvice
    • 所有的通知最终都需要转换为MethodInterceptor类型的通知,然后组成一个MethodInterceptor列表,称之为方法调用链或者拦截器链,上面列表中后面3通过下面的转换器将其包装为MethodInterceptor类型的通知

      1
      2
      3
      4
      5
      6
      org.springframework.aop.MethodBeforeAdvice ->
      org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor
      org.springframework.aop.AfterReturningAdvice ->
      org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor
      org.springframework.aop.ThrowsAdvice ->
      org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor

5种通知对应的Advice类

通知类型 对应的Advice类
@AfterThrowing org.springframework.aop.aspectj.AspectJAfterThrowingAdvice
@AfterReturning org.springframework.aop.aspectj.AspectJAfterReturningAdvice
@After org.springframework.aop.aspectj.AspectJAfterAdvice
@Around org.springframework.aop.aspectj.AspectJAroundAdvice
@Before org.springframework.aop.aspectj.AspectJMethodBeforeAdvice