记忆、淡忘

Sring Aop的实现

spring AOP的概念

1、Spring AOP中的几个基本概念:

  • 切面(Aspect):切面就是一个关注点的模块化,如事务管理、日志管理、权限管理等;
  • 连接点(Joinpoint):程序执行时的某个特定的点,在Spring中就是一个方法的执行;
  • 通知(Advice):通知就是在切面的某个连接点上执行的操作,也就是事务管理、日志管理等;
  • 切入点(Pointcut):切入点就是描述某一类选定的连接点,也就是指定某一类要织入通知的方法;
  • 目标对象(Target):就是被AOP动态代理的目标对象;

2、设计分析
Spring AOP 的核心技术是JDK的动态代理技术(参考:JDK动态代理 一文)。
Spring Aop 生效需要经过一系列的操作,首先要为目标对象建立代理对象(接口是JDK代理实现,类是第三方CGLIB 来完成),然后启动代理对象的拦截器来完成切面的织入(通过一系列的适配器来实现的)。

3、应用场景
日志功能、权限校验、以及事物处理。

AopProxy代理对象

ProxyFactory类图

  1. ProxyConfig 为ProxyFactoryBean等子类提供配置属性
  2. AdvisedSupport 封装了AOP对通知和通知器的相关操作
  3. ProxyCreatorSupport 提供AopProxyFactory的操作权限
  4. AspectJProxyFactory 集成Spring和Aspect
  5. ProxyFactoryBean AOP功能封装,可在IOC容器中声明时配置
  6. ProxyFactory 需要编程式使用AOP功能

配置ProxyFactoryBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!---实例化Advice通知->
<bean id="logAdvice" class="app.advice.LogAdvice" />
<!--初始化ProxyFactoryBean -->
<bean class="org.springframework.aop.framework.ProxyFactoryBean">
<!--接口代理,可以不配置,根据target获取接口-->
<property name="proxyInterfaces">
<value>app.service.AopService</value>
</property>
<!--通知器,通过代理对象的拦截器机制起作用-->
<property name="interceptorNames">
<list>
<value>logAdvice</value>
</list>
</property>
<!--目标对象-->
<property name="target" >
<bean class="app.service.AopServiceImpl"/>
</property>
</bean>
1
2
3
4
5
6
7
8
9
10
11
@RestController
public class TestController {
@Resource
private AopService aopService;//初始化的对象是动态代理的
@RequestMapping(value="/test",method = RequestMethod.GET)
public String testAOP() {
return aopService.testAOP();
}
}

我们可以从上面的代码中看到aopService对象注入的是生成的代理对象aopServiceProxy

详细代码可参考spring-aop demo

ProxyFactoryBean生成AopProxy代理对象

ProxyFactoryBean 需要为target对象生成Proxy代理对象;对ProxyFactoryBean而言,需要对target目标对象的增强处理(添加Advice)需要通过getObject方法包装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Object getObject() throws BeansException {
//初始化通知器链
initializeAdvisorChain();
//判断是否单例,默认true
if (isSingleton()) {
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}
return newPrototypeInstance();
}
}

下面看一下初始化通知器链的initializeAdvisorChain方法,

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
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
//判断是否已经初始化过
if (this.advisorChainInitialized) {
return;
}
//是否存在通知器
if (!ObjectUtils.isEmpty(this.interceptorNames)) {
//判断beanFactory是否存在,不存在没法获取通知器的实例
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
}
// Globals can't be last unless we specified a targetSource using the property...
if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
throw new AopConfigException("Target required after globals");
}
// 配置Advisor链的调用
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
}
if (name.endsWith(GLOBAL_SUFFIX)) {
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException(
"Can only use global advisors or interceptors with a ListableBeanFactory");
}
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}
else {
// If we get here, we need to add a named interceptor.
//检查是 singleton 或 prototype,singleton的通知器已经初始化,
//prototype的通知器只有当通知器链初始化时初始化
Object advice;
if (this.singleton || this.beanFactory.isSingleton(name)) {
// Add the real Advisor/Advice to the chain.
advice = this.beanFactory.getBean(name);
}
else {
// It's a prototype Advice or Advisor: replace with a prototype.
// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
advice = new PrototypePlaceholderAdvisor(name);
}
addAdvisorOnChainCreation(advice, name);
}
}
}
this.advisorChainInitialized = true;
}

通知器链初始化完成之后,返回Sngleton的代理对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
//如果没有直接定义要代理的接口,也就是proxyInterfaces属性的话则通过target也就是目标对象来获取代理接口
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
// Rely on AOP infrastructure to tell us what interfaces to proxy.
Class<?> targetClass = getTargetClass();
if (targetClass == null) {
throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
}
//设置代理对象的接口
setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
}
// Initialize the shared singleton instance.
super.setFrozen(this.freezeProxy);
//生成需要的代理对象
this.singletonInstance = getProxy(createAopProxy());
}
return this.singletonInstance;
}

下面我们详细看一下代理对象是如何生成的

1
2
3
protected Object getProxy(AopProxy aopProxy) {
return aopProxy.getProxy(this.proxyClassLoader);
}

