学习java,肯定绕不开Spring,学习Spring,就一定要了解IOC和AOP。虽然Spring提供了很多注解让我们可以很方便的使用这些功能,但是我们不仅要知其然更要知其所以然。下面我们从源码层面简单的解析下Spring的AOP是如何工作的。
@EnableAspectJAutoProxy作用
要开启Spring的AOP,需要在配置主配置类上加上注解@EnableAspectJAutoProxy
,我们就从这个注解入手,看看Spring基于这个注解做了什么。
可以看到@EnableAspectJAutoProxy
这个注解主要给容器导入了一个AspectJAutoProxyRegistrar
,我们接着看AspectJAutoProxyRegistrar
又做了什么。
AspectJAutoProxyRegistrar
实现了ImportBeanDefinitionRegistrar
,我们知道ImportBeanDefinitionRegistrar
的作用是是为容器注册bean的定义信息,那么AspectJAutoProxyRegistrar
为容器注册了什么bean的定义信息,我们可以看看AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
这行代码是怎么调用的
最后在AopConfigUtils中我们看到了这样一段代码
现在我们知道AspectJAutoProxyRegistrar
其实就是为容器注册了一个名为internalAutoProxyCreator
类型为AnnotationAwareAspectJAutoProxyCreator
的BeanDefinition,也就是bean的定义信息。
至此,就是注解@EnableAspectJAutoProxy
所做的事情。接下来只需要搞清AnnotationAwareAspectJAutoProxyCreator
在Spring中起了什么作用就可以搞清AOP了。
AnnotationAwareAspectJAutoProxyCreator作用
为了测试AnnotationAwareAspectJAutoProxyCreator
所起的作用,我们创建一个被代理类以及切面类,并在主配置类上加上@EnableAspectJAutoProxy
注解。
- 被代理类
public class Demo {
public void printHello(){
System.out.println("hello");
}}
- 切面类
@Aspect // 表明这是一个切面类
public class DemoAspect {
/** * 抽取公共的切点,本类以及其他切面类都可以使用 */
@Pointcut("execution(public void com.atguigu.aop.Demo.printHello(..))")
public void cut() {
}
/** * 前置通知 */
@Before("cut()")
public void beforePrint() {
System.out.println("开始执行");
}
/** * 后置通知 */
@After("cut()")
public void afterPrint() {
System.out.println("执行结束");
}
}
- 主配置类
@EnableAspectJAutoProxy // 开启自动代理
@Configuration
public class MyConfigOfAOP {
/**
* 被代理类加入容器
* @return
*/
@Bean
public Demo demo(){
return new Demo();
}
/**
* 切面类加入容器
* @return
*/
@Bean
public DemoAspect demoAspect(){
return new DemoAspect();
}
}
- 测试类
public class IOCTest_AOP {
@Test
public void test(){
//创建IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfAOP.class);
//获取容器中的对象
Demo demo=applicationContext.getBean(Demo.class);
demo.printHello();
}
}
我们用debug模式运行该测试用例,发现demo对象的类型为一个代理对象类型,并不是我们定义的类型。我们猜测这个代理对象应该是AnnotationAwareAspectJAutoProxyCreator
创建出来的,因为@EnableAspectJAutoProxy
只往容器里注册了AnnotationAwareAspectJAutoProxyCreator
这个对象。
有了上面的猜想,我们先来看下AnnotationAwareAspectJAutoProxyCreator
的继承关系:
注意到AnnotationAwareAspectJAutoProxyCreator
的继承关系中有实现了一个SmartInstantiationAwareBeanPostProcessor
的接口,而这个接口是继承了 BeanPostProcessor
,也就是说这个类是一个bean的后置处理器,我们知道bean的后置处理器会在bean对象初始化前后进行一些相应的处理。我们猜测代理对象就是在创建Demo对象时后置处理器进行创建的。在继承关系中找到与后置处理器有关的方法。有如下两个,且都位于AbstractAutoProxyCreator
中。
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 包装bean对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
第一个方法没有对bean做什么处理,我们跳过。主要看第二个方法,打上断点。发现在创建Demo对象(被代理对象)时会进入org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
方法。该方法前面有一系列的判断,Demo对象都是false,暂时先不管。主要看Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
,
/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && 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;
}
// Create proxy if we have advice.
// 判断当前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;
}
dedug进入该方法,来到org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
方法。
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
再进入来到org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
方法。该方法会首先找到所有的通知方法(标注@Before,@After,@AfterReturning,@AfterThrowing
的方法),然后找到可以应用到传入类型beanClass
的通知方法并进行排序返回。排序规则是@AfterThrowing-》@AfterReturning-》@After-》@Before
,后面我们将会看到为什么这么排序。
/**
* Find all eligible Advisors for auto-proxying this class.
* @param beanClass the clazz to find advisors for
* @param beanName the name of the currently proxied bean
* @return the empty List, not {@code null},
* if there are no pointcuts or interceptors
* @see #findCandidateAdvisors
* @see #sortAdvisors
* @see #extendAdvisors
*/
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 找到所有候选的通知方法
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 找到可以应用到当前bean的通知方法
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 给通知方法排序,
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
回到org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
方法。现在可以知道getAdvicesAndAdvisorsForBean
方法的作用就是找到能切入当前bean的排序过的通知方法。如果能找到就会创建一个代理对象返回。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
...
// Create proxy if we have advice.
// 找到能切入当前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;
}
我们再来看下代理对象的创建过程。该方法主要做了两件事:
- 创建一个
ProxyFactory
对象,并将排序好的通知方法保存在对象里。 - 创建代理对象,并将
ProxyFactory
对象保存在对象里。
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 创建一个ProxyFactory
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 将排序好的通知方法放入proxyFactory
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 创建代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}
至此,一个代理对象就创建完成。之后该对象会注入容器,后面从容器中获取该类型的对象会得到一个代理对象,执行代理对象的方法会执行性相关通知。
代理方法的调用
我们给代理对象调用方法打上断点,debug运行。
可以看到代理对象中保存了之前创建的ProxyFactory
对象,并且ProxyFactory
对象中保存了能应用到该方法的通知方法。debug进入,来到org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
方法内。
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
// 获取目标对象
target = getTarget();
if (target != null) {
// 获取目标类型
targetClass = target.getClass();
}
// 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
其中获取拦截器chain
链其实就是将通知方法转换成拦截器。我们可以来看下。
转化操作位于org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#getInterceptors
方法中。对通知的转化主要分两种:
- 通知属于
MethodInterceptor
,直接强转为MethodInterceptor
加入拦截链; - 通知不属于
MethodInterceptor
,使用AdvisorAdapter
包装成MethodInterceptor
加入拦截器链。
其中后置通知属于MethodInterceptor
,前置通知、返回通知和异常通知需要AdvisorAdapter
进行包装。
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
Advice advice = advisor.getAdvice();
// 如果通知属于MethodInterceptor,直接加入
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// 不属于MethodInterceptor,使用AdvisorAdapter包装成MethodInterceptor然后加入
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}
回到org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
再拿到拦截器链之后,进行判断。如果拦截器链为空,则直接调用目标方法。否则新建CglibMethodInvocation
对象调用procee
方法。
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
...
// 获取目标对象
target = getTarget();
if (target != null) {
// 获取目标类型
targetClass = target.getClass();
}
// 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
// 拦截器链为空,直接调用目标方法
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); // 调用目标方法
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
// 使用拦截器链调用目标方法
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
进入org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
方法。
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 判断当前拦截器是不是最后一个拦截器,如果是,执行目标方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 当前拦截器计数加一(从-1开始),获取下一个拦截器(当前拦截器计数位置的拦截器)
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
// 执行对应拦截器的invoke方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
proceed方法主要工作是,判断当前拦截器是否是拦截器链中最后一个拦截器,如果是,则执行目标方法;否则获取下一个拦截器,执行拦截器的invoke方法。按照拦截器链的顺序,各个invoke方法执行顺序为异常-》返回-》后置-》前置。
而对于不同的拦截器,invoke方法的实现不同,具体如下
- 异常拦截器
org.springframework.aop.aspectj.AspectJAfterThrowingAdvice#invoke
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeOnThrowing(ex)) {
// 执行异常通知方法
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
}
- 返回拦截器
org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor#invoke
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
// 执行返回通知方法
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
- 后置拦截器
org.springframework.aop.aspectj.AspectJAfterAdvice#invoke
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
// 执行后置通知方法
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
- 前置拦截器
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor#invoke
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 执行前置通知方法
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
这里Spring用了一个很巧妙的设计,类似于递归调用。每个拦截器的invoke方法都会调用proceed方法,proceed方法又会调用下一个拦截器链的invoke方法...直到当前拦截器是最后一个拦截器。调用最后一个拦截器的invoke方法,也会调用proceed方法。这时this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1
返回true,会执行目标方法。目标方法执行完,proceed方法结束后会回到invoke方法。invoke方法结束后,又会回到proceed方法,proceed方法结束又会回到上一个拦截器的invoke方法...借由这样的机制可以让拦截器按顺序执行。顺序为前置-》后置-》返回-》异常,和拦截器链的顺序相反。
另外,注意到前置拦截器的前置通知方法是在调用proceed方法之前执行,这就保证了前置通知会在目标方法调用之前执行。还有除了异常拦截器,其余拦截器都没有捕获异常。这就导致如果目标方法出现异常,除了前置通知方法会执行(前置通知会在目标方法调用之前执行),其余通知方法不会执行,直到来到异常拦截器执行异常通知方法。这样就实现了异常通知的功能。
以上,就是SpringAOP从创建到调用的全部解析。只是浅略的分析,以后有机会再完善。