看过了前面的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); wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); return wrappedBean; }
|
下面来具体看一下实现方式
Spring AOP的基本接口是 Advisor
, 它持有一个 AOP Advice「在joinpoint处要执行的增强行为」, 同时还持有一个决定 Advice是否适用的过滤器, 如pointcut; 也就是说 Advisor
决定是否要对某个方法进行增强以及增强的具体逻辑实现是什么
还有一个我们需要注意的接口就是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
| @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; }
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"/>
<bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="logAdvice"/> <property name="pattern" value="com.demo.spring.service.*" /> </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(); } }
|
当然我们也可以使用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 源码解析