代理对象是aopProxy生成,aopProxy又由ProxyFactory生成,aopProxyFactory是由ProxyFactoryBean的父类ProxyCreatorSupport生成,是在ProxyCreatorSupport的构造函数中默认生成DefaultAopProxyFactory对象,也就是说代理对象是在DefaultAopProxyFactory中生成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ProxyCreatorSupport extends AdvisedSupport {
private AopProxyFactory aopProxyFactory;
public ProxyCreatorSupport() {
this.aopProxyFactory = new DefaultAopProxyFactory();
}
//.....省略其他代码
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
public AopProxyFactory getAopProxyFactory() {
return this.aopProxyFactory;
}
//.....省略其他代码
}

下面看一下DefaultAopProxyFactory的源码,当代理对象是接口(并且不是SpringProxy接口)的话使用JDK动态代理(JdkDynamicAopProxy),否则的话使用CGLIB代理(CglibAopProxy)。

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
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
//有判断作用的是hasNoUserSuppliedProxyInterfaces方法
//判断代理的接口是否是指定的SpringProxy接口或者是不是没有代理接口
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
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)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
/**
* 判断是否有代理的接口是否是指定的SpringProxy接口还是没有代理接口
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
}

获取代理对象,并执行目标对象的方法

现在我们得到了aopProxy对象,下面就可以获取代理对象了,下面我们看一下JdkDynamicAopProxy代码,
其实到这里我们可以发现JdkDynamicAopProxy的作用等于动态代理DEMO中的DynamicProxyHandler,
JdkDynamicAopProxy实现了InvocationHandler的invoke方法,改方法做为代理对象Proxy的回调函数被调用,因此在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
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);
//配置类(关于代理对象的配置)
private final AdvisedSupport advised;
private boolean equalsDefined;
private boolean hashCodeDefined;
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
throw new AopConfigException("No advisors and no TargetSource specified");
}
this.advised = config;
}
@Override
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
/**
*
*/
private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) {
for (Class<?> proxiedInterface : proxiedInterfaces) {
Method[] methods = proxiedInterface.getDeclaredMethods();
for (Method method : methods) {
if (AopUtils.isEqualsMethod(method)) {
this.equalsDefined = true;
}
if (AopUtils.isHashCodeMethod(method)) {
this.hashCodeDefined = true;
}
if (this.equalsDefined && this.hashCodeDefined) {
return;
}
}
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// 没有实现Object的equlas
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// 没有实现Object的 hashCode()
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
//
return AopProxyUtils.ultimateTargetClass(this.advised);
}
//判断代理的是接口 并且 代理类是Advised类的超类 (基本是不可能的)
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
//默认false
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 获取代理类
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// 获取拦截链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 判断是否有拦截方法
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
//直接执行目标对象的方法
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 创建一个ReflectiveMethodInvocation对象
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 拦截器链执行
retVal = invocation.proceed();
}
// 获取返回类型
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
/**
* 省略代码
*/
}
目标对象方法调用

如果没有设置拦截器,直接对目标对象的方法进行调用,JdkDynamicAopProxy代理的对象通过反射机制在AopUtils.invokeJoinpointUsingReflection方法中实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)throws Throwable {
// Use reflection to invoke the method.
try {
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
}
catch (InvocationTargetException ex) {
// Invoked method threw a checked exception.
// We must rethrow it. The client won't see the interceptor.
throw ex.getTargetException();
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
method + "] on target [" + target + "]", ex);
}
catch (IllegalAccessException ex) {
throw new AopInvocationException("Could not access method [" + method + "]", ex);
}
}
拦截器方法调用

不管是JDK代理还是CGLIB代理,拦截器的调用都是通过ReflectiveMethodInvocation来实现的,在proceed方法中调用拦截器方法:

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
public Object proceed() throws Throwable {
// 从-1索引开始,如果拦截器被迭代完后直接调用目标对象的方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// 拦截器动态匹配,如果和Pointcut匹配 Advice得到执行
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// 如果不匹配,忽略当前拦截器,递归调用proceed方法,迭代下一个拦截器
return proceed();
}
}
else {
// 如果是 interceptor, 直接调用这个interceptor对应的方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}

到这里,我们已经知道拦截器和目标对象的方法被调用的过程了。

拦截器链的生成

在JdkDynamicAopProxy的invoke中获取拦截器链进行操作,下面我们看一下拦截器是怎么生成的:

AdvisedSupport 取得拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
//……
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
//……

从上面的代码片段我们可知,拦截器被(cache)缓存,下面看一下DefaultAdvisorChainFactory中拦截器具体注册的过程

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
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass) {
// 从config中获取通知器(在初始化拦截器链的过程中生成)
List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
//获取拦截器,从Advisor中去除Advice,判断是否是MethodBeforeAdvice,AfterReturningAdvice
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
if (mm.isRuntime()) {
//
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
/**
* Determine whether the Advisors contain matching introductions.
*/
private static boolean hasMatchingIntroductions(Advised config, Class<?> actualClass) {
for (int i = 0; i < config.getAdvisors().length; i++) {
Advisor advisor = config.getAdvisors()[i];
if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (ia.getClassFilter().matches(actualClass)) {
return true;
}
}
}
return false;
}
}

下面是DefaultAdvisorAdapterRegistry中获取拦截器的代码

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
private final List<AdvisorAdapter> adapters = new ArrayList<>(3);
/**
* Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
*/
public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private 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
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
}

到这里整个过程就一目了然了,在我看来Spring AOP 功能的实现就是通过及适配器设计模式把增强操作组装成一个拦截器,再在通过动态代理的方式调用目标方法的过程中执行相应的拦截器从而完成了增强操作。

示例代码https://github.com/lili1990/learning/tree/master/spring-aop/spring-aop-demo