0%

Spring AOP 使用及浅析

看过了前面的Spring Beans相关的 IOC 功能, 接下来我们来看看 AOP 是如何实现的

我们都知道 AOP 是通过动态代理来实现的, 但是代理这一步是如何实现的呢? 其实就是之前提到过的, 在Spring Bean的创建过程中, 实现BeanPostProcessor的接口可以对创建好的Bean进行修改替换等操作

1
2
3
4
5
6
7
8
9
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
invokeAwareMethods(beanName, bean);
Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd);

// 依次执行beanFactory中所有实现BeanPostProcessor的后置方法,对Bean进行修改(*AOP创建返回代理处*)
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}

下面来具体看一下实现方式

Spring AOP的基本接口是 Advisor, 它持有一个 AOP Advice「在joinpoint处要执行的增强行为」, 同时还持有一个决定 Advice是否适用的过滤器, 如pointcut; 也就是说 Advisor决定是否要对某个方法进行增强以及增强的具体逻辑实现是什么

aop

还有一个我们需要注意的接口就是Interceptor, 这个接口继承了Advice, 也就是说它是用来对方法进行修改增强的, 它还有一些子类, 如MethodBeforeAdviceInterceptor用来在方法执行前进行处理, MethodInterceptor用来在方法执行前后进行自定义逻辑处理

当定义好了 Advisor后, 需要一个类来负责在创建bean的时候用所有的Advisor进行匹配并生成对应的代理类返回, 这个类就是AbstractAutoProxyCreator的实现类, 它继承了BeanPostProcessor接口,在创建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
// ********  AbstractAutoProxyCreator   *********
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 对类进行代理包装
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

// 如果有匹配的advice,则创建代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}

this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

现在写一个例子代码来感受一下, 验证一下前面的结论

首先创建一个正常的服务

1
2
3
4
5
public class DemoService {
public void service() {
System.out.println("this is demoService");
}
}

创建Advice类

1
2
3
4
5
6
7
8
9
public class LogAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long st = System.nanoTime();
Object result = invocation.proceed();
System.out.println("cost time: " + (System.nanoTime() - st));
return result;
}
}

定义配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<bean id="logAdvice" class="com.demo.spring.advice.LogAdvice"/>
<bean id="demoService" class="com.demo.spring.service.DemoService"/>

<!-- 定义Advisor -->
<bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 定义使用的advice -->
<property name="advice" ref="logAdvice"/>
<!-- 定义匹配方式 -->
<property name="pattern" value="com.demo.spring.service.*" />
</bean>

<!-- 定义AspectJAwareAdvisorAutoProxyCreator Bean, 用来在创建bean实例时匹配生成代理类 -->
<bean class="org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator"/>

测试方法如下:

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
DemoService demoService = applicationContext.getBean(DemoService.class);
demoService.service();
}
}

// 结果
// this is demoService
// cost time: 17346041

当然我们也可以使用aop标签来简化配置文件

1
2
3
4
5
6
<aop:config proxy-target-class="false">
<aop:pointcut id="service"
expression="execution(* com.demo.spring.service..*(..)))"/>
<aop:advisor advice-ref="logAdvice"
pointcut-ref="service"/>
</aop:config>

执行结果是一样的, 只是这种配置会自动注册AspectJAwareAdvisorAutoProxyCreator Bean, Advisor等, 具体可以参数 AopNamespaceHandler

参考资料: Spring AOP 源码解析